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 | ||