Phew. A week ago, I had merely a tiny spark, a sliver of a glimmer inside my mind’s eye, reflecting off the phrase “ps1 sdk” sitting in a Google search. I began slowly, it turned out to be quite simple to bash out a simple text-only game, but it also didn’t really tell me much about what I was working with. With that in mind, and the fact that a simple text game is barely a blip on the difficulty/experience meter, I decided I’d better step it up. I present to you “RPS2: The Revengening” (clicky linky to download).
Now, it’s still no AAA title, it’s only 2D and it’s missing some stuff a commercial game might have, some of which is a limitation of the SDK such as the lack of ability to play FMVs, but it’s a functioning graphical game nonetheless. Having got this far, I’m gonna take the opportunity to go over a little of what I learned about the SDK and writing for the PS1. While I can’t cover every detail here, I’d at least like to try and inspire people to become interested in programming for consoles and other embedded devices, especially older ones which are more simple and allow you better access to the bare metal. It’s good fun, and as long as you don’t go into it expecting to wail on the keyboard for a weekend and come out the other side with a shiny 6 disc epic on your hands, it’s quite easy to get started.
Lesson 1: Emulators are not 100% accurate.
The problem with developing for a console is that unless you have a debug or development model of a console, or some method of simulating one, development is tricky. With a development console (commonly known as SDK consoles), you don’t usually need to burn a disc every time you want to test. Some use code uploaded into RAM or ROM, others use HDDs, and they usually cut you a lot of slack when it comes to whether code is signed or encrypted or not. A debug console on the other hand is slightly easier to find and usually very much like a retail console, but likely with less/no copy protection features turned on and the ability to watch a port for debug messages, a sort of half-breed between a development console and a retail one. Unless you have one of these mythical, hard to find, expensive devices, you’re a bit screwed, you have to work within whatever constraints the retail hardware provides. This usually means you have no way to quickly and easily iterate code without burning big stacks of CD-Rs or updated ROM chips, which makes development a very long, slow and annoyingly inconvenient process. If you want to run on a real retail console, you’ll generally need to mod them, unless you decide to go with something like the Dreamcast which (most models of) will happily run whatever code you feed it as long as you ask it nicely. Theoretically you can add code uploading capabilities using some of the parallel port cheat cartridges available for the PS1, with a little modification, but I’ve yet to look into this, so I don’t know what restrictions it might come with. Could come in handy though, if you’re interested in developing for the PS1, it might be an option.
There is one other class of console which is a sort of retail development console. They’re not very common as consoles models go, but Sony had one called the Net Yaroze. They weren’t super easy to get hold of (though they were considerably more accessible than a full commercial development kit would’ve been, costing only about $600), but essentially it’s an SDK version of the PS1 which you could write your own games for on a PC and upload those games into memory via a cable. It sounds cool, but you couldn’t really do much with the game once you’d written it, besides sharing it with other Net Yaroze users, the games wouldn’t run on a stock console and always had to be uploaded to the Net Yaroze PS1 via serial cable, there was no CDROM support. They’re reasonably hard to find, especially with the disc and memory card boot key that was required to use them, and ultimately not of much use for what I wanted to do. You can actually solder a cable into regular PS1 to essentially turn it into a Net Yaroze, but the same limitations apply. It’s a very neat little curiosity, but little more now we have modded consoles out the wazoo.
So enter the emulator, a virtual copy of the console that runs on your PC, it won’t bug you about unlicensed code, it doesn’t require that you write physical media to run from and if you’re lucky it may even include debugging features like memory viewers and disassemblers. Useful though they are, you do need to exercise a little caution, because no emulator is absolutely perfect simply because it’s not the original console, it’s a rough approximation of one, albeit often quite authentic. There’ll be quirks in the hardware that are too difficult, too computationally costly or just too trivial and rarely used to be present in most emulators. This means that while your game might run flawlessly in an emulator, it might fall flat on its face in a real console (or vice-versa), and being geared for retail games the console won’t give you any clues as to why. Famicoman kindly offered to get some shots of this game running on his actual PS1, which failed for a reason as yet unknown, possibly because of the plug-in cheat card he’s using rather than an internal modchip, we have no idea at this point. Another example from RPS2 is when I tried to add music. Since I don’t actually own a Sony console at present, let alone a properly modded one which can run homebrew code, I have no way to test this thing, so preciousRoy stepped in to assist. I compiled, imaged and uploaded two copies of the game in alpha form for him to test on his modded PS3. On the plus side, it booted and it ran, exactly as it should, but the two methods I tried in order to play music, from the CD as regular audio tracks and by loading converted files into the sound processor’s RAM, both failed. Why? I have no idea. Perhaps because it’s running on a PS3 and there’s some incompatibility there or because the code simply isn’t compatible with the real deal and only works in an emulator. Still, never mind, he did get this neat picture:
Regardless, that’s one real example of where my test setup didn’t match the results of a real machine. With all that in mind, emulators are still an invaluable tool for development. I mainly use ePSXe for my PS1 emulation, but as far as I’m aware it doesn’t offer the debug functionality of some other emulators such as PCSX. Here’s a snapshot of the debug output window of PCSX from my “Button Masher” reaction time tester, from boot time until when I took the screenshot:
Had I used only one emulator or even a real console there’s no way I could’ve got anywhere near that kind of feedback from my code, I can track everything it does simply by peppering my code with printf(“Something happened…”);. Without that, I’d have to rework my game to give me debug messages rendered into the game screen itself which isn’t exactly convenient. Not only would I have much less space on-screen for that information (especially if I still want to be able to see the rest of my game happening), because it’s running at 320×240 with a fairly large default font, I’d be using valuable RAM and VRAM to do it, and I’d also need to set the game up appropriately set up too. It’s all very well printing a line of text to the screen if all you have on screen is text, you’re ready and able to potentially output debug text, but if you’ve got no fonts loaded, it could be a not insignificant task to prepare yourself to output those messages. It’s tricky because…
Lesson 2: The 1990s Called, It Wants To Borrow A Cup of RAM
Yeah, so, about those specs. You can do a surprising amount with a low spec machine, but the PS1 really reminds you of how far we’ve come in 15 years. For my purposes, the ~34MHz MIPS CPU really isn’t too anaemic, it’s adequate for shuffling some strings and integers around and stuffing things into RAM. That’s great and all, but those strings/integers/things need RAM to be in. With 2MB of system RAM, 1MB of video RAM and 512KB of audio RAM, everything you know about programming for modern machines goes fluttering out of the window. It’s not longer sensible to keep everything in memory, you have to keep everything tight and slender or you have to keep pulling stuff from the CDROM, which is kinda slow. Where I gleefully ran into this issue face first is with graphics. With only 1MB of VRAM to play with and so many textures to shove into it, you have to start thinking smart. Every 2D and 3D texture has to live here, every little graphic, every little pixel, every little letter and number you want to put on the screen has to be right there in that 1024KB of RAM. Not content with such a limitation, as well as fitting into the physical RAM constraints, the PS1 demands that everything has to fit within a 1024×512 “window”. That’s 1024×512 16bit pixels, or 1MB (1024×512×16 = 8388608 bits, or 1048576 bytes, 1024KB or 1MB).
Anyway, thanks to the wonder of emulation, here’s what the VRAM looks like with my game loaded into it:
I should point out that much of that black space is either used for the stuff you seen on-screen or is unusable. It’s not all available to be used for… some reason. If I cross certain boundaries I end up wrapping back to the other side of the memory space. More reading is required to figure out why, thankfully there are plenty of docs online. I’m storing my images in 16bit form, loaded from TIM files on the CD which are converted at compile time from BMPs. I did attempt to use 8bit textures but for some reason that results in nothing being visible on-screen. You have a potential 1024×512 canvas to store everything on (and that includes the live output to the screen, twice, with double-buffering) and it can get quite Tetris-like trying to maximise what little you have to play with. Whereas on a PC you might be able to just load multi-MB bitmaps into a game willy-nilly, caring naught for what your RAM usage might be (because what’s 20MB of textures when you have 2GB of system RAM and a GPU with 1GB all of its own?), on the PS1 you really need to consider what’s essential, whether there’s a more efficient way of displaying a certain image or effect and how you’re going to arrange those pixels for maximum efficiency. You can’t just decide you want to add another font or another texture, there simply won’t be room, you’ll have to write over something else and then load whatever you destroyed back in from disc later. Wasted space is evil, too. Compare the above image with this one:
If you take a look at the stuff in the top right corner, the 7-segment display graphics, you’ll notice that in the second, older screenshot, I had cut out a copy of the entire display, complete with the metallic border. This didn’t seem like a very big deal, it was a simple enough concept, when I wanted to animate the start screen, I’d simply flip between the main start screen with, and then without, the cut out “PRESS START TO PLAY” LED display graphic on top of it. Inefficient, perhaps, but I didn’t consider it a problem when I began. Further into the project though I realised that simply having a pre-drawn image of anything I wanted to display on the LED screens wasn’t going to cut it. There was simply no way I could possibly fit that many images into VRAM, I’d have to keep reading the CD which would slow the game down and likely disrupt any CD audio which might be playing, had I managed to get that working. I had to draw each letter as a sprite so I could render any arbitrary string on the fly, in any location on the screen, without the metallic borders. Not only did I save having to draw and somehow store that many images in the VRAM, it forced me to tidy up and rearrange it to a more efficient layout. In doing so, I managed to roughly halve the space I was using to store the numbers and the “PRESS START” images, and it was now more flexible too, I could draw any alphanumeric string I liked. I even had room to add those lit up “50p Play” and “Start” textures in the same area. This really shows how you have to think carefully about how your game should be laid out, if you don’t put effort into thinking about what each decision might mean down the line, it’s going to make things difficult in the future. I’m not really one for planning ahead when I’m programming, so I ended up learning this the hard way, but if this had been, let’s say Final Fantasy 7, I’d have been in a whole heap of trouble right about now. One “small” change might mean having to redesign huge chunks of the game, both graphically and in code. That’s not a fun thing to have to do. Reusing memory is equally important, after my start screen fades out, it’s immediately overwritten in memory by the main game screen, it’s entirely gone. Why keep it in VRAM if you’re not going to use it again until the game is rebooted and the whole process starts over again? Frittering away huge areas of VRAM with stuff you “might use again, maybe later in the game” isn’t an option. One last thing to note is texture size restrictions. The PS1 won’t let you have a single texture bigger than 256×256, which I quickly found out when I tried to render the start screen and the main game screen in one big 320×240 chunk each. No can do, says the PS1, so I simply chopped the 320×240 screens right down the middle, so I’d have 2 halves at 160×240 a piece. What happens if you don’t is that the texture is truncated and wrapped, it looks a little something like this:
Which brings me to my last point today…
Lesson 3: Stuff Can, And Will, Go Wrong…
That’s my new venture into the modern art scene, it’s a digital example of an abstract… actually, I just fudged a pointer or overflowed a buffer or something. I’m still not sure exactly how I managed this, but I think it’s fair to say that I broke it. I put a thing in a thing and it all went a bit wrong, and we shall never speak of this again.
Seriously though, stuff will go wrong, and the console won’t be able to tell you why, it’s only doing exactly what I told it to do, even if I’d just told it to eat its own face from the inside out. Which it did. Without question. The only things you have to go on is your source code, any bones the compiler might be willing to throw you and your own common sense/programming smarts. I’m used to using Microsoft languages, nice and comfortably in their own IDE with gazillions of help files, contextual autocomplete and all the mod cons like that. Switching to Notepad++ and GCC… well, that’s a bit of a culture shock, but as long as you have a little patience and you try not to fat-finger the keyboard too often, it’s more than enough to set you up. Now, where’d this extra semi-colon come from…?