A.N.A.L.O.G. ISSUE 18 / MAY 1984 / PAGE 78

HBUG

Hudson’s Debugging Utility

16K Disk

by Tom Hudson

HBUG is a simple debugging utility designed for readers of ANALOG’s Boot Camp column who do not own the Atari Assembler Editor cartridge. The program’s syntax is the same as DEBUG, allowing Boot Camp readers to execute the examples and observe the results.

Why HBUG?

Why write another debug package? I’m sure quite a few readers own the MAC/65 assembler from Optimized Systems Software. This is a fine package (I use it myself, and recommend it highly). As you may know, MAC/65 comes with its own debug package, BUG/65. BUG/65 is a very powerful debugging tool, but it has a couple of shortcomings.

First, the only way to stop a program that is running in an infinite loop is to press SYSTEM RESET. As a result, there is no way to determine register contents or where the program was looping.

Second, many of Boot Camp’s illustrative programs rely on the use of the BRK instruction to stop execution at selected locations. BUG/65 doesn’t recognize the BRK instruction, and any attempt to perform a BRK locks up the system. BUG/65 allows you to set breakpoints, but these are generally cumbersome to use.

HBUG overcomes these shortcomings, making life easier for those new to assembly language.

Typing it in.

Before you start typing anything in, take a look at the listings accompanying this article.

Listing 1 is the main data and data checking routine, written in Atari BASIC. This program will create a file on your disk called HBUG.COM.

Listing 2 is the assembly source code for HBUG, written with OSS’s MAC/65. You don’t have to type this in to use HBUG, but the adventuresome types out there (you know who you are) may like the assembly typing experience.

Follow the instructions below to create the HBUG.COM file.

  1. Type Listing 1 into your Atari and verify your typing with D:CHECK2 (see page 30).
  2. Type RUN and press RETURN. The program will begin checking the data lines, printing the line numbers as it goes. You will be alerted if the program finds any problems. Fix any incorrect lines and re-RUN the program as necessary until all errors are eliminated.
  3. When all DATA lines are correct, you will be prompted to INSERT DISK, PRESS RETURN. Place a disk in drive #1 and press RETURN. The message WRITING FILE will appear, and the computer will create the HBUG.COM file, printing each line number as it goes. When the READY prompt appears, you’re ready to use HBUG. Make sure the BASIC program has been saved under a different filename before continuing.

Usually, you’ll want to load the object code of the program to be executed before loading HBUG. If the program is already in memory, simply skip ahead to the HBUG loading instructions.

If you want to load the object code in Atari DOS, go to the DOS menu screen and type:

L RETURN
Filename.OBJ RETURN

If you’re using OS/A+, type:

LOA Filename.OBJ RETURN

To load HBUG with Atari DOS, go to the DOS menu screen and type:

L RETURN
HBUG.COM RETURN

HBUG will load and run automatically.

To load HBUG with OS/A+, go to the OS/A+ input screen and type:

HBUG RETURN

HBUG will load and run automatically.

Up and running.

When running, HBUG supports 6 commands, which are a subset of the Atari assembler editor DEBUG program’s commands. These commands are:

DOS     (RETURN TO DOS)
DR      (DISPLAY REGISTER CONTENTS)
CR<     (CHANGE REGISTER CONTENTS)
D       (DISPLAY MEMORY CONTENTS)
C       (CHANGE MEMORY CONTENTS)
G       (EXECUTE AT ADDRESS)

These commands are the most important ones, as far as we’re concerned. I would have liked to include the “disassemble memory” command, but it would have increased the size of the program considerably. The use of these commands is described below.

If any invalid commands are entered, HBUG will “beep” at you and show the line you entered with the invalid character highlighted in inverse video.

DOS.

This command is simple — it transfers control to the disk operating system. Once there, you can perform any function allowed by DOS. If you’re using OS/A+, typing RUN restarts HBUG. If you’re using Atari DOS, you’ll have to reload HBUG to start it up again.

Change registers.

You can set the registers (A, X, Y, Processor status [P], and Stack pointer [S]) to any values you like before executing a test program. Be careful about changing the P and S registers, since invalid values placed here can cause a system crash.

The format of the Change Registers command is:

       CR< Ra, Rx, Ry, Rp, Rs

Where Ra = Accumulator
      Rx = K register
      Ry = Y register
      Rp = Processor status register
      Rs = stack pointer

The values entered for the registers must be hexadecimal, from 0 through FF.

Figure 1 shows several forms of the CR command and their effect on the registers.

CR<1F,6E
Changes the Accumulator to $1F and the X register to $6E.
CR<,,,4C
Changes the P register to $40. All other registers are unchanged.
Figure 1.

You can see that by inserting commas, you can leave certain registers unchanged while altering others.

Display registers.

This command is the companion of the CR command. Its format is:

DR

Whenever you want to see what the current settings of the user registers, simply type DR and press return. The computer will display something like this:

A=03 X=32 Y=81 P=33 S=E2

Display memory.

HBUG allows you to display the contents of any memory byte or group of bytes. The formats of this command are:

FORMAT 1: Dxxxx

FORMAT 2: D

FORMAT 3: Dxxxx, yyyy

Format 1 displays eight bytes of memory starting at address xxxx.

Format 2 displays the eight bytes of memory starting at the last specified address plus 8.

Format 3 displays the memory block from address xxxx to yyyy. The display can be paused by pressing CTRL-1 and stopped by pressing the BREAK key.

This command differs from the cartridge command in that the ATASCII characters corresponding to the displayed bytes are shown to the right of the hex values.

Change memory.

You can change any memory byte in system RAM with the Change Memory command. Its format is:

Cxxxx< byte1, byte2, ... , byte n

This command changes the memory starting at address xxxx to the hex values following the “<” symbol. Like the CR command, the C command allows you to skip memory locations by using commas to indicate skipped bytes. Figure 2 shows several examples of the Change memory command.

C 5000<1F,45,DE
Change location $5000 to $1F, $5001 to $45, and $5002 to $DE.
C 600<20,,,,F6
Change location $0600 to $20 and location $0604 to $F6. Leave all other locations as is.
Figure 2.

Use care when changing memory bytes! Be sure you’re using the address you want, because careless changes could wipe out vital system data, causing a fatal lockup. Also, take care not to change any locations from $2000-26FF, since this is where HBUG is located.

Execute user program.

This command is what HBUG is all about. It allows you to execute assembly code you have placed in memory. Its format is:

Gxxxx

When the G command is entered, the computer (1) changes the 6502 registers to the values contained in the user registers (see CR and DR), and (2) jumps to the address specified by xxxx.

The user program will continue to execute until it is stopped by the BREAK key or tries to execute a 6502 BRK instruction. If either of these events occurs, the program will be interrupted, HBUG will place the 6502 register contents in the user registers and HBUG will take control. The location where the program was executing and the user registers will be displayed in the following format:

40F5    A=1B X=6F Y=2D P=04 S=EA

HBUG will perform as advertised unless the user program violates the HBUG rules, outlined below.

HBUG no-nos.

  1. The user program should not change any memory locations from $2000-26FF. This is where HBUG is located, and any changes to this memory could send HBUG, your program, and the system off to never-never land.
  2. The user program should not use the deferred vertical blank vector or shut off vertical blank interrupts. HBUG uses deferred vertical blank to detect the BREAK key during user program execution. Don’t steal this vector unless you want to see HBUG crippled. If you must use vertical blank interrupts, try using the immediate vertical blank.
  3. Don’t touch the VBREAK vector ($0206-0207). These locations are used to detect the execution of a 6502 BRK instruction, and alteration of these bytes will once again cripple HBUG.

If you follow these instructions and avoid careless alteration of HBUG’s memory, HBUG should help you debug the programs that BUG/65 couldn’t.


BASIC Listing.

10 REM *** HBUG ***
20 DATA 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,10,11,12,13,14,15
30 DIM DAT$(91),HEX(22):FOR X=0 TO 22:READ N:HEX(X)=N:NEXT X:LINE=990:RESTORE 1000:TRAP 110:? "CHECKING DATA"
40 LINE=LINE+10:? "LINE:";LINE:READ DAT$:IF LEN(DAT$)<>90 THEN 160
50 DATLIN=PEEK(183)+PEEK(184)*256:IF DATLIN<>LINE THEN ? "LINE ";LINE;" MISSING!":END 
60 FOR X=1 TO 89 STEP 2:D1=ASC(DAT$(X,X))-48:D2=ASC(DAT$(X+1,X+1))-48:BYTE=HEX(D1)*16+HEX(D2)
70 IF PASS=2 THEN PUT #1,BYTE:NEXT X:READ CHKSUM:GOTO 40
80 TOTAL=TOTAL+BYTE:IF TOTAL>999 THEN TOTAL=TOTAL-1000
90 NEXT X:READ CHKSUM:IF TOTAL=CHKSUM THEN 40
100 GOTO 160
110 IF PEEK(195)<>6 THEN 160
120 IF PASS=2 THEN PUT #1,224:PUT #1,2:PUT #1,225:PUT #1,2:PUT #1,0:PUT #1,32:CLOSE #1:END 
130 ? "INSERT DISK, PRESS RETURN";:DIM IN$(1):INPUT IN$:OPEN #1,8,0,"D:HBUG.COM"
140 PUT #1,255:PUT #1,255:PUT #1,0:PUT #1,32:PUT #1,159:PUT #1,37
150 ? :? "WRITING FILE":PASS=2:LINE=990:RESTORE 1000:TRAP 110:GOTO 40
160 ? "BAD DATA: LINE ";LINE:END 
1000 DATA A9008D0ED48D8325BA8E7925AD06028D1326AD07028D1426A9248D0702A9478D0602A224A07DA907205CE4A940,79
1010 DATA 8D0ED420B82320132420EF23A200A9878D4403A9258D4503A9058D4203A97F8D48038E49032056E430E2A200BD,272
1020 DATA 8725C99BF003E810F6E000F0CC8E8225A200BD8725C920D0148AA8B98825998725C8CC8225D0F4CE82254C6C20,99
1030 DATA E8EC8225D0DFAD8225D0034C3320A9008D7E25AC7E25B9D4248D7F25BED324A000B98725DDC824D01BC8E8EC7F,862
1040 DATA 25D0F1AD7E250AAABDDA248D8025BDDB248D81256C8025EE7E25AD7E25C907D0C520F6234C3320B98725C99BF0,412
1050 DATA 034CD520A2E4A062A907205CE4AD13268D0602AD14268D07026C0A00086829EF482820B823AD7A25203024AD7D,291
1060 DATA 258D0025AD7C258DFF24AD7B25203024AD7D258DFE24AD7C258DFD24205121201324A2FDA0242017244C3320B9,530
1070 DATA 8725C99BF0034CD520205121A205A0252017244C3320A000B97525203024BE6025AD7C259D0525AD7D259D0625,388
1080 DATA C8C005D0E660A204BD75259D0726CA10F7A200A9008D0D268D0C26B98725C99BF00AC92CF00620BD214C8321AD,330
1090 DATA 0C26F006AD0D269D0726B98725C99BD00EA204BD07269D7525CA10F74C3320E8C8E005D0C14CD5208E8525A20F,194
1100 DATA DD6525F006CA10F84CEF210E0D260E0D260E0D260E0D268A0D0D268D0D26EE0C26AD0C26C903B005AE8525C860,803
1110 DATA 68684CD520B98725C99BD01DA5D418690885D4A5D5690085D5A9008D1126A9078D10264C50224CD520206123AE,555
1120 DATA 1226F0F5AE0E2686D4AE0F2686D5C99BF0DAC92CD0E3C8206123AE1226F0DAC99BD0D6AD0E2638E5D48D1026AD,656
1130 DATA 0F26E5D58D1126A920A22C9D3225CA10FAA91BA20E9D4F25CACA10F9A5D4203024AD7D258D3525AD7C258D3425,403
1140 DATA A5D5203024AD7D258D3325AD7C258D3225A000B1D4C99BF006BE2A259D4F25203024BE2225AD7D259D3825AD7C,975
1150 DATA 259D3725C8AD102638E9018D1026AD1126E9008D11261006200C244C3320C008D0C4200C2410034C3320A5D418,354
1160 DATA 690885D4A5D5690085D54C5022B98725C93CF016206123AE1226F04BC93CD047AE0E2686D4AE0F2686D5C8A200,468
1170 DATA A9008D0D268D0C26B98725C99BF00AC92CF00620BD214C0523AD0C26F00D8C86258AA8AD0D2691D4AC8625B987,181
1180 DATA 25C99BD0034C3320E8C84CFD224CD520206123C99BD01DAD1226F018AE79259AAD782548AE7625AC7725AD7525,182
1190 DATA EE8325286C0E264CD520A9008D0E268D0F268D1226B98725C99BF008C92CF004C93CD00160A20FDD6525F008CA,864
1200 DATA 10F868684CD5200E0E262E0F260E0E262E0F260E0E262E0F260E0E262E0F268A0D0E268D0E26EE1226AD1226C9,437
1210 DATA 05B0D2C84C6C23D820D923A200A9E68D4403A9248D4503A9038D4203A90C8D4A038E4B032056E460A2078E8425,861
1220 DATA A90C9D42032056E4CE8425AE842510F060A2EAA0244C1724A980198725998725A2EFA024201724A287A0254C17,511
1230 DATA 24A232A0254C1724A2E9A0248E44038C4503A200A9098D4203A97F8D48038E49032056E46048290FAABD65258D,595
1240 DATA 7D25684A4A4A4AAABD65258D7C2560AD8325D0026840D8A9008D83258E76258C7725688D7525688D78256838E9,252
1250 DATA 028D7A2568E9008D7B258E8525BA8E7925AE8525584CFD20AD8325F043A511D03FA9808511A9008D8325BABD01,185
1260 DATA 018D7725BD02018D7625BD03018D7525BD04018D7825BD05018D7A25BD06018D7B258A1869068D7925A9209D06,879
1270 DATA 01A9FD9D05014C62E4444F53445243523C44434700030508090A0BDB203A216E21F421DD223A23453A9B9B4842,393
1280 DATA 55479BFD494E505554204552524F52219B202020202020202020202020413D202020583D202020593D20202050,148
1290 DATA 3D202020533D20209B000306090C0F121501030507090B0D0F2020202020202020202020202020202020202020,456
1300 DATA 202020202020202020202020202020202020202020202020209B060B10151A3031323334353637383941424344,282
1310 DATA 454600000000000000000000000000000000000000000000000000000000000000000000000000000000000000,421

CHECKSUM DATA
(See p. 30)

10 DATA 445,957,808,431,727,198,599,553,272,701,611,112,74,561,36,7085
160 DATA 165,899,670,50,200,953,769,788,545,908,10,936,728,156,4,7781
1140 DATA 914,480,888,896,938,985,945,703,671,650,801,931,906,613,145,11466
1290 DATA 871,748,340,1959

Assembly Listing.

	.OPT	NOLIST
	*=	$2000
;
;-----------------------------
;HUDSON'S DEBUG PROGRAM (HBUG)
;-----------------------------
;
DOSVEC	=	$0A	;DOS run address
BRKKEY	=	$11	;BREAK key status
STACK	=	$0100	;hardware stack
VBREAK	=	$0206	;BRK inst vector
RUNAD	=	$02E0	;prog run addr
ICCOM	=	$0342	;CIO command
ICBADR	=	$0344	;CIO buffer address
ICBLEN	=	$0348	;CIO buffer length
ICAUX1	=	$034A	;CIO aux. byte 1
ICAUX2	=	$034B	;CIO aux. byte 2
NMIEN	=	$D40E	;interrupt enable
CIOV	=	$E456	;CIO entry point
SETVBV	=	$E45C	;VBLANK setup
XITVBV	=	$E462	;VBLANK exit
;
;Page zero usage
;
CML	=	$D4	;my two-byte
CMH	=	$D5	;address work area
;
;-------------------
;Program entry point
;-------------------
;
HBUG
	LDA	#$00	;turn off...
	STA	NMIEN	;interrupts
	STA	EXEC	;and execute flag
	TSX		;get stack pointer
	STX	USERS	;put in user area
	LDA	VBREAK	;save old BRK
	STA	BRKSVL	;vector in my
	LDA	VBREAK+1	;work area
	STA	BRKSVH	;for later
	LDA	# >BRKHAN	;now point to
	STA	VBREAK+1	;my BRK inst.
	LDA	# <BRKHAN	;routine
	STA	VBREAK
	LDX	# >VBI	;set up
	LDY	# <VBI	;vertical
	LDA	#7	;blank
	JSR	SETVBV	;interrupt
	LDA	#$40	;turn on the
	STA	NMIEN	;VBLANK interrupt
	JSR	NEWSCR	;open GR.0 screen
INPUT	JSR	PRTCR	;carriage return
	JSR	PROMPT	;print HBUG prompt
INPT2	LDX	#0
	LDA	# <MYBUFF	;point to
	STA	ICBADR	;my input
	LDA	# >MYBUFF	;buffer
	STA	ICBADR+1
	LDA	#5	;GET RECORD command
	STA	ICCOM
	LDA	#$7F	;my buffer's
	STA	ICBLEN	;length
	STX	ICBLEN+1
	JSR	CIOV	;get input!
	BMI	INPT2	;go back if error
;
;This section 'squishes' all the
;spaces out of the input line.
;
	LDX	#0	;first find end
FINDBE	LDA	MYBUFF,X	;of line
	CMP	#$9B	;CR?
	BEQ	GOTEND	;yes!
	INX		;no, next char.
	BPL	FINDBE	;keep looking!
GOTEND	CPX	#0	;CR first char?
	BEQ	INPUT	;yes, try again.
	STX	ENDPTR	;save end index
	LDX	#0	;start w/1st char
SQUISH	LDA	MYBUFF,X	;get the character
	CMP	#32	;space?
	BNE	NOSQSH	;no!
	TXA		;move index
	TAY		;to Y register
SQSHLP	LDA	MYBUFF+1,Y	;shift all
	STA	MYBUFF,Y	;characters
	INY		;back to remove
	CPY	ENDPTR	;the space.
	BNE	SQSHLP
	DEC	ENDPTR	;line 1 shorter
	JMP	SQUISH	;keep squishing!
NOSQSH	INX		;next char
	CPX	ENDPTR	;end?
	BNE	SQUISH	;no, keep going!
	LDA	ENDPTR	;was line all spaces?
	BNE	GETCMD	;no, get command
	JMP	INPUT	;get another input!
;
;Now find command & process it
;
GETCMD	LDA	#0	;start with 1st
	STA	CMDPTR	;command.
CHKCMD	LDY	CMDPTR	;get command pointer
	LDA	CMDST+1,Y	;get index of
	STA	CEND	;command text end
	LDX	CMDST,Y	;and start.
	LDY	#0	;point to 1st char
CMDCMP	LDA	MYBUFF,Y	;is buffer char
	CMP	CMDTXT,X	;= command char?
	BNE	NOTCMD	;no!
	INY		;yes, try next char
	INX		;next command char
	CPX	CEND	;end of command?
	BNE	CMDCMP	;no, keep comparing!
	LDA	CMDPTR	;yes, mult command
	ASL	A	;index by 2 to point
	TAX		;into jump table
	LDA	CMDADR,X	;get command routine
	STA	CMDJMP	;address low byte
	LDA	CMDADR+1,X	;and high byte
	STA	CMDJMP+1	;and save.
	JMP	(CMDJMP)	;jump to routine!
NOTCMD	INC	CMDPTR	;try next command
	LDA	CMDPTR	;get pointer
	CMP	#7	;more commands?
	BNE	CHKCMD	;yes!
GOTERR	JSR	ERRMSG	;oops! bad command!
	JMP	INPUT	;get another input
;
;This section handles the DOS
;command.  It shuts off the HBUG
;VBLANK and BRK vectors and JMPs
;to DOS.
;
GODOS	LDA	MYBUFF,Y	;get next char
	CMP	#$9B	;CR?
	BEQ	DOSOK	;yes, go to DOS
	JMP	GOTERR	;no, invalid command!
DOSOK	LDX	# >XITVBV	;point VBLANK
	LDY	# <XITVBV	;back to the
	LDA	#7	;system exit
	JSR	SETVBV	;point.
	LDA	BRKSVL	;restore old
	STA	VBREAK	;system
	LDA	BRKSVH	;BRK vector from
	STA	VBREAK+1	;save area
	JMP	(DOSVEC)	;go to DOS!
;
;Show regs after BREAK key or
;BRK instruction.
;
SHOBRK	PHP		;get processor
	PLA		;status in A,
	AND	#$EF	;turn off BRK flag
	PHA		;and put back in
	PLP		;status register!
	JSR	NEWSCR	;re-open screen
	LDA	USRPCL	;get prog counter low
	JSR	BINHEX	;convert to hex
	LDA	HEX2	;get low digit
	STA	BRK4	;put in line
	LDA	HEX1	;get high digit
	STA	BRK3	;put in line, too
	LDA	USRPCH	;get PC high
	JSR	BINHEX	;convert to hex
	LDA	HEX2	;put low digit
	STA	BRK2	;in line
	LDA	HEX1	;and high digit
	STA	BRK1	;in line.
	JSR	SETREG	;set rest of line
	JSR	PRTCR	;carriage return
	LDX	# <BRKTXT	;point to
	LDY	# >BRKTXT	;BREAK text
	JSR	PRINT	;print it
	JMP	INPUT	;and get input
;
;Display registers (DR)
;
SHOREG	LDA	MYBUFF,Y	;is next char
	CMP	#$9B	;a CR?
	BEQ	DROKAY	;yes, it's OK!
	JMP	GOTERR	;otherwise ERROR
DROKAY	JSR	SETREG	;get registers
	LDX	# <DRTXT	;point to the
	LDY	# >DRTXT	;DR text
	JSR	PRINT	;print it
	JMP	INPUT	;and get input
;
;Set up register disp area
;
SETREG	LDY	#0	;start w/user byte 0
SETLP	LDA	USER,Y	;get data
	JSR	BINHEX	;convert to hex
	LDX	DRPTR,Y	;get its position
	LDA	HEX1	;get first digit
	STA	DRTXT,X	;put in line
	LDA	HEX2	;get second digit
	STA	DRTXT+1,X	;put in line
	INY		;next byte
	CPY	#5	;done 5?
	BNE	SETLP	;not yet!
	RTS		;all done!
;
;Change registers (CR)
;
CHGREG	LDX	#4	;first copy user
INITMP	LDA	USER,X	;registers
	STA	TMPUSR,X	;to temporary
	DEX		;hold area
	BPL	INITMP
	LDX	#0	;1st user byte
CRSTRT	LDA	#0	;zero out...
	STA	BHOLD	;byte hold
	STA	HDIG	;and digit count
CRLOOK	LDA	MYBUFF,Y	;get input char
	CMP	#$9B	;CR?
	BEQ	STOTRY	;yes, all done.
	CMP	#$2C	;comma?
	BEQ	STOTRY	;yes, try store
	JSR	CTOBIN	;convert it to binary
	JMP	CRLOOK	;and do next one
STOTRY	LDA	HDIG	;any digits?
	BEQ	NXTCCK	;no, skip it!
	LDA	BHOLD	;yes, save the byte
	STA	TMPUSR,X	;in temporary table
NXTCCK	LDA	MYBUFF,Y	;was this
	CMP	#$9B	;a CR?
	BNE	NXTCHR	;no, keep going.
	LDX	#4	;otherwise,
SETUSR	LDA	TMPUSR,X	;copy the
	STA	USER,X	;temporary table
	DEX		;back to the
	BPL	SETUSR	;user registers
	JMP	INPUT	;get another line
NXTCHR	INX		;next user register
	INY		;next character
	CPX	#5	;done 5 regs?
	BNE	CRSTRT	;no, loop back.
	JMP	GOTERR	;UH-OH! too many!
;
;Convert char. to binary #
;
CTOBIN	STX	XHOLD	;save X register
	LDX	#$0F	;set scan index
HDSCAN	CMP	HEXDIG,X	;compare ASCII
	BEQ	GOTHD	;got it!
	DEX		;next char
	BPL	HDSCAN	;scan all 16
	JMP	CBERR	;not in table!
GOTHD	ASL	BHOLD	;shift current #
	ASL	BHOLD	;left 4 times to
	ASL	BHOLD	;multiply it by
	ASL	BHOLD	;16
	TXA		;get this digit
	ORA	BHOLD	;and add it to
	STA	BHOLD	;the hold area.
	INC	HDIG	;one more digit
	LDA	HDIG	;are there more
	CMP	#3	;than 2?
	BCS	CBERR	;yes--too big!
	LDX	XHOLD	;restore X reg
	INY		;next buffer char
	RTS		;and exit
CBERR	PLA		;remove return addr
	PLA		;from stack
	JMP	GOTERR	;and do error routine
;
;Display memory contents
;
SHOMEM	LDA	MYBUFF,Y	;is character...
	CMP	#$9B	;a CR?
	BNE	GETSAD	;no, get address.
	LDA	CML	;get last address
	CLC		;and add 8 to it
	ADC	#8	;since no address
	STA	CML	;was specified
	LDA	CMH
	ADC	#0
	STA	CMH
SHOW8	LDA	#0	;show only 8
	STA	COUNTH	;bytes
	LDA	#7
	STA	COUNTL
	JMP	SHOWLN	;go show 'em!
SMERR	JMP	GOTERR	;jump to err routine
GETSAD	JSR	GET4	;get 4-byte address
	LDX	G4DIGS	;any digits found?
	BEQ	SMERR	;no!
	LDX	ADL	;save address
	STX	CML	;in page zero
	LDX	ADH	;work area
	STX	CMH
	CMP	#$9B	;CR after address?
	BEQ	SHOW8	;yes, show 8 bytes.
	CMP	#$2C	;comma?
	BNE	SMERR	;no, bad command
	INY		;next char
	JSR	GET4	;get end address
	LDX	G4DIGS	;got any digits?
	BEQ	SMERR	;no!
	CMP	#$9B	;CR after end addr?
	BNE	SMERR	;no!
	LDA	ADL	;now subtract
	SEC		;end address
	SBC	CML	;from start
	STA	COUNTL	;to get number
	LDA	ADH	;of bytes to
	SBC	CMH	;display.
	STA	COUNTH
SHOWLN	LDA	#32	;clear out
	LDX	#44	;display line
CLRML	STA	SHOM1,X
	DEX
	BPL	CLRML
	LDA	#$1B	;and set up ESC
	LDX	#14	;characters
SETESC	STA	ASCII,X	;in the ASCII
	DEX		;display area
	DEX
	BPL	SETESC
	LDA	CML	;convert the
	JSR	BINHEX	;current address
	LDA	HEX2	;to ascii hex
	STA	SHOM4	;characters
	LDA	HEX1	;and put in
	STA	SHOM3	;the memory
	LDA	CMH	;display line.
	JSR	BINHEX	;this is done
	LDA	HEX2	;2 times, for
	STA	SHOM2	;the low and high
	LDA	HEX1	;bytes of the
	STA	SHOM1	;address
	LDY	#0	;start showing!
BILDLN	LDA	(CML),Y	;get mem byte
	CMP	#$9B	;CR?
	BEQ	NO9B	;yes, don't show it!
	LDX	ASCPOS,Y	;put in ASCII
	STA	ASCII,X	;display area
NO9B	JSR	BINHEX	;convert byte to hex
	LDX	SMPOS,Y	;get position
	LDA	HEX2	;get low char
	STA	SMDATA+1,X	;and store
	LDA	HEX1	;get high char
	STA	SMDATA,X	;and store.
	INY		;next byte
	LDA	COUNTL	;now decrement
	SEC		;the byte count
	SBC	#1	;by 1
	STA	COUNTL
	LDA	COUNTH
	SBC	#0
	STA	COUNTH
	BPL	MORESM	;more? yes!
	JSR	PDATA	;no more, print line
	JMP	INPUT	;and get next command
MORESM	CPY	#8	;done 8 bytes?
	BNE	BILDLN	;no, loop back
	JSR	PDATA	;done 8, print 'em
	BPL	NOSTOP	;no BREAK key
	JMP	INPUT	;get next command
NOSTOP	LDA	CML	;increment display
	CLC		;address by 8
	ADC	#8
	STA	CML
	LDA	CMH
	ADC	#0
	STA	CMH
	JMP	SHOWLN	;and loop back.
;
;Change memory contents (Cnnnn<)
;
CHGMEM	LDA	MYBUFF,Y	;get char
	CMP	#$3C	;'<'?
	BEQ	CMDFLT	;yes, default address
	JSR	GET4	;get the address
	LDX	G4DIGS	;got any digits?
	BEQ	CMERR	;no!
	CMP	#$3C	;next char '<'?
	BNE	CMERR	;no!
	LDX	ADL	;save the
	STX	CML	;change memory
	LDX	ADH	;address on
	STX	CMH	;page zero.
CMDFLT	INY		;next buffer char
	LDX	#0	;1st memory byte
CMSTRT	LDA	#0	;zero out...
	STA	BHOLD	;byte hold
	STA	HDIG	;and digit count
CMLOOK	LDA	MYBUFF,Y	;get char
	CMP	#$9B	;CR?
	BEQ	CMTRY	;yes, all done!
	CMP	#$2C	;comma?
	BEQ	CMTRY	;yes, store last byte
	JSR	CTOBIN	;convert char to binary
	JMP	CMLOOK	;and get next
CMTRY	LDA	HDIG	;any digits?
	BEQ	NEXTCM	;no! don't store
	STY	YHOLD	;save Y register
	TXA		;move X...
	TAY		;to Y
	LDA	BHOLD	;get byte to store
	STA	(CML),Y	;and store it!
	LDY	YHOLD	;get Y back
NEXTCM	LDA	MYBUFF,Y	;get last char
	CMP	#$9B	;was it CR?
	BNE	NXTLOC	;no, continue
	JMP	INPUT	;all done!
NXTLOC	INX		;next mem byte
	INY		;next input char
	JMP	CMSTRT	;loop back!
CMERR	JMP	GOTERR	;go to error routine
;
;Execute at address (Gnnnn)
;
EXECUT	JSR	GET4	;get the run address
	CMP	#$9B	;is that all?
	BNE	EXERR	;no!
	LDA	G4DIGS	;got any digits?
	BEQ	EXERR	;no!
	LDX	USERS	;put user stack
	TXS		;pointer in S
	LDA	USERP	;put user status
	PHA		;on stack
	LDX	USERX	;set user X reg
	LDY	USERY	;and user Y reg
	LDA	USERA	;and accumulator
	INC	EXEC	;set execute flag
	PLP		;get status off stack
	JMP	(ADL)	;go to run address!
EXERR	JMP	GOTERR	;go to error routine
;
;Get 4-character address
;
GET4	LDA	#0	;zero out...
	STA	ADL	;address low byte
	STA	ADH	;address high byte
	STA	G4DIGS	;digit count
G4LOOP	LDA	MYBUFF,Y	;get char
	CMP	#$9B	;CR?
	BEQ	G4END	;yes, all done!
	CMP	#$2C	;comma?
	BEQ	G4END	;yes, all done!
	CMP	#$3C	;'<'?
	BNE	TESTIT	;no, check digit
G4END	RTS		;exit!
TESTIT	LDX	#$0F	;set hex digit pointer
G4SCAN	CMP	HEXDIG,X	;is it this char?
	BEQ	GOTG4D	;yes!
	DEX		;try next hex digit
	BPL	G4SCAN	;loop if more.
G4ERR	PLA		;discard return
	PLA		;address,
	JMP	GOTERR	;show error.
GOTG4D	ASL	ADL	;this code
	ROL	ADH	;shifts the
	ASL	ADL	;current address
	ROL	ADH	;left 4 bits
	ASL	ADL	;in order to
	ROL	ADH	;multiply it
	ASL	ADL	;by 16.
	ROL	ADH
	TXA		;get this digit
	ORA	ADL	;and add it to
	STA	ADL	;the address.
	INC	G4DIGS	;one more digit
	LDA	G4DIGS	;how many total?
	CMP	#5	;more than 4?
	BCS	G4ERR	;yes! error!
	INY		;ok, do next character
	JMP	G4LOOP	;and loop back.
;
;Set up new graphics 0 screen
;
NEWSCR	CLD
	JSR	NOIOCB	;close all IOCB's
;
;now open screen!
;
	LDX	#0
	LDA	# <EADR	;'E:' filename
	STA	ICBADR
	LDA	# >EADR
	STA	ICBADR+1
	LDA	#3	;OPEN command
	STA	ICCOM
	LDA	#12	;I/O
	STA	ICAUX1
	STX	ICAUX2	;zero aux byte
	JSR	CIOV	;open it!
	RTS		;and return.
;
;Close all IOCB's
;
NOIOCB	LDX	#7	;first close
	STX	SAVEX	;all IOCB's.
CLOOP	LDA	#12	;CLOSE command
	STA	ICCOM,X
	JSR	CIOV	;close it!
	DEC	SAVEX	;next IOCB
	LDX	SAVEX	;more IOCB's?
	BPL	CLOOP	;yes!
	RTS		;all done!
;
;Show HBUG prompt
;
PROMPT	LDX	# <HMSG	;point to
	LDY	# >HMSG	;HBUG message
	JMP	PRINT	;and print it!
;
;Show error message
;
ERRMSG	LDA	#$80	;set high bit
	ORA	MYBUFF,Y	;inverse the
	STA	MYBUFF,Y	;invalid character
	LDX	# <ERRTXT	;point to
	LDY	# >ERRTXT	;error message
	JSR	PRINT	;print it
	LDX	# <MYBUFF	;point to
	LDY	# >MYBUFF	;input buffer
	JMP	PRINT	;print it, too!
;
;Print memory display line
;
PDATA	LDX	# <SHOM1	;point to memory
	LDY	# >SHOM1	;display line
	JMP	PRINT	;print it!
;
;Print carriage return only
;
PRTCR	LDX	# <CR	;point to CR and
	LDY	# >CR	;fall thru to print
;
;General-use print routine
;
PRINT	STX	ICBADR	;save print area lo
	STY	ICBADR+1	;and high
	LDX	#0	;zero X reg.
	LDA	#9	;PUT RECORD command
	STA	ICCOM
	LDA	#$7F	;set up...
	STA	ICBLEN	;buffer length
	STX	ICBLEN+1
	JSR	CIOV	;print it!
	RTS		;and exit.
;
;Binary-to-hex converter
;
BINHEX	PHA		;save byte
	AND	#$0F	;get low 4 bits
	TAX		;put in index
	LDA	HEXDIG,X	;lookup hex
	STA	HEX2	;and save
	PLA		;get byte again
	LSR	A	;shift right
	LSR	A	;4 times
	LSR	A	;to get
	LSR	A	;high 4 bits
	TAX		;put in index
	LDA	HEXDIG,X	;lookup hex
	STA	HEX1	;and save
	RTS		;all done!
;
;Handle 6502 BRK interrupt
;
BRKHAN	LDA	EXEC	;executing?
	BNE	SAVREG	;yes!
	PLA		;no, restore accum.
	RTI		;and return from int.
SAVREG	CLD		;no decimal mode!
	LDA	#0	;reset the
	STA	EXEC	;executing flag
	STX	USERX	;save X
	STY	USERY	;and Y
	PLA
	STA	USERA	;and accumulator
	PLA
	STA	USERP	;and status reg.
	PLA
	SEC		;now get program
	SBC	#2	;counter from stack
	STA	USRPCL	;and subtract 2
	PLA		;to get BREAK
	SBC	#0	;address.
	STA	USRPCH
	STX	XHOLD	;save X reg.
	TSX		;now store stack pntr
	STX	USERS	;in the user area.
	LDX	XHOLD	;restore X
	CLI		;clear interrupt
	JMP	SHOBRK	;and show break info
;
;Handle BREAK key in VBI
;
VBI	LDA	EXEC	;executing?
	BEQ	NOBKEY	;no!
	LDA	BRKKEY	;BREAK pressed?
	BNE	NOBKEY	;no!
	LDA	#$80	;reset BREAK
	STA	BRKKEY	;press flag
	LDA	#0	;and
	STA	EXEC	;execute flag
	TSX		;get stack pntr
	LDA	STACK+1,X	;get Y register
	STA	USERY	;and save it
	LDA	STACK+2,X	;get X register
	STA	USERX	;and save it
	LDA	STACK+3,X	;get accumulator
	STA	USERA	;and save it
	LDA	STACK+4,X	;get status register
	STA	USERP	;and save it
	LDA	STACK+5,X	;get program
	STA	USRPCL	;counter
	LDA	STACK+6,X	;and
	STA	USRPCH	;store it!
	TXA		;move stack pntr
	CLC		;to A, add 6 to
	ADC	#6	;get true value
	STA	USERS	;and save it!
	LDA	# >SHOBRK	;change return
	STA	STACK+6,X	;address to
	LDA	# <SHOBRK	;go to SHOBRK
	STA	STACK+5,X	;after VBLANK.
NOBKEY	JMP	XITVBV	;all done!
;
;Data areas
;
;Command text & pointers
;
CMDTXT	.BYTE	"DOSDRCR<DCG"
CMDST	.BYTE	0,3,5,8,9,10,11
CMDADR	.WORD	GODOS
	.WORD	SHOREG
	.WORD	CHGREG
	.WORD	SHOMEM
	.WORD	CHGMEM
	.WORD	EXECUT
;
;Miscellaneous text
;
EADR	.BYTE	"E:",$9B
CR	.BYTE	$9B
HMSG	.BYTE	"HBUG",$9B
ERRTXT	.BYTE	$FD,"INPUT ERROR!",$9B
BRKTXT
BRK1	.BYTE	32
BRK2	.BYTE	32
BRK3	.BYTE	32
BRK4	.BYTE	32
	.BYTE	"    "
DRTXT	.BYTE	"    A=   X=   Y=   "
	.BYTE	"P=   S=  ",$9B
SMPOS	.BYTE	0,3,6,9,12,15,18,21
ASCPOS	.BYTE	1,3,5,7,9,11,13,15
SHOM1	.BYTE	32
SHOM2	.BYTE	32
SHOM3	.BYTE	32
SHOM4	.BYTE	32
FILL1	.BYTE	32
SMDATA	.BYTE	"            "
	.BYTE	"           "
FILL2	.BYTE	32
ASCII	.BYTE	"                "
	.BYTE	$9B
DRPTR	.BYTE	6,11,16,21,26
HEXDIG	.BYTE	"0123456789ABCDEF"
;
;Misc. memory usage
;
USER	;		user registers
USERA	.BYTE	0	;accumulator
USERX	.BYTE	0	;X register
USERY	.BYTE	0	;Y register
USERP	.BYTE	0	;status register
USERS	.BYTE	0	;stack pointer
USRPCL	*=	*+1	;program counter low
USRPCH	*=	*+1	;program counter high
HEX1	*=	*+1	;hex digit 1
HEX2	*=	*+1	;hex digit 2
CMDPTR	*=	*+1	;command pointer
CEND	*=	*+1	;command end index
CMDJMP	*=	*+2	;command jump addr
ENDPTR	*=	*+1	;end of input pointer
EXEC	*=	*+1	;user prog execute flag
SAVEX	*=	*+1	;X reg. hold area
XHOLD	*=	*+1	;another X reg hold
YHOLD	*=	*+1	;Y reg hold
MYBUFF	*=	*+128	;input buffer
TMPUSR	*=	*+5	;user reg temp storage
HDIG	*=	*+1	;hex digit count
BHOLD	*=	*+1	;byte hold area
ADL	*=	*+1	;address low
ADH	*=	*+1	;address high
COUNTL	*=	*+1	;disp mem count,
COUNTH	*=	*+1	;low and high
G4DIGS	*=	*+1	;4-digit count
BRKSVL	*=	*+1	;BRK interrupt...
BRKSVH	*=	*+1	;vector save
;
;Tell computer where to run HBUG
;
	*=	RUNAD
	.WORD	HBUG
;
;That's all, folks!
;
	.END