diff --git a/bus.jam b/bus.jam index 3c37c69..ea112e6 100644 --- a/bus.jam +++ b/bus.jam @@ -5,15 +5,17 @@ // buffers and the four device state buffers (gpu, dma, irq, timer). // // IO addresses are dispatched to the matching device module's handler; -// everything else falls through to the flat byte buffers. The device -// modules don't import bus.jam in turn — they take whichever raw -// pointers they need as arguments — so the import graph stays acyclic. +// everything else falls through to the flat byte buffers. Device modules +// don't import bus.jam — they take whichever raw pointers they need as +// arguments — so the import graph stays acyclic. The orchestration layer +// (cpu/dma/main/tests) imports `Bus` from here for its function signatures. const { Gpu } = import("gpu"); const { dmaRead32, dmaRead16, dmaRead8, dmaWrite32, dmaWrite16, dmaWrite8, dmaAlloc, dmaFree } = import("dma"); const { irqRead32, irqRead16, irqRead8, - irqWrite32, irqWrite16, irqWrite8, irqAlloc, irqFree } = import("irq"); + irqWrite32, irqWrite16, irqWrite8, irqAlloc } = import("irq"); +const { Buf } = import("std/buf"); const { Timer } = import("timer"); const { gteAlloc, gteFree } = import("gte"); const { Cdrom } = import("cdrom"); @@ -49,7 +51,7 @@ pub extern fn fflush(stream: u64) i32; // VRAM) plus the device-state pointers. Other modules redeclare this // shape structurally — Jam v0.1.0 doesn't reliably export struct types // across modules. -const Bus = struct { +pub const Bus = struct { ram: *mut[] u8, bios: *mut[] u8, spad: *mut[] u8, @@ -57,7 +59,7 @@ const Bus = struct { vram: *mut[] u8, gpu: Gpu, dma: *mut[] u32, - irq: *mut[] u32, + irq: Buf(u32), timer: Timer, gte: *mut[] u32, cdrom: Cdrom, @@ -230,7 +232,7 @@ pub fn freeBus(b: Bus) { free(b.vram); // b.gpu is by-value; no free needed. dmaFree(b.dma); - irqFree(b.irq); + // b.irq is a Buf(u32) — auto-dropped when `b` goes out of scope. // b.timer is by-value; no free needed. gteFree(b.gte); // b.cdrom is by-value; no free needed. @@ -252,7 +254,7 @@ pub fn ioRead32(bus: Bus, off: u32) u32 { match (off) { 0x040..=0x04F { return padRead32(bus.pad, bus.mcd, bus.mcdRam, off - 0x040); } 0x050..=0x05F { return bus.sio1.read32(off - 0x050); } - 0x070..=0x07F { return irqRead32(bus.irq, off - 0x070); } + 0x070..=0x07F { return irqRead32(bus.irq.typed(), off - 0x070); } 0x080..=0x0FF { return dmaRead32(bus.dma, off - 0x080); } 0x100..=0x12F { return bus.timer.read32(off - 0x100); } 0x800..=0x803 { return bus.cdrom.read32(off - 0x800); } @@ -269,7 +271,7 @@ pub fn ioRead16(bus: Bus, off: u32) u32 { match (off) { 0x040..=0x04F { return padRead16(bus.pad, bus.mcd, bus.mcdRam, off - 0x040); } 0x050..=0x05F { return bus.sio1.read16(off - 0x050); } - 0x070..=0x07F { return irqRead16(bus.irq, off - 0x070); } + 0x070..=0x07F { return irqRead16(bus.irq.typed(), off - 0x070); } 0x080..=0x0FF { return dmaRead16(bus.dma, off - 0x080); } 0x100..=0x12F { return bus.timer.read16(off - 0x100); } 0x800..=0x803 { return bus.cdrom.read16(off - 0x800); } @@ -286,7 +288,7 @@ pub fn ioRead8(bus: Bus, off: u32) u32 { match (off) { 0x040..=0x04F { return padRead8(bus.pad, bus.mcd, bus.mcdRam, off - 0x040); } 0x050..=0x05F { return bus.sio1.read8(off - 0x050); } - 0x070..=0x07F { return irqRead8(bus.irq, off - 0x070); } + 0x070..=0x07F { return irqRead8(bus.irq.typed(), off - 0x070); } 0x080..=0x0FF { return dmaRead8(bus.dma, off - 0x080); } 0x100..=0x12F { return bus.timer.read8(off - 0x100); } 0x800..=0x803 { return bus.cdrom.read8(off - 0x800); } @@ -307,9 +309,9 @@ pub fn ioWrite32(bus: Bus, cop0: *mut[] u32, off: u32, val: u32) { match (off) { 0x040..=0x04F { padWrite32(bus.pad, bus.mcd, off - 0x040, val); return; } 0x050..=0x05F { bus.sio1.write32(off - 0x050, val); return; } - 0x070..=0x07F { irqWrite32(bus.irq, cop0, off - 0x070, val); return; } + 0x070..=0x07F { irqWrite32(bus.irq.typed(), cop0, off - 0x070, val); return; } 0x080..=0x0FF { dmaWrite32(bus.dma, bus, cop0, off - 0x080, val); return; } - 0x100..=0x12F { bus.timer.write32(bus.irq, cop0, off - 0x100, val); return; } + 0x100..=0x12F { bus.timer.write32(bus.irq.typed(), cop0, off - 0x100, val); return; } 0x800..=0x803 { bus.cdrom.write32(off - 0x800, val); return; } 0x810..=0x817 { bus.gpu.write32(bus.vram, off - 0x810, val); return; } 0x820..=0x827 { bus.mdec.write32(bus.mdecIn, bus.mdecOut, off - 0x820, val); return; } @@ -324,9 +326,9 @@ pub fn ioWrite16(bus: Bus, cop0: *mut[] u32, off: u32, val: u32) { match (off) { 0x040..=0x04F { padWrite16(bus.pad, bus.mcd, off - 0x040, val); return; } 0x050..=0x05F { bus.sio1.write16(off - 0x050, val); return; } - 0x070..=0x07F { irqWrite16(bus.irq, cop0, off - 0x070, val); return; } + 0x070..=0x07F { irqWrite16(bus.irq.typed(), cop0, off - 0x070, val); return; } 0x080..=0x0FF { dmaWrite16(bus.dma, bus, cop0, off - 0x080, val); return; } - 0x100..=0x12F { bus.timer.write16(bus.irq, cop0, off - 0x100, val); return; } + 0x100..=0x12F { bus.timer.write16(bus.irq.typed(), cop0, off - 0x100, val); return; } 0x800..=0x803 { bus.cdrom.write16(off - 0x800, val); return; } 0x810..=0x817 { bus.gpu.write16(bus.vram, off - 0x810, val); return; } 0x820..=0x827 { bus.mdec.write16(bus.mdecIn, bus.mdecOut, off - 0x820, val); return; } @@ -341,9 +343,9 @@ pub fn ioWrite8(bus: Bus, cop0: *mut[] u32, off: u32, val: u32) { match (off) { 0x040..=0x04F { padWrite8(bus.pad, bus.mcd, off - 0x040, val); return; } 0x050..=0x05F { bus.sio1.write8(off - 0x050, val); return; } - 0x070..=0x07F { irqWrite8(bus.irq, cop0, off - 0x070, val); return; } + 0x070..=0x07F { irqWrite8(bus.irq.typed(), cop0, off - 0x070, val); return; } 0x080..=0x0FF { dmaWrite8(bus.dma, bus, cop0, off - 0x080, val); return; } - 0x100..=0x12F { bus.timer.write8(bus.irq, cop0, off - 0x100, val); return; } + 0x100..=0x12F { bus.timer.write8(bus.irq.typed(), cop0, off - 0x100, val); return; } 0x800..=0x803 { bus.cdrom.write8(off - 0x800, val); return; } 0x810..=0x817 { bus.gpu.write8(bus.vram, off - 0x810, val); return; } 0x820..=0x827 { bus.mdec.write8(bus.mdecIn, bus.mdecOut, off - 0x820, val); return; } @@ -442,7 +444,7 @@ pub fn busVramPtr(bus: Bus) *mut[] u8 { return bus.vram; } // busGpuPtr removed — use `bus.gpu.bufPtr()` directly for raw slot // access. The `bus.gpu` field is now a `Gpu` struct value. pub fn busDmaPtr(bus: Bus) *mut[] u32 { return bus.dma; } -pub fn busIrqPtr(bus: Bus) *mut[] u32 { return bus.irq; } +pub fn busIrqPtr(bus: Bus) *mut[] u32 { return bus.irq.typed(); } // busTimerPtr removed — `bus.timer` is by-value now. Callers use // the Timer methods directly via `bus.timer.method(...)`. pub fn busGtePtr(bus: Bus) *mut[] u32 { return bus.gte; } diff --git a/cpu.jam b/cpu.jam index f3cebf7..4c936f0 100644 --- a/cpu.jam +++ b/cpu.jam @@ -29,6 +29,8 @@ const { busRead32, busRead16, busRead8, const { Gpu } = import("gpu"); const { dmaAlloc } = import("dma"); const { irqAlloc } = import("irq"); +const { Buf } = import("std/buf"); +const { Bus } = import("bus"); const { Timer } = import("timer"); const { gteAlloc, gteDataRead, gteDataWrite, gteCtrlRead, gteCtrlWrite, gteExec, gteOpCycles } = import("gte"); @@ -52,31 +54,6 @@ const Disc = struct { cuePtr: *mut[] u8, }; -const Bus = struct { - ram: *mut[] u8, - bios: *mut[] u8, - spad: *mut[] u8, - io: *mut[] u8, - vram: *mut[] u8, - gpu: Gpu, - dma: *mut[] u32, - irq: *mut[] u32, - timer: Timer, - gte: *mut[] u32, - cdrom: Cdrom, - disc: *mut[] Disc, - pad: *mut[] u8, - spu: *mut[] u8, - spuram: *mut[] u8, - mdec: Mdec, - mdecIn: *mut[] u8, - mdecOut: *mut[] u8, - sio1: Sio1, - mcd: Mcd, - mcdRam: *mut[] u8, - biosLoaded: u8, -}; - const Cpu = struct { pc: u32, nextPc: u32, diff --git a/dma.jam b/dma.jam index 0000623..bde82c9 100644 --- a/dma.jam +++ b/dma.jam @@ -13,6 +13,8 @@ const { Gpu } = import("gpu"); const { irqRaise, IC_DMA } = import("irq"); +const { Buf } = import("std/buf"); +const { Bus } = import("bus"); const { Cdrom } = import("cdrom"); const { Mdec } = import("mdec"); const { spuWrite16, spuRead16 } = import("spu"); @@ -35,30 +37,6 @@ const Disc = struct { cuePtr: *mut[] u8, }; -const Bus = struct { - ram: *mut[] u8, - bios: *mut[] u8, - spad: *mut[] u8, - io: *mut[] u8, - vram: *mut[] u8, - gpu: Gpu, - dma: *mut[] u32, - irq: *mut[] u32, - timer: *mut[] u32, - gte: *mut[] u32, - cdrom: Cdrom, - disc: *mut[] Disc, - pad: *mut[] u8, - spu: *mut[] u8, - spuram: *mut[] u8, - mdec: Mdec, - mdecIn: *mut[] u8, - mdecOut: *mut[] u8, - sio1: *mut[] u8, - mcd: *mut[] u8, - mcdRam: *mut[] u8, - biosLoaded: u8, -}; // d[0..20] = 7 channels × 3 regs (MADR/BCR/CHCR) // d[21] = DPCR diff --git a/irq.jam b/irq.jam index 606ed7f..ae82a81 100644 --- a/irq.jam +++ b/irq.jam @@ -12,9 +12,7 @@ // [0] I_STAT // [1] I_MASK -pub extern fn malloc(size: u64) *mut[] u8; -pub extern fn free(ptr: *mut[] u8); -pub extern fn memset(dst: *mut[] u8, c: i32, n: u64) *mut[] u8; +const { Buf } = import("std/buf"); // IRQ line bits (psxe ic.h: IC_VBLANK = 0x001, etc.). pub fn IC_VBLANK() u32 { return 0x001; } @@ -35,18 +33,11 @@ pub fn IC_LP_PIO() u32 { return 0x400; } const CAUSE_IP2: u32 = 0x00000400; const C0_CAUSE: u32 = 13; -pub fn irqAlloc() *mut[] u32 { - // 8 bytes for stat/mask + 11 × 4 bytes for per-source fire counters - // (ic[2..12]) + slack. Used by main.jam to print which interrupt - // sources are firing during a hang. - var raw: *mut[] u8 = malloc(64); - memset(raw, 0, 64); - return raw as *mut[] u32; -} - -pub fn irqFree(ic: *mut[] u32) { - free(ic as *mut[] u8); -} +// 16 u32 slots: [0]=I_STAT, [1]=I_MASK, [2..12]=per-source fire counters +// (used by main.jam to print which interrupt sources are firing during a +// hang), plus slack. The Buf owns the allocation — its drop frees on +// scope exit, no irqFree helper needed. +pub fn irqAlloc() Buf(u32) { return Buf(u32).zeroed(16); } pub fn irqStat(ic: *mut[] u32) u32 { return ic[0]; } pub fn irqMask(ic: *mut[] u32) u32 { return ic[1]; } diff --git a/main.jam b/main.jam index eb786b5..6fdf4ed 100644 --- a/main.jam +++ b/main.jam @@ -9,6 +9,7 @@ // file doesn't have to link SDL2 just to run them. const { + Bus, allocBus, freeBus, busRead32, busRead16, busRead8, busWrite32, busWrite16, busWrite8, @@ -48,6 +49,7 @@ const { Vec } = import("std").collections; const { File, exists } = import("std/fs"); const { Args, args, next, exit } = import("std").process; const { print } = import("std/fmt"); +const { Buf } = import("std/buf"); extern fn malloc(size: u64) *mut[] u8; extern fn free(ptr: *mut[] u8); @@ -106,30 +108,6 @@ const Disc = struct { cuePtr: *mut[] u8, }; -const Bus = struct { - ram: *mut[] u8, - bios: *mut[] u8, - spad: *mut[] u8, - io: *mut[] u8, - vram: *mut[] u8, - gpu: Gpu, - dma: *mut[] u32, - irq: *mut[] u32, - timer: Timer, - gte: *mut[] u32, - cdrom: Cdrom, - disc: *mut[] Disc, - pad: *mut[] u8, - spu: *mut[] u8, - spuram: *mut[] u8, - mdec: Mdec, - mdecIn: *mut[] u8, - mdecOut: *mut[] u8, - sio1: Sio1, - mcd: Mcd, - mcdRam: *mut[] u8, - biosLoaded: u8, -}; const Cpu = struct { pc: u32, @@ -338,7 +316,7 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { // psxe fires the GPU hblank/vblank events BEFORE the sysclk timer +2.) // 1. CDROM (psx.c:95) - bus.cdrom.update(bus.disc, bus.spu, bus.irq, cop0, delta); + bus.cdrom.update(bus.disc, bus.spu, bus.irq.typed(), cop0, delta); // 2. GPU (psx.c:96) — psx_gpu_update: scanline window [0,3413], HBlank // [2560,3413]. The HBlank event (line++, GPUSTAT bit-31 odd/even, the @@ -351,7 +329,7 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { if (currHb && !prevHb) { // psxe gpu_hblank_event (gpu.c:1908-1962), fired from the // HBlank-begin edge. - bus.timer.hblankBegin(bus.irq, cop0); + bus.timer.hblankBegin(bus.irq.typed(), cop0); if (line < SCANLINES_VDRAW) { if ((line & 1) != 0) { gpuBuf[46] = gpuBuf[46] | 0x80000000; @@ -363,8 +341,8 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { } line = line + 1; if (line == SCANLINES_VDRAW) { - bus.timer.vblankBegin(bus.irq, cop0); - irqRaise(bus.irq, cop0, IC_VBLANK()); + bus.timer.vblankBegin(bus.irq.typed(), cop0); + irqRaise(bus.irq.typed(), cop0, IC_VBLANK()); } else if (line == SCANLINES_TOTAL) { bus.timer.vblankEnd(); line = 0; @@ -378,23 +356,23 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { } // 3. PAD (psx.c:97) - padUpdate(bus.pad, bus.irq, cop0, delta); + padUpdate(bus.pad, bus.irq.typed(), cop0, delta); // 4. TIMER sysclk (psx.c:98) — updateCyc ignores delta, uses 2 (psxe). - bus.timer.updateCyc(bus.irq, cop0, delta); + bus.timer.updateCyc(bus.irq.typed(), cop0, delta); // 5. DMA (psx.c:99) dmaTickSpu(bus.dma, delta); - dmaUpdate(bus.dma, bus.irq, cop0); + dmaUpdate(bus.dma, bus.irq.typed(), cop0); // --- jam-only extras: psxe steps NEITHER in psx_update. Kept at the // end (least perturbation to the psxe device order above) and flagged // as divergences — SIO1 → peripherals task, SPU CPU-clock → SPU task // (psxe runs SPU on the audio thread; revisit once the deterministic // CPU surface matches psxe). - bus.sio1.update(bus.irq, cop0, delta); + bus.sio1.update(bus.irq.typed(), cop0, delta); spuAcc = spuAcc + delta; while (spuAcc >= 768) { spuAcc = spuAcc - 768; - gpuBuf[34] = spuGetSample(bus.spu, bus.spuram, bus.irq, cop0); + gpuBuf[34] = spuGetSample(bus.spu, bus.spuram, bus.irq.typed(), cop0); } } // NOTE: do NOT commitLoad here — each instruction's own commitLoad @@ -628,7 +606,7 @@ fn main() { var audioCtx: *mut[] u8 = malloc(32); setU64(audioCtx, 0, bus.spu as u64); setU64(audioCtx, 8, bus.spuram as u64); - setU64(audioCtx, 16, bus.irq as u64); + setU64(audioCtx, 16, bus.irq.typed() as u64); setU64(audioCtx, 24, cop0 as u64); const cbAddr: u64 = psone_audio_cb as u64; var audioDev: u32 = sdlAudioOpen(1024, cbAddr, audioCtx as u64); diff --git a/tests.jam b/tests.jam index 5a16a78..4e8905a 100644 --- a/tests.jam +++ b/tests.jam @@ -7,6 +7,7 @@ const { assert } = import("test"); const { + Bus, allocBus, freeBus, busWrite32 } = import("bus"); @@ -34,6 +35,7 @@ const { padAlloc, padUpdate } = import("pad"); const { spuAlloc, spuRamAlloc, spuUpdate } = import("spu"); const { Sio1 } = import("sio1"); const { irqAlloc, irqRaise, IC_VBLANK } = import("irq"); +const { Buf } = import("std/buf"); const { Timer } = import("timer"); const { Mcd } = import("mcd"); const { Mdec } = import("mdec"); @@ -56,30 +58,6 @@ const Disc = struct { cuePtr: *mut[] u8, }; -const Bus = struct { - ram: *mut[] u8, - bios: *mut[] u8, - spad: *mut[] u8, - io: *mut[] u8, - vram: *mut[] u8, - gpu: Gpu, - dma: *mut[] u32, - irq: *mut[] u32, - timer: Timer, - gte: *mut[] u32, - cdrom: Cdrom, - disc: *mut[] Disc, - pad: *mut[] u8, - spu: *mut[] u8, - spuram: *mut[] u8, - mdec: Mdec, - mdecIn: *mut[] u8, - mdecOut: *mut[] u8, - sio1: Sio1, - mcd: Mcd, - mcdRam: *mut[] u8, - biosLoaded: u8, -}; const Cpu = struct { pc: u32, @@ -575,20 +553,20 @@ fn emuRunOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { const before: u64 = c.cycles; step(c, bus, regs, cop0); const delta: u32 = (c.cycles - before) as u32; - bus.timer.updateCyc(bus.irq, cop0, delta); - dmaUpdate(bus.dma, bus.irq, cop0); - bus.cdrom.update(bus.disc, bus.irq, cop0, delta); - padUpdate(bus.pad, bus.irq, cop0, delta); - bus.sio1.update(bus.irq, cop0, delta); - spuUpdate(bus.spu, bus.spuram, bus.irq, cop0, delta); + bus.timer.updateCyc(bus.irq.typed(), cop0, delta); + dmaUpdate(bus.dma, bus.irq.typed(), cop0); + bus.cdrom.update(bus.disc, bus.irq.typed(), cop0, delta); + padUpdate(bus.pad, bus.irq.typed(), cop0, delta); + bus.sio1.update(bus.irq.typed(), cop0, delta); + spuUpdate(bus.spu, bus.spuram, bus.irq.typed(), cop0, delta); lineCyc = lineCyc + delta; if (lineCyc >= CYC_PER_SCANLINE) { lineCyc = lineCyc - CYC_PER_SCANLINE; - bus.timer.hblankBegin(bus.irq, cop0); + bus.timer.hblankBegin(bus.irq.typed(), cop0); bus.timer.hblankEnd(); if (line == SCANLINES_VDRAW) { - bus.timer.vblankBegin(bus.irq, cop0); - irqRaise(bus.irq, cop0, IC_VBLANK()); + bus.timer.vblankBegin(bus.irq.typed(), cop0); + irqRaise(bus.irq.typed(), cop0, IC_VBLANK()); } line = line + 1; }