trapezoid 0.1.2

A PSX emulator, backed by vulkano for rendering
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
<!-- <p align="center">
  <a href="https://github.com/Amjad50/trapezoid"><img alt="trapezoid" src="images/logo.svg" width="60%"></a>
  <p align="center">PSX emulator in <em>Rust</em></p>
</p> -->
# Trapezoid

[![Build status](https://github.com/Amjad50/trapezoid/workflows/Rust/badge.svg)](https://actions-badge.atrox.dev/Amjad50/trapezoid/goto)
[![dependency status](https://deps.rs/repo/github/Amjad50/trapezoid/status.svg)](https://deps.rs/repo/github/Amjad50/trapezoid)
[![license](https://img.shields.io/github/license/Amjad50/trapezoid)](./LICENSE)
[![Crates.io trapezoid](https://img.shields.io/crates/v/trapezoid)](https://crates.io/crates/trapezoid)

**trapezoid** is a [PSX/PS1](https://en.wikipedia.org/wiki/PlayStation_(console)) emulator built from scratch using [Rust].

This is a personal project for fun and to experience emulating hardware and connecting them together.

## Showcase

<img src="assets/psx.png" alt="demo" width="600"/>

## Building and installation

### Installing
You can install `trapezoid` from [`crates.io`](https://crates.io/crates/trapezoid) using `cargo`:
```
cargo install trapezoid
```

### Building
If you want to experience the latest development version, you can build `trapezoid` yourself.
```
cargo build --release
```
> The emulator will be slow without optimization, that's why we have `opt-level = 2` in `debug` profile.

## Emulator core
The emulator core is implemented as a library in [`trapezoid-core`], this library is the emulator core, and contain
all the components. You can easily take the core and build a frontend around it, or use it as a server.

Check the [`trapezoid-core`] for more info and documentation.

## Frontend

### Controls
The Frontend implementations has its own controls mapping, this can be configured
if you decide to use [`trapezoid-core`] directly

#### Keyboard
| keyboard  | PSX controller |
| --------- | -------------- |
| Enter     | Start          |
| Backspace | Select         |
| Num1      | L1             |
| Num2      | L2             |
| Num3      | L3             |
| Num0      | R1             |
| Num9      | R2             |
| Num8      | R3             |
| W         | Up             |
| S         | Down           |
| D         | Right          |
| A         | Left           |
| I         | Triangle       |
| K         | X              |
| L         | Circle         |
| J         | Square         |

### Debugging
`trapezoid` has a built-in powerfull debugger to help debug games and access to data.

This is a CLI based debugger, it can be activated by pressing `/ (forward slash)` key, it will pause the emulation and activates
the debugger.

You will get a prompt:
```text
CPU>
```
The debugger uses `rustyline` and has auto completion

#### Debugger addressing and variables
Anywhere the term `<addr>` is used, it can be a hex address, or a variable name.

There are two variable types:
- start with `$` are registers, for example `$t0` is the register `t0`, etc...
- start with `@` are special hardware registers, like `@TIMER0_TARGET` which is the timer 0 target register.

You can know these registers using the tab completion. Just start typing `$` or `@` and press tab.

#### Debugger commands

##### `h`
Prints the help message
```txt
CPU> h
h - help
r - print registers
c - continue
s - step
so - step-over
su - step-out
tt - enable trace
tf - disbale trace
stack [0xn] - print stack [n entries in hex]
bt/[limit] - print backtrace [top `limit` entries]
b <addr> - set breakpoint
rb <addr> - remove breakpoint
bw <addr> - set write breakpoint
rbw <addr> - remove write breakpoint
br <addr> - set read breakpoint
rbr <addr> - remove read breakpoint
lb - list breakpoints
m[32/16/8] <addr> - print content of memory (default u32)
p <addr>/<$reg> - print address or register value
i/[n] [addr] - disassemble instructions
hook_add <cmd[;cmd]> - add hook/s commands
hook_clear - clear all hooks
hook_list - list all hooks
hook_setting [<break_type>[=true/false]] - change when the hooks are executed
```

##### `r`
Prints the registers (example from a random game in a random point)
```txt
CPU> r
Registers:
pc: 8004A648    at: 80060000
hi: 00000000    lo: 009941F4
v0: 00003178    s0: 54042275
v1: FFFFFFFF    s1: 0000015B
a0: 00003179    s2: 0000008F
a1: 00008000    s3: 00000000
a2: 00000000    s4: 00000002
a3: 00000000    s5: 00000000
t0: 39937A40    s6: 00000000
t1: 00000000    s7: 00000000
t2: 00000000    t8: 00000000
t3: F9A700FE    t9: 801FFEE0
t4: 0000F159    k0: 8004A600
t5: 801A1D9C    k1: 00006418
t6: 00000001    gp: 8005F17C
t7: 00000003    sp: 801FFE78
fp: 801FFFF8    ra: 8004A540
```

##### `c`
Continue the emulation

##### `s`
Executes one instruction and then stops

##### `so`
Executes one instruction and then stops, if the instruction is a function call, it will execute the function and stop at the next instruction
after the call.

For example, if the code was like this
```asm
0x1000: jal 0x8004A648
0x1004: _nop            ; delay slot
0x1008: nop
```
and the PC was at `0x1000`, then `so` will execute `jal` and stop at `0x1008`

##### `su`
Will continue the emulation until the current function returns.

It will stop on the instruction after the function call.

##### `tt`
Enable trace, this will print the executed instructions, this is very heavy as it prints all instruction and will reduce the emulation speed.

Example output:
```txt
CPU> tt
Instruction trace: true
CPU> c
80000080: lui k0, 0x0000
80000084: addiu k0, k0, 0x0C80
80000088: jr k0
8000008C: _nop
00000C80: nop
00000C84: nop
00000C88: addiu k0, zero, 0x0100
00000C8C: lw k0, 0x0008(k0)
00000C90: nop
00000C94: lw k0, 0x0000(k0)
00000C98: nop
...
```

##### `tf`
Disable trace

##### `stack`
Print the stack content, you can specify the number of entries to print, default is 10
```txt
CPU> stack
Stack: SP=0x801FFC90
    8001273C
    8001273C
    00000002
    00000000
    00000000
    800C82AC
    00000012
    00000001
    00000000
    800143AC
```

##### `bt`
Print the backtrace, you can specify the number of entries to print, default, whole backtrace

For example, here we are in `59` level of the backtrace, but we only print the top 10 entries
```txt
CPU> bt/10
#59:      80012E24
#58:      000019B8
#57:      00000E28
#56:      8004AAB0
#55:      8004A888
#54:      8004AAB0
#53:      8004A888
#52:      000019B8
#51:      00000E28
#50:      000019B8
```

The addresses here are the return addresses, for example, looking at the first one `80012E24`, lets print the 2 instructions before it.

We will we have the call instruction, the delay slot, and the return address is what's in the backtrace.
```txt
CPU> i 80012E1C
0x80012E1C: jal 0x0004AF1 => 0x80012BC4
0x80012E20: _addiu a0, zero, 0xFFFF
0x80012E24: lui v0, 0x8006
```
This means that right now we are inside the function `0x80012BC4`

##### `b`
Set a breakpoint on address, the address is in hex, the `0x` prefix is optional
This will trigger when the address is executed
```txt
CPU> b 80012E24
Breakpoint added: 0x80012E24
```

##### `rb`
Remove a breakpoint
```txt
CPU> rb 80012E24
Breakpoint removed: 0x80012E24
```

##### `bw`
Set a write breakpoint on address, the address is in hex, the `0x` prefix is optional
This will trigger when the address is written to
```txt
CPU> bw 80012E24
Write breakpoint added: 0x80012E24
```

##### `rbw`
Remove a write breakpoint
```txt
CPU> rbw 80012E24
Write breakpoint removed: 0x80012E24
```

##### `br`
Set a read breakpoint on address, the address is in hex, the `0x` prefix is optional
This will trigger when the address is read from (also execute, since we are reading from this address)
```txt
CPU> br 80012E24
Read breakpoint added: 0x80012E24
```

##### `rbr`
Remove a read breakpoint
```txt
CPU> rbr 80012E24
Read breakpoint removed: 0x80012E24
```

##### `lb`
List all breakpoints
```txt
CPU> lb
Breakpoint: 0x80012E24
Write Breakpoint: 0x80012E24
Read Breakpoint: 0x80012E24
```

##### `m`
Print the memory content, you can specify the size of the read, and the number of times to read, default is 1 u32

```txt
CPU> m 80012E24
0x80012E24: 0x3C028006
CPU> m32 80012E24
0x80012E24: 0x3C028006
CPU> m32/4 80012E24
0x80012E24: 0x3C028006
0x80012E28: 0x8C427FB0
0x80012E2C: 0x00000000
0x80012E30: 0x1440FFED
CPU> m8/4 80012E24
0x80012E24: 0x06
0x80012E25: 0x80
0x80012E26: 0x02
0x80012E27: 0x3C
CPU> m16/4 80012E24
0x80012E24: 0x8006
0x80012E26: 0x3C02
0x80012E28: 0x7FB0
0x80012E2A: 0x8C42

CPU> m @GPU_STAT        ; reading gpu status register easily
0x1F801814: 0x5404220A
```

##### `p`
Print the value of a register or memory address

This is only useful for cpu registers, at least for now, there is no expression evaluation
```txt
CPU> p $t0
0x00005688
CPU> p @GPU_STAT
0x1F801814
CPU> p 12345678
0x12345678
```

##### `i`
Disassemble instructions, you can specify the number of instructions to disassemble, default is 1 at the current location of `PC`

```txt
CPU> i
0x80000084: addiu k0, k0, 0x0C80
CPU> i/10
0x80000084: addiu k0, k0, 0x0C80
0x80000088: jr k0
0x8000008C: _nop
0x80000090: nop
0x80000094: nop
0x80000098: nop
0x8000009C: nop
0x800000A0: lui t0, 0x0000
0x800000A4: addiu t0, t0, 0x05C4
0x800000A8: jr t0
CPU> i 800000A0
0x800000A0: lui t0, 0x0000
```

#### Hooks

The debugger allows to create `hooks`, these are commands, any of the above commands which will execute on certain events.
The events can be configured using `hook_setting` command.
```txt
CPU> hook_setting
Hooks will be executed on the following breakpoints:
  step: false
  step_over: false
  step_out: false
  instruction_breakpoint: false
  read_breakpoint: false
  write_breakpoint: false
```

By default, hooks aren't bound to any event.

But can be set using `hook_setting` to modify when to execute them.
```txt
CPU> hook_setting step,instruction_breakpoint=true,step_out=false
Hooks will be executed on the following breakpoints:
  step: true
  step_over: false
  step_out: false
  instruction_breakpoint: true
  read_breakpoint: false
  write_breakpoint: false
```
This will enable hooks on `step` and `instruction_breakpoint` events, and disable them on `step_out` event, and leave the rest as is.

##### `hook_add`
We can add hooks by `hook_add`, which will be executed when the event is triggered.
```txt
CPU> hook_add r;i/20
Hook added: r
Hook added: i/20
```
This adds two commands to execute on an event, `r` and `i/20`, `r` will print the registers, and `i/20` will disassemble 20 instructions from `PC`.

##### `hook_clear`
Clear all hooks

##### `hook_list`
List all hooks

```txt
CPU> hook_list
r
i/20
```

#### VRAM

We can view the raw vram state, which you can think of as an image of 1024x512 pixels

This can be triggerd with the keyboard button `v`.

![vram](assets/psx_vram.png)

### Contributions and TODO
Check the [`trapezoid-core`] for more information about TODO items related to the emulator.

Also check the [issues](https://github.com/Amjad50/Trapezoid/issues).

Really appreciate any contributions. Thanks!


### License
This project is under [MIT](./LICENSE) license.

NES is a product and/or trademark of Nintendo Co., Ltd. Nintendo Co., Ltd. and is not affiliated in any way with Plastic or its author

### References
Most of the documentation for PSX components can be found in the [consoledev website](https://psx-spx.consoledev.net/)

[Rust]: https://www.rust-lang.org/
[`trapezoid-core`]: ./trapezoid-core/README.md