;***************************************************************************
;*
;* Title:		Tiny BASIC
;* Version:		1.0
;* Updated:		8-10-2017
;* Target:		ATmega328
;*
;* Written by Dick Whipple dickwhipple@whippleway.com
;*
;* DESCRIPTION
;* An implementation of Tiny BASIC as per PCC Vol. 3 No. 4.	 
;*
;***************************************************************************
;
.equ	PLPrgm = 0				;1 to preload TB Start-Up Program and RUN
.equ    PRT = 0                 ;1 to inhibit printing
;

.LIST

;***** Reserved Registers

;	X R27:D -> TINY BASIC program pointer

;	Y R29:R28 -> Numeric Stack (NS) pointer

;***** Register Variables

.def	A=r22		;8080 Register Complement - Never safe in subroutines
.def	C=r16		;Never safe in subroutines
.def	B=r17		;Never safe in subroutines
.def	E=r18		;
.def	D=r19		;
.def	L=r20		;
.def	H=r21
.def	genStat=r23	;General status register
					;Bit 0	Inhibit leading zero if = 1
					;Bit 1	Direct Run from input buffer = 1
					;Bit 2	First digit flag
					;Bit 3	Relational Op <
					;Bit 4	Relational Op >
					;Bit 5	Relational Op =
					;Bit 6	Relational Op First pass
					;Bit 7	Run mode = 1, command mode = 0; Line delete only = 1, Not = 0
.def	ALx=r24		;Auxilliary A register low
.def	AHx=r25		;Auxilliary A register high
;
.def	errorCode=r2;Error code
.def	subLevel=r3	;Subrouine level
.def	Lx=r6		;Auxilliary L register
.def	Hx=r7		;Auxilliary H register
.def	XLx=r8		;Auxilliary XL register
.def	XHx=r9		;Auxilliary XH register
.def	SL=r10		;Stack exchange register low
.def	SH=r11		;Stack exchange register high
.def	CLNL=r12	;Current line number - low
.def	CLNH=r13	;Current line number - high
.def	IBL=r14		;Input buffer low (temp)
.def	IBH=r15		;Input buffer high (temp)
;
;	Macros
;
.macro popRP
;
	rcall	popS
	movw	@0:@1, SH:SL 
;
.endmacro
;
.macro pushRP
;
	movw	SH:SL, @0:@1
	rcall	pushS
;
.endmacro
;
.macro pushi
;
	ldi		A, low(@0)
	mov		SL, A
	ldi		A, high(@0)
	mov		SH, A
	rcall	pushS
;
.endmacro
;
.macro addi
;
        subi    @0, -@1	;subtract the negative of an immediate value
;						 
.endmacro
;
.macro  subiw
;
        subi    @1, low(@2)
        sbci    @0, high(@2)
;
.endmacro
;	
;	Misc Equates
;
.equ	inBufferSize = 64		;Input buffer size
.equ	outBufferSize = 64		;Outpu buffer size
.equ	SRSTACKSIZE = 100		;Subroutine stack size
.equ	TBEND = RAMEND - SRSTACKSIZE ;End of allowed TINY BASIC program segment
.equ	numStkSize = 64			;Numeric stack size
.equ	quote = 0x22			;ASCII quotation mark
.equ	CR = 0x0D				;ASCII carriage return
.equ	EOF = 0xFF				;End-of-file
.equ	LF = 0x0A				;ASCII line feed
.equ    BackSpace = 127         ;Back space key
.equ	BS = 0x08				;ASCII back space for terminal
.equ	ESC = 0x1b				;ASCII escape
.equ	subLevelMax = 5			;Maximum number of subroutine levels
.equ    baud_rate_const = 103   ;9600 BPS
;
 jmp RESET    ; Reset
 jmp null_int ; IRQ0
 jmp null_int ; IRQ1
 jmp null_int ; PCINT0
 jmp null_int ; PCINT1
 jmp null_int ; PCINT2
 jmp null_int ; Watchdog Timeout
 jmp null_int ; Timer2 CompareA
 jmp null_int ; Timer2 CompareB
 jmp null_int ; Timer2 Overflow
 jmp null_int ; Timer1 Capture
 jmp null_int ; Timer1 CompareA
 jmp null_int ; Timer1 CompareB
 jmp null_int ; Timer1 Overflow
 jmp TIM0_COMPA ; Timer0 CompareA
 jmp null_int ; Timer0 CompareB
 jmp null_int ; Timer0 Overflow
 jmp null_int ; SPI Transfer Complete
 jmp null_int ; USART RX Complete
 jmp null_int ; USART UDR Empty
 jmp null_int ; USART TX Complete
 jmp null_int ; ADC Conversion Complete
 jmp null_int ; eeprom Ready
 jmp null_int ; Analog Comparator
 jmp null_int ; 2-wire Serial
 jmp null_int ; SPM Ready
;
null_int:
    reti
;
;   Digital I/O Initialization Code
;
reset:
;
;	Initialize stack pointers
;
;	Subroutine stack
;
	ldi		A,low(RAMEND)
	out		SPL,A
	ldi		A,high(RAMEND)
	out		SPH,A
;
;	Initialize numeric stack
;
	ldi		YL, low(numSTK)
	ldi		YH, high(numSTK)
;
;	Zero variables
;
	rcall	zeroVariables
;
;	Clear TB program segment
;
	ldi		A, EOF				;Write EOF to first byte of TB program segment
	sts		prgStart, A
	ldi		A, low(prgStart)	;Update TB program end
	sts		prgEnd, A 
	ldi		A, high(prgStart)
	sts		prgEnd + 1, A
;
;	Seed random number generator
;
	ldi		L, 100				;Seed with 100
	ldi		H, 0
	rcall	seedRandomNumber
;
;	Clear general status register
;
	clr		genStat
;
;	Reset error code
;
	ldi		A, 0xff
	mov		errorCode, A
;
	ldi		A, CR
	sts		inBuffer, A
;
;	Set baud rate to 9600
;
	ldi 	C, low(baud_rate_const)
	ldi 	B, high(baud_rate_const)
	sts		UBRR0H, B
	sts		UBRR0L, C
;
;
;	Set 8-bit, no partity, 2-stop bits
;
;	ldi		A, (0<<UMSEL0)|(0<<UPM00)|(0<<UPM10)|(1<<USBS0)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00)
	ldi		A, (1<<USBS0)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00)
   	sts		UCSR0C, A
;
;	Enable xmt and rcv
;
	ldi		A, (1<<RXEN0)|(1<<TXEN0)
	sts		UCSR0B, A

.IF PLPrgm == 1

;
;	Load Test TB Program
;
loadTBProgram:
;
	ldi		XL,	low(prgStart)			;Point X to start of TB program segment
	ldi		XH, high(prgStart)
	ldi		ZL, low(2 * TBProgram)	    ;Point Z to test program
	ldi		ZH, high(2 * TBProgram)
	rcall	ltpLoop						;Write TB test program to TB program segment
	sbiw	X, 1			
	sts		prgEnd, XL					;Update TB program end
	sts		prgEnd + 1, XH
	rjmp	signOn					    ;Continue
;
ltpLoop:
;
	lpm									;Load from code segment LSB (Least Significant Byte)
	st		X+, R0						;Store in TB program segment 
	mov		A, R0						;Move it to A
	cpi		A, 0xFF						;EOF?
	breq	ltpDone						;If so, continue
	adiw	Z, 1						;Point to the MSB in code segment
	lpm									;Load it
	st		X+, R0						;Store it in TB program segment						
	mov		A, R0						;Move it to A
	cpi		A, 0xFF						;EOF?
	breq	ltpDone						;If so, continue
	adiw	Z, 1						;Point to next word in code segment
	rjmp	ltpLoop						;Back for more
;
ltpDone:
;
	ret									;Done...return

.ELSE

;
;	No program in TB program space
;
	ldi		A, 0xff
	sts		prgStart, A
;

.ENDIF
;
signOn:
;
;	Output sign On Message
;
	rcall	crlf					;New line
	rcall	crlf					;New line
	ldi		L, low(signOnMsg)		;Point HL to sign-on message
	ldi		H, high(signOnMsg)
	rcall	msgOut					;Write it to output buffer
	ldi		A, CR					;Write a carriage return to output buffer
	rcall	putByte
	rcall	dumpOutBuf				;Send output buffer to serial device
	rcall	crlf					;New line
;
 .IF PLPrgm == 1

    rjmp    runCommand0

 .ELSE

	rjmp	command					;Go to command mode

.ENDIF
;
;	TB Start-Up Program
;
TBProgram:
;
    .db     "10 a=0",CR, "20 pwm(a)", CR, "30 a=a+1", CR, "40 b=0", CR, "50 b=b+1:if b<100 then 50", CR, "60 if a>100 then 10", CR, "70 goto 20", CR, 0xff, 0
;
signOnMsg:
;
.db	"AVR TINY BASIC Version 1.0 (RBW 2017)", EOF
;			
;	Routine: command
;
;	Purpose: Process commands or statements in direct mode
;
command:
;
;	Basic Initialization
;
;	Subroutine stack
;
	ldi	A,low(RAMEND)
	out	SPL,A
	ldi	A,high(RAMEND)
	out	SPH,A
;
;	Initialize numeric stack
;
	ldi		YL, low(numSTK)
	ldi		YH, high(numSTK)
;
;	Clear general status register
;
	clr		genStat
;
	rcall	crlf				;New line
	ldi		A, '>'				;Send greater than (>) prompt to serial device
	rcall	xmtByte
	andi	genStat, 0b01111111	;Set command mode
	rcall	inBuf				;Get command into input buffer
	rcall	crlf				;New line
	ldi		XL, low(inBuffer)	;Point X to start of input buffer
	ldi		XH, high(inBuffer)
	rcall	skipSpace			;Skip to first non-space
	rcall	getNumber			;Line number present?
	brcs	lineProcess			;If so, continue...process the line
;
;	Process a direct command or statement
;
	clr		CLNL				;Set current line number zero
	clr		CLNH
	rcall	skipSpace			;Skip to first non-space		
	rjmp	exeStatement		;Process command/statement
;			
;	Routine: lineProcess
;
;	Purpose: Process program line currently in input buffer
;
;	Options:
;
;		If only line number in input buffer and line exists, delete it 
;		If full line in input buffer and line exists, delete it first and then insert new line
;		Otherwise, insert new line
;
lineProcess:
;
	movw	CLNH:CLNL, H:L		;Save new line number
	rcall	skipSpace			;Get next non-space byte
	ori		genStat, 0b10000000	;Set delete only bit
	cpi		A, CR				;Carriage return?
	ldi		XL, low(prgStart)	;Point X to TB program start
	ldi		XH, high(prgStart)
	breq	lineProcessLoop		;If so, continue...delete line CLN
	andi	genStat, 0b01111111	;Otherwise, clear delete only bit (i.e., prepare to delete and insert new line)
;
;	Find line CLN if it exists
;
lineProcessLoop:
;
	movw	ZH:ZL, XH:XL		;Save start of line in Z
	rcall	getNumber			;Get line number from TB program segment
	brcc	lineInsert			;No line found, try to insert if not delete only
;
;	Current TB program line number is in HL; does it match CLN?
;
	sub		L, CLNL				;HL = CLN?
	sbc		H, CLNH
	breq	lineProcess1		;Branch - Matches - delete old line and insert new line!
	brcs	lineProcess0		;Branch - TB program line number less than CLN.  Keep looking
;
	movw	H:L,ZH:ZL			;Z->HL
	rjmp	lineInsert0			;Insert new line at (Z)
;
lineProcess0:
;
	ld		A, X+				;Search for CR
	cpi		A, CR				;CR?
	brne	lineProcess0		;Otherwise, loop
	rjmp	lineProcessLoop		;Keep looking!
;
;	Reached EOF, insert new line here!
;
lineInsert:
;
	movw	H:L,XH:XL			;X->HL
	tst		genStat				;Delete only?
	brpl	lineInsert0			;Otherwise, continue
	rjmp	LineNumberNotFoundError	;If so, raise line not found error
;
lineInsert0:
;
;	Find length of line and store in BC
;
	clr		ALx					;0->Ax
	clr		AHx
	ldi		XL, low(inBuffer)	;Point X to start of input buffer
	ldi		XH, high(inBuffer)
;
lineInsert0Loop0:
;
	ld		A, X+				;Get new line byte into A
	adiw	AHx:ALx, 1			;Increment Ax 
	cpi		A, CR				;CR?
	brne	lineInsert0Loop0	;Otherwise, loop
	movw	B:C,AHx:ALx			;Ax -> BC
;
;	Length of remaining bytes in current TB program -> DE
;
	clr		AHx					;0->Ax
	clr		ALx										
	movw	XH:XL,H:L			;HL->X
;
lineInsert0Loop1:
;
	ld		A, X+				;Loop looking for EOF
	adiw	AHx:ALx, 1			;Increment Ax
	cpi		A, EOF				;End of TB program?
	brne	lineInsert0Loop1	;Otherwise, loop
	movw	D:E,AHx:ALx			;Ax->DE
;
lineInsert1:
;
;	Move remaining bytes in TB program up BC bytes to make room for new line
;
	movw	ZH:ZL,XH:XL			;X + BC -> Z
	add		ZL, C
	adc		ZH, B
	ldi		C, low(TBEND)		;Out of Memory?
	ldi		B, high(TBEND)
	sub		C, ZL
	sbc		B, ZH
	brcs	lineInsertError		;If so, error
	movw	AHx:ALx,D:E			;DE -> Ax
;
lineInsert1Loop:
;

	ld		A, -X				;(X) -> (Z)
	st		-Z, A
	sbiw	AHx:ALx, 1			;Ax - 1 -> Ax
	mov		A, AHx				;Ax = 0?
	or		A, ALx			
	brne	lineInsert1Loop
;
;	Copy new line to opened space
;
lineInsert2:
;
	movw	ZH:ZL,H:L			;HL -> Z
	ldi		XL, low(inBuffer)	;Point X to start of input buffer
	ldi		XH, high(inBuffer)
;
lineInsert2Loop:
;
	ld		A, X+				;Copy byte
	st		Z+, A
	cpi		A, CR				;CR?
	brne	lineInsert2Loop		;Otherwise, loop...keep copying bytes
;
lineInsert3:
;
;	Done, return to command mode
;
	sbiw	Z, 1
	sts		prgEnd, ZL				;Update TB program end
	sts		prgEnd + 1, ZH
	rcall	crlf					;New line
	rjmp	command + 1				;Done...go to commane=d mode
;
;
;	Delete line CLN and insert new line
;
lineProcess1:
;
;	Delete line
;
	movw	H:L,ZH:ZL			;Z->HL
	movw	XH:XL,ZH:ZL			;Z->H
;
lineDeleteLoop0:
;
	ld		A, Z+				;Look for CR
	cpi		A, CR				;CR?
	brne	lineDeleteLoop0		;Otherwise, loop
;
lineDeleteLoop1:
;
	ld		A, Z+				;Move remaining TB program down to back fill removed line
	st		X+, A
	cpi		A, EOF				;End of TB program?
	brne	lineDeleteLoop1		;Otherwise, loop
;
lineDeleteDone:
;
	sbiw	Z, 1
	sts		prgEnd, ZL			;Update TB program end
	sts		prgEnd + 1, ZH
	tst		genStat				;Delete only?
	brpl	lineInsert0			;Otherwise, insert new line
	rjmp	command	+ 1			;If so, done...go to command mode
;
lineInsertError:
;
	rjmp	outOfMemoryError
;			
;	Routine: inBuf
;
;	Purpose: Take input from serial device and place in input buffer
;
inBuf:
;
	clr		B					;Clear BC (input buffer pointer)
	clr		C
;
inBufSS:
;
	rcall	rcvByte				;Get next byte from serial device and skip leading spaces
;
	cpi		A, ESC				;ESC?
	brne	inBuf0				;Otherwise, continue
	rjmp	command				;If so, go to command mode
;
inBuf0:
;
	cpi		A, ' '				;Space?
	breq	inBufSS				;Otherwise, loop
	rjmp	inBuf1 + 1			;If so, continue...skip rcvByte statement
;
inBufLoop:
;
	cpi		A, ESC				;ESC?
	brne	inBuf1				;Otherwise, continue
	rjmp	command				;If so, go to command mode
;
inBuf1:
;
	rcall	rcvByte				;Get next byte
	cpi		A, BackSpace		;Back space?
	brne	inBuf2				;Otherwise, continue
;
;	Process back space
;
	tst		C					;At beginning of buffer?
	breq	inBufLoop			;If so, do nothing..
	ldi		A, BS				;Back up one byte
	rcall	xmtByte
	ldi		A, ' '				;Wipe byte
	rcall	xmtByte
	ldi		A, BS				;Back up again
	rcall	xmtByte
	dec		C					;Pointer back one space
	rjmp	inBufLoop			;Get another byte
;
inBuf2:
;
	cpi		A, CR				;Carriage return?
	brne	inBuf3				;if not, continue
;
;	process carriage return
;
	rcall	putByteInBuf		;Put CR in input buffer
	ret							;Done
;
inBuf3:
;
	cpi		A, ' '				;Space?
	brcs	inBufLoop			;If a control byte, ignore
	rcall	putByteInBuf		;Put byte in input buffer
	brcc	inBufLoop			;If put failed, do nothing
	rcall	xmtByte				;Output byte to serial port
	rjmp	inBufLoop			;Back for another byte
;
putByteInBuf:
;
	ldi		ZL, low(inBuffer)	;Z points to beginning of input buffer
	ldi		ZH, high(inbuffer)
	cpi		C, low(inBufferSize);End of input buffer?
	brcs	putByteInBuf0		;Otherwise, continue
	ret							;If so, do nothing. Return with carry clear
;
putByteInBuf0:
;
	add		ZL, C				;Point to input buffer write address
	adc		ZH, B
	st		Z, A				;Store byte
	inc		C					;Point next input buffer position
	sec							;Set carry
	ret							;Done...return with carry set
;			
;	Routine: rcvByte
;
;	Purpose: If byte in serial buffer, gets it in A from serial port.
;
rcvByte:
;

	lds		A, UCSR0A			;Byte available?
	sbrs	A, RXC0
	rjmp	rcvByte				;Otherwise, loop
;
;	Get received byte
;
	lds		A, UDR0				;If so, get from serial port into A
	ret							;Done
;
;	Routine: xmtByte
;
;	Purpose: Waits for transmit ready, then transmits byte in A through serial port.
;
xmtByte:
;

.IF 	PRT == 1

	ret						;If simulation, do nothing

.ELSE

	rcall	pushA			;Save A
;
xmtByteLoop:
;
	lds		A, UCSR0A		;Ready to transmit byte?
	sbrs	A, UDRE0
	rjmp	xmtByteLoop		;Otherwise, loop
;
;	Put byte in xmt buffer
;
	rcall	popA			;Restore A
	sts		UDR0, A			;Transmit it to serial port
	ret						;Done
;

.ENDIF

;						
;	Routine: crlf
;
;	Purpose: Outputs carriage return and linefeed to serial port.
;
crlf:
;
	clr		A					;Set output buffer pointer to start
	sts		outBufPtr, A
	ldi		A, CR				;Send carriage return to serial port
	rcall	xmtByte
	ldi		A, LF				;Send line feed to serial port
	rjmp	xmtByte
;
;	Routine: runCommand
;
;	Purpose: Starts execution at TB program start.
;
runCommand:
;
	tst		genStat					;Command Mode?
	brpl	runCommand0				;If so, continue
	rjmp	runModeError			;Otherwise, error
;
runCommand0:
;
	ori		genStat, 0b10000000		;Set RUN mode
	rcall	crlf					;New line	
	rcall	clearOutputBuffer		;Clear the output buffer ??? Is this really necessary?
;
	rcall	zeroVariables			;Zero variables
;
	ldi		YL, low(numSTK)			;Initialize numeric stack
	ldi		YH, high(numSTK)
;	
	ldi		XL, low(prgStart)		;Point X to TB program start
	ldi		XH, high(prgStart)
;
runP0:
;	
	rcall	getNumber				;Is there a line number?
	brcs	runP1					;If so, continue
	rjmp	command					;Otherwise, back to command mode
;
runP1:
;
	movw	CLNH:CLNL, H:L			;Update current line number
	lds		A, UCSR0A				;Byte available?
	sbrc	A, RXC0
	rcall	runP4					;If so, continue
	rcall	skipSpace				;Skip to first non-space		
	rjmp	exeStatement			;Assume statement follows and execute it
;
runP2:
;
	or		CLNH, CLNL				;Direct command?
	brne	runP3					;Otherwise, continue
	rjmp	command					;If so, go to command mode
;
runP3:
;
	rcall	nextByte				;Get next TB program byte (Should be beginning of next line number or EOF)
	rjmp	runP0					;Go see
;
runP4:
;
	lds		A, UDR0					;Get from serial port into A
	cpi		A, ESC					;ESC?
	breq	runP5					;If so, continue
	ret								;Otherwise, return
;
runP5:
;
	ldi		A,low(RAMEND)			;Reinitialize stack pointer
	out		SPL,A
	ldi		A,high(RAMEND)
	out		SPH,A
	rcall	clearOutputBuffer		;Clear output buffer
	rcall	crlf					;New line
	ldi		L, low(breakMsg)		;Point to break message
	ldi		H, high(breakMsg)
	rjmp	error1					;Finish at error code
;
done:
;
	cpi		A, CR					;CR?
	breq	runp2					;If so, done
	cpi		A, ':'					;Colon?
    brne    doneError               ;Otherwise, error
    ld      A,x+
    rcall   skipSpace
    rjmp    exeStatement
;
doneError:
;
    rjmp    syntaxError             ;report syntax error
;
doneCheck:
;
	cpi		A, CR				;CR?
	brne	doneCheck0			;Otherwise, continue
	ret							;If so, done
;
doneCheck0:
;
	cpi		A, ':'				;Colon?
	breq	doneCheck1			;If so, continue
	ret
;
doneCheck1:
;
	ldi		A, CR
	ret
;
breakMsg:
;
;
	.db		"BREAK AT LINE ", 0xff, 0
;
;
;	Routine: listCommand
;
;	Purpose: Lists program in TB program segment
;
listCommand:
;;
	tst		genStat				;Command Mode?
	brpl	listCommand0		;If so, continue
	rjmp	runModeError		;Otherwise, error
;
listCommand0:
;	
	ldi		XL, low(prgStart)	;Point X to TB program start?
	ldi		XH, high(prgStart)
;
listCommandNL:
;
	rcall	crlf				;New line
;
listCommandLoop:
;
	ld		A, X+				;Get byte?
	cpi		A, 0xFF				;EOF?
	breq	listCommandDone		;If so, done
	cpi		A, CR				;Carriage return?
	breq	listCommandNL		;If so, output new line then continue
	rcall	xmtByte
	rjmp	listCommandLoop		;Back for more
;
listCommandDone:
;
	rjmp	command				;Back to command mode
;
;	Routine: newCommand
;
;	Purpose: Clears TB program segment
;
newCommand:
;
	tst		genStat				;Command Mode?
	brpl	newCommand0			;If so, continue
	rjmp	runModeError		;Otherwise, error
;
newCommand0:
;
	ldi		A, EOF				;Write EOF to first byte of TB program segment
	sts		prgStart, A
	ldi		A, low(prgStart)	;Update TB program end
	sts		prgEnd, A 
	ldi		A, high(prgStart)
	sts		prgEnd + 1, A
;
	rjmp	command 			;Back to command mode
;
;	Routine: exeStatement
;
;	Purpose: Checks current byte(s) at (X) for a valid command/statement and if found, branches to it.
;
exeStatement:
;
	ldi		ZL, low(2 * iTable)	;Point Z to start of statement table
	ldi		ZH,	high(2 * iTable)
	rcall	findReserveWord		;Statement/command in table?
	brcs	exeStatement1		;If so, continue
	clr     c           		;Assume LET
;
exeStatement1:
;
	ldi		ZL, low(iJmpTable)	;Start of iJmpTable -> Z
	ldi		ZH, high(iJmpTable)
	clr		B					;2 * BC -> BC
	add		ZL, C				;Z + BC -> Z 
	adc		ZH, B	
	ijmp						;Jump indirect to statement
;
iTable:
;
;	Note: Command/Statement reserved word table must match iJmpTable!
;
	.db		"LE", 'T' + 0x80, 'I', 'F' + 0x80, "THE", 'N' + 0x80, "PRIN", 'T' + 0x80, "GOT", 'O' + 0x80, "GOSU", 'B' + 0x80, "RETUR", 'N' + 0x80, "INPU", 'T' + 0x80, "PW", 'M' + 0x80, "OU", 'T' + 0x80, "DL", 'Y' + 0x80, "PU", 'T' + 0x80, "RE", 'M' + 0x80, "EN", 'D' + 0x80, "RU", 'N' + 0x80, "LIS", 'T' + 0x80, "NE", 'W' + 0x80, 0xff,0
;
iJmpTable:
;
	rjmp		letStmt
	rjmp		ifStmt
	rjmp		gotoStmt
	rjmp		printStmt
	rjmp		gotoStmt
	rjmp		gosubStmt
	rjmp		returnStmt
	rjmp		inputStmt
    rjmp        pwmStmt
    rjmp        outStmt
    rjmp        dlyStmt
    rjmp        putStmt
	rjmp		remStmt	
	rjmp		endStmt
	rjmp		runCommand
	rjmp		listCommand
	rjmp		newCommand

;
;
;	Routine: findReserveWord
;
;	Purpose: Checks current byte(s) pointed to by X for a reserved word (command/statement/function)
;			 in table pointed to by Z.
;			 Returns carry set if found and in C the number of reserved word in table (base 0)
;			 Returns carry cleared if not found
;
findReserveWord:
;
	movw	XHx:XLx, XH:XL		;Save X in Xx (X auxilliary)
;
	ldi		B, -1				;Set statement found flag to -1 (no valid statement)
	ldi		C, -1				;Set statement counter to 0 (first statement)
;
findInLoop0:
;	
	movw	XH:XL, XHx:XLx		;Point X back to beginning of possible statement
	inc		C					;Point first statement in iTable
;
findInLoop1:
;
	ld		A, X+				;Get TB program byte?
	andi	A, 0b01111111		;Mask lower seven bits
	rcall	makeUpperCase		;Make upper case if letter
;
findIn:
;
	cpi		A, 'A'
	brcs	findInNone			;Branch to done if (X) less than ASCII 'A'
	cpi		A, 'Z' + 1
	brcc	findInNone			;Branch to done if (X) greater than 'Z'
	lpm		D, Z+				;(Z) -> D
	cpi		D, 0xFF
	breq	findInNone			;Branch to done if at end of table
	cpi		D, 0b10000000
	brcc	findIn0				;Branch if last letter of statement in table
	cp		A, D
	breq	findInLoop1			;Byte match! Check next TB program byte
;
;	No match
;
findInLoop2:
;
	lpm		D, Z+				;Look for last byte of current iTable statement
	cpi		D, 0b10000000
	brcs	findInLoop2
	rjmp	findInLoop0	
;
findIn0:
;
	andi	D, 0b01111111		;Mask lower seven bits
	cp		A, D
	brne	findInLoop0			;Branch in no byte match
;
;	Statement matches!
;
findInFound:
;
	sec							;Set carry
	ret							;Done
;
;	No statement found
;
findInNone:
;
	movw	XH:XL, XHx:XLx		;Point X back to beginning of statement
	clc							;Clear carry
	ret							;Done
;
;	Convert letter to upper case
;
makeUpperCase:
;
	sbrs	A, 6				;Letter?
	ret							;Otherwise, done
	andi	A, 0b11011111		;Make upper case
	ret
;
;	Routine: expression
;
;	Purpose: Evaluate expression at X
;
;	Used: All basic registers
;
expression:
;
	rcall	skipSpace			;Get first non-space byte of expression
	cpi		A, '+'				;Plus?
	brne	expr0				;Otherwise, continue
	rcall	nextByteSS			;If so, get next TB program byte then skip spaces
	rjmp	expr1
;
expr0:
;
	cpi	A, '-'					;Negation?
	brne	expr1				;Otherwise, continue
	rcall	nextByteSS			;If so, get next TB program byte then skip spaces
	rcall	term				;	Get term value on numeric stack
	rcall	twosComp16Stk		;	Negate it.	
	rjmp	expr2				;	continue
;
expr1:
;
	rcall	term				;Get first term
;
expr2:
;
	cpi	A, '+'					;Addition?
	brne	expr3				;Otherwise, continue
	rcall	nextByteSS			;If so, get next TB program byte then skip spaces
	rcall	term				;	Get next term on numeric stack
	rcall	add16Stk			;	Add top two terms on numeric stack
	rjmp	expr2				;	continue
;
expr3:
;
	cpi	A, '-'					;Subtraction?
	brne	expr4				;Otherwise, done.
	rcall	nextByteSS			;If so,	get next TB program byte then skip spaces
	rcall	term				;	Get next term on numeric stack
	rcall	twosComp16Stk		;	Neagate it
	rcall	add16Stk			;	Add top two terms on numeric stack
	rjmp	expr2				;	continue
;
expr4:
;
	ret							;Done
;
term:
;
	rcall	factor				;Get first factor
;
term0:
;
	rcall	skipSpace			;Skip to non-space byte
	cpi		A, '*'				;Multiply?
	brne	term1				;Otherwise, continue
	rcall	nextByteSS			;If so, get next TB program byte then skip spaces
	rcall	factor				;	Get next factor
	rcall	mult16Stk			;	Multiply top two factors on numeric stack
	rjmp	term0				;	continue
;
term1:
;
	cpi		A, '/'				;Divide?
	brne	term2				;Otherwise, continue
	rcall	nextByteSS			;If so, get next TB program byte then skip spaces
	rcall	factor				;	Get next factor
	rcall	div16Stk			;	Multiply top two factors on numeric stack
	rjmp	term0				;	continue
;
term2:
;
	ret							;Done
;
factor:
;
	rcall	getFunction			;Function?
	brcc	fact0				;Otherwise, continue
	rcall	skipSpace			;If so, skip to next non-space
	pushRP	H, L				;	Put function value on numeric stack
	ret							;	Done
;
fact0:
;
	rcall	getVariable			;Variable?
	brcc	fact1				;Otherwise, continue
	rcall	skipSpace			;If so, skip to next non-space byte
	pushRP	H, L				;	Put value variable found on numeric stack
	ret							;	Done
;
fact1:
;
	rcall	getNumber			;Number?
	brcc	fact2				;Otherwise, continue
	rcall	skipSpace			;If so, skip to next non-space
	pushRP	H, L				;	Put HL on numeric stack
	ret							;	Done
;
fact2:	
;
	ld		A, X				;Get current TB program byte?
	cpi		A, '('				;Begin grouping?
	breq	fact3				;If so, continue
	rjmp	syntaxError			;Otherwise, unexpected byte indicates syntax error.
;
fact3:
;
	rcall	nextByteSS			;Get next TB program byte then skip spaces
	rcall	expression			;Recursively rcall expression
	rcall	skipSpace			;Skip to first non-space
	cpi		A, ')'				;End grouping?
	breq	fact4				;If so, continue
	rjmp	unpairedParenError	;Otherwise, unpaired parentheses error
;
fact4:
;
	rjmp	nextByteSS			;Get next TB program byte then skip spaces
;
;   Statement Routines
;
;	Process LET statement
;
letStmt:
;
	rcall	skipSpace			;Skip to first non-space
	rcall	makeUpperCase		;If letter, make upper case (Variable a is same as A, etc)
	cpi		A, 'A'				;Variable name
	brcs	letStmtError		;Otherwise, syntax error
	cpi		A, 'Z' + 1
	brcc	letStmtError		;Otherwise, syntax error
	rcall	pushA				;Put variable name on numeric stack
	rcall	nextByteSS			;Get next TB program byte then skip spaces
	cpi		A, '='				;Should be equal sign?
	brne	letStmtError		;Otherwise, syntax error
	rcall	nextByteSS			;Get next TB program byte then skip spaces
	rcall	expression			;Evaluate expression
	rcall	storeVariable		;Store it
	rcall	skipSpace			;Skip to first non-space
	rjmp	done				;Done
;
letStmtError:
;
	rjmp	syntaxError
;
;	Process IF statement
;
ifStmt:
;
	rcall	skipSpace			;Skip to first non-space
	rcall	expression			;Evaluate first expression on numeric stack
	andi	genStat, 0b10000111	;Clear all relational bits
	ori		genStat, 0b01000000	;Set bit 6 indicating first pass
;
ifLoop:
;
	rcall	skipSpace			;Skip to first non-space
	cpi		A, '<'				;Less than?
	brne	if0					;Otherwise, continue looking
	ori		genStat, 0b00001000	;If so, set bit 3
	rjmp	if3
;
if0:
;
	cpi		A, '>'				;Greater than?
	brne	if1					;Otherwise, continue looking
	ori		genStat, 0b00010000	;If so, set bit 4
	rjmp	if3
;
if1:
;
	cpi		A, '='				;Equals to?
	brne	if2					;Otherwise, continue looking
	ori		genStat, 0b00100000	;If so, set bit 5
	rjmp	if3
;
if2:
;
	sbrc	genStat, 6			;First pass bit?
	rjmp	missingRelopError	;if so, expected relational operator on first pass
	rjmp	if4
;
if3:
;
	rcall	nextByteSS			;Get next TB program byte then skip spaces
	sbrs	genStat, 6			;First pass?
	rjmp	if4					;Otherwise, continue
	andi	genStat, 0b10111111	;Clear first pass bit
	rjmp	ifLoop
;
if4:
;
	rcall	expression			;Evaluate second expression
	rcall	twosComp16Stk		;Negate second expression
	rcall	add16Stk			;Add expressions	
	popRP	H, L				;Result in HL
	or		H, L				;Result zero?
	brne	if5					;Otherwise, continue
	sbrc	genStat, 5			;Equal bit set?
	rjmp	ifThen				;If so, execute THEN statement
;
ifDone0:
;
	rcall	skipSpace			;Skip to first non-space
;
ifDone0Loop:
;
	cpi		A, CR				;Carriage return?
	breq	ifDone1				;If so, execute next statement
	rcall	nextByteSS			;Otherwise, get next TB program byte then skip spaces
	rjmp	ifDone0Loop			;Keep looking
;
ifDone1:
;
	rjmp		runP2			;Done
;
if5:
;
	tst		H					;Test if H negative
	brbc	2, if6				;Otherwise, continue
	sbrc	genStat, 3			;Less than bit set?
	rjmp	ifThen				;If so, execute THEN statement
	rjmp	ifDone0
;
if6:
;
	sbrc	genStat, 4			;Greater than bit set?
	rjmp	ifThen				;If so, execute THEN statement
	rjmp	ifDone0
;
ifThen:
;
	rjmp	exeStatement		
;
ifError0:
;
	rjmp	missingRelopError
;
ifError1:
;
	rjmp	syntaxError
;
;	Process PRINT statement
;
printStmt:
;
	rcall	skipSpace			;Skip to first non-space
	rcall	printString			;String? If so, Write to outBuffer
	brcc	printStmt0			;Otherwise, continue
	rcall	skipSpace			;Skip to first non-space
	rcall	doneCheck			;CR or Colon?
	brne	printStmt2			;Otherwise, continue
	rcall	putByte				;If so, write CR to output buffer
	rjmp	printStmt6	        ;Done
;
printStmt0:
;
	rcall	skipSpace			;Skip to non-space byte
	rcall	doneCheck			;CR or Colon?
	brne	printStmt1			;Otherwise, continue
	rcall	putByte				;If so, write CR to output buffer
	rjmp	printStmt6      	;Done
;
printStmt1:
;
	rcall	expression			;Assume an expression and evaluate it
	rcall	printNumber			;Write resulting value to output buffer
	rcall	skipSpace			;Skip to non-space byte
	rcall	doneCheck			;CR or Colon?
	brne	printStmt2			;Otherwise, continue
	rcall	putByte				;If so, Write CR to output buffer
	rjmp	printStmt6
;
printStmt2:
;
	cpi		A, ';'				;Semicolon?
	brne	printStmt3			;Otherwise, check for comma.
	ldi		A, ' '				;If so, Write space to outBuffer
	rcall	putByte	
	rjmp	printStmt4			;continue
;
printStmt3:
;
	cpi		A, ','				;Comma? Space to next zone (assumes 8 byte zone)
	brne	printStmt5			;Otherwise, check for carriage return.
;
printStmt3Loop:
;
	ldi		A, ' '				;Write space character to output buffer
	rcall	putByte
	lds		A, outBufPtr		;Is output buffer pointer at end of zone?
	andi	A, 0x07				;Assumes zone width 8
	breq	printStmt4			;If so, continue
	rjmp	printStmt3Loop		;Otherwise, loop
;
printStmt4:
;
	rcall	nextByteSS			;Get next TB program byte then skip spaces
	rcall	doneCheck			;CR?
	brne	printStmt			;Otherwise, continue
    rcall   putByte
	rcall	dumpOutBuf			;Dump output buffer
	clr		A					;Set output buffer pointer to start
	sts		outBufPtr, A
	rjmp	printStmt7			;If so, done
;	
printStmt5:
;
	rcall	skipSpace			;Skip to non-space byte
	rcall	doneCheck			;CR or Colon?
	brne	printStmt			;Otherwise, continue
;
	rcall	putByte				;If so, Write CR to output buffer
;
printStmt6:
;
	rcall	dumpOutBuf			;Dump output buffer
;
	rcall	crlf				;New line
;
printStmt7:
;
    ld      A, X
    cpi     A,':'
    breq    contExe
	rjmp	runP2				;Done
;
contExe:
;
    rcall   nextByteSS
    rjmp    exeStatement
;
;	Routine: dumpOutBuf
;
;	Purpose: Sends contents of output buffer to serial device
;
dumpOutBuf:
;
	ldi		ZL, low(outBuffer)
	ldi		ZH, high(outbuffer)
;
dumpOutBufLoop:
;
	ld		A, Z+
	cpi		A, CR				;EOF?
	brne	dumpOutBuf0			;Otherwise, continue
	ret							;If so, done
;
dumpOutBuf0:
;
	rcall	xmtByte
	rjmp	dumpOutBufLoop
;
;	Routine: putByte
;
;	Purpose: Write contents of register A to output buffer
;
putByte:
;
	clr		B					;Buffer pointer -> BC
	lds		C, outBufPtr		;Past end of output buffer?
	cpi		C, outBufferSize	;BC > Buffer Size
	brcs	putByte0			;Otherwise, continue
	push	A					;If so, Save A
	ldi		A, CR				;Write CR to output buffer (extra byte)
	rcall	putbyte0
	rcall	dumpOutBuf			;Dump the output buffer
	clr		A					;Set output buffer pointer to start
	sts		outBufPtr, A
	mov		C, A				;C back to start of output buffer
	pop		A					;Restore A and continue
;
putByte0:
;	
	clr		B					;Calculate buffer address for byte
	movw	XHx:XLx, XH:XL		;Save X
	ldi		XL, low(outBuffer)	;BC + outBuffer -> X
	ldi		XH, high(outbuffer)
	add		XL, C
	adc		XH, B
	st		X, A				;Store A -> (X)
	inc		C					;Point buffer pointer to next byte
	sts		outBufPtr, C		;Store buffer pointer.
	movw	XH:XL, XHx:XLx		;Restore X
	ret							;Done
;
;
;	Routine: printString
;
;	Purpose: if A contains a quotation mark, Write string to output buffer.
;
printString:
;
	
	cpi		A, '"'				;Quotation mark? (string literal)
	
	breq	printString0		;If so, continue
	clc							;Clear carry
	ret							;Done
;
printString0:
;
	adiw	X, 1				;Point next byte.
;
printStringLoop:
;					
	ld		A, X+				;Get next byte -> A
	cpi		A, '"'				;Quotation mark? (end of string literal)
	brne	printString1		;Otherwise, continue
	ld		A, X				;If so, get next byte
	sec
	ret							;Done
;
printString1:
;
	rcall 	putByte				;Put byte in output buffer
	rjmp	printStringLoop		;Back for more
;
;	Routine: printNumber
;
;	Purpose: Print number on top of numeric stack
;
printNumber:
;	
	sbr		genStat, 0b00000001	;Inhibit leading zeros
	popRP	H, L				;Get number into HL then repush onto numeric stack
	pushRP	H, L
	tst		H					;Test for sign?
	brmi	pN0					;Branch if negative
	or		H, L				;HL = 0?
	breq	pN1					;If so, Write zero to output buffer
	rjmp	printLineNumber + 1	;Otherwise, continue
;
pN0:
;
	rcall	twosComp16Stk		;Negate the top on numeric stack
	ldi		A, '-'				;Write a minus sign to output buffer
	rcall	putByte
	rjmp	printLineNumber + 1	;Continue
;
pN1:
;
	ldi		A, '0'				;ASCII zero -> A
	rcall	putByte				;Put A in output buffer
	rjmp	printLineNumber + 1	;Continue
;
;	Routine: printLineNumber
;
;	Purpose: Print number on top of numeric stack with leading zeros
;
printLineNumber:
;	
	cbr		genStat, 0b00000001	;Print leading zeros
	pushi	10000				;Divide top of numeric stack by 10000
	rcall	divMod16Stk
	rcall	pLN0				;Write quotient to output buffer with ASCII bias
	pushi	1000				;Divide top of numeric stack (remainder of previous division) by 1000
	rcall	divMod16Stk
	rcall	pLN0				;Write quotient to output buffer with ASCII bias
	pushi	100					;Divide top of numeric stack (remainder of previous division) by 1000
	rcall	divMod16Stk
	rcall	pLN0				;Write quotient to output buffer with ASCII bias
	pushi	10					;Divide top of numeric stack (remainder of previous division) by 1000
	rcall	divMod16Stk
	rcall	pLN0				;Write quotient to output buffer with ASCII bias
;
pLN0:
;
	popRP	H, L				;Top of numeric stack -> HL
	ldi		A, '0'				;Add ASCII bias (ASCII zero)
	add		A, L
	sbrs	genStat, 0			;Check inhibit leading zero bit
	rjmp	putByte				;If cleared, output byte
	cpi		A, '0'				;If set, see if byte is an ASCII 0
	brne	pLN1				;Otherwise, continue
	ret							;If so, do nothing
;
pLN1:
;
	cbr		genStat, 0b00000001	;Clear inhibit leading zero bit
	rjmp	putByte				;Put byte in output buffer
;
;	msgOut
;
msgOut:
;
	clc							;2 * HL -> Z (in code segment)
	rol		L
	rol		H
	mov		ZL, L				;
	mov		ZH, H
;
msgOutLoop:
;
	lpm							;Get LSB byte into A
	mov		A, R0
	cpi		A, 0xFF				;EOF?
	brne	msgOut1				;Otherwise, continue
;
msgOut0:
;
	ret							;Done...return
;
msgOut1:
;
	rcall	putByte				;Write byte to output buffer
	adiw	Z, 1				;Point next byte
	lpm							;Get it into A
	mov		A, R0
	cpi		A, 0xFF				;EOF?
	breq	msgOut0				;If so, done
	rcall	putByte				;Otherwise, put it into output buffer
	adiw	Z, 1				;Point next byte
	rjmp	msgOutLoop			;Look for more
;
;	Routine: gotoStmt
;
;	Purpose: Processes the GOTO statement
;	
gotoStmt:
;
	rcall	skipSpace			;Skip to non-space byte
	call	expression
	popRP	H, L
;
goto0:
;
	ldi		XL, low(prgStart)	;Point X to TB program start
	ldi		XH, high(prgStart)
	movw	Hx:Lx, H:L			;Save HL in HLx
;
gotoLoop:
;
	rcall	getNumber			;Get line number from TB program
	brcs	goto1				;If found then a line number in HL but does it match HLx?
;
	rjmp	LineNumberNotFoundError	;Otherwise, error
;
goto1:
;
	sub		L, Lx				;HL - HLx -> HL
	sbc		H, Hx				;Match?
	breq	goto2				;If so, continue
	brcs	goto3				;If TB program line number less than GOTO line number, keep looking
;
	rjmp	LineNumberNotFoundError	;Otherwise, error
;
;
goto2:							;Ready to execute new line
;	
	movw	CLNH:CLNL, Hx:Lx	;Update current line number
	rcall	skipSpace			;Skip to first non-space
	rjmp	exeStatement		;Done
;
goto3:
;
	ld		A, X+				;Search for CR?
	cpi		A, CR				;End of line?
	brne	goto3				;Otherwise, loop	
	cpi		A, 0xFF				;End of TB program?
	brne	gotoLoop			;Otherwise, check next line number.
;
	rjmp	LineNumberNotFoundError	;If so, should've found by now; error
;
;	Routine: gosubStmt
;
;	Purpose: Processes the GOSUB statement
;	
gosubStmt:
;	
	tst		genStat				;RUN Mode?
	brmi	gosubStmt0			;If so, continue
	rjmp	directModeError		;Otherwise, error
;
gosubStmt0:
;
	mov		A,subLevel			;Max subroutine level reached?
	cpi		A, subLevelMax
	brne	goSubStmt1			;Otherwise, continue
	rjmp	subLevelMaxError	;If so, error
;
gosubStmt1:
;
	movw	H:L,XH:XL			;Save X, the current TB program location
;
gosubStmt1Loop:
;
	ld		A, X+				;Locate beginning of next TB program line?
	rcall	doneCheck			;CR or Colon?
	brne	gosubStmt1Loop		;Otherwise, loop
;
gosubStmt2:
;
	sbiw	X, 1				;Back up one location
	pushRP	XH,XL				;Save it on numeric stack
	inc		subLevel			;Increment subroutine level
	movw	XH:XL,H:L			;Restore X
	rjmp	gotoStmt			;Done
;
;	Routine: returnStmt
;
;	Purpose: Processes the RETURN statement
;	
returnStmt:
;
	tst		genStat				;RUN Mode?
	brmi	returnStmt0			;If so, continue
	rjmp	directModeError		;Otherwise, error
;
returnStmt0:
;
	tst		subLevel			;Subroutine level = 0?
	brne	returnStmt1			;Otherwise, continue
	rjmp	subReturnError		;If so, error
;
returnStmt1:
;
	dec		subLevel			;Decrement subroutine level
	popRP	XH,XL				;Get return TB program location from numeric stack
	rjmp	runP2				;Go execute
;
;	Routine: inputStmt
;
;	Purpose: Processes the INPUT statement
;	
inputStmt:
;
	tst		genStat				;RUN Mode?
	brmi	inputStmt0			;If so, continue
	rjmp	directModeError		;Otherwise, error
;
inputStmt0:
;
	ldi		A, '?'				;Output a question mark prompt
	rcall	xmtByte
	rcall	inBuf
	pushRP	XH,XL				;Save current TB program pointer (X)
	ldi		XL, low(inbuffer)	;Point X to beginning of input buffer
	ldi		XH, high(inBuffer)
	rcall	getNumber			;Get the inputted number into HL
	popRP	XH,XL				;Restore current TB program pointer (X)
	rcall	skipSpace			;Skip to first non-space
	rcall	makeUpperCase		;If letter, make upper case
	cpi		A, 'A'				;Control character?
	brcc	inputStmt1			;Otherwise, continue
	rjmp	expectedVariableError;If so, error
;
inputStmt1:
;
	cpi		A, 'Z' + 1			;ASCII letter A-Z?
	brcs	inputStmt2			;If so, continue
	rjmp	expectedVariableError;Otherwise, error
;
inputStmt2:
;
	rcall	pushA				;Save variable name on numeric stack
	pushRP	H,L					;Save inputted number on numeric stack
	rcall	storeVariable		;Store it at variable named
	rcall	nextByteSS			;Get next TB program byte skipping spaces
	cpi		A, ','				;Comma?
	brne	inputStmt3			;Otherwise, continue
	rcall	nextByteSS			;If so, get next TB program byte skipping spaces 
	rjmp	inputStmt			;Back for another	
;
inputStmt3:
;
	rcall	doneCheck			;Cr or Colon?
	breq	inputStmt4			;If so, continue
	rjmp	expectedEOLError	;EOL error
;
inputStmt4:
;
	rcall	crlf				;New line
	rjmp	runP2				;Done
;
;	Routine: endStmt
;
;	Purpose: Processes the END statement
;
endStmt:
;
	ldi		L, low(endMsg1)			;Point HL to END message
	ldi		H, high(endMsg1)
	rcall	msgOut					;Write to output buffer
	pushRP	CLNH, CLNL				;Put END line number on numeric stack
	rcall	printNumber				;Write to output buffer
	ldi		A, CR					;Write carriage return to output buffer
	rcall	putByte
	rcall	dumpOutBuf				;Send output buffer to serial device
	rcall	crlf					;New line
	rjmp	command					;Back for another command
;
endMsg1:
;
.db	"END AT LINE ", 0xff, 0 		;END message
;
remStmt:
;
	rcall	skipSpace				;Skip to next non-space byte
;
remStmtLoop:
;
	rcall	doneCheck				;CR or Colon;
	breq	remStmtDone				;If so, continue
	rcall	nextByte				;Get next TB program byte
	rjmp	remStmtLoop				;Keep looking
;
remStmtDone:
;
	rjmp	runP2					;Done
;
;   Start Custom Statements
;
;	Statement: PWM(n) Statement 
;
;   Purpose: Outputs pulse width modulated waveform to pin 9 of Redboard where n = percent duty cycle
;
pwmStmt:
;
	ld      A, X			    ;Get current byte
	cpi		A, '('				;Open parentheses?
	breq	pwmStmt0	        ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
pwmStmt0:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression
    call    skipSpace           ;Get first non-space byte
	cpi		A, ')'				;Closed parentheses?
	breq	pwmStmt1            ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
pwmStmt1:
;
	popRP	H, L                ;Get duty cycle
    or      H,L                 ;HL should be 0 to 100, so H must be zero
    breq    pwmStmt2            ;If so, continue
    rjmp    overflowError       ;Otherwise, overflow error
;
pwmStmt2:
;
    mov     A,L
    cpi     A, 101              ;L then should be 0 to 100 or less than 101
    brcs    pwmStmt3            ;If so, continue
    rjmp    overflowError       ;Otherwise, overflow error
;
pwmStmt3:
;
    sbi     ddrb,1              ;Set port b bit 1 (pin 9) output for timer 1 compare register "a"
	ldi		A,0b00000011    	;Prescaler timer 0 N=64 490 Hz
	sts		TCCR1B,A
	ldi		A,0b10000001	    ;Enable timer 1, clear on compare match, and PWM phase correct
                                ;0bxxyy00zz xx = 10 enable to clear timer 1 compare register "a" on compare match when up-counting
                                ;                   and set timer 0 compare register "a" on compare match when down-counting.
                                ;           yy = 00 timer 1 compare register "b" not used
                                ;                   and set timer 1 compare register "b" on compare match when down-counting.
                                ;           zz = 01 phase correct 8-bit mode
 	sts		TCCR1A,A
	sts		ocr1al,L            ;Preset timer 1 compare register "a" duty cycle = 0% (0 out of 255)
    sts     ocr1ah,H     
	rcall	nextByteSS		    ;Get next non-space TB program byte
	rjmp	done
;
;	Statement: OUT(n, m) 
;
;   Purpose: Outputs a low (m = 0) or high (m <> 0) to pin n of Redboard
;
outStmt:
;
	ld      A, X			    ;Get current byte
	cpi		A, '('				;Open parentheses?
	breq	outStmt0	        ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
outStmt0:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression for n
    call    skipSpace           ;Get first non-space byte
	cpi		A, ','				;Should be equal sign?
	breq	outStmt1            ;If so, continue
    rjmp    syntaxError         ;Otherwise, syntax error
;
outStmt1:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression for m
    call    skipSpace           ;Get first non-space byte
	cpi		A, ')'				;Closed parentheses?
	breq	outStmt2            ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
outStmt2:
;
	popRP	h, L                ;get n
    mov     D, H                ;save n
    mov     E, L
;
    popRP   H, L                ;get m
    or      H, H                ;m should be 0 to 13 so H must be zero?
    breq    outStmt3            ;if so, continue
    rjmp    overflowError       ;otherwise, over flow error
;
outStmt3:
;
    cpi     L, 14               ;m should be 0 to 13, so L must be less than 14
    brcs    outStmt4            ;if so, continue
    rjmp    overflowError       ;otherwise, over flow error
;
outStmt4:
;
    cpi     L, 8                ;out to port d?
    brcs    outStmt_portd       ;if so, continue to port d code
                                ;otherwise, it must be port b
    subi    L, 8                ;remove bias
    rcall   cnvtBinBit          ;get bit position into c
    in      B,ddrb              ;make sure bit is output
    or      B, C
    out     ddrb,B
    or      D, E                ;is D:E zero?
    breq    outStmt_portb_clr   ;if so, continue to clear bit
                                ;otherwise, set bit
    in      B, portb            ;read port b to B
    or      B, C                ;set bit at position C
    out     portb, B            ;update port b
    rjmp    outStmt_done
;
outStmt_portb_clr:
;
    ldi     B, 0xff             ;invert bits in C
    eor     C, B
    in      B, portb            ;read port b to B
    and     B, C                ;clear bit at position C
    out     portb, B            ;update port b
    rjmp    outStmt_done
;
outStmt_portd:
;
    rcall   cnvtBinBit          ;get bit position into C
    in      B,ddrd              ;make sure bit is output
    or      B, C
    out     ddrd,B
    or      D, E                ;is D:E zero?
    breq    outStmt_portd_clr   ;if so, continue to clear
                                ;otherwise, set bit
    in      B, portd            ;read port b to B
    or      B, C                ;set bit at position C
    out     portd, B            ;update port b
    rjmp    outStmt_done
;
outStmt_portd_clr:
;
    ldi     B, 0xff             ;invert bits in C
    eor     C, B
    in      B, portd            ;read port b to B
    and     B, C                ;clear bit at position C
    out     portd, B            ;update port b
    rjmp    outStmt_done
;
outStmt_done:
;
	rcall	nextByteSS		    ;Get next non-space TB program byte
	rjmp	done
;
;   Routine: cnvtBinBit
;
;   Purpose: Converts binary value 0 to 7 in L to bit position in C
; 
cnvtBinBit:
;
    ldi     C, 0b00000001       ;preload L with 0b00000001 and prepare to rotate
;
cnvtBinBit_loop:
;
    or      L, L                ;is L = 0?
    breq    cnvtBinBit_done     ;if so, continue
    lsl     C                   ;shift C left once
    dec     L                   ;decrement L
    rjmp    cnvtBinBit_loop     ;do again
;
cnvtBinBit_done:
;
    ret
;
;	Statement: DLY(n) 
;
;   Purpose: Introduces a delay of n = milliseconds
;
dlyStmt:
;
	ld      A, X			    ;Get current byte
	cpi		A, '('				;Open parentheses?
	breq	dlyStmt0	        ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
dlyStmt0:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression
    call    skipSpace           ;Get first non-space byte
	cpi		A, ')'				;Closed parentheses?
	breq	dlyStmt1            ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
dlyStmt1:
;
	popRP	H, L                ;Get n = number of ms
    sts     delay, H            ;save HL in delay
    sts     delay+1, L
;
	ldi		A,0b00000010	    ;enable CTC mode for timer 0
                                ;0bxxyy00zz xx = 00 timer 0 compare register "a" not connected to pin 6
                                ;           yy = 00 timer 0 compare register "b" not connected to pin 5
                                ;          wzz = 010 CTC mode (w is bit 3 of TCCR0B below)
	out		TCCR0A,A
;
    ldi     A,250               ;preset compare register "a" to K=250 giving a 1000 Hz interrupt; adjust up or down to fine tune timing
    out     ocr0a,A
;
    ldi     A,0b00000010        ;enable timer 0 interrupt on count reaching value in compare register "a"
                                ;0b00000xxx xxx = 010 interrupt on count reaching value in compare register "a"
    sts     TIMSK0,A
;
;	Main Program
;		
	ldi		A,0b00000011    	;prescaler N=64; start timer 0
	out		TCCR0B,A
;
    sei                         ;enable global interrupt
;
dlyStmt_wait:
;
    lds     H, delay            ;wait for delay = 0
    lds     L, delay+1
    or      H,L
    brne    dlyStmt_wait
;
    cli                         ;disable global interrupt
	rcall	nextByteSS		    ;Get next non-space TB program byte
	rjmp	done    
;
;   Timer 0 Interrupt Processing Routine
;
TIM0_COMPA:
;
    push    H               ;save HL & A
    push    L
    push    A
    lds     H, delay        ;load delay into hl
    lds     L, delay+1
    mov     A,L             ;if zero, do nothing
    or      A,H
    breq    TIM0_COMPA_done
    subi    L, 1            ;dec hl
    sbci    H, 0
    sts     delay, H        ;save hl
    sts     delay+1,L
;
TIM0_COMPA_done:
;
    pop     A               ;restore A, HL
    pop     L
    pop     H
    reti                    ;set gloabal interrupt and return
;
;   25lc1024 EEPROM instruction set
;
.equ    eeprom_READ = 0b00000011
.equ    eeprom_WRITE = 0b00000010
.equ    eeprom_WREN = 0b00000110
.equ    eeprom_WRDI = 0b00000100
.equ    eeprom_RDSR = 0b00000101
.equ    eeprom_WRSR = 0b00000001
.equ    eeprom_PE = 0b01000010
.equ    eeprom_SE = 0b11011000
.equ    eeprom_CE = 0b11000111
.equ    eeprom_RDID = 0b10101011
.equ    eeprom_DPD = 0b10111001   
;
;	Statement: PUT(p, l, d)
;
;   Purpose: Writes d = data to EEPROM at  p = page and l = location
;
putStmt:
;
;	Set Up SPI
;
	ldi		A,0b00101100    ;port b bits 2 (CE), 3 (MOSI), and 5 (SCK) as output                           ;                   
	out		ddrb,A          ;update data direction register B
    sbi     portb,2         ;deselect eeprom
	ldi		A,0b01010001    ;0btuvwxyzz t = 0 disable SPI interrupt (See section 23.5.1)
                            ;           u = 1 enable SPI                
                            ;           v = 1 least significant bit (LSB) first                
                            ;           w = 1 select ATmega328 as master                
                            ;           xx = 00 SPI mode 0                
                            ;           zz = 01 prescale = 16 for SPI clock = 1 Mhz ("Table 23-5" in ATmega328 Datasheet)
	out		spcr,A       ;configure SPI control register
;
;   Check that EEPROM connected
;
    rcall   EEPROM_esig         ;get EEPROM electronic signature
    cpi     A, 41               ;is 41?
    breq    putSTMT_cont        ;if so, continue
    rjmp    spiError            ;otherwise, spi error
;
putStmt_cont:
;
	ld      A, X			    ;Get current byte
	cpi		A, '('				;Open parentheses?
	breq	putStmt0	        ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
putStmt0:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression for n
    call    skipSpace           ;Get first non-space byte
	cpi		A, ','				;Should be equal sign?
	breq	putStmt1            ;If so, continue
    rjmp    syntaxError         ;Otherwise, syntax error
;
putStmt1:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression for m
    call    skipSpace           ;Get first non-space byte
	cpi		A, ','				;Should be equal sign?
	breq	putStmt2            ;If so, continue
    rjmp    syntaxError         ;Otherwise, syntax error
;
putStmt2:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression for m
    call    skipSpace           ;Get first non-space byte
	cpi		A, ')'				;Closed parentheses?
	breq	putStmt3            ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
putStmt3:
;
	popRP	H, L                ;data in HL
    push    H                   ;save HL
    push    L
    popRP   B, C                ;get location in C
    push    C                   ;save location
    subiw   B, C, 128           ;location must be <= 127?
    pop     C                   ;restore location
    brcs    putStmt4            ;if so, continue
    rjmp    overflowError       ;otherwise, overflow error
;
putStmt4:
; 
    lsl     C                   ;double location
    popRP   D, E                ;get page in DE
    push    D                   ;save page
    push    E
    subiw   D, E, 512           ;page must be <= 511
    pop     E                   ;retrieve page
    pop     D
    brcs    putSTMT5            ;if so, continue
    rjmp    overflowError       ;otherwise, overflow error
;
putStmt5:
; 
    pop     L                   ;retrieve data
    pop     H
    rcall   EEPROM_write_word;write data to EPROM
	rcall	nextByteSS		    ;Get next non-space TB program byte
	rjmp	done    
;
;   SPI Read Electronic Signature
;
EEPROM_esig:
;
    cbi     portb,2         ;select eeprom
    ldi     A,eeprom_RDID ;send esig request
    rcall   spi_write
;
    ldi     B,3           ;send 3 dummy 24-byte address
;
EEPROM_esig_loop:
;
    ldi     A,0
    rcall   spi_write
    dec     B             ;done?
    brne    EEPROM_esig_loop ;if not, loop
;
    rcall   spi_read        ;read esig
    sbi     portb,2         ;deselect eeprom
    ret
;
;   SPI Write Word (Double Byte)
;
;   HL contains the word to written
;   D:E:C contain the 24-bit address
;
EEPROM_write_word:
;
    cbi     portb,2         ;select eeprom
    ldi     A,eeprom_WREN ;send write enable request
    rcall   spi_write
    sbi     portb,2         ;deselect eeprom
    cbi     portb,2         ;select eeprom
    ldi     A,eeprom_WRITE ;send write request
    rcall   spi_write
    mov     A,D         ;send 24-bit address
    rcall   spi_write
    mov     A,E
    rcall   spi_write
    mov     A,C
    rcall   spi_write
    mov     A,H           ;send most significant byte to be written
    rcall   spi_write
    mov     A,L           ;send least significant byte to be written
    rcall   spi_write
    sbi     portb,2         ;deselect eeprom and start write cycle
;
circuit_write_word_wait:
;
    rcall   spi_read_SDSR ;read status register
    sbrc    A,0           ;wait while WIP bit 0 set (write in progress)
    rjmp    circuit_write_word_wait
    ret
;
;   SPI Read Word (Double Byte)
;
;   HL contains the byte read
;   D:E:C contain the 24-bit address
;
EEPROM_read_word:
;
    cbi     portb,2         ;select eeprom
    ldi     A,eeprom_READ ;send read request
    rcall   spi_write
    mov     A,D         ;send 24-bit address 
    rcall   spi_write
    mov     A,E
    rcall   spi_write
    mov     A,C
    rcall   spi_write
    rcall   spi_read       ;read most significant byte
    mov     H,A
    rcall   spi_read       ;read least significant byte
    mov     L,A
    sbi     portb,2        ;deselect eeprom
    ret
;
;   SPI Read Status Register into A
;
spi_read_SDSR:
;
    cbi     portb,2         ;select eeprom
    ldi     A,eeprom_RDSR ;send status register read request
    rcall   spi_write
    rcall   spi_read        ;read status register
    sbi     portb,2         ;deselect eeprom and start write cycle
    ret
;
;   SPI Write A
;
spi_write:
;
    out     spdr,A        ;send byte to SPI
;
spi_write_wait:
;
    in      A,spsr        ;write completed?
    sbrs    A,spif        ;if not, wait
    rjmp    spi_write_wait
    ret
;
;   SPI Read into A
;
spi_read:
;
    ldi     A,0           ;send dummy byte to spi
    out     spdr,A
;
spi_read_wait:
;
    in      A,spsr        ;write completed?
    sbrs    A,spif        ;if not, wait
    rjmp    spi_read_wait
    in      A,spdr        ;get return byte
    ret
;
;   End Custom Statements
;
;	Routine: skipSpace
;
;	Purpose: Skips to first non-space byte
;
skipSpace:
;
	ld		A, X					;Get TB program byte?
	cpi		A, ' '					;A space?
	breq	skipSpace0				;If so, continue
	ret								;Otherwise, done...return
;
skipSpace0:
;
	adiw	X, 1					;Point next TB program byte
	rjmp	skipSpace				;Loop	
;			
;	Routine: nextByte
;
;	Purpose: Loads next TB program byte at (X) and returns.
;
nextByte:
;
	adiw	X, 1					;Point next TB program byte?
;
getByte:
;
	ld		A, X					;Get TB program byte
	ret								;Done...return
;			
;	Routine: nextByteSS
;
;	Purpose: Loads next TB program byte at (X), then skips to first non-space  byte
;
nextByteSS:
;
	adiw	X, 1					;Point next TB program byte
	rjmp	skipSpace				;Skip spaces	
;		
;			
;	Routine: getNumber
;
;	Purpose: Checks at X for a number represented in ASCII. If present, return it in HL with carry set.
;			 Otherwise present, return with HL=0 and carry cleared.
;
getNumber:	
;
	clr		H					;0->HL
	clr		L
	cbr		genStat, 0b00000100	;Clear digit flag bit
;
gnLoop:
;
	ld		A, X				;Get TB program byte?
	subi	A, '0'				;Remove ASCII bias
	brcs	gnDone0				;If byte < ASCII zero, continue
	cpi		A, 10				;byte > ASCII nine?
	brcc	gnDone0				;If so, continue
	ldi		E, 10				;10 -> DE
	clr		D
	rcall	mult16				;10 * HL -> BC
	clr		D
	add		L, A				;10 * HL + A -> HL
	adc		H, D
	sbr		genStat, 0b00000100	;Found at least one digit, so set digit flag bit
	adiw	X, 1				;Next byte
	rjmp	gnLoop				;Loop
;
gndone0:
;
	clc							;Clear carry
	sbrc	genStat, 2			;Skip next statement if set digit flag cleared
	sec							;Set carry (at least one digit found)
	ret							;Done...return
;
;	Zero variables A-Z
;
zeroVariables:
;
	ldi	ZL, low(varStart)
	ldi	ZH, high(varStart)
;
	ldi	B, 2 * 26
	ldi	C, 0
;
zV0:
;
	st		Z+, C
	dec		B
	brne	zV0
	ret
;
;	Twos Complement top of numeric stack; result on numeric stack
;
twosComp16Stk:
;
	popRP	H, L
	rcall	twosComp16
	pushRP	H, L
	ret
;
;	Add top two numbers on numeric stack; sum on numeric stack
;
add16Stk:
;
	popRP	D, E
	popRP	H, L
	rcall	add16
	pushRP	H, L
	ret
;
;	Subtrack top two numbers on numeric stack; difference on numeric stack
;
sub16Stk:
;
	popRP	D, E
	popRP	H, L
	rcall	sub16
	pushRP	H, L
	ret
;
;	Multiply top two numbers on numeric stack; product on numeric stack
;
mult16Stk:
;
	popRP	D, E
	popRP	H, L
	rcall	mult16
	pushRP	H, L
	ret
;
;	Divide top two numbers on numeric stack; quotient on numeric stack
;
div16Stk:
;
	popRP	D, E
	popRP	H, L
	rcall	div16
	pushRP	H, L
	ret
;
;
;	Divide top two numbers on numeric stack; modulo (remainder) on numeric stack then quotient on numeric stack
;
divMod16Stk:
;
	popRP	D, E
	popRP	H, L
	rcall	div16
	pushRP	B, C
	pushRP	H, L
	ret
;
;	Modulo top two numbers on numeric stack; modulo (remainder) on numeric stack
;
mod16Stk:
;
	popRP	D, E
	popRP	H, L
	rcall	div16
	pushRP	B, C
	ret
;
;	16 Bit twosComp HL -> HL
;
twosComp16:
;
	ldi		A, 0xff
	eor		L, A
	eor		H, A
	subi	L, low(-1)
	sbci	H, high(-1)
	ret
;
;	16 Bit Add HL + DE -> HL
;
add16:
;
	add		L, E
	adc		H, D
	ret
;
;	16 Bit Subtract HL - DE -> HL
;
sub16:
;
	clc
	sub		L, E
	sbc		H, D
	ret	
;
;	16 Bit Mulitiply HL * DE -> HL
;
mult16:
;
	mul		L, E		; al * bl
	movw	B:C,r1:r0
 	mul		H, E		; ah * bl
 	add		B, r0
 	mul		D, L		; bh * al
    add		B, r0
	mov		L, C
	mov		H, B
    ret
;
;	16 Bit Divide HL / DE -> HL with BC remainder
;
div16:
;
	clr		C				;clear remainder Low byte
	sub		B,B				;clear remainder High byte and carry
	ldi		A,17			;init loop counter
;
div16_1:
;
	rol		L				;shift left dividend
	rol		H	
	dec		A				;decrement counter
	brne	div16_2			;if done
	ret						;	return
;
div16_2:
;
	rol		C				;shift dividend into remainder
	rol		B
	sub		C,E				;remainder = remainder - divisor
	sbc		B,D				;
	brcc	div16_3			;if result negative
	add		C,E				;	restore remainder
	adc		B,D
	clc						;	clear carry to be shifted into result
	rjmp	div16_1			;else
;
div16_3:
;
	sec						;set carry to be shifted into result
	rjmp	div16_1

;
;	Routine: getFunction
;
;	Purpose: Gets value in HL for function. Carry set if function found; carry cleared otherwise
;
getFunction:
;
	ldi		ZL, low(2 * fTable)	;Point Z to start of function table
	ldi		ZH,	high(2 * fTable)
	rcall	findReserveWord		;Item in table?
	brcs	getFunction0		;If so, continue...C contains number of item in table
    ld      A, X                ;Get current byte
	ret							;Otherwise, done...return with carry cleared
;
getFunction0:
;
	ldi		L, low(fCallTable)	;Point Z to start of function rcall table
	ldi		H, high(fCallTable)
 	clr		B					;
	add		L, C				;HL + BC -> HL 
	adc		H, B
    clc                         ;2*HL -> HL
    rol     L
    rol     H
    movw	ZH:ZL, H:L			;HL->Z
	lpm							;Read LSB of word in code segment
	mov		L, R0				;Save LSB in L
	adiw	Z, 1				;Point MSB of word in code segment
	lpm							;Read MSB of word in code segment
	mov		H, R0				;Save MSB in H
	movw	ZH:ZL,H:L			;HL->Z
	icall						;Call function
	sec							;Set carry
	ret							;Done...return
;
;   Function Name Table
fTable:
;
;	Note: Table must match fCallTable!
;
	.db		"RN", 'D' + 0x80, "AD", 'C' + 0x80, "PI", 'N' + 0x80, "GE", 'T' + 0x80, 0xff, 0
;
fCallTable:
;
	.db		low(getRandomNumber), high(getRandomNumber)
	.db		low(getADC), high(getADC)
	.db		low(getPin), high(getPin)
	.db		low(getGET), high(getGET)
;
;   Function Subroutines
;
;	Function: RND(n)
;
;	Purpose: Returns a psuedo-random number in HL.  If n = 0, returns same number each time.
;
getRandomNumber:
;
	ld		A, X+               ;Get current TB program byte
	cpi		A, '('				;Open parentheses?
	breq	getRN1				;If so, continue
	rjmp	unpairedParenError	;Otherwise, unpaired parentheses error
;
getRN1:
;
	rcall	expression			;Get parameter n
;
getRN2:
;
	popRP	H, L
	or		H, L				;n = 0?
	lds		L, seed3			;If so, get last random number and return
	lds		H, seed4
	brne	getRN3				;Otherwise, continue
	rjmp	getRN4				;Done
;
getRN3:
;
	ldi		ZL, low(seed4)		;Point Z to Seed4
	ldi		ZH, high(seed4)
;
getRN3Loop1:
;
	ldi		B, 8				;Prepare to loop 8 times

;
getRN3Loop2:
;
	clr		C					;Manipulate seeds
	ld		A, Z
	lsl		A
	adc		A, C
	lsl		A
	adc		A, C
	lsl		A
	adc		A, C
	ld		C, Z
	eor		A, C
	rol		A
	rol		A
	ld		A, -Z
	ld		A, -Z
	ld		A, -Z
	rcall	getRN6
	rcall	getRN6
	rcall	getRN6
	rcall	getRN6
	sbiw	Z, 1
	dec		B
	brne	getRN3Loop2
	ldi		C, 0x27
	lds		A, seed4
	andi	A, 0x3f
	sts		seed4, A
	sub		C, A
	brcs	getRN3Loop1
	brne	getRN4
	ldi		C, 0x10
	lds		A, seed3
	sub		C, A
	brcs	getRN3Loop1
;
getRN4:
;
	rcall    skipSpace 
	cpi		A, ')'  		    ;Closed parentheses?
	breq	getRN5				;If so, continue
	rjmp	unpairedParenError	;Otherwise, unpaired parentheses error
;
getRN5:
;
    rcall   nextByte            ;Get next TB program byte
	ret							;Done...return with random number in HL
;
getRN6:
;
	ld		A, Z				;(Z)->A
	rol		A					;2 * A + carry
	st		Z+, A				;A->(Z)
	ret							;Done...return with random number in HL
;
;	Routine: seedRandomNumber
;
;	Purpose: Seed random number with HL.
;
seedRandomNumber:
;
	ldi		ZL, low(seed4)		;Point Z to Seed4
	ldi		ZH, high(seed4)
	sts		seed3, L			;Manipulate seeds
	sts		seed4, H
	rol		L
	swap	L
	rol		H
	swap	H
	sts		seed1, H
	sts		seed2, L
	ret 
;
;   Start Custom Functions
;
;   Function: DAC(n)
;
;   Purpose: Returns value from A/D channel n value in HL
;
getADC:
;	
    rcall   skipSpace           ;Get next non-space TB program byte
	cpi		A, '('    		    ;Open parentheses?
	breq	getADC1				;If so, continue
	rjmp	unpairedParenError	;Otherwise, unpaired parentheses error
;
getADC1:
;
    rcall   nextByte            ;Get next TB program byte
	rcall	expression			;Get A/D channel number n into HL
    popRP	H, L                ;Is n on the range of 0 to 5?
    or      H,H                 ;Is H = 0?
    breq    getADC2             ;If so, continue
    rjmp    overflowError       ;Otherwise, overflow error
;
getADC2:
;
    cpi     L, 6                ;Is L between 0 and 5
    brcs    getADC3             ;If so, continue
    rjmp    overflowError       ;Otherwise, overflow error
;
getADC3:
;
;	Set Up Analog-to-Digital Converter
;
	ldi		A,0b01000000  ;0bxxy0zzzz xx = 01 use internal AVcc for reference  ("Table 28-3" in ATmega328 Datasheet)                           ;                   
                          ;           y = 0 right adjust result (Search for "Bit 5 – ADLAR" in ATmega328 Datasheet)
                          ;           zzzz = 0000 A/D channel 0 ("Table 28-4" in ATmega328 Datasheet)
    or      A,L           ;fix A/D channel to be read by or'ing with n
	sts		admux,A       ;ADC Multiplexer Selection Register (Section "28.9.1" in ATmega328 Datasheet)
;
	ldi		A,0b11000111  ;0bxy000zzz x = 1 enable A/D  (Search for "Bit 7 – ADEN" in ATmega328 Datasheet)
                          ;           y = 1 start conversion (Search for "Bit 6 – ADSC" in ATmega328 Datasheet)                
                          ;           zzz = 111 prescale = 128 (Section "Table 28-5")
	sts		adcsra,A      ;ADC Control and Status Register A (Section "28.9.2" in ATmega328 Datasheet)
;		
getADC4:
;
	lds		A,adcsra		;Is A/D conversion complete?
	sbrc	A,adsc	    	;ADSC cleared?
	rjmp	getADC4     	;If not, wait
	lds		L,adcl		    ;A/D result in HL
	lds		H,adch
;
	rcall   skipSpace       ;Get next non-space TB program byte
	cpi		A, ')'      	;Closed parentheses?
	breq	getADC5			;If so, continue
	rjmp	unpairedParenError	;Otherwise, unpaired parentheses error
;
getADC5:
;
    rcall   nextByte        ;Get next TB program byte
	ret						;Done...return with A/D value in HL ???
;
;   Function: PIN(n)
;
;   Purpose: Returns status of Redboard pins n = 0 to 13 (0=low 1=high) in HL
;
getPin:
;
	ld      A, X			    ;Get current byte
	cpi		A, '('				;Open Parentheses?
	breq	getPin0	            ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
getPin0:
;
	rcall	nextByteSS			;Get next non-space TB program byte
	rcall	expression			;Evaluate expression for m
    call    skipSpace           ;Get first non-space byte
	cpi		A, ')'				;Closed parentheses?
	breq	getPin1             ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
getPin1:
;
    popRP   h, l                ;Get pin number n
    or      h, h                ;n should be 0 to 13 so h must be zero?
    breq    getPin2             ;If so, continue
    rjmp    overflowError       ;Otherwise, over flow error
;
getPin2:
;
    cpi     l, 14               ;n should be 0 to 13, so l must be less than 14
    brcs    getPin3             ;If so, continue
    rjmp    overflowError       ;Otherwise, over flow error
;
getPin3:
;
    cpi     l, 8                ;Input to port d (pins 0 to 7)?
    brcs    getPin_portd        ;If so, continue to port d code
                                ;Otherwise, port b
    subi    l, 8                ;Remove bias to get bits 0 to 5
    rcall   cnvtBinBit          ;Get bit position into c
    mov     E, C                ;Save C in E
    ldi     D, 0xff
    eor     C, D                ;Complement C
    in      B,ddrb              ;Make sure bit is input
    and     B, C
    out     ddrb,B
    in      B, pinb             ;Read pin b to b
    and     B, E                ;Check bit at position c = 
    ldi     L,1                 ;Pin high?
    brne    getPin_done         ;If so, continue with L=1
    ldi     L,0
    rjmp    getPin_done         ;Otherwise, L=0       
;
getPin_portd:
;
    rcall   cnvtBinBit          ;Get bit position into C
    mov     E,C                 ;Save C in E
    ldi     D, 0xff
    eor     C, D                ;Complement C
    in      B,ddrd              ;Make sure bit is input
    and     B, C
    out     ddrd,B
    in      B, pind             ;Read pin d to b
    and     B, E                ;Check bit at position c
    ldi     L,1                 ;Pin high?
    brne    getPin_done         ;If so, continue with L=1
    clr     L
    rjmp    getPin_done         ;Otherwise, L=0       
;
getPin_done:
;
    clr     H                   ;H=0
	rcall	nextByte		    ;Get next TB program byte
	ret                         ;Done...return with pin status in HL  
;
;	Function: GET(p, l) 
;
;   Purpose: Returns data in HL from EEPROM where p = page and l = location 
;
getGet:
;
;	Set Up SPI
;
	ldi		A,0b00101100        ;Port b bits 2 (CE), 3 (MOSI), and 5 (SCK) as output                           ;                   
	out		ddrb,A              ;Update data direction register B
    sbi     portb,2             ;Deselect eeprom
	ldi		A,0b01010001        ;0btuvwxyzz t = 0 disable SPI interrupt (See section 23.5.1)
                                ;           u = 1 enable SPI                
                                ;           v = 0 most significant byte (MSB) first                
                                ;           w = 1 select ATmega328 as master                
                                ;           xx = 00 SPI mode 0                
                                ;           zz = 01 prescale = 16 for SPI clock = 1 Mhz ("Table 23-5" in ATmega328 Datasheet)
	out		spcr,A              ;Configure SPI control register
;
;   Check that EEPROM connected
;
    rcall   EEPROM_esig         ;Get EEPROM electronic signature
    cpi     A, 41               ;Is 41?
    breq    getGet_cont         ;If so, continue
    rjmp    spiError            ;Otherwise, spi error
;
getGet_cont:
;	
    ld      A, X			    ;Get current byte
	cpi		A, '('				;Open parentheses?
	breq	getGet0	            ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
getGet0:
;
	rcall	nextByteSS			;Move next non-space byte
    rcall   expression          ;Get EEPROM page on numeric stack
    rcall   skipSpace           ;Get first non-space byte
    cpi     A, ','              ;Should be a comma?
    breq    getGet1             ;If so, continue
    rjmp    syntaxError         ;Otherwise, syntax error
;
getGet1:
;
    rcall   nextByteSS          ;Get next non-space byte
	rcall	expression			;Get EEPROM location on numeric stack
    call    skipSpace           ;Get first non-space byte
	cpi		A, ')'				;Closed parentheses?
	breq	getGet2             ;If so, continue
    rjmp    unpairedParenError  ;Otherwise, unpaired parentheses error
;
getGet2:
;
    popRP   B, C                ;Get location in C
    push    C                   ;Save location
    subiw   B, C, 128           ;Location must be <= 127?
    pop     C                   ;Restore location
    brcs    getGet3             ;If so, continue
    rjmp    overflowError       ;Otherwise, overflow error
;
getGet3:
; 
    lsl     C                   ;Double location (data is two bytes)
    popRP   D, E                ;Get page in DE
    push    D                   ;Save page
    push    E
    subiw   D, E, 512           ;Page must be <= 511?
    pop     E                   ;Retrieve page
    pop     D
    brcs    getGet4             ;If so, continue
    rjmp    overflowError       ;Otherwise, overflow error
;
getGet4:
;
    rcall   EEPROM_read_word    ;Get EEPROM data into HL
    rcall   nextByte            ;Get next TB program byte
    ret
;
;   End Custom Functions
;
;	Routine: getVariable
;
;	Purpose: Gets value in HL for variable (ASCII A-Z) designated 
;			 by register A. Carry set if variable found; carry cleared otherwise.
;
getVariable:
	rcall	makeUpperCase		;If letter, make upper case
	cpi		A, 'A'				;Valid variable (A-Z)?
	brcs	gVar0				;Otherwise, continue
	cpi		A, 'Z' + 1
	brcc	gVar0				;Otherwise, continue
	subi	A, 'A'				;Remove ASCII bias
	mov		E, A				;A->DE
	clr		D
	lsl		E					;2*DE->DE (Allows two bytes per variable)
	rol		D
	ldi		ZL, low(varStart)	;Point Z to start of variables
	ldi		ZH,	high(varStart)
	add		ZL, E				;Add variable location bias
	adc		ZH, D
	ld		L, Z+				;LSB of variable to L
	ld		H, Z+				;MSB of variable to H
	adiw	X, 1				;Get next TB program byte
	sec							;Set carry for successful return
	ret							;Done...return
;
gVar0:
;
	clc							;Clear carry for unsuccessful return
	ret							;Done...return
;
;	Routine: storeVariable
;
;	Purpose: Stores value (first two bytes on numeric stack in low-high order)
;			 in the variable next on numeric stack
;
storeVariable:
;
	popRP	H, L	;value -> HL
	rcall	popA	;ASCII variable (A-Z) -> DE
	mov		E, A
	ldi		D, 0
;
;	Routine: putVariable
;
;	Purpose: Store value in HL in variable (A-Z) designated 
;			 by register DE
;
putVariable:
;
	subi	E, 'A'				;Remove	ASCII bias		;
	lsl		E					;2*DE->DE (allows two bytes per variable)
	rol		D
	ldi		ZL, low(varStart)	;Point Z to start of variables
	ldi		ZH,	high(varStart)
	add		ZL, E				;Add variable location bias				;
	adc		ZH, D
	st		Z+, L				;Store LSB
	st		Z+, H				;Store MSB
;
	ret							;Done...return
;
;	Routine: pushS
;
;	Purpose: Store SL at the numeric stack pointer location then
;			 increment numeric stack pointer (Y register); 
;			 store SH at the numeric stack pointer location then
;			 increment numeric stack pointer (Y register) 	 	
;
pushS:
;
	mov		A, SL
	rcall	pushA
	mov		A, SH
	rcall	pushA
	ret
;
;	Routine: popS
;
;	Purpose: Decrement numeric stack pointer (Y register) and 
;			 load SH from the location, then decrement numeric
;			 stack pointer (Y register) and load SL from the 
;			 location
;
;	Assumes: NA
;
popS:
;
	rcall	popA
	mov		SH, A
	rcall	popA
	mov		SL, A
	ret
;
;	Routine: pushA
;
;	Purpose: Store A at the numeric stack pointer location then
;			 increment numeric stack pointer (Y register) 			 
;
pushA:
;
	cpi		YL, low(numSTK + numStkSize)
	breq	numStackOverflowError
	st		Y+, A
	ret
;
;	Routine: popA
;
;	Purpose: Decrement numeric stack pointer (Y register) and 
;			 load A from the location
;
popA:
;
	cpi		YL, low(numSTK)
	breq	numStackUnderflowError
	ld		A, -Y
	ret
;
;	Routine: Clear output buffer
;
clearOutputBuffer:
;
	ldi		ZL, low(outBuffer)		;Point Z to start of output buffer
	ldi		ZH, high(outBuffer)
	ldi		A, outBufferSize		;Output buffer size -> A
	clr		B						;0 -> B
;
clearOutputBufferLoop:
;
	st		Z+, B					;B -> (Z) then increment Z		
	dec		A						;Decrement A
	brne	clearOutputBufferLoop	;If A not zero, loop
	clr		A						;Set output buffer pointer to zero
	sts		outBufPtr, A
	ret								;Done...return
;
;	Error handling - Set B to error code (1-xx)
;
syntaxError:
;
	ldi		b, 0
	rjmp	error
;
directModeError:
;
	ldi		B, 1
	rjmp	error
;
runModeError:
;
	ldi		B, 2
	rjmp	error
;
subLevelMaxError:
;
	ldi		b, 3
	rjmp	error
;
subReturnError:
;
	ldi		b, 4
	rjmp	error
;
lineNumberNotFoundError:
;
	ldi		b, 5
	rjmp	error
;
expectedLineNumberError:
;
	ldi		b, 6
	rjmp	error
;
overflowError:
;
	ldi		B, 7
	rjmp	error
;
divideByZeroError:
;
	ldi		B, 8
	rjmp	error
;
;
numStackOverflowError:
;
	ldi		B, 9
	rjmp	error
;
;
numStackUnderflowError:
;
	ldi		B, 10
	rjmp	error
;
outBufferOverflowError:
;
	ldi		B, 11
	rjmp	error
;
unpairedParenError:
;
	ldi		B, 12
	rjmp	error
;
missingRelopError:
;
	ldi		B, 13
	rjmp	error
;
expectedVariableError:
;
	ldi		B, 14
	rjmp	error

expectedEOLError:
;
	ldi		B, 15
	rjmp	error
;
expectedNumberError:
;
	ldi		B, 16
	rjmp	error
;
outOfMemoryError:
;
	ldi		B, 17
	rjmp	error;
;
spiError:
;
	ldi		B, 18
	rjmp	error
;
error:
;
	mov		errorCode, B		;Store error code
;
error0:
;
	rcall	clearOutputBuffer		;Clear output buffer
	rcall	crlf					;New line
	ldi		L, low(errorMsg0)		;Point HL to first error message
	ldi		H, high(errorMsg0)
	rcall	msgOut					;Write to output buffer
	mov		L, errorCode			;Put error number on numeric stack
	clr		H
	pushRP	H, L
	ldi		L, low(100)				;Put 100 on numeric stack
	ldi		H, high(100)
	pushRP	H, L
	rcall	add16Stk				;Add them; error number plus 100 now on top of numeric stack
	rcall	printNumber				;Write it to output buffer
	ldi		L, low(errorMsg1)		;Point HL to second error message
	ldi		H, high(errorMsg1)
;
error1:
;
	rcall	msgOut					;Write it to output buffer
	pushRP	CLNH, CLNL				;Put current line number on numeric stack
	rcall	printNumber				;Write it to output buffer
	ldi		A, CR					;Write a CR to output buffer				
	rcall	putByte	
	rcall	dumpOutBuf				;Send output buffer to serial port
	rcall	crlf					;New line
;	
	rjmp	command					;Back to command mode
;
errorMsg0:
;
	.db		"ERROR ", 0xff, 0
;
errorMsg1:
;
	.db		" IN LINE ", 0xff
;
;
;	Input: Binary value in A, T flag set to inhibit leading zeros
;
;	Returns: Nothing
;
;	Registers Used: r16,r17,r18,sreg
;
decout:
	mov		r18,A			;r16 -> r18
	ldi		A,' '			;output space
	rcall	xmtByte
	ldi		A,'0'-1		; ASCII 0 less 1 -> r16
dloop100:
	inc		A				;next ACSCII numeral
	subi	r18,100			;subtract 100
	brcc	dloop100		;if r18 > 0 then do again
	addi	r18,100			;otherwise, add back 100
	rcall	xmtByte			;output ASCII numneral
;
	ldi		A,'0'-1		; ASCII 0 less 1 -> r16
dloop10:
	inc		A				;next ACSCII numeral
	subi	r18,10			;subtract 10
	brcc	dloop10			;if r18 > 0 then do again
	addi	r18,10			;otherwise, add back 10
	rcall	xmtByte			;output ASCII numneral
	ldi		A,'0'-1		; ASCII 0 less 1 -> r16
dloop1:
	inc		A				;next ASCII numeral
	subi	r18,1			;subtract 1
	brcc	dloop1			;if r18 > 0 then do again
	addi	r18,1			;otherwise, add back 1
	rcall	xmtByte			;output ASCII numneral and return
self:
    rjmp    self
;
;	Data Segment
;
.dseg
;
.org	SRAM_START
;
;	Variable Storage
;
;	A - Z each a dataword
;
varStart:	.byte	2 * 26
;
;	Keyboard input buffer
;
inBuffer:	.byte	inBufferSize
;
;	Monitor output buffer
;
outBuffer:	.byte	outBufferSize + 1
;
outBufPtr:	.byte	1
;
;	Random number bytes
;
seed1:		.byte	1
seed2:		.byte	1
seed3:		.byte	1
seed4:		.byte	1
;
;	Numeric stack
;
numStk:		.byte	numStkSize
;
;   Custom SRAM
;
delay:  .byte   2   ;delay in ms
;
prgEnd:		.byte	2
;
;	TINY BASIC program start
;
prgStart:	.byte	1
