#define PCODE_INTERPRETER

//* #define DEBUG

//* #define BETA

#include "pcode.h"

//*
//*
//* PCODE INTERPRETER for C and Logo compilers
//*
//* by Randy Sargent and Fred Martin
//* Some changes by Anne Wright
//* PWM update by Julian Skidmore
//*
//*
//* REVISION HISTORY:
//*
//* VERSION 1.0
//* first created March 16, 1990
//*
//* used in the Computer Museum contest with Logo compiler in April, 1990
//*
//* update July 25, 1990:
//*	reconstructed "execute on startup" code, FGM
//*
//*
//* changes begun Oct 7, 1990:
//*	added support for arrays and floating point.
//*
//* changes as of Dec 23: begin support for multitasking.
//*
//* First release V1.00
//* LCD printing Fri Jan 11 00:45:53 1991
//* PWM motor speed Fri Jan 11 00:46:00 1991
//* LCD translation table Fri Jan 11 01:26:58 1991
//*
//* V1.01		Sun Jan 13 23:58:31 1991
//* fixed printf \n bug; 2nd \n was printed directly, now ignored.
//*
//* V1.10		Thu Jan 17 21:39:56 1991
//* swapped order of arguments to Pstartprocess to ticks first.
//* installed version number at end of RAM.
//*
//* V1.20		Thu Jan 17 21:39:56 1991
//* added zero length array next to the end of RAM.
//* fixed arefs to work
//* added checkstack, jump from stack pcodes
//* added pushblock pcode
//*
//* V1.30		Tue Jan 22 00:25:40 1991
//* made floating point trig routines use radians
//* added Psetsp
//*
//* V1.31		Tue Jan 22 14:10:17 1991
//* enabled powerdown and TOC5 interrupts from within 1kHz routine
//*
//* V1.32		Tue Jan 22 23:48:54 1991
//* fixed bug in mret_die whereby scheduler tried to use dead process
//*
//* V1.40		Wed Jan 23 01:37:58 1991
//* more convincingly fixed previous bug;
//* fixed PSTAT_DEAD to return properly and not signal UI
//*
//* V1.50
//* V1.60
//* lost in power outage, Fri Jan 25 1991
//*
//* V1.70 	Sun Jan 27 22:39:27 1991
//* arc tangent fixed
//* floating point add and sub modified so they cannot return error
//* installed trap for error 0 (which shouldn't ever happen)
//* Pbitset, Pbitclr pcode instructions added
//* IR decoding installed
//*
//* V1.80		Mon Jan 28 00:55:07 1991
//* Pfneg added
//*
//* V1.90		Mon Jan 28 01:13:23 1991
//* memory map reorganized to optimize code space
//*
//* V1.91		Tue Jan 29 02:37:11 1991
//* quadrature encoder code added
//*
//* V1.92		Sat Feb  2 00:08:32 1991
//* manual power-down + boot-up memory test added
//*
//* V2.00
//* added benchmark pcode
//* allow control of SystemInt fcns
//* allow control of IR transmission
//* randy's PWM routine
//*
//* V2.01
//* minor optimizations to IR detection.
//*
//* V2.10
//* moved memory to be compatible with special vs. non-special mode
//*
//* V2.11
//* put vectors both at bfc0 and ffc0 so same pcode may be used on different
//* boards
//*
//* V2.20
//* new opcodes (unimplemented) for support of longs, defer
//*
//* V2.21
//* merged rev2 and bookbot sources (#define SIMPLE for bookbot)
//*
//* V2.30		Sun Apr  7 19:55:19 1991
//* implemented 32-bit integer operations Padd4, Psub4, Plt4, Pgt4, and Pequal4;
//* Pdefer, and Psystime
//*
//* V2.40		Sun Apr  7 22:10:03 1991
//* implemented Pflt2lng, Plng2flt, and Pmult4
//* converted several JSR's to BSR's due to new assembler suggestions
//*
//* V2.41		Thu May 23 14:25:41 1991
//* brought over some yamabico, bookbot changes from ai lab
//* make BOOKBOT define for bookbot
//*
//* V2.42		Mon Jul 15 09:38:42 1991	fgm
//* wrote PWM code for BOOKBOT
//*
//* V2.50		Tue Sep 17 21:36:24 1991	jrs
//* wrote Pcallml
//*
//* V2.51		Sun Sep 22 19:05:32 1991	fgm
//* made some changes for s-bot:  frob switch addressing, default motor
//* state, LCD display control, power-off based on battery level sensing.
//*
//* V2.52		Wed Sep 25 21:01:58 1991	fgm
//* turned off TIC1 (power-off interrupt) for SBOT; set up port D mux
//* pins for output.
//*
//* V2.60		Thu Oct 10 17:58:48 1991	fgm
//* wrote Pinitint; installed #defines for LM576 and LM16117A LCD
//* drivers.
//*
//* V2.61		Mon Oct 14 12:50:36 1991	fgm
//* made SBOT powerdown routine halt motors
//*
//* V2.62		Thu Oct 17 09:51:04 1991        rs
//* made battery voltage parameterizable in compile
//*
//* V2.63		Thu Oct 31 16:47:56 1991	fgm
//* made sure that speaker is turned off after boot beep
//*
//* V2.64		Mon Jan  6 17:44:06 1992	fgm
//* installed REV21 flag
//* added new LCD flags:  LCD_ROWS and LCD_COLS
//* old LCD flag LM576 needed in addition to declare weird Rev 2 LCD
//*
//* V2.65		Sun Jan 12 13:30:29 1992	fgm
//* enabled OC3 interrupt within System Interrupt
//* to allow servo ctrl routine to operate properly
//*
//* V2.66		Wed Jan 15 13:40:57 1992	fgm, rs
//* fixed nasty bug in FLT2LNG routine in "math11routines.asm"
//*
//* V2.67         Thu Feb  6 19:24:50 1992        rs
//* made zero page variable to allow IC to ignore incoming serial
//*
//* V2.70         Mon Jan  4 20:09:20 EST 1993    anarch
//* added aref_arb for arbitrary size elements in an array
//*
//* V2.71         Mon Jan  4 20:09:20 EST 1993
//* added jumping jack heartbeat  rs
//* Changed frobby character to respond to process loading    anarch
//*
//* V2.8
//* Experimental version for implementing more efficient globals   anarch
//* Changes later taken out
//*
//* V2.81
//* Cleaned up for use with IC version 2.8, new distribution
//*
//* V2.81HB		Sun Jun 11 08:39:00 1995	fredm
//* Added #defines to support the Handy Board
//* fixed bug with use of "FDB" rather than "RMB" in zero page
//*
//* V2.82HB       Fri Oct 24 22:39:00 GMT 1997 skidmorj@cs.man.ac.uk
//* Modified Handyboard code to support Smooth-PWM algorithm.
//*

//* 6811 equates
EEPROM	EQU	$F800		; start of eeprom

#include "6811regs.asm"

//* Masks for serial port
PORTD_WOM	EQU	$20
BAUD1200	EQU	$B3
BAUD9600	EQU	$B0
TRENA		EQU	$0C	; Transmit, Receive ENAble
RDRF		EQU	$20	; Receive Data Register Full
TDRE		EQU	$80	; Transmit Data Register Empty

//* definitions for V2.0 board
#if defined (SBOT) | defined (REV21) | defined (HANDYBOARD)
MOTORS_OFF	EQU	$0A
#else
MOTORS_OFF	EQU	$FF
#endif

#if defined (HANDYBOARD)
DIGOUTPUT	EQU	$7F00
#else
DIGOUTPUT	EQU	$7000
#endif

#ifndef SBOT
#if defined (HANDYBOARD)
DIGINPUT	EQU	$7F00
#else
DIGINPUT	EQU	$7000
#endif
#else
DIGINPUT	EQU	$5000
#endif

FULL_POWER	EQU	255
EXPANSION_INPUTS EQU  $4000

//* ASCII definitions
CR		EQU	$0a

//*****************************************************************
//* zero page RAM definitions

	 ORG	$00

srhi		RMB	1   ; 0
srlo		RMB	1   ; 1
sr2hi   	RMB     1   ; 2
sr2lo   	RMB     1   ; 3
sr3hi   	RMB     1   ; 4
sr3lo   	RMB     1   ; 5
sr4hi		RMB	1   ; 6
sr4lo:
signbit		RMB	1	; 7 don't use sr4lo and signbit simultaneously'
current_process	RMB	2	; 8 addr. of proc entry in scheduler table
process_ticks	RMB	1   ; 0a
system_status	RMB	1   ; 0b
process_counter	RMB	2   ; 0c
motor		RMB	1	; 0e output byte to PWM routine
lcd_status	RMB	1   ; 0f
lcd_savedchar	RMB	1   ; 10
lcd_char_count	RMB	1	; 11 number of chars printed since newline
system_time_hi	RMB	2   ; 12
system_time_lo	RMB	2   ; 14
print_buffer_end RMB	2   ; 16
serial_buffer_pos RMB	2   ; 18
lcd_buffer_pos	RMB	2   ; 1a
lcd_frobline1	RMB	1   ; 1c  XX XX XX I2 I1 I3  I4  I5
lcd_frobline2	RMB	1   ; 1d  XX XX XX XX XX XX  I7  I6
scheduler_iterations RMB 2  ; 1e  count of # times through scheduler loop
curmotor	RMB	1   ; 20  used by PWM routine
motorout	RMB	1   ; 21  ditto
speeda		RMB	1   ; 22  motor A speed
speedb		RMB	1
speedc		RMB	1
speedd		RMB	1
beeptone	RMB	2
IRtone          RMB     2
IRdetect0	RMB	1
IRdetect1	RMB	1
IRdetect2	RMB	1
IRphase0	RMB	1
IRphase1	RMB	1
IRphase2	RMB	1
IRerror0	RMB	1
IRerror1	RMB	1
IRerror2	RMB	1
scounter0	RMB	2	; shaft encoder 0
sstate0		RMB	1
scounter1	RMB	2	; shaft encoder 1
sstate1		RMB	1
system_functions RMB	1	; determines code run by SystemInt
pcoderegister0	RMB	2
ignoreserial    RMB     1
#if defined (SBOT) | defined (REV21) | defined (HANDYBOARD) | defined(SMOOTHPWM)
gSpeedFracA RMB 1
gSpeedFracB RMB 1
gSpeedFracC RMB 1
gSpeedFracD RMB 1 ;Note it isn't a problem if these are overwritten occasionally!'
#endif

#include "math11vars.asm"

LCDstart EQU	*

//*****************************************************************
//* static RAM definitions (battery-backed)

	 ORG	PCODE_ORIGIN
	 FCB	0		; don't do autostart after a download'

	 ORG	JUMPTABLE

//*** START OF JUMPTABLE
jumptable:
#include "pcode_inM.h"
//*** END OF JUMPTABLE

	 ORG	MAIN_CODE

startup:
//* see if THE_ZERO_ARRAY is zero.  If so, skip memory check.
	 LDD	THE_ZERO_ARRAY
	 BEQ	startupokay

//* perform memory check with SP at $FF.  If doesn't match checksum
//* word, print error message and die.
	 LDS	#$FF
	 JSR	memorysum
	 CMPD	THE_ZERO_ARRAY
	 BNE	memory_bad

	 LDD	#0
	 STD	THE_ZERO_ARRAY	; store 0 there meaning OK
	 BRA	startupokay

memory_bad
	 JSR	LCDinit
	 LDX	#memorybadmsg
	 JSR	printstringLCD

//* wait for button to continue
button_wait
	 LDAA	DIGINPUT
#if defined (SBOT) | defined (REV21)
	 ANDA	#$03
	 EORA	#$03
#endif
#if defined (HANDYBOARD)
	 ANDA	#$C0		; handyboard buttons are top two bits
	 EORA	#$C0
#endif
	 BEQ	button_wait
	 BRA	normalinit

memorybadmsg
	 FCC	'MEMORY FAULT'
	 FCB	0

startupokay
	 LDS	#MACHINE_STACK	; Initialize Stack Pointer to top of RAM

	 CLR	ignoreserial    ; allow serial communication

#ifdef DEBUG
//* check for frob 1 or 2 being pressed.  If so, do minimal init,
//* and jump to a loop that just serves UI.
	 LDAA	DIGINPUT
#if defined (SBOT) | defined (REV21)
	 ANDA	#$03
	 EORA	#$03
#endif
#if defined (HANDYBOARD)
        ANDA    #$C0            ; handyboard buttons are top two bits
        EORA    #$C0
#endif
	 BEQ	normalinit
	 BRA	init_sequence	; expect it to jump out!
#endif

normalinit
	 BSR	init_sequence	; turns off motors, inits serial to 9600 baud,
//*				  inits pcode stack ptr (X reg)

//* check for buttons 1 AND 0 being pressed.  If so,
//* do checksum, store, and die.
#if defined (HANDYBOARD)
	 BRA	regularinit	;can't press both buttons on HB'
//*				because then it goes into bootstrap
#endif
	 LDAA	DIGINPUT
	 ANDA	#$03
#if defined (SBOT) | defined (REV21)
	 EORA	#$03
#endif
	 CMPA	#3
	 BNE	regularinit

//* move stack to top of ZP ram so that we don't change memory
	 LDS	#$FF
	 SEI			; disable interrupts
	 JSR	memorysum	; perform checksum of memory
	 STD	THE_ZERO_ARRAY	; store checksum
	 JMP	boarddies

regularinit
//* if button 1 or 0 pressed, don't do execute on startup.
	 LDAA	DIGINPUT
#if defined (SBOT) | defined (REV21)
	 ANDA	#$03
	 EORA	#$03
#endif
#if defined (HANDYBOARD)
	 ANDA	#$80		; HB "Start" button
	 EORA	#$80
#endif
	 BEQ	check_for_autostart

jmp_scheduler
	 JMP	scheduler
check_for_autostart
//* check for "execute on startup"
	 LDAA	PCODE_ORIGIN		; start of pcode program
	 DECA				; if was one, do autostart
	 BNE	jmp_scheduler

autostart
//* use UI's area to execute code starting at PCODE_ORIGIN+1
	 LDD	#PCODE_ORIGIN+1
	 LDX	#PROCESS_TABLE
	 STD	P_PC,X
	 LDAA	#PSTAT_RUNNING
	 STAA	P_STATUS,X
	 JMP	scheduler

//*************************************************************************
init_sequence

//* clear IR detect, phase, and error bytes
//* clear shaft counter and state bytes
	 LDX	#IRdetect0
	 CLRA
IR_initloop
	 STAA	0,X
	 INX
	 CPX	#sstate1+1
	 BNE	IR_initloop

	 LDX	#$1000

#ifndef BOOKBOT
//* turn off motors (assuming "1" is off)
	 LDAA	#MOTORS_OFF
	 STAA	DIGOUTPUT
	 STAA	motor

//* set motor powers to maximum
	 LDAA	#FULL_POWER
	 STAA	speeda
	 STAA	speedb
	 STAA	speedc
	 STAA	speedd
#endif

#ifdef BOOKBOT
//* initialize ports a and d correctly
	 BSET	PACTL,X %00001000
	 LDAA	#%00111110
	 STAA	DDRD,X
//* clear speeda and speedb so that motors are off
	 CLR	speeda
	 CLR	speedb
#endif

//* set beeper tone
	 LDD	#$7C0
	 STD	beeptone

//* turn on analog system
	 BSET	OPTION,X $80

#if defined (SBOT) | defined (REV21)
//* initialize port D for output to control analog muxes
	 BSET	DDRD,X %00111000
#endif

#if defined (REV2) | defined (YAMABICO)
//* set IR tone based on switch 6 (bit 4) of expansion board
	 LDAA	#%10000000
	 STAA	PACTL,X			; make TOC1 output
	 BSET	PORTA,X $80		; bit on == IR off
	 LDAA	EXPANSION_INPUTS
	 ANDA	#$10
	 BEQ	init_irtone0
	 LDD	#8000			; 125 Hz.
	 STD	IRtone
	 BRA	init_serial
init_irtone0
	 LDD	#10000			; 100 Hz.
	 STD	IRtone
#endif

init_serial
//* initialize serial port
        BCLR	SPCR,X PORTD_WOM 	; turn off wired-or mode
	 LDAA	#BAUD9600
	 STAA	BAUD,X
	 LDAA	#TRENA
	 STAA	SCCR2,X

//* tell SystemInt what to do

#ifndef BOOKBOT
	 LDAA	#PRINTBUFFER+IRDECODE+PWM ; shaft encoding is defaulted off
#else
	 LDAA	#PRINTBUFFER+PWM ; shaft encoding + IR defaulted off
#endif

	 STAA	system_functions

//* copy LCD driver to ZP ram and clear display
	 JSR	LCDinit

//********************************
//* SYSTEM INITIALIZATION

//* initialize process counter
	 LDD	#0
	 STD	process_counter

//* initialize system time
	 STD	system_time_hi
	 STD	system_time_lo

//* initialize system status
	 LDAA	#SYSSTAT_IGNOREUI
	 STAA	system_status

//* initialize output buffer
	 LDD	#PRINT_BUFFER
	 STD	print_buffer_end
	 STD	serial_buffer_pos
	 STD	lcd_buffer_pos

//* initialize pcode loop
	 LDAA	#$20		; BRA opcode
	 STAA	pcode_branch

//********************************



//********************************
//* PROCESS INITIALIZATION
//*
//* initialize process table by setting all statuses to "PSTAT_DEAD"
	 LDX	#PROCESS_TABLE
	 LDAB	#MAX_PROCESSES
	 LDAA	#PSTAT_DEAD
//*
procinitloop
	 STAA	P_STATUS,X
	 XGDX
	 ADDD	#PROCESS_SLOT_LENGTH
	 XGDX
	 DECB
	 BNE	procinitloop
//*
#ifdef DEBUG
	 LDAA	#$55
	 STAA	motor
	 STAA	DIGOUTPUT
#endif


//* initialize UI process and install as current process
	 LDX	#PROCESS_TABLE
	 STX	current_process		; system current process
	 LDD	#UI_PROCESS_BUFFER
	 STD	P_PC,X
	 LDD	#PCODE_STACK_BEGIN
	 STD	P_STACK_ORG,X
	 ADDD	#1		; must decrement before push
	 STD	P_SP,X
	 SUBD	#UI_STACK_SIZE
	 STD	P_STACK_LIM,X
	 LDAA	#UI_TICKS
	 STAA	P_TICKS,X
	 LDAA	#PSTAT_HALTED
	 STAA	P_STATUS,X
	 STX	P_PREV,X
	 STX	P_NEXT,X
	 LDD	process_counter
	 ADDD	#1
	 STD	process_counter
	 STD	P_ID,X		; UI is PID #1
//*
//********************************

initcontinuespecial
//* initialize interrupt vectors and turn interrupts on
	 JSR	Pinitint

//* print hello message on LCD screen
	 LDX	#versionmessage
initmsgloop
	 LDAA	0,X
	 BEQ	initcontinue
	 JSR	putchar_always		; assume buffer is empty
	 INX
	 BRA	initmsgloop

initcontinue
#ifndef NO_BEEPER
//* make a beep
	 LDX	#$1000
	 BSET	TCTL1,X $01
	 LDY	#$3000
	 DEY
	 BNE	*-2
	 BCLR	TCTL1,X $01
	 BCLR	PORTA,X $08	; turn off speaker when done

//* poke 0 as tone so as to reduce # of interrupts
	 LDD	#0
	 STD	beeptone
#endif
	 RTS

versionmessage
#if LCD_ROWS * LCD_COLS <= 16
	 FCC	'ICv2.81 9/28/93'
	 FCB	$0a			; carriage return
	 FCB	0
#else
	 FCC	'Interactive C   V 2.82 6/11/97'
	 FCB	$0a			; carriage return
	 FCB	0
#endif

//*************************************************************************
//*
//*	UI_COMMAND
//*                    a digit is {@ABC DEFG HIJK LMNO}
//*		     x means ignored
//*                read
//*                    r addr: x x x x dig dig dig dig-> dig dig
//*                write
//*                    w addr: x x data-dig data-dig dig dig dig dig -> 'w'
//*                clear/set bits
//*                    c addr: AND'ing-byte OR'ing-byte ADDR-hi ADDR-lo -> 'c'
//*                reset
//*                    s : x x x x x x x x -> '>'
//*		 blockwrite
//*	  	     'b' x byte-cnt-lo ADDR-hi ADDR-lo -> 'b'
//*		     data-1 ... data-n -> chksum-byte of all bytes
//*
//*
//*
//*	if read get address: print @address: goto command_loop
//*	if write get address,data: poke address,data: goto command_loop
//*	if reset goto reset
//*
//*
ui_command:

//* get a command character
//* a command is always 'a' or bigger
//* and data is always smaller than 'a' (@-O)
command_get_type
	 BSR	getchar
	 BSR	serial_putchar		; echo back command type
command_proc_command
	 CMPA	#'a;'
	 BLO	command_get_type
//* check for reset command
	 CMPA	#'s;'
	 BNE	command_getargs
	 LDAA	#'>;'
	 BRA	serial_putchar		; send prompt and finish

command_getargs
	 PSHA	; save command type
//* get 2 words of data
	 BSR	getword		; data (lsb), new pc for call: y
	 PSHX
	 PULY
	 BSR	getword		; address: x
//* branch on command type
	 PULA	; restore command type
	 CMPA	#'r;'
	 BEQ	command_read
	 CMPA	#'w;'
	 BEQ	command_write
	 CMPA	#'c;'
	 BEQ	command_clear
	 CMPA	#'b;'
	 BEQ	command_blockwrite
	 RTS
//*
command_read
	 LDAA	0,X
	 BSR	putbyte
	 BRA	command_promptreturn

command_write
	 XGDY	; move y to d.  low byte to poke is now in b
	 STAB	0,X
	 BRA	command_promptreturn

command_clear
	 XGDY	; move y to d.
	 ANDA	0,X
	 STAA	0,X
	 ORAB	0,X
	 STAB	0,X
//* fall through to command_promptreturn

command_promptreturn
	 LDAA	#'>;'
	 BRA	serial_putchar

command_blockwrite
//* uses srlo to compute checksum
	 XGDY	; A = trash, B = bytecount
//* compute checksum of X-hi, X-low, and B
	 PSHB
	 PSHX
	 PULA
	 PULB
	 ABA
	 PULB		; B restored = bytecount
	 ABA		; A = checksum
	 STAA	srlo	; save checksum
	 TSTB
	 BEQ	cmd_bwloopend
cmd_bwloop
	 BSR	getbyte	; data
	 STAA	0,X
	 ADDA	srlo
	 STAA	srlo	; checksum
	 INX
	 DECB
	 BNE	cmd_bwloop
cmd_bwloopend
	 LDAA	srlo
	 BSR	putbyte
	 BRA	command_promptreturn

//* get a word into X
getword:
	 BSR 	getbyte  ; get hi byte first
	 PSHA
	 BSR	getbyte
	 TAB
	 PULA
	 XGDX
	 RTS

//* get a character into A
getbyte:
getchar:
	 LDAA	SCSR
	 ANDA	#RDRF
	 BEQ	getchar
	 LDAA	SCDR
	 RTS

//* put a character from A.  munges B
putbyte:
serial_putchar:
	 LDAB	SCSR
	 ANDB	#TDRE
	 BEQ	serial_putchar
	 STAA	SCDR
	 RTS


//****************************************************************
//*
//*	pcode_run_process
//*
//*	call with "current_process" pointer already loaded.
//*
//*	executes pcode until
//*		system interrupt decrement "process_ticks" to zero,
//*		  which then pokes "BRN" [branch never] into pcode_run loop.
//*	   OR
//*		somebody else (i.e., an error-causing pcode)
//*		  pokes the BRN.
//*
//*	then saves process stack and PC and returns.
//*
//*	returns with process status in A register.
//*
//*
//*
pcode_run_process:

	 LDX	current_process

	 LDAA	P_TICKS,X
	 STAA	process_ticks
	 LDY	P_PC,X
	 LDX	P_SP,X

pcode_run
	 LDAB	0,Y			; [5] load opcode
	 STAB	pcr_loadjump+2		; [4] low byte of load
	 INY				; [4] skip past opcode

pcr_loadjump
	 LDD	JUMPTABLE		; [3] D gets loaded from jump table
	 STD	pcr_pcjsr+1		; [5]

pcr_pcjsr
	 JSR	$FFFF			; [6] loc'n gets stuffed;'
pcode_branch:
	 BRA	pcode_run		; [3]
//* poke me please

//* finish up and exit
	 LDAA	#$20			; BRA opcode
	 STAA	pcode_branch		; restore loop

	 LDD	current_process
	 XGDX				; X has ptr to proc slot, D has proc SP
	 STD	P_SP,X			; store SP
	 STY	P_PC,X			; store process PC
	 LDAA	P_STATUS,X		; get status into A
	 CMPA	#PSTAT_DEAD
	 BNE	pcr_done
//* process has died.  back up to previous process and store as
//* current_process for scheduler.
	 LDX	P_PREV,X
	 STX	current_process
pcr_done
	 RTS				; return to scheduler



//**********************************************************************
//*
//*	SCHEDULER
//*
//*	Scheduler gives each process its turn in order.
//*
//*	When P_STATUS of a process returns anything but STATUS_RUNNING,
//*	that status is bitwise ORed into the system status and the system
//*	status is broadcast to UI.
//*
//*	In between process calls to "pcode_run_process~, the scheduler
//*	checks to see if the system status has changed (e.g., stdout
//*	is waiting), or if a serial interrupt has occured (e.g., UI
//*	wants attention.)
//*
//*	If a process returns an error code, then all execution is halted,
//*	and the scheduler waits for the system status to change (UI must
//*	poke a new value into the status byte).  It also responds to
//*	serial interrupts (UI attention).
//*
//*
scheduler:
//* check serial line status

//* if we are supposed to ignore serial, then ignore it
	 LDAA	ignoreserial
	 BNE	sch_check_sysstat

	 LDX	#$1000
	 BRCLR	SCSR,X RDRF sch_check_sysstat ; if clear, no byte ready

	 JSR	ui_command		; serve UI
	 BRA	scheduler

sch_check_sysstat
//* check system status
	 BRSET	system_status SYSSTAT_ERRORHALT scheduler
//*			; if error, wait for UI to fix it

sch_get_process
//* get next process
//* count if hit top of process table
	 LDX	current_process
	 LDX	P_NEXT,X
	 STX	current_process		; save new proc ptr
	 CPX	#PROCESS_TABLE
	 BNE	sch_frobbing_done

	 LDD	scheduler_iterations
	 ADDD	#1
	 STD	scheduler_iterations

sch_frobbing_done
//* check process status
	 LDAA	P_STATUS,X
	 CMPA	#PSTAT_RUNNING
	 BNE	scheduler		; if this one isn't running, try;'
//*					  again
	 JSR	pcode_run_process

//* check for any error codes
	 CMPA	#PSTAT_RUNNING
	 BEQ	scheduler		; ok
	 CMPA	#PSTAT_DEAD
	 BEQ	scheduler

	 CMPA	#PSTAT_HALTED		; pcode-requested halt errorcode
	 BEQ	sch_halterr
	 LDAA	#SYSSTAT_ERRORHALT
	 BRA	sch_errornotify

sch_halterr
	 LDAA	#SYSSTAT_HALTNOTIFY

sch_errornotify
	 BSR	set_system_status
	 BRA	scheduler


//**********************************************************************
//*
//*	set_system_status
//*
//*	takes byte in A as new system status
//*	if different from old, broadcasts new status byte.
//*
set_system_status:
	 STAA	srlo
	 ANDA	system_status
	 CMPA	srlo			; did any bits change?
	 BNE	change_sysstat
	 RTS
change_sysstat
	 LDAA	srlo
	 ORAA	system_status		; OR with old status
	 STAA	system_status

	 JMP	serial_putchar			; send status byte & return



//*********************************************************************
//*
//* GUIDELINES FOR BEING A PCODE HANDLER
//*
//* When pcode instructions begin, Y register points at immediate
//* arg if there is one.
//*
//* You must increment the Y register past any immediate args before
//* exiting.
//*
//* 0,X is contents of top of stack;
//* 2,X is contents of 2nd to top (assuming 16-bit data).
//*
//* Stack is growing downwards, so incrementing X will pop an item
//* off of the stack.
//*
//* Storing an item at 2,X and then incrementing X twice is a good
//* way for a binary operation to finish (16-bit).
//*

//*****************************************************************
//***							      ***
//***		       16-BIT OPERATIONS		      ***
//***							      ***
//*****************************************************************

//***********************
//***  BINARY OPERATIONS
//***********************

//*****************************************************************
//*
//*	subtract2
//*
Psub2
	 LDD	2,X
	 SUBD	0,X
	 BRA	hpoppushd

//*****************************************************************
//*
//*	and2
//*
Pbitand2
	 LDD	2,X
	 ANDA	0,X
	 ANDB	1,X
	 BRA	hpoppushd

//*****************************************************************
//*
//*	or2
//*
Pbitor2
	 LDD	2,X
	 ORAA	0,X
	 ORAB	1,X
	 BRA	hpoppushd

//*****************************************************************
//*
//*	xor2
//*
Pbitxor2
	 LDD	2,X
	 EORA	0,X
	 EORB	1,X
	 BRA	hpoppushd

//*****************************************************************
//*
//*	addition:  pop two items off of stack and add them
//*       pop:       pop one item off of the stack
//*
Padd2
	 LDD	0,X
	 ADDD	2,X
hpoppushd:
	 STD	2,X
Ppop2
	 INX
	 INX			; pop useless item
	 RTS

//*****************************************************************
//*
//*	multiply2
//*
Pmult2:
	 BSR	top2toabs

	 LDAA	1,X		; arg 2 lo
	 LDAB	3,X		; arg 1 lo
	 MUL
	 STD	srhi

	 LDAA	0,X		; arg 2 hi
	 LDAB	3,X		; arg 1 lo
	 MUL
	 ADDB	srhi
	 STAB	srhi

	 LDD	1,X		; don't look;'
	 MUL
	 ADDB	srhi
	 STAB	srhi

absret
//* check for negation before exiting
	 TST	signbit
	 BEQ	hmultplus
	 CLRA
	 CLRB
	 SUBD	srhi
	 BRA	hpoppushd

hmultplus
	 LDD	srhi
	 BRA	hpoppushd

//* makes top two args on stack into absolute values, toggling sign bit
top2toabs
	 CLR	signbit
	 BSR	Pabs2		; makes absolute TOS, toggle signbit if -
	 INX
	 INX
	 BSR	Pabs2
	 DEX
	 DEX
	 RTS

//*****************************************************************
//*
//*	divide2
//*
Pdiv2
	 JSR	top2toabs

	 LDD	2,X		; dividend
	 PSHX
	 LDX	0,X		; divisor
	 IDIV
	 STX	srhi
	 PULX
	 BRA	absret

//*****************************************************************
//*
//*	equal2
//*
Pequal2
	 LDD	0,X
	 SUBD	2,X
	 BEQ	pushtrue
pushfalse
	 LDD	#0
	 BRA	hpoppushd
pushtrue
	 LDD	#1
	 BRA	hpoppushd

//*****************************************************************
//*
//*	greaterthan2
//*
Pgt2
	 LDD	2,X
	 SUBD	0,X
	 BGT	pushtrue
	 BRA	pushfalse

//*****************************************************************
//*
//*	lessthan2   2,x < 0,x?
//*
Plt2
	 LDD	2,X
	 SUBD	0,X
	 BLT	pushtrue
	 BRA	pushfalse

//*****************************************************************
//*
//*	lshift
//*
//*	shifts next to top-of-stack left by top-of-stack bits.
//*	shifts t the right if t.o.s. is negative.
//*
//*	uses self-modifying code
Plshift
	 PSHX
	 LDD	0,X		; get count
	 BMI	rshift		; if minus shift right
	 BNE	golshift
	 PULX
	 INX
	 INX
	 RTS			; pop count and return

golshift
	 PSHB
	 PSHA			; push counts on stack
	 LDAA	#$05		; ASLD opcode
	 STAA	lshiftloop
goshift
	 LDD	2,X		; load # to be shifted
	 PULX			; counts are in X

lshiftloop
	 FCB	0		; shift opcode goes here
	 BEQ	shiftdone
	 DEX
	 BNE	lshiftloop

//* answer is in D; pull X to get SP back
shiftdone
	 PULX
	 INX
	 INX
	 STD	0,X
	 RTS

rshift
	 CLRA
	 CLRB
	 SUBD	0,X		; complement D
	 PSHB
	 PSHA
	 LDAA	#$04		; LSRD opcode
	 STAA	lshiftloop
	 BRA	goshift


//*****************************************************************
//*
//*	absolute value2
//*	used by other pcodes (don't punt me)
Pabs2
	 TST	0,X
	 BPL	habsdone
	 CLRA
	 CLRB
	 SUBD	0,X
	 STD	0,X
	 COM	signbit		; record that we changed the sign
habsdone
	 RTS

//***********************
//***  UNARY OPERATIONS
//***********************

//*****************************************************************
//*
//*	bitnot2:  bitwise not
//*
Pbitnot2
	 COM	0,X
	 COM	1,X
	 RTS

//*****************************************************************
//*
//*	neg2:  two's complement  0 - [0,X] => [0,X]
//*
Pneg2
	 CLRA
	 CLRB
	 SUBD	0,X
	 STD	0,X
	 RTS

//*****************************************************************
//*
//*	logidn:  logical identity
//*
//*		0  ==> 0
//*	      else ==> 1
//*
Plogidn2
	 LDD	0,X
	 BEQ	ret
storetrue
	 LDD	#1
	 STD	0,X
ret	RTS

//*****************************************************************
//*
//*	lognot2:  logical not
//*
//*		0  ==> 1
//*	      else ==> 0
//*
Plognot2
	 LDD	0,X
	 BEQ	storetrue
storefalse
	 CLRA
	 CLRB
	 STD	0,X
	 RTS


//*****************************************************************
//***							      ***
//***		       32-BIT OPERATIONS		      ***
//***							      ***
//*****************************************************************

//***********************
//***  BINARY OPERATIONS
//***********************

//*****************************************************************
//*
//*	longword addition
//*
Padd4:
	 LDD	2,X		; low word, 2nd arg
	 ADDD	6,X		; low word, 1st arg
	 STD	srhi
	 LDAB	1,X		; 3rd byte, 2nd arg
	 ADCB	5,X		; 3rd byte, 1st arg
	 LDAA	0,X
	 ADCA	4,X		; D has hi word
Pbinlongrtn:
	 STD	4,X
	 LDD	srhi
	 STD	6,X
	 LDAB	#4
	 ABX
	 RTS

//*****************************************************************
//*
//*	longword subtraction
//*	subtract shallow from deep
//*
Psub4:
	 LDD	6,X
	 SUBD	2,X		; low words
	 STD	srhi
	 LDAB	5,X
	 SBCB	1,X		; 3rd byte
	 LDAA	4,X
	 SBCA	0,X		; 4th byte
	 BRA	Pbinlongrtn


//*****************************************************************
//*
//*	lessthan4   [7..4],x < [3..0],x?
//*
Plt4:
	 LDD	6,X
	 SUBD	2,X
	 LDAB	5,X
	 SBCB	1,X
	 LDAA	4,X
	 SBCA	0,X
	 BLT	Ppushtrue4
	 BRA	Ppushfalse4

//*****************************************************************
//*
//*	greaterthan4:  subtract in reverse order and test
//*		       for less than (because the Z bit isn't
//*		       valid at the end of the subtraction.)
//*
Pgt4:
	 LDD	2,X
	 SUBD	6,X
	 LDAB	1,X
	 SBCB	5,X
	 LDAA	0,X
	 SBCA	4,X
	 BLT	Ppushtrue4
	 BRA	Ppushfalse4

//*****************************************************************
//*
//*	equal4 ?
//*
Pequal4
	 LDD	6,X
	 SUBD	2,X
	 BNE	Ppushfalse4
	 LDD	4,X
	 SUBD	0,X
	 BNE	Ppushfalse4
Ppushtrue4:
	 LDAB	#6
	 ABX
	 LDD	#1
	 STD	0,X
	 RTS
Ppushfalse4:
	 LDAB	#6
	 ABX
	 LDD	#0
	 STD	0,X
	 RTS

//*****************************************************************
//*
//*	longword multiplication
//*
//*		a  b  c  d	== 4,X to 7,X
//*            x  A  B  C  D	== 0,X to 3,X
//*	     -------------
//*	       |Da Db Dc Dd
//*	    Ca |Cb Cc Cd
//*        Ba Bb |Bc Bd
//*     Aa Ab Ac |Ad
//*     ---------------------
//*    <discard> | <answer>
//*
//*	stores computed answer in srhi-srlo-sr2hi-sr2lo
//*
Pmult4:
//* convert args to positive, keeping track of sign in signbit
	 CLR	signbit
	 JSR	Pabs4
	 PSHX
	 LDAB	#4
	 ABX
	 JSR	Pabs4
	 LDX	#0
	 STX	srhi
	 PULX

//* 4th column
	 LDAA	3,X	; D
	 LDAB	7,X	; d
	 MUL
	 STD	sr2hi

//* 3rd column
	 LDAA	3,X	; D
	 LDAB	6,X	; c
	 MUL
	 ADDD	srlo
	 STD	srlo
	 LDAA	srhi
	 ADCA	#0
	 STAA	srhi

	 LDAA	2,X	; C
	 LDAB	7,X	; d
	 MUL
	 ADDD	srlo
	 STD	srlo
	 LDAA	srhi
	 ADCA	#0
	 STAA	srhi

//* 2nd column
	 LDAA	3,X	; D
	 LDAB	5,X	; b
	 MUL
	 ADDD	srhi
	 STD	srhi

	 LDAA	2,X	; C
	 LDAB	6,X	; c
	 MUL
	 ADDD	srhi
	 STD	srhi

	 LDAA	1,X	; B
	 LDAB	7,X	; d
	 MUL
	 ADDD	srhi
	 STD	srhi

//* 1st column
//*	LDAA	3,X	; D
//*	LDAB	4,X	; a
	 LDD	3,X
	 MUL
	 ADDB	srhi
	 STAB	srhi

	 LDAA	2,X	; C
	 LDAB	5,X	; b
	 MUL
	 ADDB	srhi
	 STAB	srhi

	 LDAA	1,X	; B
	 LDAB	6,X	; c
	 MUL
	 ADDB	srhi
	 STAB	srhi

	 LDAA	0,X	; A
	 LDAB	7,X	; d
	 MUL
	 ADDB	srhi
	 STAB	srhi
//* done
	 LDAB	#4
	 ABX
	 LDD	sr2hi
	 STD	2,X
	 LDD	srhi
	 STD	0,X
	 TST	signbit
	 BEQ	Pmult4done
	 BSR	Pneg4
Pmult4done
	 RTS

//***********************
//***  UNARY OPERATIONS
//***********************

//*****************************************************************
//*
//*	longword negation:   result = 0 - arg
//*
Pneg4
	 LDD	#0
	 SUBD	2,X		; low word
	 STD	srhi
	 LDAB	#0
	 SBCB	1,X
	 LDAA	#0
	 SBCA	0,X
	 STD	0,X
	 LDD	srhi
	 STD	2,X
	 RTS



//*****************************************************************
//*
//*	Pabs4	negates if necessary and complements signbit
//*
Pabs4:
	 TST	0,X
	 BPL	Pabs4done
	 BSR	Pneg4
	 COM	signbit
Pabs4done
	 RTS

//*****************************************************************
//***							      ***
//***		   STACK AND MEMORY OPERATIONS		      ***
//***							      ***
//*****************************************************************


//*****************************************************************
//*
//*	push2(immediate) ==> stack
//*
Ppush2
	 LDD	0,Y
incy2pushd
	 INY
incy1pushd
	 INY
pushd
	 DEX
	 DEX
	 STD	0,X
	 RTS

//*****************************************************************
//*
//*	push4(immediate) ==> stack
//*
//*	push4 aa bb cc dd 		prev bottom of stack
//*						dd
//*	aa is highest;				cc
//*	dd is lowest.				bb
//*						aa
//*				[SP after]==>  ....
Ppush4:
	 LDD	2,Y
	 DEX
	 DEX
	 STD	0,X
	 LDD	0,Y
	 INY
	 INY
	 INY
	 INY
	 BRA	pushd

//*****************************************************************
//*
//*	pushblock
//*
//*	takes inline byte count; then data
//*
Ppushblock:
	 LDAB	0,Y		; count of bytes
	 ABY			; point at end of data (1st byte
//*				  to be pushed
pb_loop
	 LDAA	0,Y
	 DEX
	 STAA	0,X
	 DEY
	 DECB
	 BNE	pb_loop
//* Y is pointing at count byte again
	 LDAB	0,Y
	 ABY
	 RTS


//*****************************************************************
//*
//*	peeki1(immediate addr) ==> word on stack
//*
Ppeeki1
	 PSHX
	 LDX	0,Y
	 LDAB	0,X
	 PULX
	 CLRA
	 BRA	incy2pushd

//*****************************************************************
//*
//*	peeki4(immediate addr)
//*
Ppeeki4
	 XGDX			; store X in D
	 LDX	0,Y		; get addr into X
	 LDX	2,X		; get l.s. word of value
	 XGDX			; put in D
	 DEX
	 DEX
	 STD	0,X		; push ls word on stack
//*
//*	BRA	hpeeki2		; push ms word
//*
//*****************************************************************
//*
//*	peeki2(immediate addr)
//*
Ppeeki2
	 XGDX			; store X in D
	 LDX	0,Y
	 LDX	0,X
	 XGDX			; retrieve X; put peeked val in D
	 BRA	incy2pushd

//*****************************************************************
//*
//*	speek4:     peek4(stack_pointer + 8-bit offset)
//*
Pspeek4
	 LDAB	0,Y		; get stack offset from inline code
	 PSHX
	 ABX			; add offset and stack pointer
	 LDD	0,X		; peek m.s. word
	 STD	srhi		; save it
	 LDD	2,X		; peek l.s. word
	 PULX
	 DEX
	 DEX
	 STD	0,X
	 LDD	srhi
	 BRA	incy1pushd

//*****************************************************************
//*
//*	speek2:     peek2(stack_pointer + 8-bit offset)
//*
Pspeek2
	 LDAB	0,Y		; get stack offset from inline code
	 PSHX
	 ABX			; add offset and stack pointer
	 LDD	0,X		; peek
	 PULX
	 BRA	incy1pushd

//*****************************************************************
//*
//*	pokei1:  pop word off stack and store low byte at
//*		 immediate address in pcode
Ppokei1
	 PSHX
	 LDAA	1,X		; load low byte of data from stack
	 LDX	0,Y		; load address from inline code
	 STAA	0,X		; poke
	 BRA	pokerecover

//*****************************************************************
//*
//*	pokei2:  pop word off stack and store at
//*		 immediate address in pcode
Ppokei2
	 PSHX
	 LDD	0,X		; load data from stack
	 LDX	0,Y		; load address from inline code
	 STD	0,X		; poke
pokerecover
	 INY			; increment past inline argument
pulxinyinx2
	 PULX
inyinx2
	 INY
incx2
	 INX			; pop 2 bytes
	 INX
	 RTS

//*****************************************************************
//*
//*	pokei4:  pop two words off stack and store at
//*		 immediate address in pcode
Ppokei4
	 PSHX
	 LDD	0,X		; load m.s. word from stack
	 STD	srhi		; save it
	 LDD	2,X		; load l.s. word from stack
	 LDX	0,Y		; load address from inline code
	 STD	2,X		; poke l.s. word
	 LDD	srhi
	 STD	0,X		; poke m.s. word
	 PULX
	 LDAB	#4
	 ABX			; pop 4 bytes
	 ASRB
	 ABY			; increment past inline arg
	 RTS

//*****************************************************************
//*
//*	spoke4:  (SP + inline_offset_byte - 4) => stack_longword
//*	spoke4(4) replaces next to top of stack with top of stack
Pspoke4
	 LDD	0,X		; get m.s. word
	 STD	srhi		; save it
	 LDD	2,X		; get l.s. word
	 STAB	signbit		; save B
	 LDAB	0,Y		; get offset from inline
	 PSHX
	 ABX
	 LDAB	signbit
	 STD	2,X		; store l.s. word
	 LDD	srhi
	 STD	0,X		; store m.s. word
	 PULX
	 INX
	 INX			; pop half of poked longword
	 BRA	inyinx2		; inc past inline, pop other 1/2 of l.w.

//*****************************************************************
//*
//*	spoke2:     (stack_pointer + inline_offset_byte - 2) <= stack_data
//*       spoke2(2) replaces next to top of stack with top of stack
Pspoke2
	 LDD	0,X		; get data from stack
	 STAB	srlo

	 LDAB	0,Y		; load offset from inline code
	 PSHX
	 ABX
	 LDAB	srlo		; get data again
	 STD	0,X		; store in stack location
	 BRA	pulxinyinx2

//*****************************************************************
//*
//*	poke1		takes stack
//*			  addr.
//*			  word
//*
//*	pokes low byte of word into address specified
Ppoke1:
	 LDAA	1,X		; get low byte
	 PSHX
	 LDX	2,X		; get addr to poke at
	 BEQ	pokeerror
	 STAA	0,X
	 PULX
	 BRA	Ppop4		; pop word and addr

//*****************************************************************
//*
//*	bitset		takes stack
//*			  addr.
//*			  word
//*
//*	sets bits from low byte of word into address specified
Pbitset:
	 LDAA	1,X		; get low byte
	 STAA	Pbitsetop+2	; poke byte into BSET opcode
	 PSHX
	 LDX	2,X		; get addr to poke at
	 BEQ	pokeerror
Pbitsetop:
	 BSET	0,X $ff
	 PULX
	 BRA	Ppop4		; pop word and addr

//*****************************************************************
//*
//*	bitclr		takes stack
//*			  addr.
//*			  word
//*
//*	clears bits from low byte of word into address specified
Pbitclr:
	 LDAA	1,X		; get low byte
	 STAA	Pbitclrop+2	; poke byte into BCLR opcode
	 PSHX
	 LDX	2,X		; get addr to poke at
	 BEQ	pokeerror
Pbitclrop:
	 BCLR	0,X $ff
	 PULX
	 BRA	Ppop4		; pop word and addr

//*****************************************************************
//*
//*	poke2:		takes stack
//*			  addr.
//*			  word
Ppoke2
	 LDD	0,X		; get byte
	 PSHX
	 LDX	2,X		; get addr to poke at
	 BEQ	pokeerror
	 STD	0,X
	 PULX
Ppop4
	 INX
	 INX			; pop word
	 INX
	 INX			; pop addr
	 RTS

//*****************************************************************
//*
//*	poke4:		takes stack
//*			  addr.
//*			  longword
Ppoke4
	 LDD	0,X		; get m.s. word
	 STD	srhi		; save it
	 LDD	2,X		; get l.s. word
	 PSHX
	 LDX	4,X		; get addr to poke at
	 BNE	poke4ok
pokeerror
//* generate null pointer error
	 PULX
	 LDAA	#PSTAT_NULLPOINTER
	 JMP	pcode_error_exit
poke4ok
	 STD	2,X		; poke l.s. word
	 LDD	srhi
	 STD	0,X		; poke m.s. word
	 PULX
	 INX
	 INX			; pop 1/2 of longword
	 BRA	Ppop4		; pop other 1/2 of longword, addr

//*****************************************************************
//*
//*	poke1nopop:	takes stack
//*			  addr.
//*			  word
//*
//*	pokes low byte of word into address specified
//*	leaves word on stack when done
Ppoke1nopop
	 LDD	0,X		; get word
	 PSHX
	 LDX	2,X		; get addr to poke at
	 BEQ	pokeerror
	 STAB	0,X		; poke low byte
	 PULX
	 STD	2,X		; store word where addr is now
	 INX
	 INX
	 RTS			; pop word and return

//*****************************************************************
//*
//*	poke2nopop:	takes stack
//*			  addr.
//*			  word
//*
//*	leaves word on stack when done
Ppoke2nopop
	 LDD	0,X		; get word
	 PSHX
	 LDX	2,X		; get addr to poke at
	 BEQ	pokeerror
	 STD	0,X
	 PULX
	 STD	2,X
	 INX
	 INX
	 RTS

//*****************************************************************
//*
//*	poke4nopop	takes stack
//*			  addr.
//*			  longword
//*
//*	leaves longword on stack when done
Ppoke4nopop:
	 LDD	0,X		; get ms word
	 STD	srhi		; save it
	 LDD	2,X		; get ls word
	 PSHX
	 LDX	4,X		; get addr to poke at
	 BEQ	pokeerror
	 STD	2,X		; poke ls word
	 LDD	srhi
	 STD	0,X		; poke ms word
	 PULX
	 LDD	2,X		; get ls word
	 STD	4,X		; move up two bytes
	 LDD	srhi		; get ls word
	 STD	2,X		; move up two bytes
	 INX
	 INX			; pop addr
	 RTS

//*****************************************************************
//*
//*	peek1		takes addr. on stack
//*
//*			writes peeked byte as word
Ppeek1:
	 PSHX
	 LDX	0,X		; get addr to peek at
	 BEQ	pokeerror
	 LDAB	0,X		; get byte in low of D
	 CLRA
	 PULX			; restore SP
	 STD	0,X
	 RTS

//*****************************************************************
//*
//*	peek2		takes addr. on stack
//*
Ppeek2:
	 PSHX
	 LDX	0,X		; get addr to peek at
	 BEQ	pokeerror
	 LDD	0,X		; get word
	 PULX			; restore SP
	 STD	0,X
	 RTS

//*****************************************************************
//*
//*	peek4		takes addr. on stack
//*
Ppeek4:
	 PSHX
	 LDX	0,X		; get addr to peek at
	 BEQ	pokeerror
	 LDD	0,X		; get m.s. word
	 STD	srhi		; save it
	 LDD	2,X		; get l.s. word
	 PULX			; restore SP
	 DEX
	 DEX
	 STD	2,X		; push l.s. word
	 LDD	srhi
	 STD	0,X		; push m.s. word
	 RTS



//*****************************************************************
//***							      ***
//***		   FLOW OF CONTROL OPERATIONS		      ***
//***							      ***
//*****************************************************************


//*****************************************************************
//*
//*	jumpi  pc= inline_address
//*
Pjumpi:
	 LDY	0,Y
	 RTS

//*****************************************************************
//*
//*	jump  	pc= stack_address
//*
Pjump:
	 LDY	0,X
	 INX
	 INX
	 RTS


//*****************************************************************
//*
//*	jfalse:  pc= inline_address if stack_boolean=0
//*
Pjfalse
	 LDD	0,X		; load stack boolean
	 BNE	incx2y2		; if boolean <> 0 then don't jump;'
hcjump
	 LDY	0,Y
	 INX
	 INX
	 RTS
incx2y2 INY
	 INY
	 INX
	 INX
	 RTS

//*****************************************************************
//*
//*	jtrue:  pc= inline_address if stack_boolean<>0
//*
Pjtrue
	 LDD	0,X		; load stack boolean
	 BNE	hcjump		; if boolean <> 0 then jump
	 BRA	incx2y2

//*****************************************************************
//*
//*	jptrue:  pc= inline_address if stack_boolean<>0
//*                also push 1 if stack_boolean<>0
Pjptrue
	 LDD	0,X
	 BEQ	incx2y2
	 LDD	#1
	 STD	0,X
	 LDY	0,Y
	 RTS

//*****************************************************************
//*
//*	jpfalse:  pc= inline_address if stack_boolean==0
//*                also push 0 if stack_boolean==0
Pjpfalse
	 LDD	0,X
	 BNE	incx2y2
	 LDY	0,Y
	 RTS

//***************************
//***  WARNING!!!!        ***
//***  ENTER AT OWN RISK. ***
//***  MRET CODE FOLLOWS  ***
//***************************

//*****************************************************************
//*
//*	MRET!:  magic return
//*
//*	PCODE STACK BEFORE:
//*
//*    bottom	[--|--] fcn return PC
//*		[--|--] arg1
//*		[--|--] arg2
//*	top	[--|--] return value
//*
//*	PCODE STACK AFTER (mret 6):
//*		[--|--] return value
//*	    PC = fcn. return
//*
//*	PROCEDURE:
//*	  pop the return value -> return value
//*         pop inline_word - 2 bytes of arguments from stack -> discard
//*         pop return address -> PC
//*         push return value back on stack
//*
Pmret2:
	 LDD	0,X     ; load return value
	 STD	srhi	; save return value for later

	 LDD	0,Y	; get # of bytes to pop (want to add into X)
	 STX	sr3hi
	 ADDD	sr3hi
	 XGDX		; result back to X
	 LDY	0,X	; load return address into PC
	 BEQ	mret_die ; nothing to return to; halt
	 LDD	srhi	; have return value in D
	 STD	0,X	; store back on stack (replacing return addr)
	 RTS

mret_die:
//* there is nowhere to return to, so we simply die.
//* dying is accomplished by poking PSTAT_DEAD into our status,
//* poking BRN into pcode_branch so we will exit upon return,
//* and removing ourselves from the current process list.
	 LDX	current_process
	 LDAA	#PSTAT_DEAD
	 STAA	P_STATUS,X
	 LDAA	#$21		; BRN opcode
	 STAA	pcode_branch
	 LDD	P_PREV,X	; get _PREV addr
	 LDY	P_NEXT,X	; Y = _NEXT
	 XGDX			; X = _PREV
	 STX	P_PREV,Y	; NEXT.PREV -> _PREV
	 STY	P_NEXT,X	; PREV.NEXT -> _NEXT
	 RTS

//*****************************************************************
//*
//*	mret0:	magic return without return value on stack
//*
Pmret0
	 LDD	0,Y	; get # of bytes to pop
	 STX	srhi
	 ADDD	srhi
	 XGDX		; put result back into X
	 LDY	0,X	; load return address into pcode PC
	 BEQ	mret_die
	 INX
	 INX		; pop return address
	 RTS

//*****************************************************************
//*
//*	mret4:  magic return with 4-byte return value
//*
Pmret4
	 LDD	0,X
	 STD	srhi
	 LDD	2,X
	 STD	sr2hi		; store ret'n val in sr and sr2;'

	 LDD	0,Y
	 STX	sr3hi
	 ADDD	sr3hi
	 XGDX			; pop bytes requested

	 LDY	2,X		; fetch return addr
	 BEQ	mret_die
	 LDD	srhi
	 STD	0,X
	 LDD	sr2hi
	 STD	2,X
	 RTS

//*****************************************************************
//***							      ***
//***		       	STACK FROBBING			      ***
//***							      ***
//*****************************************************************

//*****************************************************************
//*
//*	checkstack	takes word on stack;
//*			signals error if at least that many
//*			bytes do not remain.
//*
Pcheckstack:
	 STX	srhi		; save current SP for easy access
	 LDX	current_process	; get ptr to process data
	 LDD	srhi		; get current SP
	 SUBD	P_STACK_LIM,X	; number of stack bytes remaining
	 CPD	0,Y		; if less than zero, lose
	 BLO	csfail
	 INY
	 INY			; go past inline arg
	 LDX	srhi
	 RTS

csfail	DEY			; point at erroring pcode
	 LDX	srhi		; restore
	 LDAA	#PSTAT_STACKOVERFLOW
	 JMP	pcode_error_exit


//*****************************************************************
//*
//*	addsp:	adds inline signed word to SP
//*
//*		useful for reserving large blocks of stack
//*		(probably for an array)
Paddsp
	 XGDX		; get current SP into D
	 ADDD	0,Y	; add inline word
	 XGDX		; result back into X
incy2	INY
	 INY		; incr. past inline
	 RTS

//*****************************************************************
//*
//*	sprel:		pushes SP + inline_word
//*
//*			used to push base+offset for a local array ref
Psprel
	 PSHX
	 PULA
	 PULB		; copy X to D
	 ADDD	0,Y	; add offset
	 JMP	incy2pushd


//*****************************************************************
//*
//*	setsp		takes word and sets SP equal to it
//*			can't pop the word because SP changes
//*
//*
Psetsp:
	 LDX	0,X
	 RTS


//*****************************************************************
//***							      ***
//***	       	      PCODE REGISTERS			      ***
//***							      ***
//*****************************************************************


//* pop stack word into register
Ploadreg:
	 LDD	0,X
	 STD	pcoderegister0
	 INX
	 INX
	 INY			; inc past register # byte
	 RTS

//* push register onto stack
Pfetchreg:
	 LDD	pcoderegister0
	 DEX
	 DEX
	 STD	0,X
	 INY
	 RTS


//*****************************************************************
//***							      ***
//***		       	   ARRAYS			      ***
//***							      ***
//*****************************************************************

//*
//*
//*  An array of N items:
//*
//* hi mem [item n-1]
//*	 [item n-2]
//*
//*	   ...
//*
//*	 [item 2]
//*	 [item 1]
//*	 [item 0]
//* lo mem [length_word]
//*
//*
//* items may be 1, 2, or 4 bytes in length.
//*
//*****************************************************************
//*
//*	aref1		pops
//*			  base (2-byte abs. addr.)
//*			  index (2-byte +offset)
//*
//*			pushes
//*			  addr of element
//*
//*			or generates bounds error.
Paref1:
	 LDD	0,X	; get index into D
	 PSHX
	 LDX	2,X	; get ptr to base of array
	 CPD	0,X	; (index - length); should be less than zero
	 PULX		; doesn't change CCR;'
	 BHS	errorbounds1
areffinish
	 ADDD	2,X	; add base to index
	 ADDD	#2	; add offset past length
	 STD	2,X
	 INX
	 INX
	 RTS

//*****************************************************************
//*
//*	aref2:		pops
//*			  index (2-byte +offset)
//*			  base (2-byte abs. addr.)
//*			pushes
//*			  addr of element
//*
//*			or generates bounds error.
Paref2
	 LDD	0,X	; get index into D
	 PSHX
	 LDX	2,X	; get ptr to base of array
	 CPD	0,X	; (index - length); should be less than zero
	 PULX		; doesn't change CCR;'
	 BHS	errorbounds1
	 LSLD		; index * 2
	 BRA	areffinish

//*****************************************************************
//*
//*	aref4:		pops
//*			  index (2-byte +offset)
//*			  base (2-byte abs. addr.)
//*			pushes
//*			  addr of element
//*
//*			or generates bounds error.
Paref4
	 LDD	0,X	; get index into D
	 PSHX
	 LDX	2,X	; get ptr to base of array
	 CPD	0,X	; (index - length); should be less than zero
	 PULX		; doesn't change CCR;'
	 BHS	errorbounds1
	 LSLD
	 LSLD		; index * 4
	 BRA	areffinish

errorbounds1:
	 JMP	errorbounds

//*****************************************************************
//*
//*	aref_arb:	pops
//*			  index (2-byte +offset)
//*			  base (2-byte abs. addr.)
//*			pushes
//*			  addr of element
//*		size is 1 byte immediate
//*			or generates bounds error.
Paref_arb
	 LDD	0,X	; get index into D
	 PSHX
	 LDX	2,X	; get ptr to base of array
	 CPD	0,X	; (index - length); should be less than zero
	 PULX		; doesn't change CCR;'
	 BHS	errorbounds1
	 LDAA	0,Y	; load size into A
	 INY
	 MUL
	 BRA	areffinish

//*****************************************************************
//***							      ***
//***		        FLOATING POINT			      ***
//***							      ***
//*****************************************************************

//****************************************************************
//* Include floating point routines here
//*
#include "math11routinesM.asm"
//*
//*
//****************************************************************


//****************************************************************
//*
//*	floatload2:
//*
//*	loads floating point accumulators from top of pcode stack
//*
//*	FPACC2 <-- top of stack float
//*	FPACC1 <-- next to top float
//*
floatload2
	 JSR	GETFPAC2
	 INX
	 INX
	 INX
	 INX
	 JSR	GETFPAC1
	 RTS

//****************************************************************
//*
//*	Pint2fl
//*
//*	takes signed integer from stack and returns
//*	floating point number
//*
Pint2fl
	 PSHX
	 PSHY
	 LDD	0,X		; arg for routine in D
	 JSR	SINT2FLT	; perform conversion
	 PULY
	 PULX
	 DEX
	 DEX
	 BRA	PUTFPAC1	; put FPACC1 on stack and return

//****************************************************************
//*
//*	Pfl2ascii
//*
//*	takes floating point number, string pointer
//*	writes null-terminated ascii string into string
//*	leaves string pointer on stack
//*	string buffer must be 14 bytes long
//*
Pfl2ascii
	 PSHX
	 PSHY
	 INX
	 INX
	 JSR	GETFPAC1	; load FPACC1
	 DEX
	 DEX
	 LDX	0,X		; string pointer
	 JSR	FLTASC		; perform conversion
	 PULY
	 PULX
	 LDD	0,X
	 STD	4,X
	 INX
	 INX
	 INX
	 INX
	 RTS			; pop float and return


//****************************************************************
//*
//*	Pfl2lng
//*
//*	returns hi word in D, lo word in srhi
Pfl2lng
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLT2LNG
	 PULY
	 PULX
	 BCC	fl2lngok
//* error; A has error code
	 JMP	pcode_error_exit
fl2lngok
	 STD	0,X
	 LDD	srhi
	 STD	2,X
	 RTS
//****************************************************************
//*
//*	Plng2fl
//*
//*	call with hi word in D, lo word in srhi
//*
Plng2fl
	 PSHX
	 PSHY
	 LDD	2,X
	 STD	srhi
	 LDD	0,X
	 JSR	SLNG2FLT
	 PULY
	 PULX
	 JMP	PUTFPAC1

//****************************************************************
//*
//*	Pfl2int
//*
//*	uses FLTINT to convert float to integer ...
//*	rounds to nearest int.
//*
Pfl2int
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLT2INT
	 PULY
	 PULX
	 BCC	fl2intok
//* error; A has error code
	 JMP	pcode_error_exit
fl2intok
	 BRA	floatpushd

//****************************************************************
Pfadd:
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTADD
Pfpulfloatpopnoerr
	 PULY
	 PULX
	 INX
	 INX
	 INX
	 INX
	 JMP	PUTFPAC1	; convert FPACC1 to stack and return
//****************************************************************
Pfsub:
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTSUB
	 BRA	Pfpulfloatpopnoerr

//****************************************************************
Pfneg:
//* flip high bit of high byte!
	 LDAA	0,X
	 EORA	#$80
	 STAA	0,X
	 RTS

//****************************************************************
//*
//*	Pfmult
//*
//*	multiplies two floatnums and returns product
//*
Pfmult
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTMUL		; perform multiply
	 BRA	Pfpulfloatpop

//****************************************************************
//*
//*	Pfdiv
//*
//*	divides (next to top) / (top of stack)
//*	error if carry set; then A has error code
//*
Pfdiv
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTDIV

//*  assumes need to pop one float
Pfpulfloatpop

	 PULY
	 PULX
	 BCC	Pbinopok
//* error; A has error code
	 JMP	pcode_error_exit
Pbinopok
	 INX
	 INX
	 INX
	 INX
	 JMP	PUTFPAC1	; convert FPACC1 to stack and return


//****************************************************************
Pfequal
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTCMP		;if Z=1, they are equal
	 PULY
	 PULX
	 BNE	floatpushfalse

floatpushtrue
	 LDD	#1
	 INX
	 INX
	 INX
	 INX
floatpushd
	 INX
	 INX
	 STD	0,X
	 RTS
floatpushfalse
	 INX
	 INX
	 INX
	 INX
	 INX
	 INX
	 CLR	0,X
	 CLR	1,X
	 RTS

Pflt
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTCMP
	 PULY
	 PULX
	 BMI	floatpushtrue
	 BRA	floatpushfalse

Pfgt
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTCMP
	 PULY
	 PULX
	 BGT	floatpushtrue
	 BRA	floatpushfalse

Pfx2y
	 PSHX
	 PSHY
	 JSR	floatload2
	 JSR	FLTXTOY
	 BRA	Pfpulfloatpop

//****************************************************************
//*
//*	Pfsqrt:
//*
//*	square root!
//*	error if carry set; then A has error code
//*
Pfsqrt
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLTSQR

Pfpulfloat
	 PULY
	 PULX
	 BCC	Punaryopok
//* error; A has code
	 JMP	pcode_error_exit

Punaryopok
	 JMP	PUTFPAC1	; convert FPACC1 to stack and return

Pfexp
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLTETOX
	 BRA	Pfpulfloat

Pf10tx
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLT10TX
	 BRA	Pfpulfloat

Pfln
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLTLN
	 BRA	Pfpulfloat

Pflog
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLTLGT
	 BRA	Pfpulfloat

Pfatan
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	FLTATAN
	 TPA
	 PSHA			; save CCR
	 JSR	DEG2RAD		; convert from degrees for bozotron routine
	 PULA
	 TAP			; restore CCR
	 BRA	Pfpulfloat

Pfsin
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	RAD2DEG		; convert to degrees for bozotron routine
	 JSR	FLTSIN
	 BRA	Pfpulfloat

Pfcos
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	RAD2DEG		; convert to degrees for bozotron routine
	 JSR	FLTCOS
	 BRA	Pfpulfloat

Pftan
	 PSHX
	 PSHY
	 JSR	GETFPAC1
	 JSR	RAD2DEG		; convert to degrees for bozotron routine
	 JSR	FLTTAN
	 BRA	Pfpulfloat


//*****************************************************************
//***							      ***
//***		       	  LCD DISPLAY			      ***
//***							      ***
//*****************************************************************

//*****************************************************************
//*
//*	Pprintchar:
//*
//*	print word as char to LCD
//*
Pprintchar
	 LDAA	#2
	 LDAB	1,X
	 JSR	LCDstart
	 INX
	 INX
	 RTS

//*****************************************************************
//*
//*	Pprintlcd4:
//*
//*	    pop longword off of stack and print to LCD in hex
//*
Pprintlcd4
	 JSR	LCDcls
	 LDAA	0,X		; get high byte
	 JSR	Byte2Hex2LCD
	 LDAA	1,X		; low byte
	 JSR	Byte2Hex2LCD
	 INX
	 INX
	 BRA	print2nocls

//*****************************************************************
//*
//*	Pprintlcd2:
//*
//*	    pop word off of stack and print to LCD
//*
Pprintlcd2
	 JSR	LCDcls
print2nocls
	 LDAA	0,X		; get high byte
	 JSR	Byte2Hex2LCD
	 LDAA	1,X		; low byte
	 JSR	Byte2Hex2LCD
	 INX
	 INX
	 RTS			; pop and finish


//*****************************************************************
//*
//*	Pprintstring
//*
//*		pops address of null-terminated string
//*
Pprintstring
	 JSR	LCDcls
	 PSHX			; save PSP
	 LDX	0,X		; get addr of string
	 LDAA	#2		; tell LCD to print characters
Ppsloop
	 LDAB	0,X
	 BEQ	Ppsexit
	 JSR	LCDstart	; print char
	 INX
	 BRA	Ppsloop
Ppsexit
	 PULX
	 INX
	 INX
	 RTS

//*****************************************************************
//*
//*	Pprintf		print formatted string
//*
//*	takes following on stack:
//*
//*		ssss	string ptr to formatting string
//*		aaaa	arg1  \
//*		bbbb	arg2  |-- optional args: ints, floats, or
//*		cccc	arg3  /			 string ptrs.
//*		nnnn	# of arg bytes (NOT including initial fmt string)
//*
//*	returns with zero on stack.
//*
//*	accepts following formatting controls:
//*
//*		"%d"	decimal-printed integer
//*		"%x"	hex-printed integer
//*		"%f"	floating-point number
//*		"%s"	null-terminated char string
//*		"%c"	ascii-printed byte (low byte of int)
//*		"%b"	binary-printed byte (low byte of int)
//*
//*	"%" sign followed by anything else prints that character.
//*
//*		0x0a	end-of-line (clears screen when another
//*				char is printed)
//*
//*	guarantees printing up to 80 characters; a given printf that
//* 	tries to print more than 80 characters runs the risk of the
//*	excess characters being truncated.
//*
//*	uses following routines:
//*
//*	    check_printbuffer	returns # of free bytes in string
//*				buffer in D.
//*
//*	    putchar		places A in string buffer.
//*				returns with C set if failed.
//*				preserves B, X reg.
//*
//*	    putchar_always	places A in string buffer, despite
//*				putchar claiming it's full (putchar
//*				always lies and saves you one char.)
//*
Pprintf:
	 PSHY		; save PC
	 STX	srhi	; save SP for continued use

//* check that we have 81 chars free in print buffer (80 chars + \n)
	 JSR	check_printbuffer	; returns # free in D
	 CMPD	#81
	 BHS	pf_okay

pf_exitnoexecute
//* back up PC to try again later; also poke BRN
//* so that somebody else gets a chance to run
	 LDAA	#$21		; BRN opcode
	 STAA	pcode_branch
	 LDX	srhi		; restore SP
	 PULY			; restore PC
	 DEY			; back it up
	 RTS

pf_okay
//* calc ptr to formatting string
	 LDX	srhi
	 LDD	srhi
	 ADDD	#2	; point at args
	 ADDD	0,X	; D -> formatting string ptr
	 STD	sr2hi
	 SUBD	#2
	 STD	sr3hi	; sr3hi -> arglist
	 LDX	sr2hi	; X -> format string ptr
	 LDX	0,X	; X := format string ptr
	 INX
	 INX		; point past string size header
pf_loop:
	 LDAA	0,X
	 BNE	pf_check_percent
	 JMP	pf_exitnormal
//* check for either % or \ formatting characters
pf_check_percent
	 CMPA	#'%;'
	 BEQ	pf_got_percent
	 JMP	pf_normalchar
pf_got_percent
//* have %, get formatting char
	 INX
	 LDAA	0,X		; get percented char
	 CMPA	#'f;'
	 BNE	pf_notfloat
//******** print FLOAT
	 STX	sr2hi		; save fmt string ptr
	 LDX	sr3hi		; is pointing in middle of float
	 DEX
	 DEX			; point at beginning of float
	 PSHX			; save
	 JSR	GETFPAC1	; load float into FPACC1
	 PULX
	 DEX
	 DEX
	 STX	sr3hi		; back up ptr to next arg
	 LDX	#STRING_TEMP_BUFFER	; buffer for ascii conversion
	 JSR	FLTASC		; cnvt into temp buffer
	 LDX	#STRING_TEMP_BUFFER	; get buffer ptr again
	 JMP	pf_stringloop


pf_notfloat
	 CMPA	#'d;'
	 BNE	pf_notdecimal
//******** print DECIMAL
	 STX	sr2hi		; save fmt string ptr
	 LDY	#STRING_TEMP_BUFFER	; for composing ascii
	 LDX	sr3hi		; point at int
	 DEX
	 DEX
	 STX	sr3hi
	 LDD	2,X		; get int
	 BPL	pf_deccvnt
//* 2's complement D to print negative number
//* put "-" in output buffer!
	 LDAA	#'-;'
	 STAA	0,Y
	 INY
	 LDD	#0
	 SUBD	2,X
pf_deccvnt
	 LDX	#10
	 IDIV
       PSHB			; dig 0
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 1
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 2
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 3
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 4
//* now pull them off stack and place in string buffer (cvting to ascii).
//* A=1 initially meaning drop a leading zero if you find it (then clear A).
//* transfer Y to X and use X as string ptr
	 PSHY
	 PULX
	 LDAA	#1
       PULB
	 TSTB			; set regs
	 BEQ	pf_dig3
	 CLRA			; got non-zero dig
	 ADDB	#'0;'
	 STAB	0,X
	 INX
pf_dig3:
       PULB
	 TSTA
	 BEQ	pf_printdig3
	 TSTB			; set regs
	 BEQ	pf_dig2
	 CLRA			; got non-zero dig
pf_printdig3:
	 ADDB	#'0;'
	 STAB	0,X
	 INX
pf_dig2:
       PULB
	 TSTA
	 BEQ	pf_printdig2
	 TSTB			; set regs
	 BEQ	pf_dig1
	 CLRA			; got non-zero dig
pf_printdig2:
	 ADDB	#'0;'
	 STAB	0,X
	 INX
pf_dig1:
       PULB
	 TSTA
	 BEQ	pf_printdig1
	 TSTB			; set regs
	 BEQ	pf_dig0
	 CLRA			; got non-zero dig
pf_printdig1:
	 ADDB	#'0;'
	 STAB	0,X
	 INX
pf_dig0:
       PULB			; print this one always
	 ADDB	#'0;'
	 STAB	0,X
	 INX
	 CLRB
	 STAB	0,X		; terminating zero.
//* now print it
	 LDX	#STRING_TEMP_BUFFER
	 BRA	pf_stringloop

pf_notdecimal:
	 CMPA	#'x;'
	 BNE	pf_nothex
//******** print hex
	 STX	sr2hi		; save ptr to format string
	 LDX	sr3hi		; get arglist ptr
	 DEX
	 DEX
	 STX	sr3hi		; advance arglist ptr; 2,X is integer to fmt.
	 LDAA	2,X		; high byte
	 JSR	byte2hexascii	; A = hi nybble, B = lo
	 JSR	putchar
	 BCS	pf_exitoverflow
	 TBA
	 JSR	putchar
	 BCS	pf_exitoverflow
	 LDAA	3,X		; low byte
	 JSR	byte2hexascii	; A = hi nybble, B = lo
	 JSR	putchar
	 BCS	pf_exitoverflow
	 TBA
	 JSR	putchar
	 BCS	pf_exitoverflow
	 BRA	pf_continue

pf_nothex:
	 CMPA	#'c;'
	 BNE	pf_notchar
//******** print char low byte ascii
	 STX	sr2hi		; save ptr to formatting string
	 LDX	sr3hi		; get arglist ptr
	 DEX
	 DEX
	 STX	sr3hi		; advance arglist ptr
	 LDAA	3,X		; get byte to format in A
	 JSR	putchar
	 BCS	pf_exitoverflow
	 BRA	pf_continue

pf_notchar:
	 CMPA	#'b;'
	 BNE	pf_notbinary
//******** print low byte binary
	 STX	sr2hi		; save ptr to formatting string
	 LDX	sr3hi		; get arglist ptr
	 DEX
	 DEX
	 STX	sr3hi		; advance arglist ptr
	 LDAB	3,X		; get byte to format in B
	 LDY	#8		; loop printing 8 times
pf_binaryloop
	 LSLB			; C is bit to print
	 LDAA	#'0;'
	 ADCA	#0		; add C bit to A
	 JSR	putchar
	 BCS	pf_exitoverflow
	 DEY
	 BNE	pf_binaryloop
	 BRA	pf_continue

pf_notbinary:
	 CMPA	#'s;'
	 BNE	pf_normalchar
//******** print STRING
	 STX	sr2hi		; save ptr to formatting string
	 LDX	sr3hi		; get arglist ptr
	 DEX
	 DEX
	 STX	sr3hi		; advance arglist ptr
	 LDX	2,X		; get ptr to string to print
	 INX
	 INX			; point past string length header
pf_stringloop:
	 LDAA	0,X
	 BEQ	pf_stringloopdone
	 JSR	putchar
	 BCS	pf_exitoverflow	; exit if putchar failed
	 INX
	 BRA	pf_stringloop
pf_stringloopdone:
	 BRA	pf_continue

pf_normalchar:
	 JSR	putchar		; print the character, C set if failed
	 BCC	pf_continue_norestore

pf_exitoverflow:
//* putchar failed; print a \n and exit printing
	 LDAA	#CR
	 JSR	putchar_always	; ha ha there is room for one more
	 BRA	pf_exitnormal

pf_continue:
	 LDX	sr2hi		; restore ptr to format string
pf_continue_norestore:
	 INX			; point to next character
	 JMP	pf_loop

pf_exitnormal:
	 LDX	srhi		; restore SP
	 LDD	srhi
	 ADDD	#2
	 ADDD	0,X		; point to string ptr
	 XGDX			; new SP
	 LDD	#0
	 STD	0,X		; zero return value
	 PULY			; restore PC
	 RTS


/*********************************/
/*******                   *******/
/******* SPECIAL FUNCTIONS *******/
/*******                   *******/
/*********************************/

//************************************************************
//*
//*	Pbench		returns integer equalling number
//*			of machine cycles executing in
//*			1 millisecond of real time
//*
//*			if systemint took no time, this would be
//*			1000usec * (.5 us/cycle) = 2000
Pbench:
	 PSHX
//* wait for transition of system_time_lo.
	 LDD	system_time_lo
	 ADDD	#1
benchlp1
	 CPD	system_time_lo
	 BNE	benchlp1
//* start counting in X until system_time is again incremented
	 ADDD	#1		; exit value
	 LDX	#0
benchlp2
	 INX			; 3 cycles
	 CPD	system_time_lo	; 6 cycles
	 BNE	benchlp2	; 3 cycles
//* num cycles ~= X  *  12
	 XGDX			; put loop count in D
	 PULX
	 DEX
	 DEX
	 STD	0,X		; push on pcode stack
	 LDD	#12
	 DEX
	 DEX
	 STD	0,X		; push 13
	 JMP	Pmult2		; multiply and return


//************************************************************
//*
//*	Psystime	returns integer long describing
//*			system time
//*
Psystime:
	 DEX
	 DEX
	 DEX
	 DEX
	 SEI				; disable interrupts
	 LDD	system_time_lo
	 STD	2,X
	 LDD	system_time_hi
	 CLI				; enable them
	 STD	0,X
	 RTS

//**********************************
//***                            ***
//*** Machine Language Interface ***
//***                            ***
//**********************************

//************************************************************
//*
//*	Pcallml
//*                          Call machine language subroutine
//*			   pops 2 bytes, puts in D
//*			   pops address, JSR's to this address
//*			   pushes D onto stack
//*

Pcallml:
	 LDD	0,X
	 INX
	 INX
	 PSHX
	 PSHY
	 LDX	0,X
	 JSR	0,X
	 PULY
	 PULX
	 STD	0,X
	 RTS

//*****************************
//***                       ***
//*** Initialize Interrupts ***
//***                       ***
//*****************************

//************************************************************
//*
//*	Pinitint
//*
//*			Initialize 6811 interrupts
//*			to pcode defaults
//*
//*			turns interrupts off at start;
//*			exits with them on.
Pinitint:
	 SEI

	 PSHX
	 PSHY

	 LDAA	HPRIO
	 ANDA	#$40			; test SMOD bit
	 BNE	*+7
	 LDX	#$FF00			; normal mode interrupts
	 BRA	*+5
	 LDX	#$BF00			; special mode interrupts

	 PSHX				; save X for later

//* store "BadInt" into all vector locations
	 LDAB	#$C0
	 ABX				; first vector location
	 LDY	#BadInt
ini_loop STY	0,X
	 INX
	 INX
	 PSHX
	 PULA				; high byte
	 PULA				; low byte of X
	 TSTA
	 BNE	ini_loop

	 PULX
	 LDD	#startup
	 STD	RESETINT,X		; reset interrupt
	 LDD	#SystemInt
	 STD	TOC4INT,X		; TOC4 interrupt

#ifndef NO_BEEPER
	 LDD	#BeepInt
	 STD	TOC5INT,X		; TOC5 interrupt
#endif
#if defined (REV2) | defined (REV21)
	 LDD	#StopInt
	 STD	TIC1INT,X		; TIC1 interrupt
#endif
#ifdef REV2
	 LDD	#IRInt
	 STD	TOC1INT,X		; TOC1 interrupt
#endif

//* set up interrupt vector enables
//* begin by clearing them all, then setting the necessary ones
	 LDX	#BASE
	 CLR	TMSK1,X
	 CLR	TMSK2,X

//* setup TIC1 interrupt on falling edge for powerdown memory save
#if !(defined(BOOKBOT) | defined(HANDYBOARD))
	 LDAA	#%00110000
#else
	 LDAA	#%00000000
#endif

	 STAA	TCTL2,X			; set for falling edge
//* IC1:  powerdown sequence          BOOKBOT,SBOT: DISABLED  YAMABICO: ENABLED
//* TOC1:  IR generator               BOOKBOT: DISABLED  YAMABICO: ENABLED
//* OC4:  1 kHz system interrupt
//* OC5:  beeper

#if defined ( BOOKBOT )
	 BSET	TFLG1,X %00011000	; clear pending flag
	 BSET	TMSK1,X %00011000	; enable interrupt (not IR)
#endif

#if defined ( YAMABICO )
	 BSET	TFLG1,X %10011100	; clear pending flag
	 BSET	TMSK1,X %10011100	; enable interrupts (incl. IR decode)
#endif

#if defined ( SBOT)
	 BSET	TFLG1,X %10011000	; clear pending flag
	 BSET	TMSK1,X %00011000	; enable interrupts (not IR xmit)
#endif

#if defined (HANDYBOARD)
	 BSET	TFLG1,X %00011000	; enable sys interrupt
	 BSET	TMSK1,X %00011000	; and beeping only
#endif

#if defined (REV2) | defined (REV21) 
	 BSET	TFLG1,X %10011100	; clear pending flag

#ifndef NO_BEEPER
	 BSET	TMSK1,X %00011100	; enable interrupts (not IR xmit)
#else
	 BSET	TMSK1,X %00010100	; enable everything but beeper
#endif /* NO_BEEPER */

#endif

	 PULY
	 PULX

	 CLI
	 RTS

//*****************************************************************
//*
//*	PRINT BUFFER
//*
//*	is a ring buffer.
//*
//*	exists in memory from location
//*		PRINT_BUFFER
//*		        to
//*		PRINT_BUFFER + PRINT_BUFFER_SIZE - 1
//*
//*	"print_buffer_end" points at last character inserted.
//*	"serial_buffer_pos" points at last character transmitted;
//*		(use system_status bit "SYSSTAT_IGNOREUI")
//*	"lcd_buffer_pos" points at last character outputted;
//*		(use system_status bit "SYSSTAT_NOLCD")
//*
//*	when one of the "_buffer_pos" pointers equals "_buffer_end",
//*	then no characters exist in the buffer (i.e., the whole buffer
//*	is free).
//*

check_printbuffer:
//* uses sr4hi, D; preserves X,Y
//* returns # of chars vacant in the buffer in D.
	 LDAA	system_status
	 ANDA	#SYSSTAT_IGNOREUI
	 BEQ	cpb_check_serialbuf
	 LDD	#PRINT_BUFFER_SIZE
	 STD	sr4hi			; serial buf sez "i don't care"
	 BRA	cpb_check_lcdbuf

cpb_check_serialbuf
//* calc bytes left for serial output (store in sr4hi)
//* CASE 1.  If print_buffer_end >= serial_buffer_pos, then answer is
//* 	PRINT_BUFFER_SIZE - (p_b_e - s_b_p)
//*     = PRINT_BUFFER_SIZE - p_b_e + s_b_p
//*
//* CASE 2.  If serial_buffer_pos > print_buffer_end, then answer is
//*	s_b_p - p_b_e - 1  (ptrs can't hit)
	 LDD	serial_buffer_pos
	 SUBD	print_buffer_end
//* if less than or = zero, then 1st case is true
	 BLS	cpb_serial_case1
//* have case 2
	 SUBD	#1
	 STD	sr4hi
	 BRA	cpb_check_lcdbuf
cpb_serial_case1
	 LDD	#PRINT_BUFFER_SIZE
	 SUBD	print_buffer_end
	 ADDD	serial_buffer_pos
	 STD	sr4hi

cpb_check_lcdbuf
	 LDAA	system_status
	 ANDA	#SYSSTAT_NOLCD
	 BEQ	cpb_do_lcdbuf
	 LDD	#PRINT_BUFFER_SIZE	; lcd buf doesn't care;'
	 BRA	cpb_take_min		; take lesser of D and sr4hi
cpb_do_lcdbuf
//*
//* same as serial.
//*
	 LDD	lcd_buffer_pos
	 SUBD	print_buffer_end
//* if less than or = zero, then 1st case is true
	 BLS	cpb_lcd_case1
//* have case 2
	 SUBD	#1
	 BRA	cpb_take_min
cpb_lcd_case1
	 LDD	#PRINT_BUFFER_SIZE
	 SUBD	print_buffer_end
	 ADDD	lcd_buffer_pos

cpb_take_min
//* lesser of D and sr4hi is answer
	 CPD	sr4hi
	 BLS	cpb_take_d
	 LDD	sr4hi		; mem is less
cpb_take_d
	 RTS


//**********************************************************************
//*
//*	putchar		insert character into print buffer
//*	putchar_always
//*
//*	need 2 or more chars to allow operation
//* 	exit with carry set if operation failed
//*	advances serial pointer if _IGNOREUI is true
//* 	call "putchar_always" to insert *one* char after putchar failed.
//*
//*	preserve B, X, and Y
//*
putchar:
	 PSHB			; preserve B
	 PSHA			; save char to be printed
	 JSR	check_printbuffer
	 CPD	#2
	 BHS	putchar_ok
//* fail
	 SEC
	 PULA
	 PULB
	 RTS
putchar_ok:
	 PULA
	 BRA	putchar_continue
putchar_always:
//* assume there is space
	 PSHB
putchar_continue:
	 SEI				; turn off interrupts
	 PSHX
	 LDX	print_buffer_end
//* increment ptr
	 INX
	 CPX	#PRINT_BUFFER+PRINT_BUFFER_SIZE
	 BNE	putchar_ptrok
	 LDX	#PRINT_BUFFER
putchar_ptrok:
	 STX	print_buffer_end
	 STAA	0,X
//* if _IGNOREUI is true, then check if serial ptr need to be advanced.
	 LDAA	system_status
	 ANDA	#SYSSTAT_IGNOREUI
	 BEQ	putchar_finish
//* if new buffer ptr = serial ptr, advance serial.
	 CPX	serial_buffer_pos
	 BNE	putchar_finish
//* increment.
	 LDX	serial_buffer_pos
	 INX
	 CPX	#PRINT_BUFFER+PRINT_BUFFER_SIZE
	 BNE	putchar_serialptrok
	 LDX	#PRINT_BUFFER
putchar_serialptrok:
	 STX	serial_buffer_pos
putchar_finish:
	 PULX			; restore me baby
	 PULB			; restore me too
	 CLC			; exit w/o error
	 CLI			; enable interrupts
	 RTS



//*****************************************************************
//*
//*	LCDinit		calls LCDinit to get things going, then
//*			initializes the display
//*
//* 	trash D, X, and Y
//*
//*	LCDcls		clear screen on LCD
//*

LCDinit:
	 BSR	LCDloaddriver
#if LCD_ROWS == 1
	 LDD	#$0030		; 8-bit operation, 1-line display
#endif
#if LCD_ROWS == 2
	 LDD	#$0038		; 8-bit operation, 2-line display
#endif
	 JSR	LCDstart
	 LDAB	#%00001100	; display on, cursor & blink off
	 JSR	LCDstart

	 BSR	LCDcharinit

//* initialize various interrupt driver things
	 LDAA	#LCDSTAT_PRINTCHARCLS
	 STAA	lcd_status	; set status to ready to print

	 LDAA	#%00001000	; set I1
	 STAA	lcd_frobline1
	 CLRA
	 STAA	lcd_frobline2

LCDcls:
	 LDD	#$0001		; home and clear screen
	 JSR	LCDstart
	 LDAB	#$06		; set to increment char loc & cursor pos
	 JSR	LCDstart
	 LDAB	#$8F		; put cursor just to right of view window
	 JSR	LCDstart

	 RTS

//*****************************************************************
//*
//*	LCDcharinit:	loads custom characters into LCD character
//*			RAM.  Assumes driver has been loaded..
//*
LCDcharinit
	 LDD	#$0040		; begin with character 0
	 JSR	LCDstart
	 LDAA	#2		; data mode
	 LDX	#LCDchardata

LCDcharinitloop
	 LDAB	0,X
	 JSR	LCDstart
	 INX
 	CPX	#LCDchardataend
	 BNE	LCDcharinitloop

	 RTS

LCDchardata
//* character 0:  used for status frobbies
#ifdef LCD_ROWS
#ifdef LM576
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
#else
//* frob character one

#define HEARTBEAT_HEART
/* #define HEARTBEAT_PACMAN */
/* #define HEARTBEAT_JUMPING_JACKS */
	 	       
#if defined(HEARTBEAT_JUMPING_JACKS)
        FCB     %00001110
	 FCB	%00001110
        FCB     %00000100
	 FCB	%00000100
	 FCB	%00011111
	 FCB	%00000100
	 FCB	%00000100
	 FCB	%00001010
#elif defined(HEARTBEAT_PACMAN)
	 FCB	%00000000
	 FCB	%00000110
        FCB     %00001111
        FCB	%00011100
	 FCB	%00011000
	 FCB	%00011100
	 FCB	%00001111
	 FCB	%00000110
#elif defined(HEARTBEAT_HEART)
	 FCB	%00000000
	 FCB	%00001010
        FCB     %00011111
        FCB	%00011111
	 FCB	%00001110
	 FCB	%00000100
	 FCB	%00000000
	 FCB	%00000000
#else		       
	 FCB	%00000000
	 FCB	%00000000
        FCB     %00001110
        FCB	%00001110
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
#endif		       
	 	       
#endif		       
#endif		       
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00010001
	 FCB	%00010001
	 FCB	%00010001
	 FCB	%00001111
	 FCB	%00000001
	 FCB	%00001110
	 	       
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00001100
	 FCB	%00000100
	 FCB	%00001000
	 	       
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00001111
	 FCB	%00010001
	 FCB	%00010001
	 FCB	%00001111
	 FCB	%00000001
	 FCB	%00001110
	 	       
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00001100
	 FCB	%00001100
	 FCB	%00000000
	 FCB	%00001100
	 FCB	%00001100
	 FCB	%00000000
	 	       
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00001100
	 FCB	%00001100
	 FCB	%00000000
	 FCB	%00001100
	 FCB	%00000100
	 FCB	%00001000
	 	       
//* character #6	       
	 FCB	%00000010
	 FCB	%00000000
	 FCB	%00000110
	 FCB	%00000010
	 FCB	%00000010
	 FCB	%00000010
	 FCB	%00010010
	 FCB	%00001100
	 	       
#ifdef LCD_ROWS	       
#ifdef LM576	       
//* character #7	       
	 FCB	%00011111
	 FCB	%00000001
	 FCB	%00000010
	 FCB	%00000100
	 FCB	%00001000
	 FCB	%00001000
	 FCB	%00001000
	 FCB	%00000000
#else		       
//* frob character two   
	 	       
#if defined(HEARTBEAT_JUMPING_JACKS)
        FCB     %00001110
	 FCB	%00001110
        FCB     %00010101
	 FCB	%00001110
	 FCB	%00000100
	 FCB	%00000100
	 FCB	%00001010
	 FCB	%00010001
#elif defined(HEARTBEAT_PACMAN)
        FCB     %00000000
	 FCB	%00000000
        FCB     %00001110
	 FCB	%00011111
	 FCB	%00011111
	 FCB	%00011111
	 FCB	%00001110
	 FCB	%00000000
#elif defined(HEARTBEAT_HEART)
        FCB     %00000000     
	 FCB	%00000000     
        FCB     %00001010     
	 FCB	%00001110
	 FCB	%00000100
	 FCB	%00000000
	 FCB	%00000000
	 FCB	%00000000
#else
	 FCB	%00000000
	 FCB	%00000000
        FCB     %00000000
        FCB	%00000000
	 FCB	%00001110
	 FCB	%00001110
	 FCB	%00000000
	 FCB	%00000000
#endif

#endif
#endif

LCDchardataend

//******************************************************************
//*
//*	LCDloaddriver:	downloads single-char or cmd driver for LCD
//*
LCDloaddriver
	 LDX	#LCDdriver
	 LDY	#LCDstart
LCDloop
	 LDAA	0,X
	 STAA	0,Y
	 INX
	 INY
	 CPX	#LCDenddriver
	 BNE	LCDloop

	 RTS

//******************************************************************
//*
//*	LCD driver:
//*	    call routine at LCDstart
//*	    with A=command, B=data to be output to LCD
//*
//*	all regs are preserved
LCDdriver:
	 STX	LCDtempword-LCDdriver+LCDstart
	 LDX	#$1000

	 SEI				; disable interrupts
	 BCLR	HPRIO,X %00100000	; put into single chip mode
	 BCLR	PORTA,X %00010000	; turn off LCD E line

	 STAA	LCDtempbyte-LCDdriver+LCDstart

	 CLR	DDRC,X			; make port C input

//* if A is query command, just check LCD for busy-ness
	 CMPA	#LCDCMD_QUERYBUSY
	 BNE	LCDBusy

	 LDAA	#1
	 STAA	PORTB			; read operation from LCD

	 BSET	PORTA,X %00010000	; frob LCD on
	 LDAA	PORTC,X			; get status
	 BCLR	PORTA,X %00010000	; frob LCD off

	 BRA	LCDdriverexit		; exit

LCDBusy
	 LDAA	#1
	 STAA	PORTB			; read operation from LCD

	 BSET	PORTA,X %00010000	; frob LCD on
	 LDAA	PORTC,X			; get status
	 BCLR	PORTA,X %00010000	; frob LCD off

	 ANDA	#$80			; bit 7 is busy flag
	 BNE	LCDBusy

	 LDAA	#$FF
	 STAA	DDRC,X			; make port C output

	 LDAA	LCDtempbyte-LCDdriver+LCDstart
	 STAA	PORTB,X		; high byte is control
	 STAB	PORTC,X		; low byte is data

	 BSET	PORTA,X %00010000
	 BCLR	PORTA,X %00010000	; frob LCD

LCDdriverexit
	 BSET	HPRIO,X %00100000	; put into expanded chip mode

	 LDX	LCDtempword-LCDdriver+LCDstart

LCDdriverCLI
	 CLI				; enable interrupts
	 RTS				; return to monitor command loop

LCDtempword
	 FDB	0
LCDtempbyte
	 FCB	0

LCDenddriver
//****************************************************************


//****************************************************************
//*
//*	byte2hexascii	converts byte to its 2-char ASCII hex equiv.
//*
//*			INPUT:  byte in A register
//*			OUTPUT:	MSB is A, LSB is B
//*
byte2hexascii:
	 PSHA		* store it; work on B first
	 ANDA	#$0F	* get LS nybble
	 ADDA	#$30	* puts it in ASCII "0" to "?"
	 CMPA	#$3A
	 BMI	H2A1
	 ADDA	#$07	* now it's "A" to "F" ;'
H2A1	TAB		* done with LSB
	 PULA
	 LSRA		* shift that baby down
	 LSRA
	 LSRA
	 LSRA		* into the lower nybble position
	 ADDA	#$30	* puts it in ASCII "0" to "?"
	 CMPA	#$3A
	 BMI	H2A2
	 ADDA	#$07	* now it's "A" to "F";'
H2A2	RTS		* done


//****************************************************************
//*
//*	Byte2Hex2LCD	cvts byte in A reg to hex & prints to LCD
//*
Byte2Hex2LCD:
	 JSR	byte2hexascii	; A=1st char, B=2nd char
	 PSHB			; save 2nd char
	 TAB
	 LDAA	#2
	 JSR	LCDstart
	 PULB			; load 2nd char
	 JSR	LCDstart
	 RTS

//****************************************************************
//*
//*	PrintDLCD:	prints number in D to LCD
//*
PrintDLCD
	 LDX	#10
	 IDIV
       PSHB			; dig 0
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 1
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 2
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 3
	 XGDX
	 LDX	#10
	 IDIV
       PSHB			; dig 4

	 JSR	LCDstart
	 LDD	#$0001		; home and clear screen
	 JSR	LCDstart
	 LDD	#$0007		; set to increment char loc & cursor pos
	 JSR	LCDstart
	 LDD	#$008F		; put cursor just to right of view window
	 JSR	LCDstart

	 LDAA	#2
       PULB
	 ADDB	#$30
	 JSR	LCDstart
       PULB
	 ADDB	#$30
	 JSR	LCDstart
	 LDAB	#512+'.'
	 JSR	LCDstart
       PULB
	 ADDB	#$30
	 JSR	LCDstart
       PULB
	 ADDB	#$30
	 JSR	LCDstart
       PULB
	 ADDB	#$30
	 JSR	LCDstart
	 LDAB	#512+'m'
	 JSR	LCDstart
	 LDAB	#512+'s'
	 JSR	LCDstart

	 RTS

//****************************************************************
//*
//*	printstringLCD:
//*		clears screen and
//*		prints null-terminated string pointed at by X.
//*		assumes LCD driver is loaded.
printstringLCD
	 LDD	#$0002
	 JSR	LCDstart

	 LDAA	#2
psloop
	 LDAB	0,X
	 BEQ	psloopdone
	 JSR	LCDstart
	 INX
	 BRA	psloop
psloopdone
	 RTS


//*****************************************************************
//***							      ***
//***		       	PROCESS CONTROL			      ***
//***							      ***
//*****************************************************************

//*****************************************************************
//*
//*	PROCESS MANAGER
//*
//*	creates new processes.
//*
//*	Call with following on current process's stack:
//*
//*		$0000
//*		<arg 1>  \
//*		 ...     |-- args to function call
//*		<arg N>  /
//*		nnnn      -- number of arg bytes, incl. $0000
//*		pcpc	  -- addr of func call
//*		tttt	  -- # of ticks/timeslice (low byte used)
//*		ssss	  -- # of stack bytes to reserve (2 bytes)
//*
//*	All of the previous is popped from the stack.
//*	A 2-byte return value is generated:  the process number given
//*	to the new process.
//*
//*
//*	How it does it:
//*
//*	1.  Look through active processes for "best fit" of stack size
//*	required.
//*
//*		generates PSTAT_NONEWSTACKSPACE error if not enough
//*		stack exists.
//*
//*	2.  Find empty process slot and install process data.  Link
//*	process into list of active processes.
//*
//*		generates PSTAT_NOPROCSPACE error if there are no
//*		remaining process slots.
//*
//*	3.  Copy stack data onto new process's stack.  Return process ID.
//*
//*
//*
//*	temporary storage:
//*
//*	srhi		X register (SP of current process)
//* For part 1:
//*	sr2hi		addr of proc slot found
//*	sr3hi		best fit so far (0 = best)
//*
Pstartprocess:
	 PSHY			; save PC
	 STX	srhi		; save SP for later use
	 LDD	#$7FFF
	 STD	sr3hi		; best fit for finding stack

//* beginning with "UI process", look for free stack.
	 LDX	#PROCESS_TABLE

mp_findstackloop
	 LDY	P_NEXT,X	; get ptr to next process
	 CPY	#PROCESS_TABLE	; = first process?
	 BNE	mp_findslotaddr ; no
//* if reached 1st process, then have till rest of all stack
	 LDD	#PCODE_STACK_END	; have rest of total stack
	 STD	sr4hi

	 BRA	mp_findstacksize
mp_findslotaddr
	 LDD	P_STACK_ORG,Y		; lower stack limit
	 STD	sr4hi
//* Y has stack org for next proc (our limit), X points at cur proc slot
mp_findstacksize
	 LDD	P_STACK_LIM,X		; end of stack, current proc
	 SUBD	sr4hi			; D has difference
//* D has # of bytes between two stacks.  Let's see if it's big enough
//* for our uses
	 PSHX				; save curr proc ptr
	 LDX	srhi			; get ptr to pcode stack
	 SUBD	0,X			; subtract desired stack size
	 PULX
//* D now has amount of extra stack space if we were to use this slot.
//* if this is less than zero, then this slot is too small.
	 BMI	mp_trynextslot
//*
//* compare this difference with previous best difference (sr3hi).
//* if this one is better, store X as best slot so far, and this diff
//* as new best.
//*
	 CPD	sr3hi
//* if greater than zero, then current error is greater than a previous
//* best.  Try again.
	 BGT	mp_trynextslot
//*
//* hooray!  we found a better fit.  store X as addr, D as error of
//* this fit.
	 STX	sr2hi
	 STD	sr3hi
//*
//* X is addr of curr process slot.
//* Y is addr of next process slot.
//* If Y = PROCESS_TABLE, then we're done.
//*
mp_trynextslot
	 CPY	#PROCESS_TABLE
	 BEQ	mp_stacksearchdone

	 PSHY
	 PULX
	 BRA	mp_findstackloop
//*
//* OK, we finished!
//* Did we find any stack?
mp_stacksearchdone
	 LDD	sr3hi
	 CPD	#$7FFF		; pre-loaded as "best fit"
	 BNE	mp_findprocslot
//*
//* bad news boys and girls.  There was no stack to be found.
//* Exit with PSTAT_NONEWSTACKSPACE error.
	 LDAA	#PSTAT_NONEWSTACKSPACE
	 BRA	mp_exiterror

mp_findprocslot
//*
//* this is relatively easy.  We start at PROCESS_TABLE and search
//* for a process slot with status "PSTAT_DEAD".
	 LDX	#PROCESS_TABLE+PROCESS_SLOT_LENGTH	; skip UI proc

mp_findslotloop
	 LDAA	P_STATUS,X
	 CMPA	#PSTAT_DEAD
	 BEQ	mp_installproc		; found one

	 XGDX
	 ADDD	#PROCESS_SLOT_LENGTH
	 XGDX
	 CPX	#PROCESS_TABLE+PROCESS_SLOT_LENGTH*MAX_PROCESSES
	 BNE	mp_findslotloop

//*
//* uh oh.  we didn't find one
	 LDAA	#PSTAT_NOPROCSPACE
mp_exiterror:
//* exit by poking the error code into our status and
//* poking BRN, also decrementing our PC to point at the offending
//* instruction
	 LDX	srhi			; restore SP
	 PULY				; restore PC
	 DEY				; back it up
	 JMP	pcode_error_exit

mp_installproc
//*
//* 1. install new slot into proc list
//* 2. copy initial vals into new proc slot
//* 3. copy stack into new proc stack
//* 4. exit and finish!
//*
//* X has ptr to new proc slot; swap it to Y
	 PSHX
	 PULY
//* setup ptrs
	 LDX	sr2hi		; ptr to "prev" proc
	 LDX	P_NEXT,X	; ptr to "next" proc
//* X -> next
	 STY	P_PREV,X	; new <- next
	 STX	P_NEXT,Y	; new -> next

	 LDX	sr2hi
//* X -> prev
	 STY	P_NEXT,X
	 STX	P_PREV,Y
//* X -> prev, Y -> new
//* set up stack lims
	 LDD	P_STACK_LIM,X
	 SUBD	#1
	 STD	P_STACK_ORG,Y
	 ADDD	#1
	 LDX	srhi		; get ptr to pcode stack
	 SUBD	0,X		; compute stack limit from stack size
	 STD	P_STACK_LIM,Y
//* have set up _STACK_ORG, _STACK_LIM, _NEXT, _PREV
//* setup _TICKS, _PC, _STATUS
	 LDAA	3,X		; low byte of ticks
	 STAA	P_TICKS,Y
	 LDD	4,X		; PC
	 STD	P_PC,Y
	 LDAA	#PSTAT_RUNNING	; initial status
	 STAA	P_STATUS,Y
//* setup _ID
	 LDD	process_counter
	 ADDD	#1
	 STD	process_counter
	 STD	P_ID,Y
//* almost done.  copy stack-data from running pcode stack to new stack.
	 LDD	srhi		; base of pcode stack
	 ADDD	6,X		; # of stack bytes to copy
	 ADDD	#8		; D now points just above 1st byte to copy
	 LDX	6,X		; # of bytes again
	 XGDX			; count in D, ptr in X
	 DEX			; fix ptr
	 STX	srhi		; this will be our SP when done
	 STY	sr2hi		; save slot ptr of new process
	 LDY	P_STACK_ORG,Y	; get ptr to our new stack
mp_copyloop
	 PSHA
	 LDAA	0,X
	 STAA	0,Y
	 PULA
	 DEX
	 DEY
	 SUBD	#1
	 BNE	mp_copyloop
//* Y has SP of new process
	 LDX	sr2hi		; get slot ptr of new proc
	 INY			; inc to point at last byte copied
	 STY	P_SP,X		; finished setting up proc slot!
	 LDX	srhi		; SP of running proc
//* return process ID
	 DEX			; it's pointing one lower than normal,;'
//*				  just dec once.
	 LDD	process_counter
	 STD	0,X
	 PULY			; pcode PC saved initially
	 RTS



//*****************************************************************
//*
//*	Phaltnotify
//*
//*		halts current process
//*		sets status byte to "halted"
//*		pokes BRN so process really halts.
Phaltnotify:
	 LDAA	#PSTAT_HALTED		; UI halt doesn't wedge system;'
	 BRA	pcode_error_exit

//*****************************************************************
//*
//*	Pkillprocess
//*
//*		pops PID number off of stack
//*		finds the process in question and kills it.
//*
//*		if this is a suicide (current_process == addr of PID),
//*		then pokes the BRN opcode into the schedule so process
//*		doesn't keep executing.
//*
//*		returns 0 if the process was found and killed;
//*			1 if the process could not be located.
//*
Pkillprocess:
	 PSHX		; save SP
	 LDD	0,X	; get PID
	 STD	srhi	; save it for compare
	 LDX	#PROCESS_TABLE
kp_search:
	 LDD	P_ID,X	; get PID into D
	 CPD	srhi
	 BEQ	kp_found
	 LDX	P_NEXT,X	; get next process in table
	 CPX	#PROCESS_TABLE
	 BNE	kp_search
//* process not found
	 PULX
	 LDD	#1
	 STD	0,X	; put return value on stack
	 RTS
kp_found:
//* X has process ptr
	 CPX	current_process
	 BNE	kp_notsuicide
//* suicide:  use mret_die code to die
	 PULX		; pop val off of stack
	 JMP	mret_die
kp_notsuicide:
	 PSHY			; save Y, we're going to trash it;'
	 LDAA	#PSTAT_DEAD
	 STAA	P_STATUS,X
	 LDAA	#$21		; BRN opcode
	 STAA	pcode_branch
	 LDD	P_PREV,X	; get _PREV addr
	 LDY	P_NEXT,X	; Y = _NEXT
	 XGDX			; X = _PREV
	 STX	P_PREV,Y	; NEXT.PREV -> _PREV
	 STY	P_NEXT,X	; PREV.NEXT -> _NEXT
	 PULY			; restore PC
	 PULX			; restore SP
	 LDD	#0
	 STD	0,X		; return value
	 RTS

//*****************************************************************
//*
//*	Pdefer
//*
//*	pokes BRN instruction into pcode runner so that
//*	current process exits immediately
//*
Pdefer
	 LDAA	#$21		; BRN opcode
	 STAA	pcode_branch
	 RTS


//*****************************************************************
//***							      ***
//***		       	   ERRORS			      ***
//***							      ***
//*****************************************************************

Pundefined	EQU	0	; opcode for compiler use

//*
//*	NOTES ABOUT ERROR HANDLING
//*
//* When a run-time error happens, the interpreter "backs up" the machine
//* to the state of things just before the offending pcode instruction
//* was executed (the PC should point at the instruction that caused the
//* error).  It pokes an error condition code into the status byte for the
//* process causing the error, and then RTS's.
//*
//*

errorbounds:
	 DEY		; back up PC
	 LDAA	#PSTAT_ARRAYBOUNDS
	 BRA	pcode_error_exit


//**********************************************************************
//*
//*	pcode_error_exit
//*
//* call with
//*	A = error code,
//*	X = original SP, and
//*	Y = pointing at the instruction that provoked the error.
//*
//* exit by poking the error code into our status and
//* poking BRN, also decrementing our PC to point at the offending
//* instruction
//*
//* also, print message to LCD screen indicating that a
//* runtime error has occured
//*
pcode_error_exit:
	 PSHX				; save SP
	 LDX	current_process		; get ptr to our slot
	 TSTA
	 BNE	pex_errorok
	 LDAA	#PSTAT_UNKNOWN_ERROR
pex_errorok:
	 STAA	P_STATUS,X		; store error code
	 STAA	srlo			; save error code for print msg
	 LDAA	#$21			; BRN opcode
	 STAA	pcode_branch		; poke so that pcode exits immediately

	 LDAA	srlo
	 CMPA	#PSTAT_HALTED
	 BHS	pex_exit

	 LDX	#runtime_error_msg
pex_loop LDAA	0,X
	 BEQ	pex_loopdone
	 JSR	putchar
	 BCS	pex_loop		; if putchar failed, keep trying
	 INX
	 BRA	pex_loop

pex_loopdone
//* some gross code to print srlo as a number from 0 to 99 decimal
	 CLRA
	 LDAB	srlo
pex_deccvt
	 SUBB	#10
	 BLO	pex_a_ok
	 INCA
	 BRA	pex_deccvt
pex_a_ok
	 ADDB	#10		; B is ls. digit, A is m.s.

	 ADDA	#'0		; cvt to ascii;'
	 JSR	putchar
	 BCS	*-3		; loop till printed A

	 TBA
	 ADDA	#'0;'
	 JSR	putchar
	 BCS	*-3

	 LDAA	#CR		; print carriage return
	 JSR	putchar
	 BCS	*-3

pex_exit
	 PULX				; restore SP
	 RTS

runtime_error_msg
	 FCB	0x0a			; CR to clear screen
	 FCC	'RUNTIME ERR '
	 FCB	0

//*****************************************************************
//***							      ***
//***	       	   INTERRUPT ROUTINES			      ***
//***							      ***
//*****************************************************************

//******************************************************************************
//*
//*	IRInt		toggle output on beeper line
//*
//*	TIMER:	    uses TOC1
//*
IRInt:
	 LDX	#$1000		* point to register base
	 LDD	IRtone
	 ADDD	TOC1,X		* add TOC1 to D
	 STD	TOC1,X		* store back
	 BCLR	TFLG1,X %01111111	* clear OC1 for next compare
	 LDAA	PORTA
	 EORA	#%10000000
	 STAA	PORTA		; toggle IR output pin
	 RTI


//* #ifndef NO_BEEPER
//******************************************************************************
//*
//*	BeepInt:	toggle output on beeper line
//*
//*	    TIMER:	uses TOC5
//*
BeepInt
	 LDX	#$1000		* point to register base
	 LDD	beeptone
	 ADDD	TI4O5,X		* add TOC5 to D
	 STD	TI4O5,X		* store back
	 BCLR	TFLG1,X %11110111	* clear OC5 for next compare
	 RTI
//* #endif
//******************************************************************************
//*
//*	SystemInt	1 kHz system interrupt routine
//*
//*	    TIMER:	uses TOC4 for control
//*
//*
//*	System interrupt performs the following tasks:
//*
//*	    0.  sets up for next interrupt
//*	    1.  increment system time
//*	    2.  decrement "process_ticks".  If zero, pokes
//*		BRN (branch never) into pcode_run loop, so that
//*		current process exits.
//*	    3.  deals with LCD print.
//*	    4.  does PWM stuff.
//*	    5.  does shaft encoder stuff.
//*
PRINTBUFFER	EQU	%00000001
IRDECODE	EQU	%00000010
PWM		EQU	%00000100
SHAFTENCODER	EQU	%00001000

SystemInt:
	 LDX	#$1000		* point to register base

#ifdef SBOT
//* setup to read analog 7 (battery level)
	 LDAA	#%00000111
	 STAA	ADCTL,X
#endif

//* setup for next interrupt
	 LDD	#2000		; 2000 cycles = 1 millisec.
	 ADDD	TOC4,X		* add TOC5 to D
	 STD	TOC4,X		* store back
	 BCLR	TFLG1,X %11101111	* clear OC4 for next compare

//* turn on some interrupts
	 LDAA	TMSK1,X
	 PSHA			; save interrupt mask

#ifdef SBOT
	 ANDA	#%10001000	; OC1, OC5 enabled
#endif
#ifdef REV2
	 ANDA	#%10001100	; OC1, TIC1 enabled, OC5 enabled
#endif
#ifdef REV21
	 ANDA	#%10101100	; OC1, IC1, OC5, OC3 enabled
#endif
#ifdef HANDYBOARD
	 ANDA	#%00001000
#endif

	 STAA	TMSK1,X
	 CLI			; locally enable interrupts

//* increment system time
	 LDX	system_time_lo
	 INX
	 STX	system_time_lo
	 BNE	si_noinc_timehi
	 LDX	system_time_hi
	 INX
	 STX	system_time_hi
si_noinc_timehi

#ifndef SBOT_SHUTDOWN_VOLTAGE
#define SBOT_SHUTDOWN_VOLTAGE 130
#endif

#ifdef SBOT
//* check if battery level is > 130
	 LDAA	ADR1
	 CMPA	#SBOT_SHUTDOWN_VOLTAGE
	 BLS	si_boardok
si_boarddie
	 LDX	#$1000

//* sbot shutdown sequence
//* turn on IR reflectance lamp
	 BSET	PORTA,X $20
//* turn off motors
	 LDAA	#MOTORS_OFF
	 STAA	DIGOUTPUT
//* halt processor
	 CLRA
	 TAP
	 STOP

si_boardok EQU *
#endif

//* do pcode process ticks stuff
	 DEC	process_ticks
	 BNE	si_lcdcheckbusy
//* store BRN in pcode_branch
	 LDAA	#$21		; BRN opcode
	 STAA	pcode_branch

si_lcdcheckbusy
#if defined (LCD_ROWS) || defined (LCD_COLS)
//* check if LCD is busy
	 LDAA	#LCDCMD_QUERYBUSY
	 JSR	LCDstart		; high bit of A set if busy
	 ANDA	#$80
	 BEQ	si_checkfrob
	 JMP	si_lcd_done		; come back later

si_checkfrob
//* if (system_time MOD 64 is 0) and (lcd_status= _PRINTCHAR or _PRINTCHARCLS),
//* do for frobby stuff.
	 LDAA	system_time_lo+1	; lowest byte
	 ANDA	#%00111111
	 BNE	si_checkforprint
	 LDAA	lcd_status
	 CMPA	#LCDSTAT_PRINTCHARCLS
	 BHI	si_checkforprint

//* prior to frob, modify froblines based on IR sensing (if
//* IR decoding is enabled)
#ifdef LM576
//* have the weird LCD with status blips on character 15
	 LDAA	system_functions
	 ANDA	#IRDECODE
	 BEQ	si_frobout

	 LDX	#IRdetect0
	 LDAA	IR_DETECT,X
	 CMPA	#IR_DETECT_THRESHOLD	; for LCD
	 BLS	frob_bit2_off
	 LDAA	lcd_frobline2
	 ORAA	#%00000010
	 BRA	IR_storego1
frob_bit2_off
	 LDAA	lcd_frobline2
	 ANDA	#%11111101
IR_storego1
	 STAA	lcd_frobline2

	 LDAA	IR_DETECT+1,X
	 CMPA	#IR_DETECT_THRESHOLD	; for LCD
	 BLS	frob_bit1_off
	 LDAA	lcd_frobline2
	 ORAA	#%00000001
	 BRA	IR_storego2
frob_bit1_off
	 LDAA	lcd_frobline2
	 ANDA	#%11111110
IR_storego2
	 STAA	lcd_frobline2

	 LDAA	IR_DETECT+2,X
	 CMPA	#IR_DETECT_THRESHOLD	; for LCD
	 BLS	frob_bit0_off
	 LDAA	lcd_frobline1
	 ORAA	#%00000001
	 BRA	IR_storeexit
frob_bit0_off
	 LDAA	lcd_frobline1
	 ANDA	#%11111110
IR_storeexit
	 STAA	lcd_frobline1

si_frobout:
	 SEI
	 LDD	#$0040			; set CG RAM address to 0
	 JSR	LCDstart
	 LDAA	#LCDCMD_ASCII
	 LDAB	lcd_frobline1		; frob line 1 data
	 JSR	LCDstart
	 LDAB	lcd_frobline2		; frob line 2 data
	 JSR	LCDstart
	 LDD	#$0045			; set CG RAM address to 5
	 JSR	LCDstart
	 LDAA	lcd_status
	 BEQ	si_lcd_setcursor
	 JMP	si_lcd_done	; if not zero, next char causes CLS anway
si_lcd_setcursor
	 CLRA				; LCD command
	 LDAB	lcd_char_count		; count of chars = cursor pos
	 ORAB	#%10000000		; set DD RAM address to cursor pos
	 JSR	LCDstart
	 CLI
	 JMP	si_lcd_done
#endif

#if defined (LCD_ROWS) && ! defined (LM576)
//* have an LCD, but it's not the weird Rev 2 one
//* swap char 0 and char 7 in DD RAM pos'n 63+LCD_COLS
	 SEI
	 CLRA
	 LDAB	#$80+63+LCD_COLS
	 JSR	LCDstart

	 LDD	scheduler_iterations
	 CMPD	#100
	 BLT	si_leave_frob

	 LDD	#0
	 STD	scheduler_iterations
	 LDAA	lcd_frobline1
	 EORA	#%00001100		; xora I1 and I3
	 STAA	lcd_frobline1
	 		
si_leave_frob
	 LDAA	lcd_frobline1
	 ANDA	#%00001000 
	 BEQ	si_char7
	 LDAB	#0
	 BRA	si_insertchar
si_char7
	 LDAB	#7
si_insertchar
	 LDAA	#LCDCMD_ASCII
	 JSR	LCDstart
	 CLRA				; LCD command
	 LDAB	lcd_char_count		; count of chars = cursor pos
//* chars 8-15 are in DD RAM pos'ns 64-71 (Optrex model 16117A 16x1 screen)
//* if cursor pos is num of cols, advance by (64-# of cols)
	 CMPB	#LCD_COLS
	 BLO	si_ddposok
	 ADDB	#64-LCD_COLS
si_ddposok
	 ORAB	#%10000000		; set DD RAM address to cursor pos
	 JSR	LCDstart
si_nofrob
	 CLI
	 JMP	si_lcd_done
#endif


si_checkforprint
//* check if printing is enabled
	 LDAA	system_functions
	 ANDA	#PRINTBUFFER
	 BNE	si_checkcls		; if zero, skip printing
	 JMP	si_lcd_done

//* check if we are in middle of LCD clear screen.  If so, process
si_checkcls
	 LDAA	lcd_status
	 CMPA	#LCDSTAT_CLS2
	 BHS	si_lcdstat3

//* check if we need to print a character to the LCD
	 LDX	lcd_buffer_pos
	 CPX	print_buffer_end
	 BEQ	si_lcd_done		; nope

//* we must be in _PRINTCHAR or _PRINTCHARCLS mode.
//* increment buffer ptr, and fetch char to be printed in B.
	 INX
	 CPX	#PRINT_BUFFER+PRINT_BUFFER_SIZE
	 BNE	si_lcdbufptrok
	 LDX	#PRINT_BUFFER
si_lcdbufptrok
	 STX	lcd_buffer_pos
	 LDAB	0,X			; get char into B

//* okay, what state is the lcd in?
	 LDAA	lcd_status
	 CMPA	#LCDSTAT_PRINTCHAR
	 BNE	si_lcdstat2
//* print normal char
	 CMPB	#CR			; have CR?
	 BNE	si_lcdputchar
	 LDAA	#LCDSTAT_PRINTCHARCLS	; set LCD status to clear on next char
	 STAA	lcd_status
	 BRA	si_lcd_done
si_lcdputchar
//* check number of chars since last newline.  If > num of chars
//* on screen -1, punt printing it.
	 LDAA	lcd_char_count
	 CMPA	#LCD_ROWS*LCD_COLS-1
	 BEQ	si_lcd_done		; punt
	 INCA
	 STAA	lcd_char_count		; inc counter
#if LCD_ROWS == 2
//* if we're on char 8, advance cursor pos to 64 (Optrex 16117A LCD)
//* if on char LCD_COLS+1, advance
	 CMPA	#1+LCD_COLS
	 BNE	si_lcd_noadv
	 CLRA				; LCD command
	 PSHB				; save character to be printed
	 LDAB	#$C0			; position cursor
	 JSR	LCDstart
	 PULB
si_lcd_noadv	EQU *
#endif
si_translate_and_print
	 CMPB	#$20
	 BLO	si_lcdputcharlcd	; if less than $20, put it
	 CMPB	#$7F
	 BHI	si_lcdputcharlcd	; if > than $7F, put it
	 LDX	#LCD_translation_table
	 SUBB	#$20
	 ABX
	 LDAB	0,X			; translate character
si_lcdputcharlcd
	 LDAA	#LCDCMD_ASCII		; control for normal char
	 JSR	LCDstart
	 BRA	si_lcd_done

si_lcdstat2
	 CMPA	#LCDSTAT_PRINTCHARCLS
	 BNE	si_lcdstat3
//* want to initiate clear screen sequence when we get a character
//* from this state.  save char to be printed in "lcd_savedchar".
//* it will get printed when _FINISHCLS state is reached.
	 STAB	lcd_savedchar		; save real char
	 LDD	LCD_clsops		; home and clear screen code
	 JSR	LCDstart
	 LDAA	#LCDSTAT_CLS2		; setup next operation
	 STAA	lcd_status
	 BRA	si_lcd_done

si_lcdstat3
	 CMPA	#LCDSTAT_FINISHCLS	; last state
	 BEQ	si_lcdfinishcls
//* pull lcd cmd from list
	 LDX	#LCD_clsops		; point to table
	 LDAB	lcd_status		; get status
	 ABX				; add to ptr
	 LDD	0,X			; get op'n;'
	 JSR	LCDstart
	 LDAA	lcd_status
	 ADDA	#2
	 STAA	lcd_status		; increment status by 2
	 BRA	si_lcd_done

si_lcdfinishcls
//* the clear screen is done.  now we print the char saved.
	 LDAA	#LCDSTAT_PRINTCHAR
	 STAA	lcd_status
	 LDAA	#1
	 STAA	lcd_char_count		; set # of chars/line printed
	 LDAB	lcd_savedchar
//* if = 10, then it's a CR.  ignore.
	 CMPB	#CR
	 BNE	si_translate_and_print
si_lcd_done: EQU	*
#endif


#ifdef BOOKBOT
//* PWM code for bookbot ***********************************
//*
//* motor 0 is enabled by bit 4 of PORTA;
//* motor 1 is enabled by bit 5 of PORTA.
//*
//* use "speeda" and "speedb" bytes as masks for enables.

//* check if pulse width modulation is enabled
	 LDAA	system_functions
	 ANDA	#PWM
	 BEQ	SPDexit		; if zero, exit w/o modulation

	 LDX	#$1000

        LDAA	speeda          ; rotate bits in motor speed
	 ASLA
	 BCC	SPDOnA
	 BSET	PORTA,X #%00010000
	 ADDA	#1
	 BRA	SPDsetA
SPDOnA:	BCLR	PORTA,X #%00010000
SPDsetA	STAA	speeda

        LDAA	speedb          ; rotate bits in motor speed
	 ASLA
	 BCC	SPDOnB
	 BSET	PORTA,X #%00100000
	 ADDA	#1
	 BRA	SPDsetB
SPDOnB:	BCLR	PORTA,X #%00100000
SPDsetB	STAA	speedb

SPDexit EQU	*

//*************************** end of bookbot PWM code

#else
#if defined (SBOT) | defined (REV21) | defined (HANDYBOARD)
//* PWM code for sensor robot and Rev 2.1 Board  *****************8
//*
//* motor direction selected by low nybble of DIGOUTPUT
//* motors enabled by high nybble of DIGOUTPUT
//* The original code would have set all the motor on bits
//* to begin with, EOR'd the corresponding bits for any
//* pulses found, to set them to 0, then Inverted the
//* motor on bits at the end.
//*
//* The Smooth-PWM algorithm (C)Skids 1997 is based on the 
//* idea of setting the pulses for some fraction (x/256) of the time.
//* So, the total number of pulses after t ticks will have been
//*                     Pulses(t) = INT(tx/256),
//* which is equivalent to:
//*                     Pulses(t) = (tx- tx MOD 256)/256.
//* tx MOD 256 is equivalent to the lower byte of tx, and (tx-tx MOD 256)/256
//* is the upper byte. We output a new pulse whenever Pulses(t)-Pulses(t-1)
//* changes, i.e. whenever the upper byte changes. The easiest way to
//* calculate tx is to add x on every new time interval and the upper
//* byte will have changed (there will be a pulse) whenever there
//* is a carry out from the lower byte of the addition.
//*    gSpeedFrac[i] contains tx[i] MOD 256, (the lower byte).
//* speed[i] contains x[i].


//* check if pulse width modulation is enabled
	 LDAB	motor
	 LDAA	system_functions
	 ANDA	#PWM
	 BEQ	SPDexit		; if zero, exit w/o modulation
    ;dcbaDCBA is the output. All motor off bits should be 0.
	  LDAB #255
	  LDAA speeda
	  ADDA gSpeedFracA ;Calc new tx MOD 256 into A
	  STAA gSpeedFracA ;and the pulse into carry flag.
	  RORB             ;Just rotate each pulse into bit 7.
	  LDAA speedb
	  ADDA gSpeedFracB
	  STAA gSpeedFracB
	  RORB             ;they eventually get rotated into
	  LDAA speedc
	  ADDA gSpeedFracC
	  STAA gSpeedFracC
	  RORB             ;their correct positions.
	  LDAA speedd
	  ADDA gSpeedFracD
	  STAA gSpeedFracD
	  RORB             ;[Motor3][Motor2][Motor1][Motor0]....
	  ANDB motor ;All motor off bits should be 0s

SPDexit STAB	DIGOUTPUT

//*************************** end of sensor robot PWM code
#else

//***************************************************
//* SpeedInt: does motor speed control
//*	uses TOC2 interrupt running at 1kHz rate (RTI is too slow)

//* code for Rev 2 board
//*
//* Randy's code -- speed byte contains bits to be rotated through
//*		  motor
//*
//* begin speed control stuff
//* check for all motors being off and exit early
	 LDAB	motor
	 BEQ	SPDexitnofetch	; if off, poke it and exit
//* check if pulse width modulation is enabled
	 LDAA	system_functions
	 ANDA	#PWM
	 BEQ	SPDexitnofetch	; if zero, exit w/o modulation

#ifdef (SMOOTHPWM)
//* In this hardware, the motor bits are paired,
//* and both bits in the pair will be set to 1
//* if the motor should be on, otherwise they
//* will be set to ~motor. Code space is approx 11b*4+3 for Randy's.
//* This one will be 10b*4+3
	  LDAA speeda
	  ADDA gSpeedFracA
	  STAA gSpeedFracA
	  BCC NoAPulse:
	  ANDB #%11111100
NoAPulse:
	  LDAA speedb
	  ADDA gSpeedFracB
	  STAA gSpeedFracB
	  BCC NoBPulse:
	  ANDB #%11110011
NoBPulse:
	  LDAA speedc
	  ADDA gSpeedFracC
	  STAA gSpeedFracC
	  BCC NoCPulse:
	  ANDB #%11001111
NoCPulse:
	  LDAA speedd
	  ADDA gSpeedFracD
	  STAA gSpeedFracD
	  BCC NoDPulse:
	  ANDB #%00111111
NoDPulse:
SPDexitnofetch:
	  COMB
	  STAB DIGOUTPUT
#else
        LDAA	speeda          ; rotate bits in motor speed
	 ASLA
	 BCC	SPDOnA
	 ANDB	#%11111100
SPDOnA:	ADCA	#0
	 STAA	speeda

        LDAA	speedb          ; rotate bits in motor speed
	 ASLA
	 BCC	SPDOnB
	 ANDB	#%11110011
SPDOnB:	ADCA	#0
	 STAA	speedb

        LDAA	speedc          ; rotate bits in motor speed
	 ASLA
	 BCC	SPDOnC
	 ANDB	#%11001111
SPDOnC:	ADCA	#0
	 STAA	speedc

        LDAA	speedd          ; rotate bits in motor speed
	 ASLA
	 BCC	SPDOnD
	 ANDB	#%00111111
SPDOnD:	ADCA	#0
	 STAA	speedd

SPDexitnofetch:
	 COMB
	 STAB	DIGOUTPUT
#endif

#endif
#endif

#ifdef SBOT
//* check if IR decoding is enabled
	 LDAA	system_functions
	 ANDA	#IRDECODE
	 BEQ	IR_exit

//* do IR decoding
//*
IR_setup:
	 LDY	#IR_jumptable125
	 LDAA	EXPANSION_INPUTS
	 ANDA	#$10		; switch 6 of expansion board
	 BEQ	IR_go
	 LDY	#IR_jumptable100

//****************
IR_go:
	 LDAA	PORTD
	 ANDA	#IR_SENSOR0
	 LDX	#IRdetect0
	 PSHY
	 JSR	IR_doitbaby
	 PULY

	 LDAA	PORTD
	 ANDA	#IR_SENSOR1
	 INX			; LDX	#IRdetect1
	 PSHY
	 JSR	IR_doitbaby
	 PULY

	 LDAA	PORTD
	 ANDA	#IR_SENSOR2
	 INX			; LDX	#IRdetect2
	 PSHY
	 JSR	IR_doitbaby
	 PULY
IR_exit:

//* check if shaft encoding is enabled
	 LDAA	system_functions
	 ANDA	#SHAFTENCODER
	 BEQ	shaft_exit

//* do shaft encoder stuff
	 LDX	#scounter0
	 LDAB	EXPANSION_INPUTS
	 ANDB	#$03			; mask two low bits for enc. 0
	 JSR	encoder_doit

	 LDX	#scounter1
	 LDAB	EXPANSION_INPUTS
	 ANDB	#%00001100
	 LSRB
	 LSRB
	 JSR	encoder_doit

shaft_exit
#endif
//* re-enable interrupts
	 PULA
	 STAA	TMSK1

	 RTI			; old CCR will be popped with interrupts
//*				  globally enabled


//* LCD clear screen operations
LCD_clsops
#if LCD_ROWS == 1
	 FDB	$0001			; home and clear screen
	 FDB	$0006			; set cursor increment, no shift
	 FCB	$00
	 FCB	$7F+LCD_COLS		; set cursor to frob char loc'n
	 FDB	$0200			; print frobby char
	 FDB	$0002			; return cursor home
#endif
#if LCD_ROWS == 2
	 FDB	$0001			; home and clear screen
	 FDB	$0006			; set cursor increment, no shift
	 FCB	$00
	 FCB	$BF+LCD_COLS		; set cursor to frob char loc'n;'
	 FDB	$0200			; print frobby char
	 FDB	$0002			; return cursor home
#endif
//*
speedtable
	 FCB	%00000000	; speed 0
	 FCB	%00010001
	 FCB	%01001001
	 FCB	%01010101
	 FCB	%01010111
	 FCB	%01110111
	 FCB	%01111111
	 FCB	%11111111	; speed 7


//* translates from ASCII 32 to ASCII 127
LCD_translation_table
#ifdef LM576
//* for weird 15-char display
	 FCB	 ' , '!, '", '#, '$, '%, '&, ''
	 FCB	 '(, '), '*, '+,  2, '-, '., '/
	 FCB	 '0, '1, '2, '3, '4, '5, '6,  7
	 FCB	 '8, '9,  4,  5, '<, '=, '>, '?
	 FCB	 '@, 'A, 'B, 'C, 'D, 'E, 'F, 'G
	 FCB	 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O
	 FCB	 'P, 'Q, 'R, 'S, 'T, 'U, 'V, 'W
	 FCB	 'X, 'Y, 'Z, '[, '\, '], '^, '_
	 FCB	 '`, 'a, 'b, 'c, 'd, 'e, 'f,  3
	 FCB	 'h, 'i,  6, 'k, 'l, 'm, 'n, 'o
	 FCB	0xe0, 0xe1, 'r, 's, 't, 'u, 'v, 'w
	 FCB	 'x,  1, 'z, '{, '|, '}, '~, 0x7F
#endif

#if defined (LCD_ROWS) && ! defined (LM576)
//* for normal displays
	 FCB	 ' , '!, '", '#, '$, '%, '&, '';'
	 FCB	 '(, '), '*, '+,  2, '-, '., '/;'
	 FCB	 '0, '1, '2, '3, '4, '5, '6, '7
	 FCB	 '8, '9,  4,  5, '<, '=, '>, '?
	 FCB	 '@, 'A, 'B, 'C, 'D, 'E, 'F, 'G
	 FCB	 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O
	 FCB	 'P, 'Q, 'R, 'S, 'T, 'U, 'V, 'W
	 FCB	 'X, 'Y, 'Z, '[, '\, '], '^, '_
	 FCB	 '`, 'a, 'b, 'c, 'd, 'e, 'f,  3;'
	 FCB	 'h, 'i,  6, 'k, 'l, 'm, 'n, 'o;'
	 FCB	0xf0, 0xf1, 'r, 's, 't, 'u, 'v, 'w
	 FCB	 'x,  1, 'z, '{, '|, '}, '~, 0x7F
#endif


#ifdef SBOT
//*
//*
//* IR phase locked loop code
//*
//* algorithm:  compare incoming waveform to a "waveform" generated
//* internally.  sum the # of errors (when the two waves don't match)
//* over the course of one waveform.  If the error count is large,
//* then we have not "found" the desired frequency.  If the error count
//* is small, then we have.
//*
//* at rising edge of internal wave, make phase corrections by jumping
//* ahead one notch or waiting one notch.
//*
//* incoming wave:
//*
//*  +           +-- -- -- --+           +-- -- -- --+
//*  |           |           |           |           |
//*  +-- -- -- --+           +-- -- -- --+           +--
//*
//* internal wave: (for 125 Hz detection)
//*
//*            +-- -- -- --+           +-- -- -- --+
//*            |           |           |           |
//* -- -- -- --+           +-- -- -- --+           +--
//*  0  1  2  3  4  6  7  8  0  1  2  3
//*              5 (state 4 after wait)
//*
//* at state 3, if incoming wave is high, we are behind it.
//*             jump to state 6 (catch up).
//* at state 4, if incoming wave is low, we are ahead of it.
//*	      go to state 5 (wait for it to catch up).
//* all other states:  proceed to next state.
//* all states:  accumulate error if internal wave != incoming wave.
//*
//* internal wave for 100 Hz detection
//*
//*               +-- -- -- -- --+
//*               |              |
//* -- -- -- -- --+              +-- -- -- -- --
//*  0  1  2  3  4  5  7  8  9 10
//*                 6 (state 5 after wait)
//*
//*
//* register usage:
//*   A==0 if sensor was 0, not zero otherwise
//*   X has ptr to table of IRdetect, IRphase, and IRerror
//*   Y has ptr to jumptable for either 100Hz or 125Hz detection
//*      (destroyed on exit)
//*
IR_SENSOR0	EQU	%00100000	; port D bit 5
IR_SENSOR1	EQU	%00010000	;        bit 4
IR_SENSOR2	EQU	%00001000	;        bit 3

IR_DETECT	EQU	0
IR_PHASE	EQU	3
IR_ERROR	EQU	6

IR_DETECT_LIMIT	EQU	2	; max 2 errors per cycle for detect
IR_DETECT_THRESHOLD EQU 5	; # of detects for LCD status indicator

IR_doitbaby:
//* check if phase is > 10; if so, reset to zero
	 LDAB	IR_PHASE,X
	 CMPB	#10
	 BLS	IR_phaseok
	 CLRB
	 STAB	IR_PHASE,X
IR_phaseok:
//* multiply phase by 2 and calc jump
	 ASLB
	 ABY
	 LDY	0,Y		; fetch jump
	 JMP	0,Y

IR_jumptable100
	 FDB	IR_start_cycle	; phase 0
	 FDB	IR_low_phase	; phase 1
	 FDB	IR_low_phase	; phase 2
	 FDB	IR_low_phase	; phase 3
	 FDB	IR_pre_edge	; phase 4
	 FDB	IR_post_edge	; phase 5
	 FDB	IR_high_phase	; phase 6
	 FDB	IR_high_phase	; phase 7
	 FDB	IR_high_phase	; phase 8
	 FDB	IR_high_phase	; phase 9
	 FDB	IR_end_cycle	; phase 10

IR_jumptable125
	 FDB	IR_start_cycle	; phase 0
	 FDB	IR_low_phase	; phase 1
	 FDB	IR_low_phase	; phase 2
	 FDB	IR_pre_edge	; phase 3
	 FDB	IR_post_edge	; phase 4
	 FDB	IR_high_phase	; phase 5
	 FDB	IR_high_phase	; phase 6
	 FDB	IR_high_phase	; phase 7
	 FDB	IR_end_cycle	; phase 8
//* the following should get called only when user switches from 125hz to
//* 100hz detection on the fly
	 FDB	IR_start_cycle	; phase 0
	 FDB	IR_start_cycle	; phase 0


//* start cycle	calc IRdetect based on previous error; set new error
//*              	based on current sensor val.
IR_start_cycle:
	 LDAB	IR_ERROR,X
	 CMPB	#IR_DETECT_LIMIT
	 BLS	IR_detect_true
//* no detect
	 CLR	IR_DETECT,X
	 BRA	IR_check_phase0
IR_detect_true
//* increment byte until it reaches 0xFF
	 LDAB	IR_DETECT,X
	 INCB
	 BNE	IR_detect_ok
	 DECB
IR_detect_ok
	 STAB	IR_DETECT,X
IR_check_phase0
	 TSTA
	 BEQ	IR_phase0_noerr
//* error
	 LDAB	#1
	 STAB	IR_ERROR,X
	 BRA	IR_setphase1
IR_phase0_noerr
	 CLR	IR_ERROR,X
	 LDAB	#1
IR_setphase1
	 STAB	IR_PHASE,X
	 RTS

//* IR_low_phase	increment error count if incoming wave is high
//*               increment phase
IR_low_phase:
	 INC	IR_PHASE,X
	 TSTA
	 BNE	IR_incerrorreturn
	 RTS
IR_incerrorreturn
	 INC	IR_ERROR,X
	 RTS

//* IR_pre_edge	if wave is high, inc error and goto phase n+3;
//*		otherwise, inc phase & return
IR_pre_edge:
	 TSTA
	 BNE	IR_catchup
//* no prob; inc phase & return
IR_incphasereturn
	 INC	IR_PHASE,X
	 RTS
//* inc error, goto phase n+3
IR_catchup:
	 INC	IR_ERROR,X
	 LDAB	IR_PHASE,X
	 ADDB	#3
	 STAB	IR_PHASE,X
	 RTS

//* IR_post_edge	if wave is low; inc error and goto phase n+1;
//*		otherwise, proceed normally to phase n+2 and return
IR_post_edge:
	 TSTA
	 BEQ	IR_wait
//* normal exit: phase += 2
	 LDAB	IR_PHASE,X
	 ADDB	#2
	 STAB	IR_PHASE,X
	 RTS
//* inc error, inc phase
IR_wait
	 INC	IR_ERROR,X
	 INC	IR_PHASE,X
	 RTS

//* IR_high_phase	inc phase
//*               if wave is high; return; otherwise inc error & return
IR_high_phase:
	 INC	IR_PHASE,X
	 TSTA
	 BEQ	IR_incerrorreturn
	 RTS

//* IR_end_cycle	set phase to zero
//*		if wave is low incr. error
IR_end_cycle:
	 CLR	IR_PHASE,X
	 TSTA
	 BEQ	IR_incerrorreturn
	 RTS


//*
//*
//* encoder:  there are four possible states;
//*           between consecutive readings, encoder is allowed
//*           only transitions between adjacent states
//*
//*
//* Signal 0	       +-- -- -- --+	       +-- -- -- --
//*	  	       |	   |	       |
//*	    -- -- -- --+	   +-- -- -- --+
//*
//* Signal 1  -- --+           +-- -- -- --+	     +-- --
//*                |           |		 |	     |
//*      	       	 +-- -- -- --+		 +-- -- -- --+
//* sig0:      0  0  0  0  1  1  1  1  0  0  0  0  1  1  1  1
//* sig1:      1  1  0  0  0  0  1  1  1  1  0  0  0  0  1  1
//*
//*
//*                 COUNTER TRANSITION TABLE
//*
//*                      prev state
//*                  00    01    10    11
//*
//*   new      00   NOP   DOWN   UP   ERR
//*  state
//*            01    UP   NOP   ERR   DOWN
//*
//*            10   DOWN  ERR   NOP    UP
//*
//*            11   ERR    UP   DOWN  NOP
//*
//*
//* register usage:
//*    B = data from sensor (shifted into bits 0 and 1)
//*    X = encoder data in page 0
//*    Y = destroyed
//*
SE_COUNTS	EQU	0
SE_STATE	EQU	2

encoder_doit:
//* form jumptable byte based on prev. state in low two bits
	 TBA			; save new state in A
	 ASLB
	 ASLB			; shift new state up by 2
	 ORAB	SE_STATE,X	; OR in old state
	 STAA	SE_STATE,X	; store new state
	 ASLB			; shift again for jumptable
	 LDY	#encoder_jumptable
	 ABY
	 LDY	0,Y
	 JMP	0,Y

encoder_up:
	 LDD	SE_COUNTS,X
	 ADDD	#1
	 STD	SE_COUNTS,X
encoder_nop:
encoder_error:
	 RTS

encoder_down:
	 LDD	SE_COUNTS,X
	 SUBD	#1
	 STD	SE_COUNTS,X
	 RTS

encoder_jumptable:
	 FDB	encoder_nop
	 FDB	encoder_down
	 FDB	encoder_up
	 FDB	encoder_error
	 FDB	encoder_up
	 FDB	encoder_nop
	 FDB	encoder_error
	 FDB	encoder_down
	 FDB	encoder_down
	 FDB	encoder_error
	 FDB	encoder_nop
	 FDB	encoder_up
	 FDB	encoder_error
	 FDB	encoder_up
	 FDB	encoder_down
	 FDB	encoder_nop
#endif
//*****************************************************************
//*
//*	StopInt		power down STOP instruction
//*
//*			performs checksum of memory, then check
//*			that power is really gone
//*
//*			if power is back, require that it stay
//*			on for .05 sec before believing that it
//*			is truly back
//*
//*			also prints "*halted*" on LCD and
//*			turns off wired-or mode for TxD
//*
StopInt:
#ifdef DEBUG
	 BRA	boarddiesnomsg
#endif
//* delay a little while, hoping for board power to return
#ifdef SBOT
	 BRA	boarddiesnomsg
#endif
	 LDY	#1000
si_waitloop
	 DEY
	 BNE	si_waitloop

//* test if power has returned; no = die
	 LDX	#$1000
	 BRCLR	PORTA,X %00000100 boarddiesnomsg

//* require that power stay on for 50 msec before returning
	 LDD	#7142			; 100,000 clocks = 0.05 sec
si_loop2
	 BRCLR	PORTA,X %00000100 boarddiesnomsg ; [7 cycles]
	 SUBD	#1				; [4 cycles]
	 BNE	si_loop2			; [3 cycles]
	 BRA	boardlives

boarddies:
//* save D reg (has checksum)
	 PSHB
	 PSHA

//* keep LCD routine from generating more interrupts
	 BSR	LCD_noint

//* home LCD cursor
	 LDD	#$0002
	 JSR	LCDstart

//* print "*Halted*" on LCD
	 LDX	#haltmsg

	 LDAA	#2
si_loop
	 LDAB	0,X
	 BEQ	si_loopdone
	 JSR	LCDstart
	 INX
	 BRA	si_loop

si_loopdone
	 PULA
	 JSR	Byte2Hex2LCD
	 PULA
	 JSR	Byte2Hex2LCD

	 LDAA	#2
	 LDAB	#')'
	 JSR	LCDstart

boarddiesnomsg:
//* turn on wired-or mode (stop driving TxD LED)
	 LDX	#$1000
        BSET	SPCR,X #PORTD_WOM

#ifdef SBOT
//* turn on IR reflectance lamp
	 BSET	PORTA,X $20
#endif

//* setup for STOP on IC1 interrupt
	 CLRA
	 TAP
	 STOP

haltmsg
	 FCC	'*Halted* ('
	 FCB	0

//*******************************************************************
//* Y contains stack pointer
boardlives

	 BSR	LCD_noint

//* notify user that power failure has occured
	 LDX	#powerlostmessage
	 JSR	printstringLCD

	 LDX	#$1000
	 BCLR	TFLG1,X %11111011	; clear interrupt flag

	 BSR	LCD_yesint

	 RTI

powerlostmessage
	 FCC	'-POWER GLITCH- '
	 FCB	0

//****************************************
LCD_noint
	 LDAA	#$01		; NOP
	 STAA	LCDdriverCLI-LCDdriver+LCDstart
	 RTS

LCD_yesint
	 LDAA	#$0E		; CLI
	 STAA	LCDdriverCLI-LCDdriver+LCDstart
	 RTS

//************************************************************
//* performs checksum of memory
//* returns result in D
//* call with SP in zero page
//*
//* stop at location THE_ZERO_ARRAY (checksum word is stored there)
memorysum
	 LDX	#$8000
	 CLRA
	 CLRB
memsumloop
	 ADDD	0,X

	 XGDX
	 ADDD	#2
	 XGDX

	 CPX	#THE_ZERO_ARRAY
	 BNE	memsumloop

	 RTS

//* bad interrupt?  return!
BadInt:	RTI

#define RESERVED_INT       BadInt    ;$FFC0-FFD4: Reserved
#define SCI_INT            BadInt    ;$FFD6: SCI Serial System

#define SPI_INT            BadInt    ;$FFD8: SPI Serial Transfer Complete
#define PULSE_EDGE_INT     BadInt    ;$FFDA: Pulse Accumulator Input Edge
#define PULSE_OVERFLOW_INT BadInt    ;$FFDC: Pulse Accumulator Overflow
#define TIMER_OVERFLOW_INT BadInt    ;$FFDE: Timer Overflow

//* #ifndef NO_BEEPER

#define TIC4_TOC5_INT      BeepInt   ;$FFE0: Timer Input Capture 4/Output Compare 5 (TI4O5)

/* #else
//* #define TIC4_TOC5_INT      BadInt    ;$FFE0: Timer Input Capture 4/Output Compare 5 (TI4O5)
//* #endif */

#define TOC4_INT           SystemInt ;$FFE2: Timer Output Compare 4 (TOC4)
#define TOC3_INT           BadInt    ;$FFE4: Timer Output Compare 3 (TOC3)
#define TOC2_INT           BadInt    ;$FFE6: Timer Output Compare 2 (TOC2)

#ifdef SBOT
#define TOC1_INT           IRInt     ;$FFE8: Timer Output Compare 1 (TOC1)
#else
#define TOC1_INT	   BadInt    ;$FFE8: Timer Output Compare 1 (TOC1)
#endif

#define TIC3_INT           BadInt    ;$FFEA: Timer Input Capture 3 (TIC3)
#define TIC2_INT           BadInt    ;$FFEC: Timer Input Capture 2 (TIC2)
#define TIC1_INT           StopInt   ;$FFEE: Timer Input Capture 1 (TIC1)

#define RTI_INT            BadInt    ;$FFF0: Real Time Interrupt (RTI)
#define IRQ_INT            BadInt    ;$FFF2: (External Pin or Parallel I/O) (IRQ)
#define XIRQ_INT           BadInt    ;$FFF4: (Pseudo Non-Maskable Interrupt) (XIRQ)
#define SWI_INT            BadInt    ;$FFF6: Software Interrupt (SWI)

#define ILLEGAL_OP_INT     BadInt    ;$FFF8: Illegal Opcode Trap ()
#define COP_INT            BadInt    ;$FFFA: COP Failure (Reset) ()
#define CLOCK_MON_INT      BadInt    ;$FFFC: COP Clock Monitor Fail (Reset) ()
#define RESET_INT          startup   ;$FFFE: /RESET


//******************************************************************************
//*
//* DO NOT CHANGE THIS!! Instead, change the above defines.
//*
//*
	 ORG	$BFC0
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT

	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT

	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	SCI_INT

	 FDB	SPI_INT
	 FDB	PULSE_EDGE_INT
	 FDB	PULSE_OVERFLOW_INT
	 FDB	TIMER_OVERFLOW_INT

	 FDB	TIC4_TOC5_INT
	 FDB	TOC4_INT
	 FDB	TOC3_INT
	 FDB	TOC2_INT

	 FDB	TOC1_INT
	 FDB	TIC3_INT
	 FDB	TIC2_INT
	 FDB	TIC1_INT

	 FDB	RTI_INT
	 FDB	IRQ_INT
	 FDB	XIRQ_INT
	 FDB	SWI_INT

	 FDB	ILLEGAL_OP_INT
	 FDB	COP_INT
	 FDB	CLOCK_MON_INT
	 FDB	RESET_INT

//*
//* DO NOT CHANGE THIS!!
//*
	 ORG	$FFC0
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT

	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT

	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	RESERVED_INT
	 FDB	SCI_INT

	 FDB	SPI_INT
	 FDB	PULSE_EDGE_INT
	 FDB	PULSE_OVERFLOW_INT
	 FDB	TIMER_OVERFLOW_INT

	 FDB	TIC4_TOC5_INT
	 FDB	TOC4_INT
	 FDB	TOC3_INT
	 FDB	TOC2_INT

	 FDB	TOC1_INT
	 FDB	TIC3_INT
	 FDB	TIC2_INT
	 FDB	TIC1_INT

	 FDB	RTI_INT
	 FDB	IRQ_INT
	 FDB	XIRQ_INT
	 FDB	SWI_INT

	 FDB	ILLEGAL_OP_INT
	 FDB	COP_INT
	 FDB	CLOCK_MON_INT
	 FDB	RESET_INT

//*
//* DO NOT CHANGE THIS!!
//*
//******************************************************************************


	 ORG	THE_ZERO_ARRAY
	 FCB	0
	 FCB	0

	 ORG	VERSION_NUMBER

//* current version is 2.81, eh?
	 FCB	2
	 FCB	81

