[ Buildable example written in C:
If you've poked around in the Megadrive/Genesis development community for any period of time, you've probably found the official documentation from SEGA, such as it is. It's very roughly translated, and there are a few errors (if you don't have the errata, uh-oh!).
Well, if you are like me and like doing everything the hard way, this is rough. There aren't any great resources out there for doing low level Megadrive coding, excepting the occasional raw disassembly listings (like Sonic 1 and 2). Driving the sound is especially hard because you need to be roughly familiar with not just 68000, but Z80 as well.
I scoured around for what I could, but besides some very old code examples (some 'current' links are over 10 years at this point) I remained somewhat baffled. The Genesis Sound Software Manual, in particular, includes a very cryptic "example program" - essentially a listing of the FM register values.
It goes like this (not quite verbatim):
This is all well and good, but there aren't any code examples given in the manual in _either_ 68000 or Z80 (since you can use either to drive the sound chip - Sonic is a famous case where the sequel changed from a 68k music driver to a z80 music driver).
The advantage of launching the music player on the Z80 is that the 68000 can be free to do pure graphics processing and won't be caught up on certain hardware hangups, like waiting for the Z80 I/O bus to be free.
The trick of course, is actually doing it on the hardware.
This stumped me for like two months! Ouch!
So let's dig in. First of all, the manual gives this short description of activating the Z80:
68K CONTROL OF Z-80
Start-Up Operation Sequence:
1. BUS REQ ON
2. BUS RESET OFF
3. 68k copies program into Z-80 S-RAM
4. BUS RESET ON
5. BUS REQ OFF
6. BUS RESET OFF
BUS REQUEST ON
DATA 100H (WORD) -> $A11100
BUS REQ OFF
DATA 0H (WORD) -> $A11100
RESET Z80 ON
DATA 0H (WORD) -> $A11200
RESET Z80 OFF
DATA 100H (WORD) -> $A11200
* Requires 26ms
CONFIRM BUS STATUS
bit 0 of $a11100
1 - 68K can access, 0 - z80 is using
PRGSIZE equ _endprg-Z80PRGZ80_BUS equ $a11100Z80_RESET equ $a11200org *move.w $100,(Z80_BUS)move.w $100,(Z80_RESET)movea.l Z80PRG,a0movea.l $a00000,a1move.l PRGSIZE,d1.z80copyloop:move.b (a0)+,d0move.b d0, (a1)+subq #1,d1bne .z80copyloopmove.w $0,(Z80_RESET)nopnopnopnopmove.w $0,(Z80_BUS)move.w $100,(Z80_RESET)Z80PRG: defb etc etc_endprg:
First, and MOST IMPORTANTLY, the biggest rule of embedded development is DO NOT TRUST RANDOM MEMORY. Why, you may ask? Because of cases like this. The stack pointer on the Z80 could be anywhere. The memory could be clean, or it could be full of random digits that will immediately cause a stack corruption.
The emulator dgen, one of my favorites for Linux dev and disassembly, does not initialize the Z80's memory the same as actual hardware. This was a pain point for me, because I was getting sound in dgen, but not on my actual Genesis 2. After a while of poking at this and that, I came across these two lines of code in the Z80 init portion of the Sonic 2 disassembly:
ld sp, $1b80
- LD A, C ; OR B is a fast 16bit zero-check. The flag is set from register A, which we compare quickly with both registers.
- Clear memory! Watch for interrupts! Set your stack pointer!!
- Wait before _every_single_write to FM and address them directly
Full Z80 source: