;		 CSYMFMT

;		 objfile "dbuglib.o"
;		 addsym

;		 incdir  "include:"

		include "exec/types.i"
		include "exec/tasks.i"
		include "exec/execbase.i"
		include "exec/memory.i"
		include "dos/dos.i"
		include "dos/dosextens.i"
		include "dos/doshunks.i"

;		 include "lvo/exec.lvo"
;		 include "lvo/dos.lvo"

	IFD BARFLY
		BOPT	h-,l-,a+,f+,O-,wo-,ws-
		SUPER
	ENDC

********************************************************************************

sBase		equ	4

EXEC		MACRO
		xref	_LVO\1
		move.l	a6,-(sp)
		move.l	sBase,a6
		jsr	_LVO\1(a6)
		move.l	(sp)+,a6
		ENDM

DOS		MACRO
		xref	_LVO\1
		move.l	a6,-(sp)
		move.l	dBase,a6
		jsr	_LVO\1(a6)
		move.l	(sp)+,a6
		ENDM

************************************************************************

* Memory Allocation

memList 	dc.l	0

		XDEF	_MallocAny
_MallocAny	move.l	8(sp),d1
		bra.s	Malloc

		XDEF	_MallocChip
_MallocChip	moveq.l #MEMF_CHIP,d1
		bra.s	Malloc
		XDEF	_MallocFast
_MallocFast	moveq.l #MEMF_FAST,d1
		bra.s	Malloc
		XDEF	_MallocPublic
_MallocPublic	moveq.l #MEMF_PUBLIC,d1

Malloc		or.l	#MEMF_CLEAR,d1
		move.l	4(sp),d0
		addq.l	#8,d0			; allocate 8 extra bytes (link,size)
		move.l	d0,-(sp)
		EXEC	AllocMem
		move.l	(sp)+,d1
		tst.l	d0
		beq.s	MallocError
		move.l	d0,a0
		move.l	memList,(a0)+		; link into memList
		move.l	d0,memList
		move.l	d1,(a0)+		; store size, also
		move.l	a0,d0
		rts

MallocError	moveq	#0,d0
		rts

		XDEF	_Free
_Free		move.l	4(sp),a1
		subq.l	#8,a1
		lea	memList,a0
.find		move.l	(a0),d0 		; link ahead
		beq.s	.exit			; ignore if not found
		cmp.l	d0,a1			; found block pointing to me?
		beq.s	.found
		move.l	d0,a0
		bra.s	.find
.found		move.l	(a1),(a0)		; unlink from memList
		move.l	4(a1),d0
		EXEC	FreeMem
.exit		rts


		XDEF	_CleanMem
_CleanMem	move.l	memList,d0
		beq.s	.exit2			; if memory list is NULL
		move.l	d0,a1
		move.l	(a1),memList		; unlink memory from list
		move.l	4(a1),d0		; fetch the size
		EXEC	FreeMem 		; free it
		bra.s	_CleanMem		; do entire list
.exit2		rts

********************************************************************************

		XDEF	debugSR,debugPC,debugD0,debugA7
debugSR 	dc.w	0
debugPC 	dc.l	0
debugD0 	ds.l	8
debugA0 	ds.l	7
debugA7 	dc.l	0

		XREF	_debugStack,_debugStackTop
		XREF	_thisProcess,_thisCli,_exeCommandName,_commandNameSave,_systemTrapHandler
		XREF	_programState,_programSR,_programPC,_programD0,_programA7,_programStack,_programStackTop

;
; EnterProgram
;
; Synopsis:
;	This subroutine switches from debugger context to program context.
;	Program context proceeds until an Exec Exception occurs.  When this
;	happens, the Exception # from the top of stack is saved in programState,
;	and debugger mode is entered.  EnterProgram then returns to its 'C'
;	caller.  The program state variables reflect the new values, etc.
;
		XDEF	_EnterProgram
_EnterProgram	move.l	a5,-(sp)
		lea	Super,a5
		EXEC	Supervisor
		move.l	(sp)+,a5
		rts

;
; This routine is entered in supervisor mode.  This means the debugger's SR (word) and
; PC (LONG - rts instruction at end of EnterDebugger) are on the top of the stack.
; But this is the system stack and not the debugger's - that's in USP (IMPORTANT).
;
; Normally, _LVOSupervisor initiated routines exit via RTE directly back to the
; caller.  In this case, we will pop the debugger's SR and PC from the stack and
; save them.  We will also save the d0-d7/a0-a6 registers and USP (debugger's A7).
; We then push the program's SR and PC on the stack, load its register set from
; RAM variables, and RTE.  The RTE then starts executing the PROGRAM - this means
; that the task will be used by the target program until the debugger somehow gains
; control again.
;
; So far, the methodology of "swapping contexts" from debugger to program state has
; been described.  While in program state, the program might do various OS operations
; that would make the debugger not-transparent.  Since the program and debugger are
; sharing the same task, the debugger needs its own stack so the program won't see/trash
; values that would crash the debugger.  As well, there are various CLI and TASK structure
; fields that are set up by the OS for the debugger so the debugger needs to set those
; up for the program's needs.  Thus a full context switch involves swapping SR, PC, registers,
; AND OS structure fields.
;
; Notes:
;	Debugger must be cautious about using resources that might be required by the
;	program - signal bits are an obvious gotcha.
;
;	Because we are only diddling the SR and PC on the top of the stack, we should
;	not have to care about what CPU model we are on.  Leave the advanced stack
;	frames alone and let the processor do the right thing when we RTE.
;
Super		ori.w	#$700,sr		; disable interrupts during supervisor mode
	; first save debugger state
		move.w	(sp)+,debugSR
		move.l	(sp)+,debugPC
		movem.l d0-d7/a0-a6,debugD0
		move.l	usp,a0
		move.l	a0,debugA7		; all registers are trashable from here down
	; then setup program OS state
		lea	_exeCommandName,a2
		move.l	a2,d0
		lsr.l	#2,d0
		move.l	_thisCli,a2
		move.l	d0,cli_CommandName(a2)	; So argv[0] works :)
		move.l	_thisProcess,a2
		move.l	_programStack,TC_SPLOWER(a2)	 ; set task's TC_stack fields
		add.l	_programStackTop,d0
		addq.l	#2,d0
		move.l	d0,TC_SPUPPER(a2)	; upper/lower so stack bounds checking code works

	; now install trap handler so exception gets MY attention!!!
	; this may break with debugged programs that install their own (and don't follow the rules) ...
		lea	TrapHandler,a1
		move.l	a1,TC_TRAPCODE(a2)	; stuff trap code handler
	; now setup program registers and supervisor-style stack
		move.l	_programA7,a1
		move.l	a1,usp
		movem.l _programD0,d0-d7/a0-a6	 ; all registers are program's now, beware!
		move.l	_programPC,-(sp)	 ; program counter
		move.w	_programSR,-(sp)	 ; status register
		rte				; EXECUTE PROGRAM

;
; The program has executed until an exception occured.	The Exec exception handlers cause the
; exception # to be pushed on the stack, followed by the standard SR, PC supervisor stack.
; Cleverly, the STATE_ equates in the main program just happen to be the same as the
; exception #'s :)
;
; What we want to do is to go back into debugger context so the debugger can report on
; the occurance of the exception.  This involves saving the exception #, SR, and PC
; from the stack in the programState, programSR, and programPC variables.  The program's
; register set is saved to RAM, as well as the pertinent OS structure fields.  The debugger's
; register set and environment is restored and the RTE returns to the rts instruction at
; the end of EnterProgram().
;
; The debugger can cause the program to generate an exception - this is the key to making things
; like stepping and breakpoints possible.  An elegant solution for stepping is for the debugger
; to set bit 15 of programSR.  Each time EnterProgram is called, it will execute one instruction
; and then generate a STATE_TRACE exception.  Tracing will cease when the debugger clears bit
; 15 of programSR.  Breakpoints are implemented by replacing the instruction at the desired
; address with an ILLEGAL ($4afc) opcode.  When EnterProgram is called, the program will execute
; until the ILLEGAL instruction is encountered.  Then the STATE_ILLEGAL exception will occur
; and the debugger will regain control.
;
; Other tricks:
;	What happens when the program runs until it terminates?  This is a special case where
;	no exception would be generated!  The trick is for the debugger to push the address
;	of an illegal instruction on the program's stack before initially executing the
;	program.  All programs exit to the CLI by RTS!	Thus when the program does RTS,
;	it executes ILLEGAL right away and the debugger gains control.	The exitcode of
;	the program will be in programD0 and the PC will point at the ILLEGAL.
;
TrapHandler	move.l	(sp)+,_programState

	; Save program state
		move.w	(sp)+,_programSR
		move.l	(sp)+,_programPC
		movem.l d0-d7/a0-a6,_programD0
		move.l	usp,a1
		move.l	a1,_programA7			 ; registers are scratch from here down

	; Restore debugger state
		move.l	debugA7,a1
		move.l	a1,usp
		move.l	_thisProcess,a2
		move.l	_debugStack,TC_SPLOWER(a2)
		move.l	_debugStackTop,TC_SPUPPER(a2)
		move.l	_systemTrapHandler,TC_TRAPCODE(a2)
		move.l	_thisCli,a2
		move.l	_commandNameSave,cli_CommandName(a2)
		move.l	debugPC,-(sp)
		move.w	debugSR,-(sp)
		movem.l debugD0,d0-d7/a0-a6
		rte


************************************************************************

		XDEF	_TargetExit
_TargetExit	dc.w	$4afc			; ILLEGAL instruction

************************************************************************

		END
