Carrera in an SE/30 – the code part 2

3rd handler





Here’s the code doing all this:

3F9E:           MOVEQ   #1,D0
3FA0:           _SwapMMUMode  
3FA2:           PUSH.B  D0
3FAA:           MOVE.L  96(A5),D0
3FB0:           LEA     data69,A1
3FC6:           LEA     MacII_4th,A0
3FCA:           MOVE.L  A0,D2
3FCC:           LEA     data106,A0
3FD2:           SUBA.L  D2,A7
3FD4:           MOVEA.L A2,A0
3FD6:           MOVEA.L A7,A1
	  	; save the current VBR to the stack
3FD8:           MOVE.L  D2,D0
	  	; write my own VBR...
                ; This copies 180 bytes into 0x000000000 replacing the original VBR. 
                ; ... and kills Macsbug if not circumvented properly.
3FDC:           LEA     data106,A0
3FE0:           MOVEA.L A2,A1
3FE2:           MOVE.L  D2,D0
	  	; A1 = 00000 (dest)  - IIci FBB08000
	  	; D0 = B4    (count) - IIci same  
3FE6:           BSR     53_cmd_1x  ; Bring the C040 to life
3FEA:           MOVEA.L A7,A0  ; SP to A0
3FEC:           MOVEA.L A2,A1  ; SE30: 00000000
3FF0:           BRA.S   lae_163
3FF4:   lae_163 DBRA    D0,lae_162
3FF8:           ADDA.L  D2,A7 ; adjust the stack
3FFA:           POP.B   D0
3FFC:           _SwapMMUMode  
4002:           MOVE    (A7)+,SR
4004:           MOVEQ   #0,D0
4006:           RTS     
400C:           DC.L    #$00000050 ; Reset initial Program Counter
4010:           DC.L    #$00000048 ; Buserror  
4014:           DC.L    #$00000048 ; Adress Error
4018:           DC.L    #$00000048 ; Illegal Instruction
401C:           DC.L    #$00000048 ; Zero Divide
4020:           DC.L    #$00000048 ; CHK, CHK2 instruction
4028:           DC.L    #$00000048 ; Privilige Violation
402C:           DC.L    #$00000048 ; Trace
4030:           DC.L    #$00000048 ; LINE 1010 Emulation
4034:           DC.L    #$00000048 ; LINE 1111 Emulation
; and to be used to setup the 040 MMU registers.
4038: data107:  DC.L    #$0009FE00 ;  
403C:           DC.L    #$0009D6E2; 
4040:           DC.L    #$807FC040 ; 
4044:           DC.L    #$807FC040 ; SE30: 
4048:           DC.L    #$00000000 ; SE30: 00000000 
404C:           DC.L    #$00000000 ; SE30: 00000000 
4050:           CLR.L   $53000000  ; Poke 0 to $53000000
4058:           LEA     data107,A0 ; SE30: 9F900
405E:           MOVEA.L (A0)+,A1   ; 0009D6E2
4060:           MOVE.L  (A0)+,D4   ; 807FC040
4062:           MOVE.L  (A0)+,D5   ; 807FC040
4064:           MOVE.L  (A0)+,D6   ; 00000000
4066:           MOVE.L  (A0)+,D7   ; 00000000
4068:           MOVE.L  #$C000,D0
406E$           MOVEC   D0,ITT0   ; Set Instruction Transparent Translation
4072$           MOVEC   D0,DTT0   ; Set Data Transparent Translation
4076$           MOVEC   D1,SRP    ; Set Supervisor Rootpointer
407A$           MOVEC   D1,URP    ; Set User Rootpointer
407E:           MOVE.L  #$C000,D0
4084$           PFLUSHA           ; Invalidates all entries in the address translation cache
4086$           MOVEC   D0,TC 
408A:           LEA     data108,A0
4096:  data108: MOVEQ   #0,D0
4098$           MOVEC   D0,ITT0 
409C$           MOVEC   D0,DTT0 
40A0$           MOVEC   D4,ITT0 
40A4$           MOVEC   D5,DTT0 
40A8$           MOVEC   D6,ITT1 
40AC$           MOVEC   D7,DTT1 
40B0$           CINVA   BC
40B2:           NOP     
40B4:           MOVEQ   #0,D0
40B6$           MOVEC   D0,CACR
40BA:           JMP     (A1)    ; 0009D6E2
; END 040 Code being copied to somewhere by line 3FE4 

The Vector Base Register




0004:           DC.L    #$00000050 ; Reset initial Program Counter
     ; - ALL of these Vectors point to addr 0x48 -
0008:           DC.L    #$00000048 ; Buserror  
000C:           DC.L    #$00000048 ; Adress Error
0010:           DC.L    #$00000048 ; Illegal Instruction
0014:           DC.L    #$00000048 ; Zero Divide
0018:           DC.L    #$00000048 ; CHK, CHK2 instruction
0020:           DC.L    #$00000048 ; Privilige Violation
0024:           DC.L    #$00000048 ; Trace
0028:           DC.L    #$00000048 ; LINE 1010 Emulation
002C:           DC.L    #$00000048 ; LINE 1111 Emulation
0040:           DC.L    #$00000000 ; SE30: 00000000 
0044:           DC.L    #$00000000 ; SE30: 00000000 
0058:           MOVEA.L (A0)+,A1   ; 0009D6E2
005C:           MOVE.L  (A0)+,D4   ; 807FC040
0060:           MOVE.L  (A0)+,D5   ; 807FC040
0064:           MOVE.L  (A0)+,D6   ; 00000000
0068:           MOVE.L  (A0)+,D7   ; 00000000
006C:           MOVE.L  #$C000,D0
0070:           MOVEC   D0,ITT0    ; Set Instruction Transparent Translation
0074:           MOVEC   D0,DTT0    ; Set Data Transparent Translation
0078:           MOVEC   D1,SRP     ; Set Supervisor Rootpointer
007C:           MOVEC   D1,URP     ; Set User Rootpointer
0080:           MOVE.L  #$C000,D0
0084:           PFLUSHA            ; Invalidates all entries in the address translation cache
0088:           MOVEC   D0,TC 
008C:           LEA     data108,A0
009C:  data108: MOVEQ   #0,D0
00A0:           MOVEC   D0,ITT0 ; 0
00A4:           MOVEC   D0,DTT0 ; 0
00A8:           MOVEC   D4,ITT0 ; 807FC040
00AC:           MOVEC   D5,DTT0 ; 807FC040
00B0:           MOVEC   D6,ITT1 ; 00000000
00B4:           MOVEC   D7,DTT1 ; 00000000
00B8:           CINVA   BC
00BC:           NOP     
00C0:           MOVEQ   #0,D0
00C4:           MOVEC   D0,CACR
00C8:           JMP     (A1)    ; 0009D6E2
Farewell, old friend





Time for some special register explanation:


  • U – User Page Attributes – ignored by 040

Here’s an example:

807FC040 = 10000000011111111100000001000000 

which means:

  • a bit less than 2GB transparently translated (2032MB)
  • translation enabled
  • Supervisor Mode: ignore mode when matching
  • Cache mode: Noncacheable, Serialized
  • Write permitted

So let’s have a look at the code again:


The address of data108 is added to 0x53002000 and jumped to!
💡  Does 0x53002000 equal 0x00000000 for the C040?


  • Clear the ITT/DTT registers
  • Set the MMU to 0x807FC040 (see decoding example above)
  • invalidate caches and wait’a’NOP to have that happened
  • then disable all caches




6 thoughts on “Carrera in an SE/30 – the code part 2”

  1. Hi,

    Not knowing much about Mac hardware, it looks like bit 0 of $53000000 controls which CPU is active. The other CPU will likely be stalled on a memory access. This explains why the switch sequence includes NOPs, because the CPU may have prefetched some instructions and the code must make sure no changes happen to the CPU state which should be visible for the other CPU. With this knowledge in mind, I speculate 53_cmd_1x is a 030to040 switch routine, which starts on 030, switches to the 040 and stalls and will continue running on 030 when $53000000 bit 0 is cleared again. So all the code in this routine runs 030. 53_cmd_0 would then be the 040to30 counterpart. So all the code runs on 040 and the 040 is stalled somewhere around $1e00 while the 030 is active.
    The very first time the 040 is switched to, its internal state is the reset state, so the MMU etc needs to be setup. That’s what the code you mentioned here does and it ends by jumping into the 040to030 switch routine, as if a normal switch to 030 was performed. Only the state save does not need to be done, because the 040 did not have any state yet. I do wonder how the code distinguishes between a succesful 040 init and a failed one where it switches back to 030 at $0048 and puts the 040 in a permanent infinite loop. So if the 040 would ever be switched to, the system would just hang.

    1. Hey Peter,
      Thanks for commenting!
      Yes, it’s pretty clear, that “poking” a 1 to 0x5300k is the low-level command for the 040 to take over (as mentioned in the post: “After that’s been written, 53_cmd_1x is called, presumably telling the C040 to come to life”), while a “0” is switching it off again.
      Also the whole call-chain to switch between the 030 and 040 is well documented, see the full source in my github repository (search for ‘switchto030’ and ‘switchto040’ both called by ‘SetCPUmode’).
      The “infinitve loop” is IMHO a typical ‘sit and wait for interrupt’ thing.

      1. Another question: doesn’t 0x807FC040 as TTR value mean all virtual addresses from 0x80000000 to 0xffffffff are translated 1:1 to a physical address?

  2. You’re likely right about MacII_3rd being C040_MMU_Setup. I think the reason for ADDA.L #$53002000,A0 is simply that this routine has to be independent of the final MMU setup at least to the extent possible. So the code cannot rely on how the first 16MB will be mapped in the final setup, using either the page table or a transparent translation. The only guarantee is probably that the address space required for the C040 itself is mapped. Hence the procedure is as follows:
    1) setup temporary transparent translation for the first 16MB so we can continue to run when the MMU is on.
    2) load page table pointers for user and supervisor mode
    3) enable MMU
    4) jump to known good mapping (ie in the C040 address space)
    5) load final transparent translation setup
    6) switch back to 030

    1. about your question ‘Does 0x53002000 equal 0x00000000 for the C040?’ Realizing that 0x2000 is the page size, I wouldn’t be surprised the page table contains a mapping of 0x53002000 to 0x0. This can be verified by inspecting the page table ofcourse.

Leave a Reply

