From b978bded4f00ea7a9bae8ec56fb9d9c0fc742d57 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Mon, 1 Jun 2026 11:25:25 +0200 Subject: [PATCH] some debugs --- cdrom.jam | 20 ++++++++++++---- dma.jam | 9 +++++++- main.jam | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mdec.jam | 7 ++++++ 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/cdrom.jam b/cdrom.jam index 86e1507..22ab472 100644 --- a/cdrom.jam +++ b/cdrom.jam @@ -877,6 +877,22 @@ pub const Cdrom = struct { CD_ERR_INVALID_SUBFUNC); return; } + // XA-ADPCM audio sectors are consumed by the SPU and are NOT delivered + // to the game's data FIFO / do not raise INT1 -- only video/data + // sectors are. Without this filter, audio sectors pollute the game's + // STR video stream so its frame parser never assembles a frame to + // decode (cmdDecode stays 0). Avocado (cdrom.cpp:141) delivers to the + // host only when !audio || !realtime; submode is at sector offset 0x12 + // (bit 2 = Audio, bit 6 = Real-time). + const submode: u32 = self.dataBuf[18] as u32; + const isXaAudio: bool = (submode & 0x04) != 0 && (submode & 0x40) != 0; + if ((self.mode as u32 & MODE_XA_ADPCM) != 0 && isXaAudio) { + self.tryDecodeXa(spu); // SPU CD-audio (honours the filter) + self.pendingLba = lba + 1; // consume the sector, advance, no INT1 + self.delay = self.readDelay(); + return; + } + var rIdx: u32 = 24; var wEnd: u32 = 24 + 0x800; if ((self.mode as u32 & MODE_SECTOR_SIZE) != 0) { @@ -886,10 +902,6 @@ pub const Cdrom = struct { self.dataRidx = rIdx; self.dataWidx = wEnd; - if ((self.mode as u32 & MODE_XA_ADPCM) != 0) { - self.tryDecodeXa(spu); - } - self.ifr = 1; self.pushResp(self.getStat()); diff --git a/dma.jam b/dma.jam index 45f458a..3fd3070 100644 --- a/dma.jam +++ b/dma.jam @@ -30,7 +30,7 @@ const SPU_TFIFO_OFF: u32 = 0x1A8; // channel stay "busy" while the test polls CHCR — ps1-tests // spu/memory-transfer testDMAWriteTiming asserts that the // transfer is observable in-flight (loopCount > 0). -const DMA_STATE_WORDS: u32 = 32; +const DMA_STATE_WORDS: u32 = 35; // [32]=CD-DMA cnt [33]=STR-magic cnt [34]=last hdr word (TEMP FMV probe) const F_SPU_REMAIN: u32 = 29; // TEMP FMV probe: d[30]=mdecIn call count, d[31]=mdecOut call count. @@ -324,6 +324,7 @@ 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; // TEMP FMV probe: MDEC-in DMA fire count } // DMA1: MDEC output port → RAM. Match psxe exactly (dma.c:208-235). @@ -343,6 +344,7 @@ 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; // TEMP FMV probe: MDEC-out DMA fire count } // CD-ROM → RAM transfer. Each "word" in BCR.size is 4 bytes from the @@ -363,6 +365,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) { // TEMP FMV probe: STR header word of each read sector + d[34] = w; + if ((w & 0xFFFF) == 0x0160) { d[33] = d[33] + 1; } + } ramWrite32(bus, addr, w); addr = addr + step; i = i + 1; @@ -371,6 +377,7 @@ pub fn dmaDoCdrom(d: *mut[] u32, bus: Bus) { d[chBase(3) + F_CHCR] = 0; d[chBase(3) + F_BCR] = 0; d[26] = 1; + d[32] = d[32] + 1; // TEMP FMV probe: CD->RAM DMA fire count } // SPU DMA (channel 4). psxe dma.c:375-437 pipes each 32-bit word through diff --git a/main.jam b/main.jam index ebf0b8d..0ffe222 100644 --- a/main.jam +++ b/main.jam @@ -630,6 +630,7 @@ fn main() { // the drift an integer-ms target would introduce. const FRAME_MS: f64 = 1000.0 / 59.94; var nextFrameMs: f64 = SDL_GetTicks() as f64; + var probeN: u32 = 0; // TEMP: FMV display probe rate-limiter while (!quit) { // Game-EXE side-load. Two trigger paths: // 1. BIOS jumps to 0x80030000 (real-disc boot handoff). @@ -676,6 +677,74 @@ fn main() { const dm: u32 = gpuState[47]; const is24bpp: bool = (dm & 0x10) != 0; const displayOff: bool = (gpuState[46] & 0x00800000) != 0; + // TEMP FMV display probe — ~1x/sec, dump the display config + whether + // VRAM holds data at the display origin. Pinpoints "black video": + // off=1 -> display disabled (re-enable not seen) + // 24bpp=0 in FMV -> game/jamstation not in 24bpp mode + // vramNZ=0 -> frame not at (dispX,dispY): wrong origin / no upload + // vramNZ>0 + black -> 24bpp readout (sdlBlit) bug + probeN = probeN + 1; + if (probeN >= 60) { + probeN = 0; + var b24: u32 = 0; + if (is24bpp) { b24 = 1; } + var boff: u32 = 0; + if (displayOff) { boff = 1; } + const base: u32 = (dispY as u32) * 2048 + (dispX as u32) * 2; + var nz: u32 = 0; + if (base + 256 < 1048576) { + var k: u32 = 0; + while (k < 256) { + if (vram[base + k] != 0) { nz = nz + 1; } + k = k + 1; + } + } + // MDEC DMA fire counts (wired in dma.jam) + locate the VRAM row + // holding the most data (where the decoded frame actually lands). + var dmaP: *mut[] u32 = bus.dma.ptr; + const d30: u32 = dmaP[30]; + const d31: u32 = dmaP[31]; + const cdDma: u32 = dmaP[32]; // CD->RAM DMA fire count + const strMagic: u32 = dmaP[33]; // sectors read with STR magic 0x0160 + const lastHdr: u32 = dmaP[34]; // last read sector's first word + var bestY: u32 = 0; + var bestNz: u32 = 0; + var yy: u32 = 0; + while (yy < 512) { + const rb: u32 = yy * 2048; + var rnz: u32 = 0; + var j: u32 = 0; + while (j < 256) { + if (vram[rb + j] != 0) { rnz = rnz + 1; } + j = j + 1; + } + if (rnz > bestNz) { bestNz = rnz; bestY = yy; } + yy = yy + 16; + } + // MDEC status (read32 off=4 is a pure read): wordsRem (bits + // 0-15), bit27 Data-Out-Req, bit29 busy, bit31 Data-Out-empty. + // Tells us WHY DMA1 never fires: output empty (no decode) vs + // stuck waiting for input (wordsRem>0/busy) vs ready-but-ignored. + const mst: u32 = bus.mdec.read32(bus.mdecOut.ptr, 4); + const mWords: u32 = mst & 0xFFFF; + const mB27: u32 = (mst >> 27) & 1; + const mB31: u32 = (mst >> 31) & 1; + // Monotonic decode counter + last outputWords stashed by the MDEC + // at the output-buffer tail (offsets 0xFFFFC / 0xFFFF8). + var mo: *mut[] u8 = bus.mdecOut.ptr; + const decCount: u32 = (mo[0xFFFFC] as u32) | ((mo[0xFFFFD] as u32) << 8) + | ((mo[0xFFFFE] as u32) << 16) | ((mo[0xFFFFF] as u32) << 24); + const lastOW: u32 = (mo[0xFFFF8] as u32) | ((mo[0xFFFF9] as u32) << 8) + | ((mo[0xFFFFA] as u32) << 16) | ((mo[0xFFFFB] as u32) << 24); + // DECODE/SET_QT/SET_ST command-STARTED counts (op*4 @ 0xFFF00). + const cmdDec: u32 = (mo[0xFFF04] as u32) | ((mo[0xFFF05] as u32) << 8) + | ((mo[0xFFF06] as u32) << 16) | ((mo[0xFFF07] as u32) << 24); + const cmdQt: u32 = (mo[0xFFF08] as u32) | ((mo[0xFFF09] as u32) << 8) + | ((mo[0xFFF0A] as u32) << 16) | ((mo[0xFFF0B] as u32) << 24); + const cmdSt: u32 = (mo[0xFFF0C] as u32) | ((mo[0xFFF0D] as u32) << 8) + | ((mo[0xFFF0E] as u32) << 16) | ((mo[0xFFF0F] as u32) << 24); + print("FMV: 24bpp={b24} cdDma={cdDma} strMagic={strMagic} lastHdr={lastHdr} in={d30} | cmdDecode={cmdDec} cmdQt={cmdQt} cmdSt={cmdSt} done={decCount}\n"); + } if (fullVram) { sdlBlit(sdl, vram, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, false, displayOff); diff --git a/mdec.jam b/mdec.jam index 2700192..60e9bd0 100644 --- a/mdec.jam +++ b/mdec.jam @@ -311,6 +311,10 @@ pub const Mdec = struct { } self.outputWords = outBytes / 4; + // TEMP FMV probe: monotonic decode counter + last outputWords stashed + // at the tail of the 1 MB output buffer (a frame is <256 KB, no clash). + writeU32(output, 0xFFFFC, readU32(output, 0xFFFFC) + 1); + writeU32(output, 0xFFFF8, self.outputWords); self.outputEmpty = 0; self.outputIndex = 0; } @@ -444,6 +448,9 @@ pub const Mdec = struct { self.inputFull = 0; self.busy = 1; const op: u32 = (val >> 29) & 7; + // TEMP FMV probe: count command words started, per op, at the + // output-buffer tail (0xFFF00 + op*4). cmd[1]=DECODE started. + writeU32(output, 0xFFF00 + op * 4, readU32(output, 0xFFF00 + op * 4) + 1); var newWords: u32 = 0; if (op == MDEC_CMD_NOP) { self.busy = 0; -- 2.51.2