Lyle in Cube Sector for Sega Genesis / Mega Drive

Lyle in Cube Sector for Sega Genesis / Mega Drive

Updated 2024-04-29

This page is for my port of Lyle in Cube Sector to Sega Genesis / Mega Drive. It is a complete reimplementation of the original game, with attention placed towards accuracy to the source material. You may just want to go to the Download area to play it, but I've elected to write in more detail about the development of this port as reading long writeups of nerdy projects was a big part of what led me to where I am now, so it feels like the right thing to do.

Table of Contents:

About

I began work on a port of Lyle in Cube Sector to the Mega Drive platform almost ten years ago, when I was finishing college. My coursework was largely done in C++, but I had brushed up against plain C a number of times and used this game as a opportunity to become more familiar with how I might structure a larger project with it. I got started using a toolchain and library called SGDK, but in the end abandoned it and moved to my own support code.

I chose this game because of my strong familiarity with both it, and the Mega Drive as a platform, and recognized that the game would be a good fit for the hardware. It would be satisfying to get the game running smoothly and looking nice on a real high quality CRT monitor.

Development largely stopped once I graduated college and entered the world as a working adult. By the time I wanted to return to the project and keep it moving along, my abilities as a software engineer had far outgrown the crude decisions that my student self had made without an eye for software design as a process. Stuck at home during lockdowns due to COVID-19, I used a lot of my (then greatly expanded) spare time after work to restart the project from scratch, ditching the support and boot code I was using from the SGDK library.

There have been many interruptions to the process of finishing the game, and anyone who's embarked on a medium-sized personal project should know that once a point of "pretty good" is reached, it can be really hard to push yourself to the finish line in the end. Development slowed to a crawl once again, where I'd eke out progress on one or two small parts between projects at work; the similarities between the embedded software engineering I did at work and development on the Mega Drive made that context switch rather difficult. It was no help that my personally preferred coding style was nearly the polar opposite of that of my job.

It's now 2024, and I suppose I worry whether anyone will want to bother playing my release of this game. There are so many games in this genre in the indie game world these days, and my tastes in games have changed so much that I much prefer linear action platforming games over explorational ones, in terms of pacing. However, the Mega Drive is dearly lacking in games belong to the latter genre, so I'm glad I can introduce just one more to the library. I can at least say that I've got a high quality version of the game that is probably easier to run on a modern computer through an emulation layer than the original 2006 release is, and at a proper 60fps no doubt (the original is 50fps).

I don't know if it'll ever be possible to release a cartridge of the game in any capacity - that really hinges upon the agreement and cooperation of the original creator, myself, publisher(s), and the artists behind the game's original soundtrack. I did FM sound source covers of all of the original music, but cannot lay any claim to the compositions.

Download and Play

ROM Data

A binary release of the game is available here.

How to Play

The game is a 2MiB ROM image, which can be easily burned to a 27C160 or equivalent ROM and installed on any number of Genesis / Mega Drive cartridge PCBs. It expects SRAM to be present in the upper megabyte; the header is configured in a way that specifies as much. Without SRAM, the game can be played, but saves will not work (it will not crash in such a configuration, however).

Compatibility

This game has been tested on the following systems:

The game should work fine in just about any software emulator or ROM emulation device (e.g. Everdrive). I will not make any attempts to support esoteric clone devices or emulation setups through modifications to the game and have only tested against real harwdare.

Original Work

Lyle in Cube Sector is a freeware independent PC game created by Bogosoft. It was released in 2006, and I played it a lot during my early teenage years.

The game is structured similarly to Metroid and other action-adventure titles: Lyle, initially weak and without agency, explores a hostile world in which the discovery of new abilities allows access to areas that could not be reached previously.

Lyle's primary attach mechanic is throwing the titular Cubes that are distributed throughout the world at enemies. By defeating bosses, and picking up Cube Power orbs, Lyle expands his abilities in ways that improve his mobility and attack power.

I encourage you to look at the map when you are feeling lost or discouraged, and to look at yet unseen areas for answers.

Development Notes

I sought to avoid diverging from the design of the original game whenever possible. The original game was created using Multimedia Fusion, which is an earlier version of a product called Clickteam Fusion today. As a child, I too used Clickteam tools, so I went into this port with a familiarity with how the original game was "programmed". This enabled me to do my best to replicate the timings and movements of the original game.

As I was writing a new engine in C, it would be an appropriate nitpick to argue that this is a rewrite, and not a "port" of the original code as it was - the idea of porting the Multimedia Fusion runtime to the Mega Drive stops very quickly when you consider the video hardware, before any other obvious road blocks appear.

In this section, I'll detail at a high level what had to be considered with regards to bringing the game to the Mega Drive hardware.

Development Environment

I did all of the development for this game on Linux, using GCC (m68k target). The build system is regular old Make, and I have a few small bespoke tools and scripts I wrote for some data processing. In particular, the dispatch for routines that load, run, and destroy objects is generated by a python script based on a file listing of the directory for entity code.

Testing via emulation was done with a mix of BlastEm and MAME; BlastEm reflects behavior of the hardware quite well, but when I wanted to debug logic for any of my assembly code, I much preferred MAME's debugger interface.

With this small setup, the game compiles in about one second on a moderately powerful computer. I do frequent testing by pushing data over USB to an Everdrive cartridge for development, and have also burned the game onto classic EPROMs and tested in a cartridge with battery-backed saving for proper integration testing.

System Code

While I started with SGDK, I was frustrated when upgrading to a newer version caused my game to develop a lot of new and strange bugs. When I returned to develop the game years later, I also identified that I would really benefit from creating management structures for video and palette memory, and felt there were areas of the API that could be improved upon. When I rewrote the game's code from scratch with an eye on consistency and organization, I split off my "base" Mega Drive system code into its own repository that I can reuse for other projects called MDK.

Here is a link to MDK's Github page.

Some of the system code is written in assembly to try and keep it tight, but most of it is written in C. More importantly, it targets a fairly up to date GCC (GCC 12 as of writing). Queue structures are in place for DMA operations and palette updates, to decouple the game's code from the hardware a little. In addition, after working with the X68000 and the XSP Library a little, I was inspired to add support for drawing composite sprites, which uses data emitted by PNG conversion tools I made. While most of the game's code and drawing code predates this support, I did make use of this in the ending for the game. If I was to start over, I'd probably handle almost everything this way.

Game Balance

I like challenging games, and prefer to get better and earn the right to proceed in general. However, I'm willing to accept that jarring spikes in difficulty, misleading designs, and unreasonable expectations of early players lead to a bitter experience that turns away players. While I didn't want to change the feel of this game from the original, I did make a few global changes:

I am hoping these changes can help new players avoid some harsh feelings when they are first getting used to the game early on.

Framerate

The original game runs at 50fps, which was the default of Klik and Play, propagated forward even to Multimedia Fusion 2. I suppose this is based on the PAL standard of 50hz from televisions at the time, but as no Windows / DOS computer or compatible monitor was 50hz, this strikes me as a weird default. In any case, we live in a time when 60hz is a pretty standard baseline, so 50hz is not desirable.

From the start I intended to have the game nominally run at 60fps, with all physics scaled appropriately to let the game play accurately and at the right effective speed. To do this, all physics constants are lazy-initialized with a scale factor of the appropriate magnitude for the constant type, to account for the difference between NTSC 60hz and PAL 50hz. All code depends on these calculated constants, and I was able to generally avoid any special casing of the region beyond the use of constants. This extends as far as sequence counter values used for boss and intro animations.

If you play the game on a European console, it will take advantage of all 240 raster lines to match the graphical output of the original game as much as possible. Otherwise, In NTSC, it centers the available 224 lines based on the shape and size of the room on screen at the time. Nothing is cut off by accident or forgotten, and HUD placement is adjusted as well.

It could be argued that playing this in 50hz on a European machine is the "accurate" thing to do, but I'll be honest with you and say I've done most of my testing at 60hz.

Colors

The most significant thing that any game faces when crunched down to the Mega Drive is usually the colors - both in quantity, and in depth, the Mega Drive's state of fairs with regards to colors is absolutely dire. Setting aside flashy tricks, it's pragmatic to say that 61 colors can be displayed simultaneously. Lyle in Cube Sector is quite colorful, but as it anachronistically channels the aesthetics of the 8-bit era, it fortunately makes little use of long color ramps for shading, so it was possible to represent its artwork with little degradation in the end.

You can see that the blue lumpy walls in the area above are a fair bit more saturated than the original counterpart, and that the enemy's projectile has a more pale blue than the original game does. This is because it shares a palette line with Lyle; I can't guarantee the presence of both that pure blue and yellow on the same palette line in all scenes.

The Mega Drive's palette entries use 3-bit RGB, so each channel has only eight possible levels. This cuts down on an artist's ability to create appealing ramps without thinking outside of the box a little. That, combined with a simply low color count, means that it is sometimes necessary to use artistic freedom to change the appearance of an object for the sake of resource management.

When I started development, I was not in the practice of creating my own tooling for asset preparation, so I created almost all of the graphics you see here by hand redrawing them in a binary editor for Mega Drive format tiles, or at best, importing edited bitmap images of the original data when possible. All palettes were hand-typed into a hex editor. I dedicated one palette to the stage foreground and one to the background, so lots of little edits had to be made to incoming artwork to have even automatically imported data cooperate.

Backgrounds

Fitting all the backrounds in without compromise presented a few challenges. While some were simple enough to just use more or less as-is as textures, some of them contained overlapping patterns that repeat at different intervals. There simply isn't enough video memory in the Mega Drive to contain a full screen unique background, let alone independently scroll two planes while using one for the foreground already. The shot you see in the image above is the result of an enormous amount of the game's work.

Extra Paralax Planes

The screenshot above contains a "front" background layer of purple lines, and a "back" layer of darker blue ones. You might notice quickly that the back pattern is smaller, and this repeats more frequently. As the screen scrolls, the two layers of the background correctly scroll separate from one another.

This was achieved by creating special empty tiles on the foreground layer. These tiles are placed as a repeating stamp in a specific order in the empty areas of the stage, and when the background code runs, it copies shifted versions of the front texture into video memory in order to give the appearance of scrolling. The back layer just scrolls ordinarily, and the illusion is complete.

An early iteration of this had me keeping a little buffer in RAM where I drew the purple pattern in software, rather than storing the data pre-shifted. This mostly worked, but I was not happy with the performance tax it incurred, and even now this area is one of the most challenged in terms of free CPU time. The current solution is a result of accepting a higher ROM size in favor of a simpler routine.

There are lots of paralax tricks that ultimately are achieved by careful copying of texture data at the right time, but you might have a hard time spotting them the first time around. There are spots where I used the occasional sprite in the stage data to further mask the illusion. I am particularly pleased with the results for the final area, the "gauntlet", where I was able to get it to match the original virtually pixel for pixel without dropping any layers.

Creating Data

I was so unused to tools and stuck to "first principles" so much at first that all of the mapping data for the far background was created with this process:

I banged out almost all of the background mappings within the scope of a 3-hour plane ride this way, so I should point out that this process is born from a complete lack of internet access. However, if I was to start over, I'd just make or use a tool that chops up an indexed PNG or PCX file or something and emits mapping data.

Stage Editor

Multimedia Fusion didn't have any sort of grid-based stage storage system that would really help me, so all stage layouts were done by hand. However, I'm not a complete nut, and didn't do this all in a hex editor - I created a graphical stage authoring tool.

This tool is pretty scuffed but it held together for me enough to complete the game. Objects never got more representation than a simple bounding box and description, but it was adequate. Tile and palette data is loaded directly from the binary data the Mega Drive consumes; if I was to start this project over, I'd make my tilesets indexed PNG or PCX files, and the editor would just load those, while they get converted as part of my build process for MD usage.

Music

The music also required me to do a lot of hand work in rendering it on the Mega Drive's characteristic FM sound source, the Yamaha YM2612. A derivative of the YM2203 (OPN), it has six FM channels, albeit with one often nixed in favor of a crude PCM output used for sample playback (drums, voices, crusty guitars, etc). There are also three "PSG" square wave channels available courtesy of an integrated SN76489 implementation. I won't go on and on about the intricacies of doing "chiptune" work on such hardware as you can do your own searching for more details on this topic written by people better positioned to do so than I.

The original music was written with "trackers" targeting Amiga and DOS computers and wavetable synthesis. The Mega Drive couldn't be any further, but I was fortunate to have access to a sound engine for which appropriate conversion tools exist... sort of. I say "appropriate", but that's only true on paper. In practice, the tools were a little underdeveloped, and the work flow to get things going was tedious to say the least. I was able to barely get out an acceptable result, but if I was to start over today, I think I'd just accept the task of hand-transposing all of the music to MML or something and ditch the original MOD/XM files entirely.

Without a doubt, the most "different" part of the port is the music, for better or worse. At one point I intended to support optional use of the Mega CD to play back a compact disc of the original soundtrack.

Sound Effects

Despite the musical compromises, I am happy with the sound effects. I spent some time listening to the original ones and transposing the initial notes and portamento ramps they made, and did my best to recreate these in my own sound effect engine that runs independently of the music engine. As the sound engine I chose only supported one sound effect at a time, that was absolutely unacceptable, so I hacked out that engine's ability to talk to the PSG hardware entirely and allowed my own code to use those channels exclusively for sound effects. My sound effects engine runs in the horizontal blank handler, which fires 5 times per frame in NTSC and 6 times per frame in PAL. The result of this is that the engine runs at the same rate for all regions.

The engine implements a tiny virtual machine, complete with a little call stack. One execution context is executed per hardware PSG channel, and assignment for sound playback is dynamic. The playback routines were hand written in assembly to keep it lean. Sound effects were created in the assembler using macros. The engine supports use of subroutines and loop constructs to keep the creation process sane.

Language

As I'm living in Japan and want to support both Mega Drive and Genesis systems in every meaning of the phrase, the game is complete with a Japanese language script, which is used when the game detects it is on a J region console. Hiragana and Katakana are used to render the script; I must apologize for the font size and video RAM preventing me from using Kanji in the script. Please trust me when I say that many rooms are very tight on VRAM usage.

Physics

By far the physics code for both Lyle and the Cubes are the largest portion and were given a lot of attention. Most objects in the game move in "Multimedia Fusion-ish" ways, characteristic of the kinds of solutions easily created using Clickteam's software. A few things I took note of and either reproduced in full or "in spirit" to make the game feel correct:

In the original game, Lyle himself moved with an MMF extension called the Platform Movement Object. A little quick searching tells me this is something people seem to still use; my mind is blown that Fusion is being seriously used by people these days, and then even more so that an external extension I used as a child is still relied upon.

This object allowed the entry of various physics parameters and would manage the movement of an Active Object in a more nuanced and predictable way than the curiously broken built-in MMF movement routines. Outside of Lyle, however, objects have behavior code where simple newtonian physics are implemented using background checks and Alterable Values within the Active Object, which is a typical pattern.

The Platform Movement Object had sub-pixel precision and a very understandable impact was associated with all of its parameters, and I was able to get Lyle to behave much the same way without a lot of debugging. On the other hand, the Cubes and most enemies use bespoke movement code, and they handled subpixel precision in a way that tied their movement speed heavily to the game's framerate - their positions have no subpixel values at all, so if dX is 1.8px/f, it gets truncated and an integer dX of 1px/f is applied to an object's X for the frame. There is no hidden accumulation, that's all you get: integer movement speeds.

Due to this, when I say the movement speed is tied to the framerate, I don't just mean that they'd speed up if the framerate was set differently. In addition to that, the movement is fundamentally integer-only, and moving to a different framerate means that the crossover points between one integer speed and the next will also move by necessity. In my engine, I used fixed-point variables for position and physics variables in a way consistent across all moving entities. However, this MMF-ish truncated movement vector behavior meant that the Cubes didn't have the right "hang time" behavior when thrown, where a value of dY between -1.0 and 1.0 should simply not move at all vertically. If I just natively use those values, then it becomes a problem when I proportionately scale the physics constants for the 60fps version of the game. Instead, I have a version of the physics routines where the vectors are filtered to produce the equivalent movement speed in these quantized "almost 1px/f" buckets. This was not necessary for dX in practically any case, but applying to dY made all the difference in getting the right strange movement curves I needed.