Something went wrong. Try again.
A Playstation One (PS1) emulator fully written in Jam
Something went wrong. Try again.
jamstation mcd.jam
7.4 kB · 231 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232// SPDX-License-Identifier: MIT// Copyright (c) 2026 Raphael Amorim
// TX_HIZ -> 0xFF on first read (line high-Z)// TX_FLG -> flag byte (0x08 = "first access since power-on")// TX_ID1 / TX_ID2 -> 0x5A / 0x5D identification bytes// R_* -> READ subprotocol: take MSB/LSB -> addr<<7, then// ACK + 128 data bytes + checksum + 'G' end marker// W_* -> WRITE subprotocol: take MSB/LSB + 128 data bytes// + checksum, then ACK + 'G' end marker// S_* -> STATUS subprotocol (rarely used)
const { Vec } = import("std/collections");const { File } = import("std/fs");
const MCD_MEMORY_SIZE: u64 = 0x20000;
const MCD_TX_HIZ: u32 = 0;const MCD_TX_FLG: u32 = 1;const MCD_TX_ID1: u32 = 2;const MCD_TX_ID2: u32 = 3;const MCD_R_RX_MSB: u32 = 4;const MCD_R_RX_LSB: u32 = 5;const MCD_R_TX_ACK1: u32 = 6;const MCD_R_TX_ACK2: u32 = 7;const MCD_R_TX_MSB: u32 = 8;const MCD_R_TX_LSB: u32 = 9;const MCD_R_TX_DATA: u32 = 10;const MCD_R_TX_CHK: u32 = 11;const MCD_R_TX_MEB: u32 = 12;const MCD_W_RX_MSB: u32 = 13;const MCD_W_RX_LSB: u32 = 14;const MCD_W_RX_DATA: u32 = 15;const MCD_W_RX_CHK: u32 = 16;const MCD_W_RX_CHK2: u32 = 17;const MCD_W_TX_ACK1: u32 = 18;const MCD_W_TX_ACK2: u32 = 19;const MCD_W_TX_MEB: u32 = 20;const MCD_S_TX_ACK1: u32 = 21;const MCD_S_TX_ACK2: u32 = 22;const MCD_S_TX_DAT0: u32 = 23;const MCD_S_TX_DAT1: u32 = 24;const MCD_S_TX_DAT2: u32 = 25;const MCD_S_TX_DAT3: u32 = 26;
pub const Mcd = struct { state: u8, txData: u8, rxData: u8, txReady: u8, mode: u8, flag: u8, msb: u8, lsb: u8, addr: u16, pend: u16, checksum: u8, pad: [16]u8,
pub fn init() Self { return Self { state: 0, txData: 0, rxData: 0, txReady: 0, mode: 0, flag: 0x08, msb: 0, lsb: 0, addr: 0, pend: 0, checksum: 0, pad: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], }; }
pub fn reset(self: mut Self) { self.state = 0; // MCD_TX_HIZ self.txReady = 0; }
pub fn query(self: Self) bool { return self.txReady != 0; }
pub fn read(self: mut Self, ram: *mut[] u8) u32 { const st: u32 = self.state as u32; match (st) { MCD_TX_HIZ { self.txData = 0xFF; } MCD_TX_FLG { const rx: u32 = self.rxData as u32; if (rx == 0x81 || rx == 0x01) { self.txReady = 1; self.txData = 0xFF; self.state = MCD_TX_HIZ as u8; return 0xFF; } self.txData = self.flag; self.flag = 0; } MCD_TX_ID1 { self.txData = 0x5A; } MCD_TX_ID2 { self.txReady = 1; self.txData = 0x5D; const m: u32 = self.mode as u32; // 'R' = read if (m == 0x52) { self.state = MCD_R_RX_MSB as u8; // 'W' = write } else if (m == 0x57) { self.state = MCD_W_RX_MSB as u8; // 'S' = status } else if (m == 0x53) { self.state = MCD_S_TX_ACK1 as u8; } else { // Unknown mode - drop the slot. self.txReady = 0; self.txData = 0xFF; self.state = MCD_TX_HIZ as u8; } return self.txData as u32; } MCD_R_RX_MSB { self.txData = 0x00; } MCD_R_RX_LSB { self.txData = self.msb; } MCD_R_TX_ACK1 { self.txData = 0x5C; } MCD_R_TX_ACK2 { self.txData = 0x5D; } MCD_R_TX_MSB { self.txData = self.msb; self.checksum = self.msb; } MCD_R_TX_LSB { self.txData = self.lsb; self.checksum = self.checksum ^ self.lsb; self.pend = 128; } MCD_R_TX_DATA { const pend: u32 = self.pend as u32; const addr: u32 = self.addr as u32; const b: u8 = ram[addr]; self.addr = (addr + 1) as u16; self.checksum = self.checksum ^ b; self.pend = (pend - 1) as u16; if (pend - 1 == 0) { self.txData = b; } else { return b as u32; } } MCD_R_TX_CHK { self.txData = self.checksum; } MCD_R_TX_MEB { self.txReady = 0; self.state = MCD_TX_HIZ as u8; // 'G' = good end marker return 0x47; } MCD_W_RX_MSB { self.txData = 0x00; } MCD_W_RX_LSB { self.txData = self.msb; self.pend = 128; } MCD_W_RX_DATA { const pend: u32 = self.pend as u32; const addr: u32 = self.addr as u32; ram[addr] = self.rxData; self.addr = (addr + 1) as u16; self.pend = (pend - 1) as u16; if (pend - 1 == 0) { self.txData = self.rxData; } else { return self.rxData as u32; } } MCD_W_RX_CHK | MCD_W_RX_CHK2 { self.txData = self.rxData; } MCD_W_TX_ACK1 { self.txData = 0x5C; } MCD_W_TX_ACK2 { self.txData = 0x5D; } MCD_W_TX_MEB { self.txReady = 0; self.state = MCD_TX_HIZ as u8; return 0x47; } _ { } } self.txReady = 1; self.state = (self.state + 1) as u8; return self.txData as u32; }
pub fn write(self: mut Self, data: u32) { const d: u8 = (data & 0xFF) as u8; self.rxData = d; const st: u32 = self.state as u32; match (st) { MCD_TX_FLG { self.mode = d; } MCD_R_RX_MSB | MCD_W_RX_MSB { self.msb = d; } MCD_R_RX_LSB | MCD_W_RX_LSB { self.lsb = d; const a: u32 = ((self.msb as u32) << 8) | (d as u32); self.addr = (a << 7) as u16; } _ { } } }};
pub fn createMcdRam() Vec(u8) { return Vec(u8).filled(0, MCD_MEMORY_SIZE as u32);}
pub fn mcdRamLoad(ram: *mut[] u8, path: []u8) bool { match (File.open(path)) { Some(f) { const got: u64 = f.read(ram[0..MCD_MEMORY_SIZE]); f.close(); return got == MCD_MEMORY_SIZE; } None { return false; } } return false;}
pub fn mcdRamSave(ram: *mut[] u8, path: []u8) bool { match (File.create(path)) { Some(f) { const put: u64 = f.write(ram[0..MCD_MEMORY_SIZE]); f.close(); return put == MCD_MEMORY_SIZE; } None { return false; } } return false;}