Hey everyone,
I'm not sure exactly where to post this, so I'll put it in the Compact Mac forum. I've been talking with @Arbee about a weird issue I found in MAME when emulating the Classic II, and thought I should share it with the whole group. BTW, if you're not familiar with MAME, you really should check it out. It's getting more and more complete to the point that almost all 68k Macs can be emulated. You may think of arcade games when you think of MAME, but it has really turned into an excellent Mac emulator too, thanks to Arbee!
Anyway, the original problem I found is that an emulated Mac Classic II (macclas2) doesn't boot up in MAME if you have the computer set for 32-bit addressing in the Memory control panel. It displays a Sad Mac with the codes 0000000F and 00000001. I don't have a Classic II myself, but I know for sure that this problem doesn't happen on actual hardware. Trying to track this down has led me down a huge rabbit hole.
I've been disassembling the Classic II's ROM with IDA and playing in MAME's amazing debugger to try to figure out what's going on, and tracked down the problem to this section of the ROM code:
The purpose of this function is to configure the VIA2 (address of it is stored at $CEC) so that the sound interrupt is enabled. There's a jump table that selects the correct code to run on different Mac models. I haven't found anything in the ROM that would indicate that this function would be skipped. Although I don't have a Classic II that I can use to confirm this for certain, I am pretty confident that this code runs on actual hardware.
What's happening is when this routine runs at startup, it loads a byte from 0xCB3 into D0. The value it loads is 0x11 = 17. This byte is an identifier of which computer it is. The value of 17 is correct for the Classic II. It ends up being doubled so it can be used as an offset into the jump table, but as you can see, it's past the end of the table. So when it runs, it ends up jumping to 0x40A43B94, which is inside the
This causes MAME's debugger to display the following instructions as the next few instructions:
So basically it gets out of sync until 0x40A43B9C. When that last instruction
What I've discovered is that this CAS.W instruction is not quite what MAME thinks it is. To be honest, I have no idea precisely what it does. It doesn't fit the pattern of any valid CAS instruction. Apple's MacsBug debugger interprets it slightly differently:
IDA and objdump refuse to disassemble it at all.
I've determined experimentally by playing around with different 68030 machines on actual hardware (Mac IIci, LC II) that this instruction somehow ends up modifying A1. It doesn't touch any of the data registers, and it doesn't touch any of the address registers except A1. I haven't been able to figure out exactly what the operation is that it does, but what I can tell is that A1's existing value, A7, and PC all factor into what A1's final value becomes. It's something along the lines of A1 = (A1 | PC) & A7 | 0x80, but I think the 0x80 might be changing based on something else (memory contents somewhere? not sure?).
If I had an actual Classic II, I would do some ROM hacking to prove that this is what's also happening on hardware, but unfortunately I don't have one. However, since I've observed on multiple 68030-based Macs in MacsBug that this instruction changes the value of A1, it's plausible that the Classic II's ROM had a bug with this out-of-bounds jump table access and this instruction was unknowingly saving the day for Apple by putting a valid value into A1 so that the
Anyone have a Classic II, while also being comfortable with ROM hacking? It would be awesome if we could somehow confirm that this is what's actually happening on hardware. If we hacked the ROM code to do something like wait forever instead of executing the
I'm not sure exactly where to post this, so I'll put it in the Compact Mac forum. I've been talking with @Arbee about a weird issue I found in MAME when emulating the Classic II, and thought I should share it with the whole group. BTW, if you're not familiar with MAME, you really should check it out. It's getting more and more complete to the point that almost all 68k Macs can be emulated. You may think of arcade games when you think of MAME, but it has really turned into an excellent Mac emulator too, thanks to Arbee!
Anyway, the original problem I found is that an emulated Mac Classic II (macclas2) doesn't boot up in MAME if you have the computer set for 32-bit addressing in the Memory control panel. It displays a Sad Mac with the codes 0000000F and 00000001. I don't have a Classic II myself, but I know for sure that this problem doesn't happen on actual hardware. Trying to track this down has led me down a huge rabbit hole.
I've been disassembling the Classic II's ROM with IDA and playing in MAME's amazing debugger to try to figure out what's going on, and tracked down the problem to this section of the ROM code:
Code:
ROM:40A43B4A V8SNDINTPATCH1RTN:
ROM:40A43B4A moveq #0,d0
ROM:40A43B4C move.b ($CB3).w,d0
ROM:40A43B50 bpl.s loc_40A43B54
ROM:40A43B52 rts
ROM:40A43B54
ROM:40A43B54 loc_40A43B54:
ROM:40A43B54 movea.l ($2B6).w,a0
ROM:40A43B58 movea.l $110(a0),a0
ROM:40A43B5C clr.l $22(a0)
ROM:40A43B60 clr.l $1E(a0)
ROM:40A43B64 lea loc_40A43BD8,a0
ROM:40A43B68 move.l a0,($D80).w
ROM:40A43B6C add.w d0,d0
ROM:40A43B6E jmp loc_40A43B72(pc,d0.w)
ROM:40A43B72
ROM:40A43B72 loc_40A43B72:
ROM:40A43B72 bra.s loc_40A43B92 ; case 0
ROM:40A43B74 bra.s loc_40A43B92 ; case 1
ROM:40A43B76 bra.s loc_40A43B92 ; case 2
ROM:40A43B78 bra.s loc_40A43B92 ; case 3
ROM:40A43B7A bra.s locret_40A43BA4 ; case 4
ROM:40A43B7C bra.s loc_40A43BA6 ; case 5
ROM:40A43B7E bra.s loc_40A43B92 ; case 6
ROM:40A43B80 bra.s loc_40A43BB2 ; case 7
ROM:40A43B82 bra.s loc_40A43BA6 ; case 8
ROM:40A43B84 bra.s loc_40A43BA6 ; case 9
ROM:40A43B86 bra.s loc_40A43BA6 ; case 10
ROM:40A43B88 bra.s loc_40A43BA6 ; case 11
ROM:40A43B8A bra.s loc_40A43BA6 ; case 12
ROM:40A43B8C bra.s locret_40A43BA4 ; case 13
ROM:40A43B8E bra.s loc_40A43BA6 ; case 14
ROM:40A43B90 bra.s loc_40A43BA6 ; case 15
ROM:40A43B92
ROM:40A43B92 loc_40A43B92:
ROM:40A43B92 movea.l ($CEC).w,a1
ROM:40A43B96 bclr #4,$1800(a1)
ROM:40A43B9C move.b #$90,$1C00(a1)
ROM:40A43BA2 rts
ROM:40A43BA4
ROM:40A43BA4 locret_40A43BA4:
ROM:40A43BA4 rts
ROM:40A43BA6
ROM:40A43BA6 loc_40A43BA6:
ROM:40A43BA6 movea.l ($CEC).w,a0
ROM:40A43BAA move.b #$90,$13(a0)
ROM:40A43BB0 rts
ROM:40A43BB2
ROM:40A43BB2 loc_40A43BB2:
ROM:40A43BB2 movea.l ($CEC).w,a0
ROM:40A43BB6 move.b #2,8(a0)
ROM:40A43BBC rts
The purpose of this function is to configure the VIA2 (address of it is stored at $CEC) so that the sound interrupt is enabled. There's a jump table that selects the correct code to run on different Mac models. I haven't found anything in the ROM that would indicate that this function would be skipped. Although I don't have a Classic II that I can use to confirm this for certain, I am pretty confident that this code runs on actual hardware.
What's happening is when this routine runs at startup, it loads a byte from 0xCB3 into D0. The value it loads is 0x11 = 17. This byte is an identifier of which computer it is. The value of 17 is correct for the Classic II. It ends up being doubled so it can be used as an offset into the jump table, but as you can see, it's past the end of the table. So when it runs, it ends up jumping to 0x40A43B94, which is inside the
movea.l ($CEC).w,a1 instruction.This causes MAME's debugger to display the following instructions as the next few instructions:
Code:
40A43B94 cas.w D1, D0, ($4,A4); (2+) 0CEC 08A9 0004
40A43B9A move.b D0, D4 1800
40A43B9C move.b #$90, ($1c00,A1) 137C 0090 1C00
So basically it gets out of sync until 0x40A43B9C. When that last instruction
move.b #$90, ($1c00,A1) finally runs, that is what causes the bus error because A1 was left at its existing value of 0xFFFF8FBA, and that section of the address space isn't mapped to anything in the MMU.What I've discovered is that this CAS.W instruction is not quite what MAME thinks it is. To be honest, I have no idea precisely what it does. It doesn't fit the pattern of any valid CAS instruction. Apple's MacsBug debugger interprets it slightly differently:
Code:
CAS.W D1,D2,$0004(A4) | 0CEC 08A9 0004
IDA and objdump refuse to disassemble it at all.
I've determined experimentally by playing around with different 68030 machines on actual hardware (Mac IIci, LC II) that this instruction somehow ends up modifying A1. It doesn't touch any of the data registers, and it doesn't touch any of the address registers except A1. I haven't been able to figure out exactly what the operation is that it does, but what I can tell is that A1's existing value, A7, and PC all factor into what A1's final value becomes. It's something along the lines of A1 = (A1 | PC) & A7 | 0x80, but I think the 0x80 might be changing based on something else (memory contents somewhere? not sure?).
If I had an actual Classic II, I would do some ROM hacking to prove that this is what's also happening on hardware, but unfortunately I don't have one. However, since I've observed on multiple 68030-based Macs in MacsBug that this instruction changes the value of A1, it's plausible that the Classic II's ROM had a bug with this out-of-bounds jump table access and this instruction was unknowingly saving the day for Apple by putting a valid value into A1 so that the
move.b #$90, ($1c00,A1) instruction wouldn't crash.Anyone have a Classic II, while also being comfortable with ROM hacking? It would be awesome if we could somehow confirm that this is what's actually happening on hardware. If we hacked the ROM code to do something like wait forever instead of executing the
move.b #$90, ($1c00,A1) instruction, we could prove that this code is actually executing on hardware. If that proves to be successful, I would dole out bonus points to someone who could perform some sort of hack to the ROM code that allows determining what A1 is actually being loaded with after that instruction.











