![]() | by Michael Sinz (MKSoft Development) Copyright 1992-2001 - All Rights Reserved Handler.asm |
by Michael Sinz MKSoft Development Copyright 1992-2001 All Rights Reserved Documentation Enforcer Archive ![]() Source code for the main Enforcer tool: Source code for the MMU tool: Source code for SegTracker: Source code for FindHit: Source code for RebootOff: Source code for Move4K: Source code for LawBreaker:
|
* Enforcer by
* Michael Sinz
*
* Copyright 1992-2001
* All Rights Reserved
*
*****************************************************************************
* *
* Permission is hereby granted to distribute the Enforcer archive *
* containing the executables and documentation for non-commercial purposes *
* so long as the archive and its contents are not modified in any way. *
* *
* Enforcer and related tools are not in the public domain. *
* *
* Enforcer and related tools may not be distributed for a profit. *
* *
*****************************************************************************
*
*******************************************************************************
*
* Ok, so here is the really tricky part of Advanced Enforcer...
*
*******************************************************************************
*
* Advanced Enforcer assembly code parts...
*
*******************************************************************************
*
include "exec/types.i"
include "exec/lists.i"
include "exec/nodes.i"
include "exec/tasks.i"
include "exec/macros.i"
include "exec/execbase.i"
include "exec/memory.i"
include "exec/ables.i"
include "dos/dos.i"
include "dos/dosextens.i"
include "hardware/intbits.i"
include "hardware/cia.i"
include "graphics/gfxbase.i"
*
include "enforcer_rev.i"
*
xref _serdatr
xref _serdat
xref _intreq
xref _ciaa
xref _ciab
xref _LVOPermit
xref _LVOCachePreDMA
xref _LVOCachePostDMA
xref _LVOCacheControl
xref _LVOColdReboot
xref _LVOAlert
*
*******************************************************************************
*
* This is the MMU frame I use... (Note - must match what is in Enforcer.c)
*
STRUCTURE MMU_Frame,0
ULONG mmu_Flag
;
ULONG mmu_CRP ; URP for 68040/060
ULONG mmu_CRP_1 ; SRP for 68040/060
ULONG mmu_TC
;
ULONG mmu_CRP_OLD ; URP for 68040/060
ULONG mmu_CRP_OLD_1 ; SRP for 68040/060
ULONG mmu_TC_OLD
;
APTR mmu_LevelA
APTR mmu_LevelB
APTR mmu_LevelC
;
ULONG mmu_Indirect
APTR mmu_Magic
;
APTR mmu_Page0 ; 68040/060 optimization ;^)
;
ULONG mmu_InvalidA ; The invalid value at LevelA (68040/060 only)
ULONG mmu_InvalidB ; The invalid value at LevelB (68040/060 only)
ULONG mmu_InvalidC ; The invalid value at LevelC (68040/060 only)
;
STRUCT mmu_TT,4*4 ; Storage for TT regs (68040/060 only)
;
LABEL MMU_Frame_SIZE
*
*******************************************************************************
*
* This is the amount of space that the registers take on the stack...
*
STACK equ 4*(8+7)
*
*******************************************************************************
*
opt p=68030 ; Use 68030 instructions
*
*******************************************************************************
*
* Put a character (ASCII) out the hardware (serial or parallel) or to buffer...
* This will also handle ctrl-S (xoff)
* This will also stop output on ctrl-X
*
PutChar: tst.b d0 ; Check d0...
beq.s spc_exit ; We don't output NULLs...
*
* Now check for buffered I/O
*
tst.l OutputBuffer(pc) ; Check if we have a buffer...
bne.s buffer_out ; Do buffer if we have it...
*
* Non-buffered... Just hit the hardware so we need to do LF to LF/CR...
*
move.l d0,-(sp) ; Save output character...
cmp.b #10,d0 ; Check for a LF...
bne.s spc_norm ; If not one, skip...
moveq #13,d0 ; If an LF, output a CR...
bsr.s spc_wait ; (call self...)
spc_norm: move.l (sp)+,d0 ; Restore output character...
*
spc_wait: tst.l Parallel_Flag(pc) ; Check if we need to parallel
bne.s par_norm ; Do that insted...
move.w _serdatr,d1
btst #13,d1
beq.s spc_wait ; Wait for the character to finish
and.b #$7F,d1 ; Mask it...
cmp.b #$18,d1 ; Check for ^X
beq.s spc_exit ; If ^X, exit output...
cmp.b #$13,d1 ; Check for ^S
beq.s spc_wait ; Keep on waiting...
*
and.w #$ff,d0 ; Set up the character...
or.w #$100,d0 ; ...with the bits required...
move.w d0,_serdat ; ...send it out the port.
*
spc_exit: rts
*
* The parallel port workings...
*
par_norm: tst.b _ciab+ciapra ; We need to make sure that we
tst.b _ciab+ciapra ; give the CIA time to assert BUSY
tst.b _ciab+ciapra ; after a write...
*
par_wait: btst.b #CIAB_PRTRBUSY,_ciab+ciapra ; Check port status
bne.s par_wait ; Keep waiting...
*
move.b #$FF,_ciaa+ciaddrb ; Set output mode
move.b d0,_ciaa+ciaprb ; Write data...
tst.b _ciab+ciapra ; Do this to make sure
tst.b _ciab+ciapra ; output hold time is right
rts
*
* Do the buffer version...
*
buffer_out: move.l a0,-(sp) ; We need some play room...
move.l OutputBuffer(pc),a0 ; Get buffer address...
move.l WriteOffset(pc),d1 ; Get write offset...
add.l d1,a0 ; Make pointer to right spot...
move.b d0,(a0) ; Store character...
addq.l #1,d1 ; bump pointer...
cmp.l BufferSize(pc),d1 ; Are we at max?
bne.s bo_NotMax ; Did not max it yet...
moveq.l #0,d1 ; Loop back to start...
bo_NotMax: cmp.l ReadOffset(pc),d1 ; Are we at the read pointer?
beq.s bo_Full ; If so, we are full...
lea WriteOffset(pc),a0 ; Point at offset...
move.l d1,(a0) ; Store it...
bo_Done: move.l (sp)+,a0 ; Restore a0
rts ; and return...
bo_Full: moveq.l #$7F,d1 ; Get error character...
move.b d1,(a0) ; Store it...
bra.s bo_Done ; and exit...
*
*******************************************************************************
*
* Simple WriteText routine - Takes a format string and writes it to the
* I/O function.
*
* a0 = Format string ("text #hexlong ~hexshort ^hexbyte %string")
* a1 = Data stream...
*
PrintItSP: lea 4(sp),a1 ; Get sp+4 into a1...
PrintIt: movem.l d2/d3,-(sp) ; Save this...
PrintIt1: move.b (a0)+,d0 ; Get text byte...
beq.s PrintDone ; If 0, we are done...
cmp.b #'#',d0 ; Is it a HEX LONG?
beq.s Hex_Long ; If so, do hex
cmp.b #'~',d0 ; Is it a HEX WORD?
beq.s Hex_Word ; If so, do it
cmp.b #'^',d0 ; Is it a HEX BYTE?
beq.s Hex_Byte ; If so, do it
cmp.b #'%',d0 ; Is it a string
beq.s DoString ; If so, do it...
bsr PutChar ; Output the character
bra.s PrintIt1 ; Continue
PrintDone: movem.l (sp)+,d2/d3 ; Restore...
rts ; Else done...
*
DoString: movem.l d7/a0,-(sp) ; Save on stack...
move.l (a1)+,a0 ; Get string pointer...
moveq.l #127,d7 ; Max string length=128
DoString1: move.b (a0)+,d0 ; Get string
beq.s DoString2 ; Continue loop if done...
bsr PutChar ; Send character
dbra.s d7,DoString1 ; Loop back if limit not hit...
DoString2: movem.l (sp)+,a0/d7 ; Get a0 back...
bra.s PrintIt1
*
Hex_Long: move.l (a1)+,d2 ; Get long...
moveq.l #8-1,d3 ; Get char count...
Do_Hex: rol.l #4,d2 ; Shift over...
moveq.l #$F,d1 ; Get mask...
and.l d2,d1 ; Build index...
move.b HexTable(pc,d1.w),d0 ; Get character
bsr PutChar ; Output it
dbra d3,Do_Hex ; Keep doing it
bra.s PrintIt1 ; Next...
HexTable: dc.b '0123456789ABCDEF'
*
Hex_Word: move.l (a1)+,d2 ; Get long...
swap d2 ; Put upper end in...
moveq.l #4-1,d3 ; Character count
bra.s Do_Hex
*
Hex_Byte: move.l (a1)+,d2 ; Get long...
swap d2 ; Put upper end in...
rol.l #8,d2 ; Skip a byte...
moveq.l #2-1,d3 ; Character count
bra.s Do_Hex
*
*******************************************************************************
*
* Common routines for both 030 and 040 Enforcer reports...
*
*******************************************************************************
*
* Input: a1 = Args
*
* Output the main line with possible Intro string...
* ...and possible date string...
*
* Format: LONG-WRITE to C0EDBABE (INST) data=DDDD0000 PC: 07CD7486
* or: LONG-WRITE to C0EDBABE data=DDDD0000 PC: 07CD7486
* or: LONG-READ from C0EDBABE (INST) PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486 ----BUS ERROR----
*
Main_Disp: move.l a1,-(sp) ; Save a1...
lea Intro(pc),a1 ; Get argument...
tst.l (a1) ; Check if valid...
beq.s No_Intro ; If none, skip...
lea Intro_Fault(pc),a0 ; Get format string...
bsr PrintIt ; Output it...
No_Intro: tst.l DoDateStamp(pc) ; Do we do it?
beq.s No_Date ; If not, skip...
pea TimeStr(pc) ; Point at time string
pea DateStr(pc) ; Point at date string
lea DateTime(pc),a0 ; Get Format string...
bsr PrintItSP ; Print it...
addq.l #8,sp ; Clean up stack...
No_Date: move.l (sp)+,a1 ; Restore a1...
lea Main_Fault(pc),a0 ; Get format string...
bra PrintIt ; Print it and return...
*
*******************************************************************************
*
* Input: d0 = SR
* d7 = SSW
*
* Now do the second line...
*
* Format: USP: 07CF9A44 SR: 0008 SW: 0709 ( 0)(-)(-) TCB: 07CEFC70
*
USP_Disp: move.l ThisTask(a6),a4 ; TCB
move.l a4,-(sp) ; Save that...
move.l d7,-(sp) ; Save SSW
move.l d0,-(sp) ; Save SR
movec.l usp,a4 ; User stack...
move.l a4,-(sp) ; And save that...
moveq.l #'U',d1 ; Not interrupt
btst.l #13,d0 ; Check if supervisor state...
beq.s usp_NoIntr ; If not supervisor, skip...
moveq.l #'S',d1 ; Mark as supervisor...
usp_NoIntr: bfextu d0{21:3},d0 ; Get interrupt level...
add.b #'0',d0 ; Make into ASCII
lea Line_2a(pc),a0 ; Point at line...
move.b d1,(a0)+ ; Store Interrupt
move.b d0,(a0)+ ; Store level
*
moveq.l #'-',d0 ; Assume not FORBID
tst.b TDNestCnt(a6) ; Check if FORBID
bmi.s usp_NotForbid ; If negative, not forbid...
moveq.l #'F',d0 ; Set Forbid
usp_NotForbid: move.b d0,2(a0) ; Store Forbid flag...
*
moveq.l #'-',d0 ; Assume not DISABLE
tst.b IDNestCnt(a6) ; Check if DISABLE
bmi.s usp_NotDisable ; If negative, not disable...
moveq.l #'D',d0 ; Set Disable
usp_NotDisable: move.b d0,5(a0) ; Store Disable flag...
*
lea Line_2(pc),a0 ; Get format
bsr PrintItSP ; Output
lea 16(sp),sp ; Adjust stack as needed...
rts
*
*******************************************************************************
*
* Input: a4 = Register stack frame
* a5 = Pointer to PTEST routine
* d4 = PC pointer...
*
RegDisp: tst.l Small_Flag(pc) ; Check if SMALL
bne.s rd_StkDone ; If SMALL, skip to names...
*
************************
*
* This is now the data registers
*
* Format: Data: 01234567 32165497 00000000 00000000 00780021 00000017 00000020 0078FA8E
*
lea DataRegs(pc),a0 ; Get format line
move.l a4,a1 ; Get data...
bsr PrintIt ; Display it...
*
tst.l DRegCheck(pc) ; Are we to SegTracker these?
beq.s rd_AReg ; Skip if not...
move.l a4,a0 ; Get data pointer...
moveq.l #8-1,d0 ; Number to do...
do_DReg: move.l (a0)+,d3 ; Get value...
bsr SegTrack ; Do SegTracker
dbra.s d0,do_DReg ; Do all of them...
*
************************
*
* This is now the address registers
*
* Format: Addr: 01234567 32165497 00000000 00000000 00780021 00000017 00000020 --------
*
rd_AReg: lea AddrRegs(pc),a0 ; Get format line
lea 8*4(a4),a1 ; Get data...
bsr PrintIt ; Output it...
*
tst.l ARegCheck(pc) ; Are we to SegTracker these?
beq.s rd_Stack ; Skip if not...
lea 8*4(a4),a0 ; Get data pointer...
moveq.l #7-1,d0 ; Number to do...
do_AReg: move.l (a0)+,d3 ; Get value...
bsr SegTrack ; Do SegTracker
dbra.s d0,do_AReg ; Keep going...
*
************************
*
* This is now the stack lines...
*
* Format: Stck: 01234567 32165497 00000000 00000000 00780021 00000017 00000020 0078FA8E
*
rd_Stack: lea DataLine(pc),a2 ; Get format line
movec.l usp,a3 ; Get data...
move.l StackLines(pc),d2 ; Number of stack lines...
moveq.l #8*4,d3 ; Data advance...
bra.s rd_StkLoop_in ; Enter the loop...
*
rd_StkLoop: lea StackLine(pc),a0 ; Get line header...
bsr PrintIt ; Display header...
move.l a3,a1 ; Get data...
add.l d3,a3 ; Advance to the next
move.l a3,a0 ; Get into a0 for
jsr (a5) ; PTEST as needed (030/040)
bne.s rd_BadStack ; If bad, exit stack loop
move.l a2,a0 ; Get format...
bsr PrintIt ; If stack is OK, display it
rd_StkLoop_in: dbra.s d2,rd_StkLoop ; Keep going for all lines
bra.s rd_StkDone ; When done, exit...
*
rd_BadStack: move.l a3,-(sp)
lea BadStack(pc),a0 ; Get bad stack message
bsr PrintItSP ; output message...
move.l (sp)+,a3 ; Restore...
*
rd_StkDone: move.l d4,d3 ; Do PC counter...
bsr.s SegTrack ; Check it on the seglist...
move.l SegLines(pc),d2 ; Number
move.l usp,a3 ; Get data pointer...
bra.s rd_SegStart ; Start segtrack of stack
rd_SegLoop: move.l a3,a0 ; Get address to test...
jsr (a5) ; Check it (PTest)
bne.s rd_SegDone ; Exit if not valid...
move.l (a3)+,d3 ; Get number...
bsr.s SegTrack ; Do SegTrack...
rd_SegStart: dbra.s d2,rd_SegLoop ; Do loop...
rd_SegDone: rts ; We be done...
*
*******************************************************************************
*
* Input: a0 = Trap PC value
* a5 = Pointer to PTEST routine
*
* This will display the 16 long words around the PC address
*
* Format: PC-8: 01234567 32165497 00000000 00000000 00780021 00000017 00000020 0078FA8E
* PC *: 01234567 32165497 00000000 00000000 00780021 00000017 00000020 0078FA8E
*
PC_Disp: tst.l ShowPC_Flag(pc) ; Check if we should...
beq.s pc_NoPC_Flag ; If not, skip...
jsr (a5) ; PTEST as needed (030/040)
bne.s pc_PC_Invalid ; If invalid, display that...
move.l a0,a2 ; Save for a moment...
lea PC_1(pc),a0 ; Get first string
bsr PrintIt ; Display it...
lea DataLine(pc),a0 ; Get data format
lea -8*4(a2),a1 ; Point at data...
bsr PrintIt ; Display it...
lea PC_2(pc),a0 ; Get second string
bsr PrintIt ; Display it...
lea DataLine(pc),a0 ; Get data line...
move.l a2,a1 ; Get data pointer
bra.s pc_PC_PrintIt ; and print it...
*
pc_PC_Invalid: lea PC_Invalid(pc),a0 ; Get invalid string...
pc_PC_PrintIt: bsr PrintIt ; Output it...
pc_NoPC_Flag: rts ; Return...
*
*******************************************************************************
*
* Input: d3 = Address to find/display...
*
* The SegTracker interface...
*
* Format: ----> 075483D2 - "mem" Hunk 0000 Offset 00000272
*
SegTrack: movem.l a0-a3/d0,-(sp) ; Save registers...
move.l SegTracker(pc),d0 ; Get tracker FindSeg pointer
beq.s nd_NoTracker ; If NULL, no tracker
move.l d0,a3 ; Function pointer...
subq.l #4,sp ; Space for offset...
move.l sp,a2 ; Point at it...
subq.l #4,sp ; Space for hunk...
move.l sp,a1 ; Point at it...
move.l d3,a0 ; Get fault address...
jsr (a3) ; Look for segment...
move.l d0,-(sp) ; Store name pointer...
beq.s nd_NoFind ; If no name, skip output
move.l d3,-(sp) ; Store fault address...
lea SegTrackLine(pc),a0 ; String...
bsr PrintItSP ; Display it...
moveq.l #10,d0 ; Get LF
bsr PutChar ; Output LF
addq.l #4,sp ; Restore extra longword...
nd_NoFind: addq.l #4,sp ; Restore the stack
addq.l #8,sp ; to where it was...
nd_NoTracker: movem.l (sp)+,a0-a3/d0 ; Restore...
rts ; return...
*
*******************************************************************************
*
* Input: a0 = Trap PC value
* a5 = Pointer to PTEST routine
* d4 = SR...
*
* The Task name/process name/hunk-o-matic output... (Plus segtracker)
*
* Format: Name: "New_WShell" CLI: "mem" Hunk 0000 Offset 00000272
*
NameDisp: move.l a0,d3 ; Store fault address...
btst.l #13,d4 ; Check if supervisor state...
beq.s nd_User ; If not supervisor, skip...
bfextu d4{21:3},d0 ; Get interrupt level...
beq.s nd_User ; Level 0 is not an interrupt
add.b #'0',d0 ; Add in ASCII 0...
move.b d0,ProcInt2 ; Save it...
pea ProcInt(pc) ; Effective address...
lea TaskName(pc),a0 ; Get name string
bsr PrintItSP ; Display task name...
addq.l #4,sp ; Clean up stack...
bra nd_NameDisp ; And we are done...
nd_User: move.l ThisTask(a6),a3 ; TCB
subq.l #8,sp ; Space on the stack...
moveq.l #0,d6 ; Clear d6 (TYPE cache)
move.l d6,a4 ; Clear a4 (CLI pointer)
move.l a3,d0 ; Get task structure...
btst.l #0,d0 ; Is it odd?
bne nd_InvalidTask ; If so, we are invalid...
move.l a3,a0 ; for the PTEST
jsr (a5) ; Do PTEST
bne.s nd_InvalidTask ; Id
move.b LN_TYPE(a3),d6 ; get task type
cmp.b #NT_TASK,d6 ; Is it NT_TASK
beq.s nd_GoodTask ; It is a good task...
cmp.b #NT_PROCESS,d6 ; Is it NT_PROCESS
bne.s nd_InvalidTask ; If not, it is invalid...
nd_GoodTask: lea InvalidName(pc),a1 ; Get invalid name...
move.l LN_NAME(a3),d0 ; Get real name...
beq.s nd_NoName ; If no real name, skip...
move.l d0,a0 ; Get ready for PTEST
jsr (a5) ; Do PTEST
bne.s nd_NoName ; If invalid, skip...
move.l a0,a1 ; Store name...
nd_NoName: move.l a1,(sp) ; Save on stack...
lea TaskName(pc),a0 ; Get name string
bsr PrintItSP ; Display task name...
cmp.b #NT_PROCESS,d6 ; Check if process
bne.s nd_NameDone ; If not, we are done...
*
* Now to handle the process...
*
tst.l pr_TaskNum(a3) ; Check for CLI number
beq.s nd_NameDone ; No CLI?
move.l pr_CLI(a3),d0 ; Get CLI pointer (BPTR)
add.l d0,d0 ; To make a ...
bcs.s nd_InvalidCLI ; Bad BPTR?
add.l d0,d0 ; ... real pointer
bcs.s nd_InvalidCLI ; Bad BPTR?
beq.s nd_NameDone ; No CLI...
move.l d0,a0 ; Get CLI pointer for PTEST
jsr (a5) ; Do PTEST
bne.s nd_InvalidCLI ; Say invalid if so...
move.l a0,a4 ; Get CLI pointer...
lea InvalidName(pc),a1 ; Get name pointer...
move.l cli_CommandName(a4),d0 ; Get CLI name...
add.l d0,d0 ; Convert from BPTR...
bcs.s nd_NoCLIName ; Skip if bad BPTR...
add.l d0,d0 ; ... to a normal pointer
bls.s nd_NoCLIName ; Skip if NULL or invalid BPTR
move.l d0,a0 ; Get into address reg...
jsr (a5) ; Do PTEST #5,(a0),#7
bne.s nd_NoCLIName ; If invalid, skip...
tst.b (a0) ; Check if empty...
beq.s nd_ShortCLI ; If so, skip...
addq.l #1,a0 ; Point at real name...
nd_ShortCLI: move.l a0,a1 ; Name looks valid...
nd_NoCLIName: move.l a1,(sp) ; Put it onto the stack...
lea CLIName(pc),a0 ; Get format
bra.s nd_PrintIt
*
* The error cases
*
nd_InvalidCLI: lea InvalidCLI(pc),a0 ; Get invalid CLI
bra.s nd_PrintIt ; And go display it
nd_InvalidTask: lea InvalidTask(pc),a0 ; Get invalid task
nd_PrintIt: bsr PrintItSP ; Display it...
*
nd_NameDone: cmp.b #NT_PROCESS,d6 ; Check for process...
bne.s nd_HunkDone ; If not process, done...
*
* Now for the hunk-o-matic...
*
moveq.l #0,d5 ; No seglist yet...
move.l a4,d0 ; Check if we have a CLI...
beq.s nd_NoCLISeg ; No CLI segment...
move.l cli_Module(a4),d5 ; Get this SegList
bne.s nd_HaveSeg ; If we got one, just skip...
nd_NoCLISeg: move.l pr_SegList(a3),d5 ; Get seg array...
beq.s nd_HunkDone ; If none, we are done...
addq.l #3,d5 ; Point at element 4...
add.l d5,d5 ; Make from BPTR
add.l d5,d5 ; ... into real address...
move.l d5,a0 ; Get address...
move.l (a0),d5 ; Get final seglist...
beq.s nd_HunkDone ; If none, we are done...
*
* At this point we have what looks like a seglist...
*
nd_HaveSeg: moveq.l #0,d6 ; Hunk counter...
nd_SegLoop: add.l d5,d5 ; Convert seglist BPTR
bcs.s nd_InvalidSeg ; Invalid?
add.l d5,d5 ; Next...
bcs.s nd_InvalidSeg ; Invalid?
beq.s nd_HunkDone ; End of seglist?
move.l d5,a0 ; Get into address reg...
jsr (a5) ; Do PTEST
bne.s nd_InvalidSeg ; If invalid, go...
*
move.l -4(a0),d1 ; Get size...
bftst d1{0:5} ; Test upper 5 bits...
bne.s nd_InvalidSeg ; If larger than 8meg, invalid!
move.l d3,d0 ; Temp value...
sub.l d5,d0 ; Subtract...
bcc.s nd_MaybeSeg ; Could be...
*
nd_NotThisSeg: move.l (a0),d5 ; Get next pointer
addq.l #1,d6 ; Count to next one...
bra.s nd_SegLoop ; Keep looping...
*
nd_MaybeSeg: cmp.l d1,d0 ; Check if this seg...
bcc.s nd_NotThisSeg ; If no carry, not here...
subq.l #4,d0 ; Offset...
move.l d0,4(sp) ; Save offset...
move.l d6,(sp) ; Hunk number...
lea HunkInfo(pc),a0 ; Get hunk info
bra.s nd_PrintIt1 ; And print it...
*
* Output for when the seglist is invalid
*
nd_InvalidSeg: lea InvalidSeg(pc),a0 ; Get format
nd_PrintIt1: bsr PrintItSP ; Output...
*
nd_HunkDone: addq.l #8,sp ; Restore stack...
nd_NameDisp: moveq.l #10,d0 ; Get LF
bra PutChar ; Output LF (and rts)
*
*******************************************************************************
*
* This routine will flash the PowerLED as required by the settings of
* the user. *MUST NOT* change any registers...
*
FlashLED: move.l d0,-(sp) ; Save here...
move.l LED_Count(pc),d0 ; Get flash count
beq.s NoFlash ; Skip the flash if 0
bchg.b #1,$BFE001 ; Toggle the power LED
bra.s FlashLoopIn ; Enter the loop
FlashLoopHi: swap d0 ; Swap back to Hi/Lo
FlashLoop: tst.b $BFE001 ; Read CIA for delay...
FlashLoopIn: dbra.s d0,FlashLoop ; Do the flash...
swap d0 ; Swap to Lo/Hi
dbra.s d0,FlashLoopHi ; Do high-order loop...
NoFlash: move.l (sp)+,d0 ; Restore d0...
rts
*
*******************************************************************************
*
* The following code will test if you have the right hardware and if
* you can use the MMU (if it is busy)
*
* It will return FALSE if enforcer can not start. (0)
* It will return 1 if enforcer can start on a 68030 MMU
* It will return 2 if enforcer can start on a 68040 MMU
* It will return 3 if enforcer can start on a 68060 MMU
*
* On input, a6==ExecBase...
*
_Test_MMU: xdef _Test_MMU
moveq.l #0,d0 ; Assume failed...
movem.l a4-a6,-(sp) ; Save
move.l NewExecBase(pc),a6 ; Get execbase...
move.w AttnFlags(a6),d1 ; Get CPU flags
btst #AFB_68020,d1 ; Check CPU flag
beq.s Test_MMU_Exit ; Must be 68020 at least...
lea Test_MMU(pc),a5 ; Get supervisor code
btst #AFB_68040,d1 ; Is it a 68040?
beq.s Test_MMU_Sup ; If not, just do super mode
lea Test_MMU_040(pc),a5 ; If 68040, we do 68040 stuff
tst.l Lib68060(pc) ; Check if we have an 68060
beq.s Test_MMU_Sup ; If not, use the 68040 start
lea Test_MMU_060(pc),a5 ; Set for 68060 startup...
Test_MMU_Sup: JSRLIB Supervisor ; Jump into supervisor mode...
Test_MMU_Exit: movem.l (sp)+,a4-a6 ; Restore
rts
*
* Check for the 68030 MMU and Superkickstart...
*
Test_MMU: clr.l -(sp) ; Clear some stack space
clr.l -(sp) ; for the pmove
*
* Now, we do a simple MMU test. This will not catch some of the
* silly 68EC030 CPUs that do not cause errors on MMU operations...
*
move.l #1,d0 ; Assume we have an MMU...
movec.l vbr,a1 ; Get VBR
move.l $2C(a1),a5 ; Store old F-Line
move.l $10(a1),a4 ; Store old IllegalInstruction
lea BadMMU(pc),a0 ; Get new F-Line
move.l a0,$2C(a1) ; Store new F-Line
move.l a0,$10(a1) ; Store new IllegalInstruction
pmove.l tc,(sp) ; Get TC register
move.l a5,$2C(a1) ; Restore F-Line
move.l a4,$10(a1) ; Restore IllegalInstruction
tst.l d0 ; Will be 0 if no MMU...
beq.s Test_MMU_Done ; If it did, exit...
move.l (sp),d1 ; Check for MMU setting match
bpl.s Test_MMU_Done ; If not on, we continue...
*
* MMU is on, so check where the ROM is...
*
move.l ROM_Addr(pc),a0 ; Get address to locate...
*
; ptestr #5,(a0),#7,a1 ; Find the ATC for it...
dc.w $F010,$9F35 ; Assembler bug!
*
moveq.l #0,d0 ; Assume error...
moveq.l #3,d1 ; Mask for descriptor...
and.l (a1),d1 ; Mask it...
subq.l #1,d1 ; Make sure it is 1
bne.s Test_MMU_Done ; If not, we blow out of here
moveq.l #$FFFFFFFF,d0 ; Full on...
clr.b d0 ; Clear lower byte...
and.l (a1),d0 ; Get the table value...
lea ROM_Addr(pc),a0 ; Get ROM address...
move.l d0,(a0) ; Store it...
moveq.l #1,d0 ; We have a 68030...
*
Test_MMU_Done: addq.l #8,sp ; Adjust stack
rte
*
* This code will run if there is an F-Line exception during MMU work...
*
BadMMU: moveq.l #0,d0 ; Make d0 failure...
rte ; and return...
*
*******************************************************************************
*
* Copy up the VBR and location 4 into the local VBR area...
* Trashes a2 and a3 and a5...
*
MakeNewVBR: lea SetNewVBR(pc),a5 ; Build the table...
JSRLIB Supervisor ; Do the call...
JSRLIB CacheClearU ; Clear caches...
rts
SetNewVBR: movec.l vbr,a0 ; Get current VBR
lea OldVBR(pc),a1 ; Get storage for it...
move.l a0,(a1)+ ; Store and point at new VBR
move.w #256-1,d0 ; We need to do 256 vectors...
copy_VBR: move.l (a0)+,(a1)+ ; Copy vector
dbra.s d0,copy_VBR ; ...do all 256 of them...
lea NewBusError(pc),a0 ; Get vector address...
move.l a2,(a0) ; Store bus error vector...
lea NewLevel1(pc),a0 ; Get level 1 address
move.l (a0),a2 ; Store old one...
lea Level_1(pc),a3 ; Get my replacement...
move.l a3,(a0) ; Make new one...
lea OldLevel1(pc),a0 ; Get old address...
move.l a2,(a0) ; Store old address...
lea EnforcerTask(pc),a0 ; Task pointer address...
move.l ThisTask(a6),(a0) ; Store it too...
lea NewExecBase(pc),a0 ; Get execbase storage...
move.l a6,(a0) ; Store execbase...
rte
*
*******************************************************************************
*
* Turn on the MMU with the MMU Frame structure given
*
_MMU_On: xdef _MMU_On
movem.l d2-d7/a2-a6,-(sp) ; Save all registers (simple)
move.l a0,a4 ; Store frame in a4
move.l a4,mmu_Frame ; Store frame for ColdReboot()
move.l NewExecBase(pc),a6 ; Set up ExecBase...
FORBID
*
* Now, check the MMU type...
*
move.l a4,d0 ; Check for MMU structure
beq.s MMU_Error ; If none, error...
move.l mmu_Flag(a4),d0 ; Get MMU type...
subq.l #1,d0 ; Check for standard 68030
beq.s std_030_on ; If standard...
subq.l #1,d0 ; Check for 68040
beq MMU_On_040 ; Do 040 stuff...
subq.l #1,d0 ; Check for 68060
beq MMU_On_060 ; Do 060 stuff...
MMU_Error: moveq.l #0,d0 ; Error...
MMU_Off_RTS: ; Same as MMU_On_RTS...
MMU_On_RTS: PERMIT ; Let it go again...
movem.l (sp)+,d2-d7/a2-a6 ; Restore...
rts
*
* Set up the vectors
*
std_030_on: lea Patches_030(pc),a0 ; Patch table
bsr AddPatches ; Add the patches...
bset.b #7,$00DE0002 ; Mark as fake cold boot...
lea BusError(pc),a2 ; Get bus error handler...
tst.l Quiet_Flag(pc) ; Check if we are quiet
beq.s mmu_full ; IF not, skip
lea BusQuiet(pc),a2 ; Get the quiet bus handler
mmu_full: bsr MakeNewVBR ; Make the new VBR
*
* Now turn on the MMU
*
lea NewVBR(pc),a0 ; Get NewVBR...
do_MMU_030: lea MMU_030(pc),a5 ; Get address...
JSRLIB Supervisor ; Do it...
moveq.l #1,d0 ; Return TRUE...
bra.s MMU_On_RTS ; and return...
*
* This is the main MMU turn off code...
*
_MMU_Off: xdef _MMU_Off
movem.l d2-d7/a2-a6,-(sp) ; Save all registers (simple)
move.l a0,a4 ; Store frame in a4
move.l NewExecBase(pc),a6 ; Set up ExecBase...
FORBID ; Stop multitasking...
*
* Now, check the MMU type...
*
move.l a4,d0 ; Check for MMU structure
beq.s MMU_Error ; If none, error...
move.l mmu_Flag(a4),d0 ; Get MMU type...
subq.l #1,d0 ; Check for standard 68030
beq.s std_030_off ; If standard...
subq.l #1,d0 ; Check for 68040
beq MMU_Off_040 ; Do 040 stuff...
subq.l #1,d0 ; Check for 68060
beq MMU_Off_060 ; Do 060 stuff...
bra.s MMU_Error ; Return...
*
std_030_off: tst.l mmu_Frame(pc) ; Check if in ColdReboot()
beq.s cold_030 ; If so, skip patch remove...
bsr RemPatches ; Remove patches...
beq.s MMU_Error ; If not, error...
*
cold_030: bclr.b #7,$00DE0002 ; Unmark as fake cold boot...
lea mmu_CRP_OLD(a4),a0 ; Get old setting
lea mmu_CRP(a4),a1 ; Get new setting
move.l (a0)+,(a1)+ ; Copy it... (CRP)
move.l (a0)+,(a1)+ ; Copy it... (CRP_1)
move.l (a0)+,(a1)+ ; Copy it... (TC)
move.l OldVBR(pc),a0 ; Get old VBR
bra.s do_MMU_030
*
* Save off the old frame...
*
MMU_030: or.w #$0700,sr ; Full disable...
movec.l a0,vbr ; Set up vbr...
lea mmu_TC_OLD(a4),a0 ; Get old TC buffer
lea mmu_CRP_OLD(a4),a1 ; Get old CRP buffer
pmove.l tc,(a0) ; Store old TC
pmove.q crp,(a1) ; Store old CRP
lea mmu_TC(a4),a0 ; Get new TC buffer
lea mmu_CRP(a4),a1 ; Get new CRP buffer
move.l (a0),-(sp) ; Store MMU setting...
bpl.s MMU_030_off ; If off, skip...
and.l #$7FFFFFFF,(sp) ; Disable MMU
pmove.l (sp),tc ; Turn off MMU
pmove.q (a1),crp ; Set up CRP
MMU_030_off: pmove.l (a0),tc ; Set up TC
addq.l #4,sp ; Restore sp...
rte
*
*******************************************************************************
*
* This is the noisy bus error handler... (Main enforcer...)
* Note that location 4 is quick-checked for speed...
*
BusError: bclr.b #8-8,$0A+0(sp)
beq.s be_NonData ;Not a data cycle fault...
btst.b #6-0,$0A+1(sp)
beq.s be_Ignore ;Ignore writes
cmp.l #4,$10(sp)
bne.s be_Kludges ;Not a read of location 4...
move.l NewExecBase(pc),$2C(sp) ; Copy execbase pointer...
rte ;(exit 1)
*
* Now for the real handling...
*
be_Kludges: move.l Bad_ReadValue(pc),$2C(sp)
be_Ignore: bset.b #8-8,$0A+0(sp) ; Reset bit I cleared...
be_NonData: bsr FlashLED ; Flash the power LED
movem.l d0-d7/a0-a6,-(sp) ; Save registers
move.l NewExecBase(pc),a6 ; Get ExecBase
move.w STACK+$0A(sp),d7 ; Get Frame information
*
************************
*
* Put together the first hit line...
*
* Format: LONG-WRITE to C0EDBABE (INST) data=DDDD0000 PC: 07CD7486
* or: LONG-WRITE to C0EDBABE data=DDDD0000 PC: 07CD7486
* or: LONG-READ from C0EDBABE (INST) PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486 ----BUS ERROR----
*
move.l STACK+$18(sp),a5 ; Get Write data...
move.l STACK+$10(sp),a3 ; Get fault address...
moveq.l #(55-41),d6 ; Spaces needed...
*
lea Extra_Fault(pc),a0 ; Point at extra fault...
clr.b (a0) ; Clear the byte for now...
lea Inst_Fault(pc),a4 ; Get instruction fault...
bftst d7{16:2} ; Get mask...
bne.s be_InstFault ; If bits set, we have fault...
lea Norm_Fault(pc),a4 ; Otherwise, normal
be_InstFault: lea Read_Fault(pc),a2 ; Get default string...
btst.l #6,d7 ; Check for Write...
bne.s be_ReadFault ; It was a read...
lea Write_Fault(pc),a2 ; Get write fault string...
move.b #' ',(a0) ; Make Extra_Fault display...
moveq.l #0,d6 ; Number of spaces...
be_ReadFault: lea Long_Str(pc),a1 ; Get pointer to "LONG"
moveq.l #0,d5 ; Number of extra spaces...
bfextu d7{26:2},d0 ; Get the size...
beq.s be_GotSize ; If 0, we got the size...
addq.l #6,d5 ; We need 6 more if BYTE...
addq.l #7,a1 ; Point at Byte...
subq.l #1,d0 ; Is 1?
beq.s be_GotSize ; If so, BYTE is right...
subq.l #2,d5 ; 2 less spaces if WORD...
addq.l #7,a1 ; Point at WORD...
be_GotSize: lea Final_Fault(pc),a0 ; Point at final fault...
move.b 5(a1),(a0) ; Get output type...
movem.l a1-a5,-(sp) ; Arguments...
move.l sp,a1 ; Get pointer to args...
bsr Main_Disp ; Disply it...
lea 5*4(sp),sp ; Restore stack...
*
* Now for the formatted PC
*
tst.l d6 ; Is d6 0?
bne.s be_DoPC ; If not, do spaces...
move.l d5,d6 ; Get new d6...
be_DoPC: lea PC_Fault(pc),a0 ; Get fault...
sub.l d6,a0 ; Number of spaces...
move.l STACK+$02(sp),a1 ; Get PC...
pea NULL_String(pc) ; NULL physical string
subq.l #2,sp ; Space for PTEST result...
ptestw #5,(a3),#7 ; Check if MMU valid address
*
; pmove.w mmusr,(sp) ; Get status register
dc.w $F017,$6200 ; Darn assembler bug!!!
*
move.w (sp)+,d0 ; Get mmusr into d0
btst.l #10,d0 ; Check for invalid
bne.s be_NotPhy ; Not a physical bus error
btst.l #11,d0 ; Check for writeprotect
bne.s be_NotPhy ; If so, not a physical
addq.l #4,sp ; Restore stack
pea Physical(pc) ; Push the physical string...
be_NotPhy: move.l a1,-(sp) ; PC onto argument list
bsr PrintItSP ; Print it...
addq.l #8,sp ; Restore stack...
*
************************
*
tst.l Tiny_Flag(pc) ; Check if TINY
bne.s be_Cleanup ; If TINY, skip rest...
*
************************
*
* Now do the second line...
*
move.w STACK+$00(sp),d0 ; Get SR
bsr USP_Disp ; Display it (SSW in d7)
*
* Do register display as needed...
*
lea ptest_030(pc),a5 ; Get PTEST routine address
move.l STACK+$02(sp),d4 ; PC address...
move.l sp,a4 ; Point at register frame
bsr RegDisp ; Display registers/stack
*
* Do PC display as needed...
*
move.l d4,a0 ; Get PC into a0
bsr PC_Disp ; a5 is already PTEST
*
* Do Name & HUNK display as needed...
*
move.l d4,a0 ; Get PC into a0
move.w STACK+$00(sp),d4 ; Get SR into d4
bsr NameDisp ; a5 is already PTEST
*
************************
*
* Cleanup and exit...
*
be_Cleanup: ; Tell this bad task to take a quantum off. Tasks go out on
; interrupts, so we need to fake one.
or.w #SF_SAR!SF_TQE,SysFlags(a6) ; fake scheduling
move.w #INTF_SETCLR!INTF_SOFTINT,_intreq ; softint
*
movem.l (sp)+,d0-d7/a0-a6 ; Restore...
bclr.b #8-8,$0A+0(sp) ; Clear DF bit...
beq.s ebe_NonData ; If not DATA fault...
rte
*
************************
*
* Not a data fault? Too bad, we'll have to crash you. The illegal
* instruction vector points to the same place as bus error used to.
*
ebe_NonData: move.l NewIllegal(pc),-(sp) ; Put on illegal vector
rts ; Jump to it...
*
************************
*
* "Easy" Bus error handler.
* "see no evil, hear no evil, say no evil, no!"
*
BusQuiet: bclr.b #8-8,$a+0(sp)
beq.s ebe_NonData ;Not a data cycle fault...
btst.b #6-0,$a+1(sp)
beq.s ebe_Ignore ;Ignore writes
cmp.l #4,$10(sp)
bne.s ebe_Kludges ;Not a read of location 4...
move.l NewExecBase(pc),$2C(sp) ; Copy execbase pointer...
rte ;(exit 1)
ebe_Kludges: move.l Bad_ReadValue(pc),$2C(sp)
ebe_Ignore: rte ;(exit 1)
*
*******************************************************************************
*
* The following routine will do a ptest on (a0) and return
* the status in the flags. (bne invalid on rts)
* Trashes d0...
*
ptest_030: subq.l #2,sp ; Space for PTEST result...
ptestr #5,(a0),#7 ; Check if address is valid
*
; pmove.w mmusr,(sp) ; Get status register
dc.w $F017,$6200 ; Darn assembler bug!!!
*
move.w (sp)+,d0 ; Get mmusr into d0
btst #10,d0 ; Check for invalid
rts ; Return flags
*
*******************************************************************************
*
* A new Level1 handler that will jump to the old one when done...
*
Level_1: move.l OldLevel1(pc),-(sp) ; Make new return address...
move.l d0,-(sp) ; Save...
move.l ReadOffset(pc),d0 ; Get read offset...
cmp.l WriteOffset(pc),d0 ; Are they the same?
beq.s l1_NoData ; If they are, no data for me
movem.l d1/a0/a1/a6,-(sp) ; Save so re can call exec...
move.l EnforcerTask(pc),a1 ; Get task to signal...
move.l #SIGBREAKF_CTRL_F,d0 ; Get signal to use...
move.l NewExecBase(pc),a6 ; Get execbase...
JSRLIB Signal ; Signal the task...
movem.l (sp)+,d1/a0/a1/a6 ; Restore...
l1_NoData: move.l (sp)+,d0 ; Restore...
rts ; Return and run real level-1
*
*******************************************************************************
*
* Ok, now for the 68040 code...
*
opt p=68040
*
*
*******************************************************************************
*
* The following code is a bit of hacking...
* It moves certain items out of memory that is below the 4K mark...
*
* It also allocates all memory that is below that mark and throws it away
*
* Trashes almost everything... :-)
*
Hack_4k: tst.l Quiet_Flag(pc) ; Check if quiet...
bne no_gfx_hack ; If quiet, skip page-0 hacks
hack_alloc: moveq.l #1,d0 ; Allocate 1 byte...
moveq.l #MEMF_CHIP,d1 ; CHIP memory...
JSRLIB AllocVec ; Allocate it...
tst.l d0 ; Did it work?
beq.s hack_alloc_done ; (This should never happen)
bftst d0{0:20} ; Check if in lower 4K
beq.s hack_alloc ; If so, go for another...
move.l d0,a1 ; This one is too far...
JSRLIB FreeVec ; Put it back...
hack_alloc_done:
*
* Now, we check if the MemList header is still in the lower 4K...
*
FORBID ; We must FORBID in here...
lea.l MemList(a6),a3 ; Get MemList...
hack_mem: move.l (a3),a3 ; Get next node...
move.l (a3),d0 ; Check for done...
beq.s hack_mem_done ; If no more, we are done!
move.l a3,d0 ; Get node address...
bftst d0{0:20} ; Is it in the lower 4K?
bne.s hack_mem ; If not, keep looking...
*
* Ah! so this header is in low mem! Ok, we will move it...
*
moveq.l #MH_SIZE,d0 ; Get size to allocate...
moveq.l #0,d1 ; Any memory type...
JSRLIB AllocMem ; Allocate it...
tst.l d0 ; It should always work, but...
beq.s hack_mem_done ; If this ever happens...
move.l d0,a0 ; Store in address register
movem.l (a3),d0-d7 ; Get the whole thing... (8*4)
move.l a0,a3 ; Get new one...
movem.l d0-d7,(a3) ; Make new header a copy...
move.l (a0)+,a1 ; Get LN_SUCC
move.l a3,LN_PRED(a1) ; Link in the bottom
move.l (a0)+,a1 ; Get LN_PRED
move.l a3,LN_SUCC(a1) ; Link in the top...
bra.s hack_mem ; Loop back for more...
hack_mem_done: PERMIT
*
* Finally, if graphics is V39 or less, do the LastChanceMemory...
*
lea gfxName(pc),a1 ; Get library name...
moveq.l #37,d0 ; Minimum of V37
JSRLIB OpenLibrary ; Open the library...
tst.l d0 ; Did it open?
beq.s no_gfx_hack ; If not, skip it...
move.l d0,a2 ; Store in a2...
move.w LIB_VERSION(a2),d0 ; Get version
sub.w #40,d0 ; Are we over V39?
bcc.s close_gfx ; If so, skip hack...
move.l gb_LastChanceMemory(a2),a0 ; Get semaphore...
JSRLIB ObtainSemaphore ; Get a lock on LCM...
move.l gb_LCMptr(a2),d0 ; Get pointer...
bftst d0{0:20} ; Check if in the 4K
bne.s free_lcm ; If not, free the semaphore
move.l #4096,d0 ; Size of LCM memory...
move.l #MEMF_CHIP,d1 ; Type of memory...
JSRLIB AllocMem ; Allocate it...
move.l d0,d1 ; Did the allocation work?
beq.s free_lcm ; If not, skip...
move.l d0,gb_LCMptr(a2) ; Store new LCM memory...
free_lcm: move.l gb_LastChanceMemory(a2),a0 ; Get semaphore...
JSRLIB ReleaseSemaphore ; Release the lock...
close_gfx: move.l a2,a1 ; Get GfxBase
JSRLIB CloseLibrary ; Close the library
no_gfx_hack: rts
*
* The above is all hacks to make 68040/68060 Enforcer run a bit faster...
*
*******************************************************************************
*
* 68040 MMU On routine...
*
MMU_On_040: bsr Hack_4k ; Do the 4K hack...
*
* Ok, so install the patches for the 68040 cache-vs-DMA issues...
*
* First, we need to get the current cache settings
*
moveq.l #0,d0 ; Clear d0
moveq.l #0,d1 ; Clear d1
JSRLIB CacheControl ; Get the settings
and.l #CACRF_EnableD,d0 ; Mask all but data cache...
move.l d0,Cache_Settings ; Store it...
*
* Next, patch the system based on version of the 68040.library
*
lea Patches_040(pc),a0 ; Get patch table...
move.l Lib68040(pc),d0 ; Get 68040.library base...
beq.s patch_040 ; If no 68040.library, we patch
cmp.w #37,LIB_VERSION(a1) ; Check if pre-V37
bcs.s patch_040 ; Full patch if pre-V37
cmp.w #10,LIB_REVISION(a1) ; Check for V37.10
bcs.s patch_040 ; Full patch if pre-V37.10
*
* If we have V37.10 or better 68040.library, we do simple patches...
* (Beter performance...)
*
lea Patches_030(pc),a0 ; Simple patches...
patch_040: bsr AddPatches ; Do them...
*
***************
*
* Now, set up the data needed to run Enforcer...
*
lea BusError040(pc),a2 ; Get 040 bus error...
On_040_060: ; Common turn-on code...
lea ZeroPage(pc),a0 ; Get address of storage
move.l mmu_Page0(a4),a1 ; Get ZeroPage address
addq.l #3,a1 ; Point at bottom byte
move.l a1,(a0)+ ; Store Page0 address
lea mmu_Indirect(a4),a1 ; Get invalid entry
addq.l #3,a1 ; Point at bottom byte...
move.l a1,(a0)+ ; Store Invalid address
tst.l Quiet_Flag(pc) ; Check if we are quiet
beq.s full_040 ; If not, we stay full...
move.b #$41,(a1) ; Set all pages valid...
full_040: bsr MakeNewVBR ; Set up the new VBR...
lea On_040(pc),a5 ; Get code pointer...
JSRLIB Supervisor ; Do it...
moveq.l #1,d0 ; Return TRUE...
bra MMU_On_RTS
*
* This runs in 68040 supervisor mode. Turns on MMU tables...
*
On_040: lea mmu_TT(a4),a0 ; Get first of table...
or.w #$0700,sr ; Full disable for a while...
movec.l itt0,d0 ; Save the old ITTx
move.l d0,(a0)+ ; Store in table...
movec.l itt1,d0
move.l d0,(a0)+
movec.l dtt0,d0
move.l d0,(a0)+
movec.l dtt1,d0
move.l d0,(a0)+ ; Last one!
*
lea mmu_CRP_OLD(a4),a0 ; For smaller code...
movec.l urp,d0 ; Get old Uroot pointer
move.l d0,(a0)+ ; Store it...
movec.l srp,d0 ; Get old Sroot pointer
move.l d0,(a0)+ ; Store it...
movec.l tc,d0 ; Get TC
move.l d0,(a0)+ ; And sort it...
*
lea NewVBR(pc),a0 ; Get new VBR
movec.l a0,vbr ; Store new VBR...
lea mmu_CRP(a4),a0 ; Get table to load...
moveq.l #0,d0 ; Turn off MMU
movec.l d0,tc ; Ok, so it is off...
pflusha ; Flush the whole ATC!
*
move.l (a0)+,d0 ; Get URP
movec.l d0,urp ; set it...
move.l (a0)+,d0 ; Get SRP
movec.l d0,srp ; set it...
move.l (a0)+,d0 ; Get TC
movec.l d0,tc ; MMU is now set...
*
moveq.l #0,d0 ; Now, turn off all TTx
movec.l d0,itt0 ; so that the MMU is used...
movec.l d0,itt1
movec.l d0,dtt0
movec.l d0,dtt1 ; At this point we should
* ; be running with the MMU
rte
*
************************
*
* 68040 MMU Off routine...
* Check if we can get out... If someone is on top of our SetFucntion, we
* can't... If a Cache operation is running, we can't...
*
MMU_Off_060: ; We can use this on the 68060 too...
MMU_Off_040: tst.l mmu_Frame(pc) ; Check if we are in reboot
beq.s cold_040 ; If so, we don't care!
*
* This is here just to be sure we are not in the middle of a DMA...
*
tst.l Nest_Count(pc) ; Check if we can get out
bne MMU_Error ; Return error is we can't
*
* Now, remove the patches...
*
bsr RemPatches ; Remove the patches
beq MMU_Error ; Return error if not...
*
* Ok, looks like we can get out, so clean up...
*
cold_040: lea Off_040(pc),a5 ; Get MMU off routine
JSRLIB Supervisor ; Do it in supervisor state
moveq.l #1,d0 ; Return TRUE...
bra MMU_Off_RTS ; Done turning MMU off
*
***************
*
Off_040: move.l OldVBR(pc),a0 ; Get old VBR back...
or.w #$0700,sr ; Full disable for a while
movec.l a0,vbr ; Set new VBR...
*
lea mmu_TT(a4),a0 ; Get TT regs
move.l (a0)+,d0 ; Get the first one...
movec.l d0,itt0 ; and set...
move.l (a0)+,d0 ; ...from the table...
movec.l d0,itt1
move.l (a0)+,d0
movec.l d0,dtt0
move.l (a0)+,d0
movec.l d0,dtt1 ; Last one!
*
lea mmu_CRP_OLD(a4),a0 ; Get old settings table...
moveq.l #0,d0 ; Turn off MMU
movec.l d0,tc ; Ok, so it is off...
pflusha ; Flush the whole ATC!
*
move.l (a0)+,d0 ; Get URP
movec.l d0,urp ; set it...
move.l (a0)+,d0 ; Get SRP
movec.l d0,srp ; set it...
move.l (a0)+,d0 ; Get TC
movec.l d0,tc ; MMU is now set...
*
rte ; Back to the living...
*
*******************************************************************************
*
* The 68040 bus error routine...
*
BusError040: ;
; I need a full disable here to keep life running
; in the "order" that I think it needs to run in...
;
or.w #$0700,sr ; Full disable...
;
movem.l d0-d7/a0-a6,-(sp) ; Save the register frame
move.l NewExecBase(pc),a6 ; Get ExecBase
move.w STACK+$0C(sp),d7 ; Get Frame (ssw) information
moveq.l #1,d0 ; We need to set the DFC
movec.l d0,dfc ; and SFC regs to be user
movec.l d0,sfc ; space access...
move.l STACK+$14(sp),d0 ; Get fault address...
bftst d0{0:20} ; Check upper bits...
bne.s b4_RealFault ; A real access fault...
bftst d0{0:22} ; Check for a valid fault...
bne b4_ValidFault ; If above the 1K mark...
cmp.w #$4,d0 ; Check if location 4
bne.s b4_RealFault ; If not location 4, real...
btst.l #8,d7 ; Check for reads...
bne b4_ValidFault ; If read, do it...
*
* So now, we need to get nasty and tell the user! But first check
* if we are in quiet mode and if so, skip the yelling...
*
b4_RealFault: tst.l Quiet_Flag(pc) ; Check if we are quiet
bne b4_Cleanup2 ; Just cleanup, no yelling...
bsr FlashLED ; Flash the power LED
*
************************
*
* Put together the first hit line...
*
* Format: LONG-WRITE to C0EDBABE (INST) data=DDDD0000 PC: 07CD7486
* or: LONG-WRITE to C0EDBABE data=DDDD0000 PC: 07CD7486
* or: LONG-READ from C0EDBABE (INST) PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486 ----BUS ERROR----
*
move.l STACK+$24(sp),a5 ; Get Write data...
move.l STACK+$14(sp),a3 ; Get fault address...
moveq.l #(55-41),d6 ; Spaces needed...
*
lea Extra_Fault(pc),a0 ; Point at extra fault...
clr.b (a0) ; Clear the byte for now...
lea Inst_Fault(pc),a4 ; Get instruction fault...
btst.l #1,d7 ; Check for instruction...
bne.s b4_InstFault ; If bits set, we have fault...
lea Norm_Fault(pc),a4 ; Otherwise, normal
b4_InstFault: lea Read_Fault(pc),a2 ; Get default string...
btst.l #8,d7 ; Check for Write...
bne.s b4_ReadFault ; It was a read...
lea Write_Fault(pc),a2 ; Get write fault string...
move.b #' ',(a0) ; Make Extra_Fault display...
moveq.l #0,d6 ; Number of spaces...
b4_ReadFault: lea Long_Str(pc),a1 ; Get pointer to "LONG"
moveq.l #0,d5 ; Number of extra spaces...
bfextu d7{25:2},d0 ; Get the size...
beq.s b4_GotSize ; If 0, we got the size...
addq.l #6,d5 ; We need 6 more if BYTE...
addq.l #7,a1 ; Point at Byte...
subq.l #1,d0 ; Is 1?
beq.s b4_GotSize ; If so, BYTE is right...
subq.l #2,d5 ; 2 less spaces if WORD...
addq.l #7,a1 ; Point at WORD...
subq.l #1,d0 ; Is it word?
beq.s b4_GotSize ; If so, WORD is right...
subq.l #4,d5 ; Back to LONG size
addq.l #7,a1 ; Point at LINE... (final form)
b4_GotSize: lea Final_Fault(pc),a0 ; Point at final fault...
move.b 5(a1),(a0) ; Get output type...
movem.l a1-a5,-(sp) ; Arguments...
move.l sp,a1 ; Get pointer to args...
bsr Main_Disp ; Display it...
lea 5*4(sp),sp ; Restore stack...
*
* Now for the formatted PC
*
tst.l d6 ; Is d6 0?
bne.s b4_DoPC ; If not, do spaces...
move.l d5,d6 ; Get new d6...
b4_DoPC: lea PC_Fault(pc),a0 ; Get fault...
sub.l d6,a0 ; Number of spaces...
move.l STACK+$02(sp),a1 ; Get PC...
pea NULL_String(pc) ; Assume non-physical hit...
btst.l #10,d7 ; Check if ATC hit...
bne.s b4_NotPhy ; Non-physical, so skip...
addq.l #4,sp ; Restore stack
pea Physical(pc) ; Push the physical string...
b4_NotPhy: move.l a1,-(sp) ; Move PC onto argument list...
bsr PrintItSP ; Print it...
addq.l #8,sp ; Restore stack...
*
************************
*
tst.l Tiny_Flag(pc) ; Check if TINY
bne.s b4_Cleanup ; If TINY, skip rest...
*
************************
*
* Now do the second line...
*
move.w STACK+$00(sp),d0 ; Get SR
bsr USP_Disp ; Display it (SSW in d7)
*
* Do register display as needed...
*
lea ptest_040(pc),a5 ; Get PTEST routine address
move.l STACK+$02(sp),d4 ; PC address...
move.l sp,a4 ; Point at register frame
bsr RegDisp ; Display registers/stack
*
* Do PC display as needed...
*
move.l d4,a0 ; Get PC into a0
bsr PC_Disp ; a5 is already PTEST
*
* Do Name & HUNK display as needed...
*
move.l d4,a0 ; Get PC into a0
move.w STACK+$00(sp),d4 ; Get SR into d4
bsr NameDisp ; a5 is already PTEST
*
* Ok, now do some magic to check if MOVEM to invalid address...
*
b4_Cleanup: btst.l #8,d7 ; Check if write
bne.s b4_Cleanup2 ; If a read fault, skip
bclr.l #12,d7 ; Check if MOVEM (and clear)
beq.s b4_Cleanup2 ; If not, skip
*
* Check if the EA of this MOVEM is the fault address...
* If it is not, the hit was not from this MOVEM...
*
move.l STACK+$08(sp),a0 ; Get EA
cmp.l STACK+$14(sp),a0 ; Check for a match...
bne.s b4_Cleanup2 ; If not, we don't skip...
*
* What we will do now is to advance the PC stored on the stack by the
* size of the MOVEM instruction given...
*
move.l STACK+$02(sp),a0 ; Get PC...
move.w (a0)+,d1 ; Get the instruction...
move.w #$FF80,d0 ; Mask for MOVEM
and.w d1,d0 ; ...instruction word...
cmp.w #$4880,d0 ; Check if it is MOVEM...
bne.s b4_Cleanup2 ; Not MOVEM, we are lost...
*
* Ok, now decode the instruction format and size...
*
addq.l #2,a0 ; bump past the register list
bfextu d1{26:3},d0 ; Get mode mask...
cmp.w #5,d0 ; Check is less than mode 5
bcs.s b4_SetPC ; If so, a0 is new PC...
beq.s b4_SetPC2 ; If mode 5, a0+2 is new PC...
cmp.w #7,d0 ; Check if mode 7
bne.s b4_Check6 ; If so, we need to check SIZE
*
and.w #7,d1 ; Mask the register
beq.s b4_SetPC2 ; If 0, .W abs address
bra.s b4_SetPC4 ; If 1, .L abs address
*
* Now check mode 6 address...
*
b4_Check6: move.w (a0)+,d1 ; Get extention word...
btst.l #8,d1 ; Check for brief format...
beq.s b4_SetPC ; If brief, A0 is now PC
bfextu d1{30:2},d0 ; Get the lower bits of IS
beq.s b4_NoIS ; If 0, no IS
subq.l #1,d0 ; Check if 1
beq.s b4_NoIS ; If 1, NULL IS
addq.l #2,a0 ; At least 1 word of IS
subq.l #1,d0 ; Check if 2
beq.s b4_NoIS ; If 2, we are done with IS
addq.l #2,a0 ; IS was 3, so 2 words of IS
b4_NoIS: bfextu d1{26:2},d0 ; Get BD
beq.s b4_SetPC ; if 0, no BD
subq.l #1,d0 ; if 1...
beq.s b4_SetPC ; ...NULL BD
subq.l #1,d0 ; if 2...
beq.s b4_SetPC2 ; ...1 word of BD
; Fall through with 2 words of BD...
*
b4_SetPC4: addq.l #2,a0 ; Adds 4 to PC
b4_SetPC2: addq.l #2,a0 ; Adds 2 to PC
b4_SetPC: move.l a0,STACK+$02(sp) ; Store new PC
bclr.b #12-8,STACK+$0C(sp) ; Clear MOVEM bit...
*
; Tell this bad task to take a quantum off. Tasks go out on
; interrupts, so we need to fake one.
b4_Cleanup2: or.w #SF_SAR!SF_TQE,SysFlags(a6) ; fake scheduling
move.w #INTF_SETCLR!INTF_SOFTINT,_intreq ; softint
*
************************
*
* (We have to check for the write state)
* What we will do is write the data out ourselves...
*
b4_ValidFault: btst.l #8,d7 ; Check for reads...
bne b4_ReadCleanup ; If read, cleanup different
btst.l #12,d7 ; Check for valid MOVEM write
bne.s b4_ValidMoveM ; If so, do that code...
*
b4_CheckWB: move.l ZeroPage(pc),a0 ; Point at ZeroPage descriptor
pflushan ; Flush ATC
move.b (a0),d3 ; Get old status...
move.b #$59,(a0) ; Resident and used
cpushl dc,(a0) ; Push the data back out...
* Do Writeback2...
move.b STACK+$10+1(sp),d1 ; Get Writeback status...
bpl.s b4_SkipWB2 ; If empty, skip it...
bftst d1{27:2} ; Check type
bne.s b4_SkipWB2 ; Not the real one...
move.l STACK+$20(sp),a0 ; Get WB2 address
move.l STACK+$24(sp),a1 ; Get WB2 data
bsr b4_WriteBack ; Do the writeback...
b4_SkipWB2:
* Do WriteBack3...
move.b STACK+$0E+1(sp),d1 ; Get Writeback status...
bpl.s b4_SkipWB3 ; If no WB, skip it...
move.l STACK+$18(sp),a0 ; Get WB3 address
move.l STACK+$1C(sp),a1 ; Get WB3 data
bsr b4_WriteBack ; Do the writeback...
b4_SkipWB3:
*
* Now, turn the page back to invalid...
*
move.l ZeroPage(pc),a0 ; Get page...
pflushan ; Flush ATC
move.b d3,(a0) ; Set to what it should be...
cpushl dc,(a0) ; Flush change to RAM...
*
* Now, check if we had a pending TRACE
*
b4_Exit: movem.l (sp)+,d0-d7/a0-a6 ; Restore...
cmp.l #Trace040,NewTrace(pc) ; Check if our trace
bne.s b4_NoTrace ; If not, leave it alone...
bftst OldSR(pc){0:2} ; Check in any old trace...
bne.s b4_NoTrace ; if so, don't cancel trace...
*
bclr.b #13-8,$0C(sp) ; Check and clear CT
bne.s Trace040 ; If CT, do trace now...
b4_NoTrace: btst.b #1,$0C+1(sp) ; Check for Instruction error
bne.s b4_NonData ; If so, double check it...
b4_RTE: rte
*
************************
*
* This is really sick: We have a MOVEM that is writing to the low memory
* that happens to be below the 4K area but is above the 1K area...
* This is when things can get dangerous since we need to let the memory
* become writeable for a moment and turn on tracing and hope that nothing
* dies between now and the trace exception...
*
b4_ValidMoveM: move.l ZeroPage(pc),a0 ; Point at ZeroPage descriptor
pflushan ; Clear MMU non-globals...
move.b #$59,(a0) ; Resident, used, modified
cpushl dc,(a0) ; Flush to RAM
bra.s b4_SetTrace ; Set the trace mode and exit
*
************************
*
* Not a data fault? Too bad, we'll have to crash you. The illegal
* instruction vector points to the same place as bus error used to.
* But first, make sure it is a real bad instruction!
*
* Arg!!! If the instruction error was in the bottom 4K we need to
* deal with it...
*
b4_NonData: cmp.l #$1000,(sp) ; Check if in lower 4K
bcc.s b4_RTE ; If in the 4K, ok...
move.l NewIllegal(pc),-(sp) ; Put on illegal vector
rts ; Jump to it...
*
************************
*
* The 68040 trace mode routine... (for the bus error stuff)
*
Trace040: move.w sr,-(sp) ; Save our SR...
;
; I need a full disable here to keep life running
; in the "order" that I think it needs to run in...
;
or.w #$0700,sr ; Full disable...
;
move.l OldTrace(pc),NewTrace ; Restore old trace code
move.l a0,-(sp) ; Save a0
move.l ZeroPage(pc),a0 ; Point at ZeroPage descriptor
pflushan ; Clear MMU non-globals...
move.b #$4C,(a0) ; Used, writeprotect, invalid
cpushl dc,(a0) ; Push this line...
move.l InvalidPage(pc),a0 ; Get invalid page descriptor
move.b #$4C,(a0) ; Used, writeprotect, invalid
cpushl dc,(a0) ; Push this line...
*
* Ok, now restore the SR from before we started...
*
move.l d0,a0 ; Save d0 in a0...
move.w 6(sp),d0 ; Get SR...
and.w #$00FF,d0 ; Mask status...
or.w OldSR(pc),d0 ; Put back old trace settings
move.w d0,6(sp) ; Save SR...
move.l a0,d0 ; Restore d0
move.l (sp)+,a0 ; Restore a0
move.w (sp)+,sr ; Restore sr
*
bftst (sp){0:2} ; Check if any trace mode...
bne.s t4_DoIt ; Do it if so...
rte
*
* This is done if the old mode was a trace...
*
t4_DoIt: move.l OldTrace(pc),-(sp) ; Get trace function...
rts ; Do it...
*
************************
*
* Ok, so now we have this read fault...
* We need it to trace...
*
b4_ReadCleanup: moveq.l #$4D,d0 ; Get status byte ready...
pflushan ; Clear MMU non-globals...
move.l ZeroPage(pc),a0 ; Point at ZeroPage descriptor
move.b d0,(a0) ; Used, writeprotect, valid
cpushl dc,(a0) ; Push this line...
move.l InvalidPage(pc),a0 ; Get invalid page descriptor
move.b d0,(a0) ; Used, writeprotect, valid
cpushl dc,(a0) ; Push this line...
b4_SetTrace:
*
* Now, store current interrupt state and turn interrupts off...
*
move.w STACK+$00(sp),d0 ; Get old mode
move.w d0,d1 ; Save it...
and.w #$FF00,d0 ; Mask it...
move.w d0,OldSR ; Save it...
and.w #$3FFF,d1 ; Mask off trace bits
or.w #$8700,d1 ; Set T1 trace and disable...
move.w d1,STACK+$00(sp) ; Set new SR
*
lea Trace040(pc),a1 ; Get new trace handler
lea NewTrace(pc),a0 ; Get where to put it...
move.l (a0),OldTrace ; Store away the old trace vec
move.l a1,(a0) ; Store ours...
cpushl dc,(a0) ; Push the data back out...
tst.b STACK+$0E+1(sp) ; Get Writeback status...
bmi b4_CheckWB ; If writeback, do them...
bra b4_Exit ; Else, exit...
*
************************
*
* Table of routines for the various sizes of WriteBacks...
* d1=writeback status, a0=address, a1=data
*
b4_WriteBack: cmp.l #$400,a0 ; Check if in lower 1K
bcs.s b4_wd_RTS ; Don't write lower 1K
bsr.s ptest_040 ; Check it...
bne.s b4_wd_RTS ; If error, exit...
btst.l #2,d0 ; Check if writeprotected
bne.s b4_wd_RTS ; If so, exit...
bfextu d1{25:2},d0 ; Get size field...
add.l d0,d0 ; *2
move.l a1,d1 ; Store data in d1...
move.l a0,a1 ; Store address in a1...
jmp b4_wb_Size(pc,d0) ; Do the right one...
*
b4_wb_Size: bra.s b4_wd_Long
bra.s b4_wd_Byte
bra.s b4_wd_Word
b4_wd_RTS: rts ; We don't do lines...
*
b4_wd_Byte: move.b d1,(a1) ; Write back byte
rts ; Return
*
b4_wd_Word: addq.l #1,a0 ; Check other side
bsr.s ptest_040 ; Do PTEST
bne.s b4_wd_RTS ; If no good, error!
move.w d1,(a1) ; Write back word
rts ; Return
*
b4_wd_Long: addq.l #3,a0 ; We will need to check here...
bsr.s ptest_040 ; Do PTEST
bne.s b4_wd_RTS ; If no good, error!
move.l d1,(a1) ; Write back long
rts ; Return
*
*******************************************************************************
*
* The following routine will do a ptest on (a0) and return
* the status in the flags. (bne invalid on rts)
* Trashes d0... (Actually, sets d0 to MMUSR, used in b4_WriteBack)
*
ptest_040: ptestw (a0) ; Check the address for valid...
movec.l MMUSR,d0 ; Get the result...
bchg.l #0,d0 ; Flip the sense...
btst.l #0,d0 ; Check it...
ptest_bad: rts ; Return flags
*
*******************************************************************************
*
* Used to tell if we have a 68040...
* We store the ROM address in ROM_Addr...
*
Test_MMU_040: moveq.l #0,d0 ; Assume no MMU...
or.w #$0700,sr ; Full disable for now...
movec.l tc,d1 ; Get MMU setting...
tst.w d1 ; Is it on?
bmi.s Test_MMU_On ; If so, we must have one...
bset.l #15,d1 ; Set high bit (turn on)
movec.l d1,tc ; Turn on MMU...
movec.l tc,d1 ; Get MMU on status...
movec.l d0,tc ; Clear MMU status...
tst.w d1 ; Check if it worked...
bpl.s Test_040_RTS ; If not, no MMU...
*
Test_MMU_On: moveq.l #1,d0 ; We need to set the DFC
movec.l d0,dfc ; and SFC regs to be user
movec.l d0,sfc ; space access...
move.l ROM_Addr(pc),a0 ; Get address of the ROM...
ptestr (a0) ; Check if we can read...
movec.l mmusr,d0 ; Get address...
btst.l #1,d0 ; See if ITT hit...
bne.s Test_040_Done ; Default ROM address...
and.w #$F000,d0 ; Mask it...
lea ROM_Addr(pc),a0 ; Get ROM address...
move.l d0,(a0) ; Store it...
Test_040_Done: moveq.l #2,d0 ; Set to 040 type...
Test_040_RTS: rte
*
*******************************************************************************
*******************************************************************************
*******************************************************************************
*
* Now, for some of the 68060 specific routines...
*
*******************************************************************************
*
* Used to tell if we have a 68060...
* We store the ROM address in ROM_Addr...
*
Test_MMU_060: moveq.l #0,d0 ; Assume no MMU...
or.w #$0700,sr ; Full disable for now...
movec.l tc,d1 ; Get MMU setting...
tst.w d1 ; Is it on?
bmi.s Test_060_On ; If so, we must have one...
bset.l #15,d1 ; Set high bit (turn on)
movec.l d1,tc ; Turn on MMU...
movec.l tc,d1 ; Get MMU on status...
movec.l d0,tc ; Clear MMU status...
tst.w d1 ; Check if it worked...
bpl.s Test_060_RTS ; If not, no MMU...
*
Test_060_On: move.l ROM_Addr(pc),a0 ; Get address of the ROM...
bsr.s ptest_060 ; Check the MMU...
and.w #$F000,d0 ; Mask table entry...
lea ROM_Addr(pc),a0 ; Get ROM address...
move.l d0,(a0) ; Store it...
moveq.l #3,d0 ; Set to 060 type...
Test_060_RTS: rte
*
*******************************************************************************
*
* 68060 MMU turn on routine...
*
MMU_On_060: bsr Hack_4k ; Do the page-0 hacks...
*
* First, we need to get the current cache settings
*
moveq.l #0,d0 ; Clear d0
moveq.l #0,d1 ; Clear d1
JSRLIB CacheControl ; Get the settings
and.l #CACRF_EnableD,d0 ; Mask all but data cache...
move.l d0,Cache_Settings ; Store it...
*
* Next, patch the system - assume a good 68060.library
*
lea Patches_030(pc),a0 ; Simple patches...
bsr AddPatches ; Do them...
*
* We will be using much of the 68040 MMU turn-on code...
*
lea BusError060(pc),a2 ; Get 060 bus error...
bra On_040_060 ; Common code...
*
*******************************************************************************
*
* The following routine will do a ptest on (a0) and return
* the status in the flags. (bne invalid on rts)
* Trashes d0... (Much like ptest... but d0 returns the actual page dst)
*
ptest_060: movem.l d1/a0,-(sp) ; Save some scratch...
move.l a0,d1 ; The input address into d1
movec.l urp,a0 ; Get ROOT pointer...
bfextu d1{0:7},d0 ; Get the root index...
add.l d0,d0 ; *2
add.l d0,d0 ; *4
add.l d0,a0 ; Add to root pointer...
move.l (a0),d0 ; Get page entry
and.w #$FE00,d0 ; Mask into the page table
move.l d0,a0 ; Store pointer...
bfextu d1{7:7},d0 ; Get the pointer index...
add.l d0,d0 ; *2
add.l d0,d0 ; *4
add.l d0,a0 ; Add to table pointer...
move.l (a0),d0 ; Get page entry...
and.w #$FF00,d0 ; Mask to the pointer...
move.l d0,a0 ; Put into address register...
bfextu d1{14:6},d0 ; Get index into page table
add.l d0,d0 ; *2
add.l d0,d0 ; *4
add.l d0,a0 ; a0 now points at the page...
move.l (a0),d0 ; Get page entry...
btst.l #0,d0 ; Check if bit 0 is set...
bne.s ptst6_ok ; If set, we are valid...
bclr.l #1,d0 ; Check if indirect...
beq.s ptst6_ok ; If not indirect, A0 is valid
move.l d0,a0 ; a0 is now the page entry...
move.l (a0),d0 ; Get the real page...
*
* Ok, now d1 contains the real page entry for the address. Lets get the
* right result into d0...
ptst6_ok: movem.l (sp)+,d1/a0 ; Restore our registers
bchg.l #0,d0 ; Flip the sence of valid...
btst.l #0,d0 ; Test it...
rts ; return with flags set...
*
*******************************************************************************
*
* The 68040 bus error routine...
*
BusError060: ;
; I need a full disable here to keep life running
; in the "order" that I think it needs to run in...
;
or.w #$0700,sr ; Full disable...
;
movem.l d0-d7/a0-a6,-(sp) ; Save the register frame
move.l NewExecBase(pc),a6 ; Get ExecBase
move.l STACK+$0C(sp),d7 ; Get FSLW information
;
; While branch cache checking/flushing is called for,
; it is not needed on the Amiga since we do not do FTRAP
; branch replacement... We don't care about BPE
;
moveq.l #1,d0 ; We need to set the DFC
movec.l d0,dfc ; and SFC regs to be user
movec.l d0,sfc ; space access...
move.l STACK+$08(sp),d0 ; Get fault address...
bftst d0{0:20} ; Check upper bits...
bne.s b6_RealFault ; A real access fault...
bftst d0{0:22} ; Check for a valid fault...
bne b6_ValidFault ; If above the 1K mark...
cmp.w #$4,d0 ; Check if location 4
bne.s b6_RealFault ; If not location 4, real...
btst.l #24,d7 ; Check for reads...
beq.s b6_RealFault ; If not 10 or 11, fault...
btst.l #23,d7 ; Check for read-mod-write
beq b6_ValidFault ; If read, do it...
*
* So now, we need to get nasty and tell the user! But first check
* if we are in quiet mode and if so, skip the yelling...
*
b6_RealFault: tst.l Quiet_Flag(pc) ; Check if we are quiet
bne b6_Cleanup ; Just cleanup, no yelling...
bsr FlashLED ; Flash the power LED
*
************************
*
* Put together the first hit line...
*
* Format: LONG-WRITE to C0EDBABE (INST) PC: 07CD7486
* or: LONG-WRITE to C0EDBABE PC: 07CD7486
* or: LONG-READ from C0EDBABE (INST) PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486
* or: LONG-READ from C0EDBABE PC: 07CD7486 ----BUS ERROR----
*
move.l STACK+$08(sp),a3 ; Get fault address...
moveq.l #(55-41),d6 ; Spaces needed...
*
lea Extra_Fault(pc),a0 ; Point at extra fault...
clr.b (a0) ; Clear the byte for now...
lea Inst_Fault(pc),a4 ; Get instruction fault...
btst.l #17,d7 ; Check for instruction...
bne.s b6_InstFault ; If bits set, we have fault...
lea Norm_Fault(pc),a4 ; Otherwise, normal
b6_InstFault: lea Read_Fault(pc),a2 ; Get default string...
btst.l #24,d7 ; Check for Write...
bne.s b6_ReadFault ; It was a read...
lea Write_Fault(pc),a2 ; Get write fault string...
b6_ReadFault: lea Long_Str(pc),a1 ; Get pointer to "LONG"
bfextu d7{9:2},d0 ; Get the size...
add.l d0,a1 ; (add 1x)
add.l d0,d0 ; size*2
add.l d0,a1 ; (add 2x = total of 3x)
add.l d0,d0 ; size*4
add.l d0,a1 ; (add 4x = total of 7x)
movem.l a1-a4,-(sp) ; Arguments...
move.l sp,a1 ; Get pointer to args...
bsr Main_Disp ; Display it...
lea 4*4(sp),sp ; Restore stack...
*
* Now for the formatted PC
*
lea PC_Fault(pc),a0 ; Get fault...
sub.l d6,a0 ; Number of spaces...
move.l STACK+$02(sp),a1 ; Get PC...
pea NULL_String(pc) ; Assume non-physical hit...
bftst d7{26:2} ; Check if physical bus error
beq.s b6_NotPhy ; Non-physical, so skip...
addq.l #4,sp ; Restore stack
pea Physical(pc) ; Push the physical string...
b6_NotPhy: move.l a1,-(sp) ; Move PC onto argument list...
bsr PrintItSP ; Print it...
addq.l #8,sp ; Restore stack...
*
************************
*
tst.l Tiny_Flag(pc) ; Check if TINY
bne.s b6_Cleanup ; If TINY, skip rest...
*
************************
*
* Now do the second line...
*
move.w STACK+$00(sp),d0 ; Get SR
swap d7 ; Put the common part in low...
bsr USP_Disp ; Display it (SSW in d7)
swap d7 ; put it back...
*
* Do register display as needed...
*
lea ptest_060(pc),a5 ; Get PTEST routine address
move.l STACK+$02(sp),d4 ; PC address...
move.l sp,a4 ; Point at register frame
bsr RegDisp ; Display registers/stack
*
* Do PC display as needed...
*
move.l d4,a0 ; Get PC into a0
bsr PC_Disp ; a5 is already PTEST
*
* Do Name & HUNK display as needed...
*
move.l d4,a0 ; Get PC into a0
move.w STACK+$00(sp),d4 ; Get SR into d4
bsr NameDisp ; a5 is already PTEST
*
* Now, we need to do some magic since, well, things are about to go
* really bad... The 68060 is a full-restart CPU. This means that
* even writes are restarted. This means that I have to either do
* all of the instruction emulation myself (so I can move the PC)
* or I have to let the write continue. What I do here is to let
* the write continue into a "harmless" spot and then restore things
* as best as possible. How we do this is as follows:
*
* The MMU tables are updated to make them valid. We find the
* spot where the invalid operation was about to happen and
* we change the MMU to point at our nice 4K of space that
* was set up for invalid MMU activity. We then set up trace mode
* and let the system step the next instruction. In the trace
* vector, we restore things to original and restore the trace
* mode and vector (and let the original trace vector have it
* if needed...) We then also tell the task to take a leap
* from EXEC's point of view so that it will switch out if
* at all possible.
*
b6_Cleanup: ; First, for later use, we will get the "to be replaced"
; descripter into d2...
move.l InvalidPage(pc),a0 ; Get invalid page
move.l -3(a0),d2 ; Save it here for later...
;
; Now, find the location where we will place this thing...
move.l STACK+$08(sp),d1 ; Get fault address...
movec.l urp,a0 ; Get ROOT pointer...
bfextu d1{0:7},d0 ; Get the root index...
add.l d0,d0 ; *2
add.l d0,d0 ; *4
add.l d0,a0 ; Add to root pointer...
move.l (a0),d0 ; Get page entry
and.w #$FE00,d0 ; Mask into the page table
move.l d0,a0 ; Store pointer...
bfextu d1{7:7},d0 ; Get the pointer index...
add.l d0,d0 ; *2
add.l d0,d0 ; *4
add.l d0,a0 ; Add to table pointer...
move.l (a0),d0 ; Get page entry...
and.w #$FF00,d0 ; Mask to the pointer...
move.l d0,a0 ; Put into address register...
bfextu d1{14:6},d0 ; Get index into page table
add.l d0,d0 ; *2
add.l d0,d0 ; *4
add.l d0,a0 ; a0 now points at the page...
move.l (a0),d0 ; Get page entry...
btst.l #0,d0 ; Check if bit 0 is set...
bne.s b6_Cleanup0 ; If set, we are valid...
bclr.l #1,d0 ; Check if indirect...
beq.s b6_Cleanup0 ; If not indirect, A0 is valid
move.l d0,a0 ; a0 is now the page entry...
bra.s b6_Cleanup0 ; Continue...
*
************************
*
* Here we have a valid access. We will just change the Page-0 table to
* valid and let the trace thing happen...
*
b6_ValidFault: move.l ZeroPage(pc),a0 ; Get the zero-page
lea -3(a0),a0 ; Make it a real address...
move.l (a0),d2 ; Get the "replacement"
; We put this here so that page 0 reads of $4 (EXECBASE)
; will be as fast as possible...
*
b6_Cleanup0: ; At this point a0 has the pointer to the page descripter
; d2 has the basic descripter that will replace it, but
; still unmodified...
move.b #$49,d2 ; Valid, R/W, etc.
;
; Now, save the old...
lea OldPageAddress(pc),a1 ; We need to store it...
move.l a0,(a1) ; Keep it safe...
lea OldPage(pc),a1 ; Get the old page...
move.l (a0),(a1) ; Store the actual page...
;
; Get ready to make the new one...
pflushan ; Clear MMU local ATC only...
; We have a trick that lets me do that... Much faster code...
;
move.l d2,(a0) ; Change entry
cpushl dc,(a0) ; Push the cache to RAM
;
; Finally, we set up the trace as needed...
lea Trace060(pc),a1 ; Get our trace vector
lea NewTrace(pc),a0 ; Where to put it...
move.l (a0),OldTrace ; Save the old trace...
move.l a1,(a0) ; Set up the new trace...
;
; Now, set the trace mode...
move.w STACK+$00(sp),d0 ; Get SR
move.w d0,d1 ; Save it a bit...
and.w #$FF00,d0 ; Mask it...
move.w d0,OldSR ; Save it...
and.w #$3FFF,d1 ; Mask off trace bits...
or.w #$8700,d1 ; Set T1 and level-7 disable
move.w d1,STACK+$00(sp) ; Put it on the frame...
;
; Ok, now return and let the system trace the bad instruction
movem.l (sp)+,d0-d7/a0-a6 ; Restore...
rte ; and exit...
*
************************
*
* The 68060 trace mode routine... (for the bus error stuff)
*
Trace060: move.w sr,-(sp) ; Save our SR...
;
; I need a full disable here to keep life running
; in the "order" that I think it needs to run in...
;
or.w #$0700,sr ; Full disable...
;
move.l OldTrace(pc),NewTrace ; Restore old trace code
move.l a0,-(sp) ; Save a0
move.l OldPageAddress(pc),a0 ; Point at Page descriptor
pflushan ; Clear MMU non-globals...
move.l OldPage(pc),(a0) ; Restore old Page...
cpushl dc,(a0) ; Push this line...
*
* Ok, now restore the SR from before we started...
*
move.l d0,a0 ; Save d0 in a0...
move.w 6(sp),d0 ; Get SR...
and.w #$00FF,d0 ; Mask status...
or.w OldSR(pc),d0 ; Put back old trace settings
move.w d0,6(sp) ; Save SR...
move.l a0,d0 ; Restore d0
;
; Now, ping EXEC to wake up and switch this guy out...
move.l NewExecBase(pc),a0 ; Get execbase...
or.w #SF_SAR!SF_TQE,SysFlags(a0) ; fake scheduling
move.w #INTF_SETCLR!INTF_SOFTINT,_intreq ; softint
;
move.l (sp)+,a0 ; Restore a0
;
; Check if we were already in trace mode... If so, continue...
move.w (sp)+,sr ; Restore back to what was...
btst.b #7,(sp) ; Check if any trace mode...
bne.s t6_DoIt ; Do it if so...
rte
*
* This is done if the old mode was a trace...
*
t6_DoIt: move.l NewTrace(pc),-(sp) ; Get trace function...
rts ; Do it...
*
*******************************************************************************
*******************************************************************************
*******************************************************************************
*
* NewPreDMA - This routine is the new CachePreDMA code needed to
* control the 68040 cache-vs-DMA issues.
* The problem is that we need to turn off the data
* cache while DMA access to non-quadlong memory
* is happening.
*
OldPreDMA: dc.l 0 ; Storage for old function
dc.w _LVOCachePreDMA ; LVO this patches...
NewPreDMA: btst.l #DMAB_Continue,d0 ; Check if we are continue mode
bne.s ncp_Continue ; Skip the Continue case...
move.l a0,d1 ; Get address...
or.l (a1),d1 ; or in length...
and.b #$0F,d1 ; Check of non-alignment
beq.s ncp_Continue ; Don't count if aligned
lea Nest_Count(pc),a1 ; Get a1...
addq.l #1,(a1) ; Nest this...
ncp_Continue: move.l a0,d0 ; Get result...
dma_Caches: move.l d0,-(sp) ; Save result...
move.l #0,d0 ; Clear bits
moveq.l #0,d1 ; Clear mask
bsr.s NewControl ; Do the cache setting/clear
move.l (sp)+,d0 ; Restore d0
rts ; Return...
*
*******************************************************************************
*
OldPostDMA: dc.l 0 ; Storage for old function
dc.w _LVOCachePostDMA ; LVO this patches...
NewPostDMA: move.l a0,d1 ; Get address...
or.l (a1),d1 ; or in length...
and.b #$0F,d1 ; Check for non-aligned...
beq.s dma_Caches ; Don't count if aligned...
lea Nest_Count(pc),a1 ; We trash a1...
subq.l #1,(a1) ; Subtract the nest count...
bra.s dma_Caches ; Do the DMA work...
*
*******************************************************************************
*
* NewControl - This routine is the new front-end to CacheControl which
* knows about the above Pre/PostDMA kludge-fix.
*
OldControl: dc.l 0 ; Storage for old function
dc.w _LVOCacheControl ; LVO this patches...
NewControl: FORBID ; We need to forbid first...
move.l Cache_Settings(pc),-(sp) ; Save old "fake" setting...
btst.l #CACRB_EnableD,d1 ; Check if we are setting this
beq.s nc_NoChange ; If not, no change...
move.l d0,a0 ; Save in a0
and.l #CACRF_EnableD,d0 ; Mask it...
move.l d0,Cache_Settings ; Store it
move.l a0,d0 ; Restore d0
nc_NoChange: bset.l #CACRB_EnableD,d1 ; Set this bit (we will change)
bset.l #CACRB_EnableD,d0 ; Enable it...
tst.l Cache_Settings(pc) ; Check if not
beq.s nc_NoDataCache ; If not set, clear it
tst.l Nest_Count(pc) ; Check if really set...
beq.s nc_Skip1 ; If not nested, skip...
nc_NoDataCache: bclr.l #CACRB_EnableD,d0 ; Clear it...
nc_Skip1: move.l OldControl(pc),a0 ; Get old function
jsr (a0) ; Do it...
or.l (sp)+,d0 ; Or in "fake" answer
PERMIT ; Undo that...
rts
*
*******************************************************************************
*
* NewReboot - This routine is the new front-end to ColdReboot that lets
* the function work even when Enforcer is on.
*
OldReboot: dc.l 0 ; Storage for old function
dc.w _LVOColdReboot ; LVO this patches...
NewReboot: move.l OldReboot(pc),-(sp) ; Where we go to...
bsr.s quickOff ; Turn MMU off quickly...
bset.b #7,$00DE0002 ; Mark as fake cold boot...
rts ; Reboot!
*
quickOff: move.l mmu_Frame(pc),a0 ; Get Frame...
clr.l mmu_Frame ; Signal for Reboot...
bra _MMU_Off ; Turn MMU off
*
*******************************************************************************
*
* NewAlert - This routine is the new front-end to Alert that outputs the
* alert (and registers/etc) insted of just causing Enforcer hits
*
OldAlert: dc.l 0 ; Storage for old function
dc.w _LVOAlert ; LVO this patches...
NewAlert: tst.l Quiet_Flag(pc) ; Check if we are quiet
bne.s na_Alert ; If so, do normal alert.
movem.l a0-a6/d0-d7,-(sp) ; Save these
move.l NewExecBase(pc),a6 ; Get ExecBase
*
* Check if we should display a date/time stamp
*
tst.l DoDateStamp(pc) ; Do we do it?
beq.s na_NoDate ; If not, skip...
pea TimeStr(pc) ; Point at time string
pea DateStr(pc) ; Point at date string
lea DateTime(pc),a0 ; Get Format string...
bsr PrintItSP ; Print it...
addq.l #8,sp ; Clean up stack...
*
* Now, display the alert
*
na_NoDate: pea (8+7)*4(sp) ; Give stack address...
move.l ThisTask(a6),-(sp) ; Get task
move.l d7,-(sp) ; Get Alert number
lea PrintAlert(pc),a0 ; Get alert stuff
bsr PrintItSP ; Output
addq.l #8,sp ; Clean up stack
*
* Automatically did the data registers already...
*
* Show the address registers
*
lea AddrRegs(pc),a0 ; Get format line
lea 9*4(sp),a1 ; Get data...
bsr PrintIt ; Output it...
*
* Show a bit of stack...
*
lea StackLine(pc),a0 ; Get line header...
bsr PrintIt ; Display header...
lea DataLine(pc),a0 ; Get format line
move.l (sp)+,a1 ; Get return stack
move.l a1,a5 ; Store this...
bsr PrintIt ; Display 1 line of stack
move.l #7,d0 ; Number of lines to test
na_Stack: move.l (a5)+,d3 ; For segtracker...
bsr SegTrack ; Check it...
dbra.s d0,na_Stack ; Do all of the 1 stack line
*
* Cause SoftInt for local output of alerts to work instantly...
*
move.w #INTF_SETCLR!INTF_SOFTINT,_intreq ; softint
*
* Ok, restore and get out...
*
movem.l (sp)+,a0-a6/d0-d7 ; Restore
moveq.l #1,d0 ; Set true result
tst.l d7 ; Check high bit
bpl.s na_Done ; If not negative, we are done
*
* Ok, so now we are a dead-end alert...
*
bsr quickOff ; Turn off MMU frame
na_Alert: move.l OldAlert(pc),-(sp) ; Put in the real address
na_Done: rts ; Get out of here...
*
*******************************************************************************
*
* AddPatches will add the patches to the system. Called with
* a pointer to the patch table in a0
*
AddPatches: move.l a0,Installed_Patches ; Save patches we install
move.l a2,-(sp) ; Save a2
move.l a0,a2 ; Into a2
ap_Loop: move.l (a2),d0 ; Get patch
beq.s ap_Done ; If NULL, patch adding is done...
move.l d0,a1 ; Get address...
move.w 4(a1),a0 ; Get LVO...
addq.l #6,d0 ; Offset to real routine...
move.l a6,a1 ; Get ExecBase...
JSRLIB SetFunction ; patch it...
move.l (a2)+,a0 ; Get storage and point at next
move.l d0,(a0) ; Store old function address...
bra.s ap_Loop ; Loop for more...
ap_Done: move.l (sp)+,a2 ; Restore a2
rts
*
Installed_Patches: dc.l 0
*
* RemPatches will check for and remove any patches that may be installed
* but only if all checks out. Returns d0/flags...
*
RemPatches: move.l a2,-(sp) ; Save a2
move.l Installed_Patches(pc),a0
move.l a0,a2 ; Get into a2
rp_Loop: move.l (a0)+,d0 ; Get patch address
beq.s rp_DoRemove ; If end of list, go and remove them
move.l d0,a1 ; Get patch data...
move.w 4(a1),d1 ; Get offset...
addq.l #6,d0 ; Point at function...
cmp.l 2(a6,d1.w),d0 ; Check if match LVO...
beq.s rp_Loop ; If ok, check next...
moveq.l #0,d0 ; Return error...
rp_Exit: move.l (sp)+,a2 ; Restore a2
tst.l d0 ; Set flags...
rts
rp_DoRemove: move.l (a2)+,d0 ; Get patch
beq.s rp_Done ; If NULL, patch adding is done...
move.l d0,a1 ; Get address...
move.l (a1)+,d0 ; Get old function
move.w (a1),a0 ; Get LVO...
move.l a6,a1 ; Get ExecBase...
JSRLIB SetFunction ; patch it...
bra.s rp_DoRemove ; Loop for more...
rp_Done: moveq.l #1,d0 ; Set flag saying all worked...
bra.s rp_Exit ; And we are done...
*
* The patch tables... Note that the 68040 patches include the 68030 patches
* under the current design...
*
Patches_040: dc.l OldControl ; 040 CacheControl
dc.l OldPostDMA ; 040 PostDMA
dc.l OldPreDMA ; 040 PreDMA
Patches_030: dc.l OldReboot ; ColdReboot patch
*
* The alert patch must always be at the end of the table as it
* may be removed by a command line option. When it is removed,
* this longword is set to 0 such that the table ends here.
*
_AlertOFF: xdef _AlertOFF
dc.l OldAlert ; Alert patch
dc.l 0 ; End of table...
*
*******************************************************************************
*
* Some global data tables and other such values...
*
cnop 0,4 ; Align on longword for speed...
*
* This is the nesting count for the CachePreDMA/PostDMA code...
*
Cache_Settings: dc.l 0 ; A simple settings flag...
Nest_Count: dc.l 0 ; A long word...
*
* Storage for the MMU Frame pointer... For use only by NewReboot()
*
mmu_Frame: dc.l 0
*
* This is the physical address that the ROM is at...
*
_ROM_Addr: xdef _ROM_Addr
ROM_Addr: dc.l $00F80000 ; Default address...
*
* This value is the value that will be used for failed read requests
* When the deadly option is on, it will be non-NULL
*
_Bad_ReadValue: xdef _Bad_ReadValue
Bad_ReadValue: dc.l 0
*
* This longword will be TRUE if the user selected QUIET enforcer...
*
_Quiet_Flag: xdef _Quiet_Flag
Quiet_Flag: dc.l 0
*
* This longword will be TRUE if the user selected SMALL output...
*
_Small_Flag: xdef _Small_Flag
Small_Flag: dc.l 0
*
* This longword will be TRUE if the user selected TINY output...
*
_Tiny_Flag: xdef _Tiny_Flag
Tiny_Flag: dc.l 0
*
* This longword will be TRUE if the user selected DATESTAMP output...
*
_DoDateStamp: xdef _DoDateStamp
DoDateStamp: dc.l 0
*
* This longword will be TRUE if the user selected ShowPC output...
*
_ShowPC_Flag: xdef _ShowPC_Flag
ShowPC_Flag: dc.l 0
*
* This longword will be TRUE if the user selected Parallel output...
*
_Parallel_Flag: xdef _Parallel_Flag
Parallel_Flag: dc.l 0
*
* LED Flash count... The number of times to flash. 0=no flash
*
_LED_Count: xdef _LED_Count
LED_Count: dc.l 0
*
* This longword will be the number of stack lines to display...
*
_StackLines: xdef _StackLines
StackLines: dc.l 0
*
* These flags enable Address and Data register SegTracker output
*
_ARegCheck: xdef _ARegCheck
ARegCheck: dc.l 0
*
_DRegCheck: xdef _DRegCheck
DRegCheck: dc.l 0
*
* This longword will be the number of stack-seglist output lines
* to do... (0 means skip)
*
_SegLines: xdef _SegLines
SegLines: dc.l 0
*
* Function Pointer for SegTracker...
* char *FindSeg(address,ULONG *Hunk,ULONG *Offset)
* d0 a0 a1 a2
*
* d1/a0/a1 are trashed... Returns NULL if not found.
*
_SegTracker: xdef _SegTracker
SegTracker: dc.l 0
*
* These four values are used when doing non-hardware output.
* The first is a pointer to the memory buffer. The second is
* the offset of the input data into this buffer. The third is
* the offset of the read data point in this buffer. When the
* two offsets are equal, the buffer is empty. When the input
* offset is 1 less than the read offset, the buffer is full.
* The last longword is the size of the buffer in bytes.
* If the buffer pointer is NULL, hardware I/O will be done.
*
_OutputBuffer: xdef _OutputBuffer
OutputBuffer: dc.l 0
_WriteOffset: xdef _WriteOffset
WriteOffset: dc.l 0
_ReadOffset: xdef _ReadOffset
ReadOffset: dc.l 0
_BufferSize: xdef _BufferSize
BufferSize: dc.l 0
*
* The following is a pointer to a special "introduction" string
* that is output at the start of every Enforcer hit.
*
_Intro: xdef _Intro
Intro: dc.l 0
*
* The following is a pointer to the enforcer task - used by Level1 to
* signal the system...
*
EnforcerTask: dc.l 0
*
_DOSBase: xdef _DOSBase
DOSBase: dc.l 0 ; DOSBase...
*
* 68040 library base (Can be NULL if no 68040 or no valid library)
*
_Lib68040: xdef _Lib68040
Lib68040: dc.l 0
*
* 68060 library base (Can be NULL if no 68060 or no valid library)
*
_Lib68060: xdef _Lib68060
Lib68060: dc.l 0
*
* The following two locations store pointers to the page descriptors
* of ZeroPage and InvalidPage for the 68040/060 MMU setup.
*
ZeroPage: dc.l 0 ; Pointer to ZeroPage
InvalidPage: dc.l 0 ; Pointer to InvalidPage
*
*******************************************************************************
*
* The main fault line...
*
Intro_Fault: dc.b '%',0
Main_Fault: dc.b 7,10,'%-% # %'
Extra_Fault: dc.b 0,'data='
Final_Fault: dc.b '#',0
dc.b ' ' ; 14 spaces!
PC_Fault: dc.b ' PC: #%',10,0
Physical: dc.b ' ----BUS ERROR----'
NULL_String: dc.b 0
*
* The second fault line...
*
Line_2: dc.b 'USP: # SR: ~ SW: ~ ('
Line_2a: dc.b '+-)(-)(-) TCB: #',10,0
*
* Things for the third output line...
*
TaskName: dc.b 'Name: "%" ',0
CLIName: dc.b 'CLI: "%" ',0
;SegTrackLine: dc.b 'SegT: # - "%" '
SegTrackLine: dc.b '----> # - "%" '
HunkInfo: dc.b 'Hunk ~ Offset #',0
InvalidName: dc.b 'Invalid Name!!!',0
InvalidTask: dc.b 'Task pointer is invalid!!!',0
InvalidCLI: dc.b 'pr_CLI is invalid!!!',0
InvalidSeg: dc.b 'SegList is invalid!!!',0
*
* Three more output lines
*
AddrRegs: dc.b 'Addr: # # # # # # # --------',10,0
PrintAlert: dc.b 7,10,'Alert !! Alert # TCB: # USP: #',10 ; The Alert
DataRegs: dc.b 'Data:'
DataLine: dc.b ' # # # # # # # #',10,0
StackLine: dc.b 'Stck:',0
PC_1: dc.b 'PC-8:',0
PC_2: dc.b 'PC *:',0
PC_Invalid: dc.b 'PC Address invalid',10,0
BadStack: dc.b ' Invalid at address #',10,0
ProcInt: dc.b 'Processor Interrupt Level '
ProcInt2: dc.b '?',0
*
* These must be in this order and 7 bytes in length each...
*
Long_Str: dc.b 'LONG',0,'#',0
dc.b 'BYTE',0,'^',0
dc.b 'WORD',0,'~',0
dc.b 'LINE',0,'#',0
*
* Some variable text...
*
Read_Fault: dc.b 'READ from',0
Write_Fault: dc.b 'WRITE to ',0
Inst_Fault: dc.b '(INST)',0
Norm_Fault: dc.b ' ',0
*
* Some other text
*
_MyTask: xdef _MyTask
dc.b '« Enforcer »',0
VERSTAG
_Copyright: xdef _Copyright
dc.b 10
VERS
dc.b 9,'by Michael Sinz'
dc.b 10,9,9,'Copyright © 1992-2001'
dc.b 10,9,9,'All Rights Reserved'
dc.b 10
dc.b 10,9,9,'Enforcer@Sinz.org'
dc.b 10,10,0
*
DateTime: dc.b 10,'% %',0
_DateStr: xdef _DateStr
DateStr: dc.b '??-???-??',0,0,0,0,0,0,0,0,0
_TimeStr: xdef _TimeStr
TimeStr: dc.b '??:??:??',0,0,0,0,0,0,0,0,0,0
*
gfxName: dc.b 'graphics.library',0
*
*******************************************************************************
*
* This is where we put the VBR while enforcer is running...
*
cnop 0,4 ; For speed...
OldSR: dc.w 0 ; Store old SR register...
dc.w 0 ; Pad...
OldPageAddress: dc.l 0 ; For the old page entry itself
OldPage: dc.l 0 ; For the enforcer page setting
OldLevel1: dc.l 0 ; Old level1 vector...
OldTrace: dc.l 0 ; Old trace vector...
OldVBR: ds.l 257 ; Old VBR location...
NewVBR: equ OldVBR+$04 ; New vector table ($100 4-byte entries)
NewExecBase: equ NewVBR+$04 ; ExecBase location
NewBusError: equ NewVBR+$08 ; Bus error location
NewIllegal: equ NewVBR+$10 ; Illegal Instruction error location
NewTrace: equ NewVBR+$24 ; Trace vector
NewLevel1: equ NewVBR+$64 ; Level1 interrupt
*
xdef _SysBase ; Make this external...
_SysBase: equ NewExecBase
*
*******************************************************************************
*
* "A master's secrets are only as good as the
* master's ability to explain them to others." - Michael Sinz
*
*******************************************************************************
*
END
| ||
![]() |

visitors to this page.