-
Notifications
You must be signed in to change notification settings - Fork 295
ROM Analysis in Basilisk II Emulation
- Table of Contents
- Assumptions
- Disassemble original 68k Mac ROM
- Mac ROM in real Macintosh hardware
- Mac ROM in Basilisk II emulation
- Bibliography
The analysis below is based on the study of the Performa old world 32bit clean ROM (with MD5 hash af343f3f1362bf29cefd630687efaa25
). I found that this Performa 630 ROM works great for System 7 and Mac OS 8.1.
Assuming you get a Mac ROM binary file for BII from somewhere, the next step is to poke around it. A ROM file is a collection of data and M68K machine code which performs hardware test at boot time, provides low level common routines such A-Trap and etc.
For emulation purposes, BII patches the original ROM so that it can bypass hardware test and complete overwrite Macintosh hardware drive with new emulated driver. I will demonstrate disassembling this Mac ROM with cxmon and radare2.
$ cd macemu/cxmon
$ ./bootstrap
$ ./configure
$ make
$ sudo make install
The command below disassemble ROM file and dump the text into an external file.
cxmon -m ‘[ 0 “PERFORMA.ROM”’ ‘d68 0 fffff’ > PERFORMA.ROM.DISAM
You can also run cxmon in standalone interactive mode or even enable --with-mon
option in BII build to debug at runtime. Please refer to cxmon help
NOTE:
After reading the disassemble code, cxmon didn't disassemble correctly in some cases. This is due to the fact that ROM mixed with data and code. For example, read ROM starting from 0x2a offset. The machine code 0x46fc2700 should be at address $0000008c. The correct assembly should be move #?2700, sr
, instead of ori.b #$fc,d0
and move.l d0,-(a3)
.
11 000000000000002a: 4efa 0060 jmp ($0000008c,pc)
...
37 000000000000008a: 0000 46fc ori.b #$fc,d0
38 000000000000008e: 2700 move.l d0,-(a3)
39 0000000000000090: 4dfa 000a lea ($0000009c,pc),a6
Build radare2 from git repo. You also need to build acr.
The documentation of radare2 is long. TLDR; Here is an example of using it:
[Ricky@xps ROM.Disas]$ radare2 PERFORMA.ROM
-- Switch between print modes using the 'p' and 'P' keys in visual mode
[0x00000000]> e asm.arch=m68k
[0x00000000]> pd 10 arch=m68k
0x00000000 066842140000 addi.w 0x4214, 0x0(a0)
0x00000006 002a067c4efa ori.b 0x7c, 0x4efa(a2)
| 0x0000000c 00804efa007c ori.l 0x4efa007c, d0
| 0x00000012 32f10100 move.w (a1, d0.w), (a1)+
| 0x00000016 00000044 ori.b 0x44, d0
| 0x0000001a 0007ec10 ori.b 0x10, d7
| 0x0000001e 4efa1220 jmp 0x1220(pc)
| 0x00000022 000d2da0 invalid
| 0x00000026 4efa22e8 jmp 0x22e8(pc)
| 0x0000002a 4efa0060 jmp 0x60(pc)
NOTE: radare2 has the same issue as cxmon. This comes to the dead end. To read ROM disassemble code, you need to tag code segment manually. [Update Aug 25, 2017] In MPW GM, ROMMap folder provides a long list of Macintosh ROMs that segment A-Trap. However, I can't find Performa 640 ROM mapping there.
In Apple Mac IIci and Mac Quadra 900 Developer Note, it specified that ROM must be in a fixed address space. Because ROM machine code is written as position independent code. In reality, its location in memory is relocatable. That’s why BII can load ROM to whatever memory address in guest OS without breaking its logic.
So far I haven’t found an automatic way to disassemble Performa ROM without human tagging code segment. I can’t easily get a full picture of what Performa ROM contains. But reading BII ROM patches, Macintosh ToolBox trap and Macintosh OS trap, I think I need to have basic understanding of using illegal instruction exception technique in M68k CPU.
M68k CPU suspends execution instruction flow if it encounters an illegal instruction. An illegal instruction is an instruction that contains any bit pattern in its first word that does
not correspond to the bit pattern of the first word of a valid M68k instruction or is a
MOVEC
instruction with an undefined register specification field in the first extension word.
When an illegal instruction exception happens, there are four steps in processing exception [1]:
- The processor makes an internal copy of the status register. Then it sets the S bit, changing to the supervisor privilege level. Next, it inhibits tracing of the exception handler by clearing the T1 and T0 bits.
- The processor determines the vector number of the exception.
- The processor saves the current processor context and creates an exception stack frame on the active supervisor stack and fills it with context information appropriate for the type of exception.
- The processor multiples the vector number of the exception by 4 to get the offset of vector table. Vector Base Register
VBR
points to the address of vector table. So add the offset withVBR
to get the address of the exception handler routine. Then, load Program CounterPC
with the look-up address and resume CPU execution.
The following is exception vector number table:
Macintosh OS reserves the first word 1010
(0xA) unimplemented instruction to implement so called A-Trap to provide ToolBox and OS API. Its vector number is 0xA
Basilisk II also uses undefined instruction with prefix 0x71xx
to patch Mac ROM and implement its emulated drivers. However, the implementation of illegal 0x71xx
instruction in BII bypasses M68k CPU illegal instruction exception. We will discuss this in next section.
TODO add more
After BII loads the ROM from file into memory, it patches the ROM in PatchRom()
function from the file src/rom_patches.cpp
.
TODO add more
Emulation starts at relative address 0x2a
i.e (MacROMBaseMac + 0x2a) in the patched ROM. TODO add more