emulator for the original Sony PlayStation (or PS1/PSX). Still a WIP, currently it can only run a few games
daw
Check their projects out: Crawl & Brawl, Crawl & Brawl Godot
Once you ship this you can't edit the description of the project, but you'll be able to add more devlogs and re-ship it as you add new features!
I finally started work on the GTE, and it turns out it's a lot worse to emulate that I thought because:
<br/>
- Basically the only documentation is an appendix of a sony manual about using the GTE
<br/>
- the tests suck. One of them is closed source and doesn't tell you why you're failing (it also takes a billion years to run because it uses it's own software emulation of the GTE to test against) and the other one doesn't cover all the edge cases
<br/>
- it uses fixed point maths with different fractions for different registers / intermediary results and it isn't made clear which variables use what fractional bits.
<br/>
- it has undocumented quirks that you just have to figure out by trial and error or by looking at other emulators
<br/>
- it has hardware bugs
<br/>
- You have to emulate it's weird 'fast' division algorithm
<br/>
tl;dr i HATE the GTE and it SUCKS.
<br/>
Anyway so the reason it was taking me so long to start working on the GTE was because I was going to start by doing opcode 0x01 (RTPS which is used for perspective transformation), which just happens to be by far the harded instruction in the GTE. Once I moved to working on some easier opcodes like SQR (computes the square of a vector), OP (the mnemonic is a mistranslation - it computes the cross product of two vectors), and GPF/GPL (general purpose interpolation) I started getting the hang of how things worked on the GTE. Soon I had to move back to working on RTPS (because the tests make you test it really early on so you need it to work before testing the other opcodes), and even with my knowledge gained from doing easier opcodes it was still a massive pita has taken up like 90% of my time working on the GTE so far. It has a heap of quirks and edge cases, but after like a week of trying to get it working I had it passing most tests (it's still failing a flag test but i don't know why since the test doesn't tell you why it's failing).
<br/>
But after getting RTPS (and RTPT which is just RTPS repeated 3 times) into a mostly working state i'm starting to see 3D graphics. I still need to implement some more opcodes (most notably MVMVA or Multiply vector by matrix and vector addition) before 3D games start working, but I can see parts of the menu in crash bandicoot and the 3D background in castlevania, also some 3D homebrew for testing the GTE is starting to work now too.
Video: hello_cube_tex.exe
from nolibgshelloworlds
I've just been working on more GPU stuff while I try and wrap my head around the GTE, so heres what I've done:
<br/>
- implemented texture blending / colour modulation. This means that textured polygons and rectangles can be coloured dynamically, which a lot of 3D games like crash bandicoot make extensive use of. It was a bit of a pain to get right since I had a bug where I was converting it from 24 bit colour to 15 bit colour wrong or something, and i'm still not sure if it's fully accurate, but it looks good enough on most test roms.
<br/>
- I now correctly set the semi-transparency of textured GPU primitives depending on whether or not bit 15 of the texel is set. This fixes the cursor in the bios menu being semi-transparent when it shouldn't be.
<br/>
- I fixed a really strange bug where rectangles moving off the edge of the screen had this weird wrapping effect (i don't really know how to explain it). It turns out that if a rectangle was slightly outside the drawing area i just wasn't render it at all, when the correct behaviour would be to only render the portion of it that was within the drawing area.
<br/>
Next I want to actually start working on the GTE, and I found some more documentation which might help with that.
<br/>
image: the benchmark.exe
test rom demonstrating colour modulation in my emulator.
Not much has happened since my last devlog since I was on holiday for all of last week, but I've still made some progress:
<br/>
- Started work on the GTE (Geometry Transformation Engine). The GTE is the PS1's 3D maths co-processor, and it has a bunch of registers (64 iirc) and some instructions which do stuff like manipulating vectors, matrices, colours and light, as well as performing perspective transformations on vectors. It's pretty poorly documented and designed, which makes it a massive pain to emulate, so for now i've only implemented the registers (which were still a pain to get working). Once i get a few GTE instructions working i should be able to play 3D games.
<br/>
- implementing GTE registers (as well as reading and writing to them) actually fixed some crashes I was getting in Castlevania and Puzzle Bobble. It turns out both of these games were using an SDK function called SquareRoot12
, which needed reading from GTE registers to work, otherwise it would crash trying to read unmapped memory.
<br/>
- I also implemented semi-transparency for the GPU, which was actually surprisingly easy, and made Megaman X4 look a lot better.
<br/>
- I also fixed a really annoying graphical bug where some quads (on the PS1 a quad is a rectangle made out of two polygon primitives) had like a chunk taken out of them, so there were strange triangle-shaped holes in some of the textures for some games. After hacking together a wireframe view for polygons I could see that for some quads had their vertices mixed up, which was causing them to have a 'butterfly' shape instead of a rectangle shape, causing parts of the texture to not be rendered. It turns out the issue was just that I was swapping the vertices around the wrong way when rendering the polygons, so it was a one line fix lol.
<br/>
image: Mortal Kombat II using my hacked-together polygon wireframe view (for some reason this game renders the left side of the screen using rect primitives and the right side with polygon quad primitives). You can see that some quads have a 'butterfly' shape with the triangle crossing over each other instead of having a rectangular shape like other quads.
I've made even more progress since the last devlog (and i really need to start doing shorter commits/devlogs lol), so instead of listing every change i'm just gonna give a general summary of whats happened (full details can be seen here: https://github.com/Iamhere345/psx-emu/commit/ff5a46be062c4e9e7de45d30b9ee7606d544dcc9).
A heap of games work now! This includes Megaman X4, Mortal Kombat II, Puzzle Bobble, and Castlevania: Symphony of the Night. Additionally Ridge Racer and Crash Bandicoot make it to the menus, but need the GTE (the PS1's 3D maths coprocessor) to make it ingame. Also Silent Hill and Driver are showing signs of life but still need some more work to get to the menus.
I managed to fix some pretty big CDROM bugs (which is why all these games work now when before they were getting stuck reading the same CD sector over and over again). I now properly handle reading the CDROM data FIFO (my previous implementation of this was causing the games to get stuck in the read loop), and fixed a bug where an extra interrupt was being sent after the pause command was sent to the CDROM.
I also figured out that i was decoding GPU control commands (GP1 cmds) completely wrong, and so once I fixed that I had to implement a heap of them. This also allowed be to un-stub the GPUSTAT register since uses the results from a lot of GP1 commands, however doing this seemed to make games freeze (?) so I ended up hardcoding it to 0x1C000000 again. On the GPU side I also implemented the drawing offset command, which fixed double buffering in a few games and also fixed the opening screens of Crash Bandicoot.
The last main bug i fixed was making it so timer IRQs are always fired, even if the IRQ flag is still set. I'm still not handling timer IRQs correctly, but this stops Crash Bandicoot from freezing on the first screen.
P.S i put a few more screenshots in the repository README since i can only put one here.
I've made a heap of progress since the last devlog, and now I can boot a heap of games!
There are still a lot of games that i've tested which hang reading the same CD sector over and over again, which is a very vague issue but I think might all be caused by the same problem so im going to look into that next.
Games that I've tested:
- Mortal Kombat II - works with minor graphical issues
- ridge racer - gets to the menus but crashes just before getting ingame
- puzzle bobble - gets to the menus but crashes just before getting ingame
- crash bandicoot - loads textures and shows the copyright screen, then hangs
- driver - hangs reading the same CD sector over and over again
- megaman X4 - hangs reading the same CD sector over and over again
- Castlevania Symphony of the Night - hangs reading the same CD sector over and over again
- Metal Gear Solid - hangs on a black screen, don't know what it's doing
- Gran Turismo 2 (Arcade) - crashes trying to read an MDEC (image decoder) register to play an FMV
Changes made:
- stub the SPU (sound processing unit) i.e. i handle reads/writes to SPU registers but nothing more than that.
- fixed a scheduler bug that broke the timers (i'm pretty sure this was the reason why most games weren't booting at all, and the fix was one line lol)
- implemented line and polyline GPU primitives (a polyline is just a series of lines sent one after another, but bc of the way i set up my GPU state machine it was a pita the implement)
- implemented / stubbed a few CDROM commands, which were necessary to get more games booting
So next im gonna try and fix that CD sector looping issue, but anyway heres a video of ridge racer working in my emulator:
Overhauled my UI using egui_dock
to organise my debugging views into docking windows. Looks a lot better imo, and now i can see everything without having to constantly be moving windows around. (I would show what it looked like before but I can only upload one image)