Amiga Checkmark
Amiga Enforcer V37.73
by Michael Sinz (MKSoft Development)
Copyright 1992-2001 - All Rights Reserved


Handler.asm

Enforcer
by
Michael Sinz
MKSoft Development
Copyright 1992-2001
All Rights Reserved

Information
Documentation
Enforcer Archive

Contact me

Source code for the main Enforcer tool:
Enforcer.c
Handler.asm

Source code for the MMU tool:
MMU.c
MMU_Handler.asm

Source code for SegTracker:
SegTracker.c
SegTrackerPatch.asm

Source code for FindHit:
FindHit.c

Source code for RebootOff:
RebootOff.asm

Source code for Move4K:
Move4K.asm

Source code for LawBreaker:
LawBreaker.asm


Amiga Checkmark
 
*                             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
 
Amiga Checkmark

Bobby ApprovedBest viewed with any browser
There have been  Counter  visitors to this page.