The N0RAM project project has received a series of upgrades since its initial functionality as a serial bootloader. As a last bit of functionality I’ve decided to make N0RAM suitable for a BIOS replacement, able to load a ROM via serial port as it already does, and attempting to load a ROM from the other available sources (Cartridge/Card/External) if available.

Feature already available on the N0RAM project starting at V1.3

Objectives

Adapt the existing N0RAM code to make it suitable as BIOS replacement. It should be able to perform these steps in this order.

  • Attempt to download a ROM via serial port
  • If unsuccessful, either:
    • By default, auto detect ROM media and launch it
    • On user request, launch one of the ROM media unconditionally
  • Finally, if all fails, prompt user to Reset the console

Original SEGA BIOS functionality

The original SEGA BIOS functionality, for export (non-JP) consoles is checking whether each of the media slots, EXT/Card/Cartridge, has a valid ROM inside of it, then chainload into it. The detailed inner workings of all known SEGA BIOS found on consoles is described at smspower.org but can be resumed in:

  • Checking the cartridge checksum
  • Set up the SEGA mapper
  • Write the last value of port 0x3E at the RAM base address 0xC000

Only the last two behaviours are required to be emulated to avoid compatibility issues.

Replacing the SEGA BIOS

N0RAM itself can run from any media slot, be it BIOS or any of the console slots so the only real issue is the ROM detection and chainload routines, which will have to be run from RAM, since the ROM will not be available during detection.

In essence, the chainloader code has these restrictions.

  • It must run from RAM, limitation by console’s design
  • It must run from any RAM address
  • It must prevent chainloading itself when not running as BIOS

Just like with N0RAM’s requirement of zero RAM usage, this code while not restricted by RAM usage, implies these further restrictions

  • No absolute jumps with JP, JP cc or CALL
  • All jumps limited to a maximum 127 bytes for use with JR and JR cc
  • All RAM addressing must be Stack-relative, via ADD HL, SP or similar.

Code from RAM

For the chainloader code location I’ve decided the safest place to locate it for future extensions would be the stack itself. In this way, the code can be loaded, called to, and then disposed off just by modifying the SP reg.

Currently, the chainloader loading routine would look like this:

Original stack:
SP --> [N0RAMboot return addr] //Return addres to go back to N0RAM

After copying loader code to stack
SP --> [Chainloader code     ]
       [N0RAMboot return addr]

At this point we save the current value of SP to jump to it later, then we push into the stack the arguments for the chainloader code (see: Loader detection further down) and the return address for if the chainloader fails to detect any ROM, give control back to the BIOS.

SP --> [Chainload return addr]
       [Chainload arguments  ]
       [Chainloader code     ] //Stored on a reg elsewhere, DE for example
       [N0RAMboot return addr]

Finally, to jump into the chainloader from an stored register we simply execute:

PUSH DE  ;DE having the chainloader code base address
RET      ;Use RET to jump to the PUSHed address

In the event the chainloader fails, it will return and we can continue our business simply by fixing the SP register.

ROM detection

The original SEGA way of detecting a ROM is by checking the SEGA ROM header but to allow loading of any program I opted instead on testing that:

  • Two reads of the first 256 Bytes are equal (same CRC)
  • At least 16 byte-to-byte differences are encountered

This has prevented booting any empty slot and has not prevented any real ROM from being booted so far.

Loader detection

N0RAM can boot from regular media, be it card, cartridge or external port. For this reason, detecting whether a ROM media is “ourselves” is requires as to not automatically load ourselves in a loop when there might be valid media on other slots.

The solution implemented is passing a “signature” blob of bytes as an argument to the chainloader and, if it detects the given the ROM under test has the same signature at the expected place, it will skip the media during the automatic chainloader.

On the particular case implemented on N0RAM, the signature are 14 random bytes +2 Bytes CRC, as to allow detection with both direct comparison or using a CRC. Current revision directly compares the signature.