Sega Master System Coding
Besides updating my server, I’ve been quite busy with some Sega Master System (SMS from here-on-out) coding. I’ve decided to switch to SMS for now instead of Game Gear for now. It seems I’ve kind of went my own way with this project. From the looks of it, most people either use WLA-DX or, to a much lesser extent, z88dk. I don’t see too much about any other assemblers/compilers. Granted, I haven’t looked terribly hard for the topics though. So even from the get-go with using z80asm instead of WLA-DX I was using a different assembler than the rest.
I did an evaluation after a bit of a hiatus and it was pretty clear that I still have a long ways to go in terms of how I organize my assembly code. Enter C.
I had mentioned before that I had considered trying to use SDCC for a C compiler, but when I first started playing around, I didn’t have much of a foundation to work from. Really, the initial tests I did in pure Z80 proved to be very useful when it came to getting SDCC working. I had no problems editing the default crt0.s to work with the SMS. Getting that to compile and run on CrabEmu ended up being easy too. From the start, I knew I’d probably want some kind of utility library to help me out along the way. The first thing I did was create a couple simple macros to that do in/out in Z80 assembly so I could actually do something useful with the hardware. From there I converted my assembly to C and used the in/out macros.
With basic I/O in place, I quickly went on to write some functions. First I changed in/out to functions, added some register defines, tested, and things still worked. With some excitement I went to write some utilities functions for the SN79489 and TMS9918a. All of these functions ended up calling the in/out functions. Did some testing, worked out a few bugs here and there, but for the most part these worked well too. From there I decided it was time to try some real usage. The first thing I did was did a clear_vram function. The code was basically this:
void clear_vram() {
uint16_t x;
tms9918a_vram_set_write_address(0);<br />
for(x = 0; x < 0x4000; ++x)
tms9918a_ram_write(0);
}
tms9918a_vram_set_write_address sets the VDP’s RAM address to the start of the RAM, and then loops through the entire 16K writing zero’s to every byte. After this I did a few tiles (and after struggling for quite some time) drew them to the screen.
There was a problem here. It took a couple seconds before my couple tiles were displayed after the bios screen. I ended up blaming the fact that tms9918a_ram_write was a function call that called another function that ran inline assembly that then returned from a return. This may not sound like much, but on a Z80, 32K of function calls add up to quite a few CPU cycles and memory access. So I went to the SDCC documentation and found something very useful under the Z80 Storage Class Language Extensions: “sfr (in/out to 8-bit addresses).”
__sfr __at 0x78 IoPort; /* define a var in I/O space at 78h called IoPort */
IoPort = 0x01;
generates the following assembly code:
ld a,#0x01
out (_IoPort),a
Alright, you’ve got my attention with that. Sound extremely straight forward, even if it is an SDCC only language extension. So I tried it out. I created the following macros:
#define io_vdp_data __sfr __at 0xBE io_vdp_data
#define tms9918a_write(d) io_vdp_data = d
Replaced tms9918a_ram_write with tms9918a_write. Ran it again, and now the clear_vram ran in roughly half the time (ok, this is just a best guess since I didn’t do any kind of real performance measurements.) So I ended up defining a few more macros to expose all useful SMS I/O ports. With these in place I can do useful things like:
if(!(io_ab_reg & 0x20)) {
// Do something on player one press of button 1
}
Where I’m currently at is I draw the entire tile based level map and can do trivial animation on tiles. I can also output trivial tones, but I’ve been focusing on graphics currently so I don’t have too much to say about sound right now. It’s probably going to be cutting it very close for submitting something to the 2011 SMS Coding Competition, but I’ve been trying my hardest to keep on it. I have a feeling that I’ll miss the deadline though. Hopefully I’ll be able to get it mostly working since it seems like a good deal of work went very smoothly the last few days.
A few other random thoughts on the subject. While I’m currently targeting the SMS, I’ll probably also a “port” of this current project to the Game Gear. It’ll take advantage of the much larger color palette for sure. On the other hand, I’ll also have to have at least a basic AI in place for single player play. As for z88dk and why I went with SDCC instead, well, to start with because I didn’t know about z88dk. Later because I was having fun writing my own functions for my project. Basically, that’s all there is to it. It was more fun to do it myself then to get started faster. Eventually the GPL’d project source and LGPL’d library source (still not totally sure on these licenses) will be released. For now though, I haven’t released anything, and the library isn’t in a very releasable form yet. Hell, I’d even say that without fairly intimate knowledge of the SMS/GG hardware, it’s not even that useful. Another small utility that I wrote before I noticed there was (most likely) one already was a header checker/correcter. This should fix the header so it can be played on actual hardware. It was thrown together extremely quickly and doesn’t do much besides add in the correct checksum and a few other things. Again, it’s likely not going to be terribly useful, and there’s already the source to a tool that does basically this. In any case, it’s currently integrated into my build processes and happens automatically. It seems to work fine against a real bios dump and the CrabEmu-Bios. This leaves me with little reason currently to change it to something else.
In any case, that’s what I’v been primarily up to for the past 3 months. Really though, the C code has all been over the last month (Jan 27 was the Git commit date.) I’ll try to remember to post an image of the current level map animation that I have.