
;
; MSBug - Small CLI Debugger Using SIM
; Copyright © 1993 S. Walter
;
; This is a sample program that uses SIM as debugger for a CLI program.
; It is intended as example of how to interface SIM, not as a perfect
; example of a debugger. It does absolutely no fancy things such as
; checking for WB start, copying of locks and file handles, command line
; preparation etc.
;
; Usage in CLI: msbug <program> <command line>
;
; Note that SIM must be in the current directory as 'SIM2'. You can abort
; debugging with the 'Quit' command in SIM.
;

;
; This source assembles with ProAsm, A68K and DevPac. It is public domain.
; You may use the entier source or parts of it for you own needs.
;


;------------------
; A68K has no 'rs' instructions, so a converted include file s used.
;

	include	sim_equates.i


;--------------------------------------------------------------------
; Begin.
;
Start:	movem.l	d0-a6,originalregs
	movem.l	a0/d0,startregs


;------------------
; Open dos.library and intuition.library.
;
OpenLibs:
	move.l	4,a6
	lea	intname(pc),a1
	jsr	-408(a6)		;OldOpenLibrary
	move.l	d0,intbase
	beq	Exit
	lea	dosname(pc),a1
	jsr	-408(a6)		;OldOpenLibrary
	move.l	d0,dosbase
	beq	CloseInt


;------------------
; Load SIM from the current directory.
;
LoadSIM:
	move.l	d7,d1
	moveq	#-2,d2
	move.l	#simname,d1
	move.l	dosbase(pc),a6
	jsr	-84(a6)			;Lock
	move.l	d0,d7
	beq	CloseDos
	lea	-260(a7),a7
	move.l	d7,d1
	move.l	a7,d2
	jsr	-102(a6)		;Examine
	moveq	#0,d6
	tst.l	d0
	beq.s	GotLen
	move.l	124(a7),d6

GotLen:	lea	260(a7),a7
	move.l	d7,d1
	jsr	-90(a6)			;UnLock
	move.l	d6,simlen
	beq	CloseDos

	move.l	4,a6
	move.l	d6,d0
	moveq	#1,d1
	jsr	-198(a6)		;AllocMem
	move.l	d0,simptr
	beq	CloseDos

	move.l	#simname,d1
	move.l	#1005,d2
	move.l	dosbase(pc),a6
	jsr	-30(a6)			;Open
	move.l	d0,d7
	beq	FreeSIM

	move.l	d0,d1
	move.l	simptr(pc),d2
	move.l	d6,d3
	jsr	-42(a6)			;Read
	move.l	d0,d5

	move.l	d7,d1
	jsr	-36(a6)			;Close
	cmp.l	d5,d6
	bne	FreeSIM

	move.l	simptr(pc),a0
	cmp.l	#'SIM!',simb_SIMSig(a0)	;Check if it is SIM!
	bne	FreeSIM


;------------------
; Get display and work memory.
;
GetMem:
	move.l	4,a6
	move.l	#$a000,d0
	moveq	#3,d1
	jsr	-198(a6)		;AllocMem
	move.l	d0,display
	beq	FreeSIM
	moveq	#1,d0
	swap	d0
	moveq	#1,d1
	jsr	-198(a6)		;AllocMem
	move.l	d0,workmem
	beq	FreeDisplay


;------------------
; Get program name, parse to command line arguments and load program.
;
GetName:
	move.l	startregs+4(pc),a0
	lea	programname(pc),a1
	moveq	#0,d0
CopyName:
	cmp.b	#' ',(a0)
	beq.s	GotName
	cmp.b	#$a,(a0)
	beq.s	GotName
	move.b	(a0)+,(a1)+
	addq.w	#1,d0
	cmp.w	#162,d0
	beq	FreeWorkMem
	bra.s	CopyName

GotName:
	tst.w	d0
	beq	FreeWorkMem
	clr.b	(a1)

RemSpace:
	cmp.b	#' ',(a0)
	bne.s	GotCLine
	addq.w	#1,d0
	addq.l	#1,a0
	bra.s	RemSpace

GotCLine:
	sub.l	d0,startregs
	move.l	a0,startregs+4
	pea	programname(pc)
	move.l	(sp)+,d1
	move.l	dosbase(pc),a6
	jsr	-150(a6)		;LoadSeg
	move.l	d0,segments
	beq	FreeWorkMem


;------------------
; Get symbols. At this point a routine which scans the executable for symbol
; hunks should be inserted. Now it only generates symbols for each hunk start.
;
GetSyms:
	move.l	segments(pc),d7

GSLoop:	lsl.l	#2,d7
	beq.s	SetSyms
	move.l	d7,a4
	move.l	(a4)+,d7

	lea	rawdata(pc),a1
	lea	hunkraw(pc),a0
	lea	RDFSet(pc),a2
	lea	rawbuffer(pc),a3
	move.l	4,a6
	jsr	-522(a6)		;RawDoFmt	

	lea	rawbuffer(pc),a0
	moveq	#-1,d1
Count:	addq.l	#1,d1
	tst.b	(a0)+
	bne.s	Count

	lea	rawbuffer(pc),a0
	move.l	a4,d0
	move.l	symbollist(pc),a1
	bsr	StoreSymbol
	tst.l	d0
	beq	FreeSymbols
	move.l	d0,symbollist
	addq.l	#1,rawdata
	bra.s	GSLoop

RDFSet:	move.b	d0,(a3)+
	rts

SetSyms:
	move.l	simptr(pc),a0
	move.l	symbollist(pc),simb_Symbols(a0)


;------------------
; Now init SIM. Call simb_EInit anyway, then set VBR, plane, backup and
;work memory.
;
InitSIM:
	move.l	simptr(pc),a0
	jsr	simb_EInit(a0)
	tst.l	d0
	beq	FreeSymbols
	bsr	Get_VBR_To_D0
	move.l	d0,simb_VBR(a0)
	move.l	workmem(pc),simb_WMStart(a0)
	move.l	#$10000,simb_WMLength(a0)
	move.l	display(pc),simb_Plane(a0)
	clr.l	simb_Backup(a0)


;------------------
; Set up server entry.
;
InitEServer:
	move.l	#simb_Text-simb_DLength,simb_DLength(a0)
	move.l	#'MSB!',simb_DName(a0)

	moveq	#14,d0
	lea	simb_DRegisters(a0),a1
	lea	originalregs(pc),a2
SetRegs:
	move.l	(a2)+,(a1)+
	dbra	d0,SetRegs
	move.l	startregs(pc),simb_DRegisters(a0)	;d0
	move.l	startregs+4(pc),simb_DRegisters+8*4(a0)	;a0

	move.l	a7,simb_DUSPreg(a0)
	clr.l	simb_DISPreg(a0)
	clr.l	simb_DMSPreg(a0)
	clr.w	simb_DSRreg(a0)

	move.l	segments(pc),d0
	addq.l	#1,d0
	lsl.l	#2,d0
	move.l	d0,simb_DPCreg(a0)
	move.l	d0,simb_DRegisters+12*4(a0)		;a4
	move.l	(a7),truereturnaddr
	move.l	#ourreturnaddr,(a7)
	move.l	a7,stackpointer


;------------------
; Set up server quit feature.
;
SetUpQuit:
	pea	QuitFunction(pc)
	move.l	(sp)+,simb_DQuitPC(a0)
	clr.w	simb_DQuitSR(a0)

	clr.l	simb_DQuitUSP(a0)
	clr.l	simb_DQuitISP(a0)
	clr.l	simb_DQuitMSP(a0)
	clr.l	simb_DQuitDMA(a0)



;------------------
; The trap handler.
;
SetTrapHD:
	move.l	4,a6
	move.l	$114(a6),a1
	move.l	50(a1),oldthd
	pea	simb_EException(a0)
	move.l	(sp)+,50(a1)


;------------------
; Go!
;
Go:	jmp	simb_EServer(a0)


;------------------
; Return point.
;
ourreturnaddr:
	move.l	truereturnaddr(pc),-(sp)
	move.l	d0,returnvalue

	move.l	4,a6
	move.l	$114(a6),a1
	move.l	oldthd(pc),50(a1)


;------------------
; Clean up.
;
FreeSymbols:
	move.l	4,a6
	move.l	simptr(pc),a0
	clr.l	simb_Symbols(a0)
	move.l	symbollist(pc),d0
FSLoop:	beq.s	FreeSeg
	move.l	d0,a1
	move.l	ssym_Next(a1),d7
	move.l	ssym_Size(a1),d0
	jsr	-210(a6)		;FreeMem
	move.l	d7,d0
	bra.s	FSLoop

FreeSeg:
	move.l	dosbase(pc),a6
	move.l	segments(pc),d1
	jsr	-156(a6)		;UnLoadSeg

FreeWorkMem:
	move.l	4,a6
	move.l	workmem,a1
	moveq	#1,d0
	swap	d0
	jsr	-210(a6)		;FreeMem

FreeDisplay:
	move.l	display,a1
	move.l	#$a000,d0
	jsr	-210(a6)		;FreeMem

FreeSIM:
	move.l	4,a6
	move.l	simptr(pc),a1
	move.l	simlen(pc),d0
	jsr	-210(a6)		;FreeMem

CloseDos:
	move.l	dosbase(pc),a1
	jsr	-414(a6)		;CloseLibrary

CloseInt:
	move.l	intbase(pc),a1
	jsr	-414(a6)		;CloseLibrary

Exit:	move.l	returnvalue(pc),d0
	rts


;------------------
; The Quit function.
;
	dc.b	'OKAY'
QuitFunction:
	bsr	Ask_If_Quit_Okay
	tst.l	d0
	bne	QuitOkay

	move.l	#simb_Text-simb_DLength,simb_DLength(a6)
	jmp	simb_EServer(a6)

QuitOkay:
	move.l	stackpointer(pc),a7
	addq.l	#4,a7
	bra	ourreturnaddr



;--------------------------------------------------------------------
; Subroutines.
;

;------------------
; Get VB address.
;
Get_VBR_To_D0:
	move.l	4,a6
	btst	#0,297(a6)
	beq.s	M68000
	lea	GetVBR(pc),a5
	jmp	-30(a6)

GetVBR:	dc.l	$4E7A0801	;movec	vbr,d0
	rte
M68000:	moveq	#0,d0
	rts



;------------------
; Set up a requester and ask if it is okay to quit.
;
Ask_If_Quit_Okay:
	movem.l	d1-a6,-(sp)

	suba.l	a0,a0
	moveq	#0,d0
	moveq	#0,d1
	moveq	#30,d2
	lsl.l	#3,d2
	moveq	#64,d3
	lea	maintext(pc),a1
	lea	positivetext(pc),a2
	lea	negativetext(pc),a3
	move.l	intbase(pc),a6
	jsr	-348(a6)		;AutoRequester
	tst.l	d0

	movem.l	(sp)+,d1-a6
	rts

maintext:
	dc.b	0,0,0,0
	dc.w	10,8
	dc.l	0
	dc.l	text1
	dc.l	0

positivetext:
	dc.b	0,0,0,0
	dc.w	4,3
	dc.l	0
	dc.l	text2
	dc.l	0

negativetext:
	dc.b	0,0,0,0
	dc.w	4,3
	dc.l	0
	dc.l	text3
	dc.l	0

text1:		dc.b	'Abort debugging?',0
text2:		dc.b	' Okay ',0
text3:		dc.b	'Cancel',0
	even



;------------------
; Store a symbol:
;
; IN:
;       D0  Value of the symbol.
;       D1  Length of symbol name in chars.
;       A0  First byte of symbol name.
;       A1  Previous symbol list or 0 when called for the first time.
;
; OUT:
;       D0  0 if no more memory, else symbol list
;
StoreSymbol:
	movem.l	d1-a6,-(sp)
	move.l	d1,d2
	addq.l	#8,d1		;7 bytes for length, next and value
	and.w	#$fffe,d1	;plus round up to even
	move.l	d0,d6
	move.l	a1,d7

;Check if there already is a symbol list and that the symbol will fit.
	beq.s	NewNode
	cmp.l	ssym_Free(a1),d1
	bls.s	Store

;Allocate and init a new node.
NewNode:
	bsr	Get_Two_KB_Of_Memory
	beq.s	Failure
	exg.l	d0,a1
	move.l	d0,ssym_Next(a1)
	clr.l	ssym_NumOf(a1)
	move.l	#ssym_First,ssym_Used(a1)
	move.l	#$800,ssym_Size(a1)
	clr.l	ssym_Highest(a1)
	move.l	#-1,ssym_Lowest(a1)
	move.l	#$800-ssym_First,ssym_Free(a1)
;Change this one to use a unique key such as eg. the program start address.
	move.l	#'MSB!',ssym_Key(a1)

;Copy symbol name and refresh the node.
Store:	addq.l	#1,ssym_NumOf(a1)
	move.l	a1,a2
	add.l	ssym_Used(a1),a2
	add.l	d1,ssym_Used(a1)
	sub.l	d1,ssym_Free(a1)
	move.l	d6,(a2)+
	move.w	d1,(a2)
	subq.w	#4,(a2)+
	move.b	d2,(a2)+

CName:	move.b	(a0)+,(a2)+
	subq.b	#1,d2
	bne.s	CName

	cmp.l	ssym_Highest(a1),d6
	bls.s	NoHigher
	move.l	d6,ssym_Highest(a1)
NoHigher:
	cmp.l	ssym_Lowest(a1),d6
	bhi.s	NoLower
	move.l	d6,ssym_Lowest(a1)
NoLower:
	move.l	a1,d0

;Return.
Done:	movem.l	(sp)+,d1-a6
	rts

;No memory.
Failure:
	moveq	#0,d0
	bra.s	Done


;Get 2Kb of memory.
Get_Two_KB_Of_Memory:
	movem.l	d1-a6,-(sp)
	move.l	4,a6
	move.l	#$800,d0
	moveq	#1,d1
	jsr	-198(a6)
	movem.l	(sp)+,d1-a6
	rts



;--------------------------------------------------------------------
; Data.
;

simptr:		dc.l	0
simlen:		dc.l	0

dosbase:	dc.l	0
intbase:	dc.l	0
returnvalue:	dc.l	20
programname:	dc.l	0,0,0,0,0,0,0,0,0,0
		dc.l	0,0,0,0,0,0,0,0,0,0
		dc.l	0,0,0,0,0,0,0,0,0,0
		dc.l	0,0,0,0,0,0,0,0,0,0
		dc.l	0,0,0,0,0,0,0,0,0,0
		dc.l	0,0,0,0,0,0,0,0,0,0
		dc.w	0			;162	
rawdata:	dc.l	0

segments:	dc.l	0
display:	dc.l	0
workmem:	dc.l	0
truereturnaddr:	dc.l	0
stackpointer:	dc.l	0
startregs:	dc.l	0,0
originalregs:	dc.l	0,0,0,0,0,0,0,0,0,0,0,0,0,0	;14
oldthd:		dc.l	0
symbollist:	dc.l	0

dosname:	dc.b	'dos.library',0
intname:	dc.b	'intuition.library',0
simname:	dc.b	'sim2',0
hunkraw:	dc.b	'Hunk_%ld',0
rawbuffer:	dc.b	'Hunk_                 '

	end

