Tag: downland

  • Downland Unearthed Final: Porting The Game To Over A Dozen Platforms

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    As my last Downland Unearthed article, I want to talk about the journey I went through to bring the game to over a dozen different consoles. Buckle up, it’s gonna be a long one.

    But Why Though?

    You could say one of the reasons for going through this whole reverse-engineer-and-port process was to get people from all the world to play this great game. But in truth I just wanted to get Downland on a platform with actual good controllers. Never again will I have to suffer using those godawful, loose, non-centering Radio Shack joysticks.

    Horror in black plastic

    Oh sure, I could say it’s about game preservation and community, and all those good things, but really it’s those fucking joysticks.

    Okay, maybe it’s a little about game preservation and community. A tiny bit. A smidgeon. A tad.

    I digress.

    When I started looking into Downland as a project, I was surprised no one had reverse engineered the game before. It’s one of the most classic and beloved CoCo games. If nobody had done it I thought maybe I could take a shot. There didn’t seem to be a lot of competition.

    Ghidra To The Rescue

    I’m not an assembly language programmer and can’t write any real program in it to save my life. But I’m a programmer with retro console experience, so I was confident enough I could muddle my way through.

    So over the years I had made several attempts but never really got very far. I was using MAME’s debugger and I found its toolset too limiting and too cumbersome. It was hard to get the sense of the whole scope of the game, to browse the assembly, and to document how it worked. This time I decided to try Ghidra and that turned out to be a pretty good idea. I had never used it before but once I got the hang of it I managed to delve deeper and quicker into the game’s internals than I had ever done before. After so many attempts I had finally found a good foothold.

    Timeline

    I started disassembling the game in November 2024. I was using Ghidra to map out and document the code and the trs80gp emulator‘s debugging features to verify my findings. Around mid-January 2025 a large portion of the code was figured out. At that point, I had a code listing that, when converted to a LWTOOLS-compatible format, could compile to a byte-for-byte replica of the original V1.1 version of the game.

    You can find the listing here.

    Currently, the code hasn’t been 100% deciphered. But all the important gameplay code and data structures are, which was enough to start converting everything into a higher level language.

    With that done, the first goal had been achieved!

    Unearthed Discoveries

    On the way to that milestone, I made some really neat discoveries:

    And had solved the mysteries of:

    Converting To C

    The next step was of course to convert the assembly to a higher level language. There was really no question which one. I needed it to be portable and available on the largest range of devices possible. The C programming language was the only choice.

    (But you know, a brainfuck port is always possible)

    I started on the C version in late January. My hope was to be able to replicate the original gameplay and its finer details, and to prove that my understanding of the assembly was correct.

    Naturally the main challenge was getting the game object behaviors right. There aren’t very many types of objects in the game, but any perceptible difference would break the distinct feel of the game.

    Getting the Details Right

    Even with the assembly code in front of me, it still took a lot of effort to verify that the C version worked identically.

    For example, it took me an entire week to get the ball bounce physics to work exactly like the original. I got to the point where I mapped its trajectory frame by frame, comparing the game’s internal values in emulator with the C port, fixing off by one errors until I got it right. Reproducing the physics exactly was crucial for gameplay. Without it, you wouldn’t be able to hide beneath the ball-boulder in chamber 2.

    The player physics was even more important to get right. The jump is one of Downland’s most unique characteristics. It’s smooth and floaty yet still has a heft to it.

    The player can also bounce off walls and can jump on ledges pixel-perfectly. Not being to replicate that means that there are sections of chambers that are basically impossible to traverse.

    This was a much greater challenge than the ball-boulder physics. I didn’t get it 100% right. I think maybe it’s about 98% correct. There are slight differences with the wall bounce where jumping straight into a corner behaves not quite identically. But in practice it’s really close.

    A literal corner case

    The First Port (PC is the Choice For Me!)

    The first platform that got a C version of was Windows. Windows is evil of course but it’s what I’m most familiar with. I wasn’t ready to start with a Linux or MacOS port as then I’d be struggling with two problems instead of one.

    I developed using Visual Studio 2022. I used SDL3 for the graphics, controls, and sound. To be honest I don’t actually remember why I didn’t use SDL2. Knowing how lazy I am, I don’t think I would’ve immediately gone for the newer, less established API with fewer supported platforms and fewer existing examples. But there must’ve been a reason. Oh well, it’s lost to time and memory now.

    By the end of March, the majority of the game was ported and working. I still fixed bugs here and there but the most of the job was done. Finally Downland was natively playable on a new platform, forty years after its original release. I felt accomplished! Another goal reached! It was time to think about what was next.

    But First, A Fruitless Detour!

    Then I took a two month break.

    With my increased confidence in reverse engineering I foolishly decided to try a bigger project.

    I’ve always been a huge fan of the Expert Software Sega PC games from the mid-1990’s. I currently have a collection of about a dozen titles from that series. Like with Downland I had attempted to reverse engineer some of those titles like Panzer Dragoon and Sega Rally Championship. But I never really got very far. But this time I swore to myself with these newly acquired Ghidra skills I’ll finally figure one of them out. For this attempt I went with Virtua Fighter Remix because I thought it was a “simpler” game. Simpler game means be an easier time, right? Spoiler: it was not.

    I did figure out a fair bit of code and found some unknown secrets, but the path to reverse engineer anything close to 100% was going to be way too long. I wasn’t ready for that kind of multi-year commitment so I returned to Downland. It was fun and interesting while it lasted, though!

    Back On Track And Time For A Plan

    After that dalliance, it was time to start porting the game to consoles. But which ones, exactly?

    For me the choice was obvious. I’m a big Sega hardware fanboy so it was only logical that my initial goal was to port the game to all their consoles. I started with these core five:

    I told myself if I could port to these, then I’d be more than happy.

    I had a high confidence that the C code would run great at 60 fps on the first four consoles. I had never done any Dreamcast work but the hardware is really powerful and I knew the dev community was great. I had done a teeny bit of Saturn homebrew and it was pretty fine too. I spent more than a year on various 32X projects so I was pretty familiar with it, although my Achilles heel was sound playback. Genesis I had been doing homebrew since the 2010’s so I knew that was going to go well.

    I was less sure about the 8-bit Master System, however. I had worked on a homebrew project for the system recently and I really struggled with getting the performance up to a reasonable level. But Downland’s needs are a lot lighter than what I was trying to accomplish with my own project, so I was still very hopeful that it was doable. Even reaching 30fps would be more than acceptable. But I told myself I’ll worry about that when the time comes.

    Development Strategy

    These were going to be straight, no frills ports. I decided at the start that I wasn’t going to change anything in the original game if I can help it. They were going to start and run just like the original on CoCo hardware and there wouldn’t be any gameplay or graphics changes unless some hardware limitation required it.

    Time Limit

    I set myself the goal of porting the game to the core consoles by the end of 2025 and after that point, I’d stop porting. I’ve burned myself out too hard on projects in the past(see 32X, Master System) so I thought it important to give myself a time limit.

    Code Structure

    Every platform can have wildly different hardware. They have different ways to display graphics, to play sounds, and to handle input to name a few.


    I decided at the start to keep the core game code and the platform specific code separate. Every version will use the same core game code running in the background and have a bespoke layer per platform to handle the visuals, sound and inputs.

    The advantage of keeping a centralized core game code is that a bug fix on one platform improves all the others.

    All the ports for the various platforms are in the Downland_C platforms folder. Almost all of the ports have very similar folder structures.

    Porting Requirements

    Each platform needed:

    1. A supported C compiler for the chosen console.
    2. A C-based hardware API.

    I wasn’t going to write either of them from scratch because I don’t have that kind of patience or time, so I was reliant on pre-existing solutions. Luckily a lot of consoles are still popular enough to have both. I’ll go through what I used in a later section.

    IP Limitations

    To be clear I do not own the rights to Downland’s intellectual property. Michael Aichlmayr’s statement in his email to L. Curtis Boyle about his okay to copy the game didn’t make me feel comfortable enough with distributing ROMs and ISOs containing the game’s IP, even if it’s an over forty year old game. So that’s why to run the game on PC or to build the game for consoles, you need a copy of the original Downland V1.1 ROM.

    No, it’s not at all convenient, but I wanted to respect the original author.

    Sound Emulation? Yeah, Right!

    I didn’t want to emulate the game’s DAC-based sound if I didn’t have to. Sound programming is hard and I’m lazy. It’s one of those things that I never get right, I have no patience for, and will actively avoid (see 32X sound programming). So from the start I decided to use pre-recorded sound samples where possible.

    I pre-recorded the game’s eight sound effects (run, jump, land, climb up, climb down, pickup, splat, screen transition) and stored them as WAV files. I would change their sample rate and bit rate according to console capability. For the 8-bit consoles I replaced them with admittedly not-very-accurate homemade PSG-based or APU-based effects. Some 8-bit consoles can play samples, but I wanted to keep things simple.

    Even with this “simple” strategy, I still got a lot headaches trying to get some of the ports’ sound playback to work. See Below.

    Without Further Ado

    Alright, let’s talk about the ports themselves. I will go through each port chronologically, going through various details and findings.

    Sega Dreamcast (2 days – May 24 to May 26)

    Dreamcast homebrew development is where all the cool kids are. I’m always hyped up reading falco_girgis‘ posts about his latest work or news about their homebrew community. So I just had to try it.

    SDK Used: I used DreamSDK to build the port. The install was easy, getting setup with straightforward and the support online was great. No complains at all when using it!

    Testing: The Redream emulator and a Sega Dreamcast with a Terraonion MODE.

    Port Highlights: This was the pretty close to the most ideal port imaginable. The hardware easily runs the core game code as-is full speed without a sweat. I only had to write minimal code for wav playback and joystick handling. For graphics, the console is powerful enough that I could perform a basic CRT artifacting effect on the game’s framebuffer and copy that to the screen.

    I had the whole thing wrapped up and running on actual hardware in two days. A great start that obviously meant that all the other ports will be just as easy and straightforward!

    Challenges: The only real head-scratcher I encountered was with playing looped sounds. They didn’t seem to stop when I wanted to. It turned out that I was playing stereo sounds and I was stopping only one channel. Once I realized that and stopped both channels, looping sounds worked correctly.

    Known bugs: Sound playback might not be 100%. I’ve had issues where the jump sound suddenly turns into static. Tried to mitigate it but it’s probably not completely fixed.

    If I Could Go Back: I’d like to add VMU support for something. Maybe have the player character’s animations play on the VMU’s screen like the lives icons do at the stop of the screen. I’d also like to add a four player simultaneous mode. That would be fun!

    Final Thoughts: This was a fun beginning. I’d love to do more Dreamcast stuff as it’s a great platform.

    Sega Saturn (22 days – May 27 to Jun 22)

    I had experimented a little bit with Sega Saturn homebrew in the long distant past, and I don’t actually remember which SDK I used. Nowadays there are many options, including Jo Engine, libYaul, and SaturnRingLib.

    SDK Used: For Downland I went with SaturnRingLib, the new hotness on the Saturn homebrew scene. Looking back, I don’t think it was the best choice. It was still new and I feel not completely battle tested. It wasn’t as easy to use and not as flexible as I would’ve liked. It had its way of working that didn’t gel with mine so there was friction there. But in the end I made it work.

    Testing: The Mednafen emulator and a Sega Saturn with a Satiator.

    Port Highlights:

    • Unlike the Dreamcast I couldn’t rely on brute pixel pushing power. The new strategy was to draw everything as sprites and background tiles. At game start up the 1-bit sprite graphics would get extracted from the ROM stored on disk and converted to 8-bit sprites with the CRT artifacting effect pre-applied. And when the chamber was drawn on entry, the 1-bit clean buffer was converted as well. Finally I would run the full game for one frame, inspect the game state for object positions, and draw them on the screen with 8bpp sprites. I pretty much used a variation of this technique for almost all the other ports.
    • The player regeneration effect was achieved by pre-generating a series of frames at game start. I think I went this route because maybe updating sprites on the fly wasn’t possible but I don’t remember exactly why.
    • Text in the main chamber is drawn using sprites and not with background tiles.

    Challenges:

    • One limitation the SaturnRingLib had at the time was that it couldn’t play sounds in a loop. To simulate the effect I counted the number of game ticks each looping effect took to play once. For example the run sound effect took 64 frames to play. Then when I played it I ran a per-tick countdown timer from 64 and once it hit zero I would replay it.
    • I couldn’t figure out how to achieve the per-pixel screen transition effect. I had reached my third week of doing the port and I was losing patience. I wanted to be finished with it so in the end I used the Saturn’s background layers to perform a cross fade. It works but it’s not very Downland-like.
    • Waiting for the Saturn boot screen to finish (even if I pressed a button) every time I tested the game got on my nerves real fast.

    Known Bugs:

    • Sometimes a sound doesn’t not play.
    • Sometimes a looping sound doesn’t stop
    • If you make a door appear and then change rooms, the door will disappear during the transition. But it’ll work correctly the next time in the room.

    If I Could Go Back: I don’t know if I’d be able to get the same results, but I’d try a different Saturn homebrew SDK. I’d also try to get that room transition effect correct.

    Final Thoughts: While I like the Saturn I really didn’t have fun with this port.

    Game Boy Advance (11 days – Jun 23 to Jul 3)

    After the struggle I had with getting the Saturn version finished I needed a palate cleanser. And I wasn’t ready to tackle the Sega 32X version because I knew I was going to spend a long time on it (spoiler: I did). So I went in a completely different direction and started a GBA port.

    I never worked on the GBA before and I wanted to learn something new. Considering the hardware I was pretty hopeful that a port would work well. And it turns out it did!

    SDK Used: I built the game with Devkitpro as I heard good things about it. It also supports lots of other Nintendo platforms, so there’s a good chance I’ll use it again.

    Testing: the mGBA emulator and thanks to John Bunday for testing on hardware. Only much later did I find an EZ Flash cart to test on hardware at home.

    Port Highlights:

    • The graphics are handled the same way as on the Saturn where it extracts game data from the embedded ROM and uses the hardware to display 8bpp CRT artifact-ized versions of the objects and backgrounds.
    • At start up I draw all the backgrounds offscreen and process them to create a deduplicated set of tiles and a dozen tile maps. The resulting data was small enough that I could copy them all wholesale to video memory. It only takes a few seconds at start up, and switching backgrounds becomes as simple as updating a register.
    • Being used to working with VDPs on Sega consoles, the way that I could update sprites and backgrounds by simply writing directly to areas in video memory was refreshing.

    Challenges:

    • Downland uses a 256×192 screen resolution and the Gameboy Advance has a 240×160 screen resolution. The smaller screen meant having to use scrolling. Technically this was easy but it does make the game more unfair as you can’t see everything around you. You might get killed from a drop above you didn’t see and you can’t see doors when they appear when picking up keys.
    • The title screen and get ready screens had to be reworked to fit the screen.
    • The in-game UI had to be minimized. The score and timer were moved to the top of the screen and the player id and chamber text were removed. I did manage to keep the animated player lives icons, which is a Downland trademark. They were put inside an opaque row at the top of the screen so that they won’t be mixed up with the background graphics.
    • I really wanted to pull off the screen transition effect perfectly so I had to figure out how HDMA worked. It took me a minute, but I’m glad I could pull off the authentic effect.

    Known Bugs: There’s a visible split-second scrolling bug after dying and returning to the title screen.

    If I Could Go Back: There’s not much I’d want to do to the port. Maybe add co-op gameplay through the link cable? Gameboy Player support?

    Final Thoughts: I finished this port loving the GBA hardware. I learned a lot and really enjoyed my time with it. The only knock I have against it is the 240×160 resolution. I could spend the rest of my days making games for a GBA with a 256×192 screen.

    Playdate (3 days – Jul 3 to Jul 5)

    I wasn’t ready to move on to the Sega 32X yet. I wanted another “fun” console to work on. And the Playdate looked lots of fun.

    SDK Used: The Playdate SDK (duh). The C-based API wasn’t bad at all. It was pretty straightforward to use.

    Testing: The Playdate emulator and a Playdate device (but only once!)

    Port Highlights (aka And Here I Thought The Dreamcast Port Was Easy):

    • Downland has 1-bit graphics and the Playdate has 1-bit graphics. I basically just dump the core game framebuffer onto the screen and that’s it.

    Known Issues: My friend who has a Playdate device tried it only once, told me “it crashed”, and then never said anything more. So… I guess it doesn’t work but I can’t debug it!

    If I Could Go Back:

    • Maybe get a Playdate device. But those things are expensive.
    • I could make the 1-bit sprite graphics a little cleaner. As they’re setup for the CRT artifacting effect, they’re not the nicest looking.
    • I could try to take advantage of the larger 400×240 screen resolution.
    • And most importantly, I’d need to find a use for the crank.

    Final Thoughts: I thought Playdate development was pretty streamlined. I don’t know if I’d make anything for it again, but I liked its simplicity, flexibility, and fun factor.

    3DO (17 days – Jul 5 to jul 22)

    Another console not on my list. By now I’m simply in denial, avoiding my Sega 32X problems.

    I had never done anything on the 3DO before, so it had similar appeal to the GBA. Released before the Sega Saturn and Sony Playstation I was confident it was powerful enough for me to make a decent port. I also wanted to give the 3DO some love, porting a neglected game to a neglected platform.

    SDK Used: 3do-devkit. Its maintainer, trapexit, was a great help.

    Testing: Mostly used the 4DO emulator with additional testing on Pheonix and Opera. Testing on hardware was done by the friendly folks on The 3DO Community Discord. They really helped out. Shout out especially to Archive3DO for his support.

    Platform Highlights:

    • With the exception of one major thing, see below, porting the game over was pretty straightforward. The machine was flexible enough for me to attempt to draw the framebuffer with a CRT artifact effect to the screen, but sadly it wasn’t fast enough. So I switched to using the cel system to draw sprites and backgrounds.
    • I achieved the transition effect by drawing six 256-pixel wide sprites across the screen and using the cel’s many parameters to clip them vertically.

    Challenges:

    • The major thing: the biggest one was coming to grips with the cel-based way of doing things. It was pretty difficult to get right and it took me four days to get one sprite correctly displayed. But you know what, it was the good kind of challenge. When I solved it, I felt accomplished. Other than that, every other issue was pretty minor.
    • The C compiler is old-school, where you need to declare your local variables at the top of the function. Had to rework the core game to compile with it.
    • The compiler also didn’t like the game’s struct types as-is so I had to mark them all as __packed.
    • Struggled a bit figuring out which API to use to load a file from disk. The super friendly peeps on the 3DO Discord helped me out.
    • Waiting for the long boot screen to go away (which I couldn’t dismiss!) on start up really tested my patience. It was worse than waiting for the Saturn’s.
    • Encountered a bug where I would pick up a treasure on the second screen and die. Turns out it was the ball that was floating mid-air, invisible. I wasn’t de-initializing it in chambers where it wasn’t used. It affected all versions.

    Known Issues: The sound playback crackles on hardware (and emulator? I don’t remember…) and I have no idea why. I won’t ever be able to solve it without having a 3DO at home. And those things are rare and expensive.

    If I Could Go Back: I’m pretty satisfied with the port as-is, so there’s nothing critical that I’d change. If there was one thing I’d try, maybe I could try that CRT artifact effect again using the game’s framebuffer like on Dreamcast but this time using assembly.

    Final Thoughts: It wasn’t an easy port, but solving how to use cels felt rewarding. I’d like to one day try another project on it. Part of me wants to attempt a port of Cannonball, the Outrun engine to it. But only after all my other million projects are done.

    Sega 32X (20 days – Jul 20 to Aug 8)

    Finally I put my big boy pants on, grabbed the bull by the horns, and decided to tackle this version. I was fairly confident I could make it work, but I knew I was going to spend a fair bit of time figuring out how to play sound on it. I’ve avoided doing for years and it’s my secret shame.

    SDK Used: Chilly Willy 32X SDK, using my barebones32xproject as a base.

    Testing: My version of Gens Kmod emulator with custom fixes. On hardware, used a Sega Genesis Model II with a Sega 32X and an Everdrive X7 cart. I wish I could’ve been able to afford the Mega Everdrive Pro (hello Sega CD support!) but the X7 was advanced enough to sport a USB port so I could deploy directly to the hardware. Saved me lots of fiddling with copying to SD cards.

    Thanks again to John Bunday for testing on the PAL version of the Sega 32X.

    Platform Highlights:

    • Like the 3DO version I first tried converting the framebuffer to 8bpp on the fly and performance was actually pretty comparable! But it wasn’t a viable solution. I love the fact that the 32X is basically just a framebuffer, but it’s way too slow to draw on.
    • So I went with a dirty rectangle solution, very similar to how the original game works. I draw all the objects on screen to the 32X’s framebuffer and keep track of the areas I’ve drawn to. Next frame, I restore the dirtied areas of frame buffer with the clean buffer. Then redraw the sprites.
    • Like most other ports now, I extract the sprites and convert them to 8bpp CRT artifact’d versions.
    • The secondary SH2 barely does anything. It runs the sound DMA interrupt. I think I could’ve put it on the main CPU. So yeah, I’m very much not using the hardware to its fullest.

    Challenges:

    • By far it was sound playback. It took an eight day side quest to finally figure it out for good. It works and I’m proud that I did it after avoiding it for so long.
    • Integrating the sound playback side quest code revealed problems on the Genesis side. The joystick state became buggy and inconsistent. I couldn’t figure out what the problem was but switching the Genesis code to a simpler version made the problem go away. I absolutely love the 32X but it’s this kind of thing that doesn’t make me entirely enthusiastic about making software for it. I’m not patient enough to learn its intricacies.

    Known Issues: The sound playback isn’t quite 100% solid. Chilly Willy mentioned that a small number of SH2 CPUs have an interrupt bug that needs to be handled correctly and I don’t think I am. I think the overlap of people who have a faulty 32X and who want to play Downland will be very small so barely anybody should be seeing any issues. *crosses fingers*

    If I Could Go Back: I’d make sure the sound playback implementation is perfect. I’d need a faulty Sega 32X to test it with, though.

    Final Thoughts: I felt a real sense of relief that I could finally finish a complete project on the Sega 32X. Getting that sound working was a definite highlight. I still waver if I ever want to make another full project on it ever again. I still dream of reverse engineering After Burner 32X to discover how they managed to get such great performance.

    Sega Genesis (15 days – Aug 9 to Aug 24)

    Here I’m working on familiar territory. I’ve been working on various Genesis homebrews since maybe 2011. I had no doubts that the machine could run the game at full speed. The 68000 is a monster.

    SDK Used: No question, Stef’s SDGK. The gold standard. Don’t be silly using anything else.

    Testing: My version of Gens Kmod emulator with custom fixes. On hardware, the same Sega Genesis Model 2 with an Everdrive X7 cart.

    Thanks to John Bunday for testing it on a PAL Mega Drive.

    Platform Highlights:

    • I had lots of pre-existing projects I could’ve based this one on, but it had been a long time since I had tried the latest version of SGDK. So I started from scratch. It was still pretty straightforward to get up and running.
    • For generating the sprites and background tiles, instead of doing it at game start up I decided to build an command line tool that would extract and generate them as a build step. I wanted to experiment with this to get ready for the upcoming 8-bit versions of the game. Those versions won’t have the horse power nor the room to generate the sprites and tiles on the fly. The DLExporter tool I wrote generates 8bpp pngs of all the sprites and the deduped background tileset, as well as C code for tilemaps.
    • Because the hardware is tile based, writing text could only be done on 8×8 tile boundaries, so they’ve moved around compared to the original.
    • It still uses the framebuffer and clean buffer to perform object collisions, but this is the last version that does so.
    • The game uses the XGM2 sound driver and I was real thankful that there was a good pre-made solution to play wav files. There was no way I was going to write a Z80 sound driver. I’d rather write COBOL. (ok, maybe not)
    • The SGDK’s resource builder automatically converts .wav files to the correct data format for playback. This too was really handy and a lifesaver.
    • The XGM2 sound driver supports three sound channels. Downland only really plays one sound at a time but I spread out the samples to different channels for the heck of it. It was free.
    • The achieve the transition effect, I draw the game on the background plane (Plane B) and the transition effect was performed with the foreground plane (Plane A). The animated moving line was done using pre-animated tiles of the transition line and I updated the foreground plane’s tilemap every frame.

    Challenges: I can’t remember anything that was particularly difficult. Working on the Genesis is always very smooth.

    Known Issues:

    • The sound can be scratchy. I haven’t been able to figure out if it’s the driver or the source samples.

    If I Could Go Back: There’s no much I can do to improve this version. Could try to improve the sound.

    Final Thoughts: Everything went great just as I hoped. It’s no surprise why there are so many Genesis homebrew games out there. The hardware is powerful with a great dev toolkit, and it’s well supported with a large community of developers and fans.

    Sega Master System (36 days – Aug 24 to Oct 2)

    This is where the real test began. Could I get the Downland_C code running at full speed on an 8-bit console? I knew I could get something running but I wasn’t sure if I could get 60fps.

    Don’t let the long 36 day development time fool you. I had a great time working on this version.

    SDK/Tools Used: Like the SGDK on Genesis there’s only one devkit for SMS and that’s devkitsms by sverx. I used SDCC for compiling, PSGLib for sound playback, and Deflemask Legacy for composing sound effects.

    Testing: Emulicous by Calindro for emulation. Its debugging features are excellent. On hardware, as I don’t have an actual Sega Master System, I once again used my Genesis Model 2 with an Everdrive X7.

    Thanks to John Bunday for testing it on a PAL Sega Master System.

    Platform Highlights:

    • DLExport doing a lot more work, exporting game data in addition to graphics. I’m not embedding the original ROM in the game anymore to save on ROM space.
    • Optimization was the most important thing. The core game code ran but was way too slow as-is. I had to fork the game code into a special 8-bit version.
    • The framebuffer was removed. Object collisions were now handled with bounding boxes only.
    • The clean buffer isn’t built on demand. The clean buffer for each chamber is now pre-built in ROM. .
    • Struct types have been eliminated and converted into global variables.
    • Lots of profiling later, using Emulicious’ great debugging tools, managed to hit 60fps. Worst case performance scenario used only 80% of CPU time. This made me so happy.
    • To help achieve 60fps, the game alternates collision checks between the drops and everything else every frame.
    • The game uses 8×16 sprites to reduce the number of hardware sprites used. Two 8×16 sprites are used for the player, ball, bird, and pickups instead of four 8×8 sprites, reducing processing time.
    • The entire code managed to fit under 32k without trouble which was nice, and later on managed to get that down even further to under 16k, fitting in one single bank. This was useful for the SG-1000 and ColecoVision versions.
    • The tiles used by sprites and backgrounds barely just fit in the VDP tile memory which was also nice. I had to trim the number of pre-drawn regeneration sprites a little, though.
    • The hardware limit of sixty-four sprites limit wasn’t a big problem. There are areas where the game uses more than eight sprites on a line, but I implemented a flicker mitigation strategy to minimize it.
    • Unfortunately I couldn’t pull off the vertical per-line screen transition effect but instead I did a full screen horizontal wipe effect that I think still works as a Downland-authentic screen transition. The wipe effect changes direction depending on which door you enter.
    • Bank switching wasn’t much of a burden. With data for one chamber stored in one bank, and everything else in the fixed bank, I just switched banks whenever the player changes chamber.

    Challenges:

    • The SDCC compiler output was unpredictable. One function would be fine but a similar looking function would have crazy bad performance. I couldn’t even come up with strategies to guess what kind of speed difference I’d get. I had to benchmark every change.
    • One optimization example: the code that displays the timer at the bottom of the screen was more expensive than the entire player update function. I spent an unexpected amount of time optimizing it.
    • Sound playback, my old friend, once again gave me trouble. Trying to reproduce the original Downland sounds was a long wild goose chase. I spent six days trying to make it work but everything I tried was a dead end. Once I accepted that I should just make sounds from scratch using the PSG hardware as-is, I managed to move forward.
    • The VDP sprite collision flag wasn’t as useful as I had hoped. While the game basically just checks collisions between the player and everything else, the hardware sprite flag is set whenever ANY sprites overlap. So common situations like having two drops on top of each other, or the ball touching a pick up would activate it. In truth it wasn’t a big issue because bounding box collisions were sufficiently fast, but finding a way to get collision checking for free would’ve been nice.

    Known Issues: None that I remember.

    If I Could Go Back: I freed up so much more memory with the SG-1000 version that if I wanted I could remove the pre-made clean buffers in ROM and implement a true clean buffer in RAM. The 128k ROM size it currently is wouldn’t be viable had the game released for the Sega Master System in the 1980s. It would’ve been too expensive for what it was.

    Final Thoughts: I didn’t know it at the time, but getting this version to work set the foundation for three more ports (Game Gear, SG-1000, ColecoVision). The challenges I encountered were the good kind. Learned at lot. I’d sure like to do another Master System project one day.

    Time Bonus!

    At this point the core list was finally done. But I didn’t feel like I was finished. I still had enough gas in the tank to keep going.

    SG-1000 (9 days – Oct 3 to Oct 11)

    The SG-1000 was Sega’s first home console, the Sega Master System’s predecessor. While its graphics capabilities are more primitive than the Master System and has only 1k of memory, it has the same Z80 CPU at the same clock speed and the same PSG sound chip. I hadn’t planned on making a port to it but with the hardware being so close I just had to try.

    SDK/Tools Used: Once again devkitsms by sverx. SDCC for the compiler. PSGLib for sound.

    Testing: Emulicous by Calindro. I couldn’t test on hardware since I have neither SG-1000 nor Sega Master System.


    Platform Highlights:

    • All the performance issues were already solved!
    • In a reversal, the sound was finished first!
    • Sprites are entirely one color, chosen from a fixed palette of 16 mediocre looking colors. There’s no CRT artifacting effect available to simulate more colors. I had to choose colors for each type of sprite and update their graphics to make them look presentable. I made the bat-bird more bat-like.
    • The SG-1000’s VDP is real quirky: the background tileset is actually stored three times in VDP memory. Each tileset is used by one third of the screen. Never saw that kind of configuration before.
    • The SG-1000’s VDP is real quirky, part 2: each tile is actually 1-bit but each row can have one color. It affected the background graphics a little bit, basically just the tiles with horizontal ropes, but it’s barely noticeable.
    • The sprite hardware has a limit of 32 sprites on screen with a limit of no more than 4 sprites per line. That seems like a worse limitation than on the Sega Master System but it turns out one sprite can be a full 16×16 pixels. The game is just a little bit more flickery when there’s a lot of drops on the same line. The game runs probably a bit faster because it only needs to handle one sprite for pickups, the ball, the bird, and the player.
    • I managed to get the RAM usage to less than 1k. I was already hovering just above it on the Sega Master System and at the time I couldn’t find a way to bring it lower. The main problem was that I was using two instances of player data, one for each player. I couldn’t fit them both and I had to consider removing the two player mode. Then I realized that the original game actually managed to fit two players with only one instance of player data. All the code I had converted already supported it! So once I realized that I just got rid of the second player data and everything fit into memory. I’d like to thank Michael Aichlmayr for his foresight.

    Challenges:

    • Like the Sega Master System, the rom is larger than normal because of the pre-drawn clear buffers in the different banks. The problem is that on the SG-1000, carts don’t support banking by default. The port assumes that it’s running on a cartridge that does. So the game is pretty much emulator-only (Emulicious-only?) at the moment.

    Known Issues: I can’t confirm if it’ll run on hardware.

    Final Thoughts: This was also a fun port. It felt good to still find optimizations that were worthwhile.

    Game Gear (6 days – Oct 12 to Oct 17)

    Man, if I did Master System and SG-1000 might as well do the Game Gear!

    SDK/Tools Used: Everything is the same as on SMS. devkitsms by sverx. SDCC for the compiler. PSGLib for sound.


    Testing: Emulicous by Calindro as usual. I couldn’t test on hardware since I don’t have a real Game Gear, either.


    Platform Highlights:

    • With the exception of the screen size and minor handling of its extended color palette, the game is identical to the Sega Master System version.

    Challenges:

    • The Game Gear’s screen is 160×144, even smaller than the Gameboy Advance’s. Had to rework the title and the get ready screens, of course. You can see a lot less of the area around you so the game is much more unfair to play.
    • Really had to simplify the in-game UI and I had to use sprites to do it. Now it’s just one life icon with a life count at the top left corner and a timer at the bottom right. I couldn’t fit anything else.


    Known Issues: as many as the Sega Master System version.


    Final Thoughts: This was a quick straightforward port. Compared to the original Sega Master System port, this one was almost free.

    NES (18 days – Oct 18 to Nov 8)

    I grew to hate working on this port. This was my first real NES project and I don’t think porting Downland to it was the best first project for it.

    SDK/Tools Used: I chose llvm-mos because I wanted a modern optimizing compiler and it was in active development. The nesdoug samples were super helpful.


    Testing: Mesen for emulation. Couldn’t test on hardware.


    Platform Highlights:

    • I just needed a simple bank switching cart so nothing fancy like an MMC1. So I chose the UNROM format. It has one fixed 16k PRG bank and 16k PRG swappable banks. I also only needed 8k in CHR data so it fit the bill.
    • All the sprite and tiles could fit in a single CHR page, so that was nice. I appreciated the “export all tiles as a block in one shot and never think about it ever again” aspect of it.
    • The famitone2 sound library doesn’t support looping sound effects, but it supports looping music. So the looping sound effects are handled like music tracks.
    • Due to the previous 8-bit work, performance wasn’t an issue at all.
    • Even though its available, I didn’t use any sprite flipping. Like the Sega Master System version, both the left and right versions of the player sprite are loaded.


    Challenges:

    • By far the single most aggravating issue about the whole port was trying to fit the game code into the fixed 16k PRG bank, and failing. It felt like the compiled C code for the 6502 was just so much fatter than the Z80 version. All through the port I continuously had to keep finding ways to move code around so that everything would fit, even going as far as adding special cases the core game code. I wasn’t very happy about that.
    • When the code in the fixed bank would suddenly jump from a svelte 13k to over 16k, busting the limit, I really struggled to figure out the reason why. The llvm-mos team really helped me on figuring out that one.
    • Just when I had figured out my fixed bank struggles, I had to then integrate sound effects and the famitone2 library. I eventually got everything to fit, but at what mental cost???


    Known Issues: None that I can remember.


    Final Thoughts: I did not enjoy doing the NES port at all. I wouldn’t port a C-based game to it like this again. I’d probably start from scratch with an assembly language version.

    ColecoVision (1 day – Oct 20 to Oct 21)

    While I was working on the SG-1000 version sverx started implementing ColecoVision support to devkitSMS. Once he had a new version ready to go I went off and ported the game to it. I took a break from the NES version to do it.

    In terms of hardware it’s essentially identical to the SG-1000 and there were very minimal code changes.

    SDK/Tools Used: devkitsms by sverx. SDCC for the compiler. PSGLib for sound.


    Testing: ColEm, emultwo, and Gearcoleco. I don’t have a ColecoVision either so I don’t know if it actually runs on hardware.


    Platform Highlights: It’s essentially identical to the SG-1000 version, with slightly different on-screen colors. I did however revisit the SG-1000 version beforehand, bringing down the entire game code to under 16k which made it fit into the ColecoVision ROM address space.


    Challenges: None. Unless you’re talking about playing with those awful controllers.


    Known Issues: Just as many as the SG-1000 version


    Final Thoughts: The port was trivial and basically done in a day. I was quite happy to unexpectedly have a ColecoVision port because it’s my wife’s favorite console.

    TurboGrafx-16 (aka FAIL aka The End)

    I had spent some time looking into making a port to the TG-16. I couldn’t decide whether to use llvm-mos again or to use HUCC, so I experimented with both. In the end I couldn’t get past HUCC’s quirks so I went with llvm-mos, but I soon found out that its support for the TG-16 is very barebones. I couldn’t easily get it off the ground.

    With the NES port having killed a lot of my momentum, me having ported the game thirteen times already, and the end of the year approaching, I decided to stop all my porting efforts there. There was no more gas in the tank.

    Maybe I’ll revisit it once llvm-mos gets more TG16 support.

    Final Final Thoughts

    I’m done! Finished! No more! Here are my final thoughts!

    • Completed thirteen ports!
    • Super proud that the game runs at 60fps on everything.
    • Also happy that I managed to get so many versions done in a year.
    • With the exception of object collisions, managed to get the gameplay correct on every platform.
    • There are only very minor bugs!
    • Successfully ported to several consoles I had never developed on before!
    • I’m happy that every port increased the portability of the game code. With the exceptions of the NES and 3DO ports, I didn’t have to add platform-specific parts to the core game code. I’m very confident that the game can now run on anything.
    • Devkitsms is so good, handling four different platforms. Now it only needs MSX support (hint hint!)

    Dreams of Future Ports

    I’d like to think that the list of ports I did was “Phase One” of porting Downland to everything. But if I had the energy again what would a Phase Two look like?

    I’d next like to concentrate on more neglected platforms. I found SDKs for the CD-I and the Atari Jaguar. There’s the NUON too which I’ve never looked into. Making an M2 version would be the ultimate neglected console achievement.

    I’d also like to make versions for Linux, MacOS, and the web.

    So I’d probably start with the above.

    Of course since I finished the Sega console pantheon I’d have to do the same for Nintendo. I’m sure I’ll have an easier time with the SNES, Nintendo 64, GameCube, Wii, DS, and 3DS. Maybe the original Gameboy.

    Having done the Saturn version I’d like to try the original PlayStation and the PSP.

    I’d like to try porting Downland to arcade hardware. Platforms like the Capcom CPS, Outrun Hardware (there’s an SDK!), and the Neo Geo.

    The ports I’ll least likely make are for 8-bit computers, contemporaries of the Tandy Color Computer like the Commodore 64, Apple II, and Atari 400/800. I have a lot less confidence that I’d be able to pull them off. But a MSX port should be trivial, devkitSMS willing.

    Versions for the Amiga and Atari ST are much more feasible if I tried them.

    What would be really silly is if I re-ported the game back to the CoCo. I wonder if I could get it to run full speed again. Maybe one day!

    So that’s what a vague Phase Two (or Phase Three?) plan would look like.

    So What Now?

    With this post, I am taking a break from everything Downland. It’s still always on my mind, but I want to work on other projects for a bit. It’s been taking up all my free time since November 2024 so this is a good opportunity to recharge my batteries. I’ve been dabbling here and there on different things but I still haven’t been able to find that long term project I really want to get into. But I don’t mind experimenting in the meantime.

    Until Downland pulls me back,

    thanks for reading!

  • Downland Unearthed #7: Background Collisions

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    The moving objects in the world of Downland need to know the limits of where they can go. They can’t pass through walls and they can’t fall through floors. The game therefore needs a method to detect whether an object can move through space or is stopped by the chamber’s terrain. And because chambers are made out of irregular shapes, the method can’t use a simple grid as seen in tile-based games.

    What Downland uses is a method that detects per-pixel accurate collisions which, while memory hungry, provides the player with fun platforming tricks.

    Mr. Clean

    When the game draws its graphics, it does it into a frame buffer, a region of the Color Computer’s memory. The computer hardware displays this area as pixels on the screen. To achieve pixel perfect collisions, the game uses a second buffer, which I call the “clean buffer”, which contains a copy of the chamber’s background graphics.

    Game’s frame buffer is on the left, the clean buffer is on the right.

    The clean buffer is created when the player enters a chamber. First, the game draws the background to the clean buffer. Then a screen wipe transition effect copies the clean buffer to the frame buffer line by line.

    Bzzzzzuzuuzzzut

    After that, all drawing is done on the framebuffer, leaving the clean buffer intact for the game to use.

    Memory Use

    The frame and clean buffers are unfortunately not free. Both buffers, because of their 256 * 192 one bit resolution, take up 6144 bytes each in memory. This means that only Color Computers with a minimum of 16k of memory can play the game.

    Collision Fundamentals

    We’ve seen that internally Downland’s graphics are actually 1 bit and look like this:

    I’m actually blue!

    The same bit patterns that appear blue on screen are used as barriers like walls and floors. Blue pixels have a bit pattern of [0, 1]. To detect collisions when an object is moving, the game reads the pixels around it on the clean buffer. If the pixels at a certain spot are blue, matching its [0, 1] bit pattern, then a collision is detected and the object reacts. The spots checked depend on the type of object and the type of motion.

    Let’s go through the different game objects and where they detect collisions.

    Drops

    Drops when falling check for floor collisions every update. Using a pixel mask, it checks the four pixels underneath. And because it drops two pixels per frame on the Y axis, it performs another check one pixel lower.

    If the drop detects the floor, the drop manager resets the drop into wiggle state and chooses a new random location.

    Ball

    The ball performs two collision checks, one for vertical movement and one for horizontal.

    For falling, the game checks for the two pixels underneath the bottom of the ball. It doesn’t need to check any pixels below that like drops because the ball doesn’t fall faster than one pixel per frame.

    For walls, the game checks a line across the center of the ball.

    The Player

    For collisions with the ground, the game checks below the player sprite right in the middle. The check is in the same spot no matter how the player is facing.

    For wall checks, it uses the same ground detection area, except one pixel higher. This is used for stopping against walls when running and bouncing off walls when jumping.

    Showing The Ropes

    The player has one final check for detecting ropes. Notice that it’s offset to the left, meaning jumping left towards a rope will grab it faster than jumping towards the right.

    Fun Fact! When I was a kid I was sure you had to hold up on the joystick just as you reached the rope, otherwise you jumped through it and fell to your death. I only learned much later in life that the player actually automatically grabs the rope. Had I known that then it would’ve saved me pain playing with those horrible, horrible Tandy joysticks.

    Rope Quirks

    The interesting thing about the rope detection is that it only checks whether it touches white pixels on the clean buffer. It doesn’t matter if the pixels are from a vertical or horizontal rope.

    Horizontal Ropes

    For horizontal rope traversal, the game relies on the fact that if you press and hold left or right while in a climbing state, the player will hang off the side and then fall off.

    In the code, shimmying across a horizontal rope is actually the player repeatedly grabbing a rope, moving to the side, falling off, and then re-grabbing before gravity starts to affect the player’s vertical position.

    I thought this was a pretty clever way to introduce a fun traversal mechanism with existing functionality.

    More Rope Quirks (last time I swear)

    When the game draws objects, it blends pixels together. So overlapping the player over objects and the background appears like so:

    Pixelicious!

    This actually also happens with ropes:

    To prevent this, the game erases the rope behind the player, so that the sprite appears correctly.

    But it only erases the rope when the player is in the climb state. You can see the player and rope blending together when jumping on or off.

    Fun With Collisions

    Something you don’t see often with games of the era, Downland’s per-pixel collision detection lets the player jump at a wall and land on a ledge:

    This is really fun to do and is actually a technique that the player needs to master because in chamber 5 there is a key needs to be picked up this way.

    It also lets the player bypass obstacles, like in chamber 3 where they can avoid the tricky rope jumps by just jumping across on the rock.

    Porting Considerations

    The large memory requirements of the frame and clean buffers made porting Downland on certain retro consoles an interesting challenge. While platforms like the Sega Genesis, 32X, Saturn, and Dreamcast have enough memory to hold both the frame and clean buffers in memory, older platforms like the Sega Master System, SG-1000, ColecoVision, and NES do not. The ports to those older platforms had to eschew the frame buffer, which was also used for object collision detection, and use pre-drawn clean buffers stored in ROM. These pre-drawn clean buffers take up significant ROM space (12 screens at 6k each). At the price ROMs cost back in the early 1980’s I don’t think a version of the game on those platforms would’ve been viable.

    Side note: The Sega Master System port of Downland stores the clean buffers in ROM, but it turns out it doesn’t have to. The console has 8k of memory. The original port I did used over 2k of ram for game variables, not leaving enough space for a clean buffer. The rom wound up to be 128k in size. However, the SG-1000 port I did after heavily optimized memory usage and dropped it to less than 1k.. So if I wanted to, I could revisit the SMS version to use a clean buffer, removing the need to store all the per-room clean buffers in ROM, making the game smaller. But I’m lazy so who knows if ever that’ll happen!

    Next Time

    And that’s it for background collisions. While memory hungry, it provides some fun and unique gameplay features. Next time we’ll look at how the frame and clean buffers are used for player-object collisions.

  • Downland Unearthed #6: Boulder Ball Bat Bird

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    Look at them rainbow colors! I am an artiste!

    Downland has a very barren ecosystem. Other than the unrelenting acid drops of death there are two other beings roaming and lurking in the depths of the game’s chambers: the ball and the bird.

    The Ball Boulder

    In Downland the main active enemy you face other than the drops is what I call the ball, even though it’s probably a boulder. You know, a boulder that squashes and stretches when it bounces across the screen.

    I can only guess at its inspiration. Was it inspired by Indiana Jones: Raiders of the Lost Ark’s rolling boulder scene? I thought maybe it was inspired from the bouncing springs in Donkey Kong’s elevator level, but an email from Mr. Aichlmayr’s disproves this.

    In any case, in the reverse engineered assembly code and the C version I call it the ball. It’s the name that came to most naturally to me. I told myself I can name it anything I wanted as long as it’s consistent. I could’ve named it Jennifer.

    Every Day The Same Again

    The first thing that struck me about the ball behavior is that no matter what room it appears in, it always spawns in the exact same spot. It spawns at X=101 and Y=116 in logical game space, appearing at X=202 in screen space. I never realized this playing the game, even after all these years. Props to Mr. Aichlmayr for making the chambers’ geometry varied enough to mask that fact.

    Do The Locomotion

    The ball’s position uses 16 bit fixed point values. Eight bits are for the integer part and the other eight bits are the fractional part. The ball moves along the X axis 88 fractional units to the left. In comparison, the player moves 56 fractional units. The player is constantly getting mowed down by the ball. And even if the player manages to dodge it, its repeating nature just means they’ll only have a few seconds before it comes back.

    When the ball is falling its speed is increased by 18 fractional units every frame with a maximum speed of 256. When it hits the ground, it sticks to it for 4 frames (showing the squished animation frame), then bounces back up with a speed of -256 and a gravity of 10. When bouncing up, it counts 10 frames and then sets the vertical speed to 0, letting the falling gravity take effect again. This gives the effect of the ball falling faster than going up, squishing hard on the ground.

    When part of the ball overlaps a blue pixel on the background, it restarts. Here I changed the rope color to blue.

    When it collides with the terrain, the ball resets its XY position, but doesn’t reset its vertical and horizontal speeds. This has the effect of changing the ball’s trajectory as it can be in the middle of jumping up or falling down. This makes it so the player can sometimes find temporary safe spots in certain chambers, like here on the bottom stairs in chamber two:

    On every other cycle, the ball bounces clear over the player, providing a brief respite.

    Room With a View Ball

    In theory the ball can be used in any chamber. The only thing that limits this is a table that specifies which chamber it appears in. The table contains these values:

    roomsWithBouncingBall[10] = 0, 2, 5, 6, 10, 11, 12, 13, 14, 0xff

    When the chamber initializes, it goes through the values of the table to find the current chamber number. If the current chamber’s number is found, then the ball is spawned. If it’s not, it reaches 0xff and skips the ball initialization.

    Cut Content?

    The most fascinating thing about the table above is the set of values 10, 11, 12, 13, 14. As the values are chamber numbers, could this be evidence of cut chambers? Did at one time the game contain five more chambers? Or are these just place holder values? We’ll likely never know. As far as I can tell, this is the only existence of unused data I’ve found in the whole ROM. It’s fun to think about what they could’ve looked like if they had existed.

    It’s a Bird, It’s a Bat. It’s Bat Bird.

    Drops are random but easily avoidable. The ball is hard to dodge but predictable. When the chamber’s timer hits zero, however, all hell breaks loose. The bouncy zigzagging winged terror of doom is unleashed. Don’t let its simple bird-like appearance fool you. If you see it and you’re not anywhere near a door, consider yourself dead in advance.

    Your worst nightmare

    Like the ball, no matter which chamber, the bird always spawns at the same spot. It starts at X=35 and Y=26. It has a base X and Y speed of 256 fractional units and then a random speed between 0 and 255 is added on each axis. That means it’ll always spawn going down and to the right but at different angles.

    Unlike the ball, the bird doesn’t try to detect the chamber’s terrain. It just flies straight in a direction until it hits an invisible wall and then reflects off. The walls are defined by a bounding box with screen coordinates (14, 16) for the top left and (240, 177) for the bottom right.

    It continues to bounce around until you change rooms or die. If you die for any reason, the bird disappears and the timer is reset to 2048, giving you a little more time to escape.

    Next Time

    With all these hazards going around, I haven’t yet explained how the game detects collisions between them and the player. I hope to cover that in the next installment.

  • Downland Unearthed #5: Backgrounds

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    Generating Downland’s backgrounds is easily the most complex part of the game’s code. It doesn’t store its backgrounds as tiles organized on a tile map like on a console, nor does it store the background as an image. In actuality, its backgrounds are built from a string of pre-defined shapes drawn end-to-end by a virtual plotter.

    Shapes

    Downland uses twenty-two different shapes to achieve its background layouts.

    Since the original assembly doesn’t have any names for the shapes, here are the names I gave them in the C version.

    Regular shapes

    0 Stalactite
    1 WallGoingDown
    2 LeftHandCornerPiece
    3 TopRightHandCornerPiece
    4 TopRightHandCornerPiece2
    5 BottomRightSideOfFloatingPlatforms
    6 FloorPieceGoingRight
    7 WallPieceGoingUp
    8 CornerPieceGoingDownLef
    9 FloorPieceGoingLeft
    10 ShortLineGoingDown
    11 ShortLineGoingUp

    Vertical ropes

    12 VeryShortRope
    13 ShortRope
    14 MidLengthRope
    15 LongRope
    16 VeryLongRope
    17 SuperLongRope
    18 ExcessivelyLongRope
    19 RediculouslyLongRope

    Horiziontal ropes

    20 Horizontal RopeStartGoingRight
    21 Horizontal RopeEndGoingRight
    22 Horizontal RopeGoingRight

    Parts & Segments

    Shapes are made up from one or more smaller parts. And each part is made up of one or more segments. Each segment is a line with a direction and length. Most shapes are made up of just one part but the ropes are made out of multiple.

    Here is a breakdown of the Stalactite shape.

    The Stalactite is made out of one part, and that part is made out of three segments. Segments start from the previous segment’s ending position and so their start and end points overlap.

    And here is a break down of the ShortRope shape.

    The ShortRope, like all the rope shapes, is made out of three parts (A, B, C). Parts A and C are made up of one segment each while part B is made out of two.

    Harry Plotter

    The shapes are defined as a series of segments because the backgrounds are drawn using a plotter-like system. From a starting point the plotter goes through the segments of each shape, drawing into a buffer one line at a time. This is similar to how old arcade games like Asteroids and Tempest draw vectors to a CRT display.

    Plotter Colors & Hidden Links

    The plotter can be configured to draw blue, white, or black lines. Black lines are used to give the illusion of floating platforms or sub-rooms. Below are images of chambers with their hidden lines revealed in white.

    Part Data

    This is what the data for the Stalactite’s one and only part looks like. Each segment is made up of movements along the X and Y axes plus an orientation. The orientation determines which is the major axis between the two.

    Shape Data

    There’s no actual data that defines a shape. Each shape has a function that instructs the plotter which parts to draw and how to draw them.

    Segment Drawing

    Drawing a segment to the screen works similarly to sprite drawing wherein the data assumes a 128×192 resolution but the actual drawing is done in 256×192.

    The segment is drawn with white double-wide pixels but a CRT mask is applied on top that makes it appear blue. Ropes are drawn without the CRT mask to appear fully white. No background shapes use orange.

    Putting It All Together

    Please enjoy this video of all the chambers being drawn one shape at a time.

  • Downland Unearthed #4: Shifted Sprites

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    In the previous post I talked about how Downland’s 1-bit graphics are setup to take advantage of CRT artifacts to generate colors. In this article I talk about how Downland draws its sprites to the screen.

    Sprite Hardware? What Sprite Hardware?

    The Tandy Color Computer doesn’t have built-in sprite hardware like the Atari 400 or the Commodore 64, where the programmer can just set a few registers and sprites are conveniently placed on the screen. For the CoCo, drawing sprites requires directly manipulating the memory that is used for the screen.

    Screen Memory

    What the player sees as graphics is represented by a specific region in the CoCo’s RAM. Turning on bits in that region of memory will activate the corresponding pixels on the screen.

    Writer’s Block

    One problem with working with Downland’s 1-bit graphics is that you can’t directly address the bit/pixel you want to turn on. 8-bit CPUs like the Tandy Color Computer’s 6809 can only write to memory one byte (eight bits) at a time at the minimum. They can’t write individual bits. So for any changes you want to make to screen memory you have to do it in bytes.

    Another limitation is you can’t write that byte anywhere. It has to be written along byte boundaries. This means you can only write a byte at every eight bits (0, 8, 16, etc). You can’t write a byte at bit 3 and expect it to change bits 3 to 10.

    Sprite Basics

    Because the screen is 1-bit, Downland’s sprites are naturally 1-bit as well, as seen on the player sprite below. Each frame of the player sprite is 16 pixels wide and 16 pixels tall. As the graphics are 1-bit, the 16 pixels for each row of the sprite take two bytes. The first byte of each row contains the front of the player sprite while the second byte contains the back.

    8-Bit Drawing Limitations

    Because the CPU can only write 8 bits at a time, if you wanted to draw the sprite as-is to the screen you could only do it on byte boundaries. The result is the sprites appear at every 8th pixel on the screen horizontally.

    Note that the sprite can be placed anywhere vertically. There’s no limit there.

    Still, this is obviously very limiting for a platforming game like Downland which has objects going across the screen.

    Shifted Sprites

    The trick Downland uses to draw sprites along the horizontal axis is by copying the original sprite and creating new versions that are shifted to the right by a certain number of bits. These are called shifted sprites or bit-shifted sprites.

    Things to note:

    • In the game the sprites are shifted by two bits (aka pixels) instead of one because while the screen resolution is 256 pixels across, the game logic is actually half at 128. So for every X position in the game’s coordinate system, objects are drawn at twice that. Therefore it only needs shifted sprites for even-numbered positions.
    • It also shifts by two bits to maintain the CRT artifacting effect.
    • Because we only need shifted sprites for the even positions along the byte, only four shifted sprites are needed for each sprite.
    • Shifted sprites go past the second byte so they now need take up three bytes for every row.
    • There’s no version of the sprite for being shifted by 8 bits or more because at that moment you can just use the first shifted sprite again. It wraps around.

    Drawing Shifted Sprites

    The game uses the sprite’s X position to determine which shifted sprite to draw. The game takes the lower two bits of the object’s X position, which gives a value that goes from 0 to 3. It uses that value to look up which shifted sprite to draw. Zero uses the first shifted sprite, 1 uses the second, 2 uses the third, and 3 uses the fourth.

    The image below shows bit shifted sprites being drawn on screen with different X values.

    Memory Usage

    Shifted sprites are generated when the game is turned on and are stored in memory.

    As there are four shifted versions of every sprite of every object, it can take up a fair bit of memory. For example, the player’s ten frames of animation take up:

    10 frames x
    4 shifted versions x
    3 bytes per row x
    16 rows:
    1920 bytes

    With the game taking 6k bytes for the screen, and another 6k for a clean version of the background, it really starts to impact the 16k of the original Tandy Color Computer. But everything fits.

    Which Sprites?

    Shifted sprites are generated for the player, the ball, and the bird sprites. The ball and bird sprites each have two frames of animation.

    Exceptions

    The drops, the splat sprite, and the door have their own particularities when it comes to drawing them shifted.

    The drops are actually pre-shifted in the ROM, as seen here:

    Side thought: I wonder if the drops were the sprites Mr. Aichlmayr made first and then immediately realized that storing shifted versions of all the other game objects was going to take a lot of ROM space and changed strategy. Or the change in strategy came at the end of development trying to reduce the game’s final ROM size.

    For the splat sprite, it’s the only one in the game that’s 24 pixels wide.

    Unlike the other sprites above, where the shifted versions are pre-drawn, drawing the shifted splat sprite is done on the fly. When the player gets squashed the splat sprite is drawn shifted into a temporary offscreen buffer. The buffer is 32 bits (4 bytes) wide so that the shifted sprite can fit. Then the offscreen buffer containing the shifted splat sprite is drawn to the screen.

    Fun fact: the splat has a two frame animation when the player dies but there’s no second frame stored anywhere in rom. To achieve the animation, the splat sprite is first drawn to the screen and some time later the top part is erased directly from the screen’s memory.

    The door is also drawn on the fly, using the same technique as the splat sprite.

    It’s drawn when a door gets activated and also as part of the background drawing when the player enters a chamber.

    Pickups

    Finally, the game treasures (key, diamond, money bag) are the simplest drawn objects in the game. They don’t use shifted sprites at all as they’re always drawn at a multiple of 8 pixels.

    Wrapping Up

    Lacking dedicated hardware support, shifted sprites is a commonly used technique for Tandy Color Computer games to draw objects on the screen. It lets sprites to be placed anywhere on the horizontal axis at the expense of memory. Downland uses the technique in various ways to draw its game objects. In the next article, I’ll talk about how the background graphics are drawn.

  • Downland Unearthed #3: Ancient Artifacts

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    If you look at the game’s video memory, internally Downland is actually a 1 bit game. The graphics are effectively black and white.

    But the game actually has color. How does that work? Notice the spacing between the pixels on the floor and the diamond. The game draws its graphics in such a way to take advantage of CRT artifacting effects. These effects helps the game simulate a limited set of colors. This was a common technique used at the time, as seen on other computers like the Apple II.

    Colors

    Because of how the NTSC television signal works, different pixel patterns determine which color will be displayed and how it blends with other colors.

    On the CoCo’s 256×192 graphics mode, each pixel is represented by a single bit. The artifacting in this mode interprets pairs of bits like so:

    • 00 will be black
    • 01 will appear blue
    • 10 will appear orange
    • 11 will be white
    • A blue pixel next to an orange pixel (01 10) will also be white

    The Tandy Color Computer also has a quirk where the blue and orange colors can be swapped when it powers on.

    (To me, the blue version is the canonical version.)

    The above is a very horribly simplified explanation of the Coco’s CRT artifacting. A much deeper dive on how all this works can be found in this great Coco Town YouTube video:

    But in practical terms, the graphics mode effectively gives a 4 color 128×192 screen.

    This “effective” resolution explains what I was saying about drop placement in the previous Downland Unearthed article. The game logic works at 128×192 while the graphics are drawn at twice the horizontal resolution. This counts for both the background and sprite drawing.

    Another way of saying it is sprites need to move horizontally every two pixels, to maintain color stability when going across the screen. Otherwise the pixel colors would cycle like a (really limited) rainbow.

    Here’s a capture of a hacked version of Downland_C simulating the player and the ball being placed every pixel instead of every two:

    One-Bit Shapes

    What I find interesting is the way the sprites have been drawn to take advantage of the CRT artifact effect. Here’s are side-by-side comparisons of the 1-bit graphics and the color graphics. The color versions were produced using the XRoar emulator in the the 5-bit LUT Composite rendering mode.

    If you stare at the player sprite too long, you start to notice that he’s got blue AND orange hair and you can’t tell what the orange and blue parts of his face are supposed to be. Is that a blue eye at the lower half of his face? He has identically sized mustache and eyebrows? Also, he has ridiculously long arms and his shirt either has a single button or he’s just showing off his nipples. It’s hard to tell as this resolution. And who knows what the white pixel under his hand is supposed to represent!

    Without color, Downland’s iconic bouncing enemy of doom is finally revealed to be just a killer walnut.

    For a diamond, it’s drawn pretty plain, isn’t it? Still, this orange upside-down triangle gives 400 points! (more or less…)

    M is for money, obviously. I like how the stray pixels add a kind of fringe around the top of the money bag.

    Dithering makes straight lines? *mind blown*

    I never know what the shapes on the doors are supposed to represent. A porthole and a wheel? Are they actually doors from a submarine? Is Downland actually set underwater?

    Rom Storage

    All the sprites in the game are stored with these patterns, as seen here using a raw pixel viewer to look at the rom’s contents:

    (Notice how some of the sprites like standing left and right are perfectly mirrored but some, like the jump, aren’t.)

    The one exception to this is the game’s font. It’s stored without effects. I couldn’t get the viewer to align the characters perfectly, but you can see that they’re made up of solid pixels.

    Each character is 8 bits wide and 7 rows tall. The character drawing function draws each row while applying (and’ing) a bitmask (01010101) on top to turn off every other bit. This causes the character to appear blue.

    I wonder why the mask wasn’t pre-applied. The game could’ve saved a few cycles when updating the timer and score to the screen. Maybe the author wanted to keep the font pristine for easy edits, not minding the performance hit. Or maybe he started with white text and then changed his mind.

    Half The Battle

    So that’s how Downland takes advantage of CRT artifacts to generate its graphics. One obvious unanswered question is how the graphics are actually drawn on screen. The graphics are 1-bit per pixel but writing to memory is done eight bits at a time as a byte. I hope to explain how Downland draws it sprites in the future.

  • Downland Unearthed #2: Drop It Like It’s Hot

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    Acid drops. If you die in Downland, chances are it’s because of one of these falling bastards.

    Never mind the ball with its repetitive and predictable path across the screen. These white liquid drops of death are random, fast, and relentless.

    Drop in a Bucket Chamber

    For each chamber, the game data defines a number of drop spawn areas. Each drop spawn area is defined by an X and Y position, plus the number of drop points it has across the area. In the first chamber seen in the image below, there are seven drop spawn areas (boxes in red):

    The first drop spawn area has thirteen points across it to spawn points from (marked in green).

    A curious note is that in the game data the number of spawn areas and the number of drops to spawn per area is one less than it should be. So for the first chamber, the number of spawn areas is stored as 6 (not 7) and the number of drops in the first spawn area is stored as 12 (not 13). I don’t know what the advantage of this would be, if any.

    Placing Drops

    Some important background: The game’s world size is 128 by 192 points. The horizontal coordinate system goes from 0 to 127. The graphics mode that the game uses is PMODE4, which is 256 by 192 pixels. That means a point on the horizontal axis corresponds to two pixels on screen. Placing an object at the horizontal position 64 means they’ll appear at pixel 128 in the middle of the screen.

    When the game wants to spawn a drop, it first picks randomly a drop spawn area. Then it picks a number between 0 and the number of drop points for that area. The drop is given a location that is starts from the drop spawn area’s XY position and then offset to the right by the the drop point chosen multiplied by eight.

    So like this:

    dropSpawnArea = pickRandomDropArea();
    randomDropPoint = rand() % (dropSpawnArea->dropSpawnPointsCount + 1)
    newDrop->x = dropSpawnArea->X + (randomDropPoint * 8)
    newDrop->y = dropSpawnArea->Y

    That’s generally the way it works, but there are two exceptions.

    The first exception is about giving room to the player. Look at the left side of the top most drop spawn area in the image above. Notice that the drop is actually offset to the left of the box where its supposed to spawn?

    Enhance!

    That’s because the game tries to detect whether there’s a rope nearby. It checks four points to the right and six points down. If a rope is detected, the drop is offset to the left. This gives enough room around the rope to let the player climb up without being burned to death.

    The second exception is about removing that room from the player and increasing the chances of them climbing up and getting burned to death. What Downland giveth, Downland taketh away. But why?

    In the drop spawning code there is a part that says that if ever the player has completed the game three times in a row (which we must all admit is some kind of superhuman feat) the X position of the drop is made even. As the positions of the drop spawn areas are on odd positions, it effectively moves all the drops one position to the left, which corresponds to two pixels in that direction. In the image below, the small bright red boxes are the ones that have been affected by that rule.

    This makes it so that the drops on the right side of the vine will always touch and kill the player, forcing them do perform rope acrobatics when climbing. This makes the game pretty much impossible to play, because drops fall on both sides of the rope simultaneously very often. Well, maybe it’s not impossible but the chances are definitely not in your favor if ever you manage to get to that point.

    Wiggle, Wiggle, Wiggle

    When a drop is finally placed and spawned, it hangs on the ceiling for 40 frames. As it does so, the game flips its vertical speed up and down every frame so that it wiggles. There were rumors that the wiggle time was random, but it’s not. Because of how drops can be spawned over other drops it might give an effect that it’s wiggling longer.

    Gravity? What Gravity?

    Drops fall at a steady rate. They go down two points per every update (but not every frame! see below). No fancy physics calculations here!

    Yeah, But… How Many?

    The maximum number of active drops is ten. But the actual number of drops spawned depends on the chamber number and whether you’ve completed the game at least once.

    The rules that determines the number of active drops in a chamber are:

    • If the chamber number is 5 or less, then the maximum number of drops is 6.
    • If the chamber number is greater than 5, then the maximum number of drops is chamber number plus one.
    • After completing the game once, the maximum number of ten drops is used for all chambers.

    The maximum number of drops is also used for the title screen and the “get ready” screen. On those screens, the game lies to the drops system that the game has been completed so that it spawns all the drops. Also on those screens the game doesn’t actually wait for vblank so it just draws the drops as fast as possible.

    Half The Work In Half The Time

    Not all the drops are updated on every frame. The drops system only updates five drops at a time, alternating between the odd numbered and even numbered drops. This partial update means there’s more time left over for other parts of the game’s logic to be processed.

    Waitaminute, You Forgot a Section!

    So that’s the drops system in a nutshell. What I haven’t talked about is how the game handles the drop collisions with terrain and with the player. And also the drawing routines. I want to cover those topics more in-depth in later posts.

  • Downland Unearthed #1: Pick Ups and Scoring

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    Every room in Downland has five “pickups”, which are treasures that the player can collect for points or to unlock doors. At game start up, the list of pickups per room is generated. The first two items in a room’s list of five are keys that open doors. There’s a data table that determines which key opens which door. When the player completes the game once, the data table is switched to another one with different key-door mappings. I marked it as the “hard mode” version but I don’t know if it’s actually more difficult. The last three items per room are treasures, randomly chosen between the money bag and the diamond.

    Fun fact: I only realized last week when looking at a speed run on YouTube that you don’t need to collect all the keys to finish the game. Maybe one day I’ll use that information to become the Downland speed running champion.

    Each type of pickup has a base score:

    • Key: 200 points
    • Money bag: 300 points
    • Diamond: 400 points

    When the player collects them, the game gives the player the base score and an additional random amount between 0 and 127. The random number is taken from a counter that runs through the cartridge rom area from addresses 0xc000 to 0xdf5a. It takes the value at the current address and gets the first seven bits. Other processes advance the counter so it’s reasonably random. Random-ish.

    The randomness of the pickups generated and the score added makes it so that getting a high score is partially outside of the player’s control. I’m still unsure if this is was a good idea. A great run could wind up with a bad score, which would be disappointing. I don’t know what I’d replace it with, though!

  • Reverse Engineering Downland, The Tandy Color Computer Game from 1983

    This article is part of a series exploring the reverse engineered inner workings of Downland, a game for the Tandy Color Computer, released in 1983, written by Michael Aichlmayr.

    Late last year I started one of those projects I always wanted to do but could never find the time or couldn’t start it successfully. I reverse engineered Downland for the Tandy Color Computer, released in 1983.

    Downland was one of those formative games for me. My best friend had a CoCo 2 with a copy of the game and we spent hours trying to beat it. But man, that game is hard.

    The player jump physics are game’s best and aggravating aspect. They were the most fluid I had ever seen. But man, no air control whatsoever. When you leaped, you were committed all the way, like in Castlevania for the NES. The jump was floaty enough that there was enough time for a drop to appear ahead and fall on top of you just when you landed. Those drops are the worst.

    It was my third or fourth attempt at RE’ing the game. I forget. For these first attempts I had used MAME’s debugger to run through the code and memory variables. I’d try to track memory changes and figure out what they did. And of course I had to learn how to read 6809 assembly code. (hint: I am not an assembly code guy)

    At the time I could figure out some of the player state and the section of memory that tracked the drop state. But this method was too slow going for me. The MAME debugger was too cumbersome to use. I’d give up after a week or two. With just the MAME debugger and a text file, reverse engineering this way was like trying to figure out the layout of a blacked out mansion while walking around holding a match.

    This time was successful because I decided to learn Ghidra (https://github.com/NationalSecurityAgency/ghidra). It helped a lot with making sense out the code and rom layout, and figuring out the data formats. I also used the trs80gp emulator (http://48k.ca/trs80gp.html) which has great debugging capabilities. The pair of tools really opened everything up and in very little time with these new found powers I managed to eclipse the little knowledge I had gained in the past doing things manually. I soon started learning things about the game that only the original author had ever seen. Finally being able to unlock secrets of this 40 year old game gave me such a wonderful feeling.

    I forget the timeline but I think it took me over two months, maybe three, to get the disassembly in a state where I could make sense of how most of everything worked. Enough to be able to start figuring out how to convert it to the C programming language. At the same time I created a tool to convert the Ghidra listing to a buildable assembly file for LWTools (https://www.lwtools.ca/), with which I could build a byte-for-byte reproduction of the original Version 1.1 rom.

    After the initial two months I kept updating the disassembly during the year the more I had to get into the details during the conversion to C. Some corners of the disassembly could use a bit more documentation but what’s there I believe is very usable.

    The work can be found on GitHub at https://github.com/pw32x/Downland_RE

    Maybe in the future if I’m not too lazy I’ll talk about more in detail.