Something went wrong. Try again.
A Playstation One (PS1) emulator fully written in Jam
Something went wrong. Try again.
jamstation pad.jam
7.3 kB · 256 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257// SPDX-License-Identifier: MIT// Copyright (c) 2026 Raphael Amorim
const { irqRaise, IC_JOY } = import("irq");const { Mcd } = import("mcd");const { Vec } = import("std/collections");
const PAD_STATE_SIZE: u32 = 32;
const P_STAT_LO: u32 = 0;const P_STAT_HI: u32 = 1;const P_CTRL_LO: u32 = 2;const P_CTRL_HI: u32 = 3;const P_MODE_LO: u32 = 4;const P_MODE_HI: u32 = 5;const P_BAUD_LO: u32 = 6;const P_BAUD_HI: u32 = 7;const P_DEST: u32 = 8;const P_SDA_STATE: u32 = 9;const P_SW_LO: u32 = 10;const P_SW_HI: u32 = 11;const P_SDA_TXR: u32 = 12;const P_IRQ_BIT: u32 = 13;const P_CYC_LO: u32 = 14;const P_CYC_HI1: u32 = 15;const P_CYC_HI2: u32 = 16;const P_CYC_HI3: u32 = 17;
const SDA_HIZ: u32 = 0;const SDA_IDL: u32 = 1;const SDA_IDH: u32 = 2;const SDA_SWL: u32 = 3;const SDA_SWH: u32 = 4;
const CTRL_TXEN: u32 = 0x0001;const CTRL_JOUT: u32 = 0x0002;const CTRL_RXEN: u32 = 0x0004;const CTRL_ACKN: u32 = 0x0010;const CTRL_ACIE: u32 = 0x1000;const CTRL_SLOT: u32 = 0x2000;
const STAT_TXR1: u32 = 0x0001;const STAT_RXNE: u32 = 0x0002;const STAT_TXR2: u32 = 0x0004;const STAT_ACKL: u32 = 0x0080;const STAT_IRQ7: u32 = 0x0200;
const DEST_JOY: u32 = 0x01;const DEST_MCD: u32 = 0x81;
const JOY_IRQ_DELAY: u32 = 512;
pub fn createPad() Vec(u8) { var b: Vec(u8) = Vec(u8).filled(0, PAD_STATE_SIZE); b[P_SW_LO] = 0xFF; b[P_SW_HI] = 0xFF; return b;}
pub fn padSetButtons(p: *mut[] u8, mask: u32) { p[P_SW_LO] = (mask & 0xFF) as u8; p[P_SW_HI] = ((mask >> 8) & 0xFF) as u8;}
pub fn padGetU16(p: *mut[] u8, off: u32) u32 { return (p[off] as u32) | ((p[off + 1] as u32) << 8);}
pub fn padSetU16(p: *mut[] u8, off: u32, v: u32) { p[off] = (v & 0xFF) as u8; p[off + 1] = ((v >> 8) & 0xFF) as u8;}
pub fn padGetU32(p: *mut[] u8, off: u32) u32 { return (p[off] as u32) | ((p[off + 1] as u32) << 8) | ((p[off + 2] as u32) << 16) | ((p[off + 3] as u32) << 24);}
pub fn padSetU32(p: *mut[] u8, off: u32, v: u32) { p[off] = (v & 0xFF) as u8; p[off + 1] = ((v >> 8) & 0xFF) as u8; p[off + 2] = ((v >> 16) & 0xFF) as u8; p[off + 3] = ((v >> 24) & 0xFF) as u8;}
pub fn sdaRead(p: *mut[] u8) u32 { const st: u32 = p[P_SDA_STATE] as u32; var data: u32 = 0xFF; if (st == SDA_HIZ) { data = 0xFF; p[P_SDA_STATE] = SDA_IDL as u8; p[P_SDA_TXR] = 1; } else if (st == SDA_IDL) { data = 0x41; p[P_SDA_STATE] = SDA_IDH as u8; p[P_SDA_TXR] = 1; } else if (st == SDA_IDH) { data = 0x5A; p[P_SDA_STATE] = SDA_SWL as u8; p[P_SDA_TXR] = 1; } else if (st == SDA_SWL) { data = p[P_SW_LO] as u32; p[P_SDA_STATE] = SDA_SWH as u8; p[P_SDA_TXR] = 1; } else if (st == SDA_SWH) { data = p[P_SW_HI] as u32; p[P_SDA_STATE] = SDA_HIZ as u8; p[P_SDA_TXR] = 0; } return data;}
pub fn sdaWrite(p: *mut[] u8, data: u32) { if ((data & 0xFF) == 0x43) { p[P_SDA_STATE] = SDA_HIZ as u8; p[P_SDA_TXR] = 0; }}
pub fn padReadRx(p: *mut[] u8, mcd: mut Mcd, mcdRam: *mut[] u8) u32 { const ctrl: u32 = padGetU16(p, P_CTRL_LO); if (p[P_DEST] == 0) { return 0xFFFFFFFF; } if ((ctrl & CTRL_JOUT) == 0 && (ctrl & CTRL_RXEN) == 0) { return 0xFFFFFFFF; } if (p[P_DEST] == DEST_JOY as u8) { const data: u32 = sdaRead(p); if (p[P_SDA_TXR] == 0) { p[P_DEST] = 0; } return data; } if (p[P_DEST] == DEST_MCD as u8) { const data: u32 = mcd.read(mcdRam); if (!mcd.query()) { p[P_DEST] = 0; } return data; } return 0xFFFFFFFF;}
pub fn padWriteTx(p: *mut[] u8, mcd: mut Mcd, data: u32) { const ctrl: u32 = padGetU16(p, P_CTRL_LO); if ((ctrl & CTRL_TXEN) == 0) { return; } if (p[P_DEST] == 0) { const b: u32 = data & 0xFF; if (b == DEST_JOY || b == DEST_MCD) { p[P_DEST] = b as u8; if (b == DEST_JOY) { p[P_SDA_STATE] = SDA_HIZ as u8; p[P_SDA_TXR] = 1; } if (b == DEST_MCD) { mcd.reset(); } if ((ctrl & CTRL_ACIE) != 0) { padSetU32(p, P_CYC_LO, JOY_IRQ_DELAY); } } return; } if (p[P_DEST] == DEST_JOY as u8) { sdaWrite(p, data); if (p[P_SDA_TXR] == 0) { p[P_DEST] = 0; } } else if (p[P_DEST] == DEST_MCD as u8) { mcd.write(data); if ((ctrl & CTRL_ACIE) != 0) { p[P_IRQ_BIT] = 1; padSetU32(p, P_CYC_LO, 1024); return; } if (!mcd.query()) { p[P_DEST] = 0; } } else { p[P_DEST] = 0; return; } if ((ctrl & CTRL_ACIE) != 0) { p[P_IRQ_BIT] = 1; padSetU32(p, P_CYC_LO, JOY_IRQ_DELAY); }}
pub fn padHandleCtrlWrite(p: *mut[] u8, mcd: mut Mcd, value: u32) { padSetU16(p, P_CTRL_LO, value & 0xFFFF); if ((value & CTRL_JOUT) == 0) { var ctrl: u32 = padGetU16(p, P_CTRL_LO); ctrl = ctrl & (~CTRL_SLOT); padSetU16(p, P_CTRL_LO, ctrl); mcd.reset(); } if ((value & CTRL_ACKN) != 0) { var stat: u32 = padGetU16(p, P_STAT_LO); stat = stat & 0xFDC7; padSetU16(p, P_STAT_LO, stat); var ctrl: u32 = padGetU16(p, P_CTRL_LO); ctrl = ctrl & (~CTRL_ACKN); padSetU16(p, P_CTRL_LO, ctrl); }}
pub fn padReadStat(p: *mut[] u8) u32 { return padGetU16(p, P_STAT_LO) | 0x07;}
pub fn padRead32(p: *mut[] u8, mcd: mut Mcd, mcdRam: *mut[] u8, off: u32) u32 { match (off) { 0 { return padReadRx(p, mcd, mcdRam); } 4 { return padReadStat(p); } 8 { return padGetU16(p, P_MODE_LO); } 10 { return padGetU16(p, P_CTRL_LO); } 14 { return padGetU16(p, P_BAUD_LO); } _ { return 0; } }}
pub fn padRead16(p: *mut[] u8, mcd: mut Mcd, mcdRam: *mut[] u8, off: u32) u32 { return padRead32(p, mcd, mcdRam, off) & 0xFFFF;}
pub fn padRead8(p: *mut[] u8, mcd: mut Mcd, mcdRam: *mut[] u8, off: u32) u32 { return padRead32(p, mcd, mcdRam, off) & 0xFF;}
pub fn padWrite32(p: *mut[] u8, mcd: mut Mcd, off: u32, val: u32) { match (off) { 0 { padWriteTx(p, mcd, val); } 8 { padSetU16(p, P_MODE_LO, val & 0xFFFF); } 10 { padHandleCtrlWrite(p, mcd, val); } 14 { padSetU16(p, P_BAUD_LO, val & 0xFFFF); } _ {} }}
pub fn padWrite16(p: *mut[] u8, mcd: mut Mcd, off: u32, val: u32) { padWrite32(p, mcd, off, val & 0xFFFF);}
pub fn padWrite8(p: *mut[] u8, mcd: mut Mcd, off: u32, val: u32) { padWrite32(p, mcd, off, val & 0xFF);}
pub fn padUpdate(p: *mut[] u8, ic: *mut[] u32, cop0: *mut[] u32, cyc: u32) { var cd: u32 = padGetU32(p, P_CYC_LO); if (cd == 0) { return; } if (cd > cyc) { padSetU32(p, P_CYC_LO, cd - cyc); return; } padSetU32(p, P_CYC_LO, 0); if (p[P_IRQ_BIT] != 0) { var stat: u32 = padGetU16(p, P_STAT_LO); stat = stat | STAT_IRQ7; padSetU16(p, P_STAT_LO, stat); p[P_IRQ_BIT] = 0; } irqRaise(ic, cop0, IC_JOY);}