diff --git a/bus.jam b/bus.jam index ea112e6..58f24f4 100644 --- a/bus.jam +++ b/bus.jam @@ -12,23 +12,23 @@ const { Gpu } = import("gpu"); const { dmaRead32, dmaRead16, dmaRead8, - dmaWrite32, dmaWrite16, dmaWrite8, dmaAlloc, dmaFree } = import("dma"); + dmaWrite32, dmaWrite16, dmaWrite8, dmaAlloc } = import("dma"); const { irqRead32, irqRead16, irqRead8, irqWrite32, irqWrite16, irqWrite8, irqAlloc } = import("irq"); -const { Buf } = import("std/buf"); +const { Vec } = import("std/collections"); const { Timer } = import("timer"); -const { gteAlloc, gteFree } = import("gte"); +const { gteAlloc } = import("gte"); const { Cdrom } = import("cdrom"); const { discAlloc, discFree } = import("disc"); const { padRead32, padRead16, padRead8, padWrite32, padWrite16, padWrite8, - padAlloc, padFree } = import("pad"); + padAlloc } = import("pad"); const { spuRead32, spuRead16, spuRead8, spuWrite32, spuWrite16, spuWrite8, - spuAlloc, spuRamAlloc, spuFree, spuRamFree } = import("spu"); -const { Mdec, mdecInputAlloc, mdecOutputAlloc, mdecBufFree } = import("mdec"); + spuAlloc, spuRamAlloc } = import("spu"); +const { Mdec, mdecInputAlloc, mdecOutputAlloc } = import("mdec"); const { Sio1 } = import("sio1"); -const { Mcd, mcdRamAlloc, mcdRamFree } = import("mcd"); +const { Mcd, mcdRamAlloc } = import("mcd"); // Disc shape — kept in sync with disc.jam so callers can reference the // stored pointer via `bus.disc[0]`. @@ -41,9 +41,6 @@ const Disc = struct { cuePtr: *mut[] u8, }; -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; pub extern fn fflush(stream: u64) i32; // The Bus struct holds every piece of state we route through the @@ -52,27 +49,27 @@ pub extern fn fflush(stream: u64) i32; // shape structurally — Jam v0.1.0 doesn't reliably export struct types // across modules. pub const Bus = struct { - ram: *mut[] u8, - bios: *mut[] u8, - spad: *mut[] u8, - io: *mut[] u8, - vram: *mut[] u8, + ram: Vec(u8), + bios: Vec(u8), + spad: Vec(u8), + io: Vec(u8), + vram: Vec(u8), gpu: Gpu, - dma: *mut[] u32, - irq: Buf(u32), + dma: Vec(u32), + irq: Vec(u32), timer: Timer, - gte: *mut[] u32, + gte: Vec(u32), cdrom: Cdrom, disc: *mut[] Disc, - pad: *mut[] u8, - spu: *mut[] u8, - spuram: *mut[] u8, + pad: Vec(u8), + spu: Vec(u8), + spuram: Vec(u8), mdec: Mdec, - mdecIn: *mut[] u8, - mdecOut: *mut[] u8, + mdecIn: Vec(u8), + mdecOut: Vec(u8), sio1: Sio1, mcd: Mcd, - mcdRam: *mut[] u8, + mcdRam: Vec(u8), biosLoaded: u8, }; @@ -176,31 +173,33 @@ pub fn route(paddr: u32) BusRoute { // ---------- public surface --------------------------------------------- pub fn allocBus() Bus { - var ram: *mut[] u8 = malloc(RAM_SIZE as u64); - var bios: *mut[] u8 = malloc(BIOS_SIZE as u64); - var spad: *mut[] u8 = malloc(SPAD_SIZE as u64); - var io: *mut[] u8 = malloc(IO_SIZE as u64); - var vram: *mut[] u8 = malloc(VRAM_SIZE as u64); - memset(ram, 0, RAM_SIZE as u64); - memset(bios, 0, BIOS_SIZE as u64); - memset(spad, 0, SPAD_SIZE as u64); - memset(io, 0, IO_SIZE as u64); - memset(vram, 0, VRAM_SIZE as u64); + // RAM/BIOS get rewritten end-to-end by the loader (fread overwrites + // every byte), so withCapacity+setLen avoids the zero-fill traffic + // — Rust's `Vec::with_capacity(n) + set_len(n)` pattern. SPAD/IO/VRAM + // need zeroed defaults at boot (the BIOS reads them before writing), + // so they take the filled(0, N) path. + var ram: Vec(u8) = Vec(u8).withCapacity(RAM_SIZE); + var bios: Vec(u8) = Vec(u8).withCapacity(BIOS_SIZE); + ram.setLen(RAM_SIZE); + bios.setLen(BIOS_SIZE); + var spad: Vec(u8) = Vec(u8).filled(0, SPAD_SIZE); + var io: Vec(u8) = Vec(u8).filled(0, IO_SIZE); + var vram: Vec(u8) = Vec(u8).filled(0, VRAM_SIZE); // Pre-init MC1 (0x1F801000..0x1F801023) and MC2 (0x1F801060) with the // BIOS-expected default values per psxe mc1.c/mc2.c. The BIOS reads // these at boot to verify access timings and RAM mirror config; before // it writes them itself the values must match real hardware so the // self-check doesn't latch a "?" error. - bufWrite32(io, 0x00, 0x1F000000); // EXP1_BASE - bufWrite32(io, 0x04, 0x1F802000); // EXP2_BASE - bufWrite32(io, 0x08, 0x0013243F); // EXP1_DELAY - bufWrite32(io, 0x0C, 0x00003022); // EXP3_DELAY - bufWrite32(io, 0x10, 0x0013243F); // BIOS_DELAY - bufWrite32(io, 0x14, 0x200931E1); // SPU_DELAY - bufWrite32(io, 0x18, 0x00020843); // CDROM_DELAY - bufWrite32(io, 0x1C, 0x00070777); // EXP2_DELAY - bufWrite32(io, 0x20, 0x00031125); // COM_DELAY - bufWrite32(io, 0x60, 0x00000B88); // MC2 RAM_SIZE (2 MB mirror config) + bufWrite32(io.ptr, 0x00, 0x1F000000); // EXP1_BASE + bufWrite32(io.ptr, 0x04, 0x1F802000); // EXP2_BASE + bufWrite32(io.ptr, 0x08, 0x0013243F); // EXP1_DELAY + bufWrite32(io.ptr, 0x0C, 0x00003022); // EXP3_DELAY + bufWrite32(io.ptr, 0x10, 0x0013243F); // BIOS_DELAY + bufWrite32(io.ptr, 0x14, 0x200931E1); // SPU_DELAY + bufWrite32(io.ptr, 0x18, 0x00020843); // CDROM_DELAY + bufWrite32(io.ptr, 0x1C, 0x00070777); // EXP2_DELAY + bufWrite32(io.ptr, 0x20, 0x00031125); // COM_DELAY + bufWrite32(io.ptr, 0x60, 0x00000B88); // MC2 RAM_SIZE (2 MB mirror config) var b: Bus = Bus { ram: ram, bios: bios, spad: spad, io: io, vram: vram, gpu: Gpu.init(), @@ -225,80 +224,74 @@ pub fn allocBus() Bus { } pub fn freeBus(b: Bus) { - free(b.ram); - free(b.bios); - free(b.spad); - free(b.io); - free(b.vram); + // b.ram.ptr/bios/spad/io/vram are Vec(u8) — auto-dropped with `b`. // b.gpu is by-value; no free needed. - dmaFree(b.dma); - // b.irq is a Buf(u32) — auto-dropped when `b` goes out of scope. + // b.dma is a Vec(u32) — auto-dropped when `b` goes out of scope. + // b.irq is a Vec(u32) — auto-dropped when `b` goes out of scope. // b.timer is by-value; no free needed. - gteFree(b.gte); + // b.gte is a Vec(u32) — auto-dropped when `b` goes out of scope. // b.cdrom is by-value; no free needed. discFree(b.disc); - padFree(b.pad); - spuFree(b.spu); - spuRamFree(b.spuram); + // b.pad is a Vec(u8) — auto-dropped when `b` goes out of scope. + // b.spu and b.spuram are Vec(u8) — auto-dropped when `b` goes out of scope. // b.mdec is by-value; no free needed. - mdecBufFree(b.mdecIn); - mdecBufFree(b.mdecOut); + // b.mdecIn and b.mdecOut are Vec(u8) — auto-dropped when `b` goes out of scope. // b.sio1 is by-value; no free needed. // b.mcd is by-value; no free needed. - mcdRamFree(b.mcdRam); + // b.mcdRam is a Vec(u8) — auto-dropped when `b` goes out of scope. } // Dispatch an IO-region address to the right device. The offset is // relative to 0x1F801000 (i.e. r.off from the BusRoute). pub fn ioRead32(bus: Bus, off: u32) u32 { match (off) { - 0x040..=0x04F { return padRead32(bus.pad, bus.mcd, bus.mcdRam, off - 0x040); } + 0x040..=0x04F { return padRead32(bus.pad.ptr, bus.mcd, bus.mcdRam.ptr, off - 0x040); } 0x050..=0x05F { return bus.sio1.read32(off - 0x050); } - 0x070..=0x07F { return irqRead32(bus.irq.typed(), off - 0x070); } - 0x080..=0x0FF { return dmaRead32(bus.dma, off - 0x080); } + 0x070..=0x07F { return irqRead32(bus.irq.ptr, off - 0x070); } + 0x080..=0x0FF { return dmaRead32(bus.dma.ptr, off - 0x080); } 0x100..=0x12F { return bus.timer.read32(off - 0x100); } 0x800..=0x803 { return bus.cdrom.read32(off - 0x800); } - 0x810..=0x817 { return bus.gpu.read32(bus.vram, off - 0x810); } - 0x820..=0x827 { return bus.mdec.read32(bus.mdecOut, off - 0x820); } - 0xC00..=0xFFF { return spuRead32(bus.spu, bus.spuram, off - 0xC00); } + 0x810..=0x817 { return bus.gpu.read32(bus.vram.ptr, off - 0x810); } + 0x820..=0x827 { return bus.mdec.read32(bus.mdecOut.ptr, off - 0x820); } + 0xC00..=0xFFF { return spuRead32(bus.spu.ptr, bus.spuram.ptr, off - 0xC00); } _ {} } if (off >= IO_SIZE) { return 0; } - return bufRead32(bus.io, off); + return bufRead32(bus.io.ptr, off); } pub fn ioRead16(bus: Bus, off: u32) u32 { match (off) { - 0x040..=0x04F { return padRead16(bus.pad, bus.mcd, bus.mcdRam, off - 0x040); } + 0x040..=0x04F { return padRead16(bus.pad.ptr, bus.mcd, bus.mcdRam.ptr, off - 0x040); } 0x050..=0x05F { return bus.sio1.read16(off - 0x050); } - 0x070..=0x07F { return irqRead16(bus.irq.typed(), off - 0x070); } - 0x080..=0x0FF { return dmaRead16(bus.dma, off - 0x080); } + 0x070..=0x07F { return irqRead16(bus.irq.ptr, off - 0x070); } + 0x080..=0x0FF { return dmaRead16(bus.dma.ptr, off - 0x080); } 0x100..=0x12F { return bus.timer.read16(off - 0x100); } 0x800..=0x803 { return bus.cdrom.read16(off - 0x800); } - 0x810..=0x817 { return bus.gpu.read16(bus.vram, off - 0x810); } - 0x820..=0x827 { return bus.mdec.read16(bus.mdecOut, off - 0x820); } - 0xC00..=0xFFF { return spuRead16(bus.spu, bus.spuram, off - 0xC00); } + 0x810..=0x817 { return bus.gpu.read16(bus.vram.ptr, off - 0x810); } + 0x820..=0x827 { return bus.mdec.read16(bus.mdecOut.ptr, off - 0x820); } + 0xC00..=0xFFF { return spuRead16(bus.spu.ptr, bus.spuram.ptr, off - 0xC00); } _ {} } if (off >= IO_SIZE) { return 0; } - return bufRead16(bus.io, off); + return bufRead16(bus.io.ptr, off); } pub fn ioRead8(bus: Bus, off: u32) u32 { match (off) { - 0x040..=0x04F { return padRead8(bus.pad, bus.mcd, bus.mcdRam, off - 0x040); } + 0x040..=0x04F { return padRead8(bus.pad.ptr, bus.mcd, bus.mcdRam.ptr, off - 0x040); } 0x050..=0x05F { return bus.sio1.read8(off - 0x050); } - 0x070..=0x07F { return irqRead8(bus.irq.typed(), off - 0x070); } - 0x080..=0x0FF { return dmaRead8(bus.dma, off - 0x080); } + 0x070..=0x07F { return irqRead8(bus.irq.ptr, off - 0x070); } + 0x080..=0x0FF { return dmaRead8(bus.dma.ptr, off - 0x080); } 0x100..=0x12F { return bus.timer.read8(off - 0x100); } 0x800..=0x803 { return bus.cdrom.read8(off - 0x800); } - 0x810..=0x817 { return bus.gpu.read8(bus.vram, off - 0x810); } - 0x820..=0x827 { return bus.mdec.read8(bus.mdecOut, off - 0x820); } - 0xC00..=0xFFF { return spuRead8(bus.spu, bus.spuram, off - 0xC00); } + 0x810..=0x817 { return bus.gpu.read8(bus.vram.ptr, off - 0x810); } + 0x820..=0x827 { return bus.mdec.read8(bus.mdecOut.ptr, off - 0x820); } + 0xC00..=0xFFF { return spuRead8(bus.spu.ptr, bus.spuram.ptr, off - 0xC00); } _ {} } if (off >= IO_SIZE) { return 0; } - return bufRead8(bus.io, off); + return bufRead8(bus.io.ptr, off); } // Write dispatch — same table for writes. Note that DMA writes need @@ -307,53 +300,53 @@ pub fn ioRead8(bus: Bus, off: u32) u32 { // require those parameters at the outer interface. 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; } + 0x040..=0x04F { padWrite32(bus.pad.ptr, bus.mcd, off - 0x040, val); return; } 0x050..=0x05F { bus.sio1.write32(off - 0x050, 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.typed(), cop0, off - 0x100, val); return; } + 0x070..=0x07F { irqWrite32(bus.irq.ptr, cop0, off - 0x070, val); return; } + 0x080..=0x0FF { dmaWrite32(bus.dma.ptr, bus, cop0, off - 0x080, val); return; } + 0x100..=0x12F { bus.timer.write32(bus.irq.ptr, 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; } - 0xC00..=0xFFF { spuWrite32(bus.spu, bus.spuram, off - 0xC00, val); return; } + 0x810..=0x817 { bus.gpu.write32(bus.vram.ptr, off - 0x810, val); return; } + 0x820..=0x827 { bus.mdec.write32(bus.mdecIn.ptr, bus.mdecOut.ptr, off - 0x820, val); return; } + 0xC00..=0xFFF { spuWrite32(bus.spu.ptr, bus.spuram.ptr, off - 0xC00, val); return; } _ {} } if (off >= IO_SIZE) { return; } - bufWrite32(bus.io, off, val); + bufWrite32(bus.io.ptr, off, val); } 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; } + 0x040..=0x04F { padWrite16(bus.pad.ptr, bus.mcd, off - 0x040, val); return; } 0x050..=0x05F { bus.sio1.write16(off - 0x050, 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.typed(), cop0, off - 0x100, val); return; } + 0x070..=0x07F { irqWrite16(bus.irq.ptr, cop0, off - 0x070, val); return; } + 0x080..=0x0FF { dmaWrite16(bus.dma.ptr, bus, cop0, off - 0x080, val); return; } + 0x100..=0x12F { bus.timer.write16(bus.irq.ptr, 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; } - 0xC00..=0xFFF { spuWrite16(bus.spu, bus.spuram, off - 0xC00, val); return; } + 0x810..=0x817 { bus.gpu.write16(bus.vram.ptr, off - 0x810, val); return; } + 0x820..=0x827 { bus.mdec.write16(bus.mdecIn.ptr, bus.mdecOut.ptr, off - 0x820, val); return; } + 0xC00..=0xFFF { spuWrite16(bus.spu.ptr, bus.spuram.ptr, off - 0xC00, val); return; } _ {} } if (off >= IO_SIZE) { return; } - bufWrite16(bus.io, off, val); + bufWrite16(bus.io.ptr, off, val); } 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; } + 0x040..=0x04F { padWrite8(bus.pad.ptr, bus.mcd, off - 0x040, val); return; } 0x050..=0x05F { bus.sio1.write8(off - 0x050, 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.typed(), cop0, off - 0x100, val); return; } + 0x070..=0x07F { irqWrite8(bus.irq.ptr, cop0, off - 0x070, val); return; } + 0x080..=0x0FF { dmaWrite8(bus.dma.ptr, bus, cop0, off - 0x080, val); return; } + 0x100..=0x12F { bus.timer.write8(bus.irq.ptr, 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; } - 0xC00..=0xFFF { spuWrite8(bus.spu, bus.spuram, off - 0xC00, val); return; } + 0x810..=0x817 { bus.gpu.write8(bus.vram.ptr, off - 0x810, val); return; } + 0x820..=0x827 { bus.mdec.write8(bus.mdecIn.ptr, bus.mdecOut.ptr, off - 0x820, val); return; } + 0xC00..=0xFFF { spuWrite8(bus.spu.ptr, bus.spuram.ptr, off - 0xC00, val); return; } _ {} } if (off >= IO_SIZE) { return; } - bufWrite8(bus.io, off, val); + bufWrite8(bus.io.ptr, off, val); } // `busRead/Write` is the public memory-access surface — `cop0` is only @@ -364,9 +357,9 @@ pub fn busRead32(bus: Bus, addr: u32) u32 { const paddr: u32 = addr & regionMask(addr); const r: BusRoute = route(paddr); match (r.kind) { - 1 { return bufRead32(bus.ram, r.off & (RAM_SIZE - 1)); } - 2 { return bufRead32(bus.bios, r.off & (BIOS_SIZE - 1)); } - 3 { return bufRead32(bus.spad, r.off & (SPAD_SIZE - 1)); } + 1 { return bufRead32(bus.ram.ptr, r.off & (RAM_SIZE - 1)); } + 2 { return bufRead32(bus.bios.ptr, r.off & (BIOS_SIZE - 1)); } + 3 { return bufRead32(bus.spad.ptr, r.off & (SPAD_SIZE - 1)); } 4 { return ioRead32(bus, r.off); } // kind 5: EXP1 (no cart loaded) — psxe exp1.c reads return 0xFF per byte. 5 { return 0xFFFFFFFF; } @@ -378,9 +371,9 @@ pub fn busRead16(bus: Bus, addr: u32) u32 { const paddr: u32 = addr & regionMask(addr); const r: BusRoute = route(paddr); match (r.kind) { - 1 { return bufRead16(bus.ram, r.off & (RAM_SIZE - 1)); } - 2 { return bufRead16(bus.bios, r.off & (BIOS_SIZE - 1)); } - 3 { return bufRead16(bus.spad, r.off & (SPAD_SIZE - 1)); } + 1 { return bufRead16(bus.ram.ptr, r.off & (RAM_SIZE - 1)); } + 2 { return bufRead16(bus.bios.ptr, r.off & (BIOS_SIZE - 1)); } + 3 { return bufRead16(bus.spad.ptr, r.off & (SPAD_SIZE - 1)); } 4 { return ioRead16(bus, r.off); } 5 { return 0xFFFF; } _ { return 0; } @@ -391,9 +384,9 @@ pub fn busRead8(bus: Bus, addr: u32) u32 { const paddr: u32 = addr & regionMask(addr); const r: BusRoute = route(paddr); match (r.kind) { - 1 { return bufRead8(bus.ram, r.off & (RAM_SIZE - 1)); } - 2 { return bufRead8(bus.bios, r.off & (BIOS_SIZE - 1)); } - 3 { return bufRead8(bus.spad, r.off & (SPAD_SIZE - 1)); } + 1 { return bufRead8(bus.ram.ptr, r.off & (RAM_SIZE - 1)); } + 2 { return bufRead8(bus.bios.ptr, r.off & (BIOS_SIZE - 1)); } + 3 { return bufRead8(bus.spad.ptr, r.off & (SPAD_SIZE - 1)); } 4 { return ioRead8(bus, r.off); } 5 { return 0xFF; } // EXP2 DTL: ATC_STAT (off 0) reads atc_stat|8; jam never drives @@ -408,8 +401,8 @@ pub fn busWrite32(bus: Bus, cop0: *mut[] u32, addr: u32, val: u32) { const paddr: u32 = addr & regionMask(addr); const r: BusRoute = route(paddr); match (r.kind) { - 1 { bufWrite32(bus.ram, r.off & (RAM_SIZE - 1), val); } - 3 { bufWrite32(bus.spad, r.off & (SPAD_SIZE - 1), val); } + 1 { bufWrite32(bus.ram.ptr, r.off & (RAM_SIZE - 1), val); } + 3 { bufWrite32(bus.spad.ptr, r.off & (SPAD_SIZE - 1), val); } 4 { ioWrite32(bus, cop0, r.off, val); } _ {} } @@ -419,8 +412,8 @@ pub fn busWrite16(bus: Bus, cop0: *mut[] u32, addr: u32, val: u32) { const paddr: u32 = addr & regionMask(addr); const r: BusRoute = route(paddr); match (r.kind) { - 1 { bufWrite16(bus.ram, r.off & (RAM_SIZE - 1), val); } - 3 { bufWrite16(bus.spad, r.off & (SPAD_SIZE - 1), val); } + 1 { bufWrite16(bus.ram.ptr, r.off & (RAM_SIZE - 1), val); } + 3 { bufWrite16(bus.spad.ptr, r.off & (SPAD_SIZE - 1), val); } 4 { ioWrite16(bus, cop0, r.off, val); } _ {} } @@ -430,21 +423,21 @@ pub fn busWrite8(bus: Bus, cop0: *mut[] u32, addr: u32, val: u32) { const paddr: u32 = addr & regionMask(addr); const r: BusRoute = route(paddr); match (r.kind) { - 1 { bufWrite8(bus.ram, r.off & (RAM_SIZE - 1), val); } - 3 { bufWrite8(bus.spad, r.off & (SPAD_SIZE - 1), val); } + 1 { bufWrite8(bus.ram.ptr, r.off & (RAM_SIZE - 1), val); } + 3 { bufWrite8(bus.spad.ptr, r.off & (SPAD_SIZE - 1), val); } 4 { ioWrite8(bus, cop0, r.off, val); } _ {} } } // Direct buffer accessors. -pub fn busRamPtr(bus: Bus) *mut[] u8 { return bus.ram; } -pub fn busBiosPtr(bus: Bus) *mut[] u8 { return bus.bios; } -pub fn busVramPtr(bus: Bus) *mut[] u8 { return bus.vram; } +pub fn busRamPtr(bus: Bus) *mut[] u8 { return bus.ram.ptr; } +pub fn busBiosPtr(bus: Bus) *mut[] u8 { return bus.bios.ptr; } +pub fn busVramPtr(bus: Bus) *mut[] u8 { return bus.vram.ptr; } // 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.typed(); } +pub fn busDmaPtr(bus: Bus) *mut[] u32 { return bus.dma.ptr; } +pub fn busIrqPtr(bus: Bus) *mut[] u32 { return bus.irq.ptr; } // 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; } +pub fn busGtePtr(bus: Bus) *mut[] u32 { return bus.gte.ptr; } diff --git a/cpu.jam b/cpu.jam index 4c936f0..07d3ca1 100644 --- a/cpu.jam +++ b/cpu.jam @@ -29,7 +29,7 @@ const { busRead32, busRead16, busRead8, const { Gpu } = import("gpu"); const { dmaAlloc } = import("dma"); const { irqAlloc } = import("irq"); -const { Buf } = import("std/buf"); +const { Vec } = import("std/collections"); const { Bus } = import("bus"); const { Timer } = import("timer"); const { gteAlloc, gteDataRead, gteDataWrite, @@ -39,10 +39,9 @@ const { Mcd } = import("mcd"); const { Sio1 } = import("sio1"); const { Mdec } = import("mdec"); -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; pub extern fn putchar(c: i32) i32; + +const { Vec } = import("std/collections"); pub extern fn fflush(stream: u64) i32; const Disc = struct { @@ -107,22 +106,12 @@ const CAUSE_OV: u32 = 0x30; // ---------- register file ---------------------------------------------- // -// 32 GPRs + 16 COP0 registers — both live in heap-allocated `*mut[] u32` -// buffers. The buffers are cast from malloc's `*mut[] u8` return; the -// codegen now honours pointer-type-aware loads and stores so a 4-byte -// read is a single `LDR` rather than the hand-rolled little-endian decode -// the original implementation needed. - -pub fn allocRegFile(n: u32) *mut[] u32 { - const bytes: u64 = (n as u64) * @sizeOf(u32); - var raw: *mut[] u8 = malloc(bytes); - memset(raw, 0, bytes); - return raw as *mut[] u32; -} - -pub fn freeRegFile(p: *mut[] u32) { - free(p as *mut[] u8); -} +// 32 GPRs + 16 COP0 registers — both live in heap-allocated `Vec(u32)` +// buffers that auto-drop with their owning scope. Callers pass `regs.ptr` +// (typed `*mut[] u32`) into the gprRead/Write/cop0Read/Write helpers so +// the hot loop stays as a single `LDR` per access (no per-call Vec +// indirection in the helpers). +pub fn allocRegFile(n: u32) Vec(u32) { return Vec(u32).filled(0, n); } pub fn gprRead(regs: *mut[] u32, idx: u32) u32 { return regs[idx]; } pub fn gprWrite(regs: *mut[] u32, idx: u32, val: u32) { @@ -879,32 +868,32 @@ pub fn iCop2Unimpl(c: mut Cpu, opc: u32, bus: Bus, // MFC2 — unconditional pending-load commit, like psxe (cpu.c:1449). commitLoad(c, regs); c.loadD = opRt(opc); - c.loadV = gteDataRead(bus.gte, opRd(opc)); + c.loadV = gteDataRead(bus.gte.ptr, opRd(opc)); return; } if (rs == 0x02) { // CFC2 — unconditional pending-load commit, like psxe (cpu.c:1458). commitLoad(c, regs); c.loadD = opRt(opc); - c.loadV = gteCtrlRead(bus.gte, opRd(opc)); + c.loadV = gteCtrlRead(bus.gte.ptr, opRd(opc)); return; } if (rs == 0x04) { const v: u32 = gprRead(regs, opRt(opc)); commitLoad(c, regs); - gteDataWrite(bus.gte, opRd(opc), v); + gteDataWrite(bus.gte.ptr, opRd(opc), v); return; } if (rs == 0x06) { const v: u32 = gprRead(regs, opRt(opc)); commitLoad(c, regs); - gteCtrlWrite(bus.gte, opRd(opc), v); + gteCtrlWrite(bus.gte.ptr, opRd(opc), v); return; } // COP2 math op (RTPS, NCLIP, ...) — bottom 25 bits encode the // operation; gteExec dispatches on the low 6. commitLoad(c, regs); - gteExec(bus.gte, opc); + gteExec(bus.gte.ptr, opc); // GTE math ops cost more than the 2-cycle base step() already charged // (RTPS=15, NCDT=44, ...); add the remainder so device timing matches // psxe through GTE-heavy code (cpu.c psx_cpu_execute GTE switch). @@ -924,7 +913,7 @@ pub fn iCop2Lwc(c: mut Cpu, opc: u32, bus: Bus, raiseException(c, cop0, CAUSE_ADEL); return; } - gteDataWrite(bus.gte, opRt(opc), busRead32(bus, addr)); + gteDataWrite(bus.gte.ptr, opRt(opc), busRead32(bus, addr)); } pub fn iCop2Swc(c: mut Cpu, opc: u32, bus: Bus, @@ -941,7 +930,7 @@ pub fn iCop2Swc(c: mut Cpu, opc: u32, bus: Bus, // is isolated. Real R3000 routes the access to the I-cache instead // of memory; emulators just no-op the store. if ((cop0Read(cop0, C0_SR) & SR_ISC) != 0) { return; } - busWrite32(bus, cop0, addr, gteDataRead(bus.gte, opRt(opc))); + busWrite32(bus, cop0, addr, gteDataRead(bus.gte.ptr, opRt(opc))); } pub fn illegal(c: mut Cpu, opc: u32, regs: *mut[] u32, cop0: *mut[] u32) { @@ -1220,7 +1209,7 @@ pub fn step(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { // (cpu.c psx_cpu_cycle: total_cycles += last_cycles, then return). var irqCyc: u64 = fetchCyc; if ((irqOpc & 0xFE000000) == 0x4A000000) { - gteExec(bus.gte, irqOpc); + gteExec(bus.gte.ptr, irqOpc); irqCyc = irqCyc + (gteOpCycles(irqOpc) as u64); } c.cycles = c.cycles + irqCyc; diff --git a/dma.jam b/dma.jam index bde82c9..8c76489 100644 --- a/dma.jam +++ b/dma.jam @@ -8,12 +8,12 @@ // // dma.jam does NOT import bus.jam (that would form a cycle through // bus.jam ↦ dma.jam ↦ bus.jam). Instead the Bus type is redeclared -// structurally and we reach into bus.ram / bus.gpu directly, calling +// structurally and we reach into bus.ram.ptr / bus.gpu directly, calling // gpuGp0 / vram helpers from gpu.jam where necessary. const { Gpu } = import("gpu"); const { irqRaise, IC_DMA } = import("irq"); -const { Buf } = import("std/buf"); +const { Vec } = import("std/collections"); const { Bus } = import("bus"); const { Cdrom } = import("cdrom"); const { Mdec } = import("mdec"); @@ -21,9 +21,6 @@ const { spuWrite16, spuRead16 } = import("spu"); const SPU_TFIFO_OFF: u32 = 0x1A8; -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; // Disc shape — keep in sync with disc.jam / bus.jam so the structural // match holds at the call site. Members aren't accessed here; only the @@ -65,16 +62,14 @@ const DICR_FLAGS: u32 = 0x7F000000; const DICR_IRQEN: u32 = 0x00800000; const DICR_IRQSI: u32 = 0x80000000; -pub fn dmaAlloc() *mut[] u32 { - var raw: *mut[] u8 = malloc((DMA_STATE_WORDS as u64) * 4); - memset(raw, 0, (DMA_STATE_WORDS as u64) * 4); - var d: *mut[] u32 = raw as *mut[] u32; - d[21] = 0x07654321; - return d; +// Owned DMA-state buffer. d[21] = DPCR is pre-set to the psxe default +// (psxe dma.c). Auto-drops with the Bus. +pub fn dmaAlloc() Vec(u32) { + var b: Vec(u32) = Vec(u32).filled(0, DMA_STATE_WORDS); + b[21] = 0x07654321; + return b; } -pub fn dmaFree(d: *mut[] u32) { free(d as *mut[] u8); } - pub fn chBase(ch: u32) u32 { return ch * 3; } // ---------- direct RAM access (DMA never targets BIOS/scratchpad) ------- @@ -86,7 +81,7 @@ pub fn chBase(ch: u32) u32 { return ch * 3; } pub fn ramRead32(bus: Bus, addr: u32) u32 { const off: u32 = addr & 0x001FFFFC; - var p: *mut[] u8 = bus.ram; + var p: *mut[] u8 = bus.ram.ptr; return (p[off] as u32) | ((p[off + 1] as u32) << 8) | ((p[off + 2] as u32) << 16) @@ -95,7 +90,7 @@ pub fn ramRead32(bus: Bus, addr: u32) u32 { pub fn ramWrite32(bus: Bus, addr: u32, val: u32) { const off: u32 = addr & 0x001FFFFC; - var p: *mut[] u8 = bus.ram; + var p: *mut[] u8 = bus.ram.ptr; p[off] = (val & 0xFF) as u8; p[off + 1] = ((val >> 8) & 0xFF) as u8; p[off + 2] = ((val >> 16) & 0xFF) as u8; @@ -104,7 +99,7 @@ pub fn ramWrite32(bus: Bus, addr: u32, val: u32) { pub fn ramWrite16(bus: Bus, addr: u32, val: u32) { const off: u32 = addr & 0x001FFFFE; - var p: *mut[] u8 = bus.ram; + var p: *mut[] u8 = bus.ram.ptr; p[off] = (val & 0xFF) as u8; p[off + 1] = ((val >> 8) & 0xFF) as u8; } @@ -278,7 +273,7 @@ pub fn dmaDoGpuRequest(d: *mut[] u32, bus: Bus) { var i: u32 = 0; while (i < size) { const data: u32 = ramRead32(bus, addr); - bus.gpu.gp0(bus.vram, data); + bus.gpu.gp0(bus.vram.ptr, data); addr = addr + step; i = i + 1; } @@ -291,7 +286,7 @@ pub fn dmaDoGpuRequest(d: *mut[] u32, bus: Bus) { // composites, etc.). var i: u32 = 0; while (i < size) { - const data: u32 = bus.gpu.read32(bus.vram, 0); + const data: u32 = bus.gpu.read32(bus.vram.ptr, 0); ramWrite32(bus, addr, data); addr = addr + step; i = i + 1; @@ -311,7 +306,7 @@ pub fn dmaDoGpuLinked(d: *mut[] u32, bus: Bus) { while (size > 0) { addr = (addr + step) & 0x001FFFFC; const cmd: u32 = ramRead32(bus, addr); - bus.gpu.gp0(bus.vram, cmd); + bus.gpu.gp0(bus.vram.ptr, cmd); size = size - 1; } addr = hdr & 0xFFFFFF; @@ -334,7 +329,7 @@ pub fn dmaDoMdecIn(d: *mut[] u32, bus: Bus) { var i: u32 = 0; while (i < size) { const w: u32 = ramRead32(bus, addr); - bus.mdec.write32(bus.mdecIn, bus.mdecOut, 0, w); + bus.mdec.write32(bus.mdecIn.ptr, bus.mdecOut.ptr, 0, w); addr = addr + step; i = i + 1; } @@ -352,7 +347,7 @@ pub fn dmaDoMdecOut(d: *mut[] u32, bus: Bus) { var addr: u32 = d[chBase(1) + F_MADR]; var i: u32 = 0; while (i < size) { - const w: u32 = bus.mdec.read32(bus.mdecOut, 0); + const w: u32 = bus.mdec.read32(bus.mdecOut.ptr, 0); ramWrite32(bus, addr, w); addr = addr + step; i = i + 1; @@ -418,16 +413,16 @@ pub fn dmaDoSpu(d: *mut[] u32, bus: Bus) { var i: u32 = 0; while (i < words) { const data: u32 = ramRead32(bus, addr); - spuWrite16(bus.spu, bus.spuram, SPU_TFIFO_OFF, data & 0xFFFF); - spuWrite16(bus.spu, bus.spuram, SPU_TFIFO_OFF, (data >> 16) & 0xFFFF); + spuWrite16(bus.spu.ptr, bus.spuram.ptr, SPU_TFIFO_OFF, data & 0xFFFF); + spuWrite16(bus.spu.ptr, bus.spuram.ptr, SPU_TFIFO_OFF, (data >> 16) & 0xFFFF); addr = addr + step; i = i + 1; } } else { var i: u32 = 0; while (i < words) { - const lo: u32 = spuRead16(bus.spu, bus.spuram, SPU_TFIFO_OFF); - const hi: u32 = spuRead16(bus.spu, bus.spuram, SPU_TFIFO_OFF); + const lo: u32 = spuRead16(bus.spu.ptr, bus.spuram.ptr, SPU_TFIFO_OFF); + const hi: u32 = spuRead16(bus.spu.ptr, bus.spuram.ptr, SPU_TFIFO_OFF); ramWrite32(bus, addr, lo | (hi << 16)); addr = addr + step; i = i + 1; diff --git a/gpu.jam b/gpu.jam index c709853..bb5781e 100644 --- a/gpu.jam +++ b/gpu.jam @@ -34,10 +34,6 @@ const { irqRaise, IC_VBLANK } = import("irq"); -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 GPU_STATE_WORDS: u32 = 80; const VRAM_PIXELS: u32 = 524288; // 1024 * 512 @@ -53,22 +49,6 @@ pub fn gpuToBgr555(c: u32) u32 { | ((c & 0xF80000) >> 9); } -pub fn gpuAlloc() *mut[] u32 { - var raw: *mut[] u8 = malloc((GPU_STATE_WORDS as u64) * 4); - memset(raw, 0, (GPU_STATE_WORDS as u64) * 4); - var g: *mut[] u32 = raw as *mut[] u32; - g[19] = GP_RECV_CMD; - // psxe init (gpu.c:59): only sets bit 23 (display disabled). Reads - // OR 0x1C000000 on the fly. Our previous 0x14802000 was wrong — - // bit 28 is OR'd by read anyway, bit 13 is junk that leaked into - // game state and produced a GPUSTAT diff at PC=0x8005C204. - g[46] = 0x00800000; - g[47] = 1; // default display_mode - return g; -} - -pub fn gpuFree(g: *mut[] u32) { free(g as *mut[] u8); } - // End-of-run diagnostic: how many GP0 commands of each family did the // BIOS issue? Family index = top 3 bits of the command byte (0 = misc, // 1 = polygon, 2 = line, 3 = rect, ...). diff --git a/gte.jam b/gte.jam index 0b3b35e..b0fb701 100644 --- a/gte.jam +++ b/gte.jam @@ -45,9 +45,7 @@ // 29 ZSF3, 30 ZSF4 // 31 FLAG (saturation/overflow bits) -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 { Vec } = import("std/collections"); const GTE_REGS: u32 = 144; // 0..63 = data/control regs, 64..143 = debug counters const D_V0: u32 = 0; @@ -104,13 +102,8 @@ const GTE_UNR_TABLE: [257]u8 = [ 0x00, ]; -pub fn gteAlloc() *mut[] u32 { - var raw: *mut[] u8 = malloc((GTE_REGS as u64) * 4); - memset(raw, 0, (GTE_REGS as u64) * 4); - return raw as *mut[] u32; -} - -pub fn gteFree(g: *mut[] u32) { free(g as *mut[] u8); } +// Owned GTE-register buffer (zero-initialised). Auto-drops with the Bus. +pub fn gteAlloc() Vec(u32) { return Vec(u32).filled(0, GTE_REGS); } // Data and control share the same storage buffer: data at index 0..31, // control at index 32..63. diff --git a/irq.jam b/irq.jam index ae82a81..ea27ee5 100644 --- a/irq.jam +++ b/irq.jam @@ -12,7 +12,7 @@ // [0] I_STAT // [1] I_MASK -const { Buf } = import("std/buf"); +const { Vec } = import("std/collections"); // IRQ line bits (psxe ic.h: IC_VBLANK = 0x001, etc.). pub fn IC_VBLANK() u32 { return 0x001; } @@ -35,9 +35,10 @@ const C0_CAUSE: u32 = 13; // 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); } +// hang), plus slack. Filled with 0 via typed slot writes (safe; the u32 +// loop collapses to a memset at -O1+). The Buf owns the allocation — +// its drop frees on scope exit, no irqFree helper needed. +pub fn irqAlloc() Vec(u32) { return Vec(u32).filled(0, 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 6fdf4ed..c7d1109 100644 --- a/main.jam +++ b/main.jam @@ -33,7 +33,7 @@ const { Timer } = import("timer"); const { freshCpu, freshCpuAt, - allocRegFile, freeRegFile, + allocRegFile, gprRead, gprWrite, cop0Read, cop0Write, run, runHooked, @@ -49,7 +49,6 @@ 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); @@ -316,7 +315,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.typed(), cop0, delta); + bus.cdrom.update(bus.disc, bus.spu.ptr, bus.irq.ptr, 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 @@ -329,7 +328,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.typed(), cop0); + bus.timer.hblankBegin(bus.irq.ptr, cop0); if (line < SCANLINES_VDRAW) { if ((line & 1) != 0) { gpuBuf[46] = gpuBuf[46] | 0x80000000; @@ -341,8 +340,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.typed(), cop0); - irqRaise(bus.irq.typed(), cop0, IC_VBLANK()); + bus.timer.vblankBegin(bus.irq.ptr, cop0); + irqRaise(bus.irq.ptr, cop0, IC_VBLANK()); } else if (line == SCANLINES_TOTAL) { bus.timer.vblankEnd(); line = 0; @@ -356,23 +355,23 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { } // 3. PAD (psx.c:97) - padUpdate(bus.pad, bus.irq.typed(), cop0, delta); + padUpdate(bus.pad.ptr, bus.irq.ptr, cop0, delta); // 4. TIMER sysclk (psx.c:98) — updateCyc ignores delta, uses 2 (psxe). - bus.timer.updateCyc(bus.irq.typed(), cop0, delta); + bus.timer.updateCyc(bus.irq.ptr, cop0, delta); // 5. DMA (psx.c:99) - dmaTickSpu(bus.dma, delta); - dmaUpdate(bus.dma, bus.irq.typed(), cop0); + dmaTickSpu(bus.dma.ptr, delta); + dmaUpdate(bus.dma.ptr, bus.irq.ptr, 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.typed(), cop0, delta); + bus.sio1.update(bus.irq.ptr, cop0, delta); spuAcc = spuAcc + delta; while (spuAcc >= 768) { spuAcc = spuAcc - 768; - gpuBuf[34] = spuGetSample(bus.spu, bus.spuram, bus.irq.typed(), cop0); + gpuBuf[34] = spuGetSample(bus.spu.ptr, bus.spuram.ptr, bus.irq.ptr, cop0); } } // NOTE: do NOT commitLoad here — each instruction's own commitLoad @@ -458,8 +457,8 @@ fn pathEndsWithExe(p: []u8) bool { } fn main() { - var regs: *mut[] u32 = allocRegFile(32); - var cop0: *mut[] u32 = allocRegFile(16); + var regs: Vec(u32) = allocRegFile(32); + var cop0: Vec(u32) = allocRegFile(16); var bus: Bus = allocBus(); var c: Cpu = freshCpu(); @@ -468,15 +467,15 @@ fn main() { // back to the same file so saves survive across runs. Mirrors // psxe's psx_mcd_init / psx_mcd_destroy lifecycle. var mcdPath: []u8 = "./slot1.mcd"; - if (mcdRamLoad(bus.mcdRam, mcdPath)) { + if (mcdRamLoad(bus.mcdRam.ptr, mcdPath)) { var loadMsg: []u8 = "Loaded memcard: ./slot1.mcd\n"; write(STDOUT, loadMsg.ptr, loadMsg.len); } // Match psxe's initial COP0 state. The 0x10900000 value seeds COP0 // SR with CU0 enabled, BEV set, and a few breakpoint controls. - cop0Write(cop0, C0_SR, 0x10900000); - cop0Write(cop0, C0_PRID, 0x00000002); + cop0Write(cop0.ptr, C0_SR, 0x10900000); + cop0Write(cop0.ptr, C0_PRID, 0x00000002); // CLI: jamstation [GAME_PATH] // argv[1] = BIOS image — REQUIRED, no default. @@ -555,7 +554,7 @@ fn main() { } } if (fileExists(demoPath)) { - loadExe(demoPath, bus, regs, c); + loadExe(demoPath, bus, regs.ptr, c); flushAll(); } else if (fileExists(gamePath)) { pendingGameExe = true; @@ -604,10 +603,10 @@ fn main() { // cop0 — pack them into a 32-byte AudioCtx so we can pass a // single pointer through SDL_AudioSpec.userdata. 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.typed() as u64); - setU64(audioCtx, 24, cop0 as u64); + setU64(audioCtx, 0, bus.spu.ptr as u64); + setU64(audioCtx, 8, bus.spuram.ptr as u64); + setU64(audioCtx, 16, bus.irq.ptr as u64); + setU64(audioCtx, 24, cop0.ptr as u64); const cbAddr: u64 = psone_audio_cb as u64; var audioDev: u32 = sdlAudioOpen(1024, cbAddr, audioCtx as u64); if (audioDev == 0) { @@ -660,7 +659,7 @@ fn main() { // in this range when no disc is present (e.g. 0x8003D78C = // ps1-tests run path). if (pendingGameExe && c.pc >= 0x80030000 && c.pc < 0x80040000) { - loadExe(gamePath, bus, regs, c); + loadExe(gamePath, bus, regs.ptr, c); pendingGameExe = false; idleFrames = 0; flushAll(); @@ -672,7 +671,7 @@ fn main() { if (idleFrames >= IDLE_TRIGGER) { var msg: []u8 = "BIOS shell idle — sideloading game.exe fallback\n"; write(STDOUT, msg.ptr, msg.len); - loadExe(gamePath, bus, regs, c); + loadExe(gamePath, bus, regs.ptr, c); pendingGameExe = false; idleFrames = 0; flushAll(); @@ -682,7 +681,7 @@ fn main() { lastPoly = curPoly; } } - runOneFrame(c, bus, regs, cop0); + runOneFrame(c, bus, regs.ptr, cop0.ptr); // Audio: nothing to do on the main thread anymore. SDL's audio // thread is pulling samples from psone_audio_cb at 44.1 kHz. var vram: *mut[] u8 = busVramPtr(bus); @@ -710,7 +709,7 @@ fn main() { // Push the latest keyboard state into the SDA pad. Cheap (2-byte // write); doing it once per frame is enough since controller // polling runs at VBlank cadence in the BIOS. - padSetButtons(bus.pad, buttons[0]); + padSetButtons(bus.pad.ptr, buttons[0]); frames = frames + 1; // FMV-debug probe: periodic GP0 family histogram. Once every @@ -724,13 +723,11 @@ fn main() { // Persist memcard slot 1 to disk before freeing the bus. psxe does // the equivalent in psx_mcd_destroy. - mcdRamSave(bus.mcdRam, mcdPath); + mcdRamSave(bus.mcdRam.ptr, mcdPath); sdlAudioClose(audioDev); free(audioCtx); free(buttons as *mut[] u8); sdlQuit(sdl); - freeRegFile(regs); - freeRegFile(cop0); freeBus(bus); } diff --git a/mcd.jam b/mcd.jam index 72aa7cc..26818e8 100644 --- a/mcd.jam +++ b/mcd.jam @@ -21,9 +21,7 @@ // buffer is kept as a separate heap allocation (Bus.mcdRam) so callers // can load/save it from disk without owning the device state. -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 { Vec } = import("std/collections"); // File I/O via std.fs (its libc externs are the single source). const { File } = import("std/fs"); @@ -234,14 +232,14 @@ pub const Mcd = struct { } }; -pub fn mcdRamAlloc() *mut[] u8 { - var raw: *mut[] u8 = malloc(MCD_MEMORY_SIZE); - memset(raw, 0, MCD_MEMORY_SIZE); - return raw; +// 128 KB memory-card backing store, zero-filled via typed slot writes +// (Buf.filled is safe for any T; optimizer collapses the u8 loop to a +// memset at -O1+). Owned by the Bus's `mcdRam` Buf — its drop frees on +// scope exit, no mcdRamFree helper needed. +pub fn mcdRamAlloc() Vec(u8) { + return Vec(u8).filled(0, MCD_MEMORY_SIZE as u32); } -pub fn mcdRamFree(r: *mut[] u8) { free(r); } - // Read a 128 KB memory-card image from `path` into `ram`. Returns 1 on // success, 0 if the file is absent or unreadable. We use access(R_OK=4) // first because Jam v0.1.0 can't null-check fopen's return. diff --git a/mdec.jam b/mdec.jam index 371266e..6a8504e 100644 --- a/mdec.jam +++ b/mdec.jam @@ -17,11 +17,10 @@ // stay heap-allocated separately (256KB + 1MB) so we avoid per-command // malloc/free. -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; pub extern fn memcpy(dst: *mut[] u8, src: *mut[] u8, n: u64) *mut[] u8; +const { Vec } = import("std/collections"); + const MDEC_INPUT_SIZE: u32 = 0x40000; // 256 KB const MDEC_OUTPUT_SIZE: u32 = 0x100000; // 1 MB @@ -495,15 +494,9 @@ pub const Mdec = struct { // ---------- input/output buffer allocation (still heap-resident) ----- -pub fn mdecInputAlloc() *mut[] u8 { - return malloc(MDEC_INPUT_SIZE as u64); -} - -pub fn mdecOutputAlloc() *mut[] u8 { - return malloc(MDEC_OUTPUT_SIZE as u64); -} - -pub fn mdecBufFree(p: *mut[] u8) { free(p); } +// Owned MDEC IO buffers. Auto-drop with the Bus. +pub fn mdecInputAlloc() Vec(u8) { return Vec(u8).filled(0, MDEC_INPUT_SIZE); } +pub fn mdecOutputAlloc() Vec(u8) { return Vec(u8).filled(0, MDEC_OUTPUT_SIZE); } // ---------- free helpers (no state required) ------------------------- diff --git a/pad.jam b/pad.jam index 44dc6af..0e72004 100644 --- a/pad.jam +++ b/pad.jam @@ -22,11 +22,10 @@ const { irqRaise, IC_JOY } = import("irq"); const { Mcd } = import("mcd"); -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; pub extern fn fflush(stream: u64) i32; +const { Vec } = import("std/collections"); + const PAD_STATE_SIZE: u32 = 32; const P_STAT_LO: u32 = 0; // u16 @@ -76,17 +75,15 @@ const DEST_MCD: u32 = 0x81; const JOY_IRQ_DELAY: u32 = 512; -pub fn padAlloc() *mut[] u8 { - var raw: *mut[] u8 = malloc(PAD_STATE_SIZE as u64); - memset(raw, 0, PAD_STATE_SIZE as u64); +// Owned PAD-state buffer (32 bytes). Auto-drops with the Bus. +pub fn padAlloc() Vec(u8) { + var b: Vec(u8) = Vec(u8).filled(0, PAD_STATE_SIZE); // sw defaults to 0xFFFF (no buttons pressed). - raw[P_SW_LO] = 0xFF; - raw[P_SW_HI] = 0xFF; - return raw; + b[P_SW_LO] = 0xFF; + b[P_SW_HI] = 0xFF; + return b; } -pub fn padFree(p: *mut[] u8) { free(p); } - // Update the SDA digital-pad button mask (active-low: bit clear = // pressed). PSX bit layout: 0=Select, 3=Start, 4=Up, 5=Right, 6=Down, // 7=Left, 8=L2, 9=R2, 10=L1, 11=R1, 12=Triangle, 13=Circle, 14=Cross, diff --git a/spu.jam b/spu.jam index efab785..4044222 100644 --- a/spu.jam +++ b/spu.jam @@ -17,9 +17,7 @@ const { irqRaise, IC_SPU } = import("irq"); -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 { Vec } = import("std/collections"); const SPU_REG_SIZE: u32 = 0x400; const VS_SIZE: u32 = 256; @@ -202,25 +200,22 @@ pub fn adpcmNeg(f: u32) i32 { // ---------- allocation ------------------------------------------------- -pub fn spuAlloc() *mut[] u8 { - var raw: *mut[] u8 = malloc(SPU_STATE_SIZE as u64); - memset(raw, 0, SPU_STATE_SIZE as u64); - // psxe init: ENDX = 0x00FFFFFF (all voices ended), irq9addr = 0xFFFF. - setU16(raw, R_ENDXL, 0xFFFF); - setU16(raw, R_ENDXH, 0x00FF); - setU16(raw, R_IRQADDR, 0xFFFF); - spuGaussInit(raw); - return raw; +// Owned SPU-state buffer (~29KB). Initialised via setU16 + spuGaussInit +// (psxe's reset values: all voices ended, irq9addr = 0xFFFF). Auto-drops +// with the Bus. +pub fn spuAlloc() Vec(u8) { + var b: Vec(u8) = Vec(u8).filled(0, SPU_STATE_SIZE); + var p: *mut[] u8 = b.ptr; + setU16(p, R_ENDXL, 0xFFFF); + setU16(p, R_ENDXH, 0x00FF); + setU16(p, R_IRQADDR, 0xFFFF); + spuGaussInit(p); + return b; } -pub fn spuRamAlloc() *mut[] u8 { - var raw: *mut[] u8 = malloc(SPU_RAM_SIZE as u64); - memset(raw, 0, SPU_RAM_SIZE as u64); - return raw; -} - -pub fn spuFree(p: *mut[] u8) { free(p); } -pub fn spuRamFree(p: *mut[] u8) { free(p); } +// Owned SPU sample-RAM buffer (512 KB), zero-initialised. Auto-drops +// with the Bus. +pub fn spuRamAlloc() Vec(u8) { return Vec(u8).filled(0, SPU_RAM_SIZE); } // ---------- CD-audio FIFO --------------------------------------------- // diff --git a/tests.jam b/tests.jam index 4e8905a..f9d48f4 100644 --- a/tests.jam +++ b/tests.jam @@ -14,7 +14,7 @@ const { const { freshCpu, freshCpuAt, - allocRegFile, freeRegFile, + allocRegFile, gprRead, gprWrite, cop0Read, cop0Write, run, runHooked, @@ -25,7 +25,7 @@ const { // Force device modules into this module's codegen context so bus.jam's // allocBus body (which calls gpuAlloc / dmaAlloc / etc.) resolves. const { Gpu } = import("gpu"); -const { gteAlloc, gteFree, gteExec, gteDivide, +const { gteAlloc, gteExec, gteDivide, gteDataRead, gteDataWrite, gteCtrlWrite, gteCtrlRead, gteOpCycles } = import("gte"); const { Cdrom } = import("cdrom"); @@ -35,7 +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 { Vec } = import("std/collections"); const { Timer } = import("timer"); const { Mcd } = import("mcd"); const { Mdec } = import("mdec"); @@ -65,9 +65,6 @@ const Cpu = struct { savedPc: u32, hi: u32, lo: u32, - traceFp: u64, - traceInit: u32, - traceN: u64, loadD: u32, loadV: u32, branch: u8, @@ -86,14 +83,14 @@ const Cpu = struct { // allocate / free with one call each. const Harness = struct { cpu: Cpu, - regs: *mut[] u32, - cop0: *mut[] u32, + regs: Vec(u32), + cop0: Vec(u32), bus: Bus, }; fn allocHarness() Harness { - var regs: *mut[] u32 = allocRegFile(32); - var cop0: *mut[] u32 = allocRegFile(16); + var regs: Vec(u32) = allocRegFile(32); + var cop0: Vec(u32) = allocRegFile(16); var bus: Bus = allocBus(); var cpu: Cpu = freshCpuAt(0x80000000); var h: Harness = Harness { cpu: cpu, regs: regs, cop0: cop0, bus: bus }; @@ -101,101 +98,99 @@ fn allocHarness() Harness { } fn freeHarness(h: Harness) { - freeRegFile(h.regs); - freeRegFile(h.cop0); freeBus(h.bus); } -fn placeOp(bus: Bus, cop0: *mut[] u32, addr: u32, opc: u32) { - busWrite32(bus, cop0, addr, opc); +fn placeOp(bus: Bus, cop0: Vec(u32), addr: u32, opc: u32) { + busWrite32(bus, cop0.ptr, addr, opc); } fn tOriImm() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0D, 0, 8, 0x1234)); - run(h.cpu, h.bus, h.regs, h.cop0, 4); - const v: u32 = gprRead(h.regs, 8); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0D, 0, 8, 0x1234)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 4); + const v: u32 = gprRead(h.regs.ptr, 8); freeHarness(h); return v; } fn tLuiOri() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0F, 0, 9, 0xABCD)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0D, 9, 9, 0xEF01)); - run(h.cpu, h.bus, h.regs, h.cop0, 4); - const v: u32 = gprRead(h.regs, 9); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0F, 0, 9, 0xABCD)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0D, 9, 9, 0xEF01)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 4); + const v: u32 = gprRead(h.regs.ptr, 9); freeHarness(h); return v; } fn tAdduChain() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x09, 0, 8, 7)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 9, 5)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x21)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const v: u32 = gprRead(h.regs, 10); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x09, 0, 8, 7)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 9, 5)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x21)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const v: u32 = gprRead(h.regs.ptr, 10); freeHarness(h); return v; } fn tSltSigned() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x09, 0, 8, 0xFFFF)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 9, 1)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x2A)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const v: u32 = gprRead(h.regs, 10); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x09, 0, 8, 0xFFFF)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 9, 1)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x2A)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const v: u32 = gprRead(h.regs.ptr, 10); freeHarness(h); return v; } fn tSltuSigned() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x09, 0, 8, 0xFFFF)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 9, 1)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x2B)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const v: u32 = gprRead(h.regs, 10); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x09, 0, 8, 0xFFFF)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 9, 1)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x2B)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const v: u32 = gprRead(h.regs.ptr, 10); freeHarness(h); return v; } fn tShiftSra() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0F, 0, 8, 0x8000)); - placeOp(h.bus, h.cop0, 0x80000004, encR(0x00, 0, 8, 9, 2, 0x03)); - run(h.cpu, h.bus, h.regs, h.cop0, 4); - const v: u32 = gprRead(h.regs, 9); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0F, 0, 8, 0x8000)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encR(0x00, 0, 8, 9, 2, 0x03)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 4); + const v: u32 = gprRead(h.regs.ptr, 9); freeHarness(h); return v; } fn tAndOr() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0D, 0, 8, 0xF0F0)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0D, 0, 9, 0x0FF0)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x24)); - placeOp(h.bus, h.cop0, 0x8000000C, encR(0x00, 8, 9, 11, 0, 0x25)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const a: u32 = gprRead(h.regs, 10); - const o: u32 = gprRead(h.regs, 11); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0D, 0, 8, 0xF0F0)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0D, 0, 9, 0x0FF0)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 10, 0, 0x24)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encR(0x00, 8, 9, 11, 0, 0x25)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const a: u32 = gprRead(h.regs.ptr, 10); + const o: u32 = gprRead(h.regs.ptr, 11); freeHarness(h); return (a << 16) | o; } fn tBranchDelay() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0D, 0, 8, 7)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x04, 8, 8, 2)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0D, 0, 9, 1)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x0D, 0, 10, 0xFFFF)); - placeOp(h.bus, h.cop0, 0x80000010, encI(0x0D, 0, 11, 0x5555)); - run(h.cpu, h.bus, h.regs, h.cop0, 6); - const t1: u32 = gprRead(h.regs, 9); - const t2: u32 = gprRead(h.regs, 10); - const t3: u32 = gprRead(h.regs, 11); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0D, 0, 8, 7)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x04, 8, 8, 2)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0D, 0, 9, 1)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x0D, 0, 10, 0xFFFF)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encI(0x0D, 0, 11, 0x5555)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 6); + const t1: u32 = gprRead(h.regs.ptr, 9); + const t2: u32 = gprRead(h.regs.ptr, 10); + const t3: u32 = gprRead(h.regs.ptr, 11); freeHarness(h); return (t1 << 24) | (t2 << 16) | t3; } @@ -203,13 +198,13 @@ fn tBranchDelay() u32 { fn tJumpDelay() u32 { var h: Harness = allocHarness(); const tgt: u32 = (0x80000020 >> 2) & 0x03FFFFFF; - placeOp(h.bus, h.cop0, 0x80000000, encJ(0x02, tgt)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0D, 0, 8, 1)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0D, 0, 9, 0xFFFF)); - placeOp(h.bus, h.cop0, 0x80000020, encI(0x0D, 0, 9, 0xAA)); - run(h.cpu, h.bus, h.regs, h.cop0, 6); - const t0: u32 = gprRead(h.regs, 8); - const t1: u32 = gprRead(h.regs, 9); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encJ(0x02, tgt)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0D, 0, 8, 1)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0D, 0, 9, 0xFFFF)); + placeOp(h.bus, h.cop0.ptr, 0x80000020, encI(0x0D, 0, 9, 0xAA)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 6); + const t0: u32 = gprRead(h.regs.ptr, 8); + const t1: u32 = gprRead(h.regs.ptr, 9); freeHarness(h); return (t0 << 16) | t1; } @@ -217,105 +212,105 @@ fn tJumpDelay() u32 { fn tJalJr() u32 { var h: Harness = allocHarness(); const tgt: u32 = (0x80000020 >> 2) & 0x03FFFFFF; - placeOp(h.bus, h.cop0, 0x80000000, encJ(0x03, tgt)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 0, 0)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0D, 0, 11, 0x33)); - placeOp(h.bus, h.cop0, 0x80000020, encI(0x0D, 0, 8, 0x42)); - placeOp(h.bus, h.cop0, 0x80000024, encR(0x00, 31, 0, 0, 0, 0x08)); - placeOp(h.bus, h.cop0, 0x80000028, encI(0x09, 0, 0, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 12); - const t0: u32 = gprRead(h.regs, 8); - const t3: u32 = gprRead(h.regs, 11); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encJ(0x03, tgt)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 0, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0D, 0, 11, 0x33)); + placeOp(h.bus, h.cop0.ptr, 0x80000020, encI(0x0D, 0, 8, 0x42)); + placeOp(h.bus, h.cop0.ptr, 0x80000024, encR(0x00, 31, 0, 0, 0, 0x08)); + placeOp(h.bus, h.cop0.ptr, 0x80000028, encI(0x09, 0, 0, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 12); + const t0: u32 = gprRead(h.regs.ptr, 8); + const t3: u32 = gprRead(h.regs.ptr, 11); freeHarness(h); return (t0 << 16) | t3; } fn tLwSw() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0F, 0, 8, 0xDEAD)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0D, 8, 8, 0xBEEF)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0F, 0, 9, 0x8000)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x0D, 9, 9, 0x0100)); - placeOp(h.bus, h.cop0, 0x80000010, encI(0x2B, 9, 8, 0)); - placeOp(h.bus, h.cop0, 0x80000014, encI(0x23, 9, 10, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const v: u32 = gprRead(h.regs, 10); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0F, 0, 8, 0xDEAD)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0D, 8, 8, 0xBEEF)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0F, 0, 9, 0x8000)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x0D, 9, 9, 0x0100)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encI(0x2B, 9, 8, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000014, encI(0x23, 9, 10, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const v: u32 = gprRead(h.regs.ptr, 10); freeHarness(h); return v; } fn tLbSignExt() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0D, 0, 8, 0x80)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0F, 0, 9, 0x8000)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0D, 9, 9, 0x0100)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x28, 9, 8, 0)); - placeOp(h.bus, h.cop0, 0x80000010, encI(0x20, 9, 10, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const v: u32 = gprRead(h.regs, 10); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0D, 0, 8, 0x80)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0F, 0, 9, 0x8000)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0D, 9, 9, 0x0100)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x28, 9, 8, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encI(0x20, 9, 10, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const v: u32 = gprRead(h.regs.ptr, 10); freeHarness(h); return v; } fn tMultSigned() u64 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x09, 0, 8, 0xFFFF)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 9, 2)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 0, 0, 0x18)); - placeOp(h.bus, h.cop0, 0x8000000C, encR(0x00, 0, 0, 10, 0, 0x10)); - placeOp(h.bus, h.cop0, 0x80000010, encR(0x00, 0, 0, 11, 0, 0x12)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const hi: u64 = gprRead(h.regs, 10) as u64; - const lo: u64 = gprRead(h.regs, 11) as u64; + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x09, 0, 8, 0xFFFF)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 9, 2)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 0, 0, 0x18)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encR(0x00, 0, 0, 10, 0, 0x10)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encR(0x00, 0, 0, 11, 0, 0x12)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const hi: u64 = gprRead(h.regs.ptr, 10) as u64; + const lo: u64 = gprRead(h.regs.ptr, 11) as u64; freeHarness(h); return (hi << 32) | lo; } fn tDivu() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x09, 0, 8, 100)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 9, 7)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 0, 0, 0x1B)); - placeOp(h.bus, h.cop0, 0x8000000C, encR(0x00, 0, 0, 10, 0, 0x12)); - placeOp(h.bus, h.cop0, 0x80000010, encR(0x00, 0, 0, 11, 0, 0x10)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const q: u32 = gprRead(h.regs, 10); - const r: u32 = gprRead(h.regs, 11); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x09, 0, 8, 100)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 9, 7)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 0, 0, 0x1B)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encR(0x00, 0, 0, 10, 0, 0x12)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encR(0x00, 0, 0, 11, 0, 0x10)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const q: u32 = gprRead(h.regs.ptr, 10); + const r: u32 = gprRead(h.regs.ptr, 11); freeHarness(h); return (q << 16) | r; } fn tSumLoop() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x09, 0, 8, 0)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x09, 0, 9, 5)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x00, 8, 9, 8, 0, 0x21)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x09, 9, 9, 0xFFFF)); - placeOp(h.bus, h.cop0, 0x80000010, encI(0x05, 9, 0, 0xFFFD)); - placeOp(h.bus, h.cop0, 0x80000014, encI(0x09, 0, 0, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 64); - const v: u32 = gprRead(h.regs, 8); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x09, 0, 8, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x09, 0, 9, 5)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x00, 8, 9, 8, 0, 0x21)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x09, 9, 9, 0xFFFF)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encI(0x05, 9, 0, 0xFFFD)); + placeOp(h.bus, h.cop0.ptr, 0x80000014, encI(0x09, 0, 0, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 64); + const v: u32 = gprRead(h.regs.ptr, 8); freeHarness(h); return v; } fn tCop0Roundtrip() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0F, 0, 8, 0xDEAD)); - placeOp(h.bus, h.cop0, 0x80000004, encR(0x10, 4, 8, 12, 0, 0)); - placeOp(h.bus, h.cop0, 0x80000008, encR(0x10, 0, 9, 12, 0, 0)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x09, 0, 0, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 8); - const v: u32 = gprRead(h.regs, 9); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0F, 0, 8, 0xDEAD)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encR(0x10, 4, 8, 12, 0, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encR(0x10, 0, 9, 12, 0, 0)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x09, 0, 0, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 8); + const v: u32 = gprRead(h.regs.ptr, 9); freeHarness(h); return v; } fn tSyscallVector() u32 { var h: Harness = allocHarness(); - cop0Write(h.cop0, C0_SR, SR_BEV); - placeOp(h.bus, h.cop0, 0x80000000, encR(0x00, 0, 0, 0, 0, 0x0C)); - run(h.cpu, h.bus, h.regs, h.cop0, 1); + cop0Write(h.cop0.ptr, C0_SR, SR_BEV); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encR(0x00, 0, 0, 0, 0, 0x0C)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 1); const pc: u32 = h.cpu.pc; freeHarness(h); return pc; @@ -323,36 +318,36 @@ fn tSyscallVector() u32 { fn tLoadDelay() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0F, 0, 29, 0x8000)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0D, 29, 29, 0x0100)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0D, 0, 9, 42)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x2B, 29, 9, 0)); - placeOp(h.bus, h.cop0, 0x80000010, encI(0x23, 29, 8, 0)); - placeOp(h.bus, h.cop0, 0x80000014, encI(0x0D, 8, 10, 0)); - placeOp(h.bus, h.cop0, 0x80000018, encI(0x0D, 8, 11, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 16); - const t2: u32 = gprRead(h.regs, 10); - const t3: u32 = gprRead(h.regs, 11); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0F, 0, 29, 0x8000)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0D, 29, 29, 0x0100)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0D, 0, 9, 42)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x2B, 29, 9, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encI(0x23, 29, 8, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000014, encI(0x0D, 8, 10, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000018, encI(0x0D, 8, 11, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 16); + const t2: u32 = gprRead(h.regs.ptr, 10); + const t3: u32 = gprRead(h.regs.ptr, 11); freeHarness(h); return (t2 << 16) | t3; } fn tLwlLwr() u32 { var h: Harness = allocHarness(); - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0F, 0, 8, 0x1122)); - placeOp(h.bus, h.cop0, 0x80000004, encI(0x0D, 8, 8, 0x3344)); - placeOp(h.bus, h.cop0, 0x80000008, encI(0x0F, 0, 9, 0xAABB)); - placeOp(h.bus, h.cop0, 0x8000000C, encI(0x0D, 9, 9, 0xCCDD)); - placeOp(h.bus, h.cop0, 0x80000010, encI(0x0F, 0, 29, 0x8000)); - placeOp(h.bus, h.cop0, 0x80000014, encI(0x0D, 29, 29, 0x0100)); - placeOp(h.bus, h.cop0, 0x80000018, encI(0x2B, 29, 8, 0)); - placeOp(h.bus, h.cop0, 0x8000001C, encI(0x2B, 29, 9, 4)); - placeOp(h.bus, h.cop0, 0x80000020, encI(0x0D, 0, 10, 0)); - placeOp(h.bus, h.cop0, 0x80000024, encI(0x22, 29, 10, 6)); - placeOp(h.bus, h.cop0, 0x80000028, encI(0x26, 29, 10, 3)); - placeOp(h.bus, h.cop0, 0x8000002C, encI(0x09, 0, 0, 0)); - run(h.cpu, h.bus, h.regs, h.cop0, 20); - const v: u32 = gprRead(h.regs, 10); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0F, 0, 8, 0x1122)); + placeOp(h.bus, h.cop0.ptr, 0x80000004, encI(0x0D, 8, 8, 0x3344)); + placeOp(h.bus, h.cop0.ptr, 0x80000008, encI(0x0F, 0, 9, 0xAABB)); + placeOp(h.bus, h.cop0.ptr, 0x8000000C, encI(0x0D, 9, 9, 0xCCDD)); + placeOp(h.bus, h.cop0.ptr, 0x80000010, encI(0x0F, 0, 29, 0x8000)); + placeOp(h.bus, h.cop0.ptr, 0x80000014, encI(0x0D, 29, 29, 0x0100)); + placeOp(h.bus, h.cop0.ptr, 0x80000018, encI(0x2B, 29, 8, 0)); + placeOp(h.bus, h.cop0.ptr, 0x8000001C, encI(0x2B, 29, 9, 4)); + placeOp(h.bus, h.cop0.ptr, 0x80000020, encI(0x0D, 0, 10, 0)); + placeOp(h.bus, h.cop0.ptr, 0x80000024, encI(0x22, 29, 10, 6)); + placeOp(h.bus, h.cop0.ptr, 0x80000028, encI(0x26, 29, 10, 3)); + placeOp(h.bus, h.cop0.ptr, 0x8000002C, encI(0x09, 0, 0, 0)); + run(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr, 20); + const v: u32 = gprRead(h.regs.ptr, 10); freeHarness(h); return v; } @@ -394,47 +389,42 @@ fn gteSetIdentity(g: *mut[] u32) { } fn tGteRtpsMac() u32 { - var g: *mut[] u32 = gteAlloc(); - gteSetIdentity(g); - gteDataWrite(g, 0, (200 << 16) | 100); // V0 = (100, 200, z) - gteDataWrite(g, 1, 300); // V0.z = 300 - gteExec(g, 0x4A080001); // RTPS, sf=12 + var g: Vec(u32) = gteAlloc(); + gteSetIdentity(g.ptr); + gteDataWrite(g.ptr, 0, (200 << 16) | 100); // V0 = (100, 200, z) + gteDataWrite(g.ptr, 1, 300); // V0.z = 300 + gteExec(g.ptr, 0x4A080001); // RTPS, sf=12 // MAC1 = ((TR<<12) + 4096*100) >> 12 = 100 (pre-fix it was 409600). - const mac1: u32 = gteDataRead(g, 25); - gteFree(g); + const mac1: u32 = gteDataRead(g.ptr, 25); return mac1; } fn tGteRtpsIr() u32 { - var g: *mut[] u32 = gteAlloc(); - gteSetIdentity(g); - gteDataWrite(g, 0, (200 << 16) | 100); - gteDataWrite(g, 1, 300); - gteExec(g, 0x4A080001); - const ir3: u32 = gteDataRead(g, 11); // clamp(MAC3=300) = 300 - gteFree(g); + var g: Vec(u32) = gteAlloc(); + gteSetIdentity(g.ptr); + gteDataWrite(g.ptr, 0, (200 << 16) | 100); + gteDataWrite(g.ptr, 1, 300); + gteExec(g.ptr, 0x4A080001); + const ir3: u32 = gteDataRead(g.ptr, 11); // clamp(MAC3=300) = 300 return ir3; } fn tGteIrSext() u32 { - var g: *mut[] u32 = gteAlloc(); - gteDataWrite(g, 9, 0x0000FFFF); // IR1 = -1 (16-bit) - const v: u32 = gteDataRead(g, 9); // MFC2 sign-extends → -1 - gteFree(g); + var g: Vec(u32) = gteAlloc(); + gteDataWrite(g.ptr, 9, 0x0000FFFF); // IR1 = -1 (16-bit) + const v: u32 = gteDataRead(g.ptr, 9); // MFC2 sign-extends → -1 return v; } fn tGteDivide() u32 { - var g: *mut[] u32 = gteAlloc(); - const q: u32 = gteDivide(g, 0x800, 0x1000); // 2048/4096 → 0x8000 - gteFree(g); + var g: Vec(u32) = gteAlloc(); + const q: u32 = gteDivide(g.ptr, 0x800, 0x1000); // 2048/4096 → 0x8000 return q; } fn tGteDivideOvf() u32 { - var g: *mut[] u32 = gteAlloc(); - const q: u32 = gteDivide(g, 0x2000, 0x1000); // n >= 2d → saturates - gteFree(g); + var g: Vec(u32) = gteAlloc(); + const q: u32 = gteDivide(g.ptr, 0x2000, 0x1000); // n >= 2d → saturates return q; } @@ -442,28 +432,25 @@ fn tGteDivideOvf() u32 { // 0x7FFFF000 with bit 31 synthesized from the 0x7F87E000 error summary. fn tGteSqrFlag() u32 { - var g: *mut[] u32 = gteAlloc(); - gteDataWrite(g, 9, 0x7FFF); // IR1 = 32767 - gteExec(g, 0x4A080028); // SQR, sf=12 → MAC1=262143, IR1 saturates - const f: u32 = gteCtrlRead(g, 31); // bit 24 (IR1 sat) + bit 31 summary - gteFree(g); + var g: Vec(u32) = gteAlloc(); + gteDataWrite(g.ptr, 9, 0x7FFF); // IR1 = 32767 + gteExec(g.ptr, 0x4A080028); // SQR, sf=12 → MAC1=262143, IR1 saturates + const f: u32 = gteCtrlRead(g.ptr, 31); // bit 24 (IR1 sat) + bit 31 summary return f; } fn tGteDivideFlag() u32 { - var g: *mut[] u32 = gteAlloc(); - const q: u32 = gteDivide(g, 0x2000, 0x1000); // n>=2d → bit 17 (divide ovf) - const f: u32 = gteCtrlRead(g, 31); // bit 17 + bit 31 summary - gteFree(g); + var g: Vec(u32) = gteAlloc(); + const q: u32 = gteDivide(g.ptr, 0x2000, 0x1000); // n>=2d → bit 17 (divide ovf) + const f: u32 = gteCtrlRead(g.ptr, 31); // bit 17 + bit 31 summary return f; } fn tGteNoFlag() u32 { - var g: *mut[] u32 = gteAlloc(); - gteDataWrite(g, 9, 100); // IR1 = 100 - gteExec(g, 0x4A080028); // SQR sf=12 → MAC1=2, no saturation - const f: u32 = gteCtrlRead(g, 31); // FLAG must be clean - gteFree(g); + var g: Vec(u32) = gteAlloc(); + gteDataWrite(g.ptr, 9, 100); // IR1 = 100 + gteExec(g.ptr, 0x4A080028); // SQR sf=12 → MAC1=2, no saturation + const f: u32 = gteCtrlRead(g.ptr, 31); // FLAG must be clean return f; } @@ -473,13 +460,12 @@ fn tGteNoFlag() u32 { // old plain IR clamp keyed bit 22 off the unshifted 0x100000 and wrongly // set it (FLAG = 0x400000; bit 22 is excluded from the bit-31 summary). fn tGteRtpsClampIrZ() u32 { - var g: *mut[] u32 = gteAlloc(); - gteSetIdentity(g); - gteDataWrite(g, 0, 0); // V0 = (0, 0, z) - gteDataWrite(g, 1, 256); // V0.z = 256 - gteExec(g, 0x4A000001); // RTPS, sf=0 - const f: u32 = gteCtrlRead(g, 31); - gteFree(g); + var g: Vec(u32) = gteAlloc(); + gteSetIdentity(g.ptr); + gteDataWrite(g.ptr, 0, 0); // V0 = (0, 0, z) + gteDataWrite(g.ptr, 1, 256); // V0.z = 256 + gteExec(g.ptr, 0x4A000001); // RTPS, sf=0 + const f: u32 = gteCtrlRead(g.ptr, 31); return f; } @@ -488,15 +474,14 @@ fn tGteRtpsClampIrZ() u32 { // overflow) — even though the full MAC1 sum (RT12=-0x8000 pulls it back) // stays in 44-bit range, which is all the old single clamp_mac saw. fn tGteMvmvaNestedOvf() u32 { - var g: *mut[] u32 = gteAlloc(); - gteCtrlWrite(g, 5, 0x7FFFFFFF); // TRX = max - gteCtrlWrite(g, 0, 0x80007FFF); // RT11 = 0x7FFF, RT12 = -0x8000 - gteCtrlWrite(g, 1, 0); // RT13 = 0 - gteDataWrite(g, 0, 0x7FFF7FFF); // V0 = (0x7FFF, 0x7FFF) - gteDataWrite(g, 1, 0); // V0.z = 0 - gteExec(g, 0x4A080012); // MVMVA: rot, V0, TR, sf=12, lm=0 - const f: u32 = gteCtrlRead(g, 31) & 0x40000000; - gteFree(g); + var g: Vec(u32) = gteAlloc(); + gteCtrlWrite(g.ptr, 5, 0x7FFFFFFF); // TRX = max + gteCtrlWrite(g.ptr, 0, 0x80007FFF); // RT11 = 0x7FFF, RT12 = -0x8000 + gteCtrlWrite(g.ptr, 1, 0); // RT13 = 0 + gteDataWrite(g.ptr, 0, 0x7FFF7FFF); // V0 = (0x7FFF, 0x7FFF) + gteDataWrite(g.ptr, 1, 0); // V0.z = 0 + gteExec(g.ptr, 0x4A080012); // MVMVA: rot, V0, TR, sf=12, lm=0 + const f: u32 = gteCtrlRead(g.ptr, 31) & 0x40000000; return f; } @@ -545,48 +530,48 @@ const SCANLINES_TOTAL: u32 = 263; // Mirror main.jam's per-cycle peripheral tick (post-Phase-3). Tests // should run the same scheduler as production so they catch real // regressions. -fn emuRunOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { +fn emuRunOneFrame(c: mut Cpu, bus: Bus, regs: Vec(u32), cop0: Vec(u32)) { var line: u32 = 0; var lineCyc: u32 = 0; while (line < SCANLINES_TOTAL) { - biosHook(c, bus, regs); + biosHook(c, bus, regs.ptr); const before: u64 = c.cycles; - step(c, bus, regs, cop0); + step(c, bus, regs.ptr, cop0.ptr); const delta: u32 = (c.cycles - before) as u32; - 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); + bus.timer.updateCyc(bus.irq.ptr, cop0.ptr, delta); + dmaUpdate(bus.dma.ptr, bus.irq.ptr, cop0.ptr); + bus.cdrom.update(bus.disc, bus.irq.ptr, cop0.ptr, delta); + padUpdate(bus.pad.ptr, bus.irq.ptr, cop0.ptr, delta); + bus.sio1.update(bus.irq.ptr, cop0.ptr, delta); + spuUpdate(bus.spu.ptr, bus.spuram.ptr, bus.irq.ptr, cop0.ptr, delta); lineCyc = lineCyc + delta; if (lineCyc >= CYC_PER_SCANLINE) { lineCyc = lineCyc - CYC_PER_SCANLINE; - bus.timer.hblankBegin(bus.irq.typed(), cop0); + bus.timer.hblankBegin(bus.irq.ptr, cop0.ptr); bus.timer.hblankEnd(); if (line == SCANLINES_VDRAW) { - bus.timer.vblankBegin(bus.irq.typed(), cop0); - irqRaise(bus.irq.typed(), cop0, IC_VBLANK()); + bus.timer.vblankBegin(bus.irq.ptr, cop0.ptr); + irqRaise(bus.irq.ptr, cop0.ptr, IC_VBLANK()); } line = line + 1; } } - commitLoad(c, regs); + commitLoad(c, regs.ptr); bus.timer.vblankEnd(); } -fn emuRunFrames(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32, +fn emuRunFrames(c: mut Cpu, bus: Bus, regs: Vec(u32), cop0: Vec(u32), frames: u32) { var i: u32 = 0; while (i < frames) { - emuRunOneFrame(c, bus, regs, cop0); + emuRunOneFrame(c, bus, regs.ptr, cop0.ptr); i = i + 1; } } -fn emuBoot(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) i32 { - cop0Write(cop0, C0_SR, 0x10900000); - cop0Write(cop0, C0_PRID, 0x00000002); +fn emuBoot(c: mut Cpu, bus: Bus, regs: Vec(u32), cop0: Vec(u32)) i32 { + cop0Write(cop0.ptr, C0_SR, 0x10900000); + cop0Write(cop0.ptr, C0_PRID, 0x00000002); return emuLoadBios(bus); } @@ -599,18 +584,15 @@ fn emuPcInRamOrScratchpad(pc: u32) bool { } tfn emuBiosShellBoots() { - var regs: *mut[] u32 = allocRegFile(32); - var cop0: *mut[] u32 = allocRegFile(16); + var regs: Vec(u32) = allocRegFile(32); + var cop0: Vec(u32) = allocRegFile(16); var bus: Bus = allocBus(); var c: Cpu = freshCpu(); - if (emuBoot(c, bus, regs, cop0) == 0) { - freeRegFile(regs); freeRegFile(cop0); freeBus(bus); + if (emuBoot(c, bus, regs.ptr, cop0.ptr) == 0) { freeBus(bus); return; } - emuRunFrames(c, bus, regs, cop0, 60); + emuRunFrames(c, bus, regs.ptr, cop0.ptr, 60); assert(emuPcInRamOrScratchpad(c.pc), true); - freeRegFile(regs); - freeRegFile(cop0); freeBus(bus); } @@ -621,23 +603,20 @@ tfn emuBiosShellBoots() { tfn emuDiscBootsExec() { var diskPath: []u8 = "game.bin"; if (!fileExists(diskPath)) { return; } - var regs: *mut[] u32 = allocRegFile(32); - var cop0: *mut[] u32 = allocRegFile(16); + var regs: Vec(u32) = allocRegFile(32); + var cop0: Vec(u32) = allocRegFile(16); var bus: Bus = allocBus(); var c: Cpu = freshCpu(); - if (emuBoot(c, bus, regs, cop0) == 0) { - freeRegFile(regs); freeRegFile(cop0); freeBus(bus); + if (emuBoot(c, bus, regs.ptr, cop0.ptr) == 0) { freeBus(bus); return; } discOpen(bus.disc, diskPath); - emuRunFrames(c, bus, regs, cop0, 120); + emuRunFrames(c, bus, regs.ptr, cop0.ptr, 120); // Bare minimum: BIOS handed control out of ROM (0xBFC*) into RAM // or scratchpad. Stricter "PC inside game text segment" check is // a TODO — currently fails for the Harvest Moon SIO0-WaitEvent // stall where PC stays in BIOS scratchpad at 0x00001EB0. assert(emuPcInRamOrScratchpad(c.pc), true); - freeRegFile(regs); - freeRegFile(cop0); freeBus(bus); } @@ -664,8 +643,8 @@ fn tGteCycDflt() u32 { return gteOpCycles(0x4A000000); } // non-math → 2 // A plain instruction fetched from cached RAM costs 2 (0 fetch + 2 base). fn tCycRam() u32 { var h: Harness = allocHarness(); // pc = 0x80000000 (KSEG0) - placeOp(h.bus, h.cop0, 0x80000000, encI(0x0D, 0, 8, 0x1234)); // ORI - step(h.cpu, h.bus, h.regs, h.cop0); + placeOp(h.bus, h.cop0.ptr, 0x80000000, encI(0x0D, 0, 8, 0x1234)); // ORI + step(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr); const v: u32 = h.cpu.cycles as u32; freeHarness(h); return v; @@ -675,12 +654,12 @@ fn tCycRam() u32 { // base is charged before dispatch, so the cost is independent of what // the (zeroed) BIOS actually decodes to, as long as it isn't a GTE op. fn tCycBios() u32 { - var regs: *mut[] u32 = allocRegFile(32); - var cop0: *mut[] u32 = allocRegFile(16); + var regs: Vec(u32) = allocRegFile(32); + var cop0: Vec(u32) = allocRegFile(16); var bus: Bus = allocBus(); var cpu: Cpu = freshCpuAt(0xBFC00000); // KSEG1 BIOS entry var h: Harness = Harness { cpu: cpu, regs: regs, cop0: cop0, bus: bus }; - step(h.cpu, h.bus, h.regs, h.cop0); + step(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr); const v: u32 = h.cpu.cycles as u32; freeHarness(h); return v; @@ -692,9 +671,9 @@ fn tCycGteRtps() u32 { // Enable COP2 (SR.CU2, bit 30 of C0_SR=12) — dispatch raises a // coprocessor-unusable exception for COP2 ops otherwise, and the GTE // cycle cost would never be charged. - cop0Write(h.cop0, 12, 0x40000000); - placeOp(h.bus, h.cop0, 0x80000000, 0x4A000001); // RTPS (COP2 imm) - step(h.cpu, h.bus, h.regs, h.cop0); + cop0Write(h.cop0.ptr, 12, 0x40000000); + placeOp(h.bus, h.cop0.ptr, 0x80000000, 0x4A000001); // RTPS (COP2 imm) + step(h.cpu, h.bus, h.regs.ptr, h.cop0.ptr); const v: u32 = h.cpu.cycles as u32; freeHarness(h); return v;