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.
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).
- Color happymac icon! PPC's are the only ones with color boot icons anymore. Discussion and links in this thread of the mac68k.info forums.
- Default startup PRAM settings! An experimental version of the driver with a control panel to control driver settings, such as defaulting to booting from ROM or RAM (or not at all), and changing the disk image size. Discussion and links in this post of the mac68k.info forums.
- Boot from serial! The Serial Disk driver has been integrated into the IIsi ROM along with the ROM disk driver. Discussion and links in this post of the mac68k.info forums.
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.
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:
IsItFloppy: 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) isfloppy: rtsThat 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 RtsThe 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)+ nopThis way, 32bit and 24bit addressing can have different values.
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).
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 D7So 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.
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.