Something went wrong. Try again.
A Playstation One (PS1) emulator fully written in Jam
Something went wrong. Try again.
jamstation sio1.jam
4.6 kB · 146 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147// SPDX-License-Identifier: MIT// Copyright (c) 2026 Raphael Amorim
const { irqRaise, IC_SIO } = import("irq");
const CTRL_TXEN: u32 = 0x0001;const CTRL_DTR: u32 = 0x0002;const CTRL_RXEN: u32 = 0x0004;const CTRL_ACKN: u32 = 0x0010;const CTRL_RESET: u32 = 0x0040;const CTRL_TX_IE: u32 = 0x0400;const CTRL_RX_IE: u32 = 0x0800;const CTRL_DSR_IE: u32 = 0x1000;
const STAT_TXR1: u32 = 0x0001;const STAT_RXNE: u32 = 0x0002;const STAT_TXR2: u32 = 0x0004;const STAT_PERR: u32 = 0x0008;const STAT_OVERR: u32 = 0x0010;const STAT_BSB: u32 = 0x0020;const STAT_IRQ: u32 = 0x0200;
const SIO_IRQ_DELAY: u32 = 200;
pub const Sio1 = struct { stat: u32, mode: u16, ctrl: u16, baud: u16, rxFifo: [8]u8, rxHead: u8, rxTail: u8, txPending: u8, cyclesUntilIrq: u32, pad: [16]u8,
pub fn init() Self { return Self { stat: 0, mode: 0, ctrl: 0, baud: 0, rxFifo: [0, 0, 0, 0, 0, 0, 0, 0], rxHead: 0, rxTail: 0, txPending: 0, cyclesUntilIrq: 0, pad: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], }; }
pub fn rxPush(self: mut Self, b: u32) { const tail: u32 = self.rxTail as u32; self.rxFifo[tail & 0x07] = (b & 0xFF) as u8; self.rxTail = ((tail + 1) & 0xFF) as u8; }
pub fn rxPop(self: mut Self) u32 { var head: u32 = self.rxHead as u32; const tail: u32 = self.rxTail as u32; if (head == tail) { return 0xFF; } const b: u32 = self.rxFifo[head & 0x07] as u32; head = (head + 1) & 0xFF; self.rxHead = head as u8; return b; }
pub fn rxEmpty(self: Self) bool { return self.rxHead == self.rxTail; }
pub fn readStat(self: Self) u32 { var stat: u32 = self.stat; stat = stat | STAT_TXR1; if (self.txPending == 0) { stat = stat | STAT_TXR2; } if (!self.rxEmpty()) { stat = stat | STAT_RXNE; } return stat; }
pub fn writeData(self: mut Self, val: u32) { if ((self.ctrl as u32 & CTRL_TXEN) == 0) { return; } self.rxPush(0xFF); self.txPending = 1; self.cyclesUntilIrq = SIO_IRQ_DELAY; }
pub fn writeCtrl(self: mut Self, val: u32) { const v: u32 = val & 0xFFFF; if ((v & CTRL_RESET) != 0) { const mode: u16 = self.mode; const baud: u16 = self.baud; self.stat = 0; self.ctrl = 0; self.rxFifo = [0, 0, 0, 0, 0, 0, 0, 0]; self.rxHead = 0; self.rxTail = 0; self.txPending = 0; self.cyclesUntilIrq = 0; self.mode = mode; self.baud = baud; return; } self.ctrl = v as u16; if ((v & CTRL_ACKN) != 0) { self.stat = self.stat & (~(STAT_PERR | STAT_OVERR | STAT_BSB | STAT_IRQ)); self.ctrl = self.ctrl & ((~CTRL_ACKN) as u16); } }
pub fn read32(self: mut Self, off: u32) u32 { match (off) { 0x00 { return self.rxPop(); } 0x04 { return self.readStat(); } 0x08 { return self.mode as u32; } 0x0A { return self.ctrl as u32; } 0x0E { return self.baud as u32; } _ { return 0; } } }
pub fn read16(self: mut Self, off: u32) u32 { return self.read32(off) & 0xFFFF; } pub fn read8(self: mut Self, off: u32) u32 { return self.read32(off) & 0xFF; }
pub fn write32(self: mut Self, off: u32, val: u32) { match (off) { 0x00 { self.writeData(val); } 0x08 { self.mode = (val & 0xFFFF) as u16; } 0x0A { self.writeCtrl(val); } 0x0E { self.baud = (val & 0xFFFF) as u16; } _ {} } }
pub fn write16(self: mut Self, off: u32, val: u32) { self.write32(off, val & 0xFFFF); } pub fn write8(self: mut Self, off: u32, val: u32) { self.write32(off, val & 0xFF); }
pub fn update(self: mut Self, ic: *mut[] u32, cop0: *mut[] u32, cyc: u32) { if (self.cyclesUntilIrq == 0) { return; } if (self.cyclesUntilIrq > cyc) { self.cyclesUntilIrq = self.cyclesUntilIrq - cyc; return; } self.cyclesUntilIrq = 0; self.txPending = 0; const ctrl: u32 = self.ctrl as u32; if ((ctrl & (CTRL_TX_IE | CTRL_RX_IE)) != 0) { self.stat = self.stat | STAT_IRQ; irqRaise(ic, cop0, IC_SIO); } }};