To-do: check exactly which cartridge slot lines are connected in each revision and in the 32X (if you want to play it safe, the signals for standard address decoding are pretty much always available).
Table of contents
CPU and memory issues
Z80 accessing 68000 bus when 68000 accesses I/O ports
According to official documentation, if the Z80 tries to access the 68000 bus when the 68000 is trying to access the I/O ports, the Z80's bus cycle may be shortened and lead to invalid data being read.
The usual workaround for this is to halt the Z80 while accessing the I/O ports. Since this is a very short period of time (just the access itself needs to be guarded), this hopefully shouldn't cause issues with PCM playback.
Flashcarts may initialize memory on boot
Many flashcarts such as the Everdrive series will initialize RAM with zeroes, but on real hardware RAM is unpredictable garbage. This can lead to overlook issues where you forget to initialize some value and only realize when you program the game into a dedicated cartridge.
TAS instruction only partially works on most
consoles as it doesn't write back (being more or less like
#7) but it does write back the result in a model 3 VA2
(behaving more like
LD IYH,nn instruction
LD IYH,nn (where nn is an
immediate value) is not implemented on the Genesis Flashback, in case
you're really interested on supporting that clone. Other obscure
instructions may also be missing, but no extensive research has been
done about it.
Earlier versions of Echo are broken by this implementation issue, since
they use the
LD IYH,40h instruction. It was worked around
by changing it to
LD IY,4000h (since
being overwritten immediately after, anyway).
Z80 and DMA bus clash
If you try to initiate a DMA transfer at the same time the Z80 tries to access the 68000 bus (e.g. to read ROM or write PSG) the DMA transfer may not work and load garbage instead. Earlier consoles seem more prone to show severe glitches but every console is affected to some extent.
The common approach to avoid this issue is to stop the Z80 during DMA transfers, but depending on the sound driver this may lead to noisy PCM playback. Other approaches to keep the Z80 out of the way may work too.
VSRAM is 40 words long on most revisions, but was changed to 64 words in model 2 VA4 onwards. The behavior of column -1's vertical scroll (the up to 15px to the left that you're supposed to hide) has also changed, being garbage in earlier hardware and a copy of column 0 in later hardware.
DMA transfers can't cross a 128KB boundary
When performing a DMA transfer, data can't cross a 128KB address boundary. If you attempt it then the transfer will wrap around back to the beginning of that 128KB block. This is not an issue for RAM (which doesn't cross such a boundary) but can be an issue when doing a transfer from cartridge (e.g. uncompressed graphics in ROM) or Mega CD's WORD-RAM.
The possible workarounds for this are to either make sure that data never crosses a 128KB boundary or to check if the boundary is crossed and split the transfer in two in that case.
DMA with length = 0 is actually length = 65536
If the length of a DMA operation is set to 0 then the VDP will interpret it as 65536 instead. This can lead to memory corruption if your code doesn't make sure that transfers with no length are not executed.
Some early emulators (such as Gens and most of its forks) don't implement this properly and treat length = 0 as no transfer, which led to many old Sonic hacks loading garbage when graphics were exactly a multiple of 4KB (due to an oversight in the decompression routine that failed to check for this situation).
Safe margins on screen
Two tiles (16px) at the sides and one tile (8px) at the top and bottom are prone to get cut off depending on TV settings (especially old TVs), so don't put any important information there.
On 50Hz systems the vertical resolution is much higher so the output is squished vertically. In that case you won't risk data getting cut off vertically but there will be thick borders on the top and bottom so you need to make sure that the border color doesn't stick out.
YM2612 status register
The YM2612 status register must be only read from
$4000 (Z80) and only bits 7, 1 and 0 are
meaningful. Trying to read from other addresses or expecting bits 6-2
to have a specific value will not work across revisions, as their
behavior changed between discrete YM2612 and the integrated version.
Some games don't follow this and have issues in some revisions, e.g. music is slower in Hellfire ('90s release) in newer revisions, while Earthworm Jim has broken tempo in older revisions. Some homebrew also doesn't work on some consoles for this reason.
PSG is on the 68000 bus
The PSG is actually a component of the VDP and hence is accessed from the 68000 bus. This is something that needs to be taken into account when trying to work around issues caused by DMA but is often overlooked.
- Model 1 outputs stereo through the headphone jack, while AV jack is mono only.
- Model 2 outputs stereo through the AV jack.
- Model 3 outputs mono through the AV jack even if you use a stereo cable (many clones do this as well).
Audio lines in cartridge slot
The audio-in pins in the cartridge slot are not connected in the Nomad and model 3 (as well as most clones), it requires modification to make them work. Also, most revisions have resistors on the audio lines but they're missing in a couple of them, so the cartridge's audio needs to have 16 ohm worth of impedance on their own.
SSG-EG support in clones
YM2612's SSG-EG feature is not properly implemented in the clones sold by AtGames (and likely Tectoy's 2017 console) and will result in ear-wrecking noise if used. If you wish to support those clones then either avoid SSG-EG, or detect them and disable SSG-EG when needed (which isn't great but will be less awful at least).
Many old emulators implement SSG-EG wrong as well (albeit usually not as awful) so you could also try to detect them to be on the safe side.
Delay when toggling the TH line when reading a controller
When reading a controller, you're supposed to have a 2µsec delay between writing the TH line (bit 6) and reading back the buttons. This is not required by most controllers, but it was intended to give some leeway for more complex pads relying on a microcontroller.
Backwards compatibility with 6-button controllers
6-button controllers achieve backwards compatibility by requiring the game to toggle the TH line (bit 6) many times in a short period in order to return the extra buttons. For most games this is not an issue, since they only read the controller once every frame, but a few games toggle it too many times and it breaks.
The Mode button is intended to help work around this: if a game misbehaves with a 6-button controller, hold down the Mode button while powering on the console. In the case of infrared wireless controllers, you have to press Mode+X+C instead.
No Reset button on a Nomad
The Nomad is missing the Reset button, so any feature relying on this will not work on a Nomad (infamously, X-Men is unbeatable on a Nomad because of this).
Modem port availability
The modem port is only present on early consoles and as such you shouldn't make peripherals that rely on it (it's a shame, but that's how it is).
Slow I/O pin pull-up when toggling pin direction
The I/O pins have a pull-up that will make them show up as 1 if left floating when set to input. However, when switching from output to input there's a small transition period where the old value remains since the pull-up is slow — except on the Nomad, where the transition happens faster.
This breaks Decap Attack, which for some reason toggles the direction of the TL pin and expects the button values to remain by reading them immediately after. This works on consoles due to the slow pull-up but not on the Nomad.
3-button compatibility with Master System games
The 3-button controller (and the 6-button one by extension) is intended to be backwards compatible with Master System games, where the B and C buttons become the 1 and 2 buttons, respectively. This requires the TH line to be held high or floating (i.e. set as an input), which is normally the case for most Master System games but a few of them accidentally set TH as a low output.
If you're making a Master System game, make sure to keep TH as an input in order to let the 3-button controller to work (unless you want to make use of the extra buttons).
This is a problem for Master System and Mega Drive, but not on Mark III which does not let you set the TH pin direction (it's hardwired to be an input). However, this also means that on a Mark III you can't make use of the extra buttons and you'll be forced to treat the 3-button controller like a Master System one.
Sports Pad on a Nomad
The Master System's Sports Pad doesn't work on a Nomad (even in digital mode), so if you're planning to support it in your homebrew you will want to include a disclaimer. The issue seems to be with the Sports Pad itself (looks like it doesn't like the more noisy power rails and struggles to read the trackball motion as a result).
Many emulators initialize memory on boot
Same issue as with flashcarts initializing memory with zeroes, except it's way more prevalent with emulators and often there isn't a way to change this.
BlastEm defaults to initializing with zeroes, but you can change this setting by going to Settings → System → Initial RAM Value and changing its value to "random".
68000 can't do word or longword accesses on odd addresses
The 68000 can't perform word or longword accesses to odd addresses, attempting to do so will lead to an address error. Many early (and not so early) emulators don't implement this for performance reasons which can lead to bugs not getting caught until trying it on real hardware.
Hardware delays are usually not emulated
There are several parts of the hardware that require delays after each write in order to work properly (e.g. writing to YM2612 registers needs a short delay before writing again, or resetting Z80+YM2612 requires you to wait at least 192 cycles (68000 clock)). Emulation often lets you go as fast as possible with no repercussions and this can lead to code that doesn't work on real hardware.
Unpadded ROMs may trip file type detection
Some emulators may try to autodetect if the ROM is a raw binary or some copier format. If the ROM isn't padded to a safe boundary then there's a chance that the emulator will detect the wrong format (you can usually tell because the title will be garbage instead of what's in the ROM header).