Game Raccoon Revision 0 Technical Report - January 2020
This is a description of the operation of the Game Raccoon Revision 0. For a more narrative progression, please see the post 'Matt's Chronoblogical Misadventures in Game Raccoon Land - January 2020'.
This information is provided for education, research and informative purposes. Readers are advised to not attempt to build Game Raccoons of their own at this time, if only for the simple reason that this design doesn't currently work. :)Game Raccoon Revision 0 dated 2019-11-27, firmware 2019-12-29
What is the Game Raccoon?
Game Raccoon is a project for me to practice my hardware design and software skills. In this case, making an interoperability cartridge for loading software onto systems where loading one's own software is difficult.
The Game Raccoon Revision 0 is an SD card-based cartridge designed to be compatible with the PAL Model 1 Sega Mega Drive, using rewritable Flash memory to present software to the Mega Drive's system bus.
The Game Raccoon boots from its BOOT partition and displays a menu showing the current contents of the inserted SD card. The user can nagivate the directory tree of the card, and select an image to install to the GAME partition on the Raccoon's Flash memory. When installation is complete, the user can press Start to switch to the GAME partition to play the currently installed image. During gameplay, the Game Raccoon imitates a 4 Megabyte sized mask-ROM cartridge without save game capability.
The Game Raccoon exists to allow software developers a straightforward, easy-to-use method to prototype new games on Sega Mega Drive or Genesis hardware without any hardware knowledge or the use of EEPROM rewriters.
What software can I use on my Game Raccoon?
My own YM2612 Instrument Editor with graphics by Aly James allows you to explore the sounds the Mega Drive's YM2612 sound chip is capable of producing.
Over fifty homebrew Sega Mega Drive games are listed on Sega Retro, with many of them being offered as free downloads. Of course, any original Mega Drive games you purchase from sites such as itch.io that require a Mega Drive emulator ought to work. More new games for the Mega Drive / Genesis are being released every month, these are tracked on release sites such as pdroms.de.
There are many demo productions (free running animations showing novel video and audio effects) such as Overdrive by TiTAN.
The retail games Zero Tolerance by Technopop (as well as a prototype of the sequel), and Wiz 'n' Liz: The Frantic Wabbit Rescue are offered free to download from their original authors.
On the Steam Workshop page for Sega's own 'SEGA Mega Drive & Genesis Classics', over 2,474 items are available for download at the time of writing. You can find your purchased Workshop mods in X:\Games\Steam\steamapps\workshop\content\34270\; these files should be Raccoon-compatible.
Organisation and electronic concepts
The Game Raccoon Revision 0 contains an 8 megabyte 16-bit wide Flash reprogrammable ROM, a short form factor SD card socket, an on-board 16-bit PIC MCU, a control logic section, a 16-bit register, three 16-bit dual-supply transceivers and a voltage regulator.
There are three 16-bit dual supply transceivers at the lower edge of the Game Raccoon PCB. The leftmost two are collectively the 'Address/Signals Bus' transceivers, and the rightmost transceiver is the 'Data Bus' transceiver.
The dual supply transceivers perform the function of translating between the Game Raccoon's internal 3.3V signalling and the 5V signalling expected by the Mega Drive. No 5V signal from the Mega Drive is connected to any other component on the board in any way, with the exception of a pull-up on D0 described below. (I've been told that the Mega Drive is compatible with a 3.3V high logic level - the 68000 processor datasheet claims a VIH minumum of 2.0V - and several (unofficial) Mega Drive devices communicate in this way, but I didn't want to chance it. I didn't have any information about the custom chips on the motherboard, and their capacitances, etc.) The dual supply transceivers use both the 5V and internal 3.3V supplies.
The dual supply transceivers are also used to control the directionality and tri-statedness of the communicaton between the Game Raccoon PCB and the Mega Drive's buses. The MCU signal !68000 (pulled low) is used to indicate that the Game Raccoon should be entirely isolated from the Mega Drive's buses. When the signal is high, isolation is active, and the PIC is able to use both the internal address and data buses freely to communicate with the register and Flash.
Before this state is reached, the Mega Drive should be currently executing from code in Mega Drive RAM since the contents of the Flash ROM will be inaccessible. To allow the Mega Drive to determine whether isolation is currently active or not, a pull-up resistor is attached to the D0 signal on the Mega Drive-facing data bus causing the least significant bit of any word to be read as a 1 when isolation is in effect. In the Mega Drive ROM header, the least significant bit of a longword or word value is known to be 0 at many fixed locations in a valid game ROM image: specifically, anywhere in the Mega Drive header where a word-aligned address value is stored, such as the stack pointer initial value stored in longword $00 and the cartridge entry point address stored in longword $04. With a valid Mega Drive ROM installed in a Flash partition, longword $00 (or word $02) will have D0 == 0 when the cartridge is connected and D0 == 1 when the cartridge is isolated.
The directionality of the Address/Signals Bus transceivers are set to 5V -> 3.3V. The Game Raccoon never attempts to put a value onto the Mega Drive's address or signals buses. When the !68000 signal is high, the Address/Signals Bus transceivers isolate the address and signals buses from those of the Mega Drive. This allows the PIC to use the internal address and signals buses to communicate with the Flash for reading or writing. When !68000 is set low, the Game Raccoon attempts to implement the behaviour of a standard 4 megabyte mask ROM: it will only drive the data bus when a valid read operation is directed at the cart's address space (respecting the remapping that takes place if a Mega CD is installed).
The directionality and enable of the Data Bus transceiver are determined by the logic section using the (post-transceiver) signals from the Mega Drive. The transceiver is only enabled if isolation is not in effect and a valid action is directed at address spaces handled by the Game Raccoon. The only two actions the Game Raccoon will respond to are a ROM read action and a TIME write action.
A valid ROM read action occurs when the Mega Drive is reading data or instructions from the currently selected Flash partition as if it were an ordinary cartridge game. It is determined by FLASH!EXTERNALOE = CART!C_CE + CART!AS + !R: an address strobe occurs while a read is signalled and the current address is in cartridge space. When this occurs, the Data Bus directionality is set to 3.3V -> 5V and is enabled. This places the 16-bit data value output by the Flash IC onto the EDGE data output bus to be read by the Mega Drive.
The 16-bit register is used to allow one-way communication from the Mega Drive to the Game Raccoon board. The register is written to when a write to the !TIME address space is performed by the 68000 processor in the Mega Drive.
A valid TIME write occurs when the Game Raccoon boot firmware running from the Mega Drive's RAM communicates with the Game Raccoon board. It is determined by REGISTERCLK = CART!TIME + CART!AS + !W: an address strobe occurs while a write is signalled and the current address is in !TIME address space. When the write finishes (the strobe ends and the register stores the value), the PIC detects the change in the D-type register's clock signal, allowing the PIC to awaken and isolate the Game Raccoon from the bus, making the bus available for the PIC to read the value. This exchange is necessary since the PIC would almost certainly not be able to react in time to read the value from the data bus itself when the 68000 performs the write: the register allows the written value to be replayed and read back slower by the PIC. All TIME write actions cause the Game Raccoon to isolate itself from the bus (since this is necessary to allow the register to be read using the internal data bus).
The SD card socket provides storage of game or firmware images to be installed onto the Game Raccoon's Flash partitions. The PIC communicates with the SD card using SPI channel 2. The part number is WURTH 693063020911 (it took some searching to find this!) and is wired as shown. The 10R resistor is to prevent large inrush currents (as I was told, at least) during power-on (or if you're gutsy enough to remove the SD card while power is on?!?). I used FatFs - Generic FAT Filesystem Module R0.14 with the PIC24 SPI reference driver to communicate with the SD card. It's necessary to modify several constants and functions in FatFs to get it to work with the Game Raccoon board - specifically, it needs to be set to SPI2, have the card insertion detection and write protection detection functions forced, and the correct internal tick timer interrupt supplied. Surprisingly, it all seemed to work first time! (More than I can say for Microchip's own implementations: File I/O or MLA.)
The Flash IC provides 8 megabytes of non-volatile storage with a 16-bit wide data bus. As the Mega Drive's motherboard is set up to support a 4 megabyte address space for game cartridges, the Flash is conceptually divided two 4 megabyte halves, controlled by the highest address line A21. (There are 22 address lines A0-A21 on the Flash, providing 4 megawords of storage.) The partitions are simply flat 4 megabyte regions of the Flash memory, not filesystems. (To be exact, the Flash IC chosen for the Raccoon identifies the highest 32 kilowords as a special region that needs to be erased at a finer granularity, but this is easily abstracted and can be ignored.)
At any time, either the lower BOOT or upper GAME partition is active: the A21 line of the Flash is referred to as !BOOT/GAME throughout my Game Raccoon notes, and it acts as a flag enabling one partition or the other. This signal is pulled down by a resistor, which means that when the Mega Drive is powered on, the BOOT partition is immediately activated, and remains this way until the PIC changes its FLASH!GAME/BOOT signal from input to output to override this value.
All devices on the board are powered by a LM1117 3.3 volt linear voltage regulator, converting the 5V power supplied by the Mega Drive into 3.3V used by all the components on the Game Raccoon PCB, as well as the SD card socket. Internally, the Game Raccoon uses 3.3V signalling (LVC logic family) between all its components. Adjacent to the voltage regulator are two pads connected to the 5V and Ground nets. These allow an auxiliary 5V power supply (such as a USB-compatible wall wart) to be connected to the Game Raccoon, allowing it to be externally fully powered when not installed within a Mega Drive during programming or debugging.
At the top of the Game Raccoon PCB are pads to mount a five pin 0.1 inch header. These pins correspond with pins 1-5 of a PICKIT 2 programmer/debugger: !MCLR, 3.3V, GND, DATA and CLOCK. To program or debug the Game Raccoon's PIC, connect a PICKIT 2 to these pins using DuPont jump leads. And in turn, program the BOOT partition of the Flash by uploading a debug firmware that does nothing but immediately isolate the bus and write the contents of some binary file on the SD card to the BOOT partition. When you do this, don't forget to reupload the normal reactive Game Raccoon PIC firmware when you're done. :D I wouldn't recommend using the PICKIT 2 when external 5V power is not supplied - this would entail operating the dual supply transceivers with only a 3.3V supply connected.
Two surface mounted indicator LEDs are controlled by the Game Raccoon PIC firmware to provide generic signalling back to the user/programmer. Various blink sequences can be defined to communicate the current condition when the Game Raccoon is used in a Mega Drive and therefore it wouldn't be safe to have the debugger also connected. The values of the series resistors are chosen to reduce the active current to approximately 5mA per LED during operation (using the forward voltage/current graphs in the LED datasheets). As it happens, the mega bright blue LED consumes 2mA, while the somewhat dull green LED consumes 7mA (tested by grounding the MCU-facing side of the resistor through an ammeter).
There are two different EEPROM firmwares present on the Game Raccoon board, with different functions:
The PIC firmware provides the functional brains of the Game Raccoon. It is written in C and compiled using xc16 in Microchip MPLAB. The PIC responds to commands from the Mega Drive, and can isolate the Game Raccoon from the Mega Drive to allow it to read ROM images from the SD card and write them to the Flash. It also reads the full directory structure of the SD card and writes this to the directory cache in the BOOT partition.
The Flash firmware provides the user-facing interactive part of the Game Raccoon. It is written in 68000 assembly language and assembled using vasmm68k_mot_win32.exe and a Python makefile-like system. This firmware provides the menu system allowing the user to select a game to install, and boot the currently installed game. When the Mega Drive is turned on, the Game Raccoon's signalling acts as a 4 megabyte ROM cartridge providing the contents of the BOOT partition at the $000000 to $3FFFFF address range, allowing the Mega Drive to boot into the Game Raccoon's Flash-resident firmware. (Since !C_CE is used to chip select the Flash, this range should automatically migrate to $400000 to $7FFFFF if a Mega CD is installed, but I haven't tested this.)
Theory of Operation
This is the sequence of events from my design of Game Raccoon Revision 0, as posted on various forums asking for advice in October 2019. Notes in italics are deviations from my ideal.
1) On boot, !VRES is asserted, then negated. The cart starts connected to the 68000 address and data buses. The MCU sets up a rising edge interrupt on DETECTREGISTERCLK, then idles. The default state of !BOOT/GAME is to bankswitch in the lower 4 megabyte BOOT partition of the Flash containing the bootstrap code. The PIC internal architecture only allows for separate rising/falling-edge interrupts on specially designated INT pins. Most other pins can be used as 'Change Notification' pins, which trigger interrupts on any state change. Not what I wanted, but given the physical layout of the board, it was an acceptable choice.
2) The Mega Drive performs the normal boot/TMSS sequence reading from the lower 4MByte of the Flash, and then boots into the set-up sequence of my cart's firmware.
3) The intro graphic is displayed, while the RAM-resident Game Raccoon menu is copied to the Mega Drive RAM.
4) The RAM-resident code is jumped to. The stub writes a magic number to the !TIME address $A13000, then NOPs for 0.5s, then continually reads $000006.w and loops continually while the LSB is set.
5) The write to the !TIME address $A13000 is decoded by the cart logic and both populates the content of the 16-bit register and triggers the MCU interrupt. The MCU NOPs briefly to allow the write operation to definitely finish before acting.
6) The MCU raises MCU!68000 high, which isolates the cartridge from the 68000 bus by tristating all the transceivers in both directions. Since D0 is pulled up by the cart, any word reads by the Mega Drive to cart space will have their LSB set (other bits indeterminate). Since I know that the lowest bit of $06.w will be zero (since this is the entry point vector in the header, it must be a multiple of two), the 68000 stub will continually read 1 in the LSB of $06.w while isolated and 0 when the cart is non-isolated.
7) While isolated, the MCU reads the filenames and directory structure from the SD card and uses FLASH!WE, the cart address and data buses to write the retrieved data to a reserved block within the BOOT partition of the Flash. While isolated from the 68000 bus, the Flash chip's !C_CE will be pulled low.
8) When finished, the MCU reactivates the interrupt on DETECTREGISTERCLK and lowers MCU!68000 to reconnect the cart to the 68000 buses.
9) The 68000 can now exit the busy loop and run the menu program, with all the available games' data available within the reserved block of Flash, accessible as if it were always present within the cartridge ROM.
10) The user selects a game - this is encoded as a 16-bit value and written to !TIME, triggering the isolation as before.
11) The value written to the register is retrieved into the MCU using MCU!REGISTEROE and the cart's data bus.
12) The MCU raises !BOOT/GAME to switch in the upper 4MByte of the Flash. The MCU can now open the indicated game file from the SD card and write it to the upper 4MByte of Flash. !BOOT/GAME is set back to BOOT to allow the menu to resume after the isolation is complete.
13) When done, the MCU enters a low-power idle state and takes no further action. All of its pins are inputs except !BOOT/GAME which is high.
14) The 68000 is released from its busy loop when the cart buses are reconnected.
15) The Mega Drive cleans up any RAM/VRAM it needs to using its stub (in case certain software cares?), zeroes out itself as much as it can, then jumps to $04.l to boot the game present in the upper 4MByte of the Flash. From this point on, the cart is completely dumb ROM. Writes to !TIME will populate the register, but it will never be read - games that use save SRAM will read garbage from !TIME and bankswitching to SRAM will silently fail.
Logic section design
The following is my description of the logic design in October 2019 asking for advice. Text in italics are clarifications not present in the original post.
My signal names are (usually) prefixed by their source (68000 bus or MCU). EDGE signals are those at the edge of the PCB where the board meets the Mega Drive, and CART signals are post-transceiver and so are forced to their default values by pull-up/down resistors when !68000 isolation is in effect.
I'm using transceivers to split the 5V 68000 world from the 3.3V internal world. The two 16-bit transceivers on the left should be considered as one 32-bit transceiver for the 'address and signals bus'. I'm using the !C_CE B17, !AS B18, !LDS, !UDS, !TIME and !VRES signals from the cartridge slot to detect chip enables, reads, writes, strobes and reset conditions. My understanding is that !C_CE is asserted on reads or writes to $000000 - $3FFFFF and !TIME is asserted on reads or writes to $A13000-$A130FF.
I'm using the 16-bit register because the MCU is way too slow to react to the 68000 quickly writing a word to the !TIME address space. With the register, I can store the value and use the rising edge of CLK to awaken the MCU, then it can isolate the cart and read the written register value at its own pace.
FLASH!RST = 68000!VRESMCU!RESET = 68000!VRES The PICkit is also connected to this pin when I'm programming the MCU, but you should never program the PIC while the cart is within the MD anyway.
Address decoding logic:
FLASH!CE = 68000!C_CE (pulled low on isolate) The Flash should be selected on access to $000000-$3FFFFF, or always if the cart is isolated.
FLASH!OE_EXTERNAL = 68000!C_CE + 68000!AS + !R This signal represents an !OE request from the 68000. This happens when !C_CE is low, !AS is low and !R is low. This signal represents the full condition that the Flash outputs from its data bus - it represents !CE with !OE and is used below.
FLASH!OE = MCUFLASH!FORCEOE * FLASH!OE_EXTERNAL I want the Flash to output a value if the MCU is forcing it to, or if the 68000 signals ask it to.
REGISTER!WE = 68000!TIME + 68000!AS + !W I'm going to have the register respond to any write in the $A130xx range, since that's simple. The register writes on the ascending edge of this signal, so it's also listed as REGISTERCLK.
MCUDETECTREGISTERCLK = REGISTERCLK On the rising edge of the register clock, the MCU awakes at the same time as the register is written.
A bus = 3.3V
B bus = 5.0V
DIR L = 3.3V output <- 5V input (value into cart from 68000 - DATA: a write operation, ADDRESS: 68000 Ax enters cart address bus.)
DIR H = 3.3V input -> 5V output (value from cart into 68000 - DATA: a read operation, ADDRESS: cart address bus leaves onto 68000 Ax, do not allow this.)
ADDRESSTRANSCEIVERDIR = low Only valid direction is an address from the 5V port placed on 3.3V port if ADDRESSTRANSCEIVER!OE is low.
ADDRESSTRANSCEIVER!OE = MCU!68000 When MCU is in !RESET or if this pin has not been raised, the 68000 address appears on the 3.3V address bus.
DATATRANSCEIVERDIR = !W When the write takes place, the data flow is 5V -> 3.3V. (The way I remember it is that Low makes the transceiver lower a signal from 5V to 3.3V. But then I always double check with the datasheet instead of relying on my memory.)
DATATRANSCEIVER!OE = MCU!68000 + (FLASH!OE_EXTERNAL * REGISTER!WE) I want to connect the data bus if isolation is not in effect and we're valid reading the Flash or valid writing the register. (This is why we synthesize a combination !CE with !OE signal previously so the transceiver will actively output under the same conditions as the Flash IC does.)
I'm pulling up !AS, !LDS, !UDS, !TIME and !VRES high whenever the cart is isolated, preventing any of the above conditions occuring. This disables the Flash and register ICs unless the MCU activates them itself.
While the MCU is in the reset state, all its pins are tristated. My pull- resistors need to make this a good safe state for the cart to be in before the menu begin, and while the real game is running and the MCU is idle:
MCU!BOOT/GAME, low default, if low select the boot menu half of Flash, if high select the game half of Flash.
MCUFLASH!WE, high default, allows the MCU to program the Flash
MCU!68000, low default, if high the cartridge transceivers tristate the cart from the 68000 buses.
MCUFLASH!FORCEOE, high default, if low the MCU is reading the Flash (to verify a write)
MCUREGISTER!OE, high default, if low the MCU is reading the last value written to a !TIME address
DETECTREGISTERCLK, a rising edge wakes the cart for either the 'ready to read games' state or the 'please play game in directory record cache number #REGISTER' state.
!RESET, standard reset.
The Flash indicates a busy state by toggling a Qx pin on consecutive reads during busy, so there's no busy pin to poll.
The schematic above shows a connection between the Flash's READY pin and an MCU GPIO. Sadly, I didn't pay enough attention to the datasheet and overlooked that this pin is open-drain on the Flash, so the connection is useless since it's never pulled high. The toggle pin-polling approach still works, so that's what I use. :)
The reason this design is this way is that I was working from the book Microprocessor Systems Design by Alan Clements, which shows timing diagrams using !AS and R/!W. The Mega Drive cartridge interface doesn't supply R/!W so I produce my own !R and !W with the following:
DERIVED !W = !UDW and !LDWDERIVED !R = !UDW nand !LDW
Why is Flash used and not SRAM?
Regardless of whether Flash or SRAM is used for storing the game image, some nonvolatile storage is necessary for the Mega Drive to boot into. Merging the role of the non-volatile boot region and the rewritable game partition into a single device simplifies the design greatly.
The disadvantages of this approach are that the Flash erasure and writing process is significantly slower than SRAM writing, but since the PIC is likely to be somewhat slow anyway, I don't care. I'm used to cassette tapes. :D Flash also has a limited lifetime. The datasheet for the S29GL064S Flash IC chosen quotes a '100,000 Erase Cycles per Sector Minimum'. Since the Game Raccoon doesn't contain any sector mapping hardware and presents the upper 4 megabytes containing the game as a single contiguous region, the lowest sector of the game partition will necessarily be erased every time a new ROM image is installed into it. This means that you'll get a minimum maximum of 100,000 game installations before the cartridge may malfunction irreperably. If you were testing a game and wrote 20 game images per day, every day, the Game Raccoon would last 13.7 years. If you were playing Mobius Evolution and wrote one game image per week and played that one installed game off-and-on, you'd get 1923 years out of a Game Raccoon. Either way, I think the Mega Drive itself would fail before the cart did. (N.B. The very first dumb test version of the Game Raccoon firmware that's current as of 2019/12/29 regenerates the directory cache every time the cart is booted, so the same rules regarding the 100,000 uses applies to power cycles of the Mega Drive. The final firmware would only regenerate the directory cache when asked, allowing other games to be selected from the same SD card without regenerating the cache.)
The advantage of this is that the last installed game will be immediately available on the cartridge without any further SD card access. On an intent level, the act of installing a game onto the cartridge makes more sense to me anyway.
Why aren't saves supported?
To answer this, consider what would be needed to support saves. There isn't a single set method by which games support saving; it's possible for a game to attempt to save by any means the author can conceive. Most of these ways involve the !TIME address space, which the Raccoon uses for communication. Adding FeRAM/FRAM for battery-free, non-volatile, SRAM-like saving would require yet another device on the internal buses beside the Flash and the PIC and would make one heck of a mess to route.
I received the first manufactured Game Raccoon Revision 0s from PCBWAY on 19th December 2019.
I ordered five, and as far as I can tell there aren't any manufacturing defects, such as warped silkscreening, misdrilled vias (nothing obscene at least) or poorly soldered components on any of them.
I soldered the PICKIT header pins and auxiliary power supply on one of the Raccoons and I had no issues connecting to them with a standalone PICKIT 2 programmer to identify the chip. My projects through MPLAB compile and upload and debug fine.
I am noticing some very strange debugger behaviour, though - values not changing when they ought to. I think that's more of an issue with MPLAB than anything else - after having to manually work around breakpoint skidding with a whole bunch of i++; i++; i++; i++;every single damn time I use a breakpoint, I get used to anything.
All the address and data bus pins seem to connect to all the appropriate terminals on the Flash, PIC and transceivers. SD cards physically fit into and out of the socket with no issues whatsoever; I'm very pleased with this choice of socket. I'm using it with a Sandisk Extreme III 2.0gb card with no issues. It doesn't seem to be very happy with the stupid hidden .Trash-1000 folder that Debian keeps automatically adding to the root though. FatFs has SD sector size support as preprocessor defines: I've only enabled 512 bytes, and Windows 7 is perfectly happy to format the SD card to that format, and the Game Raccoon works fine with it.
The menu isn't particularly flashy, but it works enough to boot games according to my model. The code is abysmal, but the string sorting and comparisons all worked first time, which is pretty amazing for somewhat-rusty 68000 assembly.
The first design flaw that became apparent was when I was programming the Flash program and erase low-level abstractions. I first used a hardware timer set to wake when the maximum time indicated in the Flash datasheet for each kind of operation had expired. This worked fine to get things running quickly. I then tried to use the READY pin from the Flash to the PIC to query whether the Flash was currently busy. Unfortunately, I didn't pay enough attention to the datasheet and overlooked that this pin is open-drain on the Flash, so the connection is useless since I never added a pull-up resistor and so the pin is never pulled high. The toggle pin-polling approach still works, so that's what I use instead. The consequence is that I have to waste some time switching the TRIS data bus of the PIC from output to input to read the toggling value from the Flash and then back to output to perform the next action. Easily worked around, either with the bit polling or the timers.
The writing isn't especially fast, but I like it. I think I could knock some seconds off (yes it's that slow) if I compiled the firmware image with a higher optimisation level (it's -O0 at present), since there is a lot of friendly function-filled abstractions wrapping the low-level interactions with the Flash. The Flash seems smart enough to know whether or not it needs to erase a particular sector - it probably has a microprocessor core in there that's more powerful than the Mega Drive itself...
The second flaw is some strange behaviour which I think is to do with powering up the Raccoon, and transceiving !VRES and connecting it to the PIC and Flash with a pull-up resistor. When the Mega Drive is powered on, sometimes the Game Raccoon will show a fully blank screen. The first thing the Game Raccoon Revision 0 boot ROM does is satisfy TMSS, so it should be displaying the PRODUCED BY OR UNDER LICENSE screen. If you press the Reset button on the console while powered on, the cart boots normally. I'm not sure why this is, but it isn't preventing anything from working so I'm not going to think too hard about it. It's a quirk. :)
The third flaw is almost certainly something I've overlooked in the 68000 boot ROM. Whenever the boot ROM starts on real hardware, there's a 50/50 chance that the text will appear garbled. What seems to be happening is that the text tile graphics are being written one word forwards in VRAM than they ought to be. I belive that's to do with the VDP's command system, and the way sometimes you write longwords to it to erase certain parts and words at other times. I think I'm just clearing one of the palettes in a strange way and not resetting the VDP to a good state before drawing the text. It doesn't affect the functionality of the menu, or the logic or graphics in any of the games, so I don't think it's a problem with the 68000's communication with the Flash.
The final flaw is a lot more serious unfortunately, and it's something anybody designing hardware for the Mega Drive ought to be aware of:
In designing the logic section of the board, my objective was to identify the circumstances in which the various internal devices should be activated, and the correct directionality and tri-statedness of the transceivers in every scenario in order to provide the correct communications into the cartridge and correct, but most importantly safe responses. If at any point two devices were to output at the same time (a transceiver and the Flash, or the PIC and the register), dangerous bus contention would result with a 68000 crash in the best case, and permanent damage to the Mega Drive motherboard, Game Raccoon PCB, or power supply in the worst case.
To ensure the Game Raccoon only responded when the address was stable, I designed the Game Raccoon's logic around the use of the !AS signal, as described in the timing diagrams in the book Microprocessor Systems Design by Alan Clements. Since the 68000's !AS signal is presented on the cartridge port, this works great for all 68000-based communication: code execution, data reading, etc. all work fine.
To test the Game Raccoon, we need a game that has good, clear graphics; interesting sound (preferably lots of speech and samples); and a recognisable intro sequence that I don't have to press any buttons to get to.
In short, we need Awesome Possum Kicks Dr. Machino's Butt.
Here is Awesome Possum Kicks Dr. Machino's Butt's glorious opening chorus as played by the Game Raccoon Revision 0:
I chose Awesome Possum because of this long opening chorus - if the !OE logic was faulty, then the sequence wouldn't play, or the graphics would be corrupted or the audio would be glitchy. As you can see, it's stable and clear.
Unfortunately, past this point things go awry.
Ignore the background music, it's supposed to sound like that.
What isn't working in Awesome Possum:
• Alphabetical characters on the prologue screen and level card. Strangely, the comma and numbers work.
• Background tiles during gameplay.
• The left and rightmost animals during the quiz.
All of these things are showing up as solid blocks of a single colour.
At first I thought that the PIC's copy operation from the SD card to the Flash was faulty - as if there was a block of $FF being written at some point in the ROM image. I tried to reproduce this effect in an emulator by overwriting parts of the image, but I couldn't. Replacing large chunks of the end of the image with $FF to simulate the effect of the copy being interrupted part way through (as if the file size was being reported incorrectly by FatFs, or the SD card was browning out due to the power-inline resistor) had no effect except making the game not boot at all eventually.
Tests with some other software showed that it was only ever graphics that were affected, backgrounds in some games, sprites in others (such as Ecco: The Tides of Time, where Ecco becomes a lovely fluid mass of squares, and Mobius Evolution and I expect other Sonic 1 hacks), HUD elements in others. Music, sound effects, samples and gameplay were all uninterrupted and perfect.
For some reason, certain graphical elements were being copied into VRAM as full $FF, which reminded me of the VDP's DMA operations: the ability of the VDP copy blocks of data from ROM to VRAM without the intervention of the 68000 processor, for a boost in speed under certain circumstances. This was better known by the gaming public as 'Blast Processing'. (Well, I imagine multiple different, contradictory or completely imaginary effects and techniques were all referred to as 'Blast Processing' at the time by various people, but this is the actual Blast Processing as far as I know.)
At first I thought that the somewhat haphazard logic section was too slow to serve the VDP's requests. If the VDP was attempting to read from the Game Raccoon faster than the logic could decode its requests, forward them to the Flash and serve them back out the data bus, then graphics would go bad all over the place. But they wouldn't be solid $FF...
So I did a bit more researching and saw some posts that indicated !AS wasn't asserted by the VDP during copy operations.
Which is annoying because I swear I could have researched this before I designed the logic section and saw some posts that said the opposite:
I can only imagine that this final post on segaxtreme has swapped around some of the labels of the signals in their explanation. All the signals do seem to go by a half dozen different names depending on the author. Ah well.
This means that the VDP cheats and doesn't provide the full set of signals that the 68000 timing diagram indicates. Instead the VDP provides only the composite convenience signals of !CE B17 and !OE B16. If you design a cart to rely on !AS and it relies on !AS for anything relating to the VDP, you're in for a bad time.
In other words, I've invented a Mega Drive cartridge that can do everything except Blast Processing. ;_;
Needless to say, Overdrive by TiTAN doesn't work exactly as intended.
Standard Sega carts 'listen' to only !CE and !OE signals, not !AS. On the board above you can see six connections for the address bus on B4-B9, a pair of connections: B16 !OE and B17 !CE, and four connections B22-B25 for the data bus. My own Megaboard which uses a pair of 28C256 EEPROMs also works with DMA on these signals. (This NBA Live 95 cart uses save RAM and doesn't use !AS either. I think it uses B9 A21 to select SRAM for accesses to $200000-$3FFFFF and B28 !LDS to write to it.)
I think the correct way to consider these signals is as follows:
B17 !CE should be treated as a chip enable for reads or writes to the cartridge ROM 4 megabyte range (taking into account the potential to be remapped by Mega CD).
B16 !OE should be treated as a read strobe for anything you want your cartridge to respond to.
B31 !TIME should be treated as a chip enable for devices you want to respond to reads or writes to the !TIME address range.
B28 !LDS and B29 !UDS should be treated as write strobes for write access to anything you want your cartridge to respond to.
Game Raccoon Revision 1 is going completely ignore !AS and use these four signals properly. (I hope!)
What confuses me though is that a lot of documents (such as Mel's pinout) list B16 !CAS0 as being set for reads and writes. If that was the case, then the Mega Drive would putting data on the bus at the same time as the cartridge was outputting it.
Game Raccoon Revision 0 Downloads
These are presented for your own personal use for educational, research and informative purposes. You use any of the information, designs, layouts or software included here at your own risk. As I said above, Revision 0 of the Game Raccoon has many limitations, not least of which it not working.
Licenses may change with future revisions. The licensor and copyright holder is Mathew Carr, my email address is at the bottom of this web page. This license does not grant you rights to use any copyright holder or any other party's name, logo, or trademarks, such as the Game Raccoon name, logo, trademark or character.
Where third-party items have been included, modifications may have been used to adapt these to the project. The original authors of third-party inclusions do not offer support for these items.
The following downloads are offered under the Affero General Public License Version 3 with Additional Terms. You can read this license here. The license and licensing notices are included in each of the zip files below.
Revision 0: Source code and binary image for the BOOT partition of the Game Raccoon, containing the menu system and handshaking necessary to browse, install and begin games. The font used is TrioDX by usr_share.
Use python fruitsalad.py complete_boot_rom_image to assemble the BOOT ROM. Open the various .py and .68k.asm files to understand the stages involved in assembling and linking the image. game_raccoon_boot_image_directory_cache.bin is suitable to write to the lower partition.
Revision 0: Source code and binary image for the PIC of the Game Raccoon, containing the flow to interact with the boot menu using isolation and transferring to the game partition; and all the routines for interacting with the SD card and Flash memory: scanning the directory tree and writing the directory record cache, and erasing and programming the Flash. Includes FatFs - Generic FAT Filesystem Module R0.14 modified for PIC24 on SPI2.
Use MPLAB IDE and xc16 to compile the firmware image. Be very careful:
The source file is set up to compile the firmware image that follows the normal Game Raccoon menu flow. To debug the Game Raccoon or force-install an image into either the BOOT or GAME partitions, it is necessary to recompile the firmware with the following #defines set:
#define DEBUG_WRITE_IMAGE "game_raccoon_boot_image_directory_cache.bin"#define DEBUG_WRITE_IMAGE_PARTITION FLASH_PARTITION_BOOT
This will produce a firmware that, when powered-up, will immediately isolate the bus and begin writing the given file from the SD card to the Flash partition. Once this is done, it is advisable to verify the image was correctly written.
Then you must recompile the firmware and reupload the normal Game Raccoon firmware flow image into the cartridge. If you don't, the Game Raccoon will attempt this Flash write every time it is powered on.
The following downloads are offered under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal. You can read this license here. The license and licensing notices are included in each of the zip files below.
Revision 0: EAGLE Schematic and Board layout for EAGLE 9.4.2.
Revision 0: Gerber files zipped together. Use 1.6mm thickness. Edge bevelling for the connector if you can get it.
From my PCBWAY dashboard, the settings I chose were: "61x84mm 2 Layers, Thickness:1.6 mm, Finished Copper:1, Surface Finish:HASL lead free, Material: FR-4 TG130, Via Process: Tenting vias, Finished Copper: 1 oz Cu"
Revision 0: Schematic, board image and pick and place orientation verification image.
Revision 0: Bill of Materials and Pick and Place file for components used in Game Raccoon Revision 0 manufacture. Always get visual confirmation of the layout from your manufacturer before committing to an order.
The following third-party files are offered for convenience, and are not copyright Mathew Carr:
Revision 0: Collection of EAGLE part libraries (mostly from eagle.componentsearchengine.com) used in Game Raccoon Revision 0 board. Also includes Bruce Tomlin's cartridge PCB library.
Datasheets for components, etc.
Things to think about
About the register
It's possible that the register can be integrated into the preceding data transceiver using something like the TI SN74LVC16646A 16-BIT BUS TRANSCEIVER AND REGISTER WITH 3-STATE OUTPUTS, but I don't think that part is dual supply (although it is 5V-tolerant), so I'd be relying on the Mega Drive responding to its 3.3V signalling.
3.3V address/signals bus transceivers
There's really no reason to use dual-supply transceivers on the address/signals bus when you think about it: they only ever output into the internal buses at 3.3V. I could use any 5V-tolerant unidirectional buffer with enable. I used these for simplicity and uniformity of design; when I was first designing the Game Raccoon board, I was going to use JLCPCB who would have imposed a per-kind-of-component charge.
Bidirectional register communication
With some thought, I could add TIME read as a valid action - responding to reads from the !TIME address space with the current contents of the register to allow bidirectional communication. I didn't need it for my first design so I didn't put it in, that's all.
The directory cache
The directory cache contains a list of the full contents of the SD card. It is regenerated whenever the magic number $FFFE is sent to the TIME register. Currently this happens every time the Game Raccoon is powered on - the final firmware would allow you to do this on-demand. (Or not, if you're certain the SD card contents are unchanged.)
The directory cache consists of a series of records indicating either a directory a file. Records (or nodes if you prefer) are linked together in a tree structure.
The directory cache begins at address $200000 in the BOOT partition, and each record is $100 bytes long.
The first item in the directory cache is always . representing the root of the SD card.
Both directory and file records begin at offset $00 with a $00-terminated full pathname of the item. Directories do not have a trailing slash.
Directory items have four uint16_t fields located at offsets $F8, $FA, $FC and $FE.
$F8: Number of directories D in this directory.
$FA: Number of files F in this directory.
$FC: Record index containing the start of the list of contents of this directory.
$FE: Record index of parent directory. (This is 0 for the root.)
The contents of a directory are located at the record index indicated by $FC. This is always a contiguous list of D records for the directories (if any) followed by F records for the files (if any).
File records consist of just the $00-terminated full pathname beginning at $00.
This means that the maximum pathname to a file can be 255 characters, and 247 for directories. Since the directory cache lives in the region $200000-$3FFFFF, it can hold 8192 (including both directories and files). However, testing these limits is very unwise. :D
python imitate_directory_cache.py can be used to create a directory cache offline. This reads a local folder and makes an imitation directory cache as if it had come from the PIC in a real Game Raccoon to be linked into the Game Raccoon boot ROM binary for testing in emulators.
python directory_cache_read.py friendly prints the contents of a directory cache data block.
0: (3,2,1,0) .1: (0,3,6,0) ./freeware2: (0,10,9,0) ./homebrew3: (0,3,19,0) ./demoscene4: (0,0,0,0) ./YM2612maker.bin5: (0,0,0,0) ./TANGLEWD_demo.BIN6: (0,0,0,0) ./freeware/wiznliz.bin7: (0,0,0,0) ./freeware/Zero Tolerance.md8: (0,0,0,0) ./freeware/Beyond Zero Tolerance (prototype).bin9: (0,0,0,0) ./homebrew/RetailClerk89_2019-08-18.bin10: (0,0,0,0) ./homebrew/Airstriker.bin11: (0,0,0,0) ./homebrew/ULTETRIS.BIN12: (0,0,0,0) ./homebrew/SegGalaV0.06.SMD13: (0,0,0,0) ./homebrew/omega_blast.bin14: (0,0,0,0) ./homebrew/MegaAtoms.bin15: (0,0,0,0) ./homebrew/PRINGLES.BIN16: (0,0,0,0) ./homebrew/Misplaced-EN.bin17: (0,0,0,0) ./homebrew/Uwol_Quest_For_Money.bin18: (0,0,0,0) ./homebrew/OldTowers12.bin19: (0,0,0,0) ./demoscene/TiTAN_Overdrive.bin20: (0,0,0,0) ./demoscene/fullscreen_niccc2000.bin21: (0,0,0,0) ./demoscene/TiTAN_Overdrive2.bin
Consider the root directory 0: (3,2,1,0) .
The first number before the colon, 0, is the record index. This isn't stored in the cache and is implicit in the address of the record in PROM.
The 3,2,1 means that there are three directories and two files, found starting at record 1. Adding together three and two gives five, the total number of items contained in the root directory.
The final 0 means that the parent of this directory is record 0. This means that if you were to try to ascend from the root, you'd not change directory. (Note that menus and so on detect that you are in the root by checking that the current record index is 0.)
If we look at the list starting at record index 1:
1: (0,3,6,0) ./freeware2: (0,10,9,0) ./homebrew3: (0,3,19,0) ./demoscene4: (0,0,0,0) ./YM2612maker.bin5: (0,0,0,0) ./TANGLEWD_demo.BIN
Using the D and F values from the root directory, we know that the first three items ./freeware, ./homebrew and ./demoscene are directories and the next two items ./YM2612maker.bin and ./TANGLEWD_demo.BIN are files.
Notice how the directory cache list when printed in linear form is a very straightforward array. Specifically, since the filename strings are always present at offset $00, then address $200000 + $100*index is always a pointer to the fully qualified pathname of item index. When the user selects a file from the Game Raccoon boot ROM menu, it's the record index that is written to the TIME register. On the PIC, this is used as an index into the Flash ROM's directory cache to retrieve the full pathname to pass into FatFs' f_open().
When a directory is selected, the record index number is set as the current directory and the menu is redrawn with no communication with the PIC.
When the menu is printed to the screen, only the filename part before the last forward slash (if present) is printed for each file.
Notice that the directory cache contents aren't sorted. The PIC writes the entries to the directory cache as they're reported by FatFs' f_readdir() function, which in turn is the order that they appear in the filesystem, most likely the order that the original file entries were created on the card. The PIC doesn't have the RAM to sort these, so they're written unsorted. When they're navigated to by the Game Raccoon menu, they're sorted by the 68000 (or rather, a list of pointers to record indices are sorted) when the menu reads the list of items in the present directory.
A very useful end result of this is that the filenames in the directory cache aren't at all linked to any part of the FAT filesystem on the card, so if, for some reason, the files are moved physically on the drive geometry, the directory cache would still work. If the SD card were removed and some files added, then any part of the directory cache that refers to files that still exist would still function normally.
If there is no file at the given pathname on the SD card, the Game Raccoon will lock up in isolated mode with the LEDs flashing to indicate an error. This isn't especially pretty, but it shouldn't happen unless you've done something very inadvisable.
/ FatFs - Generic FAT Filesystem Module R0.14 /
/ Copyright (C) 2019, ChaN, all right reserved.
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
/ that the following condition is met:
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.