diff --git a/bus.jam b/bus.jam index 76a4a49..161c01e 100644 --- a/bus.jam +++ b/bus.jam @@ -5,25 +5,25 @@ // buffers and the four device state buffers (gpu, dma, irq, timer). const { Gpu } = import("gpu"); const { dmaRead32, dmaRead16, dmaRead8, - dmaWrite32, dmaWrite16, dmaWrite8, dmaAlloc } = import("dma"); + dmaWrite32, dmaWrite16, dmaWrite8, createDma } = import("dma"); const { irqRead32, irqRead16, irqRead8, - irqWrite32, irqWrite16, irqWrite8, irqAlloc } = import("irq"); + irqWrite32, irqWrite16, irqWrite8, createIrq } = import("irq"); const { Vec } = import("std/collections"); const { Timer } = import("timer"); -const { gteAlloc } = import("gte"); +const { createGte } = import("gte"); const std = import("std"); const { Cdrom } = import("cdrom"); -const { Disc, discAlloc, discClose } = import("disc"); +const { Disc, createDisc, discClose } = import("disc"); const { Box } = import("std/box"); const { padRead32, padRead16, padRead8, padWrite32, padWrite16, padWrite8, - padAlloc } = import("pad"); + createPad } = import("pad"); const { spuRead32, spuRead16, spuRead8, spuWrite32, spuWrite16, spuWrite8, - spuAlloc, spuRamAlloc } = import("spu"); -const { Mdec, mdecInputAlloc, mdecOutputAlloc } = import("mdec"); + createSpu, createSpuRam } = import("spu"); +const { Mdec, createMdecInput, createMdecOutput } = import("mdec"); const { Sio1 } = import("sio1"); -const { Mcd, mcdRamAlloc } = import("mcd"); +const { Mcd, createMcdRam } = import("mcd"); pub const Bus = struct { ram: Vec(u8), @@ -147,7 +147,7 @@ pub fn route(paddr: u32) BusRoute { return r; } -pub fn allocBus() Bus { +pub fn createBus() Bus { // RAM/BIOS are zero-initialised. // // note!!! On Darwin uninitialised pages happen to read as zero (NOP), @@ -173,21 +173,21 @@ pub fn allocBus() Bus { var b: Bus = Bus { ram: ram, bios: bios, spad: spad, io: io, vram: vram, gpu: Gpu.init(), - dma: dmaAlloc(), - irq: irqAlloc(), + dma: createDma(), + irq: createIrq(), timer: Timer.init(), - gte: gteAlloc(), + gte: createGte(), cdrom: Cdrom.init(), - disc: discAlloc(), - pad: padAlloc(), - spu: spuAlloc(), - spuram: spuRamAlloc(), + disc: createDisc(), + pad: createPad(), + spu: createSpu(), + spuram: createSpuRam(), mdec: Mdec.init(), - mdecIn: mdecInputAlloc(), - mdecOut: mdecOutputAlloc(), + mdecIn: createMdecInput(), + mdecOut: createMdecOutput(), sio1: Sio1.init(), mcd: Mcd.init(), - mcdRam: mcdRamAlloc(), + mcdRam: createMcdRam(), biosLoaded: 0, }; return b; diff --git a/cpu.jam b/cpu.jam index e818e67..cee2e21 100644 --- a/cpu.jam +++ b/cpu.jam @@ -24,14 +24,14 @@ const { busRead32, busRead16, busRead8, // Even though cpu.jam doesn't call into these directly, the module // resolver only registers pub prototypes from modules in the *root* // import set. Forcing an import here brings each device module's pub -// fns into the codegen context so bus.jam's allocBus/dispatch helpers +// fns into the codegen context so bus.jam's createBus/dispatch helpers // resolve cleanly when cpu.jam is the root module. const { Gpu } = import("gpu"); -const { dmaAlloc } = import("dma"); -const { irqAlloc } = import("irq"); +const { createDma } = import("dma"); +const { createIrq } = import("irq"); const { Bus } = import("bus"); const { Timer } = import("timer"); -const { gteAlloc, gteDataRead, gteDataWrite, +const { createGte, gteDataRead, gteDataWrite, gteCtrlRead, gteCtrlWrite, gteExec, gteOpCycles } = import("gte"); const { Cdrom } = import("cdrom"); const { Mcd } = import("mcd"); @@ -96,7 +96,7 @@ const CAUSE_OV: u32 = 0x30; // (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 createRegFile(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) { diff --git a/cue.jam b/cue.jam index d65bf8b..c2c07e5 100644 --- a/cue.jam +++ b/cue.jam @@ -103,12 +103,19 @@ pub fn strEqAscii(a: *mut[] u8, b: *const[] u8, n: u32) bool { pub fn parseMsf(raw: *mut[] u8) u32 { var c: *mut[] Cue = raw as *mut[] Cue; - if (!isDigit(c[0].cur)) { return 0; } + if (!isDigit(c[0].cur)) { + return 0; + } const m: u32 = parseNumber(raw); - if (c[0].cur != 0x3A) { return 0; } + if (c[0].cur != 0x3A) { + return 0; + } c[0].cur = c[0].file.readByte() as u32 & 0xFF; const s: u32 = parseNumber(raw); - if (c[0].cur != 0x3A) { return 0; } + if (c[0].cur != 0x3A) { + return 0; + } + c[0].cur = c[0].file.readByte() as u32 & 0xFF; const f: u32 = parseNumber(raw); return f + (s * 75) + (m * 4500); @@ -131,7 +138,6 @@ pub fn skipWs(raw: *mut[] u8) { } } -// Read an ASCII keyword (alpha + '/' + digits) into a 16-byte buf. pub fn parseKeyword(raw: *mut[] u8, buf: *mut[] u8) u32 { var c: *mut[] Cue = raw as *mut[] Cue; var n: u32 = 0; @@ -145,8 +151,6 @@ pub fn parseKeyword(raw: *mut[] u8, buf: *mut[] u8) u32 { return n; } -// Match a parsed keyword against a known list of cue directives. -// Returns: // 1 = FILE, 2 = TRACK, 3 = INDEX, 4 = REM, 5 = PREGAP, 6 = POSTGAP, // 7 = FLAGS, 8 = MODE1/2048, 9 = MODE1/2352, 10 = MODE2/2336, // 11 = MODE2/2352, 12 = AUDIO, 13 = BINARY, 0 = unknown @@ -180,20 +184,6 @@ pub fn kwId(buf: *mut[] u8) u32 { return 0; } -// allocator - -// Allocate a Box(Cue) with each inner Vec sized to its MAX_/cap. The -// Vecs are filled-with-zero so the existing index-based writes by -// cueParse (`c.files.ptr[fi].sectors = ...`) land on valid slots - -// matches the old memset-after-malloc semantics. When the owning Bus -// goes out of scope, Box(Disc).drop -> @dropInPlace -> Disc.cuePtr Box -// drop -> @dropInPlace on Cue -> Cue's cfn drop closes open files -> -// Cue's field-walk drops each Vec -> libc free. -// Close every open per-cue-file handle and zero the counters so the -// same Box(Cue) can be repopulated by a fresh cueParse. The Vec backings -// stay live - only the user-visible "we have N tracks/files loaded" -// state is reset. Called from discClose AND from discOpen's parse- -// failure path so partial state never leaks into a subsequent open. pub fn cueReset(c: *mut[] Cue) { var i: u32 = 0; while (i < c[0].fileCount) { @@ -210,7 +200,7 @@ pub fn cueReset(c: *mut[] Cue) { c[0].rootLen = 0; } -pub fn cueAlloc() Box(Cue) { +pub fn createCue() Box(Cue) { return Box(Cue).init(Cue { files: Vec(CueFile).filled( CueFile { file: File { handle: 0 as *mut[] u8 }, @@ -234,10 +224,6 @@ pub fn cueAlloc() Box(Cue) { }); } -// track-range computation -// -// After parsing all FILE/TRACK/INDEX lines, walk each file's track list -// and compute startLba/endLba. pub fn cueComputeRanges(raw: *mut[] u8) { var c: *mut[] Cue = raw as *mut[] Cue; // Initial LBA is 150 (00:02:00 lead-in). @@ -245,9 +231,6 @@ pub fn cueComputeRanges(raw: *mut[] u8) { var fi: u32 = 0; while (fi < c[0].fileCount) { c[0].files.ptr[fi].startLba = lba; - // Find tracks for this file and assign ranges. - // Single-track file: track spans the whole file. - // Multi-track file: each track ends where the next begins. var firstTrack: u32 = MAX_TRACKS; var lastTrack: u32 = 0; var ti: u32 = 0; @@ -259,7 +242,7 @@ pub fn cueComputeRanges(raw: *mut[] u8) { ti = ti + 1; } if (firstTrack == MAX_TRACKS) { - // No tracks for this file - skip. + // No tracks for this file. fi = fi + 1; continue; } @@ -283,10 +266,6 @@ pub fn cueComputeRanges(raw: *mut[] u8) { if (ti == lastTrack) { c[0].tracks.ptr[ti].endLba = lba + fileSectors; } else { - // endLba is exclusive, so it equals the next track's - // startLba - contiguous, no 1-sector gap at the boundary - // (mednafen/Avocado compute track length = next.index1 - - // this.index1 with no -1). c[0].tracks.ptr[ti].endLba = (c[0].tracks.ptr[ti + 1].index1 as u32) + 150; } @@ -298,8 +277,6 @@ pub fn cueComputeRanges(raw: *mut[] u8) { } } -// lookup helpers - pub fn cueGetTrackCount(raw: *mut[] u8) u32 { var c: *mut[] Cue = raw as *mut[] Cue; return c[0].trackCount; @@ -327,8 +304,6 @@ pub fn cueGetTrackNumber(raw: *mut[] u8, lba: u32) u32 { return 1; } -// Locate the track owning `lba`. Returns trackCount on "past-end"; a -// value < trackCount with mode=AUDIO means CDDA. pub fn cueFindTrack(raw: *mut[] u8, lba: u32) u32 { var c: *mut[] Cue = raw as *mut[] Cue; var i: u32 = 0; @@ -341,10 +316,6 @@ pub fn cueFindTrack(raw: *mut[] u8, lba: u32) u32 { return c[0].trackCount; } -// Pregap sector layout: byte 0 = 0x00, bytes 1..10 = 0xFF (sync pattern), -// byte 11 = 0x00, the rest zero. Local -// duplicate of disc.fillPregapSector - keeps cue.jam acyclic (disc -// imports cue, not the other way around). fn fillPregap(buf: *mut[] u8) { var i: u32 = 0; while (i < SECTOR) { buf[i] = 0; i = i + 1; } @@ -352,7 +323,7 @@ fn fillPregap(buf: *mut[] u8) { while (i < 11) { buf[i] = 0xFF; i = i + 1; } } -// Read a 2352-byte sector at the given absolute LBA. Returns 1 on +// read a sector at the given absolute LBA. Returns 1 on // success, 0 on out-of-range or read failure. pub fn cueRead(raw: *mut[] u8, lba: u32, buf: *mut[] u8) i32 { var c: *mut[] Cue = raw as *mut[] Cue; @@ -393,11 +364,6 @@ pub fn cueQuery(raw: *mut[] u8, lba: u32) u32 { return 0; } -// parser -// -// Stream the .cue text char-by-char (fgetc model). Each keyword -// dispatches to its handler; FILE pushes onto files[], TRACK pushes -// onto tracks[] with the most recent file, INDEX records its MSF. pub fn cueParse(raw: *mut[] u8, path: []u8) i32 { var c: *mut[] Cue = raw as *mut[] Cue; match (File.open(path)) { @@ -405,19 +371,6 @@ pub fn cueParse(raw: *mut[] u8, path: []u8) i32 { None { return 1; } } c[0].cur = c[0].file.readByte() as u32 & 0xFF; - // Resolve any symlinks in the .cue path so the rootPath we extract is - // the directory of the actual file. Without this, a symlinked - // `./game.cue` whose target lives elsewhere produces rootPath="./", - // and the FILE entries in the .cue (which use basenames) fail to - // open relative to "./". - // - // access(path, 4) above already confirmed the file is readable, so - // realpath() should succeed; we still pre-fill the buffer with the - // original path as a fallback for the unlikely failure case (Jam - // can't null-check pointer returns). - // 4096 = PATH_MAX-ish, fits any realistic resolved path on - // macOS/Linux. Array literal [0; N] already zero-fills the - // buffer; no memset needed. var resolved: [4096]u8 = [0; 4096]; var pi: u32 = 0; while (path[pi] != 0 && pi < 4095) { @@ -441,8 +394,6 @@ pub fn cueParse(raw: *mut[] u8, path: []u8) i32 { c[0].rootLen = lastSlash; skipWs(raw); - // 16 bytes is the longest CUE keyword (FILE/TRACK/INDEX/REM/etc.) - // plus a little slack; parseKeyword bounds itself to this size. var kwbuf: [16]u8 = [0; 16]; while (!c[0].file.eof()) { @@ -464,7 +415,6 @@ pub fn cueParse(raw: *mut[] u8, path: []u8) i32 { } skipWs(raw); } - // kwbuf / resolved auto-drop at function-scope exit. c[0].file.close(); cueComputeRanges(raw); return 0; diff --git a/disc.jam b/disc.jam index 66b0eb9..39c97ef 100644 --- a/disc.jam +++ b/disc.jam @@ -18,7 +18,7 @@ // real discs prefix the data with 2 seconds (150 sectors) of pregap // that's not part of the image file. -const { Cue, cueAlloc, cueReset, cueParse, cueRead, cueQuery, +const { Cue, createCue, cueReset, cueParse, cueRead, cueQuery, cueGetTrackCount, cueGetTrackLba, cueGetTrackNumber } = import("cue"); const { Box } = import("std/box"); @@ -49,14 +49,14 @@ pub const Disc = struct { // (discOpen / discRead / ...) still take a raw `*mut[] Disc` so the // callsites can keep the `d[0].field` idiom; callers extract the raw // pointer with `box.ptrMut()`. -pub fn discAlloc() Box(Disc) { +pub fn createDisc() Box(Disc) { return Box(Disc).init(Disc { file: File { handle: 0 as *mut[] u8 }, loaded: 0, isCue: 0, pad1: 0, pad2: 0, - cuePtr: cueAlloc(), + cuePtr: createCue(), }); } diff --git a/dma.jam b/dma.jam index 7d9bc69..31fed77 100644 --- a/dma.jam +++ b/dma.jam @@ -50,7 +50,7 @@ const DICR_IRQSI: u32 = 0x80000000; // Owned DMA-state buffer. d[21] = DPCR is pre-set to its reset default // (0x07654321). Auto-drops with the Bus. -pub fn dmaAlloc() Vec(u32) { +pub fn createDma() Vec(u32) { var b: Vec(u32) = Vec(u32).filled(0, DMA_STATE_WORDS); b[21] = 0x07654321; return b; diff --git a/gte.jam b/gte.jam index d934f83..1bb0b87 100644 --- a/gte.jam +++ b/gte.jam @@ -103,7 +103,7 @@ const GTE_UNR_TABLE: [257]u8 = [ ]; // Owned GTE-register buffer (zero-initialised). Auto-drops with the Bus. -pub fn gteAlloc() Vec(u32) { return Vec(u32).filled(0, GTE_REGS); } +pub fn createGte() 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 9e39329..95ddfda 100644 --- a/irq.jam +++ b/irq.jam @@ -38,7 +38,7 @@ const C0_CAUSE: u32 = 13; // 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 createIrq() Vec(u32) { return Vec(u32).filled(0, 16); } // Update COP0_CAUSE.IP2 based on the current (stat & mask). Called // after every write and every irqRaise so the CPU sees the right diff --git a/main.jam b/main.jam index a779adc..80420a9 100644 --- a/main.jam +++ b/main.jam @@ -10,26 +10,26 @@ const { Bus, - allocBus, busRead32, busRead16, busRead8, + createBus, busRead32, busRead16, busRead8, busWrite32, busWrite16, busWrite8 } = import("bus"); // Force device modules into main.jam's codegen context. const { Gpu } = import("gpu"); -const { gteAlloc } = import("gte"); +const { createGte } = import("gte"); const { Cdrom } = import("cdrom"); -const { Disc, discAlloc, discOpen } = import("disc"); -const { dmaAlloc, dmaTickSpu, dmaUpdate } = import("dma"); -const { padAlloc, padUpdate, padSetButtons } = import("pad"); -const { spuAlloc, spuRamAlloc, spuGetSample } = import("spu"); +const { Disc, createDisc, discOpen } = import("disc"); +const { createDma, dmaTickSpu, dmaUpdate } = import("dma"); +const { createPad, padUpdate, padSetButtons } = import("pad"); +const { createSpu, createSpuRam, spuGetSample } = import("spu"); const { Sio1 } = import("sio1"); const { Mdec } = import("mdec"); -const { irqAlloc, irqRaise, IC_VBLANK } = import("irq"); +const { createIrq, irqRaise, IC_VBLANK } = import("irq"); const { Mcd, mcdRamLoad, mcdRamSave } = import("mcd"); const { Timer } = import("timer"); const { freshCpu, freshCpuAt, - allocRegFile, + createRegFile, gprRead, gprWrite, cop0Read, cop0Write, run, @@ -352,12 +352,12 @@ fn pathEndsWithExe(p: []u8) bool { } fn main() { - var regs: Vec(u32) = allocRegFile(32); + var regs: Vec(u32) = createRegFile(32); // 32 COP0 slots (not 16): MFC0/MTC0 take a 5-bit rd (0..31), so a // reserved index like $16+ must land in a valid slot rather than read/ // write past the end. Matches PCSX-Redux's 32-entry COP0 array. - var cop0: Vec(u32) = allocRegFile(32); - var bus: Bus = allocBus(); + var cop0: Vec(u32) = createRegFile(32); + var bus: Bus = createBus(); var c: Cpu = freshCpu(); // Memory card slot 1 - pull saved data from ./slot1.mcd if it diff --git a/mcd.jam b/mcd.jam index 7aa6b94..34eb745 100644 --- a/mcd.jam +++ b/mcd.jam @@ -233,7 +233,7 @@ pub const Mcd = struct { // (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) { +pub fn createMcdRam() Vec(u8) { return Vec(u8).filled(0, MCD_MEMORY_SIZE as u32); } diff --git a/mdec.jam b/mdec.jam index 09c55f5..086a19d 100644 --- a/mdec.jam +++ b/mdec.jam @@ -524,8 +524,8 @@ pub const Mdec = struct { // input/output buffer allocation (still heap-resident) // 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); } +pub fn createMdecInput() Vec(u8) { return Vec(u8).filled(0, MDEC_INPUT_SIZE); } +pub fn createMdecOutput() Vec(u8) { return Vec(u8).filled(0, MDEC_OUTPUT_SIZE); } // free helpers (no state required) diff --git a/pad.jam b/pad.jam index 26b3843..c342cae 100644 --- a/pad.jam +++ b/pad.jam @@ -74,7 +74,7 @@ const DEST_MCD: u32 = 0x81; const JOY_IRQ_DELAY: u32 = 512; // Owned PAD-state buffer (32 bytes). Auto-drops with the Bus. -pub fn padAlloc() Vec(u8) { +pub fn createPad() Vec(u8) { var b: Vec(u8) = Vec(u8).filled(0, PAD_STATE_SIZE); // sw defaults to 0xFFFF (no buttons pressed). b[P_SW_LO] = 0xFF; diff --git a/spu.jam b/spu.jam index 6d3f47a..d3e25d0 100644 --- a/spu.jam +++ b/spu.jam @@ -1,6 +1,6 @@ // SPU - Sound Processing Unit. // -// Layout of the byte buffer returned by spuAlloc: +// Layout of the byte buffer returned by createSpu: // 0x000..0x3FF : register file (the voice[24] register slots + globals) // 0x400.. : 24 × VS_SIZE per-voice runtime data (playing, counter, // current_addr, ADSR state, decoded buf[28], etc.) @@ -32,7 +32,7 @@ const CD_FIFO_CAP: u32 = 1024; // entries (power of two) const CD_FIFO_MASK: u32 = 1023; // Gaussian-interpolation coefficient table - 512 i16 entries = // 1024 bytes. The standard PSX Gaussian table. Populated by -// spuGaussInit at spuAlloc time so we don't need a runtime fopen. +// spuGaussInit at createSpu time so we don't need a runtime fopen. const SPU_GAUSS_OFF: u32 = 0x6D00; const SPU_STATE_SIZE: u32 = 0x6D00 + 1024; // 0x7100 const SPU_RAM_SIZE: u32 = 0x80000; @@ -244,7 +244,7 @@ fn spuStepNoise(s: *mut[] u8, spucnt: u32) { // Owned SPU-state buffer (~29KB). Initialised via setU16 + spuGaussInit // (reset values: all voices ended, irq9addr = 0xFFFF). Auto-drops // with the Bus. -pub fn spuAlloc() Vec(u8) { +pub fn createSpu() Vec(u8) { var b: Vec(u8) = Vec(u8).filled(0, SPU_STATE_SIZE); var p: *mut[] u8 = b.ptr; setU16(p, R_ENDXL, 0xFFFF); @@ -256,7 +256,7 @@ pub fn spuAlloc() Vec(u8) { // 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); } +pub fn createSpuRam() Vec(u8) { return Vec(u8).filled(0, SPU_RAM_SIZE); } // CD-audio FIFO // @@ -1063,7 +1063,7 @@ pub fn spuUpdate(s: *mut[] u8, ram: *mut[] u8, // Replaces the previous nearest-neighbor sample lookup with the 4-tap // Gaussian filter PSX hardware uses. Lookup table lives at offset // SPU_GAUSS_OFF inside the SPU state buffer, populated once at -// spuAlloc via spuGaussInit. Per-sample math: +// createSpu via spuGaussInit. Per-sample math: // // idx = (counter >> 4) & 0xff // out = (table[0x0FF - idx] * s3 diff --git a/tests.jam b/tests.jam index 4cbf41e..4b7ae5e 100644 --- a/tests.jam +++ b/tests.jam @@ -8,13 +8,13 @@ const { assert } = import("test"); const { Bus, - allocBus, + createBus, busWrite32 } = import("bus"); const { freshCpu, freshCpuAt, - allocRegFile, + createRegFile, gprRead, gprWrite, cop0Read, cop0Write, run, @@ -23,18 +23,18 @@ const { } = import("cpu"); // Force device modules into this module's codegen context so bus.jam's -// allocBus body (which calls gpuAlloc / dmaAlloc / etc.) resolves. +// createBus body (which calls gpuAlloc / createDma / etc.) resolves. const { Gpu } = import("gpu"); -const { gteAlloc, gteExec, gteDivide, +const { createGte, gteExec, gteDivide, gteDataRead, gteDataWrite, gteCtrlWrite, gteCtrlRead, gteOpCycles } = import("gte"); const { Cdrom } = import("cdrom"); -const { Disc, discAlloc, discOpen } = import("disc"); -const { dmaAlloc, dmaUpdate } = import("dma"); -const { padAlloc, padUpdate } = import("pad"); -const { spuAlloc, spuRamAlloc, spuUpdate } = import("spu"); +const { Disc, createDisc, discOpen } = import("disc"); +const { createDma, dmaUpdate } = import("dma"); +const { createPad, padUpdate } = import("pad"); +const { createSpu, createSpuRam, spuUpdate } = import("spu"); const { Sio1 } = import("sio1"); -const { irqAlloc, irqRaise, IC_VBLANK } = import("irq"); +const { createIrq, irqRaise, IC_VBLANK } = import("irq"); const { Vec } = import("std/collections"); const { Timer } = import("timer"); const { Mcd } = import("mcd"); @@ -75,9 +75,9 @@ const Harness = struct { }; fn createHarness() Harness { - var regs: Vec(u32) = allocRegFile(32); - var cop0: Vec(u32) = allocRegFile(32); - var bus: Bus = allocBus(); + var regs: Vec(u32) = createRegFile(32); + var cop0: Vec(u32) = createRegFile(32); + var bus: Bus = createBus(); var cpu: Cpu = freshCpuAt(0x80000000); var h: Harness = Harness { cpu: cpu, regs: regs, cop0: cop0, bus: bus }; return h; @@ -337,7 +337,7 @@ tfn psxLwlLwr() { assert(tLwlLwr(), 0xBBCCDD11); } // GTE (COP2) unit tests // -// gteAlloc gives a standalone register buffer, so the math ops can be +// createGte gives a standalone register buffer, so the math ops can be // exercised without a full Bus. These lock in the GTE fixes: // RTPS stores MAC >> sf (not the raw 44-bit sum), MFC2 sign-extends the // 16-bit registers, and the perspective divide uses the UNR LUT. @@ -352,7 +352,7 @@ fn gteSetIdentity(g: *mut[] u32) { } fn tGteRtpsMac() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteSetIdentity(g.ptr); gteDataWrite(g.ptr, 0, (200 << 16) | 100); // V0 = (100, 200, z) gteDataWrite(g.ptr, 1, 300); // V0.z = 300 @@ -363,7 +363,7 @@ fn tGteRtpsMac() u32 { } fn tGteRtpsIr() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteSetIdentity(g.ptr); gteDataWrite(g.ptr, 0, (200 << 16) | 100); gteDataWrite(g.ptr, 1, 300); @@ -373,20 +373,20 @@ fn tGteRtpsIr() u32 { } fn tGteIrSext() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); 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: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); const q: u32 = gteDivide(g.ptr, 0x800, 0x1000); // 2048/4096 -> 0x8000 return q; } fn tGteDivideOvf() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); const q: u32 = gteDivide(g.ptr, 0x2000, 0x1000); // n >= 2d -> saturates return q; } @@ -395,7 +395,7 @@ fn tGteDivideOvf() u32 { // 0x7FFFF000 with bit 31 synthesized from the 0x7F87E000 error summary. fn tGteSqrFlag() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); 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 @@ -403,14 +403,14 @@ fn tGteSqrFlag() u32 { } fn tGteDivideFlag() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); 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: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); 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 @@ -423,7 +423,7 @@ 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: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteSetIdentity(g.ptr); gteDataWrite(g.ptr, 0, 0); // V0 = (0, 0, z) gteDataWrite(g.ptr, 1, 256); // V0.z = 256 @@ -437,7 +437,7 @@ 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: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteCtrlWrite(g.ptr, 5, 0x7FFFFFFF); // TRX = max gteCtrlWrite(g.ptr, 0, 0x80007FFF); // RT11 = 0x7FFF, RT12 = -0x8000 gteCtrlWrite(g.ptr, 1, 0); // RT13 = 0 @@ -454,7 +454,7 @@ fn tGteMvmvaNestedOvf() u32 { // gte.cpp:343). The old `(g[9] as i32)` bit-cast read IR1=-1 as +65535 -> 0x1F; // with all three IR = -1 the packed result must be 0, not 0x7FFF. fn tGteIrgbNeg() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteDataWrite(g.ptr, 9, 0x0000FFFF); // IR1 = -1 gteDataWrite(g.ptr, 10, 0x0000FFFF); // IR2 = -1 gteDataWrite(g.ptr, 11, 0x0000FFFF); // IR3 = -1 @@ -464,7 +464,7 @@ fn tGteIrgbNeg() u32 { // Positive packing still works after the sign-extension fix: IR1=3968->31, // IR2=128->1, IR3=0 ⇒ 0x1F | (1<<5) | 0 = 0x3F. fn tGteIrgbPos() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteDataWrite(g.ptr, 9, 0x0F80); // IR1 = 3968 -> >>7 = 31 gteDataWrite(g.ptr, 10, 0x0080); // IR2 = 128 -> >>7 = 1 gteDataWrite(g.ptr, 11, 0x0000); // IR3 = 0 @@ -479,7 +479,7 @@ fn tGteIrgbPos() u32 { // cleanly isolates whether intermediate vertices wrongly ran the DQ. With the // fix only V2's DQ runs -> bit 12 clear. (Pre-fix: V0/V1 saturate it -> 0x1000.) fn tGteRtptDqGate() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteSetIdentity(g.ptr); gteCtrlWrite(g.ptr, 26, 2); // H = 2 gteCtrlWrite(g.ptr, 27, 0x1000); // DQA = 4096 @@ -497,7 +497,7 @@ fn tGteRtptDqGate() u32 { // MAC1 = IR0*ir1f hugely overflows 255 on R alone; bits 19/20/21 masked must // read 0x200000 (R). The old inline clampU8 set none of these (returned 0). fn tGteNcdsRgbSat() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteCtrlWrite(g.ptr, 21, 0x00010000); // RFC large -> ir1f saturates high gteDataWrite(g.ptr, 8, 0x7FFF); // IR0 = max gteExec(g.ptr, 0x4A000013); // NCDS, sf=0 @@ -510,7 +510,7 @@ fn tGteNcdsRgbSat() u32 { // IR0 = clamp(0x10000>>12) = 16. The old s32 read used 65537, giving a huge // MAC0 that saturated IR0 to 0x1000. H=SZ3=4096 ⇒ div = 0x10000. fn tGteDqaWidth() u32 { - var g: Vec(u32) = gteAlloc(); + var g: Vec(u32) = createGte(); gteSetIdentity(g.ptr); gteCtrlWrite(g.ptr, 26, 0x1000); // H = 4096 gteCtrlWrite(g.ptr, 27, 0x00010001); // DQA: low16=1 (s16), high bits set @@ -626,9 +626,9 @@ fn emuPcInRamOrScratchpad(pc: u32) bool { } tfn emuBiosShellBoots() { - var regs: Vec(u32) = allocRegFile(32); - var cop0: Vec(u32) = allocRegFile(32); - var bus: Bus = allocBus(); + var regs: Vec(u32) = createRegFile(32); + var cop0: Vec(u32) = createRegFile(32); + var bus: Bus = createBus(); var c: Cpu = freshCpu(); if (emuBoot(c, bus, regs.ptr, cop0.ptr) == 0) { return; @@ -644,9 +644,9 @@ tfn emuBiosShellBoots() { tfn emuDiscBootsExec() { var diskPath: []u8 = "game.bin"; if (!fileExists(diskPath)) { return; } - var regs: Vec(u32) = allocRegFile(32); - var cop0: Vec(u32) = allocRegFile(32); - var bus: Bus = allocBus(); + var regs: Vec(u32) = createRegFile(32); + var cop0: Vec(u32) = createRegFile(32); + var bus: Bus = createBus(); var c: Cpu = freshCpu(); if (emuBoot(c, bus, regs.ptr, cop0.ptr) == 0) { return; @@ -693,9 +693,9 @@ 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: Vec(u32) = allocRegFile(32); - var cop0: Vec(u32) = allocRegFile(32); - var bus: Bus = allocBus(); + var regs: Vec(u32) = createRegFile(32); + var cop0: Vec(u32) = createRegFile(32); + var bus: Bus = createBus(); 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.ptr, h.cop0.ptr);