A Playstation One (PS1) emulator fully written in Jam
3.8 kB
127 lines
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Raphael Amorim
3
4// Two registers live at 0x1F801070..0x1F801077:
5// 0x00 I_STAT status - set by devices raising IRQs, cleared by
6// writing 0 to the matching bit
7// 0x04 I_MASK mask - when (stat & mask) != 0, COP0 CAUSE bit IP2
8// (= 0x00000400) is asserted, which causes the CPU to
9// take an external interrupt the next time it samples
10// pending IRQs at fetch time.
11//
12// State buffer (two u32 entries):
13// [0] I_STAT
14// [1] I_MASK
15
16const { Vec } = import("std/collections");
17
18pub const IC_VBLANK: u32 = 0x001;
19pub const IC_GPU: u32 = 0x002;
20pub const IC_CDROM: u32 = 0x004;
21pub const IC_DMA: u32 = 0x008;
22pub const IC_TIMER0: u32 = 0x010;
23pub const IC_TIMER1: u32 = 0x020;
24pub const IC_TIMER2: u32 = 0x040;
25pub const IC_JOY: u32 = 0x080;
26pub const IC_SIO: u32 = 0x100;
27pub const IC_SPU: u32 = 0x200;
28pub const IC_LP_PIO: u32 = 0x400;
29
30const CAUSE_IP2: u32 = 0x00000400;
31const C0_CAUSE: u32 = 13;
32
33pub fn createIrq() Vec(u32) {
34 return Vec(u32).filled(0, 16);
35}
36
37pub fn irqReevaluate(ic: *mut[] u32, cop0: *mut[] u32) {
38 const pending: u32 = ic[0] & ic[1];
39 var cause: u32 = cop0[C0_CAUSE];
40 if (pending != 0) {
41 cause = cause | CAUSE_IP2;
42 } else {
43 cause = cause & (~CAUSE_IP2);
44 }
45 cop0[C0_CAUSE] = cause;
46}
47
48pub fn irqRead32(ic: *mut[] u32, off: u32) u32 {
49 match (off) {
50 0x00 { return ic[0]; }
51 0x04 { return ic[1]; }
52 _ { return 0; }
53 }
54}
55
56pub fn irqRead16(ic: *mut[] u32, off: u32) u32 {
57 match (off) {
58 0x00 { return ic[0] & 0xFFFF; }
59 0x02 { return (ic[0] >> 16) & 0xFFFF; }
60 0x04 { return ic[1] & 0xFFFF; }
61 0x06 { return (ic[1] >> 16) & 0xFFFF; }
62 _ { return 0; }
63 }
64}
65
66pub fn irqRead8(ic: *mut[] u32, off: u32) u32 {
67 match (off) {
68 0x00 { return ic[0] & 0xFF; }
69 0x01 { return (ic[0] >> 8) & 0xFF; }
70 0x02 { return (ic[0] >> 16) & 0xFF; }
71 0x03 { return (ic[0] >> 24) & 0xFF; }
72 0x04 { return ic[1] & 0xFF; }
73 0x05 { return (ic[1] >> 8) & 0xFF; }
74 0x06 { return (ic[1] >> 16) & 0xFF; }
75 0x07 { return (ic[1] >> 24) & 0xFF; }
76 _ { return 0; }
77 }
78}
79
80pub fn irqWrite32(ic: *mut[] u32, cop0: *mut[] u32, off: u32, val: u32) {
81 match (off) {
82 0x00 { ic[0] = ic[0] & val; }
83 0x04 { ic[1] = val; }
84 _ {}
85 }
86 irqReevaluate(ic, cop0);
87}
88
89pub fn irqWrite16(ic: *mut[] u32, cop0: *mut[] u32, off: u32, val: u32) {
90 const v: u32 = val & 0xFFFF;
91 match (off) {
92 0x00 { ic[0] = ic[0] & (0xFFFF0000 | v); }
93 0x02 { ic[0] = ic[0] & (0x0000FFFF | (v << 16)); }
94 0x04 { ic[1] = (ic[1] & 0xFFFF0000) | v; }
95 0x06 { ic[1] = (ic[1] & 0x0000FFFF) | (v << 16); }
96 _ {}
97 }
98 irqReevaluate(ic, cop0);
99}
100
101pub fn irqWrite8(ic: *mut[] u32, cop0: *mut[] u32, off: u32, val: u32) {
102 const v: u32 = val & 0xFF;
103 match (off) {
104 0x00 { ic[0] = ic[0] & (0xFFFFFF00 | v); }
105 0x01 { ic[0] = ic[0] & (0xFFFF00FF | (v << 8)); }
106 0x02 { ic[0] = ic[0] & (0xFF00FFFF | (v << 16)); }
107 0x03 { ic[0] = ic[0] & (0x00FFFFFF | (v << 24)); }
108 0x04 { ic[1] = (ic[1] & 0xFFFFFF00) | v; }
109 0x05 { ic[1] = (ic[1] & 0xFFFF00FF) | (v << 8); }
110 0x06 { ic[1] = (ic[1] & 0xFF00FFFF) | (v << 16); }
111 0x07 { ic[1] = (ic[1] & 0x00FFFFFF) | (v << 24); }
112 _ {}
113 }
114 irqReevaluate(ic, cop0);
115}
116
117pub fn irqRaise(ic: *mut[] u32, cop0: *mut[] u32, lines: u32) {
118 ic[0] = ic[0] | lines;
119 var b: u32 = 0;
120 while (b < 11) {
121 if ((lines & (1 << b)) != 0) {
122 ic[2 + b] = ic[2 + b] + 1;
123 }
124 b = b + 1;
125 }
126 irqReevaluate(ic, cop0);
127}