diff --git a/cdrom.jam b/cdrom.jam index 140eff4..43a586f 100644 --- a/cdrom.jam +++ b/cdrom.jam @@ -1,12 +1,7 @@ -// CD-ROM controller - a minimal implementation covering -// the BIOS-boot subset of commands plus the FIFO + IRQ + state-machine -// plumbing every command depends on. Audio (CDDA) is stubbed; XA-ADPCM -// streams through to the SPU CD-audio FIFO when MODE.XA is set. +// register map at 0x1F801800..0x1F801803 // -// Register map at 0x1F801800..0x1F801803 - every register is bank- -// selected: writes to address 0x800 set bits 0-1 of the bank index, -// then addresses 0x801..0x803 take different meanings per bank. The -// dispatch table: +// every register is bank-selected: writes to address 0x800 set bits 0-1 of the bank index, +// then addresses 0x801..0x803 take different meanings per bank. The dispatch table: // // bank read 0x800 status (busy/parm-empty/resp-empty/...) // bank read 0x801 pop one byte from the response FIFO @@ -18,13 +13,6 @@ // bank 0 write 0x803 request register (bit 7 = data req) // bank 1 write 0x802 IER // bank 1 write 0x803 IFR ack (clears bits in IFR that are set in val) -// -// Acknowledge protocol: each command schedules a response that fires -// after `CD_DELAY_FR` CPU cycles. When the delay elapses, the response -// handler pushes bytes to the response FIFO, sets IFR to a 1..7 INT -// number, and raises IC_CDROM. The BIOS handler reads response bytes -// then writes 0x07 to IFR to ack - the FIFO and IFR stay sticky until -// then. const { irqRaise, IC_CDROM } = import("irq"); const { Disc, discRead, discLoaded, @@ -38,21 +26,21 @@ const { spuPushCdSample } = import("spu"); const SECTOR_BYTES: u32 = 2352; -// State machine states (IDLE/TX_RESP1/TX_RESP2/READ/PLAY). const CD_STATE_IDLE: u32 = 0; const CD_STATE_TX_RESP1: u32 = 1; const CD_STATE_TX_RESP2: u32 = 2; const CD_STATE_READ: u32 = 3; const CD_STATE_PLAY: u32 = 4; -// Response / read scheduling delays (CPU cycles). -const CD_DELAY_1MS: u32 = 33869; // 1ms at 33.8688MHz +// 1ms at 33.8688MHz +const CD_DELAY_1MS: u32 = 33869; const CD_DELAY_FR: u32 = 50401; const CD_DELAY_INIT_FR: u32 = 81102; -const CD_DELAY_READ_SS: u32 = 451584; // 33_868_800 / 75 -const CD_DELAY_READ_DS: u32 = 225792; // 33_868_800 / (2*75) +// 33_868_800 / 75 +const CD_DELAY_READ_SS: u32 = 451584; +// 33_868_800 / (2*75) +const CD_DELAY_READ_DS: u32 = 225792; -// Command codes (matches CDL_* in cdrom.h). const CDL_GETSTAT: u32 = 0x01; const CDL_SETLOC: u32 = 0x02; const CDL_PLAY: u32 = 0x03; @@ -82,13 +70,15 @@ const CDL_RESET: u32 = 0x1C; const CDL_GETQ: u32 = 0x1D; const CDL_READTOC: u32 = 0x1E; -// MODE register bits. -const MODE_SECTOR_SIZE: u32 = 0x20; // 0 = 0x800 payload, 1 = 0x924 raw -const MODE_SPEED: u32 = 0x80; // 0 = 1x, 1 = 2x -const MODE_XA_ADPCM: u32 = 0x40; // 1 = enable XA-ADPCM streaming -const MODE_XA_FILTER: u32 = 0x08; // 1 = honour CdlSetFilter file/chan +// 0 = 0x800 payload, 1 = 0x924 raw +const MODE_SECTOR_SIZE: u32 = 0x20; +// 0 = 1x, 1 = 2x +const MODE_SPEED: u32 = 0x80; +// 1 = enable XA-ADPCM streaming +const MODE_XA_ADPCM: u32 = 0x40; +// 1 = honour CdlSetFilter file/chan +const MODE_XA_FILTER: u32 = 0x08; -// Status byte bits (CD_STAT_*). const CD_STAT_PLAY: u32 = 0x80; const CD_STAT_SEEK: u32 = 0x40; const CD_STAT_READ: u32 = 0x20; @@ -98,40 +88,49 @@ const CD_STAT_SEEKERROR: u32 = 0x04; const CD_STAT_SPINDLE: u32 = 0x02; const CD_STAT_ERROR: u32 = 0x01; -// Error codes (CD_ERR_*). +// error codes (CD_ERR_*). const CD_ERR_NO_DISC: u32 = 0x80; const CD_ERR_INVALID_COMMAND: u32 = 0x40; const CD_ERR_WRONG_PARAM_CNT: u32 = 0x20; const CD_ERR_INVALID_SUBFUNC: u32 = 0x10; const CD_ERR_SEEK_FAILED: u32 = 0x04; -// CD-ROM controller state. Each field replaces a named byte offset from -// the old buffer-based layout. Two 32-byte FIFOs (param / resp) ride -// alongside the registers; the 2352-byte data buffer holds one raw -// Mode2/2352 sector at a time and `dataRidx` / `dataWidx` track the -// DMA-side cursor through it. pub const Cdrom = struct { - // 1-byte control / status registers. - index: u8, // bank select 0-3 - ifr: u8, // interrupt flag register, low 5 bits - ier: u8, // interrupt enable register - busy: u8, // command in progress - state: u8, // CD_STATE_* + // bank select 0-3 + index: u8, + // interrupt flag register, low 5 bits + ifr: u8, + // interrupt enable register + ier: u8, + // command in progress + busy: u8, + // CD_STATE_* + state: u8, prevState: u8, pendingCmd: u8, - dataReq: u8, // request register bit 7 + // request register bit 7 + dataReq: u8, mode: u8, - discType: u8, // 0=licensed, 1=audio, 2=unlicensed - region: u8, // 0=Japan, 1=America, 2=Europe - version: u8, // CDR_VERSION_* index - readOngoing: u8, // sector pump active - xaPlaying: u8, // XA-ADPCM streaming active (status reg bit2 ADPBUSY) + // 0=licensed, 1=audio, 2=unlicensed + discType: u8, + // 0=Japan, 1=America, 2=Europe + region: u8, + // CDR_VERSION_* index + version: u8, + // sector pump active + readOngoing: u8, + // XA-ADPCM streaming active (status reg bit2 ADPBUSY) + xaPlaying: u8, hasDisc: u8, cmdPending: u8, - xaFile: u8, // SetFilter file - xaChannel: u8, // SetFilter channel - seekPrec: u8, // SeekL sets, GetLocP reads - prevSpeed: u8, // previous MODE.SPEED for SetMode delay + // SetFilter file + xaFile: u8, + // SetFilter channel + xaChannel: u8, + // SeekL sets, GetLocP reads + seekPrec: u8, + // previous MODE.SPEED for SetMode delay + prevSpeed: u8, paramR: u8, paramW: u8, respR: u8, @@ -140,18 +139,16 @@ pub const Cdrom = struct { delay: u32, lba: u32, pendingLba: u32, - pendingSpeedSwitch: u32, // 1x<->2x speed-change resync delay + pendingSpeedSwitch: u32, dataRidx: u32, dataWidx: u32, // XA-ADPCM per-channel history (2 i32 each). Persists across // sectors so block-N's predictor sees block-(N-1)'s tail. xaLh: [2]i32, xaRh: [2]i32, - // Last resampled OUTPUT sample per channel - the `ls` seed for the next - // sector's XA->44.1kHz rate conversion. xaPrevL: i32, xaPrevR: i32, - // Parameter / response FIFOs. Each is a 32-byte ring with u8 + // Parameter / response FIFOs. each is a 32-byte ring with u8 // head/tail; the indexes wrap modulo 32 via `& 0x1F`, and the // empty test compares the raw head/tail bytes directly. paramFifo: [32]u8, @@ -171,10 +168,6 @@ pub const Cdrom = struct { seekPrec: 1, prevSpeed: 0, paramR: 0, paramW: 0, respR: 0, respW: 0, - // Start at LBA 150 (00:02:00, first data sector). - // jam's LBA is pregap-inclusive - cueRead - // maps off=(lba-startLba)*SECTOR with track1 startLba=150 - - // so 0 would read a negative offset (zero pregap), not sector 0. delay: 0, lba: 150, pendingLba: 150, pendingSpeedSwitch: 0, dataRidx: 0, dataWidx: 0, xaLh: [0, 0], @@ -190,12 +183,6 @@ pub const Cdrom = struct { if (discLoaded(disc) != 0) { self.hasDisc = 1; } } - // FIFO ops - // Two separate FIFOs (param / resp) - methods are duplicated rather - // than parameterized to keep the index/buffer pairing type-safe. - // Same ring-buffer math the old `qPush`/`qPop` did, just with - // typed fields. - pub fn pushParam(self: mut Self, v: u32) { const idx: u32 = (self.paramW as u32) & 0x1F; self.paramFifo[idx] = (v & 0xFF) as u8; @@ -210,11 +197,11 @@ pub const Cdrom = struct { return v as u32; } - pub fn paramEmpty(self: mut Self) bool { + pub fn paramEmpty(self: Self) bool { return self.paramR == self.paramW; } - pub fn paramFull(self: mut Self) bool { + pub fn paramFull(self: Self) bool { return (((self.paramW as u32) - (self.paramR as u32)) & 0x1F) == 16; } @@ -237,7 +224,7 @@ pub const Cdrom = struct { return v as u32; } - pub fn respEmpty(self: mut Self) bool { + pub fn respEmpty(self: Self) bool { return self.respR == self.respW; } @@ -246,12 +233,10 @@ pub const Cdrom = struct { self.respW = 0; } - // status byte - // Status byte: SPINDLE always, plus // READ while the sector pump is active, SHELLOPEN if no disc, // IDERROR if mode bit 4 is set. - pub fn getStat(self: mut Self) u32 { + pub fn getStat(self: Self) u32 { var s: u32 = CD_STAT_SPINDLE; if (self.readOngoing != 0) { s = s | CD_STAT_READ; } if ((self.mode as u32 & 0x10) != 0) { s = s | CD_STAT_IDERROR; } @@ -262,7 +247,7 @@ pub const Cdrom = struct { // register I/O // Status register at offset 0 (always, regardless of bank). - pub fn readStatus(self: mut Self) u32 { + pub fn readStatus(self: Self) u32 { var r: u32 = (self.index as u32) & 0x3; // bit 2 (ADPBUSY): XA-ADPCM streaming active. Set when a ReadN/ReadS // runs in XA mode. @@ -706,7 +691,7 @@ pub const Cdrom = struct { self.pendingLba = 0; } - pub fn readDelay(self: mut Self) u32 { + pub fn readDelay(self: Self) u32 { if ((self.mode as u32 & MODE_SPEED) != 0) { return CD_DELAY_READ_DS; } return CD_DELAY_READ_SS; } @@ -1027,7 +1012,7 @@ pub const Cdrom = struct { } // Read one sector at `lba` into the dataBuf. Returns 1 on success. - pub fn discReadAt(self: mut Self, disc: *mut[] Disc, lba: u32) i32 { + pub fn discReadAt(self: Self, disc: *mut[] Disc, lba: u32) i32 { return discRead(disc, lba, self.dataBuf.asMutPtr()); } diff --git a/gpu.jam b/gpu.jam index 3f9a239..d0af3af 100644 --- a/gpu.jam +++ b/gpu.jam @@ -1352,37 +1352,37 @@ pub const Gpu = struct { return s; } - pub fn familyCount(self: mut Self, family: u32) u32 { + pub fn familyCount(self: Self, family: u32) u32 { return gpuFamilyCount(self.buf.asMutPtr(), family); } - pub fn read8(self: mut Self, vram: *mut[] u8, off: u32) u32 { + pub fn read8(self: Self, vram: *mut[] u8, off: u32) u32 { return gpuRead8(self.buf.asMutPtr(), vram, off); } - pub fn read16(self: mut Self, vram: *mut[] u8, off: u32) u32 { + pub fn read16(self: Self, vram: *mut[] u8, off: u32) u32 { return gpuRead16(self.buf.asMutPtr(), vram, off); } - pub fn read32(self: mut Self, vram: *mut[] u8, off: u32) u32 { + pub fn read32(self: Self, vram: *mut[] u8, off: u32) u32 { return gpuRead32(self.buf.asMutPtr(), vram, off); } - pub fn write8(self: mut Self, vram: *mut[] u8, off: u32, val: u32) { + pub fn write8(self: Self, vram: *mut[] u8, off: u32, val: u32) { gpuWrite8(self.buf.asMutPtr(), vram, off, val); } - pub fn write16(self: mut Self, vram: *mut[] u8, off: u32, val: u32) { + pub fn write16(self: Self, vram: *mut[] u8, off: u32, val: u32) { gpuWrite16(self.buf.asMutPtr(), vram, off, val); } - pub fn write32(self: mut Self, vram: *mut[] u8, off: u32, val: u32) { + pub fn write32(self: Self, vram: *mut[] u8, off: u32, val: u32) { gpuWrite32(self.buf.asMutPtr(), vram, off, val); } // DMA channel 2 GPU path (linked-list + request modes) calls // these per word from dma.jam. - pub fn gp0(self: mut Self, vram: *mut[] u8, val: u32) { + pub fn gp0(self: Self, vram: *mut[] u8, val: u32) { gpuGp0(self.buf.asMutPtr(), vram, val); } @@ -1390,7 +1390,7 @@ pub const Gpu = struct { // display-mode / disp_x / etc. fields directly. Bypasses the // method API for the small set of read-only readouts the host // pump needs each frame. - pub fn bufPtr(self: mut Self) *mut[] u32 { + pub fn bufPtr(self: Self) *mut[] u32 { return self.buf.asMutPtr(); } }; diff --git a/main.jam b/main.jam index 6160b20..a779adc 100644 --- a/main.jam +++ b/main.jam @@ -624,5 +624,5 @@ fn main() { mcdRamSave(bus.mcdRam.ptr, mcdPath); sdlAudioClose(audioDev); sdlQuit(sdl); - print("\njamstation quitted\n"); + print("jamstation: quitting...\n"); } diff --git a/mcd.jam b/mcd.jam index c00f8c7..7aa6b94 100644 --- a/mcd.jam +++ b/mcd.jam @@ -100,7 +100,7 @@ pub const Mcd = struct { self.txReady = 0; } - pub fn query(self: mut Self) bool { + pub fn query(self: Self) bool { return self.txReady != 0; } diff --git a/mdec.jam b/mdec.jam index 772e1b2..09c55f5 100644 --- a/mdec.jam +++ b/mdec.jam @@ -132,7 +132,7 @@ pub const Mdec = struct { // Two-pass 8×8 transform. The block (blk) provides both the input // and final output; we ping-pong through the scratch slot at // blk + 64 (i.e. the second half of the 128-element block array). - pub fn realIdct(self: mut Self, blk: *mut[] i16) { + pub fn realIdct(self: Self, blk: *mut[] i16) { idctPassPtr(blk, blk, 64, self.scale.asMutPtr()); idctPassPtr(blk, blk, 64, self.scale.asMutPtr()); // Note: the iDCT ping-pongs blk↔scratch via 64-entry offset. @@ -146,7 +146,7 @@ pub const Mdec = struct { // block into `blk` (a 128-entry i16 array; lower 64 are the live // block, upper 64 the iDCT scratch). `quant` selects Y or UV. // Returns the new input word index. - pub fn rlDecodeBlock(self: mut Self, input: *mut[] u8, inIdx: u32, + pub fn rlDecodeBlock(self: Self, input: *mut[] u8, inIdx: u32, blk: *mut[] i16, quant: *mut[] u8) u32 { // Clear the live block. var i: u32 = 0; @@ -194,7 +194,7 @@ pub const Mdec = struct { } // YUV -> RGB - pub fn yuvToRgb(self: mut Self, output: *mut[] u8, outBase: u32, + pub fn yuvToRgb(self: Self, output: *mut[] u8, outBase: u32, xx: u32, yy: u32) { const depth: u32 = self.outputDepth; const signed_: u32 = self.outputSigned;