
;  This code is hereby released into the public domain and may be freely
; copied and distributed by provided the following notice is included:
;
;	Copyright (c) 1988 by Axonn Corporation
;		      101 West Robert E. Lee Blvd #402
;		      New Orleans, LA 70124
;
;  All of the included code is exerpted from a functional and tested product
; developed by Axonn Corp., a research and development laboratory.
; Nevertheless, the code is supplied strictly as a public service and no
; responsibility for bugs can be assumed.

; OVERVIEW:
;   During the development of an 87c751 based product at Axonn, we chose to
; use the product itself as the ideal testbed for development since,
; unfortunately, the microwatt hardware and real time aspects of our product
; made the use of an emulator inconvenient and this debugger resulted.
;
;   The debugger has a few simple commands that allow user subroutines to be
; called, and their results examined.  RAM locations can be examined and
; modified.  While this falls rather short of a full-blown debugger, we found
; it quite convenient compared to logic analyzers and oscilliscopes for
; real time work.  Those who desire more features and have memory to spare
; should find it easy to add them.

; DESCRIPTION:
;   The debugger utilizes two spare bits as a software serial port that
; allows the 87c751 to be connected to and debugged from a serial terminal (we
; used an AT-clone and Procomm).  Only the minimum possible 87c751 resources
; were used so the debugger is quite compact and RAM efficient, naturally at
; the expense of fancy features.  All port references are hard-coded because
; my assembler required it - sorry.
;
;  Commands: C-all <address>	calls the specified routine at the specified
;				16 bit address.  The Acc, SP, and R0-R2 are
;				printed on return from the routine.
;
;	     M-odify <address>	modify consecutive RAM locations, each in turn
;				until ANY invalid input is encountered (yes,
;				this can be irritating).  <Address> is an
;				8-bit RAM address.
;
;	     D-ump <address>	prints the contents of 16 RAM locations
;				starting with the specified 8-bit address.
;
;	     V-ersion		prints out the version of software in use.  We
;				use this to track versions of our application
;				software after the security bits have been
;				programmed.
;
;  Serial Port Parameters:  The serial port sends 8 bit data at 2400 baud with
;			    one start bit, one stop bit, and no parity.  All
;			    timing is accomplished with software timing loops
;			    that assume a 16 Mhz crystal.  Adequate information
;			    is provided to allow simple accomodation of other
;			    clock speeds.
;
;  General:  No special hardware features of the 87c751 have been used, which
;	    should make the debugger portable to other 8051 family processors,
;	    as well as tolerant of most target system configurations.


; SUGGESTIONS FOR ADDED FEATURES:
;   The most glaring omission is of a single-stepping function, one which I
; intended to implement, but found to be unnecessary.  My plan was to jump to
; the target instruction, and set the timer for an immediate interrupt.  The
; interrupt handler would finish the work.  Such an implementation would
; naturally interfere with normal interrupt operation, and could thus be
; undesirable.
;
;   Other possible conveniences:
;		- Dumping of all registers upon subroutine exit
;		- Permit calling of subroutines with specified register values
;	These features can be easily added with minor changes as required.


; DEBUGGING TACTICS:
;   As a parting note, I wish to include a few suggestions on debugging
; techniques that I found helpful in my task.
;
;	1) Write independent subroutines that can be debugged individually.
;	   Call and debug those that you can with the C-all command.
;
;	2) use extra output port bits as signals when debugging real-time
;	   routines.  I pulsed one port bit when a START bit was detected,
;	   another when the serial data was actually being sampled, and
;	   a third for the STOP bit.
;
;	3) use the putchr routine to print out a trail as your program
;	   executes, "a" at the first decision point, "b" at the second, etc.
;	   If serial output degrades your real-time operation, use the SIGNAL
;	   routine to the same end.
;
;					Here's wishing all best luck,
;						The Axonn Staff


crshcode equ 1

ramr0	equ 0		;addresses of registers stored in ram
ramr1	equ 1
ramr2	equ 2

;-------------- Data --------------
	org 3fh-8

crctmp	ds 2		;temporary for CRC16
ramchk	ds 2		;RAM check sequence used to ensure RAM integrity.
timeout	ds 2		;communications timer



;<<<<--------------------------- Main Program ---------------------------->>>>
	ORG 0
	sjmp main

	ORG 0bh
	ajmp timerint

	ORG 28h
	ajmp debug

; This is where the processor goes after reset
main:	mov a,#(LOW debug8)	;place debug return address on the stack
	push acc
	mov a,#(HIGH debug8)
	push acc

; ------------------------------------------------------
; ----------- Application Program goes here ------------
;   This is where an application program should reside.
; ------------------------------------------------------

	sjmp $
;===================== End of Main Program ========================

	
;- Debug -
;  This primitive debugger allows the following operations:
;	M-odify RAM location
;	D-ump RAM
;	C-all subroutine
;	V-ersion

debug0:	mov sp,#7
	mov a,#(LOW debug8)	;place return address on the stack
	push acc
	mov a,#(HIGH debug8)
	push acc

debug:	acall putstr
	db 10,13
	db "debug>",0		;prompt James with  ">"
	mov timeout, #0
	mov timeout+1,#0
	acall getc
	anl a,#0dfh		;lower to uppercase
	acall putc
	cjne a,#"M",debug4

;   If we arrive here, we have a -MODIFY- command
	push ramr0
	acall getaddr
	jc debug2		;if the address was bad, quit this command
	mov r0,a
debug1:	mov a,@r0
	acall putsp		;  ELSE print the contents of the next location
	acall puthex
	mov a,#"<"		;prompt for a new value to deposit
	acall putc
	acall getaddr		;input the new value, quit if it's invalid
	jc debug2
	mov @r0,a
	inc r0
	sjmp debug1

debug2:	pop ramr0
	ajmp debug7


;   If we are here, we have a -CALL- command
debug4:	cjne a,#"C",debug5
	acall putsp		;get the address to call in DPTR.
	acall getaddr
	jc debug7		;quit if address was bad
	mov dph,a
	acall getaddr
	jc debug7		;quit if address was bad
	mov dpl,a
	mov a,#(LOW debug8)	;place return address on the stack
	push acc
	mov a,#(HIGH debug8)
	push acc
	push dpl
	push dph
	ret

;  D-ump memory command.  Print the hex form of 16 bytes.
debug5:	cjne a,#"D",debug7a
	acall getaddr
	mov r0,a
	mov r2,#8
debug6:	acall putsp
	mov a,@r0
	acall puthex
	inc r0
	djnz r2, debug6
	mov a,#"-"
	acall putc

	mov r2,#8
debug61:
	acall putsp
	mov a,@r0
	acall puthex
	inc r0
	djnz r2, debug61
	ajmp debug


;  This is where we handle invalid inputs
debug7:	mov a,#"?"
	acall putc
	ajmp debug

debug7a:
	cjne a,#"V",debug7
	acall putstr
	db 13,10
	db "V1.00 (c) 1988 Axonn Corp., New Orleans, LA 70124"
	db " edited Tue 11-01-88 jda.",13,10,0
	ajmp debug


;  This is where the processor returns when the debugger regains control.
debug8:	push sp			;show the registers on exit
	push ramr2
	push ramr1
	push ramr0
	push acc
	push psw
	acall putstr
	db 10,13, " Stopped. ",0
	pop psw
	jnc debug9
	mov a,#"C"
	acall putc
	acall putsp

debug9:	acall putstr		;Accumulator
	db "A=",0
	pop acc
	acall puthex

	acall putstr
	db " R0=",0		;R0
	pop acc
	acall puthex

	acall putstr
	db " R1=",0		;R1
	pop acc
	acall puthex

	acall putstr
	db " R2=",0		;R2
	pop acc
	acall puthex

	acall putstr
	db " SP=",0		;SP
	pop acc
	acall puthex

	mov sp,#8	;make sure the Stack Pointer and register select bits
	mov psw,#0	;are in a reasonable state.
	ajmp debug


downXf:	setb c
	ret		;download exit with failure

downX:	clr c
	ret		;download exit
;----- end of Download -----



;%%%%%%%%%%%%%%%%%%%%%%%%%%%% Various Subroutines %%%%%%%%%%%%%%%%%%%%%%%%%%

;-- crc16 --
; computes crc given the start address in R1 and the length in R2.  R0 should
;point to the desired seed value (permitting chaining of crc16 computations).
;     INPUT: r0=sequence address, r1=start address, r2=length
;    OUTPUT: crctmp
; Polynomial: x^16 + x^15 + x^2 + 1	(1100 0000 0000 0101)
crc16:
	mov crctmp,@r0		;transfer sequence to crctmp
	inc r0
	mov crctmp+1,@r0

;  get the next byte to be CRC'd
crc002:	mov a,@r1
	xrl crctmp+1,a
	mov r3,#8		;r3 will count bits

;  accumulate each bit into the CRC
crc004:	clr c
	mov a,crctmp
	rrc a
	mov crctmp,a
	mov a,crctmp+1
	rrc a
	mov crctmp+1,a
	jnc crc008

	xrl crctmp, #0a0h	;XOR polynomial into CRC (bit reversed)
	xrl crctmp+1,#01h

crc008:
	djnz r3, crc004	;we must do all 8 bits.

;  here we have finished a byte
	inc r1		;point to the next byte
	djnz r2,crc002	;if not all bytes CRC'd, then loop.

;  move the temporary to the desired sequence location
	mov @r0,crctmp+1
	dec r0
	mov @r0,crctmp
	ret
; ----- End of CRC16 ------


;ok -- getaddr --
;Input an 8 bit hex number, returned in A.
getaddr:
	mov timeout,#0
	mov timeout+1,#0
	acall getc
	acall putc
	acall hexbin
	jc getadX
	swap a
	mov r2,a
	acall getc
	acall putc
	acall hexbin
	orl a,r2
getadX:	ret


; ----- getc -----
;   Get a single character from the programming terminal.
;   Baud rate is set at 2400 baud for a 16Mhz crystal.
;    INPUT: timeout in 18uS units @ 16Mhz. (0=no timeout)
;   OUTPUT: A
getc:	push ramr0
	push ramr1
	setb p3.5		;enable serial in
	mov a,timeout
	mov r1,timeout
	orl a,timeout+1		;IF timeout <> 0
	jnz getc0		;  THEN use the timed input routine

getc0a:	jb p3.5,getc0a		;wait for start bit, no timeout

getc0:	jnb p3.5,getc0d		;2 Timed Input Procedure
	nop			;1
	mov r0,#4		;1
getc0c:	jnb p3.5,getc0d
	djnz r0,getc0c		;16 = 4x4
	mov a,timeout+1		;1
	dec timeout+1		;1
	jnz getc0		;2
	djnz r1,getc0
	setb c			;set the carry for timeout error
	sjmp getcX		;EXIT

getc0d:	mov r0,#206		;206 * 4 * 750ns = 1+1/2 bit @ 2400 baud
getc01:	nop
	nop
	djnz r0,getc01

;  now we should be in the middle of the first bit
	mov r0,#8
getc02:	mov c,p3.5
	rrc a
	acall wait0
	djnz r0,getc02
	mov c,p3.5		;if stop bit is bad, set the carry
	cpl c
getcX:	pop ramr1
	pop ramr0
	ret


;ok -- hexbin --
;convert an ASCII hex digit in A into its' binary equivalent.
;C flag set indicates invalid hex digit.

hexbin:	clr c
	subb a,#"A"		;lower to uppercase conversion
	jc hex01
	anl a,#0dfh
hex01:	add a,#"A"

;the char is now uppercase and/or numeric
	clr c
	subb a,#"0"		;IF char < "0"
	jc hexbiX		;  THEN exit
	subb a,#10		;IF char <= "9"
	jc hex03		;  THEN it is numeric

;if we get here, the char may be alphabetic
	add a,#10
	subb a,#("A"-"0")	;IF "9" < char < "A"
	jc hexbiX		;  THEN error
	subb a,#6
	jc hex02		;IF "F" < char,
	setb c			;  THEN error
	sjmp hexbiX

hex02:	add a,#6
hex03:	add a,#10
	clr c
hexbiX:	ret
;---- end of Hexbin ----


;ok ----- Crlf -----
; Send a <crlf> onthe serial programming port
crlf:	push acc
	mov a,#0dh
	acall putc
	mov a,#0ah
	acall putc
	pop acc
	ret

;ok ----- PutHex -----
; Prints A in hex ASCII on the serial programming port.
puthex:	push acc
	swap a
	acall puth1
	pop acc
puth1:	anl a,#15
	add a,#0f6h	;cmp a,9
	jnc puth2
	add a,#7
puth2:	add a,#3ah
	acall putc
	ret

;ok ----- putc -----
;   Output one character on the serial programming port.
;    INPUT: A
putc:	push acc
	push ramr0
	push ramr1
	mov r0,#9	;8 bits + stop bit
	clr p3.6	;send start bit
	setb c		;stop bit=1
	acall wait0
putc02:	rrc a
	mov p3.6,c
	acall wait0
	djnz r0,putc02
	pop ramr1
	pop ramr0
	pop acc
	ret


;ok-- putsp --
;  Print a space
putsp:	push acc
	mov a,#" "
	acall putc
	pop acc
	ret


;-- putstr --
;  Print the zero-delimited string that follows the call to this routine.
;	Example:
;		ACALL putstr
;		DB "Hi Mr. Rhinocerous",0

putstr:	pop dph
	pop dpl
	push acc
putstr1:
	mov a,#0
	movc a,@a+dptr
	jz putstrX
	acall putc
	mov a,dpl
	add a,#1
	mov dpl,a
	jnc putstr1
	inc dph
	sjmp putstr1

putstrX:
	inc dpl
	mov a,dpl
	jnz putstrX1
	inc dph
putstrX1:
	pop acc
	push dpl
	push dph
	ret


;----- signal ----
;   This routine zips out the code in the accumulator in serial fashion using
; the bits p3.3, p3.4.  This is a useful way of examining a process in real
; time with the use of an oscilliscope.
;    INPUT: Acc
;   OUTPUT: p3.3 = data
;	    p3.4 = clock
signal:	push ramr2
	anl p3,#(NOT 18h)	;clear our two signalling bits
	mov r2,#8
signl1:	rlc a
	mov p3.3,c
	nop
	setb p3.4
	clr p3.4
	nop
	nop
	djnz r2,signl1
	pop ramr2
	orl p3,#18h		;leave the bits high when done
	ret


;----- timerint -----
;  Used for debugging.
timerint:
	reti

;ok ----- wait0 -----
;delay one bit time @ 2400 baud.
wait0:	mov r1,#137
wait01:	nop
	nop
	djnz r1,wait01
	ret
	end
