psx-emu (PS1 Emulator) banner

psx-emu (PS1 Emulator)

7 devlogs
40h 7m 20s

Updated Project: I have improved the accuracy of the emulator and added new features:

  • Physical controller support (includes DualShock emulation/controller vibration)
  • Implemented the MDEC (video decoder)
  • Implemented XA-ADPCM audio
  • f…

Updated Project: I have improved the accuracy of the emulator and added new features:

  • Physical controller support (includes DualShock emulation/controller vibration)
  • Implemented the MDEC (video decoder)
  • Implemented XA-ADPCM audio
  • fixed lots of bugs
    These improvements have greatly improved game compatibility and mean the emulator is (nearly) complete.
    emulator for the original Sony PlayStation (or PS1/PSX). Nearly finished, and can run a decent number of games.
Demo Repository

Loading README...

Luke

Shipped this project!

Hours: 40.12
Cookies: 🍪 815
Multiplier: 20.32 cookies/hr

I have improved the accuracy of the emulator and added new features:

  • Physical controller support (includes DualShock emulation/controller vibration)
  • Implemented the MDEC (video decoder)
  • Implemented XA-ADPCM audio
  • fixed lots of bugs
    These improvements have greatly improved game compatibility and mean the emulator is (nearly) complete.
Luke

Since flavortown is nearly over, I spent a heap of time on this over the past few days to get it read to ship. I tried porting the emulator to WASM, but had issues reading files (needed for the BIOS and game disc) so had to abandon that. Instead, I worked on XA-ADPCM audio. XA-ADPCM is compressed audio that is decoded and played directly by the CDROM drive, and is used by a lot of games for videos and sometimes background music.

After filtering XA-ADPCM CD sectors from regular data sectors, decoding the audio is relatively simple, since the ADPCM pseudocode is very similar to whats used in the SPU (the PS1s audio chip). The main difference is that XA audio is played at 37800hz or 18900hz instead of the standard 44100hz, meaning the audio needs to be resampled to 44100hz, which can be complicated. After a couple hours I was getting some recognisable sound, but had this weird clipping noise (very similar to the CDDA issue I wrote about). After many more hours of debugging and fixing my decoder, I verified that it was producing the correct samples, but realised the sample buffer wasn’t being read fully before being overwritten by a new XA sector. To fix this I ended up adjusting my CD read timings so that the XA buffer could be fully consumed before a new sector was read (tbh I should have realised this earlier, I wasted hours debugging this).

I wasted even more time trying to update my project to the 2024 edition of rust (it turns out my WSL installation of rust was messing things up), but now FMVs have audio and every game has music! This is basically the last feature I needed to implement for my emulator (besides memory cards which I might not do), so its pretty much done now (just minor bugs to fix).

Video: Tony Hawks Pro Skater intro

0
Luke

So I decided to properly implement DualShock controllers (so controller vibration would work), and emulating the controller itself was surprisingly simple: the DualShock has a config mode with 16 commands, which are all pretty simple (stuff like SetLedState, GetLedState and some other commands that reply with arbitrary data). The most important command is Get/SetRumbleProtocol, which specifies which bytes in the communication sequence are used to set the rumble motor intensity (e.g 3rd TX byte is for the small motor, 4th TX byte is for the large motor). This was all pretty easy to get working, but I still had to implement controller vibration in the frontend (so the physical controller would actually vibrate).

This was significantly more painful because the input library im using, Gilrs, is garbage - its not well maintained and has a horrible, poorly-documented API (unfortunately its only the only option besides SDL3 bindings). For some reason I couldn’t get vibration to work at all - when I tried to build their example program I found that even that hadn’t worked since 2019 lmao. So after hours of trying to figure out what was going on I found out that the game engine Bevy also used Gilrs and had working controller vibration. Looking at their code I found a few undocumented things I wasn’t doing properly (e.g if the vibration effect gets dropped the vibration is cancelled, adding a repeat() to each effect), which helped somewhat but it turns out gilrs just doesnt work with the controller I was using anymore lol (DualShock 4), but after running it through a virtual controller it finally worked.

TL;DR

  • Implemented DualShock controller emulation
  • Getting controller vibration working in the frontend was super annoying because of Gilrs
  • I also finally fixed the aspect ratio so games dont look stretched anymore

Next I might either port my emulator to the web with WASM or start working on XA-ADPCM.
Image: (I couldn’t find a better way of showing vibration working) Gran Turismo with logs showing rumble (controller vibration) is working (M1 is the large/strong motor, M2 is the small/weak motor).

Attachment
0
Luke

I implemented 15/24bit colour modes for MDEC decoding, meaning MDEC emulation is now complete! While I got block decoding working in my previous devlog, decoding colour macroblock is significantly more complicated, as it involves decoding multiple blocks (2 for chrominance, 4 for luminance), and then scaling and combining them for an RGB output. This took a while to get working properly, since I had to fix bugs in how I was writing to the quant and scale tables and how I was reading from the input FIFO, but now that its done a heap of games have working FMVs (including Final Fantasy VII and the Resident Evil games).

One issue with my MDEC implementation is that I dont handle timing correctly, so the decoding happens instantly which can break a few games (notably Policenauts, an FMV-based game made by Hideo Kojima), but I’m not going to worry about it too much since it would require re-architecting my whole emulator to implement and only affects a small number of games. Another issue is that some games freeze during FMVs (Tony Hawks Pro Skater 3 does this), but I think this might be related to the CDROM drive or my lack of XA audio emulation rather than the MDEC itself.

Next im either going to implement XA-ADPCM audio so the FMVs have sound, or properly implement DualShock controller vibration.

Video: Cool Boarders 3 intro FMV
Image 1: Resident Evil
Image 2: Policenauts

Attachment
Attachment
0
Luke

I started work on the MDEC (Macroblock Decoder / video decoder), and It’s going pretty well so far. Emulating it is pretty simple since it only has three commands, and all the decoding logic is documented as pseudocode on PSX-SPX, so it was straightforward to translate that to rust code.

So far I’ve only implemented decoding 4bit and 8bit colour-depth (monochrome) frames, and I ran into an issue where the test I was using was either outputting garbage or a white square. After A few hours of debugging I eventually figured out that this was causing by:

  1. Not writing to the quant tables and scale table properly (I wasn’t converting u32 input words into u8s and i16s properly)
  2. Not clipping the output to 9bit range correctly

After fixing those two issues I’m now decoding video properly! Unfortunately not a single PS1 game actually uses the 4bit/8bit modes, so I cant actually play any videos yet :/. At least now I can run the 4bit/8bit MDEC tests (which just prints a tiny heart to the screen).

Next I’m going to work on the 15bit and 24bit colour modes so I can actually play FMVs in my emulator (which should fix the majority of games that have issues atm).

Image: the 8bit MDEC test running correctly

Attachment
0
Luke

Still haven’t had a lot of time to work on this because of school but I have made a bit of progress.

I finally figured out why CD audio was making a buzzing noise, it was a really stupid bug lol: When I was reading the audio sector, I wasn’t including the first 12 bytes, since in data sectors those bytes aren’t included in reads, but CDDA audio uses the entire sector, so I was skipping 12 bytes of every 2352 byte sector, causing a high-pitched buzzing noise. I thought it was something complicated like an issue with volume clamping or the SPU (PS1’s sound chip), but no it was super simple and was just a 4 line fix lol.

Another stupid bug that I’ve been stumped by for months is this issue where a game will start sending garbage data to the GPU, overwriting all the textures. Again, I thought it had to be something super complicated, so I combed through all my CDROM code to try and find an issue with how I was reading data from the disc, and I found a few minor bugs but it didn’t fix what I was trying to fix (I was stuck on this for ages lol). I Also looked at the DMA controller but still nothing (although I did find some clues).

By this point its 4am so I went to bed, but in the morning I discovered the actual cause of the bug, and it was literally two characters that were causing this whole thing. In the GPU, I was storing the length of the transfer in a u16 instead of a u32, so if the transfer length was two large it would just truncate the value to 16 bits and the GPU would go out of sync with the DMAC, causing it to interpret DMA data as GPU commands, corrupting VRAM. To fix this I just changed it from u16 to u32. I stayed up all night trying to fix this and the fix was just 2 characters >:(

This fixing this one issue fixed most of the games that weren’t working before (to some degree at least)! Next I’m going to work on the video decoder (MDEC).

Attachment
1

Comments

Luke
Luke about 2 months ago

Things i couldn’t add because it wouldn’t let me post the full devlog even though it was under 2000 chars:

  • One of the reasons I thought it was a CDROM issue for so long was that Wipeout 2097 kept printing CDROM read errors which I thought was related to this
  • A lot of bugs are super agonising like this in emulators because there are so many complicated, interconnected systems that can cause issues and its very difficult to debug them when something goes wrong.
  • Some of the games that have been fixed include: Spyro, Gran Turismo 1, Wipeout 2097, Quake II, Colin McRae Rally, and Driver (gets to the menu)
Luke

I haven’t really made much progress over the holidays (lack of motivation), but I have gotten controllers almost fully working! This means instead of using my keyboard to use the emulator I can use an actual controller (e.g a dualshock 4).

Getting basic controller input was pretty simple, although it took a while since i was a bit rusty from doing basically no coding since SoM. Initially I made Gilrs (the gamepad input library) select the first controller that connects to receive from, but for some reason my computer has a non-existent gamepad called HID-compliant game controller which always connects first, so annoyingly I had to hardcode it to not connect to controllers with that name :/.

Getting analog input to work was a bit more complicated, as in the emulator i was emulating the digital controller (which doesnt have joysticks). I decided to instead emulate the dual analog joypad (which was only on sale for a year before it was replaced with the dualshock 1), which has two joysticks but doesn’t have vibration motors. I chose not to emulate the dualshock 1 because it uses a different communication sequence which can stop games from booting if not implemented perfectly.

After that i wanted to start improving the CDROM drive emulation - in particular I wanted to improve CDDA audio sound quality (there is a buzzing noise in the background of every CDDA track for some reason), and get the BIOS CD player/audio visualiser working. For now I have hacked in support for the CD player by making the bios think all CDs are audio CDs. To get the audio visualiser working I needed to implement a weird edge case where 16-bit writes to the DMA controller are treated like 32-bit writes (the BIOS writes 16 bits when it should be writing 32 bits). Getting all this working should hopefully make debugging CDDA audio easier.

Video: the BIOS CD player / audio visualiser (i’m also using a controller in this clip, but you cant see that obviously).

1

Comments

chefpenguino
chefpenguino 3 months ago

woah this is really cool

Luke

I haven’t had a chance to work on my emulator for about 3 months (since SoM finished), so im hoping to make progress towards actually finishing this emulator over the winter holidays (you can’t really ever finish an emulator though - you can always improve the accuracy).
\n

What im hoping to get done:

  • physical controller support
  • analog/dualshock controller emulation
  • improving the accuracy of the CDROM drive
  • FMV decoding (MDEC)
  • CD-XA audio
  • memory cards (maybe - there super painful to emulate because of timing quirks)
Attachment
0