#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
*
*
* 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

* 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

#include "../motorola/floating_point/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_in.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.81 9/28/93'
	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 "../motorola/floating_point/math11routines.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
#ifndef (BOOKBOT) | (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
*

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

        LDAA	speeda          ; rotate bits in motor speed
	ASLA
	BCC	SPDoffA
	ADDA	#1
	EORB	#%00010000
	BRA	SPDsetA
SPDoffA	ORAB	#%00010000
SPDsetA	STAA	speeda

        LDAA	speedb          ; rotate bits in motor speed
	ASLA
	BCC	SPDoffB
	ADDA	#1
	EORB	#%00100000
	BRA	SPDsetB
SPDoffB	ORAB	#%00100000
SPDsetB	STAA	speedb

        LDAA	speedc          ; rotate bits in motor speed
	ASLA
	BCC	SPDoffC
	ADDA	#1
	EORB	#%01000000
	BRA	SPDsetC
SPDoffC	ORAB	#%01000000
SPDsetC	STAA	speedc

        LDAA	speedd          ; rotate bits in motor speed
	ASLA
	BCC	SPDoffD
	ADDA	#1
	EORB	#%10000000
	BRA	SPDsetD
SPDoffD	ORAB	#%10000000
SPDsetD	STAA	speedd

	EORB	#$F0

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

        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

#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

