Not directly related to the course, but, should anybody want to see what programming on the PS1 would look like using only modern tools (latest GCC, CMake, no third party libraries), I've written a few bare metal C examples that explain in depth how the console's hardware works [1]. Currently only graphics and input are covered, but I'm planning to add examples showing how to handle interrupts, play audio and access the CD-ROM next.
There are homebrew tools that can be installed on a PS1 memory card [1] and allow for executables to be loaded from a host machine into RAM through the serial port on the back of the console, in a similar way to what Sony's official Net Yaroze loader did back in the day. These tools can also use undocumented CD-ROM drive commands to disable region checks without the need for a modchip, provide semihosting (host filesystem access) and so on.
On the PS2 it's slightly more complicated, as there is no way to launch the "native" PS1 backwards compatibility mode other than to use a modchip (or firmware mod on some models) and burn the executable onto a disc; the serial port is not exposed either, making debugging much harder. It can still be done, but it's much easier to just use an actual PS1.
The author's Learn 3D Graphics Programming is one of those extremely rare courses that takes you from the basics and peels back the curtain to show you the Wizard.
One of the best courses I've ever taken. (Thanks Gustavo!)
By the way Gustavo, with your talent for explanation, graphics and Maths, maybe you should do some courses on Machine Learning!
(You'll make a lot of money ;-)
If you're taking requests, I'd love a good linear-algebra refresher course. I recently ran into the term "eigenvector" in a professional setting and that's a word I hadn't heard since my freshman year of college, which is over 2 decades at this point.
Since you mentioned math: If you can unravel the 4D being that is a quaternion and explain it to mortals, I would shower you with all the money I have on hand.
Also, it's probably premature but I would love some Rust courses in the context of game programming. I think that's something I'll need to pick up one day.
More C stuff, I really enjoy learning these fundamentals using C. I did your game physics course in C and really enjoyed it. I'll be taking this course to learn more about 3D graphics and C together.
I'd love to see a real-time multi-threaded & SIMD raytracer in C/C++/Rust. Plenty of math in there, but an opportunity to discuss thread & data parallelism, PBR, data structures for large scenes, profiling and optimization, and so on. It's like the next step after your existing 3D programming course.
I agree. As a low-level dev myself I really enjoy his courses. I did originally have issues with his game engine course where he was implementing the software in a rather inefficient fashion, but he rewrote most (if not all?) of the course from scratch and it's much better now. This is an instant buy from me
To note that PS1 was the first games console on the home market[0] to fully embrace C on its SDK, until then it was Assembly all the way, hence why so many studios were happy to jump into it.
[0] - The arcades adopted it much earlier, and there were UNIX and VAX/VMS devkits with cross-compiling capabilities for them.
I'm reverse-engineering a PlayStation video game that is rather unoptimized and I'm amazed that the game managed to do so much with such horrible game code. That SDK really was fairly forgiving for its time to new, inexperienced developers.
Yet to squeeze out performance out of the in-order, single-issue 33 MHz MIPS CPU you had to be very mindful of low-level details. The 4 KiB instruction cache hates code bloat, the lack of data cache means spilling registers anywhere but onto the 1 KiB of scratchpad memory is costly, yet the god-awful C compilers of the era liked to do both of these things. That's just for the CPU, the rest of the system also has plenty of details you need to pay attention to for performance.
I haven't actually programmed for the console, but the achievements done by talented developers such as Naughty Dogs on this dirt-cheap system are truly impressive. The console might be easy to develop for, but to make it really fly still takes a lot of skill.
Yes, for Crash Bandicoot we had to entirely skip the C SDK for everything performance-critical; pushing arguments onto the stack and the SDK popping them off for each function call used more cycles than the underlying operation in many of these SDK calls.
Sony explicitly forbade this, presumably because they envisioned the API established by the C SDK as a way to ensure future backward-compatibility. We just ignored the rules and hoped the superior performance we could achieve would overcome any bureaucratic obstacles.
We also had to reverse engineer some of the PS1’s really nice capabilities. I only learned the hardware supported vector pipelining for strip rendering by noticing the coordinate values move from one register set to the next in the debugger.
Seeing that was a revelation: when rendering a polygonal mesh, you naively have to project three coordinates for each triangle. But if you convert your mesh into sequences of polygonal strips where each triangle shares an edge with the next triangle in the mesh, you only need to project a single additional vertex for each additional polygon in the strip. Observing the behavior in the debugger, it was obvious to me that the Sony hardware had in fact been optimized for this approach. But not only were we not given any documentation on this, we were instead told to use the C SDK, which didn’t expose this capability at all.
The PS1 also had 2KB of “scratchpad” that was slower than registers but much faster than RAM. Hearsay was this was a way for the CPU designers to make use of the physical space on the die meant for floating point instructions which the MIPS CPU in the PS1 didn’t have.
I used the scratchpad to store projected 2D “stamps” of the 3D world (stored in an octree) and Crash: a kind of low-res Crash Flatland. I could then check for X/Y collisions between Crash and the world very rapidly by inspecting the flatland bitmap stored in the 2K scratchpad. This was Mark Cerny’s idea; he was our producer on the Crash games and has also been responsible for PS2 through PS5 hardware.
Not only do you only have 4KB of instruction cache, but it's direct mapped.
So if the linker just so happens to place the function you are calling at some address that's some multiple of 4KB away from your current hot loop, then the code for your hot loop, and the function you call will get continually evicted and reloaded for each loop iteration.
A small change to a random function could have large impacts on the alignment of all other functions in your game, and could result in large performance impact. The simple solution is to just inline everything your hot loops call. That avoids the chance of the pathological cases (and has the bonus of avoiding function call overhead), but at the cost of increasing code bloat in general (which the cache hates).
I've been vaguely considering making linker that's aware of direct mapped caches, and uses the call graph to guide the placement of functions (and data too, for machines with direct mapped data caches). Not only would it be useful for retro consoles with direct mapped caches like the ps1 and n64 (and probably help for the PS2 too, which is only 2-way set associative) but it would be useful for modern microcontrollers that do XIP execution of code from flash. They often have simplistic direct mapped caches too.
Have you published anything? There are a few games that I would love to be able to round trip between the original disc contents and a build with a few tweaks or experimental features. I know there is a Wipeout decompile/source leak, but having more examples is always wonderful.
Something like the Mario 64 reverse engineering project has made the game playable effectively everywhere with the ability to take advantage of modern hardware (16:9 displays). It would be great to see efforts like that for any console.
> We'll also learn how to use the official Sony Psy-Q libraries
Is there not a cleanroom SDK for the PS1? I doubt Sony cares too much at this point, but selling a course which relies on leaked proprietary tools still feels a bit dicey.
The Portal N64 demake got shut down because it used the leaked N64 SDK rather than cleanroom tools.
That was interesting because the author didn't receive a legal takedown notice, and the request came from Valve instead of Nintendo. It's possible nintendo didn't care, and likely that sony cares less than nintendo.
I think Nintendo cares a lot. In fact I think Nintendo gives so much fucks about its IP (of any kind, not only games/stories/characters, but also SDKs/APIs/console design/etc) that Valve fears any kind of association with potentially illegal or at least dubious origin software related to Nintendo.
Remember Valve is a company that normally celebrates and promotes the modding community, even when using their IP directly (i.e. Black Mesa). Valve's decision was totally and exclusively because it included BOTH their and Nintendo's IPs in the equation.
I believe what happened is that Valve said something along the lines of: "We are a little concerned that Nintendo might object, you can only continue if you get an official notice from Nintendo saying they don't object."
It's entirely possible that Nintendo don't care, but the chance of Nintendo agreeing to go on the record about such things is basically zero.
PSn00bSDK maintainer here. Unfortunately the project cannot really be considered clean room; the original versions of most libraries contained code that was either lifted straight from Psy-Q disassemblies or heavily inspired by them. I have since rewritten pretty much all of it (with the exception of the GTE library which still has some Sony code) using only Psy-Q API documentation as a reference, but the "ship of Theseus" nature of the rewrite makes it hard to argue that it is a clean and legally safe project.
On the flip side, there are plenty of other open source PS1 SDK options that have been written from scratch, do not reimplement the same API as Psy-Q and can thus be considered clean for the most part. Here's a few of them:
Hi, what prompted you to create a 25 hour course about programming the original PlayStation? Don't get me wrong, it's very cool that such a resource exists, but it is a rather peculiar topic for a course.
Hey. Good question! People think I'm crazy, but I promise you I'm very sober about my approach to my courses.
I teach CS & mathematics at a university here in London. Empirically, I've noticed that students can find ways to be productive but they are seriously lacking the fundamentals.
To be fair, it is nothing short of overwhelming to try and grok the fundamentals of computing using modern machines, modern OSs, and a modern dev toolchain.
So what I try to do with the courses is to go back in time and limit myself with a closed hardware box that is still small and simple (compared to what we have today).
Instead of using a fake instruction set, I can have students learn the basics of MIPS, use a very simple assembler, output binary code, and execute those instructions on a hardware that exists (or existed once). That allows me to explain about CPU architecture with real examples, talk about DMA, endianness, interrupts, and so many other concepts that students can read online but they don't really grok until they get their hands dirty with grease.
Weirdly enough, so far it's been proven to be a good idea. It's definitely not for everyone, but if someone is at a stage of their career where they think it's fun to learn about RISC pipeline, fixed-point math, game physics, 3D graphics, compressing assets, computer architecture, etc., then I still think this type of course is valid!
I'm positive that in 25 hours learning how to program the original PlayStation they'll learn a lot more about computer science than taking an expensive class to read about 'Digital Transformation' or something like that.
There definitely is one [1], albeit perhaps not as large and active as the homebrew communities for 8/16 bit consoles (which benefit from low-code tools such as GB Studio and the ability to sell physical game cartridges that do not require modchips) or more modern ones (which tend to have a much lower barrier to entry, given the better specs and availability of SDL ports and whatnot). It is definitely growing though, as I'm seeing more and more people get interested in developing for the PS1.
I started collecting obscure retro consoles almost a decade ago when Net Yarozes could be found for prices that weren’t completely outlandish. Now it’s common to see them on popular marketplaces for $700 or more.
Their aesthetic is amazing. The PS1 holds up, imho, as one of the best looking Sony products and consoles of all time and the dark gray color way is a hint of what the next three consoles would look like.
With modern quality emulators like DuckStation, there's really no reason to use real hardware anymore as the primary target. Besides, you'd probably just have a standard PlayStation console with a modchip on hand and iterating with CD-Rs would be very slow and wasteful.
I took this dudes Atari 2600 Course on Udemy cause it was only $13 bucks. It is awesome/great. Made the whole thing super simple to learn. Very idiot proof. It's too the point (which I find helpful).
[1]: https://github.com/spicyjpeg/ps1-bare-metal
PS: thank you for all the good help on the PSX Discord, as always.
On the PS2 it's slightly more complicated, as there is no way to launch the "native" PS1 backwards compatibility mode other than to use a modchip (or firmware mod on some models) and burn the executable onto a disc; the serial port is not exposed either, making debugging much harder. It can still be done, but it's much easier to just use an actual PS1.
[1]: https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmw...
Buy one with FMCB preloaded from AliExpress, and take it from there
https://consolemods.org/wiki/PS2:FMCB
And also check out for example MX4SIO as well
You can buy one of those from AliExpress as well, but only certain versions of FMCB are compatible with MX4SIO out of the box
https://pikuma.com/courses/learn-3d-computer-graphics-progra...
One of the best courses I've ever taken. (Thanks Gustavo!)
By the way Gustavo, with your talent for explanation, graphics and Maths, maybe you should do some courses on Machine Learning! (You'll make a lot of money ;-)
I'm still deciding what to tackle next. I had a couple of ideas, but since my true background is really math I'll probably consider that.
I'm not sure there's much money in what I do, but it sure it's a fun topic. Fun always trumps everything else at the end of the day. :)
Also, it's probably premature but I would love some Rust courses in the context of game programming. I think that's something I'll need to pick up one day.
Get 60fps on an RPi5 for extra challenge.
[0] - The arcades adopted it much earlier, and there were UNIX and VAX/VMS devkits with cross-compiling capabilities for them.
Yet to squeeze out performance out of the in-order, single-issue 33 MHz MIPS CPU you had to be very mindful of low-level details. The 4 KiB instruction cache hates code bloat, the lack of data cache means spilling registers anywhere but onto the 1 KiB of scratchpad memory is costly, yet the god-awful C compilers of the era liked to do both of these things. That's just for the CPU, the rest of the system also has plenty of details you need to pay attention to for performance.
I haven't actually programmed for the console, but the achievements done by talented developers such as Naughty Dogs on this dirt-cheap system are truly impressive. The console might be easy to develop for, but to make it really fly still takes a lot of skill.
Sony explicitly forbade this, presumably because they envisioned the API established by the C SDK as a way to ensure future backward-compatibility. We just ignored the rules and hoped the superior performance we could achieve would overcome any bureaucratic obstacles.
We also had to reverse engineer some of the PS1’s really nice capabilities. I only learned the hardware supported vector pipelining for strip rendering by noticing the coordinate values move from one register set to the next in the debugger.
Seeing that was a revelation: when rendering a polygonal mesh, you naively have to project three coordinates for each triangle. But if you convert your mesh into sequences of polygonal strips where each triangle shares an edge with the next triangle in the mesh, you only need to project a single additional vertex for each additional polygon in the strip. Observing the behavior in the debugger, it was obvious to me that the Sony hardware had in fact been optimized for this approach. But not only were we not given any documentation on this, we were instead told to use the C SDK, which didn’t expose this capability at all.
The PS1 also had 2KB of “scratchpad” that was slower than registers but much faster than RAM. Hearsay was this was a way for the CPU designers to make use of the physical space on the die meant for floating point instructions which the MIPS CPU in the PS1 didn’t have.
I used the scratchpad to store projected 2D “stamps” of the 3D world (stored in an octree) and Crash: a kind of low-res Crash Flatland. I could then check for X/Y collisions between Crash and the world very rapidly by inspecting the flatland bitmap stored in the 2K scratchpad. This was Mark Cerny’s idea; he was our producer on the Crash games and has also been responsible for PS2 through PS5 hardware.
So if the linker just so happens to place the function you are calling at some address that's some multiple of 4KB away from your current hot loop, then the code for your hot loop, and the function you call will get continually evicted and reloaded for each loop iteration.
A small change to a random function could have large impacts on the alignment of all other functions in your game, and could result in large performance impact. The simple solution is to just inline everything your hot loops call. That avoids the chance of the pathological cases (and has the bonus of avoiding function call overhead), but at the cost of increasing code bloat in general (which the cache hates).
I've been vaguely considering making linker that's aware of direct mapped caches, and uses the call graph to guide the placement of functions (and data too, for machines with direct mapped data caches). Not only would it be useful for retro consoles with direct mapped caches like the ps1 and n64 (and probably help for the PS2 too, which is only 2-way set associative) but it would be useful for modern microcontrollers that do XIP execution of code from flash. They often have simplistic direct mapped caches too.
Something like the Mario 64 reverse engineering project has made the game playable effectively everywhere with the ability to take advantage of modern hardware (16:9 displays). It would be great to see efforts like that for any console.
Is there not a cleanroom SDK for the PS1? I doubt Sony cares too much at this point, but selling a course which relies on leaked proprietary tools still feels a bit dicey.
The Portal N64 demake got shut down because it used the leaked N64 SDK rather than cleanroom tools.
Remember Valve is a company that normally celebrates and promotes the modding community, even when using their IP directly (i.e. Black Mesa). Valve's decision was totally and exclusively because it included BOTH their and Nintendo's IPs in the equation.
It's entirely possible that Nintendo don't care, but the chance of Nintendo agreeing to go on the record about such things is basically zero.
On the flip side, there are plenty of other open source PS1 SDK options that have been written from scratch, do not reimplement the same API as Psy-Q and can thus be considered clean for the most part. Here's a few of them:
- https://github.com/grumpycoders/pcsx-redux/tree/main/src/mip...
- https://github.com/ChenThread/candyk-psx
- https://github.com/cuckydev/CKSDK
- https://github.com/spicyjpeg/ps1-bare-metal (shameless plug)
wow I'm getting old
More actively maintained...
Thank you so much for sharing. :)
I teach CS & mathematics at a university here in London. Empirically, I've noticed that students can find ways to be productive but they are seriously lacking the fundamentals.
To be fair, it is nothing short of overwhelming to try and grok the fundamentals of computing using modern machines, modern OSs, and a modern dev toolchain.
So what I try to do with the courses is to go back in time and limit myself with a closed hardware box that is still small and simple (compared to what we have today).
Instead of using a fake instruction set, I can have students learn the basics of MIPS, use a very simple assembler, output binary code, and execute those instructions on a hardware that exists (or existed once). That allows me to explain about CPU architecture with real examples, talk about DMA, endianness, interrupts, and so many other concepts that students can read online but they don't really grok until they get their hands dirty with grease.
Weirdly enough, so far it's been proven to be a good idea. It's definitely not for everyone, but if someone is at a stage of their career where they think it's fun to learn about RISC pipeline, fixed-point math, game physics, 3D graphics, compressing assets, computer architecture, etc., then I still think this type of course is valid!
I'm positive that in 25 hours learning how to program the original PlayStation they'll learn a lot more about computer science than taking an expensive class to read about 'Digital Transformation' or something like that.
To each, its own.
[1]: https://psx.dev/
https://en.m.wikipedia.org/wiki/Net_Yaroze
Their aesthetic is amazing. The PS1 holds up, imho, as one of the best looking Sony products and consoles of all time and the dark gray color way is a hint of what the next three consoles would look like.