I recently ordered the Mega EverDrive X5, a cartridge that enables running any SEGA Genesis or even SEGA Master System ROM on a real SEGA Genesis console. This inspired me to try creating a Genesis ROM of my own for that great 1988 console.
The SEGA Genesis has a Motorola 68000 CPU running at 7.6 MHz, 64 KB main RAM, 64 KB Video RAM, a Z80 at 3.58 MHz with 8 KB audio RAM for controlling the audio chips, an Yamaha YM2612 FM chip and the Texas Instruments SN76489 Programmable Sound Generator (PSG).
For now, I have focused on the 68000, and getting a 'hello world' ROM up and running.
Reference: Wikipedia:Sega Genesis
Having done a little bit of Amiga programming, the Motorola 68000 was not entirely foreign to me, so the trickiest part of making a Genesis ROM was understanding the particulars of the Genesis hardware.
I looked at a few different tutorials for getting started, but Matt Phillips's blog bigevilcorporation.co.uk ended up being the most helpful. He's working on a new Genesis game, Tanglewood, which looks like a very cool project.
Based on Lionel Sanderson's revision of Matt Phillips' example code, I made a simple animated text output example with vertical sync.
The font bitmap data in Matt's example only had the letters 'DEHLORW', so I ended up writing my own font. It isn't pretty, but it'll do.
I added this text rendering loop to the example initialization code, it renders the text 'SYLTEFAR SAYS HELLO ' 92 times with an offset that changes every frame:
vdp_control = $C00004 move.w #0,d6 ; d6 = offset MainLoop: lea Text,a0 ; a0 = text ptr add.w d6,a0 ; add offset (animation) move.w #92,d1 ; d1 = 92 (text repeat count) move.l #$40000003,$00C00004 ; Set up VDP to write to VRAM ; address $C000 (Plane A) .loop: clr.w d0 ; d0 = 0 move.b (a0)+,d0 ; set lower byte to character sub.b #$40,d0 ; subtract (ASCII value of 'A' - 1) ; 'A' becomes 1, 'B' becomes 2, etc. move.w d0,$00C00000 ; write to VDP cmp #TextEnd,a0 ; repeat until end of string bne .loop lea Text,a0 ; reset dbra d1,.loop ; repeat d1 times jsr WaitVBlankEnd ; Vertical Sync move.b #0,d5 ; d5 = 0 add.w #1,d6 ; d6 += 1 cmp.b #9,d6 ; d6 = 9 ? bne MainLoop clr.w d6 ; if so, d6 = 0 jmp MainLoop ; next iteration WaitVBlankEnd: move.w vdp_control,d0 ; Move VDP status word to d0 andi.w #$0008,d0 ; AND with bit 4 (vblank), ; result in status register beq WaitVBlankEnd ; Branch if equal (to zero) rts Text: dc.b "SYLTEFAR SAYS HELLO " TextEnd:
A particular line of code of the original example caused problems for me:
;========================================= ;8. Clearing the Registers and Tidying Up ;========================================= move.l #$00000000,a0 ; Move $0 to a0 movem.l (a0),d0-d7/a1-a7 ; Multiple move 0 to all registers
This seems like a mistake. The instruction:
MOVEM <ea>,list ; Source -> Listed Registers
Copies the contents of 'ea' to the listed registers. In the example code, a0 is set to 0, which, according to sega2.doc points to the start of the ROM, which contains random stuff. Specifically, the stack pointer was overwritten, causing subroutines to return to bad locations.
After removing that line, the example code worked just fine.
I chose to use the open source vasm assembler, built with Motorola-style syntax. Building the ROM was as simple as outputting a raw binary file:
vasm -Fbin main.asm -o roms\test1.gen
For testing, I started out using the great emulator mednafen.
Mednafen debug command-line (pauses on first instruction):
mednafen -debugger.autostepmode 1 roms\test1.gen
A few important mednafen debug commands:
R Run S Step Up/Down, Page Up/Down Navigate code Space Set breakpoint Alt+1 CPU debugger view Alt+3 Memory view
See Mednafen Debugger Documentation.
Debugging the stack pointer problem mentioned above, I had to inspect the stack pointer for corruption, which doesn't seem to appear in the mednafen debugger. To solve this problem, I switched to using the MAME debugger, which is more full-featured and shows all the registers.
MAME Genesis debug command-line (pauses on first instruction):
mame64 genesis -debug -window -cart roms\test1.gen
A few MAME debugger hotkeys:
F10 Step over F11 Step into Ctrl-M Show memory
go [ADDR] Run until reaching ADDR help The most important command of all