Despite making the YM2612 Instrument Maker, a simple music player for the Gravity Beam Master Gaiden
soundtrack, and the Game Raccoon Revision 0 menu, I don't consider myself any kind of authority on matters Mega Drive. In fact, it would possibly be accurate to say I don't have a flippin' clue, and any clue I might have once had has gotten a little hazy in the six years since I worked on anything Mega Drive-related. The Revision 0 menu works, but it's hardly a work of art. And, you know, the reset bug and the corrupted text bug.
The best thing for me to do is to throw out any dusty, lingering ideas I have about the Mega Drive hardware, put on some music, and start again completely from scratch.
I need some documents.
Charles MacDonald's genvdp.txt at http://jiggawatt.org/genvdp.txt
is a very good start. It's heavy and confusing, but most technical things are when you're looking at them from the outside as a series of unrelated alien concepts, before you've mentally digested them, separated out the important knowledge from the trivia (as both will be combined together with equal weighting in a thorough technical document), and created your own mental model consisting of the moving parts that are relevant to the task at hand.
Sega Retro's wiki at https://segaretro.org/Sega_Mega_Drive/VDP_general_usage
has a lot of useful information and tables, but at the time of writing it's spread across several different pages which are only linked together in an ad-hoc wiki fashion but without an obvious index. So here's an index:
The MegaCatStudios VDP graphics guide
has an overview of everything the Mega Drive can do.
It's a very good idea to save all these to your hard drive!
I have to pick one part of the menu and just get on with it; see what happens, so let's start at the start: the title screen of the menu firmware.
The title screen is going to have the Game Raccoon cloud logo on it, perhaps the clouds could be animated?, as well as a progress bar of some description underneath.
The 'Handshaking...' stage of the Revision 0 isn't just for show: on boot, the first thing I do is ensure is that both the Mega Drive and PIC processors are both aware of one another. The ROM-resident Mega Drive has to send a magic number to the !TIME
register, which triggers isolation and activates the PIC, which can then do some housekeeping and status checks of the board, Flash and SD card, before deisolating.
Revision 1 has an added ability: the !TIME
register logic is now bi-directional, allowing the PIC to write a word for the Mega Drive to read. I plan to use this to allow the PIC to return status/error codes after performing operations requested by the Mega Drive. These conditions include 'no SD card inserted', 'unable to mount SD card', 'SD card empty', 'unknown hardware error' and so on.
The handshaking will take place on the title screen while the logo is visible, with the text below changing depending on the success of the handshaking operation.
What screen resolutions does the Mega Drive support?
• 256x224 (32x28 tiles) PAL NTSC
• 320x224 (40x28 tiles) PAL NTSC
• 256x240 (32x30 tiles) PAL -----
• 320x240 (40x30 tiles) PAL -----
I need all the space I can for text, but I don't want to restrict the Game Raccoon to working on only PAL systems, so 320x224
• The Game Raccoon logo needs to be converted and prepared for display.
• The title screen transitions from black somehow. The screen has the Game Raccoon logo, a background, maybe the mascot, status bar, copyright text.
• The handshake signal is sent, making the title screen enter isolation.
• 'Handshaking...' is displayed.
• The deisolation test takes place at regular intervals.
• On deisolation, the returned code is tested and used to show the current status and options. The available options depend on the current status: you can't browse the SD card if it isn't inserted or the filesystem is faulty.
That's a lot of functional ideas, but what about the design? After all, I know I can do the functional part: the majority of the work in the Revision 1 menu is going to be making things pretty. Really not my strong suit, but I'm confident I can make something that I can be proud of. The idea here is to make something that looks potentially sellable. Not because I want to sell it, but because I want to show off. :)
I can still change the Game Raccoon cloud logo if I want. I'm not stuck with it just because it's currently on the Revision 0 website and silkscreened onto the cartridge. If I eventually go with an opaque shell or have reason to design a Revision 2, I can use whatever new logo I like.
I want smooth fades between screens / interfaces. The Game Boy Advance has hardware support for fades to and from white and black. The Mega Drive... doesn't.
The sketch on the right is labelled 'checkerboard with raccoon-face silhouettes'; the background of the menu shouldn't be black - black is boring. I want something like the Net Yaroze menu, like I've already mentioned. One look that never fails to work is the 'pastel coloured diagonally scrolling background' look, which decade-old distant memories are telling me I've seen in the intro to some cartoons at some point, but the simplest and prettiest example is always Tetris Attack
on the SNES, even if it wasn't the first.
From left to right:
• Tetris Attack
• Kirby's Dream Course
• Pop'n TwinBee
• The Brazil-only Mega Games 10 in 1
(Mega Drive), and
I was thinking something closest to Tetris Attack
's design but with little raccoon faces embedded in some of the repeating tiles. I wonder why the SNES games all go to the lower left, while the Mega Drive games go in all kinds of directions. I'm not a fan of how quickly the 10-in-1
menu is scrolling. On the real system the palette changes every few seconds, which is nice but also distracting. I wanted to include a gif of the game Marsupilami
as well, but the GIF was six megabytes big... Aero the Acrobat 2
has one of these too, but it doesn't scroll in a constant rate or direction!
If you're wondering why these types of backgrounds recur, it's because they're really easy to program and require little preparation and zip calculation time per frame to update. You just advance the scroll registers or RAM for your tile layer, and everything is drawn appropriately shifted. The Amiga has it a little less easy, but you could adjust the bitplane pointers and split the screen in the copper to create the loop. On the Atari ST and ZX Spectrum you would be completely schtumped, since these lack any way to display the contents of memory either reading from a different position or displaying at a different position. Even VGA cards have a variety of magic features to help you with this in certain modes.
I've already done the type of background I want before: it was in my very first complete game I wrote in C using Allegro way back in 2004!
Most programmers cut their game programming teeth on making a clone of Tetris
. I don't think I've ever written a Tetris
, but I have written a Dr. Mario
According to the docs it's my second C game, but I don't think the snow simulation Snowdon
counts as a full game. And, even worse, the docs say it wasn't even the first time I did that background, because I did it in my BlitzPlus isometric tank game engine prototype secretly internally codenamed Kirby's Advance Wars Course
And, triply worse, while I was finding these projects to make animations from them, I remembered that perhaps the Snake-like game in Blast Arena Advance
had a scrolling background, and lo and behold it does as well! This is getting beyond a joke...
The final animation is from a cute game selection menu I wrote to go with a Ruby game engine written by my illustrious friend GoBusto.
I didn't design Catopuma himself, but if you look close, you'll see he's clearly holding a Mega Drive pad. What a coincidence, huh? :D
What was I talking about again?
The Mega Drive's VDP has 64 kibibytes of VRAM. VRAM is used for storing the graphical content of tiles, as well as the nametable datas containing the indices of the tiles to render. Everything you want displayed on the screen has to be stored in VRAM somewhere. VRAM is an area of memory separate from the normal memory addressable by the 68000.
There are three planes: Plane A, Plane B and the Window Plane. These planes draw grids of tiles from the tile graphics stored in VRAM, and are used to draw the foreground and background scenery in games like Sonic
. Planes A and B can have their size set from anywhere between roughly one and three screens wide, and one or two screens tall. They can also be independently scrolled freely in any direction, with their contents looping in both horizontal and vertical directions - that's how those looping backgrounds are generated. The Window Plane is as large as the screen and cannot be scrolled. The Window Plane can be selectively shown in any rectangular region extending from the top, bottom, left or right of the screen. Wherever the Window Plane is active, Plane A will not be shown.
The indices of the tiles to draw in each Plane are specified in 'nametables', which are also present in VRAM alongside the tile graphic data itself. The horizontal scroll table is also stored in VRAM, but the palette colours and vertical scroll data are stored in separate dedicated VRAM regions within the VDP. (They're VRAM since they're not system RAM, but they're not in 'the VRAM', if you catch my drift.)
The contents and organisation of the VRAM is entirely the responsibility of the programmer: the location of the nametables is at the programmer's discretion subject to alignment restrictions, and tile graphics can be stored in any unused space.
I want to use all three planes, as well as sprites, so the nametables for all three need to be allocated VRAM space. At the bottom of this page you can see the layout I've chosen:
• I want tile index 0 onwards to be graphics data, so the nametables will be installed at the end of VRAM. I'll copy my data into VRAM starting at tile index 1. This prevents any weirdness where Tile 0 isn't transparent.
• Plane A must be at a multiple of $2000
, and goes at $C000-$CFFF
. I'm using the 512x256 plane size, so I'm only using $1000
• Plane B must be at a multiple of $2000
, and goes at $E000-$EFFF
. I'm using the 512x256 plane size, so I'm only using $1000
• Window Plane must be at a multiple of $1000
, and goes at $D000-$DFFF
. In the 320x224 screen mode, the Window Plane is 512x256 plane size, and can fit between Planes A and B in VRAM.
• The sprite list must be a multiple of $400
, and goes at $F000-$F3FF
• The horizontal scroll data must be a multiple of $400
, and goes at $F400-$F7FF
This leaves at 1536 tiles between $0000-$BFFF
(indices 0-1535) and 64 tiles at $F800-$FFFF
The layer order is given on the Sega Retro 'Priority' page, but also on the right of my sketch here.
Here's my starting design for the game browsing menu.
There's a large dark area containing the game list, with the current path shown at the top. At the bottom are captions showing the options.
I want the middle section containing the text items to scroll freely but be clipped to only show within the dark area. The Mega Drive doesn't have any direct methods for restricting the display of a Plane to particular region of the screen, except for displaying the tiles using the Window Plane, which has restrictions on where it can be used and cannot be scrolled.
The approach that first came to mind designing the menu at the time though was to use the Window Plane to clip the text layer: anywhere where the Window Plane is active, Plane A is not displayed. All I need to do is ensure the Window Plane is somehow displayed both above and below a pair of thresholds. The Mega Drive can't do that either: the Window Plane has to be anchored to and extend outwards from one of the screen edges, but there's a way around that. I hope. I'll see what happens when I test it!
(Writing this blog, I think you could also simulate clipping by using the horizontal scroll register on the text plane to shift offscreen all the visible tiles above and below a given Y position, as long as the size of the plane being scrolled is at least two screens wide. That would obviate any mid-screen timing tricks, but I'd have to make sure it played nice with vertical scrolling.)
At the same time, I want to retain the scrolling background from the title screen and have it keep moving in the background. The Mega Drive doesn't support translucency, so the dark area will have to be achieved using a separate set of tiles and colours, or using checkerboard dithering.
The natural approach to making a scrolling background is to use the hardware plane scrolling capability of the VDP, but if I try that here, I'll run out of layers: I can't have the background texture, the dark area and the scrollable text all active at once in two planes. I could fake the scrolling by keeping the plane position static and modifying the tile graphics instead. I'd have to calculate the new tile contents either in real-time or in advance. Because no isolation takes place on this screen and I have a large ROM, I could easily do that. I'd have to check how much VRAM copy bandwidth the Mega Drive DMA has and how to use it.
If the dark region of the background has the same graphics as the light region, then if I palette fade the dark palette towards the light palette, I can make the dark area emerge from the light background without redrawing the screen. That would prevent requiring a fade to a solid colour in between screens.
The current directory has to be displayed somewhere: either above the file list or above the options at the bottom of the screen.
I have to design a cursor sprite and arrow widgets to indicate when the list extends beyond the visible screen.
I don't know what options should be available on the game browser screen yet. Should the user be able to regenerate the directory cache? Should the user be able to boot into the currently installed game? What about a Help or an About screen?
How can the user update the menu firmware? A special button combination activates the selected item as a menu firmware, or should the Game Raccoon recognise a magic filename like UPDATE.BIN
? I'll have to inform the user to restart the system after updating the menu firmware, somehow.
What should the installation screen look like, and what messages do I need?
Over on the right, notice I've called a nametable entry a 'tegel'. That's a term I picked up from TONC
How will all of this look in code?
• Initialise everything.
• Prepare screenmode.
I need to clear all the system RAM, VRAM, VSRAM and CRAM before I do anything. Each independent screen should be initialising its own state RAM and VRAM resources on entry. Not initialising everything is a good way to test whether the screens that come later are correctly initialising themselves. If you perform a super-clean at the start, then shoddy screens will chug along happily taking credit for the initialisation that was done earlier by someone else, and then something weird might happen when you change the order later.
The Mega Drive's screen mode is configured in the VDP registers. There are surprisingly few settings, and once you've set them there's little need to change them, unless you're transitioning to a new conceptual screen with vastly different requirements.
• Prepare resources for next screen - background tiles, logo image, font, raccoon sprite.
• Write to screen planes?
• Begin realtime loop for screenstate? - Can I make this nicer, event-driven, modern?
• Advance background logically.
• Advance all "active interactive pieces"?
• Receive input - react.
• Tells interactive pieces to act?
Games like Blast Arena Advance
, Lemmings DS
, Gravity Beam
, Gravity Beam Master Gaiden
and Star Lynx
are written in a very explicit fashion (in every sense of the word). There's possibly a way to write game software in a way that's compatible with a modern, event-driven messaging system, but it's never seemed like the correct approach for any of these embedded-system-like games. That doesn't mean they're written messily or poorly, but they're constructed out of the simplest, most understandable, least engineered pieces I thought I required to solve the immediate tasks at hand. YAGNI, perhaps, but mostly expedience.
The Game Raccoon menu is a tool rather than a game, so it's worth me spending a few minutes seeing if there's anything I ought to be elevating into a little framework of 'interactive pieces' for handling input/buttons/options/backgrounds rather than writing five lines of assembly to solve each isolated problem as I see it.
• 7.600489 MHz CPU, PAL.
The Mega Drive has a lovely CPU with lots of registers and a big-sounding clock speed. However, unlike the Ocelot's/Game Raccoon's PIC, every instruction on the 68000 takes multiple cycles depending on the operation and argument size chosen. The instructions are also variable length sized and not pipelined, so the instruction fetch itself contributes a lot to the instruction execution time. Each bus cycle is four clock cycles long, not one, so those seven million cycles get clumped up into a little over a million MIPS when all is said and done. Divide those MIPS by the number of frames per second and you get an estimation of the number of instructions per frame that are possible, spread out over both active display region and the vertical blank region. It runs out fast! If you're a nerd, read yacht.txt
• Interactive loops ought to be RAM-resident.
• DMA can help with scrolling background update? It's a block copy.
• But ROM is not accessive during isolation - fit in sys RAM.
During isolation, the menu firmware ROM is inaccessible. This means that any graphics, text or code that a screen that will be active during isolation relies on must be copied to either RAM or VRAM before isolation begins. I plan to have a cute animated mascot raccoon doing stuff on the loading screens, which means that all of his graphics, tegels and scripting has to fit in active RAM.
Here's a diagram of the various states, which options are available to the user and what triggers the transitions.
It's very nice and covered with arrows, like a proper diagram.
I'm finding it very difficult to get started with programming this thing, hence the diagrams. I've been looking through all of the Revision 0 code trying to find some piece that I can confidently use as a base, but it's all completely useless. That's what you get for writing something in a hurry as a test. What I've got is like a fused together lump of chewing gum, when what I was hoping for was Meccano.