68k Mac ROM Boot Disk

The Mac Classic is the only mac that shipped capable of booting from a builtin ROM disk, when holding cmd-opt-x-o. I've created a similar concept on a Mac IIx. This is based on the IIsi ROM and a custom driver. When holding down the 'r' key on boot, the system can boot from an image stored in the ROM. Additionally, if 'a' is also held down, the driver will copy the disk image to RAM, allowing a writeable boot disk, which is required for things like AppleShare. This has been tested on the IIx, IIsi, IIci, and SE/30.

romdrv0.9.6.cpt.hqx ROMDisk driver source. This is just the driver. There's a lot of other mods to the ROM its self to make this work. This also now includes a control panel.

iisi+romdrv0.9+nomem+nosum.bin Modified IIsi ROM with driver, without disk image appended. This also disables the memory test on boot, resulting in much quicker boot times for macs with lots of RAM. The ROM checksum test is also disabled in this image.
iisi+romdrv0.9+nomem+nosum+img.bin Modified IIsi ROM with driver, with bootable 7.0.1 (Minimal system install for any mac) disk image. Also has the RAM test disabled.
iisi+romdrv0.9.6+nomem+nosum-8mb+chm.bin As above, without disk image appended, setup for the 8MB ROM SIMM (7.5mb disk image).

To use your own 1.5MB disk image with the driver, you can create one using your favorite emulator, minivmac and BasiliskII have both been known to work. Make sure the disk image is the way you want it (including the desktop file being correct, the window positions and sizing, etc.). Then append it to your modified ROM. I use cat(1). Once you've got the whole image ready, program it into your ROM SIMM with dougg3's ROM SIMM programmer.

Change history:
0.9.6: A single location to edit for both location of the disk image (0x524fc), and size of the disk image (0x52500). This also includes a fix for 24bit RAM booting I introduced when merging dougg3's change.

0.9.5: Substantially faster 32bit mode RAM booting due to a different method of allocating the RAM. It includes PRAM settings for using the ROMDisk for default startup, and selecting RAM vs. ROM. It includes a color HappyMac boot icon. It also includes 24bit RAM booting implemented by dougg3.

0.9.4: This fixes an issue with SCSI booting, and with ROM booting System 7.5.x. The memory allocation for the RAM copy of the ROM disk image is delayed until later in the boot process. As a result, when booting from RAM instead of a slight delay before the happy mac icon, the delay happens before Finder loads. This delay is copying the contents of the ROM disk image into RAM.

0.9: Rearranges the startup code. If 'r' is not held down on the first boot attempt, the driver will deallocate all its memory, and will not attempt to boot from the ROM disk again until a reboot occurs.

0.8: Adds custom disk icon from olePigeon of the 68kmla forums.

0.7: Refactors the RAM boot disk memory allocation into the Open call instead of on the first Prime operation. Memory allocations in Prime are bad mkay.
This also adds a counter for failing read operations on boot when 'r' is not held down. This helps ensure it doesn't inadvertently boot from ROM when using SCSI disks that take forever to spin up.

0.6: Adds the ability to copy the contents of the disk image to RAM, in order to boot from a writeable RAM disk. Hold down 'r' and 'a' on boot.

0.5: Enables > 1MB ROM access (>512KB disk images).

Related Developments:

The most recent testing versions, worklog, and discussions can be found in this thread of the mac68k.info forums, with some additional investigation of the .netBOOT driver here and disabling the memory test here.

Technical Description:
The IIsi ROM was chosen as the basis of this modification mainly because it has usable space. The IIx ROM is 256KB and fairly dense. The IIsi ROM is 512KB and has unused drivers, and extra padding all over the place, which makes it easy to add code. The IIsi ROM is also frequently used by IIx and SE/30 owners to get a 32bit clean ROM and not need MODE32.

There are at least 2 completely unused drivers in the IIsi ROM: .netBOOT and .ATBOOT. These drivers appear intended to implement booting over appletalk, but are disabled in the shipping image. They are flagged as disabled to the resource manager, and therefore cannot be loaded through normal means. Specifically, the resource header in the IIsi ROM looks like this for the .Sony (floppy) driver:

0006c3b0  78 00 00 00 00 00 00 00  00 06 ab 60 00 06 c3 e0  |x..........`....|
0006c3c0  44 52 56 52 00 04 58 05  2e 53 6f 6e 79 44 3e 3e  |DRVR..X..SonyD>>|
0006c3d0  5b 28 63 29 c0 a0 00 00  00 00 46 c0 00 00 01 84  |[(c)......F.....|
The first byte there is flags on the resource, starting at byte 8 is a 32bit location of the next resource, starting at byte 16 is the 4 char type code (DRVR), starting at byte 20 is the 16 bit resource id (4), starting at byte 23 is the pascal string name of the resource. The last 2 bytes listed here are the size of the resource (0x184)
Now here is the .netBOOT driver resource header:
00051d40  18 00 00 00 00 00 00 00  00 00 00 00 00 05 1d 70  |...............p|
00051d50  44 52 56 52 00 31 58 08  2e 6e 65 74 42 4f 4f 54  |DRVR.1X..netBOOT|
00051d60  74 65 72 2c c0 a0 00 00  00 00 07 d0 00 00 02 44  |ter,...........D|
The flags are different, and this one never loads. Changing those flags from 0x18 to 0x78 causes the driver to be loaded.

So overwriting the .netBOOT driver is pretty straightforward, and it can now be loaded. This allows a vector that doesn't reduce stock functionality.
However, once the driver is loaded, a non-SCSI, non-Slot based disk isn't recognized by the Startup control panel (the PRAM boot values don't know how to deal with this either), so making it bootable is the next step. Floppy drives and the Classic ROM disk are both special cased as being preferred booting devices when they're available, so I've tried to follow that scheme here.

Here is the startup selection code that identifies whether a drive in the drive queue is a floppy:
1700   0C6A FFFB 0008            Cmp       $-5, $8(A2)
1706   4E75                      Rts
1708   0000 0000 0000            DC.B      '      '
170E   0000                      DC.B      '  '
This is comparing the driver reference number from the drive queue entry to -5, which is the floppy driver. There's also 8 bytes following that routine which are unused. So, the routine can be replaced by the following code to add our driver to the mix:
cmp #-5, 8(A2)
beq.b isfloppy
cmp #-50, 8(A2)
That will make the boot system treat drives with our driver as floppy disks. Specifically, it will always try to boot from it if it is available. The only problem is we don't want to always boot from the ROM disk, only when we choose to.

The obvious path is to only boot from the drive when keys are down. There isn't enough room in the 8 bytes above to add that check (the code we added uses all 8 bytes). The next best choice is to check in the driver's Open routine, and only add the drive queue entry structure when the keys are down. But the keyboard has not been initialized when our driver is Opened, so we can't check then.

This driver does the check in the Prime call. When the driver is Opened, it allocates some private storage, such as the drive queue entry, and some initialization data in our driver's dCtlStorage handle. Later, when we are chosen to boot, the Startup Manager will attempt to read boot blocks from the drive. The driver then checks to see if the driver has been initialized, and if not, checks if the 'r' key is held down. If it is not, the Prime call returns an error, removes our entry from the system's drive queue, and deallocates all associated memory. The boot blocks didn't get read, and the Start Manager continues looking for another disk to boot from. If the 'r' key is held down, the read is performed normally and the system boots. On subsequent Prime calls, the driver has been initialized, and the key down check isn't performed again. Additionally, if the 'a' key is held down the driver will copy the disk image from ROM into RAM, and refers to that for all subsequent accesses. This allows for a writeable boot volume, which is required for things like AppleShare.

24bit vs 32bit access
Enabling the full 1.5MB of available ROM space for the disk image was another chore. There are a couple of challenges. To start with, the ROM image in 24bit mode is only mapped from 0x00800000 to 0x008FFFFF, or 1MB. So, accessing the ROM in 24bit mode will always be limited to the first 1MB, which means only a 512KB disk image. In 32bit addressing, the ROM lives at 0x40800000, which is convenient for 24/32bit compatibility. In order to access the image there, we need to be in 32bit mode. The ROM loads the driver in 24bit mode from a resource map embedded in the ROM. This means the driver exists with a 24bit pointer, not a 32bit pointer, and when executing the driver, the program counter is not a valid 32bit address. If we just switch to 32bit mode while executing, we'll crash because we'll suddenly be executing an invalid portion of memory. To get around this, I moved the ROM access into a separate function which is copied into RAM when the driver is opened, and has a safe 24/32bit address. This function can safely toggle between addressing modes without fear of the program counter getting confused.

Finally, even when accessing 0x40800000 in 32 bit mode, only the first 1MB of ROM was mapped in. In order to map in the full ROM space, I needed to find the portion of ROM that controls that. For the IIsi image, it is this:
41B5A  C38B                      Exg.L     D1, A3
41B5C  32FC 0002                 Move      $2, (A1)+
41B60  32FC 0001                 Move      $1, (A1)+
41B64  47F9 FFFB E49C DT106:     Lea.L     ($-41B64), A3
41B6A  47FB B8F8                 Lea.L     DT106(A3.L), A3 ; Get the base of ROM
41B6E  22CB                      Move.L    A3, (A1)+       ; Store the base of ROM in 32bit addressing
41B70  202B 0040                 Move.L    $40(A3), D0     ; Get the size of the ROM stored at offset 0x40
41B74  22C0                      Move.L    D0, (A1)+       ; Store the size of the ROM
41B76  C38B                      Exg.L     D1, A3
41B78  222D FF88                 Move.L    $-78(A5), D1
41B7C  22C1                      Move.L    D1, (A1)+       ; Store the start of ROM in 32bit addressing
41B7E  22C0                      Move.L    D0, (A1)+       ; Store the size of the ROM
41B80  0281 00FF FFFF            And.L     $FFFFFF, D1     ; Strip off the top byte to get 24bit address
41B86  22C1                      Move.L    D1, (A1)+       ; Store the start of ROM in 24bit addressing
41B88  22C0                      Move.L    D0, (A1)+       ; Store the size of 24bit ROM
41B8A  C38B                      Exg.L     D1, A3
41B8C  4E75                      Rts
The obvious thing to try here was to alter the size of the ROM at offset 0x40. This has a couple problems: first, storing the ROM size as 2MB confuses things in 24bit mode, since that only has 1MB of address space available. Second, other parts of the ROM refer to that ROM size, and we're not *really* extending the ROM that large (for instance, the DeclROM is at the 512KB mark, not the 2MB mark).

So, I ended up rewriting this routine starting at 0x41B64 to just use hard coded constants for everything:
move.l #$40800000, (A1)+
move.l #$00200000, (A1)+
move.l #$40800000, (A1)+
move.l #$00200000, (A1)+
move.l #$00800000, (A1)+
move.l #$00080000, (A1)+
This way, 32bit and 24bit addressing can have different values.

RAM Test
When the system cold boots (first powers on), it performs a memory test. This test was pretty quick for the small values of memory used when these systems were produced, but many 68k macs now have more RAM than ever, and the memory test can take an excessive amount of time with these larger values.
There are 2 memory test functions located at 0x467E0 and 0x468F8 in the IIsi ROM. Both functions return via JMP (A6). To disable the memory tests, I've overwritten the starts of these functions with opcode 0x4ED6, which is JMP (A6).

ROM Checksum
The first 4 bytes of the ROM contain a checksum of the entire image. It is advantageous for us to disable the checksum validation for a couple reasons: it makes development easier, it makes it easier for people to make their own changes (tweaking icons, sounds, values), and some Daystar accelerators key off the ROM checksum to alter their behavior. Being able to set the ROM checksum to arbitrary values to alter the behavior of these peripherals can be useful.

The call to validate the checksum is here:

464DA  3E3C 0001      L496:      Move      $1, D7
464DE  4DFA 0006                 Lea.L     DT132, A6 ; load address of the next instruction into A6, to be used as a return vector
464E2  4EFA 0586                 Jmp       L497      ; L497 here is StartUpROMTest
464E6  4A86           DT132:     Tst.L     D6        ; Check the return value
464E8  6600 2630                 BNE       L470      ; L470 is Error1Handler, aka bad chimes.
464EC  4247                      Clr       D7
So replacing 464E2 through 464EB with NOP (4E71) disables both the call to check ROM and the test of the return result.

Machine specific details:
Development was done nearly exclusively with a IIx. So far it has been tested on a IIsi, IIci, and SE/30 all with varying amounts of RAM.

The disk image was created using minivmac. It is 1.5MB and is System 7.0.1 with the "Minimal System for any Macintosh" custom installation option selected.

Additional Tools:
dumpdrvr.cpt.hqx A simple tool that displays all loaded device drivers, and whether they're ROM or RAM based. Some ROM drivers (notably all DeclROM drivers) get loaded and run from RAM, so this isn't an indication of where the driver originated, simply where it is currently running from.
dumpdsk.cpt.hqx Another simple tool that displays the drive number, and the drive's driver entry number, of all drives in the system drive queue. Use this to see if the ROMDisk's drive is in the queue, but not not mounted. Use this in conjunction with the dumpdrvr tool to see what driver is associated with each drive in the queue. This also lets you post a diskEvt notification for a disk to get the Finder to try mounting it.
sc68 The sc68 atari project includes an assembler, as68, which was helpful for generating machine code to patch the IIsi ROM. The assembler can run under linux or OSX, and just dumps the machine code for the assembly you've written, without any sort of executable file type wrapper.

Updated January 23 2013