diff --git a/bus.jam b/bus.jam index ff6ed7d..3c37c69 100644 --- a/bus.jam +++ b/bus.jam @@ -43,7 +43,6 @@ 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; -pub extern fn printf(format: *const[] u8, ...) i32; // TEMP memwrite-trace // The Bus struct holds every piece of state we route through the // memory bus: raw buffers (RAM, BIOS, scratchpad, flat-IO fallback, @@ -407,12 +406,7 @@ 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 { - if ((r.off & (RAM_SIZE - 1)) == 0x50f2c) { - var f: []u8 = "[MW32] 50f2c <= %08x\n"; printf(f.ptr, val); - } - bufWrite32(bus.ram, r.off & (RAM_SIZE - 1), val); - } + 1 { bufWrite32(bus.ram, r.off & (RAM_SIZE - 1), val); } 3 { bufWrite32(bus.spad, r.off & (SPAD_SIZE - 1), val); } 4 { ioWrite32(bus, cop0, r.off, val); } _ {} @@ -423,12 +417,7 @@ 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 { - if ((r.off & (RAM_SIZE - 1)) == 0x50f2c) { - var f: []u8 = "[MW16] 50f2c <= %04x\n"; printf(f.ptr, val & 0xFFFF); - } - bufWrite16(bus.ram, r.off & (RAM_SIZE - 1), val); - } + 1 { bufWrite16(bus.ram, r.off & (RAM_SIZE - 1), val); } 3 { bufWrite16(bus.spad, r.off & (SPAD_SIZE - 1), val); } 4 { ioWrite16(bus, cop0, r.off, val); } _ {} diff --git a/cdrom.jam b/cdrom.jam index b218ef0..32ba440 100644 --- a/cdrom.jam +++ b/cdrom.jam @@ -58,6 +58,7 @@ const CD_STATE_READ: u32 = 3; const CD_STATE_PLAY: u32 = 4; // psxe's CD_DELAY_FR / CD_DELAY_INIT_FR / CD_DELAY_READ_* (CPU cycles). +const CD_DELAY_1MS: u32 = 33869; // psxe CD_DELAY_1MS const CD_DELAY_FR: u32 = 50401; const CD_DELAY_INIT_FR: u32 = 81102; const CD_DELAY_READ_SS: u32 = 451584; // 33_868_800 / 75 @@ -493,8 +494,11 @@ pub const Cdrom = struct { self.pushResp(self.getStat()); self.state = CD_STATE_TX_RESP2 as u8; // seek_precision is set in resp2 on success, like psxe — not - // here in resp1. - self.delay = CD_DELAY_FR; + // here in resp1. psxe cdrom_cmd_seekl sets the 2nd-response delay + // to CD_DELAY_1MS (NOT FR) — the FR=50401 vs 1MS=33869 gap (16532 + // cyc) made jam's SeekL INT2 fire ~8266 instrs late, the root of + // the FMV CD-INT divergence at ~102.7M. + self.delay = CD_DELAY_1MS; return; } if (cmd == CDL_GETID) { @@ -639,9 +643,10 @@ pub const Cdrom = struct { self.ifr = 3; self.pushResp(self.getStat()); self.state = CD_STATE_TX_RESP2 as u8; - self.delay = CD_DELAY_FR; + self.delay = CD_DELAY_1MS; // psxe cdrom_cmd_seekp: 1MS, not FR return; } + if (cmd == CDL_SETSESSION) { self.ifr = 3; self.pushResp(self.getStat()); @@ -899,7 +904,7 @@ pub const Cdrom = struct { self.ifr = 1; self.pushResp(self.getStat()); - self.lba = lba + 1; + self.pendingLba = lba + 1; // psxe cdrom_handle_read: deferred via pending_lba, lba promoted on next process_setloc (GetLocL/P returns current sector, not next) self.delay = self.readDelay(); } @@ -969,6 +974,12 @@ pub const Cdrom = struct { pub fn dbgFifo(self: mut Self) u32 { return self.dataRidx | (self.dataWidx << 12) | ((self.dataReq as u32) << 28); } + // TEMP CD-state trace: the delay countdown, and pending cmd | prevState. + pub fn dbgDelay(self: mut Self) u32 { return self.delay; } + pub fn dbgPend(self: mut Self) u32 { + return (self.pendingCmd as u32) | ((self.prevState as u32) << 8) + | ((self.busy as u32) << 16); + } // Read one byte from the data FIFO (the 0x1F801802 register). pub fn readData(self: mut Self) u32 { diff --git a/cpu.jam b/cpu.jam index 947431d..feb773c 100644 --- a/cpu.jam +++ b/cpu.jam @@ -50,9 +50,9 @@ pub extern fn fprintf(stream: u64, fmt: *const[] u8, ...) i32; // emit one line every TRACE_STRIDE instructions, starting at TRACE_START. // STRIDE=10000 downsamples the whole run to localize the diverging frame; // then STRIDE=1 + START= detail-traces it. -const TRACE_START: u64 = 50325000; +const TRACE_START: u64 = 154843000; const TRACE_STRIDE: u64 = 1; -const TRACE_END: u64 = 50335000; +const TRACE_END: u64 = 154850000; const Disc = struct { file: *mut[] u8, @@ -576,6 +576,23 @@ pub fn iSw(c: mut Cpu, opc: u32, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { raiseException(c, cop0, CAUSE_ADES); return; } + // TEMP FMV probe: PC that writes the MDEC command port (0x1F801820). + // psxe issues all MDEC cmds (tables + 88 decodes) from pc=0x800d2ecc; + // see if jam reaches that PC for the decode or only the tables. + if ((addr & 0x1FFFFFFF) == 0x1F801820 && (t & 0x80000000) == 0) { + var pf: []u8 = "[MDECstore] pc=%08x op=%u val=%08x ra=%08x\n"; + printf(pf.ptr, c.savedPc, (t >> 29) & 7, t, gprRead(regs, 31)); + // Dump the function body around the store so we can read the + // decode-loop gate (disassemble manually). Same code in psxe. + var df: []u8 = "[DIS] %08x: %08x\n"; + var a: u32 = 0x800d2bc0; // decode-loop region around the 0x800d2c70 F-call + var k: u32 = 0; + while (k < 110) { + printf(df.ptr, a, busRead32(bus, a)); + a = a + 4; + k = k + 1; + } + } busWrite32(bus, cop0, addr, t); } @@ -1259,7 +1276,7 @@ pub fn step(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { // Leading %llu = cumulative cycle count BEFORE this instruction // (jam c.cycles ↔ psxe total_cycles). Lets `cmp` of field 1 // pinpoint where the cycle models first disagree. - var tfmt: []u8 = "%llu %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n"; + var tfmt: []u8 = "%llu %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n"; fprintf(c.traceFp, tfmt.ptr, c.cycles, c.savedPc, topc, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7], regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15], @@ -1267,7 +1284,8 @@ pub fn step(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { regs[24], regs[25], regs[26], regs[27], regs[28], regs[29], regs[30], regs[31], c.hi, c.lo, bus.timer.dbgTimerCounter(0), bus.timer.dbgTimerCounter(1), - bus.timer.dbgTimerCounter(2)); + bus.timer.dbgTimerCounter(2), bus.irq[0], bus.irq[1], + bus.cdrom.dbgDelay(), bus.cdrom.dbgInfo(), bus.cdrom.dbgLba()); } if ((ic & 0x3FFFFF) == 0) { fflush(c.traceFp); } } @@ -1306,6 +1324,10 @@ pub fn step(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { } const opc: u32 = busRead32(bus, c.pc); + if (opc == 0x0c034b99) { // TEMP: jal 0x800d2e64 (MDEC-cmd helper F) + var jf: []u8 = "[jalF] from=%08x ra=%08x\n"; + printf(jf.ptr, c.savedPc, gprRead(regs, 31)); + } c.pc = c.nextPc; c.nextPc = c.nextPc + 4; diff --git a/dma.jam b/dma.jam index 1f37df5..0000623 100644 --- a/dma.jam +++ b/dma.jam @@ -22,7 +22,6 @@ 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; -pub extern fn printf(format: *const[] u8, ...) i32; // TEMP FMV-debug // 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 @@ -246,7 +245,13 @@ pub fn dmaTrigger(d: *mut[] u32, bus: Bus, cop0: *mut[] u32, ch: u32) { 6 { dmaDoOtc(d, bus); } _ {} } - dmaUpdate(d, bus.irq, cop0); + // No store-time dmaUpdate: psxe's psx_dma_write32 only runs the transfer + // and defers all IRQ folding to the per-instruction psx_dma_update. The + // old store-time call here was a workaround for the obsolete per-scanline + // update granularity; with per-instruction dmaUpdate (main.jam) it's + // redundant and caused DMA IRQs to fire re-entrantly mid-store, ahead of + // psxe. The next instruction's dmaUpdate folds the done-flag in the same + // iteration psxe would (DICR polls one instruction later still see it). } pub fn chcrBusy(d: *mut[] u32, ch: u32) bool { @@ -348,7 +353,6 @@ pub fn dmaDoMdecIn(d: *mut[] u32, bus: Bus) { const size: u32 = bcrSize(d, 0) * bcrBlocks(d, 0); const step: u32 = chcrStep(d, 0); var addr: u32 = d[chBase(0) + F_MADR]; - const startAddr0: u32 = addr; var i: u32 = 0; while (i < size) { const w: u32 = ramRead32(bus, addr); @@ -360,11 +364,6 @@ pub fn dmaDoMdecIn(d: *mut[] u32, bus: Bus) { d[chBase(0) + F_CHCR] = 0; d[chBase(0) + F_BCR] = 0; d[23] = size; - d[30] = d[30] + 1; - if (d[30] <= 20 || (d[30] % 500) == 0) { - var pf: []u8 = "[MDECin] #%u size=%u madr=%08x mdecStat=%08x\n"; - printf(pf.ptr, d[30], size, startAddr0, bus.mdec.read32(bus.mdecOut, 4)); - } } // DMA1: MDEC output port → RAM. Match psxe exactly (dma.c:208-235). @@ -384,11 +383,6 @@ pub fn dmaDoMdecOut(d: *mut[] u32, bus: Bus) { d[chBase(1) + F_CHCR] = 0; d[chBase(1) + F_BCR] = 0; d[24] = size; - d[31] = d[31] + 1; - if (d[31] <= 20 || (d[31] % 500) == 0) { - var pf: []u8 = "[MDECout] #%u size=%u madr=%08x\n"; - printf(pf.ptr, d[31], size, d[chBase(1) + F_MADR]); - } } // CD-ROM → RAM transfer. Each "word" in BCR.size is 4 bytes from the @@ -401,11 +395,7 @@ pub fn dmaDoCdrom(d: *mut[] u32, bus: Bus) { var size: u32 = bcrSize(d, 3); if (size == 0) { size = 0x10000; } var addr: u32 = d[chBase(3) + F_MADR]; - const startAddr: u32 = addr; const step: u32 = chcrStep(d, 3); - var firstW: u32 = 0; - var nonZero: u32 = 0; - const fifo0: u32 = bus.cdrom.dbgFifo(); // FIFO state BEFORE the transfer var i: u32 = 0; while (i < size) { const b0: u32 = bus.cdrom.readData(); @@ -413,16 +403,10 @@ pub fn dmaDoCdrom(d: *mut[] u32, bus: Bus) { const b2: u32 = bus.cdrom.readData(); const b3: u32 = bus.cdrom.readData(); const w: u32 = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); - if (i == 0) { firstW = w; } - if (w != 0) { nonZero = nonZero + 1; } ramWrite32(bus, addr, w); addr = addr + step; i = i + 1; } - const cdi: u32 = bus.cdrom.dbgInfo(); - var df: []u8 = "[CDdma] size=%u firstW=%08x nonZero=%u lba=%u req=%u ridx=%u widx=%u\n"; - printf(df.ptr, size, firstW, nonZero, bus.cdrom.dbgLba(), - (fifo0 >> 28) & 1, fifo0 & 0xFFF, (fifo0 >> 12) & 0xFFF); d[chBase(3) + F_MADR] = addr; d[chBase(3) + F_CHCR] = 0; d[chBase(3) + F_BCR] = 0; @@ -526,9 +510,26 @@ pub fn dmaTickSpu(d: *mut[] u32, delta: u32) { } } +// Called once per CPU instruction (main.jam), exactly like psxe's +// psx_dma_update (psx.c:99). MDEC-in/out (ch0/ch1) are the only channels +// psxe genuinely DELAYS: it decrements mdec_in/out_irq_delay by 1 per call +// and raises the completion flag only when it reaches 0 — so the MDEC +// DMA-done IRQ fires `size` instructions AFTER the transfer (dma.c:525-539). +// d[23]/d[24] hold that remaining-instruction COUNT (set to `size` by the +// transfer). All other channels fire on the next update (psxe zeroes their +// delay before the !delay test = immediate). Brave Fencer's FMV chains the +// next MDEC frame-decode off this IRQ; jam's old immediate fire landed it +// before the handler was ready, so the decode pipeline never re-fed the +// MDEC → black video. pub fn dmaUpdate(d: *mut[] u32, ic: *mut[] u32, cop0: *mut[] u32) { - if (d[23] != 0) { d[23] = 0; if ((d[22] & 0x00010000) != 0) { d[22] = d[22] | 0x01000000; } } - if (d[24] != 0) { d[24] = 0; if ((d[22] & 0x00020000) != 0) { d[22] = d[22] | 0x02000000; } } + if (d[23] != 0) { + d[23] = d[23] - 1; + if (d[23] == 0 && (d[22] & 0x00010000) != 0) { d[22] = d[22] | 0x01000000; } + } + if (d[24] != 0) { + d[24] = d[24] - 1; + if (d[24] == 0 && (d[22] & 0x00020000) != 0) { d[22] = d[22] | 0x02000000; } + } if (d[25] != 0) { d[25] = 0; if ((d[22] & 0x00040000) != 0) { d[22] = d[22] | 0x04000000; } } if (d[26] != 0) { d[26] = 0; if ((d[22] & 0x00080000) != 0) { d[22] = d[22] | 0x08000000; } } if (d[27] != 0) { d[27] = 0; if ((d[22] & 0x00100000) != 0) { d[22] = d[22] | 0x10000000; } } diff --git a/main.jam b/main.jam index e833543..b004e6d 100644 --- a/main.jam +++ b/main.jam @@ -337,33 +337,19 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { const before: u64 = c.cycles; step(c, bus, regs, cop0); const delta: u32 = (c.cycles - before) as u32; - bus.timer.updateCyc(bus.irq, cop0, delta); - dmaTickSpu(bus.dma, delta); - dmaUpdate(bus.dma, bus.irq, cop0); + // Per-instruction device order MIRRORS psxe psx_update (psx.c:95-99) + // EXACTLY: cdrom → gpu → pad → timer → dma. (Was timer→dma→cdrom→…→gpu; + // the order decides which IRQ bit lands in I_STAT first each step, and + // psxe fires the GPU hblank/vblank events BEFORE the sysclk timer +2.) + + // 1. CDROM (psx.c:95) bus.cdrom.update(bus.disc, bus.spu, bus.irq, cop0, delta); - padUpdate(bus.pad, bus.irq, cop0, delta); - bus.sio1.update(bus.irq, cop0, delta); - // SPU audio OUTPUT is generated on the audio thread (psone_audio_cb), - // but the ADSR ENVELOPE state must advance locked to the CPU so the - // level the game polls (voice reg 0xC) is deterministic — step it - // once per 768 CPU cycles (44.1 kHz). Without this the envelope rode - // the wall-clock audio thread and desynced, looping the FMV intro. - spuAcc = spuAcc + delta; - while (spuAcc >= 768) { - spuAcc = spuAcc - 768; - // Advance the WHOLE SPU (envelope + playback + ENDX) one sample, - // CPU-locked, so all SPU state the game polls is deterministic. - // (psone_audio_cb is now silent so it doesn't double-advance; - // audio output to be reconnected via a ring buffer later.) Stash - // the sample in a free GPU scratch slot to consume the return. - gpuBuf[34] = spuGetSample(bus.spu, bus.spuram, bus.irq, cop0); - } - // Mirror psxe psx_gpu_update (gpu.c:1962-1983): a scanline is the - // GPU-cycle window [0,3413]; HBlank is [2560,3413]. The HBlank event - // — line++, GPUSTAT bit-31 odd/even flag, VBlank IRQ — fires on the - // crossing INTO [2560,3413] (~75% through the line), NOT at the - // scanline end; the scanline wraps on the crossing back out. + // 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 + // hblank/vblank timer hooks + VBlank IRQ) fires on the crossing INTO + // [2560,3413] (~75% through the line); the scanline wraps on crossing + // out. This runs BEFORE the sysclk timer update, matching psxe. const prevHb: bool = acc >= GPU_CYCLES_HDRAW && acc <= GPU_CYCLES_SCANL; acc = acc + (delta as f32) * ratio; const currHb: bool = acc >= GPU_CYCLES_HDRAW && acc <= GPU_CYCLES_SCANL; @@ -395,6 +381,26 @@ fn runOneFrame(c: mut Cpu, bus: Bus, regs: *mut[] u32, cop0: *mut[] u32) { bus.timer.hblankEnd(); acc = acc - GPU_CYCLES_SCANL; } + + // 3. PAD (psx.c:97) + padUpdate(bus.pad, bus.irq, cop0, delta); + // 4. TIMER sysclk (psx.c:98) — updateCyc ignores delta, uses 2 (psxe). + bus.timer.updateCyc(bus.irq, cop0, delta); + // 5. DMA (psx.c:99) + dmaTickSpu(bus.dma, delta); + dmaUpdate(bus.dma, bus.irq, cop0); + + // --- jam-only extras: psxe steps NEITHER in psx_update. Kept at the + // end (least perturbation to the psxe device order above) and flagged + // as divergences — SIO1 → peripherals task, SPU CPU-clock → SPU task + // (psxe runs SPU on the audio thread; revisit once the deterministic + // CPU surface matches psxe). + bus.sio1.update(bus.irq, cop0, delta); + spuAcc = spuAcc + delta; + while (spuAcc >= 768) { + spuAcc = spuAcc - 768; + gpuBuf[34] = spuGetSample(bus.spu, bus.spuram, bus.irq, cop0); + } } // NOTE: do NOT commitLoad here — each instruction's own commitLoad // handles the R3000 load-delay slot. diff --git a/mdec.jam b/mdec.jam index 371266e..9bdb408 100644 --- a/mdec.jam +++ b/mdec.jam @@ -439,6 +439,10 @@ pub const Mdec = struct { self.inputFull = 0; self.busy = 1; const op: u32 = (val >> 29) & 7; + // TEMP FMV probe: log every MDEC command word. op=1 DECODE is the + // frame decode the FMV needs; op=2/3 are the quant/IDCT tables. + var pf: []u8 = "[MDECcmd] op=%u val=%08x words=%u\n"; + printf(pf.ptr, op, val, val & 0xFFFF); var newWords: u32 = 0; if (op == MDEC_CMD_NOP) { self.busy = 0; diff --git a/pad.jam b/pad.jam index 0ecd3f8..44dc6af 100644 --- a/pad.jam +++ b/pad.jam @@ -25,7 +25,6 @@ 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 printf(format: *const[] u8, ...) i32; pub extern fn fflush(stream: u64) i32; const PAD_STATE_SIZE: u32 = 32; diff --git a/spu.jam b/spu.jam index e9afdbc..efab785 100644 --- a/spu.jam +++ b/spu.jam @@ -20,7 +20,6 @@ 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; -pub extern fn printf(format: *const[] u8, ...) i32; const SPU_REG_SIZE: u32 = 0x400; const VS_SIZE: u32 = 256; diff --git a/timer.jam b/timer.jam index de66782..708a4e8 100644 --- a/timer.jam +++ b/timer.jam @@ -1,5 +1,3 @@ -// Timers (port of psxe/psx/dev/timer.c). -// // Three 16-bit timers at 0x1F801100 / 0x1F801110 / 0x1F801120, each // exposing three registers (counter, mode, target). They count at // different clock sources — system clock, dotclock, hblank — and can @@ -13,11 +11,6 @@ const { irqRaise, IC_TIMER0, IC_TIMER1, IC_TIMER2 } = import("irq"); // flag bits stay u32 so they round-trip cleanly through the mode // register's pack/unpack without per-field width juggling. pub const TimerChannel = struct { - // psxe stores the counter as a `float` (timer.h:100) and accumulates - // `+= (float)cyc` / `/8.0f` / `*dotclock_div`, so it DRIFTS from an - // exact integer over time. We mirror that f32 drift bit-for-bit (an - // exact u32 counter diverged from psxe ~0.3% by frame ~224, found via - // REG_TRACE cmp). Reads truncate to u16; IRQ/target compare the float. counter: f32, target: u32, syncEnable: u32, @@ -251,9 +244,16 @@ pub const Timer = struct { pub fn updateCyc(self: mut Self, ic: *mut[] u32, cop0: *mut[] u32, cyc: u32) { - self.updateOne(0, cyc, ic, cop0); - self.updateOne(1, cyc, ic, cop0); - self.updateOne(2, cyc, ic, cop0); + // psxe's psx_timer_update (timer.c:338-345) IGNORES its cyc arg and + // advances all three timers by a hardcoded 2 per instruction — + // NOT the real per-instruction cycle count. (timer_update_timer0/1/2 + // do `+= (float)cyc`, but cyc is always 2.) Feeding the real `cyc` + // (fetchCyc+2 = 20 for BIOS-resident code) made jam's sysclk timers + // run ~10× too fast in BIOS/kernel loops, drifting any timer value the + // game reads. Match psxe: pass 2. (`cyc` kept for signature parity.) + self.updateOne(0, 2, ic, cop0); + self.updateOne(1, 2, ic, cop0); + self.updateOne(2, 2, ic, cop0); } pub fn updateOne(self: mut Self, idx: u32, cyc: u32, diff --git a/xa.jam b/xa.jam index 6f654ad..906b0b9 100644 --- a/xa.jam +++ b/xa.jam @@ -26,10 +26,6 @@ // are decoded as they stream. Playback (mixing into the SPU CDIN path) is // not yet integrated — psxe routes XA through SDL audio in audio.c. -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; - // ADPCM predictor coefficient tables (5 entries; filter index 0..3 maps // to entries 0..3; entry 4 is psxe's table padding and rarely hit). pub fn xaPosCoef(idx: u32) i32 {