;BUG.68K	APR-21-88
;A debugger similar to Stride's SDT.
; by Loren Blaney
;
;REVISION HISTORY:
;FEB-22-87, Original based on version by King & Knight, transcribed by
;	John Klein
;APR-21-88, Modified for Mac II. Uses Apex system console.
;
;COMMANDS:
;
; DISPLAY:
;  Dm addr	Display memory
;  DR		Display registers
;  DDn		Display Data registers (n is ignored)
;  DAn		Display Address registers
;  DPc		Display Program Counter register
;  DUsp		Display User Stack Pointer register (SSP = A7)
;  DSr		Display Status register (not SSP)
;  DBn		Display breakpoints
;
; SET:
;  Sm addr	Set memory
;  SR val	Set register (begins with D0)
;  SDn val	Set Data register
;  SAn val	Set Address register
;  SPc val	Set Program Counter register
;  SUsp val	Set User Stack Pointer register
;  SSr val	Set Status register
;  SBn val	Set breakpoint (0= cleared)
;
;	SET MODE COMMANDS:
;		00000400: 2345 = val
;		00000402: 0AF0 =
;		Return and down arrow move to next word
;		Up arrow moves back a word
;		Multiple entries may be made on one line
;		Period (.) (RETURN) terminates entry mode
;		Registers are set the same way
;
;
; EXECUTE:
;  Go pc	Go execute at address (PC). Defaults to current PC
;		if address is not specified. That is, it resumes after
;		a breakpoint. Notice that if you Go directly to a
;		location which contains a breakpoint, it will not break.
;  Tr pc	Trace beginning at address (PC). Defaults work same as
;		Go command. Breakpoints have no effect.
;
;
; MISCELLENA
;  F s e p	Fill (byte, word, or long is determined by pattern size)
;  M s e t	Move memory start.end>to
;* C s e t	Compare memory start.end>to
;* L s e p	Look for pattern (mask byte??)
;* H		Help (also help key)
;* R u b a s	Read and write disk blocks (unit, block, addr, size)
;* W u b a s
;
;  Z bytes	Set frame size for memory dump
;*		Download
;  CTRL-C	Jump back to main command mode
;  CTRL-P	Restart and reinitialize the debugger
;
;* Indicates unimplemented features.
;
;======================================================================
	NOLIST
	INCLUDE	SYSPAG
	LIST

;ADDITIONAL ASCII CHARACTERS:
UP	EQU	'K'-CTRL	;Up arrow
DOWN	EQU	'J'-CTRL	;Down arrow

SSPDEF	EQU	$3000		;Default supervisor stack pointer

SRDEF	EQU	$2000		;Supervisor mode

SIZEDEF	EQU	$40		;Default memory display frame size

;----------------------------------------------------------------------
	ORG	$400
	JMP	START.L

RAMBASE	ORG	$1000

;The following tables must be in order because of the way SETREG works
SAVD	DS.L	8		;Space for 8 data registers
SAVA	DS.L	8		;Space for 8 address registers
SAVPC	DS.L	1		;Space for program counter
SAVUSP	DS.L	1		;Space for user stack pointer
SAVSR	DS.L	1		;Space for status register
	DS.L	1		;Dummy spacer
BRKITBL	DS.W	8		;Space for 8 breakpoint instructions
BRKATBL	DS.L	8		;Space for 8 breakpoint addresses
				; (only the low word is actually used)
; (end order restriction)

ACCESS	DS.L	1		;Bus and address error access address
FUNCODE	DS.W	1		;Bus and address error function code

ADDRDIS	DS.L	1		;Default address for memory display
ADDRSET	DS.L	1		;Default address for memory set
SIZEDIS	DS.L	1		;Memory display frame size (bytes)

BPFLAG	DS.B	1		;Flag: resume execution thru breakpoint
GOFLAG	DS.B	1		;Flag: user's program is running

BKCHAR	DS.B	1		;Last char read by CHIN
BKFLAG	DS.B	1		;Flag: BACKUP command, reread last char

RAMEND	EQU	@		;End of RAM area (+1)

;======================================================================
;DEBUGGER ENTRY POINT
;
BASE	EQU	$3000
	ORG	BASE
;	ORG	$7EF00
START	BSR	INIT

RESTART	BSR	CRLF		;(Entry from CTRL-C)

GETCMD	BSR	TEXT		;Display prompt message
	ASCII	'BUG'
	DC.B	'>' + $80

	BSR	OPENI		;Flush keyboard buffer
	BSR	GETCH		;Get uppercase character into D0
	LEA	CMDTBL.L,A0	;Point to command search table
	BSR	EXECUTE		;Call routine to handle command
	BRA.S	GETCMD		;Loop back

;Main command table:
CMDTBL	DC.W	'D'		;Display memory and registers
	DC.W	CMD_D_-CMDTBL
	DC.W	CR		;Display memory
	DC.W	CMD_DM-CMDTBL
	DC.W	DOWN		;Display memory
	DC.W	CMD_DM-CMDTBL
	DC.W	UP		;Display memory
	DC.W	CMD_DMU-CMDTBL
	DC.W	'S'		;Set memory and registers
	DC.W	CMD_S_-CMDTBL
	DC.W	'G'		;Start or continue program (Go)
	DC.W	CMD_G-CMDTBL
	DC.W	'T'		;Trace program
	DC.W	CMD_T-CMDTBL
	DC.W	'F'		;Fill memory with pattern
	DC.W	CMD_F-CMDTBL
	DC.W	'M'		;Move a block of memory
	DC.W	CMD_M-CMDTBL
	DC.W	'Z'		;Move a block of memory
	DC.W	CMD_Z-CMDTBL
	DC.W	$00		;Mark end of table
	DC.W	CMD_BAD-CMDTBL	;Routine to handle illegal commands

;----------------------------------------------------------------------
;Routine to handle illegal commands
;
CMD_BAD	BSR	TEXT
	ASCII	'I BEG YOUR PARDON?'
	DC.B	BEL, CR + $80
	RTS

;----------------------------------------------------------------------
;Initialization stuff (mostly cold start)
;
INIT	BSR	OPEN

;Set up the exception vectors.
;First, set all vectors to the unassigned exception handler.
;This fills memory locations $0000 - $00FF.
	LEA	UA_EXCP.L,A1	;Address of unassigned exception handler
	SUBA.L	A0,A0		;Point to location 0
	MOVE.W	#63,D0		;Get number of vectors (-1)
INIT10	MOVE.L	A1,(A0)+
	DBF	D0,INIT10

	LEA	IN_EXCP.L,A1	;Address of unassigned interrupt handler
	MOVE.W	#191,D0		;Get number of vectors (-1)
INIT15	MOVE.L	A1,(A0)+	;Resume loading where unassigned vectors
	DBF	D0,INIT15	; left off

	LEA	$64,A0		;Point to start of interrupt autovectors
	MOVE.W	#7,D0		;Get number of vectors (-1)
INIT17	MOVE.L	A1,(A0)+
	DBF	D0,INIT17

	LEA	TI_EXCP.L,A1
	LEA	$80,A0		;Set up TRAP vectors
	MOVE.W	#15,D0		;Get number of exception vectors (-1)
INIT19	MOVE.L	A1,(A0)+
	DBF	D0,INIT19

;Now, set up the other vectors.
	LEA	VECTBL.L,A1	;Point to table of vectors
	SUBA.L	A0,A0		;Point to location 0
	MOVE.W	#11,D0		;Get number of exception vectors -1
INIT20	MOVE.L	(A1)+,(A0)+
	DBF	D0,INIT20

	LEA	$60,A0		;Set up spurious interrupt vector
	MOVE.L	(A1)+,(A0)
	LEA	$BC,A0		;Set up breakpoint TRAP (15)
	MOVE.L	(A1)+,(A0)

	LEA	RAMBASE.L,A0	;Clear all variables
	MOVE.W	#[RAMEND-RAMBASE]/4-1,D0 ;Size of area to clear (-1)
INIT30	CLR.L	(A0)+		;Clear data area
	DBF	D0,INIT30

	MOVE.L	#SSPDEF,SAVA+28	;Set default supervisor statck pointer
	MOVE.W	#SRDEF,SAVSR+2	;Set default status register
	MOVE.L	#SIZEDEF,SIZEDIS ;Set default memory display size

	BSR	TEXT		;Display sign-on message
	DC.B	CR
	ASCII	'-- DEBUGGER, V1.0x1 --'
	DC.B	CR + $80
	RTS

;Vector table
VECTBL	DC.L	SSPDEF		;0 Reset:  Initial SSP
	DC.L	START		;1 Reset: Initial PC
	DC.L	BE_EXCP		;2 Bus Error
	DC.L	AE_EXCP		;3 Address Error
	DC.L	II_EXCP		;4 Illegal Instruction
	DC.L	ZD_EXCP		;5 Zero Divide
	DC.L	CI_EXCP		;6 CHK Instruction
	DC.L	TV_EXCP		;7 TRAPV Instruction
	DC.L	PV_EXCP		;8 Privilege Violation
	DC.L	TR_EXCP		;9 Trace
	DC.L	LA_EXCP		;10 Line 1010 Emulator
	DC.L	LF_EXCP		;11 Line 1111 Emulator

	DC.L	SI_EXCP		;12 Spurious Interrupt
	DC.L	BP_EXCP		;15 Breakpoint Trap

;======================================================================
;DISPLAY COMMANDS
;----------------------------------------------------------------------
;Dispatch to display routine
;
CMD_D_	BSR	GETCH		;Get uppercase character into D0
	LEA	DCMDTBL.L,A0	;Point to command search table
	BRA	EXECUTE		;(PBRA) Call routine to handle command

;Display command table:
DCMDTBL	DC.W	'M'		;Display memory
	DC.W	CMD_DM-DCMDTBL
	DC.W	CR		;Display memory
	DC.W	CMD_DM-DCMDTBL
	DC.W	SPACE		;Display memory	???
	DC.W	CMD_DM-DCMDTBL
	DC.W	'R'		;Display all registers
	DC.W	CMD_DR-DCMDTBL
	DC.W	'D'		;Display data registers
	DC.W	CMD_DD-DCMDTBL
	DC.W	'A'		;Display address registers
	DC.W	CMD_DA-DCMDTBL
	DC.W	'P'		;Display program counter
	DC.W	CMD_DP-DCMDTBL
	DC.W	'U'		;Display user stack pointer
	DC.W	CMD_DU-DCMDTBL
	DC.W	'S'		;Display status register
	DC.W	CMD_DS-DCMDTBL
	DC.W	'B'		;Display breakpoints
	DC.W	CMD_DB-DCMDTBL
	DC.W	$00		;Mark end of table
	DC.W	CMD_BAD-DCMDTBL	;Routine to handle illegal commands

;----------------------------------------------------------------------
;Up arrow command -- back up and display memory
;
CMD_DMU	MOVE.L	SIZEDIS,D0	;Back up two frame lengths
	ADD.L	D0,D0
	SUB.L	D0,ADDRDIS	;(PFALL) Fall into display memory

;----------------------------------------------------------------------
;DM command -- display memory
; Format:
;00000400:  0000 1111 2222 3333   4444 5555 6666 7777   ........  ......
;
; Destroys registers D0, A0, and A1
;
CMD_DM	BSR	BACKUP
	BSR	HEXIN		;Get address
	BEQ.S	CDM01		;Branch if no address given
	MOVE.L	D0,ADDRDIS	;Set newly specified address

CDM01	MOVEA.L	ADDRDIS,A0	;Get address for start of display

CDM00	MOVE.L	A0,D0		;Display the address
	BSR	HEX8OUT
	BSR	TEXT		;Follow it with a colon and 2 spaces
	ASCII	': '
	DC.B	SPACE + $80

	MOVEA.L	A0,A1		;Get working copy of address into A1
CDM10	MOVE.B	(A1)+,D0	;Display the byte at the address
	BSR	HEX2OUT

	MOVE.W	A1,D0		;Get current address
	BTST	#0,D0		;Is it odd or even?
	BNE.S	CDM10		;Branch back if it is odd
	BSR	SPOUT		;Display a space between words

	ANDI.B	#$07,D0		;Is current address evenly divisible by
	BNE.S	CDM10		; 8?  Branch if not
	BSR	SPOUT		;Display 2 more spaces
	BSR	SPOUT

	MOVE.W	A1,D0		;Get current address
	ANDI.B	#$0F,D0		;Is it evenly divisible by 16?
	BNE.S	CDM10		;Branch if not

CDM30	MOVE.B	(A0)+,D0	;Display the ASCII values...
	ANDI.B	#$7F,D0		;Convert byte to ASCII
	CMPI.B	#$20,D0		;Is it a displayable character?
	BHS.S	CDM40		;Branch if yes
	MOVEQ	#'.',D0		;Else replace it with a dot
CDM40
	CMPI.B	#$7F,D0		;Is it a delete character?
	BNE.S	CDM50		;Branch if not
	MOVEQ	#'.',D0		;Else replace it with a dot
CDM50
	BSR	CHOUT		;Display ASCII character

	MOVE.W	A0,D0		;Put 2 spaces after the 8th byte
	ANDI.B	#$07,D0
	BNE.S	CDM30
	BSR	SPOUT
	BSR	SPOUT

	MOVE.W	A0,D0		;Put a CRLF after the 16th byte
	ANDI.B	#$0F,D0
	BNE.S	CDM30
	BSR	CRLF

	MOVEA.L	ADDRDIS,A1	;Is A1 >= Frame length + ADDRDIS?
	ADDA.L	SIZEDIS,A1
	CMPA.L	A1,A0
	BLO.S	CDM00		;Loop back if not
	MOVE.L	A0,ADDRDIS	;Else update default address and exit
	RTS

;----------------------------------------------------------------------
;DR command -- display all registers
;
CMD_DR	BSR.S	CMD_DD		;Display data registers
	BSR.S	CMD_DA		;Display address registers
	BSR.S	CMD_DP		;Display PC, etc.
	BRA.S	CMD_DB		;(PBRA) Display breakpoints

;----------------------------------------------------------------------
;DD command -- display data registers
;
CMD_DD	LEA	SAVD.L,A0		;Point to data register table
	MOVEQ	#'D',D0		;Get register letter
	BRA.S	CDR_		;(PBRA) Display registers

;----------------------------------------------------------------------
;DA command -- display address registers
;
CMD_DA	LEA	SAVA.L,A0		;Point to address register table
	MOVEQ	#'A',D0		;Get register letter
	BRA.S	CDR_		;(PBRA) Display registers

;----------------------------------------------------------------------
;DB command -- display breakpoints
;
CMD_DB	LEA	BRKATBL.L,A0	;Point to breakpoint addresses
	MOVEQ	#'B',D0		;Get register letter
				;(PFALL) CDR_ Display registers

;----------------------------------------------------------------------
;Local subroutine to display registers
; Inputs:
;	D0 = Contains character describing register type (D, A, or B)
;	A0 = Pointer to table of values for each register
; Destroys D0, D1 and A0.
; Format:
; D0:  01234567 01234567 01234567 01234567   01234567 01234567 ...
;
CDR_	BSR	CHOUT		;Display D, A, or B

	BSR	TEXT
	ASCII	'0: '
	DC.B	SPACE + $80

	MOVEQ	#7,D1
CDR10	MOVE.L	(A0)+,D0	;Get the register's value
	BSR	HEX8OUT		;Display it
	BSR	SPOUT		;Followed by a space

	CMP.B	#4,D1		;Display 2 more spaces before register 4
	BNE.S	CDR20		;Branch if not register 4
	BSR	SPOUT
	BSR	SPOUT

CDR20	DBF	D1,CDR10

	BRA	CRLF		;(PBRA) Move to next line and return

;----------------------------------------------------------------------
;DPc, DUsp, & DSr commands -- display program counter, user stack
; pointer, and status register.
; Registers D0-D2/A0 are destroyed.
;
CMD_DP
CMD_DU
CMD_DS	BSR	TEXT		;Display program counter
	ASCII	'PC: '
	DC.B	SPACE + $80
	MOVE.L	SAVPC,D0
	BSR	HEX8OUT

	BSR	TEXT		;Display user stack pointer
	ASCII	'   USP: '
	DC.B	SPACE + $80
	MOVE.L	SAVUSP,D0
	BSR	HEX8OUT

	BSR	TEXT		;Display status register
	ASCII	'   SR: '
	DC.B	SPACE + $80
	MOVE.L	SAVSR,D0
	BSR	HEX4OUT

	BSR	TEXT		;Show status register bits
	ASCII	' '
	DC.B	'(' + $80

	MOVE.W	D0,D1
	LEA	FLAGS.L,A0	;Point to list of flag names
	MOVEQ	#15,D2		;Display T if trace flag is set
	BSR.S	FLGOUT
	MOVEQ	#13,D2		;Display S if supervisor state
	BSR.S	FLGOUT

	MOVE.W	D1,D0		;Display interrupt level (0..7)
	LSR.W	#8,D0
	BSR	HEX1OUT

	MOVEQ	#4,D2		;Display status flags: XNZVC
CDS20	BSR.S	FLGOUT
	DBF	D2,CDS20

	BSR	TEXT
	DC.B	')', CR + $80
	RTS

;----------------------------------------------------------------------
;Local subroutine to display flag bits.
; Inputs:
;	D1 = Copy of status register
;	D2 = Bit (flag) in status register to display
;	A0 = Pointer to string of flag names
; D0 is destroyed.
;
FLGOUT	MOVE.B	(A0)+,D0	;Assume flag is set
	BTST	D2,D1		;Test bit in copy of status register
	BNE.S	FLO10		;Branch if flag is in fact set
	MOVEQ	#SPACE,D0	;Else substitute a space character
FLO10	BRA	CHOUT		;(PBRA) Output character and return

FLAGS	ASCII	'TSXNZVC'	;Status register flag names

;======================================================================
;SET COMMANDS
;----------------------------------------------------------------------
;Dispatch to routine to set memory and registers
CMD_S_	BSR	GETCH		;Get uppercase character into D0
	LEA	SCMDTBL.L,A0	;Point to command search table
	BRA	EXECUTE		;(PBRA) Call routine to handle command

;Set command table:
SCMDTBL	DC.W	'M'		;Set memory
	DC.W	CMD_SM-SCMDTBL
	DC.W	CR		;Set memory
	DC.W	CMD_SM-SCMDTBL
	DC.W	SPACE		;Set memory
	DC.W	CMD_SM-SCMDTBL
	DC.W	'R'		;Set all registers
	DC.W	CMD_SR-SCMDTBL
	DC.W	'D'		;Set data registers
	DC.W	CMD_SD-SCMDTBL
	DC.W	'A'		;Set address registers
	DC.W	CMD_SA-SCMDTBL
	DC.W	'P'		;Set program counter
	DC.W	CMD_SP-SCMDTBL
	DC.W	'U'		;Set user stack pointer
	DC.W	CMD_SU-SCMDTBL
	DC.W	'S'		;Set status register
	DC.W	CMD_SS-SCMDTBL
	DC.W	'B'		;Set breakpoints
	DC.W	CMD_SB-SCMDTBL
	DC.W	$00		;Mark end of table
	DC.W	CMD_BAD-SCMDTBL	;Routine to handle illegal commands

;----------------------------------------------------------------------
;SM command -- set memory
; Destroys registers D0 and A0
;
CMD_SM	CMPI.B	#CR,D0		;If last char input was a CR then
	BEQ.S	CSM10		; don't try to look for hex (hangs)

	BSR	HEXIN		;Get address
	BEQ.S	CSM10		;Branch if no address given
	MOVE.L	D0,ADDRSET	;Set newly specified address

CSM10	MOVEA.L	ADDRSET,A0	;Get address for start of display

	MOVE.L	A0,D0		;Display the address
	BSR	HEX8OUT
	BSR	TEXT		;Follow it with a colon and 2 spaces
	ASCII	': '
	DC.B	SPACE + $80

	MOVE.B	(A0)+,D0	;Display the byte at the address
	BSR	HEX2OUT
	MOVE.B	(A0)+,D0	;Display the next byte
	BSR	HEX2OUT

	BSR	TEXT		;Follow it with a...
	ASCII	' ='
	DC.B	SPACE + $80

	BSR	GETCH		;Get uppercase character into D0
	LEA	XCMDTBL.L,A0	;Point to command search table
	BSR	EXECUTE		;Call routine to handle command
	BRA.S	CSM10		;Loop until "." (or CTRL-C)

;Set-memory command table:
XCMDTBL	DC.W	CR		;Move to next location
	DC.W	CSMDOWN-XCMDTBL
	DC.W	DOWN
	DC.W	CSMDOWN-XCMDTBL
	DC.W	UP
	DC.W	CSMUP-XCMDTBL
	DC.W	'.'
	DC.W	CSMEXIT-XCMDTBL
	DC.W	$00		;End marker flag
	DC.W	CSMASCI-XCMDTBL	;Assume ASCII and change memory contents

;----------------------------------------------------------------------
;Return from CMD_SM (and SETREGS).
;
CSMEXIT	ADDQ.L	#4,SP		;Discard first return address
	RTS

;----------------------------------------------------------------------
;Move ahead (or down) one word in memory.
;
CSMDOWN	ADDQ.L	#2,ADDRSET
	RTS

;----------------------------------------------------------------------
;Move back (or up) one word in memory.
;
CSMUP	SUBQ.L	#2,ADDRSET
	RTS

;----------------------------------------------------------------------
;Get new value to store into current word
;
CSMASCI	MOVEM.L	D0/A0,-(SP)	;Save registers

	BSR	BACKUP		;Reread first hex digit
	BSR	HEXIN
	BNE.S	CSMA10		;Branch if no error

	BSR	TEXT
	ASCII	'HEX NUMBER EXPECTED'
	DC.B	BEL, CR + $80
	BSR	OPENI		;Flush the line buffer
	BRA.S	CSMA90		;Exit
CSMA10
	MOVEA.L	ADDRSET,A0	;Get current address
	MOVE.W	D0,(A0)+	;Store hex into word
	MOVE.L	A0,ADDRSET

CSMA90	MOVEM.L	(SP)+,D0/A0	;Restore registers
	RTS

;----------------------------------------------------------------------
;SD command -- set data registers
; Destroys registers D0 and D1
;
CMD_SR				;Also, SR command -- set all registers
CMD_SD	BSR	HEXIN		;Get register number
	ANDI.B	#$07,D0		;Force to legal range
	BRA.S	SETREGS		;Enter set-register code

;----------------------------------------------------------------------
;SA command -- set address registers
;
CMD_SA	BSR	HEXIN		;Get register number
	ANDI.B	#$07,D0		;Force to legal range
	ADDQ.L	#8,D0		;Add address register table offset
	BRA.S	SETREGS		;Enter set-register code

;----------------------------------------------------------------------
;SP command -- set value of program counter
;
CMD_SP	MOVEQ	#$10,D0		;Get offset to PC table entry
	BRA.S	SETREGS		;Enter set-register code

;----------------------------------------------------------------------
;SU command -- set user stack pointer
;
CMD_SU	MOVEQ	#$11,D0		;Get offset to US table entry
	BRA.S	SETREGS		;Enter set-register code

;----------------------------------------------------------------------
;SS command -- set value of status register
;
CMD_SS	MOVEQ	#$12,D0		;Get offset to SR table entry
	BRA.S	SETREGS		;Enter set-register code

;----------------------------------------------------------------------
;SB command -- set breakpoints
;
CMD_SB	BSR	HEXIN		;Get breakpoint number
	ANDI.B	#$07,D0		;Force to legal range
	ADDI.B	#$18,D0		;Add breakpoint table offset
				;Enter set-register code

;----------------------------------------------------------------------
;Set register values (D0-D7, A0-A7, PC, USP, SR, B0-B7).
; Inputs:
;	D0 = Register number (0..$12)
;
; Destroys registers D0, D1, and A1
;
SETREGS	BSR	OPENI		;Flush line to save confusion
	MOVE.L	D0,D1		;Save the register number in D1

SETR00	MOVE.L	D1,D0		;A1 = D1 * 4 + SAVD
	ASL.L	#2,D0
	ADD.L	#SAVD,D0
	MOVEA.L	D0,A1

	CMPI.B	#$08,D1		;Is it a data register?
	BGE.S	SETR10		;Branch if not
	MOVEQ	#'D',D0
	BRA.S	SHOREG
SETR10
	CMPI.B	#$10,D1
	BGE.S	SETR20
	MOVEQ	#'A',D0
	BRA.S	SHOREG
SETR20
	BNE.S	SETR23		;Branch if not PC
	BSR	TEXT		;Display program counter
	ASCII	'PC: '
	DC.B	SPACE + $80
	MOVE.L	(A1),D0		;Display the current value
	BSR	HEX8OUT
	BRA.S	SETR30
SETR23
	CMPI.B	#$11,D1		;Is it the USP?
	BNE.S	SETR26		;Branch if not
	BSR	TEXT		;Display user stack pointer
	ASCII	'USP: '
	DC.B	SPACE + $80
	MOVE.L	(A1),D0		;Display the current value
	BSR	HEX8OUT
	BRA.S	SETR30
SETR26
	CMPI.B	#$12,D1		;Is it the SR?
	BNE.S	SETR28		;Branch if not
	BSR	TEXT		;Display status register
	ASCII	'SR: '
	DC.B	SPACE + $80
	MOVE.L	(A1),D0		;Display the current value
	BSR	HEX4OUT
	BRA.S	SETR30
SETR28
	MOVEQ	#'B',D0		;It must be a breakpoint

SHOREG	BSR	CHOUT		;Display register letter

	MOVE.L	D1,D0		;Get register index
	ANDI.B	#$07,D0		;Mask to 0..7 range
	BSR	HEX1OUT

	BSR	TEXT		;Follow it with a colon and 2 spaces
	ASCII	': '
	DC.B	SPACE + $80

	MOVE.L	(A1),D0		;Display the current value
	BSR	HEX8OUT
SETR30
	BSR	TEXT		;Follow it with a...
	ASCII	' ='
	DC.B	SPACE + $80

	BSR	GETCH		;Get uppercase character into D0
	LEA	RCMDTBL.L,A0	;Point to command search table
	BSR	EXECUTE		;Call routine to handle command
	BRA	SETR00		;Loop until "." (or CTRL-C)

;Set-register command table:
RCMDTBL	DC.W	'.'
	DC.W	CSMEXIT-RCMDTBL
	DC.W	CR		;Move to next location
	DC.W	CSRDOWN-RCMDTBL
	DC.W	DOWN
	DC.W	CSRDOWN-RCMDTBL
	DC.W	UP
	DC.W	CSRUP-RCMDTBL
	DC.W	$00		;End marker flag
	DC.W	CSRASCI-RCMDTBL	;Assume ASCII and change memory contents

;----------------------------------------------------------------------
;Move ahead (or down) a register
;
CSRDOWN	ADDQ.B	#1,D1
	CMPI.B	#$1F,D1
	BLS.S	CSRD10
	MOVEQ	#0,D1
CSRD10	CMPI.B	#$13,D1
	BNE.S	CSRD20
	MOVEQ	#$18,D1
CSRD20	RTS

;----------------------------------------------------------------------
;Move back (or up) one register
;
CSRUP	SUBQ.B	#1,D1
	BGE.S	CSRU10
	MOVEQ	#$1F,D1
CSRU10	CMPI.B	#$17,D1
	BNE.S	CSRU20
	MOVEQ	#$12,D1
CSRU20	RTS

;----------------------------------------------------------------------
;Get new value to store into current register
; Inputs A1 = Pointer to the entry in the table for the current register
;
CSRASCI	MOVEM.L	D0,-(SP)	;Save registers

	BSR	BACKUP		;Reread first hex digit
	BSR	HEXIN
	BNE.S	CSRA10		;Branch if no error

	BSR	TEXT
	ASCII	'HEX NUMBER EXPECTED'
	DC.B	BEL, CR + $80
	BSR	OPENI		;Flush the line buffer
	BRA.S	CSRA90		;Exit
CSRA10
	MOVE.L	D0,(A1)		;Store hex into register save loc
	BSR.S	CSRDOWN		;Get terminator

CSRA90	MOVEM.L	(SP)+,D0	;Restore registers
	RTS

;======================================================================
;F command -- fill memory with pattern
; Three arguments must be given.
; FILL (START, END, PATTERN)
; The pattern argument is tested to determine if it is to be repeated
; every byte, word, or long. Word and long patterns have a couple of
; problems. Some word and long patterns are impossible, for example
; $0034 is interpreted as a byte, not a word. FILL (100, 200, 12345678)
; will fill up through location 203, not 200.
;
; Destroys registers D0, D1, and A0.
;
CMD_F	BSR	HEXIN		;Get START argument
	BEQ	CMD_BAD		;(PBRA) Must have 3 hex values
	MOVEA.L	D0,A0		;Initialize pointer to START

	BSR	BACKUP		;Check for CR
	BSR	HEXIN		;Get END argument
	BEQ	CMD_BAD		;(PBRA) Check for 3 hex values
	MOVE.L	D0,D1		;Save END in D1
	CMP.L	A0,D1		;If END < START then error
	BLO	CMD_BAD		;(PBRA)

	BSR	BACKUP		;Check for CR
	BSR	HEXIN		;Get PATTERN argument
	BEQ	CMD_BAD		;(PBRA) Check for 3 hex values
	CMPI.L	#$000000FF,D0	;Is it only one byte?
	BHI.S	FIL20		;Branch if not
FIL10	MOVE.B	D0,(A0)+	;Fill START through END with byte
	CMPA.L	D1,A0		; PATTERN
	BLS.S	FIL10
	BRA.S	FIL90		;Exit
FIL20
	CMPI.L	#$0000FFFF,D0	;Is it one word?
	BHI.S	FIL40		;Branch if not -- it's a long
FIL30	MOVE.W	D0,(A0)+	;Fill START through END with word
	CMPA.L	D1,A0		; PATTERN
	BLS.S	FIL30
	BRA.S	FIL90		;Exit
FIL40
FIL50	MOVE.L	D0,(A0)+	;Fill START through END with long
	CMPA.L	D1,A0		; PATTERN
	BLS.S	FIL50
FIL90	RTS

;----------------------------------------------------------------------
;M command -- move a block of memory
; The block begins at the first argument, START, and ends at the second
; argument, END. The beginning of the block is moved to the address
; given by the third argument, TO.
;	MOVE (START, END, TO)
;
; Destroys registers D0, D1, A0, and A1.
;
CMD_M	BSR	HEXIN		;Get START argument
	BEQ	CMD_BAD		;(PBRA) Must have 3 hex values
	MOVEA.L	D0,A0		;Point A0 to START

	BSR	BACKUP		;Check for CR
	BSR	HEXIN		;Get END argument
	BEQ	CMD_BAD		;(PBRA) Check for 3 hex values
	CMP.L	A0,D0		;If END < START then error
	BLO	CMD_BAD		;(PBRA)
	SUB.L	A0,D0		;LENGTH:= END - START + 1
	ADDQ.L	#1,D0
	MOVE.L	D0,D1		;Save LENGTH in D1

	BSR	BACKUP		;Check for CR
	BSR	HEXIN		;Get TO argument
	BEQ	CMD_BAD		;(PBRA) Check for 3 hex values
	MOVEA.L	D0,A1

	MOVE.W	D1,D0		;Get low 16 bits of LENGTH into D0 and
	SWAP	D1		; high 16 bits into D1

	CMPA.L	A0,A1		;If TO > START (i.e: moving forward in
	BEQ.S	MOVE90		; memory) then don't branch, else
	BLO.S	MOVE20		; enter loop checking for LENGTH = 0
				; (this is actually unnecessary)
	ADDA.L	D0,A1		;Move starting at the end of the block
	ADDA.L	D0,A0		;Add LENGTH to TO and START
	BRA.S	MOVE40		;Enter loop checking for LENGTH = 0

MOVE10	MOVE.B	(A0)+,(A1)+	;Move block backward, pointers forward
MOVE20	DBF	D0,MOVE10	;Loop unitl D0 = -1
	DBF	D1,MOVE10	; and also D1 = -1
	BRA.S	MOVE90		;Exit

MOVE30	MOVE.B	-(A0),-(A1)	;Move block forward, pointers backward
MOVE40	DBF	D0,MOVE30	;Loop unitl D0 = -1
	DBF	D1,MOVE30	; and also D1 = -1
MOVE90	RTS

;----------------------------------------------------------------------
;Z command -- set memory display frame size.
;
CMD_Z	BSR	HEXIN		;Get size argument
	BEQ	CMD_BAD		;(PBRA) We must have it, else error
	MOVE.L	D0,SIZEDIS	;Set new memory display size
	RTS

;======================================================================
;EXECUTE COMMANDS
;----------------------------------------------------------------------
;T command -- trace program
;
CMD_T	BSET	#7,SAVSR+2	;Set trace bit in status register

	BSR	HEXIN		;Read entry point
	BEQ.S	TR10
	MOVE.L	D0,SAVPC	;Update in save PC
TR10
	CLR.B	BPFLAG		;Clear execute thru breakpoint flag
	BRA	GO50		;Enter common code

;----------------------------------------------------------------------
;G command -- start or continue program
;
CMD_G	BSR	HEXIN		;Read entry point
	BEQ.S	GO10
	MOVE.L	D0,SAVPC	;Update in save PC
GO10
	CLR.B	BPFLAG		;Clear execute thru breakpoint flag
	BCLR	#7,SAVSR+2	;Clear trace mode

;Handle breakpoints. This inserts the breakpoint TRAP #$F instructions.
; If we are about to execute a breakpoint, the TRAP is not inserted.
; Instead, the trace flag is set along with the BPFLAG which allows the
; instruction at the breakpoint to be executed. The trace flag causes
; control to be returned to the debugger which clears the trace flag
; and resumes execution of the user's program. The BPFLAG is used to
; distinguish this one-instruction trace from trace mode.

	LEA	BRKATBL.L,A1	;Point to breakpoint table
	LEA	BRKITBL.L,A2
	MOVEQ	#7,D0		;Set loop counter (7..0)

GO20	MOVEA.L	(A1)+,A0	;Get breakpoint address
	MOVE.W	(A0),(A2)+	;Save instruction in table
	CMPA.L	SAVPC,A0	;Are we about to execute a breakpoint?
	BNE.S	GO30		;Branch if not
	BSET	#7,SAVSR+2	;Set trace bit
	ST	BPFLAG		;Set flag to continue thru breakpoint
	BRA.S	GO40

GO30	MOVE.W	#$4E4F,(A0)	;Replace instruction with TRAP #15
GO40	DBF	D0,GO20		;Loop for all breakpoints

;Enter from "continue past breakpoint" code
GO50	MOVEA.L	SAVUSP,A0	;Restore user stack pointer
	MOVE	A0,USP
	MOVEA.L	SAVA+28,SP	;Restore supervisor stack pointer
	MOVE.L	SAVPC,-(SP)	;Stack saved PC
	MOVE.W	SAVSR+2,-(SP)	;Stack saved SR
	MOVEM.L	SAVD,D0-D7/A0-A6  ;Restore user's registers
	ST	GOFLAG		;Indicate that the user's prog is going
	RTE			;Restore PC & SR and resume execution

;======================================================================
;EXCEPTION HANDLERS
;----------------------------------------------------------------------
;Jump table for exception handlers.
; The exception vectors point to the various entries in this table.
; There is an entry for each type of exception. BSR.S pushes a return
; address which is used to determine which type of exception occurred.
;
BE_EXCP	BSR.S	EXCP10		;2 Bus Error
AE_EXCP	BSR.S	EXCP10		;3 Address Error
II_EXCP	BSR.S	EXCP20		;4 Illegal Instruction
ZD_EXCP	BSR.S	EXCP20		;5 Zero Divide
CI_EXCP	BSR.S	EXCP20		;6 CHK Instruction
TV_EXCP	BSR.S	EXCP20		;7 TRAPV Instruction
PV_EXCP	BSR.S	EXCP20		;8 Privilege Violation
TR_EXCP	BSR.S	EXCP20		;9 Trace
LA_EXCP	BSR.S	EXCP20		;10 Line 1010 Emulator
LF_EXCP	BSR.S	EXCP20		;11 Line 1111 Emulator
SI_EXCP	BSR.S	EXCP20		;12 Spurious Interrupt
IN_EXCP	BSR.S	EXCP20		;13 Unassigned Interrupt
TI_EXCP	BSR.S	EXCP20		;14 Unassigned TRAP Instruction
BP_EXCP	BSR.S	EXCP20		;15 TRAP #$F (Breakpoint)
UA_EXCP	BSR.S	EXCP20		;16 Unassigned exception

;----------------------------------------------------------------------
;Exception handler for bus errors and address errors (group 0).
; After D0 and A0 are pushed, the supervisor stack looks like this:
;
;	SSP -->	 0  D0 Hi
;		 2  D0 Lo
;		 4  A0 Hi
;		 6  A0 Lo
;		 8  Return Address Hi
;		10  Return Address Lo
;		12  Function Codes, etc.
;		14  Access Address Hi
;		16  Access Address Lo
;		18  Instruction Register
;		20  Status Register
;		22  PC Hi
;		24  PC Lo
;
;
EXCP10	MOVEM.L	D0/A0,-(SP)	;Save registers

	MOVE.L	14(SP),ACCESS	;Save access address and function
	MOVE.W	12(SP),FUNCODE	; codes to be displayed later

	MOVEA.L	22(SP),A0	;Get PC
	MOVE.W	18(SP),D0	;Get instruction register

	CMP.W	-(A0),D0	;Since the PC value varies a few words,
	BEQ.S	EXCP12		; look for the instruction in the code
	CMP.W	-(A0),D0	; that matches the instruction register
	BEQ.S	EXCP12		; and set PC to point to it.
	CMP.W	-(A0),D0
	BEQ.S	EXCP12
	CMP.W	-(A0),D0
	BEQ.S	EXCP12
	SUBQ	#2,A0
EXCP12
	MOVE.L	A0,22(SP)	;Restore corrected PC

	MOVEM.L	(SP)+,D0/A0	;Restore registers
	MOVE.L	(SP),8(SP)	;Move return address ahead and adjust
	ADDQ	#8,SP		; stack to look like a group 1 or 2
				; execption, and fall into EXCP20

;----------------------------------------------------------------------
;Exception handler for group 1 and 2 exceptions.
; Upon entering, the supervisor stack looks like this:
;
;	SSP -->  0  Return Address Hi (exception type)
;		 2  Return Address Lo (exception type)
;		 4  Status Register
;		 6  PC Hi
;		 8  PC Lo
;
;
EXCP20	TST.B	GOFLAG		;Don't save registers unless we were
	BEQ.S	EXCP25		; running the user's program
	MOVEM.L	D0-D7/A0-A7,SAVD  ;Save all user registers
EXCP25
	MOVEA.L	(SP)+,A1	;Get address of routine which called
	SUBQ.L	#2,A1		; this exception handler and save in A1

	MOVE.W	(SP)+,D0	;Get SR and PC from the stack
	MOVEA.L	(SP)+,A0

	TST.B	GOFLAG		;Don't save registers unless we were
	BEQ.S	EXCP28		; running the user's program
	MOVE.W	D0,SAVSR+2	;Save SR
	MOVE.L	A0,SAVPC	;Save PC
	MOVE	USP,A0		;Get user stack pointer
	MOVE.L	A0,SAVUSP	;Save USP
	MOVE.L	SP,SAVA+28	;Save SP
	CLR.B	GOFLAG		;We're no longer running the user prog
	BRA.S	EXCP29

EXCP28	BSR	CRLF		;New line if internal exception
EXCP29

;Restore all breakpoints with their original instructions
	LEA	BRKATBL.L,A0	;Point A0 to breakpoint table
	LEA	BRKITBL.L,A2
	MOVEQ	#7,D0		;Loop for 8 breakpoints (0..7)
EXCP30	MOVEA.L	(A0)+,A3	;Get the address of the breakpoint
	MOVE.W	(A2)+,(A3)	;Restore the original instruction
	DBF	D0,EXCP30	;Loop for all breakpoints

	LEA	TR_EXCP.L,A0	;Is this a trace exception?
	CMPA.L	A0,A1
	BNE.S	EXCP50		;Branch if not

;Check for continue through breakpoint (i.e. a one-instruction trace)?
	TST.B	BPFLAG		;Is this a continue through breakpoint?
	BEQ.S	EXCP40		;Branch if not

	CLR.B	BPFLAG
	BCLR	#7,SAVSR+2	;Turn trace bit off

;Insert the missing breakpoint. (We must insert them all, because we
; don't know how many words were in the last instruction.)
	LEA	BRKATBL.L,A1	;Point to breakpoint table
	LEA	BRKITBL.L,A2
	MOVEQ	#7,D0		;Set loop counter (7..0)
EXCP35	MOVEA.L	(A1)+,A0	;Get breakpoint address
	MOVE.W	(A0),(A2)+	;Save instruction in table
	MOVE.W	#$4E4F,(A0)	;Replace instruction with TRAP #15
	DBF	D0,EXCP35	;Loop for all breakpoints
	BRA	GO50		;Resume execution of user's program

;Handle normal trace exception
EXCP40	BSR	TEXTABO		;Display trace message
	BSR	CMD_DR		;Display registers

	LEA	CMDTBL.L,A0	;Point to command table
	PEA	GETCMD.L	;Push ret adr of com loop
	BSR	TEXT		;Display prompt for special trace mode
	DC.B	'T', '>' + $80
	BSR	GETCH		;Get next chr
	CMP.B	#CR,D0		;Carriage return?
	BNE	EXECUTE		;(PBNE) No, search for command
	BSET	#7,SAVSR+2	;Set trace bit in status register
	BRA	TR10

;Check if this is a breakpoint
EXCP50	LEA	BP_EXCP.L,A0	;Is it a TRAP #$F?
	CMPA.L	A0,A1
	BNE.S	EXCP60		;Branch if not

;Is the TRAP #$F actually one of our breakpoints?
;If an entry in BRKATBL = SAVPC -2 then it is.
	MOVEA.L	SAVPC,A0	;Get PC and adjust it to point to the
	SUBQ.L	#2,A0		; TRAP #$F instruction
	LEA	BRKATBL.L,A2	;Point to breakpoint address table
	MOVEQ	#7,D1		;Loop for 8 breakpoints
EXCP53	CMPA.L	(A2)+,A0	;Does this entry match the address?
	DBEQ	D1,EXCP53	;Decrement and branch if not
	BNE.S	EXCP59		;Branch if not

	MOVE.L	A0,SAVPC	;Fix PC to point to address of TRAP
	BSR	TEXTABO		;Display breakpoint message
	BSR	CMD_DR		;Display registers
	BRA	GETCMD		;Enter main loop of debugger

EXCP59	LEA	TI_EXCP.L,A1	;Change "BREAKPOINT" to "TRAP"

;Check for interrupt exception
EXCP60	LEA	IN_EXCP.L,A0	;Interrupt exception?
	CMPA.L	A0,A1
	BNE.S	EXCP70		;Branch if not

	BSR	TEXT
	ASCII	'INTERRUPT AT LEVEL'
	DC.B	BEL, SPACE + $80
	MOVE	SR,D0		;Get interrupt level from SR
	LSR.W	#8,D0
	BSR	HEX1OUT
	BSR	TEXTABO		;Display message
	BRA	GETCMD		;Enter main loop of debugger

;Check for TRAP instructions
EXCP70	LEA	TI_EXCP.L,A0	;Trap instruction?
	CMPA.L	A0,A1
	BNE.S	EXCP80		;Branch if not

	BSR	TEXT
	ASCII	'TRAP #'
	DC.B	BEL, '$' + $80
	MOVEA.L	SAVPC,A0	;Get trap level from instruction
	MOVE.W	-(A0),D0
	BSR	HEX1OUT

	BSR	TEXT
	ASCII	' INSTRUCTIO'
	DC.B	'N' + $80
	BSR	TEXTABO		;Display message
	BRA	GETCMD		;Enter main loop of debugger

;Check for bus and address error exceptions
EXCP80	LEA	BE_EXCP.L,A0	;Bus error?
	CMPA.L	A0,A1
	BEQ.S	EXCP85		;Branch if it is
	LEA	AE_EXCP.L,A0	;Address error?
	CMPA.L	A0,A1
	BNE.S	EXCP90		;Branch if not
EXCP85
	BSR.S	BEEP
	BSR.S	TEXTABO
	BSR	TEXT
	ASCII	'ACCESS ADDRESS:'
	DC.B	SPACE + $80
	MOVE.L	ACCESS,D0
	BSR	HEX8OUT

	BSR	TEXT
	DC.B	CR
	ASCII	'FUNCTION CODE:'
	DC.B	SPACE + $80
	MOVE.W	FUNCODE,D0
	BSR	HEX2OUT
	BSR	CRLF
	BRA	GETCMD		;Enter main loop of debugger

;Display message for remaining exceptions
EXCP90	BSR.S	BEEP
	BSR	TEXTABO		;Display exception error message
	BRA	GETCMD		;Enter main loop of debugger

;----------------------------------------------------------------------
;Make beep noise
;
BEEP	MOVEQ	#BEL,D0		;Beep
	BRA	CHOUT		;(PBRA)

;----------------------------------------------------------------------
;Display an exception message.
; Inputs:
;	A1 = Address of exception handler
; Destroys D0, A0, A1, and A2
;
TEXTABO	LEA	BE_EXCP.L,A0	;Get base address of jump table
	SUBA.L	A0,A1		;Get word offset for current exception
	MOVE.L	A1,D0
	LEA	ABOTBL.L,A2	;Get pointer to abort table base
	MOVEA.W	0(A2,D0.L),A0	;Get offset to base of message
	ADDA.L	A2,A0		;Add base to point to string
	BSR	TEXTA0		;(PBRA) Display message and return

	BSR	TEXT
	ASCII	'   PC: '
	DC.B	SPACE + $80
	MOVEA.L	SAVPC,A0	;Get current PC
	MOVE.L	A0,D0		;Display it
	BSR	HEX8OUT
	BSR	TEXT
	ASCII	': '
	DC.B	SPACE + $80

	MOVE.W	(A0),D0		;Display instruction at PC
	BSR	HEX4OUT		; (this is the next instruction to be
	BSR	CRLF		; executed)

	RTS

;Table of pointers to exception messages.
ABOTBL	DC.W	AB2-ABOTBL
	DC.W	AB3-ABOTBL
	DC.W	AB4-ABOTBL
	DC.W	AB5-ABOTBL
	DC.W	AB6-ABOTBL
	DC.W	AB7-ABOTBL
	DC.W	AB8-ABOTBL
	DC.W	AB9-ABOTBL
	DC.W	AB10-ABOTBL
	DC.W	AB11-ABOTBL
	DC.W	AB12-ABOTBL
	DC.W	AB13-ABOTBL
	DC.W	AB14-ABOTBL
	DC.W	AB15-ABOTBL
	DC.W	AB16-ABOTBL

AB2	ASCII	'BUS ERROR'
	DC.B	SPACE + $80
AB3	ASCII	'ADDRESS ERROR'
	DC.B	SPACE + $80
AB4	ASCII	'ILLEGAL INSTRUCTION'
	DC.B	SPACE + $80
AB5	ASCII	'ZERO DIVIDE'
	DC.B	SPACE + $80
AB6	ASCII	'CHK INSTRUCTION'
	DC.B	SPACE + $80
AB7	ASCII	'TRAPV INSTRUCTION'
	DC.B	SPACE + $80
AB8	ASCII	'PRIVILEGE VIOLATION'
	DC.B	SPACE + $80
AB9	ASCII	'TRACE'
	DC.B	SPACE + $80
AB10	ASCII	'LINE 1010 EMULATOR'
	DC.B	SPACE + $80
AB11	ASCII	'LINE 1111 EMULATOR'
	DC.B	SPACE + $80
AB12	ASCII	'SPURIOUS INTERRUPT'
	DC.B	SPACE + $80
AB13	DC.B	SPACE + $80
AB14	DC.B	SPACE + $80
AB15	ASCII	'BREAKPOINT'
	DC.B	SPACE + $80
AB16	ASCII	'UNASSIGNED EXCEPTION'
	DC.B	SPACE + $80

;======================================================================
;SUBROUTINES
;----------------------------------------------------------------------
;Search the command table pointed to by A0 for the character in D0.
; This calls the routine which corresponds to the character in D0.
; The calling technique is a bit tricky. When the called command routine
; returns, it returns to the location where this search routine was
; called.
;
EXECUTE	MOVE.L	D1,-(SP)	;Save D1 (MOVEM won't work here)
	MOVE.L	A0,-(SP)	; and A0
	BRA.S	SRCH20		;Enter search loop

SRCH10	CMP.B	D1,D0		;Does entry match character in D0?
	BEQ.S	SRCH30		;Branch if it does -- found
	ADDQ.W	#2,A0		;Skip rest of the entry
SRCH20	MOVE.W	(A0)+,D1	;Get character from table
	BNE.S	SRCH10		;Loop if not end of table

SRCH30	MOVEA.W (A0)+,A0
	ADDA.L	(SP),A0		;Add table base addr, the original A0

	MOVE.L	4(SP),D1	;Restore D1
	MOVE.L	A0,4(SP)	;Put routine address on stack
	MOVEA.L	(SP)+,A0	;Restore A0
	RTS			;Call routine

;======================================================================
;INPUT ROUTINES
;----------------------------------------------------------------------
;Get character from keyboard buffer, convert it to upper case, and
; return it in D0.
;
GETCH	BSR	CHIN

	CMPI.B	#'a',D0		;Shift character in D0 to uppercase
	BLO.S	UPC90
	CMPI.B	#'z',D0
	BHI.S	UPC90
	ANDI.B	#$DF,D0		;Convert to uppercase

UPC90	RTS

;-----------------------------------------------------------------------
;This routine inputs hex ASCII digits from the terminal and converts
; them to a 32-bit binary value which is returned in D0. 
;
; If a hex value is successfully read, the Z flag is returned cleared
; (NE status). This routine skips any initial non-hex characters, but
; always returns when it finds a carriage return (CR). If a CR is read
; before finding any hex digit, this returns with the Z flag set (EQ
; status) and D0 = 0. A Hex value is normally terminated by a non-hex
; character, such as a space or CR.
;
; If the hex value is more than 8 digits long, only the first 8 digits
; will be read in, thus allowing additional calls to handle all of the
; digits.
;
; The hex characters A - F may be lowercase (a - f).
;
; Register Usage:
;	D0 = Digit
;	D1 = Accumulated value
;	D2 = Digit counter
;
;
HEXIN	MOVEM.L	D1-D2,-(SP)	;Save registers
	MOVEQ	#0,D1		;Clear result register
	MOVEQ	#7,D2		;Init digit counter (7 down through 0)

HEXI00	BSR	CHIN		;Get character
	CMPI.B	#CR,D0		;Is it a carriage return?
	BEQ.S	HEXI90		;Branch if yes -- exit
	CMPI.B	#UP,D0		;Is it an up arrow?
	BEQ.S	HEXI90		;Branch if yes -- exit
	CMPI.B	#DOWN,D0	;Is it a down arrow?
	BEQ.S	HEXI90		;Branch if yes -- exit

	CMPI.B	#'0',D0		;Is character in range 0 thru 9?
	BLO.S	HEXI40		;Branch if not
	CMPI.B	#'9',D0
	BHI.S	HEXI20		;(May be A-F)
	SUBI.B	#'0',D0		;Convert ASCII to binary value
	BRA.S	HEXI30		;Go combine with other digits

HEXI20	ANDI.B	#$DF,D0		;Force to uppercase
	CMPI.B	#'A',D0		;Is character in range A through F?
	BLO.S	HEXI40		;Branch if not -- it's not hex
	CMPI.B	#'F',D0
	BHI.S	HEXI40
	SUBI.B	#'A'-10,D0	;Convert ASCII to binary value

HEXI30	ASL.L	#4,D1		;Multiply current value by 16
	ADD.B	D0,D1		;Add new digit
	DBF	D2,HEXI00	;Exit if we have 8 digits

HEXI40	CMPI.B	#7,D2		;Did we find a hex digit?
	BEQ.S	HEXI00		;Branch if not -- keep trying

HEXI90	MOVE.L	D1,D0		;Return the hex value in D0
	CMPI.B	#7,D2		;Set Z flag if no digits were read
	MOVEM.L	(SP)+,D1-D2	;Restore registers
	RTS

;======================================================================
;OUTPUT ROUTINES
;----------------------------------------------------------------------
;Output a space character.
;
SPOUT	MOVE.L	D0,-(SP)	;Save D0

	MOVEQ	#SPACE,D0	;Load a space character
	BSR	CHOUT		;Output it

	MOVE.L	(SP)+,D0	;Restore D0
	RTS

;----------------------------------------------------------------------
;Output a text string. The string immediately follows the call to this
; routine. I.e:
;	BSR	TEXT		;Call to this subroutine
;	ASCII	'STRIN'		;String to output
;	DC.B	'G' + $80	;Last character must have MSB set
;
TEXT	MOVEM.L	A0/A1,-(SP)	;Save registers

	MOVEA.L	8(SP),A0	;Get the "return address" which points
	MOVEA.L	A0,A1		; to the text string, and get a copy
TXT10	TST.B	(A1)+		;Scan for the end-of-string (MSB set)
	BPL.S	TXT10		;Loop unitl the last character is found

	ADDQ.L	#1,A1		;Set up the real return address
	MOVE.L	A1,8(SP)	;Make sure it is on the word boundary
	ANDI.B	#$FE,11(SP)	; following the string

	BSR.S	TEXTA0		;Output the string pointed to by A0

	MOVEM.L	(SP)+,A0/A1	;Restore registers
	RTS

;----------------------------------------------------------------------
;Output a text string pointed to by A0.
; The string is terminated with a character having its MSB set.
;
TEXTA0	MOVEM.L	D0/A0,-(SP)	;Save registers

	BRA.S	TXTA20		;Enter loop

TXTA10	BSR	CHOUT		;Output D0
TXTA20	MOVE.B	(A0)+,D0	;Get char from string
	BPL.S	TXTA10		;Loop unitl the last character

	ANDI.B	#$7F,D0		;Clear MSB
	BSR	CHOUT		;Output D0

	MOVEM.L	(SP)+,D0/A0	;Restore registers
	RTS

;----------------------------------------------------------------------
;Output D0 in ASCII hex (8 digits).
;
HEX8OUT	SWAP	D0		;Get high word
	BSR.S	HEX4OUT		;Output it
	SWAP	D0		;(PFALL) get low word back

;----------------------------------------------------------------------
;Output D0 in ASCII hex (4 digits)
;
HEX4OUT	ROR.W	#8,D0		;Move high byte down (and save low byte)
	BSR.S	HEX2OUT		;Output it
	ROR.W	#8,D0		;(PFALL) get low byte

;----------------------------------------------------------------------
;Output D0 in ASCII hex (2 digits)
;
HEX2OUT	ROR.B	#4,D0		;Move high nybble down (save low nybble)
	BSR.S	HEX1OUT		;Output it
	ROR.B	#4,D0		;(PFALL) get low nybble

;----------------------------------------------------------------------
;Output D0 in ASCII hex (1 digit)
;
HEX1OUT	MOVE.B	D0,-(SP)	;Save D0

	ANDI.B	#$0F,D0		;Work with low nybble only
	CMPI.B	#10,D0
	BLO.S	H1O10
	ADDQ.B	#7,D0
H1O10	ADDI.B	#'0',D0		;Convert to ASCII
	BSR	CHOUT		;Output digit

	MOVE.B	(SP)+,D0	;Restore D0
	RTS

;======================================================================
;CONSOLE HANDLER ROUTINES
;
OPEN	BSR.S	OPENI
	BRA.S	OPENO

;
CRLF	MOVE.L	D0,-(SP)
	MOVEQ	#CR,D0
	BSR.S	CHOUT
	MOVE.L	(SP)+,D0
	RTS

;
BACKUP	ST	BKFLAG
	RTS

;
OPENI	CLR.B	BKFLAG
	MOVEM.L	A6,-(SP)
	CLR.B	DEVICE
	MOVEA.W	#0,A6
	JSR	VDEVHAN
	MOVEA.L	(SP)+,A6
	RTS

;
OPENO	MOVEM.L	A6,-(SP)
	CLR.B	DEVICE
	MOVEA.W	#4,A6
	JSR	VDEVHAN
	MOVEA.L	(SP)+,A6
	RTS

;
CHIN	TST.B	BKFLAG
	BEQ.S	CHIN20
	CLR.B	BKFLAG
	MOVEQ	#0,D0
	MOVE.B	BKCHAR,D0
	BRA.S	CHIN90
CHIN20
	MOVEM.L	A6,-(SP)
	CLR.B	DEVICE
	MOVEA.W	#8,A6
	JSR	VDEVHAN
	MOVE.B	D0,BKCHAR
	MOVEA.L	(SP)+,A6
CHIN90	RTS

;
CHOUT	MOVE.L	A6,-(SP)
	CLR.B	DEVICE
	MOVEA.W	#12,A6
	JSR	VDEVHAN
	MOVEA.L	(SP)+,A6
	RTS


;----------------------------------------------------------------------
;APEX SYSPAG PARAMETERS

END	EQU	@

	ORG	USRMEM
	DC.L	BASE

	ORG	PROSIZ
	DC.L	[END-BASE+255]/$100

	END
----------
;APEX SYSPAG PARAMETERS

END	EQU	@

	ORG	USRMEM
	DC.L	BA