|
Joined: Sep 2014
Posts: 7 Likes: 4
Member
|
Member
Joined: Sep 2014
Posts: 7 Likes: 4 |
Hi everyone, I've been a lurker for over a decade, but now I'm ready to post something. I've got a weird one. The original problem I found is that macclas2 doesn't boot up 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: 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: 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: 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. Is anyone able to double-check me? Regardless of whether this is truly how the hardware Classic II acts or not, MAME isn't currently doing what a real 68030 CPU does when it encounters this undocumented/accidental instruction. Is there any way to figure out what it actually does? Thanks!
|
|
|
|
Joined: Mar 2001
Posts: 17,239 Likes: 263
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,239 Likes: 263 |
To fill in a little more, the invalid A1 it's trying to write through is 0xFFFFABBA or 0xFFFF8BBA depending on I think -ramsize. In 32-bit mode that causes an MMU fault. In 24-bit mode the top 8 bits are stripped off and the result makes it through the MMU because that's a possible place for a PDS slot card to claim but in theory it should cause a bus error if no card is present.
|
|
|
|
Joined: Sep 2014
Posts: 7 Likes: 4
Member
|
Member
Joined: Sep 2014
Posts: 7 Likes: 4 |
Interesting! From what I can tell, the bad A1 value of 0xFFFF8FBA comes from the LEA instruction at 0x40A4AB86, so when adding the 0x1C00 offset to it, it should always be 0xFFFFABBA. I don't think it depends on RAM size, but I could definitely be wrong!
|
|
|
|
Joined: May 2012
Posts: 570 Likes: 12
Senior Member
|
Senior Member
Joined: May 2012
Posts: 570 Likes: 12 |
Speaking of weird instructions -- FMOVEM with an empty register list currently doesn't execute correctly (PC does not fully advance). It was fixed previously ( https://github.com/mamedev/mame/issues/8703) but regressed. Test cases: fmovem.x , ($C, a7) f237 f000 0170 0000 000c fmovem.x ($c, A7), f237 d000 0170 0000 000c
|
|
|
|
Joined: Sep 2014
Posts: 7 Likes: 4
Member
|
Member
Joined: Sep 2014
Posts: 7 Likes: 4 |
I ran a few more tests in MacsBug on my IIci and LC II (using a custom ROM SIMM on the IIci to put the instruction at 0x40A43B94), and found that even with the exact same input register values in A0-A7, D0-D7, and PC, the value that the instruction stores into A1 varies slightly. With the following setup: D0=22
D1=55B4
D2=40A16500
D3=FFFFFFF6
D4=800000
D5=0
D6=FFFFC
D7=30011
A0=40A43BD8
A1=FFFF8FBA
A2=FFFF73D4
A3=40A16ABA
A4=40A09AE6
A5=FC7F0
A6=FCA60
A7=FC63C
PC=40A43B94 The IIci ends up with A1=F86BC and the LC II ends up with A1=FA6BE. I don't know if this means that the mystery instruction is also using the contents of memory somewhere, or if what it does varies on different CPUs.
|
|
|
|
Joined: Sep 2014
Posts: 7 Likes: 4
Member
|
Member
Joined: Sep 2014
Posts: 7 Likes: 4 |
I bought a Classic II, repaired its logic board, set it up with a Pi Pico converting its video output to VGA, powered it with an ATX power supply, and performed some ROM hacking in order to see what's going on in hardware. Test 1: Replace the code at 0x40A43B9C ( move.b #$90, ($1c00,A1)) with a jump to a chunk of code I wrote that draws A1 to the screen. In other words, replace the instruction that causes a Sad Mac in MAME so that we see what A1 is in the hardware at that point. Result: ![[Linked Image from i.imgur.com]](https://i.imgur.com/A4t7TGE.png) Interesting, how did 0x40A4BBB2 get in there? That's a ROM address, so it's not really a valid base address to write to, but it doesn't cause a Sad Mac on hardware. Test 2: Replace the code at 0x40A43B94 (the invalid code that MacsBugs says is CAS.W D1,D2,$0004(A4)) with the same "jump to draw A1". In other words, replace the invalid CAS instruction so we can see what A1 is when we first reach it. Result: ![[Linked Image from i.imgur.com]](https://i.imgur.com/u5vOx2t.png) Yep, A1 has that same 0xFFFF8FBA value that we see on MAME. And the fact that we actually jumped to my drawing code there means that hardware is definitely making that same invalid out-of-bounds table jump we see on MAME. Test 3: NOP out the invalid CAS instruction at 0x40A43B94 so we can see what happens if the 68030 doesn't accidentally repair the value stored in A1. Result: ![[Linked Image from i.imgur.com]](https://i.imgur.com/VkEE6OB.png) Exactly the same problem that happens in MAME. BTW, all of these tests behave exactly the same regardless of whether I have 24-bit or 32-bit addressing selected. So MAME is being more forgiving than actual hardware with the invalid access in 24-bit addressing mode. Conclusion: I've confirmed that hardware follows the same code path as MAME, and that the invalid CAS instruction is changing A1 to point to somewhere in ROM and thus preventing a Sad Mac. This is a bug in the Classic II's ROM that the 68030 magically/accidentally worked around. Pretty cool!
|
3 members like this:
Vas Crabb, R. Belmont, Duke |
|
|
|
Joined: Sep 2014
Posts: 7 Likes: 4
Member
|
Member
Joined: Sep 2014
Posts: 7 Likes: 4 |
One last thing: I added more code to save and print all of the registers, and ran tests directly before and after the invalid CAS instruction. Data registers D0-7 are in the left column, A0-7 are in the right column. Before (jump added at 0x40A43B94): ![[Linked Image from i.imgur.com]](https://i.imgur.com/JXNCeja.png) After (jump added at 0x40A43B9A): ![[Linked Image from i.imgur.com]](https://i.imgur.com/R1h2m0M.png) So yeah, the equation I came up with earlier for what the invalid instruction does was way off. But the end result either way, is that A1 changes quite dramatically and everything else remains unchanged.
|
|
|
|
Joined: Sep 2014
Posts: 7 Likes: 4
Member
|
Member
Joined: Sep 2014
Posts: 7 Likes: 4 |
Another thing I want to mention -- I think one piece of knowledge to come out of this is that MAME's debugger is also incorrectly decoding valid CAS instructions.
If you change the data to 0C EC 00 81 00 04, which is the "correct" version of this instruction with several bits set to 0 as they are supposed to be, it still thinks it is cas.w D1, D0, ($4,A4). That's wrong -- if you look in the 68000 Programmer's Reference Manual and decode the instruction manually, you will see that Du is D2, not D0, because bits 8:6 of the second word are 010 = D2.
|
|
|
|
Joined: Mar 2001
Posts: 17,239 Likes: 263
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,239 Likes: 263 |
Interesting. I'll have to see why that is.
|
|
|
|
Joined: Mar 2006
Posts: 1,080 Likes: 7
Very Senior Member
|
Very Senior Member
Joined: Mar 2006
Posts: 1,080 Likes: 7 |
This is a bit of a poorly-educated guess as to why this opcode is doing what it is, but my guess is since bits 5,4,3 of the second opcode word are not zero, its accidentally executing a chunk of microcode that shouldn't be run which is pulling random internal state into some internal data bus, possibly ending up with two values driving said bus at the same time and the result being an OR of them.
0000 1100 1110 1100 0x0CEC CAS.w (d16, A4) 0000 1SS0 11MM MRRR SS = 10 = Word; MMM = 101 = (d16, An); RRR = 100 = A4
0000 1000 1010 1001 0x08A9 0000 x00U UUII ICCC x = doesn't care; UUU = 010 = D2 (Du, value to be read); III = 101 = (d16, An) INVALID, CCC = 001 = both D1 as comparison value *AND* A1+disp as destination value?!?
0000 0000 0000 0100 0x0004 = displacement of 4 for the d16, A4?
GUESS: so Dc (CCC above) gets used accidentally for two purposes simultaneously: the data register that should be compared to by CAS, and also the offset of the register that should be written to, since III is not 000 * III = 000 means write to an internal register which redirects to the mode/reg combination from the first word, this is the normal case * III = 101 means it ends up treating CCC as a d16, Ax offset, so CCC = 001 so its writing to A1, but since there's presumably no actual data register specified by the microcode, it ends up mashing the current program counter (which was what was last on the IDB?) AND the A1 current value, AND the D2 value specified by UUU onto the internal data bus simultaneously, so A1 | (PC[+4?]) | D2, and writing that back to A1
The only way to really properly emulate this mess is likely microcode/nanocode-level emulation of the 68030 as well as emulating internal bus conflict voodoo.
Another possible side effect of this would be a potential security issue of possibly being able to leak contents of the supervisor registers in user mode, not unlike a more limited version of the AMD ZenBleed bug.
"When life gives you zombies... *CHA-CHIK!* ...you make zombie-ade!"
|
1 member likes this:
robcfg |
|
|
0 members (),
80
guests, and
6
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,331
Posts122,197
Members5,077
|
Most Online1,283 Dec 21st, 2022
|
|
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!
|
|
|
|