	TITLE	MICROCEPHALIC DISPLAY TERMINAL SOFTWARE
	NAME	GRAPHBOX
*	EDITED 01/13/78 1900 HOURS
*	MICROCEPHALIC DISPLAY TERMINAL SOFTWARE
*	COPYRIGHT (C) 1977 BY IRA D. BAXTER
*	17914 S. LAURELBROOK PLACE
*	CERRITOS, CALIF. 90701
*	ALL RIGHTS RESERVED
*
*	NEXT PASS: MAKE SCREEN WINDOW ON 65536 X 65536 SPACE
*		INSTALL DRAWCIRCLE, 2 STOP BITS ON 150 BAUD AND LOWER
*		MAKE (X,A) ENTRYPOINTS INTO (X, (0,B)) ENTRY POINTS
*		MODIFY SPEAKER TO BE CB2 BIT INSTEAD OF MSB OF DATA!
*		CURSOR BLINK STEPS ON TOP LINE OF SCREEN!
*		CROSSHAIRS STILL MOVE JERKY
*		USE EXTRA FN KEYS TO IMPLEMENT ODDBALL FNS
*		BREAK XMIT ?
*		AFTER SCROLL, CHAR TOP LINE = LINE 0 ?
*		'OR' MODE WORKS TERRIBLE!
*		ADD AUTOBOOT BFEATURE
*		WHY TO CROSSHAIRS DISAPPEAR AT 0,0 ?
*		ADD "RESET ASSOCITAION TABLE" COMMAND
*

*
*
*	DEFINITIONS
*
VERSION	EQU	$01	LEFT DIGIT = MAJOR REV, RIGHT DIGIT = MINOR REV
*
CHARHEIGHT	EQU	9	# ROWS IN A CHARACTER
CHARWIDTH	EQU	6	# COLUMNS IN A CHARACTER POSITION
SCREENHEIGHT	EQU	256	NUMBER OF ROWS ON THE SCREEN
SCREENWIDTH	EQU	512	NUMBER OF DOTS ACROSS THE SCREEN
CHARLEFTSIDE	EQU	1	CURSOR X AFTER NEWPAGE
CHARTOPSIDE	EQU	SCREENHEIGHT-2	CURSOR Y AFTER NEWPAGE
NBROW		EQU	SCREENWIDTH/8	NUMBER OF BYTES PER ROW
NROWS		EQU	SCREENHEIGHT	NUMBER OF ROWS DOWN SCREEN
SCREENBOTTOM	EQU	$8000	LOWEST ADDRESS DISPLAY BYTE
SCREENTOP	EQU	$8000+16383	HIGHEST ADDRESS DISPLAY BYTE
SCREENDOWNFUDGE	EQU	255	DOWN SHIFT REQ'D TO PUT LOC $8000 ON LOWEST LINE OF SCREEN
SCREENLEFTFUDGE	EQU	$1D8	LEFT SHIFT REQ'D TO PUT LOC $8000 AS LEFTMOST BYTE OF SCREEN

ROM	EQU	$E000	PROGRAM ROM AREA

GRAPHRAM	EQU	$FC00	STORAGE FOR GRAPHBOX VARIABLES

IOBASE	EQU	$FF00	BASE OF I/O DEVICE AREA

*	PIA AND ACIA INTERFACE DEFINITIONS

DDRACCESS	EQU	%00000000	TURN OFF THIS BIT TO ACCESS DATA DIRECTION REGISTER
DATAACCESS	EQU	%00000100	TURN DDRACCESS BIT ON TO GET TO DATA REGISTER

DOWNSHIFTPIA	EQU	$FFE9	PIA CONTROL WORD FOR DOWNSHIFTDATA
DOWNSHIFTDATA	EQU	DOWNSHIFTPIA-1	PIA REGISTER TO TELL HARDWARE HOW FAR TO DOWNSHIIFT
DOWNSHIFTDDR	EQU	%11111111	DOWN SHIFT DATA DIRECTION IS ALL OUTPUT

NMIACK	EQU	DOWNSHIFTDATA	READING THIS CLEARS NMI INTERRUPT
NMISENSE	EQU	DOWNSHIFTPIA	MSB INDICATES NMI PENDING OR ACTIVE
NMICA1	EQU	%00000011	THIS FORCES CA1 TO BE INTERRUPT INPUT ON REFRESH RISE
BGDCA2	EQU	%00110000	THIS CAUSES CA2 TO BE BACKGROUND OUTPUT
BLACKBGD	EQU	%00001000	THIS PATTERN IN DOWNSHIFTPIA MAKES BLACK BACKGROUND
WHITEBGD	EQU	%00000000	THIS PATTERN IN DOWNSHIFTPIA MAKES WHITE BACKGROUND
VIDEOINVERT	EQU	%00001000	TOGGLE THIS BIT TO INVERT VIDEO IN DOWNSHIFTPIA
DOWNSHIFTCW	EQU	BGDCA2+DATAACCESS+NMICA1	CONTROL WORD FOR DOWNSHIFTPIA

LEFTSHIFTPIA	EQU	DOWNSHIFTPIA+2	PIA CONTROL WORD FOR SCREEN LEFT SHIFT DISTANCE
LEFTSHIFTMSB	EQU	LEFTSHIFTPIA	MSB OF LEFT SHIFT STORED HERE
LEFTSHIFTDATA	EQU	LEFTSHIFTPIA-1	LEFT SHIFT DISTANCE MOD 256
LEFTSHIFTDDR	EQU	%11111111	LEFT SHIFT DATA DIRECTION IS ALL OUTPUT
LEFTSHIFTCB1	EQU	%00000000	KILLS CB1 SO IT CAN'T AFFECT ANYTHING
LEFTSHIFTCB2	EQU	%00110000	MAKES CB2 INTO AN OUTPUT BIT (FOR LEFTSHIFT MSB)
LEFTSHIFTCW	EQU	LEFTSHIFTCB2+DATAACCESS+LEFTSHIFTCB1	CONTROL WORD FOR LEFTSHIFTPIA

KEYPIA	EQU	$FFE5	KEYBOARD PIA CONTROL WORD
KEYDATA	EQU	KEYPIA-1	KEYBOARD DATA WORD
SPEAKER	EQU	KEYDATA		MSB OF KEYDATA IS DIRECT I/O TO SPEAKER
KEYDDR	EQU	%10000000	DATA DIRECTION: MSB IS OUTPUT TO SPEAKER
*				LEAST SIGNIFICANT 7 BITS ARE KEYBOARD CHARACTER
KEYCA1	EQU	%00000011	THIS FORCES CA1 TO BE INTERRUPT INPUT ON KEY STROKE
KEYSENSE	EQU	%10000000	BIT IN KEYPIA INDICATING KEY WAS STRUCK
KEYCW	EQU	DATAACCESS+KEYCA1	CONTROL WORD FOR KEYPIA

JOYBAUDPIA	EQU	KEYPIA+2	PIA CONTROL WORD FOR JOYSTICK SENSE AND BAUD RATE
JOYSTICK	EQU	JOYBAUDPIA-1	JOYSTICK DATA WORD (BITS 4-7 ARE SENSE BITS)
BAUDRATE	EQU	JOYSTICK	BAUD RATE IS BITS 0-3 TO "JOYSTICK"
JOYDDR	EQU	%00001111		DATA DIRECTION: BITS 7-4 ARE INPUT
HORZBSENSE	EQU	%10000000	SENSE BIT FOR HORIZONTAL BLANKING ON CRT
VERTBSENSE	EQU	%01000000	SENSE BIT FOR VERTICAL BLANKING ON CRT
HORZBCB1	EQU	%00000010	MAKES CB1 AN INPUT FOR HORIZONTAL BLANKING
VERTBCB2	EQU	%00010000	MAKES CB2 AN INPUT FOR VERTICAL BLANKING
JOYBAUDCW	EQU	VERTBCB2+DATAACCESS+HORZBCB1	NORMAL CONTROL WORD FOR JOYBAUDPIA
CROSSUP	EQU	%10000000	BIT IN JOYSTICK MEANS "MOVE CROSSHAIRS UP"
CROSSDN	EQU	%01000000	BIT IN JOYSTICK MEANS "MOVE CROSSHAIRS DOWN"
CROSSRT	EQU	%00100000	BIT IN JOYSTICK MEANS "MOVE CROSSHAIRS RIGHT"
CROSSLF	EQU	%00010000	BIT IN JOYSTICK MEANS "MOVE CROSSHAIRS LEFT"
*
ACIACTRL	EQU	$FFEC	ASYNCHRONOUS COMMUNICATIONS INTERFACE CONTROL REGISTER
ACIASTATUS	EQU	ACIACTRL	ACIA STATUS BITS
ACIAXMITD	EQU	ACIACTRL+1	ACIA TRANSMIT DATA REGISTER
ACIARCVD	EQU	ACIAXMITD	ACIA RECEIVE DATA REGISTER

ACIARESET	EQU	%00000011	TELLS ACIA TO RESET INTERNAL GUTS
ACIADIV16	EQU	%00000001	TELLS ACIA THAT CLOCK FREQ = 16 * BAUD RATE
ACIA8BITS	EQU	%00010100	TELLS ACIA "8 DATA BITS + 1 STOP BIT (NO PARITY)"
ACIARTSD	EQU	%00000000	"REQUEST TO SEND" + XMIT INTERRUPT DISABLED
ACIARTSI	EQU	%00100000	"REQUEST TO SEND" + XMIT INTERRUPT ENABLE
ACIARCVI	EQU	%10000000	RECEIVER INTERRUPT ENABLE
ACIACW	EQU	ACIARCVI+ACIA8BITS+ACIADIV16	CONTROL WORD FOR NORMAL USE OF ACIA
ACIARDRF	EQU	%00000001	ACIA STATUS: RECEIVER DATA REGISTER FULL
ACIATDRE	EQU	%00000010	ACIA STATUS: TRANSMITTER DATA REGISTER EMPTY
ACIAERR	EQU	%01110000	ACIA STATUS: RECEIVED CHARACTER HAS ERROR
ACIAIRQ	EQU	%10000000	ACIA STATUS: INTERRUPT PENDING

*	TIME DELAYS

ONESEC60HZ	EQU	60	# 60THS SECOND IN ONE SECOND

* (1 BYTE/800 NS)*(1 ROW/79 BYTES)*(1 TICK/32 ROWS)*(1E9SEC/1NS)= TICKS/SEC
ONESECNMI	EQU	10000/8*10/32*100/79	IN NON-MASKABLE INTERRUPTS PER SECOND
CLICK	EQU	ONESECNMI/100		KEYSTROKE CLICK TIME (NMI TICKS)
DINGTIME	EQU	ONESECNMI/5	"BELL" TIME FOR CONTROL G (NMI TICKS)
CURSORBLINK	EQU	ONESEC60HZ/2	TIME BETWEEN CURSOR BLINKS (60THS SEC)

*	CROSS HAIR PHYSICS

CROSSACCEL	EQU	$11	=17/256=.066 DOTS/60TH SEC/60TH SEC = 239 DOTS/SEC/SEC
CROSSVMAX	EQU	5	= 300 DOTS/SEC
CROSSPTIME	EQU	ONESECNMI/60	# CLOCK TICKS BETWEEN CROSS HAIR PROCESSING EVENTS (NMI TICKS)

*	JOY STICK DEAD TIMES

JOYDEADSS	EQU	$100//CROSSACCEL*4	= TIME IN 60THS SEC TO MOVE ONE UNIT OF DISTANCE
JOYDEADSTOP	EQU	ONESEC60HZ/3	JOYSTICK DEAD TIME AFTER INSTANT STOP (60THS SEC)
*
*	FUNNY FRACTIONS
*
POINT5	EQU	$8000	= .5 IF FOUND IN FRACTIONX, FRACTIONY
*
*	SPECIAL KEYSTROKES
*
CTRLD	EQU	$04	CONTROL D (ACTIVATE DEBUG)
LF	EQU	$0A	LINE FEED
CR	EQU	$0D	CARRIAGE RETURN
ESCAPE	EQU	$1B	"STOP PROGRAM GRACEFULLY", START GRAPHICS SEQUENCE
XON	EQU	$11	REQUEST HOST TO SEND MORE COMMANDS
XOFF	EQU	$13	NOTIFY HOST THAT BUFFER IS FULL, STOP SENDING

*	PICTURE BLOCK DISPLACEMENTS
*
PICTXSIZ	EQU	0	X SIZE OF PICTURE IN BITS
PICTYSIZ	EQU	PICTXSIZ+2	Y SIZE OF PICTURE IN ROWS
PICTBYT	EQU	PICTYSIZ+1		1ST BYTE OF PICTURE DATA

*	CROSSHAIR VELOCITY CONTROL BLOCKS

CROSSDIR	EQU	0	DIRECTION BIT MASK (UP, DOWN, LEFT, RIGHT)
CROSSVPTR	EQU	CROSSDIR+1	POINTER TO VELOCITY WORD
CROSSVINC	EQU	CROSSVPTR+2	POINTER TO VELOCITY INCREMENT WORD

*	CROSSHAIR AXIS VELOCITY BLOCK

CROSSV	EQU	0	VELOCITY WORD ALONG AN AXIS (X OR Y)

*	CROSS HAIR POSITION FUDGE FACTOR (YOU'LL LOVE THIS!)

CROSSXFUDGE	EQU	3	SO THAT CROSSX+CROSSXFUDGE = X POSITION OF CENTER OF CROSSHAIRS
CROSSYFUDGE	EQU	-2	SO THAT CROSSY+CROSSYFUDGE = Y POSITION OF CENTER OF CROSSHAIRS

*	STACK DISPLACEMENTS FOR MACHINE REGISTERS

AREG	EQU	3		DISPLACEMENT INTO STACK FOR A REGISTER
PC	EQU	6		DISPLACEMENT INTO STACK FOR P-COUNTER

*	WAKE UP CODES

WAKBUFCH	EQU	1	DISPLAY TASK WOKE BECAUSE NEW DATA ARRIVED IN BUFFER
WAK60HZ	EQU	2	DISPLAY TASK WOKE BECAUSE ITS TIME TO MOVE CROSSHAIRS

*	DATA INITIALIZATION LIST ENTRIES

IADDR	EQU	0	POINTER TO BYTE(S) TO INITIALIZE
IDATA	EQU	IADDR+2	DATA BYTE(S) TO STORE AT SPECIFIED ADDRESS

*	BUFFER SIZES

KEYBUFSIZ	EQU	10	SIZE OF KEYSTROKE INPUT BUFFER (<=255!)
XMITBUFSIZ	EQU	10	SIZE OF XMITTER BUFFER (<=255!)
DISPLAYBUFSIZ	EQU	255	SIZE OF DISPLAY TASK INPUT BUFFER (<=255!)

*	OPERATING SYSTEM KERNAL DEFINITIONS

MINSTACK	EQU	(7+2)+7+7	9 BYTES FOR INTERRUPT, 7 FOR NMI, AND 7 FOR CAUSEINT$

*	TCB (TASK CONTROL BLOCK) DEFIINITIONS

TCBLNK	EQU	0		POINTER TO NEXT TCB IN QUEUE
TCBSTK	EQU	TCBLNK+2	STACK POINTER FOR TASK
*	ON TOP OF A STACK IS ALWAYS A CONTEXT BLOCK CONTAINING...
*	REGISTERS, TEMPB AND TEMPA
TCBCND	EQU	TCBSTK+2	TASK'S WAKE UP ROUTINE
TCBSIZ	EQU	TCBCND+2	SIZE OF TASK CONTROL BLOCK
	PAGE	GRAPHBOX CONTROL VARIABLES
	ORG	GRAPHRAM	SPECIAL RAM FOR GRAPHBOX VARIABLES

TEMPX	RMB 2	LOCATIONS 0 AND 1 ARE TASK DEPENDENT
TEMP	EQU	TEMPX	FOR CONVENIENCE
TEMPA	EQU	TEMPX	TEMP STORAGE FOR A REGISTER
TEMPB	EQU	TEMPX+1	TEMP STORAGE FOR B REGISTER

*	SWI INTERRUPT TRANSFERS CONTROL HERE
SWINT	JMP	DEBUG	USER PROGRAM MUST FILL IN JMP ADDRESS

*	I/O INTERRUPT TRANSFERS CONTROL HERE
IOINT	JMP	WHODIDIT	USER PROGRAM CAN CHANGE THIS

*	DEBUGGER ENTRY POINT
DEBUG	JMP	*		DEBUGGER SHOULD INITZ THIS PLACE

SCHEDSTK	FDB	SSTK	DEFAULT VALUE FOR SCHEDULER'S STACK
CURSORX	RMB 2	LOGICAL X POSITION IN DOTS FROM LOWER SCREEN LEFT
CURSORY	RMB 1	LOGICAL Y POSITION IN DOTS FROM LOWER SCREEN LEFT
CURSORBYTE	RMB 2	POINTER TO SCREEN BYTE CONTAINING CURSORBIT
CURSORBIT	RMB 2	BIT NUMBER OF SCREEN BYTE SELECTED BY (CURSORX, CURSORY)
WRITEATOSCREEN	RMB	6	SUBROUTINE TO WRITE (A) TO (X)
*				(EORMODE, IORMODE OR ANDMODE)
NEWX		RMB 2	NEW VALUE OF CURSORX
SHIFT		RMB 2	POINTER TO ROUTINE TO SHIFT DATA PATTERN
ROWCOUNT	RMB 1	NUMBER OF ROWS OF CHARACTER LEFT TO COPY TO SCREEN
TARGETLEFT	RMB 2	PLACE ON SCREEN TO PUT LEFT BYTE OF NEXT ROW OF CHARACTER
TARGETRIGHT	RMB 2	PLACE ON SCREEN TO PUT RIGHT BYTE OF NEXT ROW OF CHARACTER
DELTAXSIGN	RMB 1	SIGN OF DELTA X (0 OR :FF)
DELTAYSIGN	RMB 1	SIGN OF DELTA Y (0 OR :FF)
*	NOTE! DELTAX, DELTAY MUST BE IN THIS EXACT ORDER FOR "RASSOCIATE"
DELTAX		RMB 2	DIFFERENCE BETWEEN CURSOR AND TARGETX
DELTAY		RMB 2	DIFFERENCE BETWEEN CURSORY AND TARGETY
TARGETX	RMB 2	LOGICAL X POSITION TARGET
TARGETY	RMB 1	LOGICAL Y POSITION TARGET
FRACTIONX	RMB 2	FRACTIONAL PART OF CURSORX (USED IN DDA ROUTINES)
FRACTIONY	RMB 2	FRACTIONAL PART OF CURSORY (USED IN DDA ROUTINES)

*	DOWN- AND LEFTSHIFTDIST ARE LOGICAL SCREEN ROTATIONS
*	ASSUMING THAT SCREENBOTTOM BYTE IS LOWEST, LEFTMOST BYTE ON DISPLAY...
*	WHEN DOWNSHIFTDIST=0=LEFTSHIFTDIST
*	THE PIA SHIFT CONTROL REGISTERS ACTUALLY HOLD FUDGED VERSIONS OF DOWN- AND LEFTSHIFTDIST
DOWNSHIFTDIST	RMB	1	DOWN SHIFT DISTANCE (DOWNSHIFTDATA-SCREENDOWNFUDGE)
LEFTSHIFTDIST	RMB	2	LEFT SHIFT DISTANCE (LEFTSHIFTDATA-SCREENLEFTFUDGE)

*	CROSS HAIR STUFF (ORDER DETERMINED BY CROSSHAIR CONTROL BLOCKS)

CROSSXVEL	RMB	1	CROSSHAIRS X VELOCITY (SIGNED)
CROSSXVFRA	RMB	1	CROSSHAIRS X FRACTIONAL VELOCITY (.005... .995)
CROSSYVEL	RMB	1	CROSSHAIRS Y VELOCITY (SIGNED)
CROSSYVFRA	RMB	1	CROSSHAIRS Y FRACTIONAL VELOCITY (.005... .995)
CROSSX		RMB	2	CROSSHAIRSX POSITION (INTEGER PART) 0..511
CROSSY		RMB	1	CROSSHAIRS Y POSITION (INTEGER PART) 0..255
CROSSXFRA	RMB	1	CROSSHAIRS X FRACTION PART (.005.. .995)
CROSSYFRA	RMB	1	CROSSHAIRS Y FRACTION PART (.005 .. .995)
CROSSON	RMB	1	0 --> CROSSHAIRS NOT DISPLAYED; <>0 --> DISPLAYED
CURSORON	RMB	1	0 --> CURSOR NOT DISPLAYED; <>0 --> DISPLAYED

LCTIME	RMB	1	LAST TIME CROSS HAIRS WERE PROCESSED (IN TICKS)
BLINKFUSE	RMB 1	ONE SECOND TIMER FOR CURSOR BLINKER

PICTUREP	RMB 2	POINTER TO ASSOCIATION TABLE ENTRY
PICTURE	RMB	2	POINTER TO PICTURE BLOCK
PICTOP	RMB	2	POINTER TO TOP OF PICTURE BLOCK REGION

MULTIPLIER	RMB 1	USED TO COMPUTE # BYTES IN A PICTURE
PWIDTH		RMB 1	WIDTH OF PICTURE (ROUNDED UP) IN BYTES
WIDTHCNT	RMB 1	DOWN COUNTER USED TO SCAN PICTURE ROW
RESIDUE		RMB 1	PARTIAL PICTURE BYTE NOT YET PLACED ON SCREEN

LEFTEND	RMB	1	LEFT END OF TOP (BOTTOM) LINE OF A BOX
LEFTCOLUMN	RMB 1	LEFT EDGE OF (SIDE OF) EMPTY BOX
RIGHTEND	RMB 1	RIGHT END OF TOP (BOTTOM) LINE OF A BOX
RIGHTCOLUMN	RMB 1	RIGHT EDGE OF (SIDE OF) BOX

JOYDEADTIME	RMB 1	<>0 --> JOYSTICK IS IGNORED FOR N 60THS OF A SECOND
DURATION	RMB 1	<>0 --> BEEPER TONE TIME IN NMI INTERRUPTS
TIME		RMB 4	TIME SINCE RESTART IN NMI TICKS

KEYBUFILL	RMB 2	POINTER TO NEXT FREE BYTE IN KEYBUFFER
KEYBUFMPT	RMB 2	POINTER USED TO EMPTY THE KEYBUFFER
KEYBUFREE	RMB 1	# BYTES FREE IN KEY BUFFER

DISPLAYBUFILL	RMB 2	POINTER TO NEXT FREE BYTE IN DISPLAY BUFFER
DISPLAYBUFMPT	RMB 2	POINTER USED TO EMPTY THE DISPLAY BUFFER
DISPLAYBUFFREE	RMB 1	NUMBER OF UNUSED BYTES REMAINING IN DISPLAY BUFFER
CHARREJECT	RMB 1	<>0 --> LASTCHAR WAS REJECTED
LASTCHAR	RMB 1	LAST CHARACTER RETURNED BY GETDB

XMITBUFILL	RMB 2	POINTER USED TO FILL TRANSMIT BUFFER
XMITBUFMPT	RMB 2	POINTER USED TO EMPTY TRANSMIT BUFFER
XMITBUFREE	RMB 1	# BYTES FREE IN TRANSMIT BUFFER
PRIORITYXMIT	RMB 1	DATA BYTE TO BE SENT INSTEAD OF BYTE FROM TRANSMIT BUFFER

CURRENTASK	RMB 2	POINTER TO CURRENTLY EXECUTING TASK
TASKQUEUE	RMB 2	POINTER TO LIST OF TASKS TO RUN
DONTSTOPME	RMB	1	-1 --> OK TO SCHEDULE OR INTERRUPT;
*				0 --> IN SCHEDULER
*				>= 0 --> ONLY OK TO INTERRUPT
SURPRISE	RMB 1	0--> SCHEDULING DECISION IS OK

PARAMETERERROR	RMB 2	POINTER TO PARAMETER ERROR RECOVERY ROUTINE

ADDRESS	RMB	2	STORAGE FOR DOWNLOAD
CKSUM	RMB	1	CHECKSUM BYTE FOR LOAD RECORDS
COUNT	RMB	1	GENERAL PURPOSE COUNTER

ESCCOUNT	RMB	1	NUMBER OF ESCAPE CHARACTERS RECEIVED

VALUE	RMB	2	16 BIT NUMBER TO BE SENT TO REMOTE HOST
ACC	RMB	3	5 BCD DIGIT BUFFER IN WHICH TO CONVERT BINARY TO DECIMAL
*	DISPLAY TASK CONTROL BLOCK

DISPLAYTCB	RMB	TCBSIZ

	RMB	MINSTACK+30	STACK SPACE FOR INTERRUPT, NMI, + SLOP
DISPSTACK	EQU	*-1	DISPLAY TASK'S STACK

*	KEYBOARD TASK CONTROL BLOCK

KEYTCB	RMB	TCBSIZ

	RMB	MINSTACK+20	STACK SPACE FOR INTERRUPT, NMI + WORKING SPACE
KEYSTACK	EQU	*-1	KEYBOARD TASK'S STACK

*	KEYBUFFER

KEYBUFBAS	EQU	*	KEYSTROKE BUFFER (CIRCULAR)
	RMB	KEYBUFSIZ	MUST NOT EXCEED 255 BYTES!
KEYBUFTOP	EQU	*

*	TRANSMIT BUFFER

XMITBUFBAS	EQU	*	TRANSMITTER BUFFER (CIRCULAR)
	RMB	XMITBUFSIZ	MUST NOT EXCEED 255 BYTES!
XMITBUFTOP	EQU	*

*	SCHEDULER'S STACK SPACE
*	ALL INTERRUPTS USE THIS STACK FOR SCRATCH SPACE, TOO

	RMB	MINSTACK+10	16 BYTES MINIMUM TO ALLOW INTERRUPT AND NMI
SSTK	EQU	*-1	(EXTRA BYTES IF INTERRUPT ROUTINES DO PUSHES)

*	DISPLAYBUFFER

DISPLAYBUFBAS	EQU	*	DISPLAY (COMMAND) BUFFER (CIRCULAR)
	RMB	DISPLAYBUFSIZ	NEEDS TO BE BIG ENOUGH TO ALLOW FOR SOFTWARE DELAYS
DISPLAYBUFTOP	EQU	*

	IF *>=IOBASE
	+GRAPHRAM OVERALLOCATED+
	FIN
	PAGE
	ORG	$80		ARBITRARY STARTING POINT FOR ASSOC TABLE

*	ASSOCIATION TABLE

ASSOCIATIONTBL	EQU	*	SET OF POINTERS TO ASSOCIATED PICTURES
	RMB	64*2	2 BYTES/POINTER * 64 ASSOCIATION CODES
ASSOCTBLEND	EQU	*	END OF ASSOCIATION TABLE

*	FROM HERE ON IS USER/ASSOCIATED PICTURE MEMORY

FREESPACE	EQU	*
	PAGE	APPLICATION PROGRAM ENTRY POINTS

	ORG	ROM	SET PROGRAM BASE FOR THE ROM

*	*** A LINKING LOADER WOULD SAVE A LOT OF ROM! ***

*	OS ENTRY POINTS

	JMP	WAIT$		ENTRY POINT FOR TASK WAIT
	JMP	CAUSEINT$	ENTRY POINT FOR TASK TO CAUSE FAKE INTERRUPT
	JMP	FORCESCHEDULE	ENTRY POINT FOR INTERRUPT ROUTINE TO FORCE SCHEDULING
	JMP	IORTI		I/O INTERRUPT RETURN IF NOTHING SIGNIFICANT HAPPENED

*	CONTROL FUNCTION ENTRY POINTS

	JMP	DEBUG		$00 NULL (ILLEGAL)
	JMP	SETCHCURSOR	$01 CONTROL A; (A) = ROW, (B)= COL
	JMP	RETURN		$02 CONTROL B
	JMP	RETURN		$03 CONTROL C
	JMP	RETURN		$04 CONTROL D
	JMP	RETURN		$05 CONTROL E
	JMP	RETURN		$06 CONTROL F
	JMP	DING		$07 CONTROL G
	JMP	BACKSPACE	$08 CONTROL H
	JMP	RETURN		$09 CONTROL I
	JMP	CHCURSORDOWN	$0A CONTROL J
	JMP	RETURN		$0B CONTROL K
	JMP	NEWPAGE		$0C CONTROL L
	JMP	ENDLINE		$0D CONTROL M
	JMP	RETURN		$0E CONTROL N
	JMP	RETURN		$0F CONTROL O
	JMP	RETURN		$10 CONTROL P
	JMP	RETURN		$11 CONTROL Q
	JMP	CHCURSORRIGHT	$12 CONTROL R
	JMP	CHCURSORLEFT	$13 CONTROL S
	JMP	RETURN		$14 CONTROL T
	JMP	RETURN		$15 CONTROL U
	JMP	RETURN		$16 CONTROL V
	JMP	RETURN		$17 CONTROL W
	JMP	RETURN		$18 CONTROL X
	JMP	RETURN		$19 CONTROL Y
	JMP	RETURN		$1A CONTROL Z
	JMP	RETURN		$1B CONTROL [ (ESCAPE)
	JMP	RETURN		$1C CONTROL \
	JMP	RETURN		$1D CONTROL ]
	JMP	CHCURSORUP	$1E CONTROL ^
	JMP	RETURN		$1F CONTROL _
	PAGE	GRAPHICS ENTRY POINTS

	JMP	DISPLAYCHAR	PRINTING CHARACTER OUTPUT TO CRT; (A) CONTAINS CHARACTER
	JMP	PROCESSCHAR	BYTE STREAM OUTPUT TO CRT; (A) CONTAINS CHARACTER
	JMP	SENDCHAR	BYTE STREAM OUTPUT TO REMOTE CPU
	JMP	DRAWASSOCIATED	ESC $40-$7F: (A) CONTAINS PICTURE NUMBER

	JMP	READCROSSHAIRS	ESC ENQ ($05):  RETURNS (X)=CROSSX, (Y)=CROSSY
	JMP	READCURSORXY	ESC ACK ($06):  RETURNS (X)=CURSORX, (Y)=CURSORY
	JMP	ROLLSCREENLEFT	ESC DC1 ($11)
	JMP	ROLLSCREENRIGHT	ESC DC2 ($12)
	JMP	ROLLSCREENUP	ESC DC3 ($13)
	JMP	ROLLSCREENDOWN	ESC DC4 ($14)
	JMP	IMAGEROWTOP	ESC FS ($1C):  (X) POINTS TO IMAGE ROW (64 BYTES)
	JMP	IMAGEROWBOTTOM	ESC GS ($1D):  (X) POINTS TO IMAGE ROW (64 BYTES)
	JMP	IMAGECOLLEFT	ESC RS ($1E):  (X) POINTS TO IMAGE COLUMN (32 BYTES)
	JMP	IMAGECOLRIGHT	ESC US ($1F):  (X) POINTS TO IMAGE COLUMN (32 BYTES)
	JMP	SETCURSORXY	ESC SP ($20):  (X) CONTAINS NEW CURSORX, (A) CONTAINS NEW CURSOR Y
	JMP	DOTCURSORXY	ESC !  ($21):  (X) CONTAINS NEW CURSORX, (A) CONTAINS NEW CURSOR Y
	JMP	SETCURSORX	ESC "  ($22):  (X) CONTAINS NEW CURSORX
	JMP	SETCURSORY	ESC #  ($23):  (A) CONTAINS NEW CURSORY
	JMP	DRAWDOT		ESC $  ($24):  DRAWS DOT AT CURSOR
	JMP	BACKBLACK	ESC %  ($25):  SET SCREEN BACKGROUND TO BLACK
	JMP	DRAWLINE	ESC &  ($26):  (X) CONTAINS TARGET X, (A) CONTAINS TARGET Y
	JMP	DOTLINE		ESC '  ($27):  (X) CONTAINS TARGET X, (A) CONTAINS TARGET Y
	JMP	DRAWPICTURE	ESC (  ($28):  (X) POINTS TO PICTURE BLOCK
	JMP	EMPTYBOX	ESC *  ($2A):  (X) CONTAINS X BOX SIZE, (A) CONTAINS Y BOX SIZE
	JMP	SOLIDBOX	ESC +  ($2B):  (X) CONTAINS X BOX SIZE, (A) CONTAINS Y BOX SIZE
	JMP	BACKTOGGLE	ESC ,  ($2C):  TOGGLE SCREEN BACKGROUND COLOR
	JMP	BLANKREGION	ESC -  ($2D):  (X) CONTAINS X REGION SIZE, (A) CONTAINS Y REGION SIZE
	JMP	BACKWHITE	ESC .  ($2E):  SET SCREEN BACKGROUND TO WHITE
	JMP	MOVECU		ESC 0  ($30):  MOVE CURSOR UP (ONE DOT)
	JMP	MOVECUR		ESC 1  ($31):  MOVE CURSOR UP AND RIGHT
	JMP	MOVECR		ESC 2  ($32):  MOVE CURSOR RIGHT
	JMP	MOVECRD		ESC 3  ($33):  MOVE CURSOR RIGHT AND DOWN
	JMP	MOVECD		ESC 4  ($34):  MOVE CURSOR DOWN
	JMP	MOVECDL		ESC 5  ($35):  MOVE CURSOR DOWN AND LEFT
	JMP	MOVECL		ESC 6  ($36):  MOVE CURSOR LEFT
	JMP	MOVECLU		ESC 7  ($37):  MOVE CURSOR LEFT AND UP
	JMP	DOTAMCU		ESC 8  ($38):  DRAW DOT AND MOVE CURSOR UP
	JMP	DOTAMCUR	ESC 9  ($39):  DRAW DOT AND MOVE CURSOR UP AND RIGHT
	JMP	DOTAMCR		ESC :  ($3A):  DRAW DOT AND MOVE CURSOR RIGHT
	JMP	DOTAMCRD	ESC ;  ($3B):  DRAW DOT AND MOVE CURSOR RIGHT AND DOWN
	JMP	DOTAMCD		ESC <  ($3C):  DRAW DOT AND MOVE CURSOR DOWN
	JMP	DOTAMCDL	ESC =  ($3D):  DRAW DOT AND MOVE CURSOR DOWN AND LEFT
	JMP	DOTAMCL		ESC >  ($3E):  DRAW DOT AND MOVE CURSOR LEFT
	JMP	DOTAMCLU	ESC ?  ($3F):  DRAW DOT AND MOVE CURSOR LEFT AND UP
	PAGE	CONTROL CHARACTER BRANCH TABLE
CONTROLBRANCH	EQU	*	CONTROL CHARACTER BRANCH TABLE
	FDB	RETURN		$00 NULL
	FDB	RSETCHCURSOR	$01 CONTROL A
	FDB	RETURN		$02 CONTROL B
	FDB	RETURN		$03 CONTROL C
	FDB	RETURN		$04 CONTROL D
	FDB	RETURN		$05 CONTROL E
	FDB	RETURN		$06 CONTROL F
	FDB	DING		$07 CONTROL G
	FDB	BACKSPACE	$08 CONTROL H
	FDB	RETURN		$09 CONTROL I
	FDB	CHCURSORDOWN	$0A CONTROL J
	FDB	RETURN		$0B CONTROL K
	FDB	NEWPAGE		$0C CONTROL L
	FDB	ENDLINE		$0D CONTROL M
	FDB	RETURN		$0E CONTROL N
	FDB	RETURN		$0F CONTROL O
	FDB	RETURN		$10 CONTROL P
	FDB	RETURN		$11 CONTROL Q
	FDB	CHCURSORRIGHT	$12 CONTROL R
	FDB	CHCURSORLEFT	$13 CONTROL S
	FDB	RETURN		$14 CONTROL T
	FDB	RETURN		$15 CONTROL U
	FDB	RETURN		$16 CONTROL V
	FDB	RETURN		$17 CONTROL W
	FDB	RETURN		$18 CONTROL X
	FDB	RETURN		$19 CONTROL Y
	FDB	RETURN		$1A CONTROL Z
	FDB	REMOTEESCAPE	$1B CONTROL [ (GRAPHICS COMMAND PREAMBLE)
	FDB	RETURN		$1C CONTROL BACKSLASH
	FDB	RETURN		$1D CONTROL ]
	FDB	CHCURSORUP	$1E CONTROL UP ARROW
	FDB	RETURN		$1F CONTROL LEFT ARROW
	PAGE	GRAPHICS COMMAND BRANCH TABLE
GRAPHICSBRANCH	EQU	*	BRANCH TABLE FOR REMOTE GRAPHICS COMMANDS
	FDB	BADCOMMAND	ESC NUL ($00)
	FDB	RSELECTWINDOW	ESC SOH ($01): XORIGIN, YORIGIN <CR><LF>
	FDB	BADCOMMAND	ESC STX ($02)
	FDB	BADCOMMAND	ESC ETX ($03)
	FDB	BADCOMMAND	ESC EOT ($04)
	FDB	RREADCROSSHAIRS	ESC ENQ ($05):  RETURNS CROSSX, CROSSY
	FDB	RREADCURSORXY	ESC ACK ($06):  RETURNS CURSORX, CURSORY
	FDB	BADCOMMAND	ESC BEL ($07)
	FDB	BADCOMMAND	ESC BS  ($08)
	FDB	BADCOMMAND	ESC HT  ($09)
	FDB	BADCOMMAND	ESC LF  ($0A)
	FDB	BADCOMMAND	ESC VT  ($0B)
	FDB	BADCOMMAND	ESC FF  ($0C)
	FDB	BADCOMMAND	ESC CR  ($0D)
	FDB	BADCOMMAND	ESC SO  ($0E)
	FDB	ZAPASSOCIATIONS	ESC SI  ($0F)
	FDB	DUMPMODE	ESC DLE ($10)   SWITCH TO CHARACTER DUMP MODE
	FDB	ROLLSCREENLEFT	ESC DC1 ($11)
	FDB	ROLLSCREENRIGHT	ESC DC2 ($12)
	FDB	ROLLSCREENUP	ESC DC3 ($13)
	FDB	ROLLSCREENDOWN	ESC DC4 ($14)
	FDB	RDRAWCIRCLE	ESC NAK ($15):  RADIUS? ENDPOINT? <CR><LF>
	FDB	SELECTIORMODE	ESC SYN ($16)
	FDB	RGOTO		ESC ETB ($17):  HEXADDRESS <CR><LF>
	FDB	SELECTANDMODE	ESC CAN ($18)
	FDB	SELECTEORMODE	ESC EM  ($19)
	FDB	RDOWNLOAD	ESC SUB ($1A):  SEQUENCE OF MIKBUG RECORDS, S9 <CR><LF>
	FDB	RALTERNATESET	ESC ESC ($1B)
	FDB	RIMAGEROWTOP	ESC FS  ($1C):  IMAGE ROW OF 64 BYTES <CR><LF>
	FDB	RIMAGEROWBOTTOM	ESC GS  ($1D):  IMAGE ROW OF 64 BYTES <CR><LF>
	FDB	RIMAGECOLLEFT	ESC RS  ($1E):  IMAGE COLUMN OF 32 BYTES <CR><LF>
	FDB	RIMAGECOLRIGHT	ESC US  ($1F):  IMAGE COLUMN OF 32 BYTES <CR><LF>
	FDB	RSETCURSORXY	ESC SP  ($20):  NEW CURSORX, NEW CURSOR Y <CR><LF>
	FDB	RDOTCURSORXY	ESC !   ($21):  NEW CURSORX, NEW CURSOR Y <CR><LF>
	FDB	RSETCURSORX	ESC "   ($22):  NEW CURSORX <CR><LF>
	FDB	RSETCURSORY	ESC #   ($23):  NEW CURSORY <CR><LF>
	FDB	DRAWDOT		ESC $   ($24)   DRAWS DOT AT CURSOR
	FDB	BACKBLACK	ESC %   ($25)   SET SCREEN BACKGROUND TO BLACK
	FDB	RDRAWLINE	ESC &   ($26):  TARGET X, TARGET Y <CR><LF>
	FDB	RDOTLINE	ESC '   ($27):  TARGET X, TARGET Y <CR><LF>
	FDB	RDRAWPICTURE	ESC (   ($28):  X SIZE, Y SIZE <CR><LF> HEX BYTES OF PICTURE <CR><LF>
	FDB	RASSOCIATE	ESC )   ($29):  X SIZE, Y SIZE <CR><LF> HEX BYTES OF PICTURE <CR><LF>
	FDB	REMPTYBOX	ESC *   ($2A):  X BOX SIZE, Y BOX SIZE <CR><LF>
	FDB	RSOLIDBOX	ESC +   ($2B):  X BOX SIZE, Y BOX SIZE <CR><LF>
	FDB	BACKTOGGLE	ESC ,   ($2C)   TOGGLE SCREEN BACKGROUND COLOR
	FDB	RBLANKREGION	ESC -   ($2D):  X REGION SIZE, Y REGION SIZE <CR><LF>
	FDB	BACKWHITE	ESC .   ($2E)   SET SCREEN BACKGROUND TO WHITE
	FDB	BADCOMMAND	ESC /   ($2F)
	FDB	MOVECU		ESC 0   ($30):  MOVE CURSOR UP (ONE DOT)
	FDB	MOVECUR		ESC 1   ($31):  MOVE CURSOR UP AND RIGHT
	FDB	MOVECR		ESC 2   ($32):  MOVE CURSOR RIGHT
	FDB	MOVECRD		ESC 3   ($33):  MOVE CURSOR RIGHT AND DOWN
	FDB	MOVECD		ESC 4   ($34):  MOVE CURSOR DOWN
	FDB	MOVECDL		ESC 5   ($35):  MOVE CURSOR DOWN AND LEFT
	FDB	MOVECL		ESC 6   ($36):  MOVE CURSOR LEFT
	FDB	MOVECLU		ESC 7   ($37):  MOVE CURSOR LEFT AND UP
	FDB	DOTAMCU		ESC 8   ($38):  DRAW DOT AND MOVE CURSOR UP
	FDB	DOTAMCUR	ESC 9   ($39):  DRAW DOT AND MOVE CURSOR UP AND RIGHT
	FDB	DOTAMCR		ESC :   ($3A):  DRAW DOT AND MOVE CURSOR RIGHT
	FDB	DOTAMCRD	ESC ;   ($3B):  DRAW DOT AND MOVE CURSOR RIGHT AND DOWN
	FDB	DOTAMCD		ESC <   ($3C):  DRAW DOT AND MOVE CURSOR DOWN
	FDB	DOTAMCDL	ESC =   ($3D):  DRAW DOT AND MOVE CURSOR DOWN AND LEFT
	FDB	DOTAMCL		ESC >   ($3E):  DRAW DOT AND MOVE CURSOR LEFT
	FDB	DOTAMCLU	ESC ?   ($3F):  DRAW DOT AND MOVE CURSOR LEFT AND UP

*	CODES $40 TO $7F ARE 64 PICTURE ASSOCIATION CODES
	PAGE	CHARACTER SET BIT PATTERNS
CHARACTERSET	EQU	*	ASCII TO BIT PATTERN CONVERSION TABLE
*	EACH CHARACTER BIT PATTERN OCCUPIES A 5 BY 7 MATRIX
*	THIS BIT PATTERN OCCUPIES 7 CONTIGUOUS BYTES...
*	WITH THE FIRST BYTE BEING THE FIRST DOT ROW OF THE CHARACTER, ETC.
*	THE 5 X 7 BIT MATRIX IS RIGHT JUSTIFIED IN THE BLOCK OF BYTES
*	THE TABLE IS ORDERED IN ASCII SEQUENCE...
*	SO THE PROCESS OF FINDING THE BIT MATRIX FOR A CHARACTER...
*	IS SIMPLY: LOC(BIT MATRIX):=CHARACTERSET+(CHAR-$21)*7
*	1ST BYTE OF CHARACTER HAS SIGN BIT SET IF CHARACTER HAS TAIL (SMALL G,J,P,Q,Y)

CHBANG	FCB	%00100	! BANG
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00000
	FCB	%00100

CHDBLQ	FCB	%01010	" DOUBLE QUOTE
	FCB	%01010
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00000

CHHASH	FCB	%01010	# HASH
	FCB	%01010
	FCB	%11111
	FCB	%01010
	FCB	%11111
	FCB	%01010
	FCB	%01010

CHDOLR	FCB	%00100	$ DOLLAR
	FCB	%01111
	FCB	%10100
	FCB	%01110
	FCB	%00101
	FCB	%11110
	FCB	%00100

CHPCNT	FCB	%11001	% PERCENT
	FCB	%11001
	FCB	%00010
	FCB	%00100
	FCB	%01000
	FCB	%10011
	FCB	%10011

CHAMP	FCB	%01100	& AMPERSAND
	FCB	%10010
	FCB	%01100
	FCB	%11000
	FCB	%10101
	FCB	%10010
	FCB	%01101

CHSGQ	FCB	%00100	' SINGLE QUOTE
	FCB	%00100
	FCB	%00100
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00000

CHLP	FCB	%00010	( LEFT PARENTHESES
	FCB	%00100
	FCB	%01000
	FCB	%01000
	FCB	%01000
	FCB	%00100
	FCB	%00010

CHRP	FCB	%01000	) RIGHT PARENTHESES
	FCB	%00100
	FCB	%00010
	FCB	%00010
	FCB	%00010
	FCB	%00100
	FCB	%01000

CHSTAR	FCB	%00100	* ASTERISK
	FCB	%10101
	FCB	%01110
	FCB	%11111
	FCB	%01110
	FCB	%10101
	FCB	%00100

CHPLUS	FCB	%00000	+ PLUS
	FCB	%00100
	FCB	%00100
	FCB	%11111
	FCB	%00100
	FCB	%00100
	FCB	%00000

CHCOMA	FCB	%00000	, COMMA
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00010
	FCB	%00010
	FCB	%00100

CHDASH	FCB	%00000	- DASH
	FCB	%00000
	FCB	%00000
	FCB	%11111
	FCB	%00000
	FCB	%00000
	FCB	%00000

CHDOT	FCB	%00000	. DOT
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00110
	FCB	%00110

CHSLS	FCB	%00001	/ SLASH
	FCB	%00001
	FCB	%00010
	FCB	%00100
	FCB	%01000
	FCB	%10000
	FCB	%10000

CH0	FCB	%01110	0 ZERO
	FCB	%10001
	FCB	%10011
	FCB	%10101
	FCB	%11001
	FCB	%10001
	FCB	%01110

CH1	FCB	%00100	1 ONE
	FCB	%01100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%11111

CH2	FCB	%01110	2 TWO
	FCB	%10001
	FCB	%00001
	FCB	%01110
	FCB	%10000
	FCB	%10000
	FCB	%11111

CH3	FCB	%01110	3 THREE
	FCB	%10001
	FCB	%00001
	FCB	%01110
	FCB	%00001
	FCB	%10001
	FCB	%01110

CH4	FCB	%10010	4 FOUR
	FCB	%10010
	FCB	%10010
	FCB	%11111
	FCB	%00010
	FCB	%00010
	FCB	%00010

CH5	FCB	%11111	5 FIVE
	FCB	%10000
	FCB	%10000
	FCB	%11110
	FCB	%00001
	FCB	%00001
	FCB	%11110

CH6	FCB	%01110	6 SIX
	FCB	%10001
	FCB	%10000
	FCB	%11110
	FCB	%10001
	FCB	%10001
	FCB	%01110

CH7	FCB	%11111	7 SEVEN
	FCB	%10001
	FCB	%00010
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100

CH8	FCB	%01110	8 EIGHT
	FCB	%10001
	FCB	%10001
	FCB	%01110
	FCB	%10001
	FCB	%10001
	FCB	%01110

CH9	FCB	%01110	9 NINE
	FCB	%10001
	FCB	%10001
	FCB	%01111
	FCB	%00001
	FCB	%00010
	FCB	%11100

CHCLN	FCB	%00000	: COLON
	FCB	%00110
	FCB	%00110
	FCB	%00000
	FCB	%00110
	FCB	%00110
	FCB	%00000

CHSMI	FCB	%00000	; SEMICOLON
	FCB	%00100
	FCB	%00100
	FCB	%00000
	FCB	%00100
	FCB	%00100
	FCB	%01000

CHLAB	FCB	%00011	< LEFT ANGLE BRACKET
	FCB	%00100
	FCB	%01000
	FCB	%10000
	FCB	%01000
	FCB	%00100
	FCB	%00011

CHEQ	FCB	%00000	= EQUAL
	FCB	%00000
	FCB	%11111
	FCB	%00000
	FCB	%11111
	FCB	%00000
	FCB	%00000

CHRAB	FCB	%11000	> RIGHT ANGLE BRACKET
	FCB	%00100
	FCB	%00010
	FCB	%00001
	FCB	%00010
	FCB	%00100
	FCB	%11000

CHQMK	FCB	%01110	? QUESTION MARK
	FCB	%10001
	FCB	%00010
	FCB	%00100
	FCB	%00000
	FCB	%00110
	FCB	%00110

CHAT	FCB	%01110	@ AT SIGN
	FCB	%10001
	FCB	%10101
	FCB	%10111
	FCB	%10110
	FCB	%10000
	FCB	%01111

CHA	FCB	%01110	LETTER A
	FCB	%10001
	FCB	%10001
	FCB	%11111
	FCB	%10001
	FCB	%10001
	FCB	%10001

CHB	FCB	%11110	LETTER B
	FCB	%10001
	FCB	%10001
	FCB	%11110
	FCB	%10001
	FCB	%10001
	FCB	%11110

CHC	FCB	%01110	LETTER C
	FCB	%10001
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%10001
	FCB	%01110

CHD	FCB	%11110	LETTER D
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%11110

CHE	FCB	%11111	LETTER E
	FCB	%10000
	FCB	%10000
	FCB	%11100
	FCB	%10000
	FCB	%10000
	FCB	%11111

CHF	FCB	%11111	LETTER F
	FCB	%10000
	FCB	%10000
	FCB	%11100
	FCB	%10000
	FCB	%10000
	FCB	%10000

CHG	FCB	%01110	LETTER G
	FCB	%10001
	FCB	%10000
	FCB	%10000
	FCB	%10011
	FCB	%10001
	FCB	%01110

CHH	FCB	%10001	LETTER H
	FCB	%10001
	FCB	%10001
	FCB	%11111
	FCB	%10001
	FCB	%10001
	FCB	%10001

CHI	FCB	%11111	LETTER I
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%11111

CHJ	FCB	%01111	LETTER J
	FCB	%00010
	FCB	%00010
	FCB	%00010
	FCB	%00010
	FCB	%10010
	FCB	%01100

CHK	FCB	%10001	LETTER K
	FCB	%10010
	FCB	%10100
	FCB	%11000
	FCB	%10100
	FCB	%10010
	FCB	%10001

CHL	FCB	%10000	LETTER L
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%11111

CHM	FCB	%10001	LETTER M
	FCB	%11011
	FCB	%10101
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001

CHN	FCB	%10001	LETTER N
	FCB	%10001
	FCB	%11001
	FCB	%10101
	FCB	%10011
	FCB	%10001
	FCB	%10001

CHO	FCB	%01110	LETTER OH
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%01110

CHP	FCB	%11110	LETTER P
	FCB	%10001
	FCB	%10001
	FCB	%11110
	FCB	%10000
	FCB	%10000
	FCB	%10000

CHQ	FCB	%01110	LETTER Q
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10101
	FCB	%10010
	FCB	%01101

CHR	FCB	%11110	LETTER R
	FCB	%10001
	FCB	%10001
	FCB	%11110
	FCB	%10100
	FCB	%10010
	FCB	%10001

CHS	FCB	%01110	LETTER S
	FCB	%10001
	FCB	%10000
	FCB	%01110
	FCB	%00001
	FCB	%10001
	FCB	%01110

CHT	FCB	%11111	LETTER T
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100

CHU	FCB	%10001	LETTER U
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%01110

CHV	FCB	%10001	LETTER V
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%01010
	FCB	%00100

CHW	FCB	%10001	LETTER W
	FCB	%10001
	FCB	%10001
	FCB	%10101
	FCB	%10101
	FCB	%11011
	FCB	%10001

CHX	FCB	%10001	LETTER X
	FCB	%10001
	FCB	%01010
	FCB	%00100
	FCB	%01010
	FCB	%10001
	FCB	%10001

CHY	FCB	%10001	LETTER Y
	FCB	%10001
	FCB	%01010
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100

CHZ	FCB	%11111	LETTER Z
	FCB	%00001
	FCB	%00010
	FCB	%00100
	FCB	%01000
	FCB	%10000
	FCB	%11111

CHLSB	FCB	%11111	[ LEFT SQUARE BRACKET
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%11111

CHBSL	FCB	%10000	BACK SLASH
	FCB	%10000
	FCB	%01000
	FCB	%00100
	FCB	%00010
	FCB	%00001
	FCB	%00001

CHRSB	FCB	%11111	] RIGHT SQUARE BRACK
	FCB	%00001
	FCB	%00001
	FCB	%00001
	FCB	%00001
	FCB	%00001
	FCB	%11111

CHUPA	FCB	%00100	^ UP ARROW OR CARET
	FCB	%01110
	FCB	%10101
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100

CHLFA	FCB	%00000	_ LEFT ARROW OR UNDERSCORE
	FCB	%00100
	FCB	%01000
	FCB	%11111
	FCB	%01000
	FCB	%00100
	FCB	%00000

CHAGR	FCB	%01000	` ACCENT GRAVE
	FCB	%00100
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00000
	FCB	%00000

CHSA	FCB	%00000	SMALL A
	FCB	%00000
	FCB	%01110
	FCB	%10010
	FCB	%10010
	FCB	%10010
	FCB	%01111

CHSB	FCB	%10000	SMALL B
	FCB	%10000
	FCB	%10000
	FCB	%11110
	FCB	%10001
	FCB	%10001
	FCB	%11110

CHSC	FCB	%00000	SMALL C
	FCB	%00000
	FCB	%01110
	FCB	%10000
	FCB	%10000
	FCB	%10000
	FCB	%01110

CHSD	FCB	%00001	SMALL D
	FCB	%00001
	FCB	%00001
	FCB	%01111
	FCB	%10001
	FCB	%10001
	FCB	%01111

CHSE	FCB	%00000	SMALL E
	FCB	%00000
	FCB	%01110
	FCB	%10001
	FCB	%11110
	FCB	%10000
	FCB	%01110

CHSF	FCB	%00110	SMALL F
	FCB	%01001
	FCB	%01000
	FCB	%11100
	FCB	%01000
	FCB	%01000
	FCB	%01000

CHSG	FCB	%00111+$80	SMALL G
	FCB	%01001
	FCB	%01001
	FCB	%00111
	FCB	%00001
	FCB	%00001
	FCB	%00110

CHSH	FCB	%10000	SMALL H
	FCB	%10000
	FCB	%10000
	FCB	%11110
	FCB	%10001
	FCB	%10001
	FCB	%10001

CHSI	FCB	%00100	SMALL I
	FCB	%00000
	FCB	%01100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00110

CHSJ	FCB	$82	SMALL J
	FCB	%00000
	FCB	%00110
	FCB	%00010
	FCB	%00010
	FCB	%00010
	FCB	%01100

CHSK	FCB	%10000	SMALL K
	FCB	%10000
	FCB	%10010
	FCB	%10100
	FCB	%11000
	FCB	%10100
	FCB	%10010

CHSL	FCB	%01100	SMALL L
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%00100
	FCB	%01110

CHSM	FCB	%00000	SMALL M
	FCB	%10000
	FCB	%11010
	FCB	%10101
	FCB	%10101
	FCB	%10101
	FCB	%10101

CHSN	FCB	%00000	SMALL N
	FCB	%10000
	FCB	%11100
	FCB	%10010
	FCB	%10010
	FCB	%10010
	FCB	%10010

CHSO	FCB	%00000	SMALL O
	FCB	%00000
	FCB	%01110
	FCB	%10001
	FCB	%10001
	FCB	%10001
	FCB	%01110

CHSP	FCB	%11100+$80	SMALL P
	FCB	%10010
	FCB	%10010
	FCB	%11100
	FCB	%10000
	FCB	%10000
	FCB	%10000

CHSQ	FCB	%00111+$80	SMALL Q
	FCB	%01001
	FCB	%01001
	FCB	%00111
	FCB	%00001
	FCB	%00001
	FCB	%00001

CHSR	FCB	%00000	SMALL R
	FCB	%00000
	FCB	%10110
	FCB	%11001
	FCB	%10000
	FCB	%10000
	FCB	%10000

CHSS	FCB	%00000	SMALL S
	FCB	%00000
	FCB	%01110
	FCB	%10000
	FCB	%01110
	FCB	%00001
	FCB	%01110

CHST	FCB	%00000	SMALL T
	FCB	%01000
	FCB	%11100
	FCB	%01000
	FCB	%01000
	FCB	%01000
	FCB	%00100

CHSU	FCB	%00000	SMALL U
	FCB	%00000
	FCB	%10010
	FCB	%10010
	FCB	%10010
	FCB	%10010
	FCB	%01101

CHSV	FCB	%00000	SMALL V
	FCB	%00000
	FCB	%10001
	FCB	%10001
	FCB	%01010
	FCB	%01010
	FCB	%00100

CHSW	FCB	%00000	SMALL W
	FCB	%00000
	FCB	%10001
	FCB	%10001
	FCB	%10101
	FCB	%10101
	FCB	%01010

CHSX	FCB	%00000	SMALL X
	FCB	%00000
	FCB	%10001
	FCB	%01010
	FCB	%00100
	FCB	%01010
	FCB	%10001

CHSY	FCB	%01001+$80	SMALL Y
	FCB	%01001
	FCB	%01001
	FCB	%00111
	FCB	%00001
	FCB	%00001
	FCB	%00110

CHSZ	FCB	%00000	SMALL Z
	FCB	%00000
	FCB	%01111
	FCB	%00001
	FCB	%00010
	FCB	%00100
	FCB	%01111

CHLCB	FCB	%00110	LEFT CURLY BRACKET
	FCB	%01000
	FCB	%01000
	FCB	%10000
	FCB	%01000
	FCB	%01000
	FCB	%00110

CHVB	FCB	%00100	VERTICAL BAR
	FCB	%00100
	FCB	%00100
	FCB	%00000
	FCB	%00100
	FCB	%00100
	FCB	%00100

CHRCB	FCB	%01100	RIGHT CURLY BRACKET
	FCB	%00010
	FCB	%00010
	FCB	%00001
	FCB	%00010
	FCB	%00010
	FCB	%01100

CHTIL	FCB	%00000	TILDE
	FCB	%00000
	FCB	%01001
	FCB	%10101
	FCB	%10010
	FCB	%00000
	FCB	%00000

CHRUB	FCB	%11111	RUBOUT
	FCB	%11111
	FCB	%11111
	FCB	%11111
	FCB	%11111
	FCB	%11111
	FCB	%11111
	PAGE	REMOTE GRAPHICS TERMINAL EMULATION
*	PROCESSES RECEIVED CHARACTERS

REMOTECHAR	EQU	*	PROCESSES PRINTING CHARACTERS
	JSR	DISPLAYCHAR

REMOTELOOP	EQU	*
	JSR	GETDBLINK	WAIT FOR NEXT COMMAND BYTE TO ARRIVE
*				THIS IS ONLY PLACE CURSOR BLINKS OR CROSS MOVES
	CMPA	#$1F		A CONTROL CHARACTER ?
	BHI	REMOTECHAR	B/ PRINTING CHARACTER, GO PRINT
REMOTECONTROL	EQU	*
	ASLA			IT'S A CONTROL CHARACTER !
	CLRB			DOUBLE TO MAKE JUMP TABLE INDEX
	ADDA	#CONTROLBRANCH&$FF	BRANCH ON IT...
	ADCB	#CONTROLBRANCH/256
REMOTEJSRAB	EQU	*	JSR TO @(B,A)
	STAA	TEMPB
	STAB	TEMPA
	LDX	TEMPX
	LDX	0,X
	JSR	0,X		CALL PROCESSING SUBROUTINE
	BRA	REMOTELOOP	GO PROCESS NEXT CHARACTER

REMOTEESCAPE	EQU	*	PROCESS SEQUENCE THAT STARTS WITH "ESC ..."
	INS			POP RETURN ADDRESS PUSHED BY "JSR 0,X" (ABOVE)
	INS
	JSR	GETDB		GET GRAPHICS COMMAND CODE
	CMPA	#$3F		A DRAW ASSOCIATED PICTURE COMMAND ?
	BHI	DRAWASSOCIATED	YES, GO PROCESS
	ASLA			DOUBLE TO MAKE BRANCH INDEX, SUBTRACT $80
	CLRB			(CLEAR UPPER HALF OF 16 BIT SUM)
	ADDA	#GRAPHICSBRANCH&$FF	NO, BRANCH ON COMMAND CODE
	ADCB	#GRAPHICSBRANCH/256
	BRA	REMOTEJSRAB	GO JSR TO @(B,A)

DING	LDAA	#DINGTIME	GET LENGTH OF DING TIME
	STAA	DURATION	SET SPEAKER TONE DURATION
RETURN	RTS			YOU GUESS...
	PAGE	DRAW ASSOCIATED, DRAW PICTURE
DRAWASSOCIATED	EQU	*
	JSR	CHECKFORUSERRAM	AND DON'T RETURN IS THERE ISN'T ANY!
	ASLA			DOUBLE TO GET BYTE INDEX...
	ADDA	#(ASSOCIATIONTBL-2*$40)&$FF	INTO TABLE
	LDAB	#0
	ADCB	#(ASSOCIATIONTBL-2*$40)/256
	STAA	TEMPX+1
	STAB	TEMPX
	LDX	TEMPX		GET POINTER TO PICTURE BLOCK
	LDX	0,X
	BEQ	PICTXIT		GO IF NO PICTURE TO DRAW
DRAWPICTURE	EQU	*
	STX	PICTURE		SAVE POINTER TO PICTURE BLOCK
	LDAA	PICTYSIZ,X	FETCH PICTURE SIZE
	LDX	PICTXSIZ,X
	BEQ	PICTXIT		EXIT IF PICTURE IS 0 BYTES WIDE
	JSR	MAKESPACE	MAKE SURE ENOUGH SPACE EXISTS
	JSR	GENCURSORBYTE	FIGURE OUT WHERE FIRST BYTE GOES
	STX	TARGETLEFT
	LDX	NEWX		UPDATE CURSORX PAST THE PICTURE
	STX	CURSORX
SPLAT1	EQU	*		SPLAT PICTURE ON SCREEN WHERE I SAY, DAMMIT!
	CLRA			COMPUTE POINTER TO SHIFT ROUTINE
	ASLB			DOUBLE BIT INDEX FOR WORD TABLE BRANCH
	ADDB	#DRAWSHF&$FF
	ADCA	#DRAWSHF/256
	STAA	TEMPX		WHICH SHIFTS EACH PICTURE BYTE...
	STAB	TEMPX+1		BEFORE PLACING IT ONTO THE SCREEN
	LDX	TEMPX
	LDX	0,X		FETCH POINTER FROM TABLE
	STX	SHIFT		SAVE IT WHERE WE CAN GET TO IT FAST
	LDAA	DELTAX		COMPUTE PICTURE WIDTH IN BYTES
	LDAB	DELTAX+1
	ADDB	#7		ROUND WIDTH IN DOTS TO BYTES
	ADCA	#0
	LSRA			# DOTS + 7 <= 512 + 7 = 519 MAX
	RORB
	LSRA
	RORB
	LSRB
	BEQ	PICTXIT		QUIT IF PICTURE IS ZERO BYTES WIDE
	STAB	PWIDTH		SAVE PICTURE WIDTH
PICTLINE	EQU	*	DRAW REMOTE PICTURE LINE ON SCREEN
	LDAB	PWIDTH		GET NUMBER OF BYTES PER PICTURE ROW
	STAB	WIDTHCNT	USE AS LOOP LIMIT COUNT
	CLR	RESIDUE		ZERO THE RESIDUE BYTE
PICTBYTE	EQU	*	DRAW REMOTE PICTURE BYTE ON SCREEN
	LDX	PICTURE		FETCH PICTURE BYTE TO PLACE ON SCREEN
	LDAA	PICTBYT,X
	INX			BUMP POINTER TO PICTURE BLOCK
	STX	PICTURE		SAVE SO WE CAN GET NEXT BYTE
	CLRB			NEED 8 ZERO BITS TO SHIFT INTO
	LDX	SHIFT		GET POINTER TO SHIFT ROUTINE
	JMP	0,X		GO SHIFT BYTE; THEN COPY TO SCREEN

PICTXIT	EQU	*
	RTS

PLSHIFT4	ASLA		LEFT SHIFT 4 PLACES
	ROLB
PLSHIFT3	ASLA		LEFT SHIFT 3 PLACES
	ROLB
PLSHIFT2	ASLA		LEFT SHIFT 2 PLACES
	ROLB
PLSHIFT1	ASLA		LEFT SHIFT 1 PLACE
	ROLB
	ORAB	RESIDUE		COMBINE WITH RESIDUE FROM BYTE TO LEFT
	STAA	RESIDUE		WHAT'S LEFT OVER IS RESIDUE FOR NEXT TIME
	LDX	CURSORBYTE	PLACE ON SCREEN TO PUT THE BYTE
	TBA			COPY BYTE TO (A)
	JSR	WRITEATOSCREEN	KICK PICTURE BYTE TO SCREEN
PICTNEXT	EQU	*	COMPUTE SCREEN ADDRESS OF NEXT PICTURE BYTE
	LDAA	CURSORBYTE+1	BUMP SCREEN POINTER TO NEXT BYTE IN ROW
	TAB			(BUT LEAVE IT IN SAME ROW!)
	INCB			THIS IS NEXT BYTE
	ANDB	#NBROW-1	MOD ROW SIZE
	ANDA	#-NBROW		THIS IS ROW NUMBER, IGNORING BYTE NUMBER
	ABA			THIS IS NEW BYTE ADDRESS
	STAA	CURSORBYTE+1	STORE UPDATED SCREEN POINTER
	DEC	WIDTHCNT	MORE BYTES IN THIS PICTURE ROW ?
	BNE	PICTBYTE	YES, GO COPY TO SCREEN
	LDX	CURSORBYTE	NO, FINISH OFF THIS ROW...
	LDAA	RESIDUE		BY COPYING RESIDUE TO SCREEN
	JSR	WRITEATOSCREEN
	DEC	DELTAY		MORE PICTURE ROWS TO SHOW ?
	BEQ	PICTXIT		NO, I GIVE UP
	LDAB	TARGETLEFT+1	YES, REPOSITION BACK TO START OF ROW
	LDAA	CURSORBYTE
	SUBB	#NBROW		MOVE DOWN THE SCREEN ONE ROW
	SBCA	#0
	BMI	PICT1		GO IF ROW ADDRESS IS STILL VALID
	LDAA	#SCREENTOP/256	RAN OFF BOTTOM, SKIP BACK TO TOP OF SCREEN MEMORY
PICT1	STAA	CURSORBYTE	STORE NEW ROW POINTER
	STAB	CURSORBYTE+1
	STAB	TARGETLEFT+1	SAVE BEGINNING OF ROW SO WE CAN COMPUTE NEXT ROW
	BRA	PICTLINE	GO DRAW NEXT PICTURE LINE

PRSHIFT3	LSRA		SHIFT RIGHT 3
	RORB
PRSHIFT2	LSRA		SHIFT RIGHT 2
	RORB
PRSHIFT1	LSRA		SHIFT RIGHT 1
	RORB
PRSHIFT0	EQU	*	SHIFT RIGHT 0
	ORAA	RESIDUE		COMBINE SHIFTED STUFF WITH RESIDUE FROM LAST TIME
	STAB	RESIDUE		WHAT'S LEFT OVER IS RESIDUE FOR NEXT TIME
	LDX	CURSORBYTE	GET POINTER TO SCREEN BYTE
	JSR	WRITEATOSCREEN	COPY PICTURE BYTE TO SCREEN
	BRA	PICTNEXT	FIGURE OUT WHERE NEXT BYTE SHOULD GO

DRAWSHF	EQU	*	REMOTE PICTURE BYTE SHIFT TABLE
	PRSHIFT0		BIT 0
	PRSHIFT1		BIT 1
	PRSHIFT2		BIT 2
	PRSHIFT3		BIT 3
	PLSHIFT4		BIT 4
	PLSHIFT3		BIT 5
	PLSHIFT2		BIT 6
	PLSHIFT1		BIT 7
	PAGE	REMOTE DRAWPICTURE
RDRAWPICTURE	EQU	*
	JSR	GETXY		GET X AND Y DIMENSIONS OF PICTURE
	STX	DELTAX		SAVE X SIZE OF PICTURE IN DOTS
	STAA	DELTAY		REMEMBER DEPTH OF PICTURE
	JSR	MAKESPACE	MAKE SURE ENOUGH SPACE ON SCREEN
	JSR	GENCURSORBYTE	FIGURE OUT WHERE FIRST BYTE GOES
	STX	TARGETLEFT
	CLRA			COMPUTE POINTER TO SHIFT ROUTINE
	ASLB			DOUBLE BIT INDEX FOR WORD BRANCH TABLE
	ADDB	#RDRAWSHF&$FF
	ADCA	#RDRAWSHF/256
	STAA	TEMPX		WHICH SHIFTS EACH PICTURE BYTE...
	STAB	TEMPX+1		BEFORE PLACING IT ONTO THE SCREEN
	LDX	TEMPX
	LDX	0,X		FETCH POINTER FROM TABLE
	STX	SHIFT		SAVE IT WHERE WE CAN GET TO IT FAST
	LDAA	DELTAX		COMPUTE PICTURE WIDTH IN BYTES
	LDAB	DELTAX+1
	ADDB	#7		ROUND WIDTH IN DOTS TO BYTES
	ADCA	#0
	LSRA			# DOTS + 7 <= 512 + 7 = 519 MAX
	RORB
	LSRA
	RORB
	LSRB
	BEQ	RPICTXIT	QUIT IF PICTURE IS ZERO BYTES WIDE
	STAB	PWIDTH		SAVE PICTURE WIDTH
RPICTLINE	EQU	*	DRAW REMOTE PICTURE LINE ON SCREEN
	LDAB	PWIDTH		GET NUMBER OF BYTES PER PICTURE ROW
	STAB	WIDTHCNT	USE AS LOOP LIMIT COUNT
	CLR	RESIDUE		ZERO THE RESIDUE BYTE
RPICTBYTE	EQU	*	DRAW REMOTE PICTURE BYTE ON SCREEN
	LDX	#RPICTXIT	GIVE UP IF BAD PICTURE BYTE SENT
	JSR	GETHEXBYTE	GET PICTURE BYTE TO PLACE ON SCREEN
	CLRB			NEED 8 ZERO BITS TO SHIFT INTO
	LDX	SHIFT		GET POINTER TO SHIFT ROUTINE
	JMP	0,X		GO SHIFT BYTE; THEN COPY TO SCREEN

RPLSHIFT4	ASLA		LEFT SHIFT 4 PLACES
	ROLB
RPLSHIFT3	ASLA		LEFT SHIFT 3 PLACES
	ROLB
RPLSHIFT2	ASLA		LEFT SHIFT 2 PLACES
	ROLB
RPLSHIFT1	ASLA		LEFT SHIFT 1 PLACE
	ROLB
	ORAB	RESIDUE		COMBINE WITH RESIDUE FROM BYTE TO LEFT
	STAA	RESIDUE		WHAT'S LEFT OVER IS RESIDUE FOR NEXT TIME
	LDX	CURSORBYTE	PLACE ON SCREEN TO PUT THE BYTE
	TBA			SET UP TO COPY BYTE TO SCREEN
	JSR	WRITEATOSCREEN	KICK PICTURE BYTE TO SCREEN
RPICTNEXT	EQU	*	COMPUTE SCREEN ADDRESS OF NEXT PICTURE BYTE
	LDAA	CURSORBYTE+1	BUMP SCREEN POINTER TO NEXT BYTE IN ROW
	TAB			(BUT LEAVE IT IN SAME ROW!)
	INCB			THIS IS NEXT BYTE
	ANDB	#NBROW-1	MOD ROW SIZE
	ANDA	#-NBROW		THIS IS ROW NUMBER, IGNORING BYTE NUMBER
	ABA			THIS IS NEW BYTE ADDRESS
	STAA	CURSORBYTE+1	STORE UPDATED SCREEN POINTER
	DEC	WIDTHCNT	MORE BYTES IN THIS PICTURE ROW ?
	BNE	RPICTBYTE	YES, GO COPY TO SCREEN
	LDX	CURSORBYTE	NO, FINISH OFF THIS ROW...
	LDAA	RESIDUE		BY COPYING RESIDUE TO SCREEN
	JSR	WRITEATOSCREEN
	DEC	DELTAY		MORE PICTURE ROWS TO SHOW ?
	BEQ	RPICTXIT	NO, I GIVE UP
	LDAB	TARGETLEFT+1	YES, REPOSITION BACK TO START OF ROW
	LDAA	CURSORBYTE
	SUBB	#NBROW		MOVE DOWN THE SCREEN ONE ROW
	SBCA	#0
	BMI	RPICT1		GO IF ROW ADDRESS IS STILL VALID
	LDAA	#SCREENTOP/256	RAN OFF BOTTOM, SKIP BACK TO TOP OF SCREEN MEMORY
RPICT1	STAA	CURSORBYTE	STORE NEW ROW POINTER
	STAB	CURSORBYTE+1
	STAB	TARGETLEFT+1	SAVE BEGINNING OF ROW SO WE CAN COMPUTE NEXT ROW
	BRA	RPICTLINE	GO DRAW NEXT PICTURE LINE

RPRSHIFT3	LSRA		SHIFT RIGHT 3
	RORB
RPRSHIFT2	LSRA		SHIFT RIGHT 2
	RORB
RPRSHIFT1	LSRA		SHIFT RIGHT 1
	RORB
RPRSHIFT0	EQU	*	SHIFT RIGHT 0
	ORAA	RESIDUE		COMBINE SHIFTED STUFF WITH RESIDUE FROM LAST TIME
	STAB	RESIDUE		WHAT'S LEFT OVER IS RESIDUE FOR NEXT TIME
	LDX	CURSORBYTE	GET POINTER TO SCREEN BYTE
	JSR	WRITEATOSCREEN	COPY PICTURE BYTE TO SCREEN
	BRA	RPICTNEXT	FIGURE OUT WHERE NEXT BYTE SHOULD GO

RPICTXIT	EQU	*
	JSR	EATCRLF		EAT TRAILING <CR><LF> IF SENT
	LDX	NEWX		UPDATE CURSORX PAST PICTURE
	STX	CURSORX
	RTS

RDRAWSHF	EQU	*	REMOTE PICTURE BYTE SHIFT TABLE
	RPRSHIFT0	BIT 0
	RPRSHIFT1	BIT 1
	RPRSHIFT2	BIT 2
	RPRSHIFT3	BIT 3
	RPLSHIFT4	BIT 4
	RPLSHIFT3	BIT 5
	RPLSHIFT2	BIT 6
	RPLSHIFT1	BIT 7
	PAGE	DISPLAY PRINTING CHARACTER
DISPLAYCHAR	EQU	*	DISPLAY PRINTING CHARACTER (A)
	LDAB	CURSORX		IS THIS LINE FULL ?
	BEQ	CHAR1		(IF CURSORX <256, THEN "NO")
	LDAB	CURSORX+1	(CURSORX >= 256...)
	CMPB	#(SCREENWIDTH-CHARWIDTH-1)&$FF	... ?
	BLS	CHAR1		NO, GO CHECK FOR OVERRUN BOTTOM OF SCREEN
	LDX	#CHARLEFTSIDE	CHARACTER WOULD RUN OFF RIGHT SIDE OF SCREEN
	STX	CURSORX		SO PUT CURSOR BACK TO LEFT SIDE OF SCREEN
	LDAB	CURSORY		NOW MOVE DOWN THE SCREEN A ROW
	SUBB	#CHARHEIGHT	RUN OFF BOTTOM OF SCREEN ? **** WHAT IF CHARTOPSIDE <> 0 ???
	BCS	CHAR2		YES, MUST ROLL SCREEN
	STAB	CURSORY		(ADVANCE CURSORY DOWN A CHARACTER ROW)
CHAR1	EQU	*	CHECK FOR OVERRUN BOTTOM OF SCREEN
	LDAB	CURSORY		GET VERTICAL CURSOR POSITION
	CMPB	#CHARHEIGHT-1	DOES NEXT LINE RUN OFF BOTTOM OF SCREEN ?
	BCC	CHARPUTC	NO, JUST GO DISPLAY THE CHARACTER
CHAR2	EQU	*		MUST ROLL SCREEN MORE THAN 1 CHARACTER LINE
	SUBB	#CHARHEIGHT-1+(SCREENHEIGHT-(SCREENHEIGHT/CHARHEIGHT)*CHARHEIGHT)
*				ADD ANOTHER LINE TO OVERRUN DISTANCE
CHARR	EQU	*		ROLL THE SCREEN; (B) = - # LINES
	PSHA			SAVE THE CHARACTER, WE NEED IT LATER
	JSR	ROLLUPCHARLINE	ROLL THE SCREEN UP ONE CHARACTER LINE
	PULA			GIMME MY CHARACTER BACK !
*	NEXT, WE OUTPUT THE CHARACTER TO THE SCREEN
CHARPUTC	EQU	*	NOW PUT CHARACTER ON THE SCREEN
	SUBA	#$20		GET RID OF CONTROL CHARACTER BIAS
	BEQ	CHARPUTX	HE SAID "OUTPUT A SPACE"
	STAA	TEMPA		MULTIPLY CHARACTER BY 7...
	ASLA			CHARACTER * 2
	CLRB
	ASLA
	ROLB			CHARACTER * 4
	ASLA
	ROLB			CHARACTER * 8
	SUBA	TEMPA		CHARACTER * 7
	SBCB	#0
	ADDA	#(CHARACTERSET-7)&$FF	COMPUTE POINTER TO DOT MATRIX PATTERN
	ADCB	#(CHARACTERSET-7)/256
	STAB	PICTURE		AND SAVE IT
	STAA	PICTURE+1
	LDAA	CURSORY		IF WE ARE DISPLAYING A LETTER WITH A TAIL,
	LDX	PICTURE		(SMALL G,J,P,Q,Y)
	LDAB	0,X		THEN PLACE ON SCREEN 2 LINES BELOW NORMAL
	BPL	CPUT1		B/ NO TAIL
	SUBA	#2		TAIL, SKIP DOWN SCREEN TWO LINES
CPUT1	JSR	GENCURSORCHAR	GET POINTER TO BYTE WHICH CURSOR SELECTS
	STX	TARGETLEFT	SAVE POINTER TO LEFT TARGET BYTE
	CLRA			COMPUTE POINTER INTO SHIFT JUMP TABLE
	ASLB			DOUBLE BIT INDEX FOR WORD JUMP TABLE
	ADDB	#CSHIFTBL&$FF
	CLRA
	ADCA	#CSHIFTBL/256
	STAA	TEMPX		MOVE POINTER TO (X)...
	STAB	TEMPX+1
	LDX	TEMPX
	LDX	0,X		SO WE CAN FETCH POINTER TO SHIFT ROUTINE
	STX	SHIFT		SAVE POINTER TO SHIFT ROUTINE
	LDAA	TARGETLEFT	NOW COMPUTE POINTER TO RIGHT TARGET BYTE
	STAA	TARGETRIGHT	ITS IN THE SAME BLOCK OF 64 (256) AS TARGETLEFT
	LDAA	TARGETLEFT+1	= NEXT BYTE IN SAME SCREEN ROW AS TARGETLEFT
	TAB			MAKE COPY
	INCB			COMPUTE NEXT BYTE IN ROW OF 64
	ANDB	#NBROW-1	MASK OFF THE WHICH-OF-64 PART
	ANDA	#-NBROW		MAKE IT WRAP AROUND INTO SAME ROW
	ABA			GOT IT !
	STAA	TARGETRIGHT+1	TA DA !
	LDAA	#7		GET NUMBER OF ROWS TO INSERT
	STAA	ROWCOUNT	SET UP LOOP LIMIT COUNTER
*
*	THE PUMP IS PRIMED, NOW LET'S INSERT THE CHARACTER DOT MATRIX
*
	LDX	PICTURE		GET POINTER TO CHARACTER ROW
	LDAA	0,X		FETCH 1ST ROW OF DOT MATRIX TO INSERT
	ANDA	#$1F		MASK OFF "TAIL" FLAG
	CLRB			IN CASE WE MUST SHIFT RIGHT
	LDX	SHIFT		GET ADDRESS OF SHIFT ROUTINE
	JMP	0,X		GO SHIFT AND INSERT ROW

CHARPUTX	EQU	*	DONE INSERTING CHARACTER ON SCREEN
	LDAA	CURSORX		GRAB CURSOR POINTER
	LDAB	CURSORX+1
	ADDB	#CHARWIDTH	ADVANCE IT TO NEXT CHARACTER POSITION
	ADCA	#0		(THIS CANNOT OVERRUN THE RIGHT EDGE OF THE SCREEN)
	STAA	CURSORX
	STAB	CURSORX+1
	RTS			RETURN TO CALLER

CSHIFTL3	ASLA		SHIFT DOT MATRIX LEFT 3 BITS
CSHIFTL2	ASLA		SHIFT DOT MATRIX LEFT 2 BITS
CSHIFTL1	ASLA		SHIFT DOT MATRIX LEFT 1 BIT
CSHIFT0	LDX	TARGETLEFT	GET POINTER TO LEFT HALF OF TARGET
	JSR	WRITEATOSCREEN	STORE BIT PATTERN FOR CHARACER TO SCREEN
	JMP	CPUT2		GO PROCESS NEXT CHARACTER ROW (JMP IS FASTER!)

CSHIFTR4	LSRA		SHIFT RIGHT 4 PLACES
	RORB
CSHIFTR3	LSRA		SHIFT RIGHT 3 PLACES
	RORB
CSHIFTR2	LSRA		SHIFT RIGHT 2 PLACES
	RORB
CSHIFTR1	LSRA		SHIFT RIGHT 1 PLACE
	RORB
	LDX	TARGETLEFT	WRITE LEFT BYTE OF CHARACTER TO SCREEN
	JSR	WRITEATOSCREEN
	LDX	TARGETRIGHT	GET POINTER TO RIGHT HAND TARGET BYTE
	TBA			COPY RIGHT HALF OF CHARACTER TO (A)
	JSR	WRITEATOSCREEN	INSERT RIGHT HALF OF CHARACTER
	LDAB	TARGETRIGHT+1	ADVANCE POINTER TO RIGHT HALF OF CHARACTER
	SUBB	#NBROW
	STAB	TARGETRIGHT+1
CPUT2	DEC	ROWCOUNT	ANY MORE ROWS LEFT TO INSERT ?
	BEQ	CHARPUTX	NO, JUST ADVANCE THE CURSOR
	LDAB	TARGETLEFT+1	ADVANCE TARGET BYTE POINTER TO NEXT ROW
	SUBB	#NBROW
	STAB	TARGETLEFT+1
	BCC	CPUTN1		(GO IF UPPER HALF DOESN'T NEED ADJUSTMENT)
	LDAA	TARGETLEFT	BORROW FROM UPPER HALF
	DECA
	BMI	CPUTN2		GO IF WE DIDN'T RUN OFF BOTTOM OF SCREEN MEMORY
	LDAA	#SCREENTOP/256	OFF BOTTOM OF DISPLAY AREA, FORCE WRAPAROUND
CPUTN2	EQU	*	FINISH UPDATE OF UPPER HALF
	STAA	TARGETLEFT	FINISH UPDATE OF UPPER HALF
	STAA	TARGETRIGHT	DO SAME FOR RIGHT HALF TARGET
CPUTN1	EQU	*	ADVANCE POINTERS TO NEXT ROW OF DOT MATRIX
	LDX	PICTURE		GET POINTER TO NEXT BYTE OF DOT MATRIX
	INX			BUMP POINTER TO NEXT ROW
	STX	PICTURE		AND SAVE IT
	LDAA	0,X		FETCH NEXT ROW OF DOT MATRIX
	CLRB			IN CASE WE MUST SHIFT RIGHT
	LDX	SHIFT		GET POINTER TO SHIFT ROUTINE
	JMP	0,X		GO SHIFT AND INSERT CHARACTER ROW

CSHIFTBL	EQU	*	POINTERS TO CHARACTER SHIFT ROUTINES
	CSHIFTL3		BIT # =0 --> SHIFT LEFT 3
	CSHIFTL2		=1 --> LEFT 2
	CSHIFTL1		ETC.
	CSHIFT0
	CSHIFTR1
	CSHIFTR2
	CSHIFTR3
	CSHIFTR4
	PAGE	"GOTO ROUTINE" (THAT WAS DOWNLOADED BY HOST)
*	PROCESS REMOTE "GOTO" COMMAND

RGOTO	JSR	GETHEXBYTE	GET LEFT HALF OF TARGET ADDRESS
	PSHA
	JSR	GETHEXBYTE	GET RIGHT HALF OF TARGET ADDRESS
	PULB
	PSHA			PUSH ADDRESS ON STACK
	PSHB
*	RTS			BYE!!!
*
*	EATCRLF -- EAT <CR><LF> OPTIONAL SEQUENCE
*	(<CR><LF> TERMINATES PARAMETERIZED GRAPHICS COMMAND)
*	(MAKES IT VERY CONVENIENT FOR "BASIC" PROGRAMS AND THE LIKE TO DRIVE GRAPHBOX)
*
EATCRLF	JSR	GETDB		GET TERMINATOR CHARACTER
	CMPA	#CR		A CARRIAGE RETURN ?
	BNE	EATREJECT	B/ NO, OPTIONAL SEQUENCE NOT SUPPLIED
	JSR	GETDB		GET OPTIONAL <LF>
	CMPA	#LF		IS IT ?
	BEQ	EATRTS		B/ YES, IT'S GONE !
EATREJECT	EQU	*
	INC	CHARREJECT	NO, LET SOMEBODY PROCESS IT!
EATRTS	RTS
	PAGE	CURSOR CONTROL
RSETCURSORX	EQU	*	REMOTE SET CURSORX
	BSR	GETX		GET THE X VALUE
	BSR	EATCRLF		EAT COMMAND TERMINATOR
	LDX	TARGETX		GET X VALUE BACK
SETCURSORX	EQU	*	SETS X VALUE OF CURSOR TO (X)
	STX	CURSORX
	RTS

RSETCURSORY	EQU	*	REMOTE SET Y VALUE OF CURSOR TO 0..255
	BSR	GETY		WAIT FOR PARAMETER TO ARRIVE
	BSR	EATCRLF		EAT COMMAND TERMINATOR
	LDAA	TARGETY		GET Y VALUE BACK
SETCURSORY	EQU	*	SETS Y VALUE OF CURSOR TO (A)
	STAA	CURSORY		STORE AS CURSORY Y VALUE
	RTS			AND EXIT

RSETCURSORXY	EQU	*	REMOTE SET CURSORX, CURSORY
	BSR	GETXY		GET BOTH X AND Y PARAMETERS
SETCURSORXY	EQU	*	SET CURSOR X TO (X), CURSORY TO (A)
	STX	CURSORX
	STAA	CURSORY
	RTS
	PAGE
*	GETX-- GET X DIMENSION
*	MASKS TO 9 BITS, RETURNED IN (A,B) AND (X)
*
GETX	LDX	#RETURN		NO RECOVERY IF ERROR IN PARAMETER
GETXX	JSR	GETINTEGERX	GO CONVERT INPUT STREAM VALUE
	ANDA	#(SCREENWIDTH-1)/256	MASK TO 9 BITS
	STAA	TARGETX		COPY TO (X)
	STAB	TARGETX+1
	LDX	TARGETX
	RTS
*
*	GETY-- GET Y DIMENSION
*	MASKED TO 8 BITS, RETURNED IN (A) ONLY
*
GETY	LDX	#RETURN		NO RECOVERY IF ERROR IN PARAMETER
GETYX	JSR	GETINTEGERX	GO CONVERT INPUT STREAM VALUE
	TBA			COPY VALUE TO (A) AS PROMISED
	STAA	TARGETY		SAVE IT, JUST IN CASE...
	RTS
*
*	GETXY-- GET X AND Y DIMENSIONS
*	SELECTS "NO ERROR RECOVERY"
*	RETURNS X DIMENSION IN (X) AND Y IN (A)
*	CALLS "EATCRLF"
*
GETXY	BSR	GETX		GO GET THE X VALUE
	BSR	GETY
	BSR	EATCRLF
	LDX	TARGETX		GET STORED X AND Y PARAMETERS
	LDAA	TARGETY
	RTS
	PAGE	DRAW DOT
*	DRAWDOT  --  SUBROUTINE TO DRAW DOT AT (CURSORX, CURSORY)
*
DRAWDOT	EQU	*
	JSR	GENCURSORBYTE	GET BIT NUMBER
	CLRA			CONVERT BIT NUMBER TO BIT MASK
	ADDB	#BITMSK&$FF
	ADCA	#BITMSK/256
	STAA	TEMPX
	STAB	TEMPX+1
	LDX	TEMPX
	LDAA	0,X		FETCH EQUIVALENT BIT MASK
	LDX	CURSORBYTE	GO UPDATE THE SELECTED BYTE
	JMP	WRITEATOSCREEN	MODIFY THE DISPLAY MEMORY
*	RTS

BITMSK	FCB	%10000000	BIT # 0
	FCB	%01000000	BIT # 1
	FCB	%00100000	BIT # 2
	FCB	%00010000	BIT # 3
	FCB	%00001000	BIT # 4
	FCB	%00000100	BIT # 5
	FCB	%00000010	BIT # 6
	FCB	%00000001	BIT # 7
	PAGE	DRAW DOT AND MOVE CURSOR ROUTINES
RDOTCURSORXY	EQU	*	SET CURSOR, THEN DRAW DOT
	BSR	GETXY
DOTCURSORXY	EQU	*	SET CURSOR X,Y AND DRAW DOT
	BSR	SETCURSORXY
	BSR	DRAWDOT
	RTS

DOTAMCU	EQU	*	DRAW DOT AND MOVE CURSOR UP
	BSR	DRAWDOT
MOVECU	EQU	*	MOVE CURSOR UP
	BSR	BUMPCURSORY	MOVE CURSOR UP
	RTS

DOTAMCUR	EQU	*	DRAW DOT AND MOVE CURSOR UP AND RIGHT
	BSR	DRAWDOT
MOVECUR	EQU	*	MOVE CURSOR UP AND RIGHT
	BSR	BUMPCURSORY
	BSR	BUMPCURSORX
	RTS

DOTAMCR	EQU	*	DRAW DOT AND MOVE CURSOR RIGHT
	BSR	DRAWDOT
MOVECR	EQU	*	MOVE CURSOR RIGHT
	BSR	BUMPCURSORX
	RTS

DOTAMCRD	EQU	*	DRAW DOT AND MOVE CURSOR RIGHT AND DOWN
	BSR	DRAWDOT
MOVECRD	EQU	*	MOVE CURSOR RIGHT AND DOWN
	BSR	BUMPCURSORX
	BSR	DECCURSORY
	RTS

DOTAMCD	EQU	*	DRAW DOT AND MOVE CURSOR DOWN
	BSR	DRAWDOT
MOVECD	EQU	*	MOVE CURSOR DOWN
	BSR	DECCURSORY
	RTS

DOTAMCDL	EQU	*	DRAW DOT AND MOVE CURSOR DOWN AND LEFT
	BSR	DRAWDOT
MOVECDL	EQU	*	MOVE CURSOR DOWN AND LEFT
	BSR	DECCURSORY
	BSR	DECCURSORX
	RTS

DOTAMCL	EQU	*	DRAW DOT AND MOVE CURSOR LEFT
	BSR	DRAWDOT
MOVECL	EQU	*	MOVE CURSOR LEFT
	BSR	DECCURSORX
	RTS

DOTAMCLU	EQU	*	DRAW DOT AND MOVE CURSOR LEFT AND UP
	BSR	DRAWDOT
MOVECLU	EQU	*	MOVE CURSOR LEFT AND UP
	BSR	DECCURSORX
	BSR	BUMPCURSORY
	RTS
*	BUMPCURSORX -- BUMPS CURSORX OR LEFT ROTATES THE SCREEN
*
BUMPCURSORX	EQU	*
	LDX	CURSORX		DO WHAT WE SAID !
	CPX	#SCREENWIDTH-1	AT RIGHT EDGE OF SCREEN ?
	BEQ	ROLLSCREENLEFT	(B/ YES)
	INX			NO, SO BUMP THE CURSORX
	STX	CURSORX		SAVE UPDATED VALUE
	RTS			AND EXIT

ROLLSCREENLEFT	EQU	*	MUST SHIFT SCREEN LEFT
	LDAA	LEFTSHIFTDIST	GET LEFT SHIFT DISTANCE
	LDAB	LEFTSHIFTDIST+1
	ADDB	#1		BUMP LEFT SHIFT DISTANCE
	ADCA	#0
*
*	MODIFYLEFTSHIFT -- SETS LOGICAL LEFT SHIFT DISTANCE TO (A,B)
*	ALSO SETS HARDWARE LEFT SHIFT CONTROL REGISTER
*	DESTROYS (A,B)
*
MODIFYLEFTSHIFT	EQU	*	ENTRY POINT FOR DECCURSORX
	ANDA	#1		( FIRST, TAKE ANSWER MOD 512 )
	STAA	LEFTSHIFTDIST	REMEMBER THE LOGICAL LEFT SHIFT DISTANCE
	STAB	LEFTSHIFTDIST+1
	ADDB	#SCREENLEFTFUDGE&$FF	ADJUST LOGICAL LEFTSHIFT...
	ADCA	#SCREENLEFTFUDGE/256		FOR HARDWARE
	ANDA	#1		MASK TO 9 BITS
	ASLA			OUTPUT NEW LEFT SHIFT DISTANCE TO THE HARDWARE
	ASLA
	ASLA
	ORAA	#LEFTSHIFTCW	THESE FUNNIES ARE FOR THE PIA CTRL WORD
	STAA	LEFTSHIFTMSB
	STAB	LEFTSHIFTDATA
	RTS
*
*	DECCURSORX -- DECREMENT CURSORX OR RIGHT ROTATE THE SCREEN
*
DECCURSORX	EQU	*
	LDX	CURSORX		DO WHAT HE SAID
	BEQ	ROLLSCREENRIGHT	AT LEFT EDGE OF SCREEN ?
	DEX			NO, SO DECREMENT THE CURSORX
	STX	CURSORX		STORE THE UPDATED VALUE
	RTS			AND EXIT

ROLLSCREENRIGHT	EQU	*	MUST ROTATE THE SCREEN RIGHT
	LDAA	LEFTSHIFTDIST	GET LEFT SHIFT DISTANCE
	LDAB	LEFTSHIFTDIST+1	WHILE THE OTHER 8 BITS LIVE HERE
	SUBB	#1		DECREASE LEFT SHIFT --> RIGHT SHIFT
	SBCA	#0
	BRA	MODIFYLEFTSHIFT
*	BUMPCURSORY -- BUMPS CURSORY OR ROLLS SCREEN DOWN
*
BUMPCURSORY	EQU	*
	INC	CURSORY		DO IT
	BNE	BUMPRTS		EXIT IF ITS OK
	DEC	CURSORY		RAN OFF TOP OF SCREEN
ROLLSCREENDOWN	EQU	*
	LDAA	DOWNSHIFTDIST	MODIFY DOWNSHIFT INSTEAD
	INCA			BY ADDING ONE
*
*	MODIFYDOWNSHIFT -- SETS LOGICAL DOWN SHIFT DISTANCE TO (A)
*	ALSO SETS HARDWARE DOWN SHIFT CONTROL REGISTER
*	DESTROYS (A); PRESERVES (B)
*
MODIFYDOWNSHIFT	EQU	*
	STAA	DOWNSHIFTDIST	SO WE CAN READ LOGICAL DOWNSHIFT W/O LOSING NMI
	ADDA	#SCREENDOWNFUDGE	CONVERT LOGICAL DOWNSHIFT TO PHYSICAL DOWNSHIFT
	STAA	DOWNSHIFTDATA	NOW TELL THE HARDWARE
BUMPRTS	RTS

*	DECCURSORY -- DECREMENTS CURSORY OR ROLLS SCREEN UP
*
DECCURSORY	EQU	*
	LDAA	CURSORY		ARE WE AT BOTTOM OF SCREEN ?
	BNE	DECCURSORY1	NO, WE CAN DO IT!
ROLLSCREENUP	EQU	*
	LDAA	DOWNSHIFTDIST	RATS, MUST ROLL SCREEN UP
	DECA			DECREMENT THE LOGICAL DOWNSHIFT
	BRA	MODIFYDOWNSHIFT

DECCURSORY1	EQU	*
	DECA			DO WHAT WE PROMISED
	STAA	CURSORY
	RTS			AND GET OUT...
*	GENCURSORBYTE -- RETURNS POINTER TO BYTE ON SCREEN IN (X)
*	WHICH CONTAINS THE BIT SPECIFIED BY CURSORX, CURSORY
*	ALSO SETS CURSORBYTE TO (X)
*	(B) = BIT NUMBER  (0 = MSB, 7 = LSB)
*	BYTE = [(CURSORY+DOWNSHIFT) MOD 256]*64...
*		+INT([(CURSORX+LEFTSHIFT) MOD 512]/8)
*	BIT = (CURSORX+LEFTSHIFT) MOD 8
*
*	GENCURSOR1 IS ENTRY POINT WITH (A,B) = X POSITION
*		AND TOP OF STACK = Y POSITION
*
GENCURSORBYTE	EQU	*
	LDAA	CURSORY		GET Y POSITION
GENCURSORCHAR	EQU	*	ENTRY TO GENERATE CURSORBYTE FOR CHARACTER DISPLAY
	PSHA			AND PUSH ONTO STACK
	LDAA	CURSORX		GET X POSITION...
	LDAB	CURSORX+1	TO (A,B)
GENCURSOR1	EQU	*	ALTERNATE ENTRY POINT
	ADDB	LEFTSHIFTDIST+1	COMPUTE RAM ADDRESS EQUIVALENT
	ADCA	LEFTSHIFTDIST
	STAB	TEMPB		SAVE DESIRED BIT NUMBER
	LSRA			TAKE MODULUS 512 AND DIVIDE BY 2...
	RORB			LEAVING RESULT IN (B) ONLY
	PULA			FETCH (ROW NUMBER - DOWNSHIFT) MOD 256...
	ADDA	DOWNSHIFTDIST	MULTIPLIED BY 256
	LSRA			DIVIDE BY 4...
	RORB			LEAVING ROW NUMBER * 256/4 (=64) ...
	LSRA			AND COLUMN NUMBER DIVIDED BY 8
	RORB
	ORAA	#SCREENBOTTOM/256	ADD DISPLACEMENT TO SCREEN BASE
	STAA	CURSORBYTE	SAVE POINTER WHERE WE PROMISED
	STAB	CURSORBYTE+1
	LDX	CURSORBYTE	ALSO LOAD IN X
	LDAB	TEMPB		GET BIT NUMBER BACK
	ANDB	#7		= (CURSORX + LEFTSHIFT) MOD 8
	RTS			AND EXIT
*	CLEARLINES -- SUBROUTINE TO CLEAR HORIZONTAL SCREEN LINES
*	(X) POINTS TO 1ST BYTE OF LINE
*	(B) CONTAINS LINE COUNT
*	CLEARS LINES ABOVE AND INCLUDING (X) ON SCREEN
*
CLEARLINES	EQU	*
	STX	TEMPX		SAVE (X) FOR LATER ADD
CLEARLOOP	EQU	*
	CLRA			MAKE A ZERO TO STORE
	STAA	(*-CLEARLOOP-1)/2,X	CLEAR 64 BYTES (1 SCREEN ROW)
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	STAA	(*-CLEARLOOP-1)/2,X
	DECB			ALL ROWS ZEROED ?
	BEQ	CLEARTS		YES, GIVE UP
	LDAA	TEMPX+1		ADVANCE POINTER 1 ROW UP THE SCREEN
	ADDA	#NBROW	...
	STAA	TEMPX+1	...
	BCC	CLEARLDX	GO IF NO CARRY TO UPPER HALF
	INC	TEMPX		(BUMP UPPER HALF OF POINTER)
CLEARLDX	EQU	*
	LDX	TEMPX		RESTORE POINTER TO (X)
	JMP	CLEARLOOP	GO CLEAR ANOTHER ROW

CLEARTS	EQU	*
	RTS
	PAGE	DRAWLINE -- ROUTINE TO DRAW LINE FROM CURSOR TO TARGET
RDRAWLINE	EQU	*	REMOTE ENTRY POINT TO DRAW A LINE
	JSR	GETXY		WAIT FOR TARGET POINT TO ARRIVE
DRAWLINE	EQU	*	ENTERED WITH (X)=TARGETX, (A)=TARGETY
	STX	TARGETX		SAVE THIS, WE NEED IT LATER
	STAA	TARGETY		SAVE TARGETY, TOO.
	SUBA	CURSORY		COMPUTE DELTAY
	STAA	DELTAY+1
	LDAB	#0		DELTAYSIGN = BORROW FROM DELTAY COMPUTATION
	SBCB	#0
	STAB	DELTAYSIGN
	STAB	DELTAY
	LDAA	TARGETX		COMPUTE DELTAX
	LDAB	TARGETX+1
	SUBB	CURSORX+1
	SBCA	CURSORX
	STAA	DELTAX
	STAB	DELTAX+1
	LDAA	#0		COMPUTE DELTAXSIGN
	SBCA	#0		= BORROW OF DELTA X COMPUTATION
	STAA	DELTAXSIGN
	ORAB	DELTAX		NON-ZERO LENGTH LINE ?
	ORAB	DELTAY+1	...
	BEQ	CLEARTS		NO, LINE SEGMENT IS NULL
	LDX	#POINT5		SET UP THE FRACTIONAL PARTS OF CURSORX,CURSORY
	STX	FRACTIONX	FOR AUTOMATIC ROUNDING
	STX	FRACTIONY
DRAWNORMLOOP	EQU	*	LOOP TO NORMALIZE SLOPE OF LINE
	LDAA	DELTAXSIGN	"NORMALIZED" MEANS MAXIMIZE DELTAX, DELTAY...
	EORA	DELTAX		WITHOUT CHANGING LINE SLOPE
	BMI	DRAWLINEBIT	NORMALIZATION COMPLETE
	LDAA	DELTAYSIGN	NORMALIZATION OCCURS...
	EORA	DELTAY		WHEN SIGN OF THE DELTA...
	BMI	DRAWLINEBIT	IS DIFFERENT THAN MSB OF DELTA
	ASL	DELTAX+1	SLOPE NOT YET NORMALIZED...
	ROL	DELTAX		SO DOUBLE BOTH DELTAX AND DELTAY
	ASL	DELTAY+1	WHICH LEAVES SLOPE THE SAME
	ROL	DELTAY		REASON FOR NORMALIZING IS TO SPEED...
	BRA	DRAWNORMLOOP	DDA GENERATION OF DOTS
DRAWLINEBIT	EQU	*	NEW DOT REACHED, TURN ON THE BIT
	JSR	DRAWDOT
DRAWLINEDDA	EQU	*	NOW GENERATE NEXT DOT POSTION BY DDA TECHNIQUE
	LDAA	FRACTIONX	ADD DELTAX TO (CURSORX,FRACTIONX)
	LDAB	FRACTIONX+1	AND DELTAY TO (CURSORY,FRACTIONY)
	ADDB	DELTAX+1	IF CURSOR CHANGES, A NEW DOT IS REACHED
	ADCA	DELTAX		AND WE GO SET THE BIT
	STAB	FRACTIONX+1	OTHERWISE, REPEAT THE DDA LOOP
	STAA	FRACTIONX	THIS LOOP CANNOT OCCUR MORE THAN TWICE...
	LDAA	CURSORX		WITHOUT A NEW POINT BEING GENERATED,
	LDAB	CURSORX+1	SINCE THE DELTAS ARE NORMALIZED...
	ADCB	DELTAXSIGN	WITH RESPECT TO UNITY IN CURSORX, CURSORY
	ADCA	DELTAXSIGN	(I.E., .5 <= DELTA < 1)
	LDX	CURSORX		SAVE OLD CURSORX
	STAA	CURSORX		STORE NEW CURSORX
	STAB	CURSORX+1
	LDAA	FRACTIONY	NOW ADD DELTAY TO (CURSORY,FRACTIONY)
	LDAB	FRACTIONY+1
	ADDB	DELTAY+1	SAME TRICK AS FOR CURSORX COMPUTATION
	ADCA	DELTAY
	STAA	FRACTIONY
	STAB	FRACTIONY+1
	LDAA	CURSORY
	TAB			SAVE OLD CURSORY
	ADCA	DELTAYSIGN	COMPUTE NEW CURSORY
	STAA	CURSORY		AND SAVE IT
	CBA			DID CURSORY CHANGE ?
	BNE	DRAWLINECOMPARE	YES, MIGHT BE END POINT OF LINE
	CPX	CURSORX		DID CURSORX CHANGE ?
	BEQ	DRAWLINEDDA	NO, NEXT PASS WILL CHANGE CURSOR FOR SURE
DRAWLINECOMPARE	EQU	*	CHECK TO SEE IF AT ENDPOINT OF LINE
	LDX	CURSORX		AT TARGET POINT YET ?
	CPX	TARGETX		WELL ?
	BNE	DRAWLINEBIT	NO, GO TURN ON THE BIT
	CMPA	TARGETY		... ?
	BNE	DRAWLINEBIT	NOPE
	RTS			HIT END OF LINE, QUIT HERE !
	PAGE	CHARACTER CURSOR CONTROL
NEWPAGE	EQU	*
	LDX	#SCREENBOTTOM	ZERO THE ENTIRE SCREEN
	LDAB	#SCREENHEIGHT&$FF	(ALL 256 LINES)
	JSR	CLEARLINES
	LDX	#CHARLEFTSIDE	POSITION CURSOR...
	STX	CURSORX		AT TOP LEFT CORNER OF SCREEN
	LDAA	#CHARTOPSIDE
	STAA	CURSORY
	CLR	CURSORON	FORCE RE-DRAWING OF CURSOR...
	CLR	CROSSON		AND CROSSHAIRS
	LDX	#(0-CROSSXFUDGE)&(SCREENWIDTH-1)
	STX	CROSSX		MAKE CROSSHAIRS DISAPPEAR
	LDAA	#(0-CROSSYFUDGE)&(SCREENHEIGHT-1)
	STAA	CROSSY
	RTS			ALL DONE !

RSETCHCURSOR	EQU	*
	JSR	GETDB		WAIT FOR ROW NUMBER TO ARRIVE
	PSHA			SAVE ROW NUMBER
	JSR	GETDB		WAIT FOR COLUMN NUMBER TO ARRIVE
	TAB			COPY TO (B)
	PULA			RESTORE ROW NUMBER
SETCHCURSOR	EQU	*	(A)= ROW, (B)= COLUMN
	IF	CHARHEIGHT=9
	STAA	TEMPA		PREPARE FOR *9
	FIN
	ASLA			REMEMBER, EACH ROW IS CHARHEIGHT DOT LINES
	ASLA			*** THIS CODE NEEDS FIXING IF CHARHEIGHT CHANGES ***
	ASLA
	IF	CHARHEIGHT=9
	ADDA	TEMPA		FINISH *9
	FIN
	NEGA			AND ROW #0 = Y POSITION OF 255
	ADDA	#CHARTOPSIDE-1
	STAA	CURSORY
	TBA			REMEMBER, EACH CHARACTER COLUMN IS 6 DOTS WIDE
	ABA			*2
	ABA			*3 (NOTE: NO OVERFLOW CHECK IS MADE!)
	CLRB			PREPARE FOR *6
	IF	CHARLEFTSIDE=0
	ASLA			*6
	ELSEIF	CHARLEFTSIDE=1
	SEC			+1 TO BIAS LIKE NEWPAGE DOES
	ROLA
	FIN
	ROLB
	STAB	CURSORX		RESULT <= 511 GAURANTEED!!
	STAA	CURSORX+1
	RTS
	PAGE
BACKSPACE	EQU	*	BACKSPACE THE CHARACTER CURSOR AND ERASE
	LDAA	CURSORX		GET CURRENT CURSOR POSITION
	LDAB	CURSORX+1
	SUBB	#CHARWIDTH	BACK UP ONE CHARACTER WIDTH
	SBCA	#0
	BCC	BACKSPACE1	B/ DIDN'T RUN OFF LEFT SIDE OF SCREEN
	CLRA			DID RUN OFF LEFT SIDE, STOP IT AT LEFT SIDE
	LDAB	#CHARLEFTSIDE
BACKSPACE1	EQU	*
	STAA	CURSORX		UPDATE CURSOR POSITION
	STAB	CURSORX+1
	LDX	#CHARWIDTH	NOW STOMP ON THE CHARACTER
	LDAA	#CHARHEIGHT
	JSR	BLANKREGION
CHCURSORLEFT	EQU	*	MOVE CHARACTER CURSOR LEFT
	LDAA	CURSORX		GET CURRENT CURSOR POSITION
	LDAB	CURSORX+1
	SUBB	#CHARWIDTH	MOVE IT LEFT 6 DOT POSITIONS
	SBCA	#0
	BCS	ENDLINE		GO IF RUNS OFF LEFT SIDE OF SCREEN
CHCURSORXSTAB	EQU	*	STORE (A,B) INTO CURSORX
	STAA	CURSORX
	STAB	CURSORX+1
	RTS

CHCURSORRIGHT	EQU	*	MOVE CURSOR ON CHARACTER POSITION TO THE RIGHT
	LDAA	CURSORX		GET THE CURRENT CURSOR
	LDAB	CURSORX+1
	ADDB	#CHARWIDTH	ADVANCE TO RIGHT 6 COLUMNS
	ADCA	#0
	CMPA	#(SCREENWIDTH/CHARWIDTH*CHARWIDTH-CHARWIDTH)/256
	BHI	CHCURSORXMAX	GO IF RUNS OFF RIGHT SIDE OF SCREEN
	BNE	CHCURSORXSTAB	GO STORE UPDATED VALUE IF OK
	CMPB	#(SCREENWIDTH/CHARWIDTH*CHARWIDTH-CHARWIDTH)&$FF
	BLS	CHCURSORXSTAB	GO IF UPDATED VALUE IS OK
CHCURSORXMAX	EQU	*	NEW VALUE FOR CURSOR IS PAST RIGHT SIDE OF SCRREN
	LDX	#(SCREENWIDTH/CHARWIDTH)*CHARWIDTH-CHARWIDTH
	STX	CURSORX		FORCE TO RIGHTMOST SCREEN CHARACTER POSITION
	RTS			AND QUIT

ENDLINE	EQU	*	END OF LINE SEEN ($0D)
	LDX	#CHARLEFTSIDE	MOVE CURSOR TO LEFT SIDE OF SCREEN
	STX	CURSORX		WITHOUT MOVING DOWN A ROW
	RTS			GO PROCESS THE NEXT COMMAND
	PAGE
CHCURSORUP	EQU	*	MOVE CURSOR UP ONE CHARACTER ROW
	LDAB	CURSORY		GET CURRENT ROW POSITION
	ADDB	#CHARHEIGHT	BUMP IT BY ONE CHARACTER ROW
	BCC	CHCURSORYSTAB	GO STORE THE VALUE IF NOT OFF TOP OF SCREEN
	LDAB	#SCREENHEIGHT-1	OFF TOP OF SCREEN, MOVE BACK
CHCURSORYSTAB	EQU	*	STORE (A) INTO CURSORY
	STAB	CURSORY		DO IT !
	RTS

CHCURSORDOWN	EQU	*	MOVE CURSOR DOWN ONE CHARACTER ROW
	LDAB	CURSORY		GET CURRENT ROW POSITION
	SUBB	#CHARHEIGHT	MOVE DOWN SCREEN ONE CHARACTER ROW
	BCS	CHCURSORYMIN	OOPS, RAN OFF BOTTOM OF SCREEN
	CMPB	#CHARHEIGHT-1	NOT QUITE, STILL A CHARACTER ROW LEFT ?
	BCC	CHCURSORYSTAB	YES, GO STORE VALUE
CHCURSORYMIN	EQU	*	RAN OFF BOTTOM OF SCREEN, ROLL SCREEN
	SUBB	#CHARHEIGHT-1+(SCREENHEIGHT-(SCREENHEIGHT/CHARHEIGHT)*CHARHEIGHT)
*				COMPUTE MINUS NUMBER OF LINES TO CLEAR
*
*	ROLLUPCHARLINE -- ROLL SCREEN UP -(B) SCREEN LINES AND CLEAR
*	ADJUSTS DOWNSHIFT
*	SETS CURSORY TO CHARHEIGHT-1
*	(= LOWEST ROW ON SCREEN WHICH DOESN'T CHOP OFF PART OF A ROW AT THE TOP)
*
ROLLUPCHARLINE	EQU	*
	LDAA	DOWNSHIFTDIST	DOWN SHIFT = 0 OR > 127 ?
	BLE	ROLLUPCHARLINE1	YES, CLEAR A SINGLE BLOCK OF LINES
	ABA			NO, COMPUTE NEW DOWN SHIFT
	BPL	ROLLUPCHARLINE2	IF >= 0, GO CLEAR A BLOCK OF SCREEN LINES
	PSHA			SAVE NEW DOWNSHIFT
	SBA			OLD DOWNSHIFT IS # LINES...
	TAB			AT SCREENBOTTOM TO CLEAR
	LDX	#SCREENBOTTOM	GO CLEAR THEM
	JSR	CLEARLINES
	PULB			GET NEW DOWNSHIFT, WHICH IS ALSO...
	CLRA			THE NEGATIVE OF # LINES TO CLEAR AT SCREENTOP
ROLLUPCHARLINE1	EQU	*
	ABA			COMPUTE NEW DOWN SHIFT
ROLLUPCHARLINE2	EQU	*
	JSR	MODIFYDOWNSHIFT	TELL THE HARDWARE ABOUT IT
	LDAA	DOWNSHIFTDIST	WE NEED THIS IN A MOMENT...
	NEGB			CONVERT - LINE COUNT TO + LINE COUNT
	STAB	TEMPA		NUMBER OF SCREEN LINES TO ZERO
	LDAB	#1		COMPUTE ADDRESS OF 1ST LINE TO ZERO
	LSRA			= SCREENBOTTOM + DOWNSHIFT * 64
	RORB			= SCREENBOTTOM + (DOWNSHIFT*256)/4
	RORA
	RORB
	STAB	TEMPB
	LDAB	TEMPA		FETCH # LINES TO CLEAR
	STAA	TEMPA
	LDX	TEMPX		GET ADDRESS OF 1ST LINE TO ZERO
	JSR	CLEARLINES	GO ZERO THEM
	LDAB	#CHARHEIGHT-1+(SCREENHEIGHT-(SCREENHEIGHT/CHARHEIGHT)*CHARHEIGHT)
*				SET NEW CURSOR POSITION...
	STAB	CURSORY		TO TOP OF BOTTOM CHARACTER LINE
	RTS			WHEW! ALL DONE
	PAGE	ASSOCIATE PICTURE
RASSOCIATE	EQU	*
	JSR	CHECKFORUSERRAM	AND DON'T RETURN IF THERE ISN'T ANY!
	JSR	GETDB		NOW GET ASSOCIATION CODE
	ANDA	#$3F		ONLY 64 CODES; IGNORE GARBAGE
	ASLA			TWO BYTES PER ASSOCIATION TABLE ENTRY
	CLRB			FIND SLOT IN ASSOCIATION TABLE
	ADDA	#ASSOCIATIONTBL&$FF
	ADCB	#ASSOCIATIONTBL/256
	STAB	PICTUREP	SAVE POINTER TO PICTURE POINTER IN TABLE
	STAA	PICTUREP+1
	JSR	GETXY		GET PICTURE X AND Y DIMENSIONS
	STX	DELTAX		SAVE PICTURE X SIZE IN DOTS
	STAA	DELTAY		SAVE PICTURE Y DIMENSION
	LDX	PICTUREP	ARE WE GETTING A NEW ASSOCIATION ?
	LDX	0,X		(IS TABLE ENTRY NULL ?)
	BEQ	ASSOCIATEINSERT	YES, JUST GO DO IT
	STX	PICTURE		NO, MUST DUMP OLD PICTURE ASSOCIATION
	JSR	PICTURESIZE	DECIDE HOW BIG OLD PICTURE IS (IN BYTES)
	LDX	#ASSOCIATIONTBL	RELOCATE REST OF ASSOCIATED PICTURE POINTERS
RASSOCL	EQU	*	RELOCATE A POINTER FROM ASSOCIATION TABLE
	LDAA	0,X		GET POINTER TO RELOCATE
	LDAB	1,X
	CMPA	PICTURE		DOES IT POINT INTO REGION WE WILL SHUFFLE DOWN ?
	BHI	RASSOC1		YES, MUST BE RELOCATED
	BNE	RASSOC2		NO, LEAVE IT ALONE
	CMPB	PICTURE+1	(...?)
	BLS	RASSOC2		(NO)
RASSOC1	EQU	*	MUST ADJUST THIS POINTER
	SUBB	TEMPB		SUBTRACT SIZE OF OLD PICTURE FROM POINTER
	SBCA	TEMPA
	STAA	0,X		UPDATE ASSOCIATION TABLE ENTRY
	STAA	1,X
RASSOC2	EQU	*	NOW ADVANCE TO NEXT ASSOCIATION TABLE ENTRY
	INX
	INX
	CPX	#ASSOCTBLEND	DONE WITH ENTIRE TABLE ?
	BNE	RASSOCL		NO, GO RELOCATE ANOTHER POINTER
*	ALL ASSOCATION TABLE POINTERS READJUSTED
*	NOW SHUFFLE DOWN OTHER PICTURES TO RECAPTURE THE SPACE
	LDAA	PICTOP		COMPUTE NEW "TOP OF ASSOCIATED PICTURE REGION"
	LDAB	PICTOP+1	= OLD TOP...
	SUBB	TEMPB		MINUS SIZE OF PICTURE WE ARE DELETING
	SBCA	TEMPA
	STAA	PICTOP		(PICTOP POINTS TO 1ST UNUSED BYTE)
	STAB	PICTOP+1
	LDAA	PICTURE		FORM "COPY FROM..." POINTER
	LDAB	PICTURE+1
	ADDB	TEMPB		= OLD PICTURE ADDRESS...
	ADCA	TEMPA		+ OLD PICTURE SIZE
	STAA	TEMPX
	STAB	TEMPX+1
	LDX	PICTURE		DO WE HAVE TO SHUFFLE ?
	CPX	PICTOP		(DELETING LAST PICTURE ?)
	BEQ	ASSOCIATEINSERT	NO, JUST SKIP SHUFFLE STEP
SHUFFLEPICTURES	EQU	*
	LDX	TEMPX		GET POINTER TO BYTE TO SHUFFLE DOWN
	LDAA	0,X		GET THE BYTE TO SHUFFLE DOWN
	INX			ADJUST THE SHUFFLE DOWN SOURCE POINTER
	STX	TEMPX		AND STORE ITS UPDATED VALUE
	LDX	PICTURE		THIS IS WHERE TO PUT THE SHUFFLED BYTE
	STAA	0,X		DO THE OBVIOUS
	INX			UPDATE THE SHUFFLE TARGET
	STX	PICTURE
	CPX	PICTOP		SHUFFLE COMPLETED ?
	BNE	SHUFFLEPICTURES	LOOP UNTIL DONE
ASSOCIATEINSERT	EQU	*
	LDX	#DELTAX		COMPUTE SIZE OF NEW PICTURE
	JSR	PICTURESIZE
	LDX	PICTUREP	SET ASSOCIATION TABLE ENTRY...
	LDAA	PICTOP		POINTING TO WHERE WE WILL PUT PICTURE BODY
	LDAB	PICTOP+1
	STAA	0,X
	STAB	1,X
	ADDB	TEMPA		COMPUTE NEW TOP OF ASSOCIATED PICTURE REGION
	ADCA	TEMPB
	STAA	PICTOP
	STAB	PICTOP+1
	LDX	0,X		FIND OUT WHERE WE PUT THE PICTURE
	LDAA	DELTAX		COPY PICTURE X AND Y BOUNDS...
	LDAB	DELTAX+1	INTO THE HEADER FOR THE PICTURE
	STAA	PICTXSIZ,X
	STAB	PICTXSIZ+1,X
	LDAA	DELTAY	...
	STAA	PICTYSIZ,X
	INX			BUMP (X) PAST PICTURE HEADER
	INX
ASSOCIATELOOP	EQU	*	COLLECT PICTURE BYTES
	INX			BUMP POINTER TO NEXT AVAILABLE BYTE
	CPX	PICTOP		ALL PICTURE BYTES STORED ?
	BEQ	ASSOCIATERTS	YES, SO EXIT
	STX	PICTURE		NO, SAVE POINTER TO NEXT PICTURE BYTE
	JSR	GETHEXBYTE	GET THE PICTURE BYTE
	LDX	PICTURE		WHERE TO PUT BYTE, I WONDER ?
	STAA	0,X		HOW 'BOUT HERE ?
	BRA	ASSOCIATELOOP	GO STORE ANOTHER

ASSOCIATERTS	EQU	*	THIS IS OUR ACE UP OUR SLEEVE !
	JSR	EATCRLF		EAT COMMAND TERMINATOR
	RTS			DONE ! (WHEW...)
*	PICTURESIZE -- (X) POINTS TO (XSIZE, YSIZE)
*	RETURNS IN (A,B) THE SIZE OF THE PICTURE BLOCK IN BYTES
*
PICTURESIZE	EQU	*
	LDAB	PICTXSIZ,X	GET X SIZE IN DOTS
	LDAA	PICTXSIZ+1,X
	ADDA	#7		ROUND UP TO BYTES (PER ROW OF PICTURE)
	ADCB	#0
	LSRB			= (SIZE IN DOTS + 7) / 8
	RORA
	LSRB			COULD NOT EXCEED 512+7 = 519 DOTS
	RORA
	LSRA
	LDAB	PICTYSIZ,X	MULTIPLY ROW SIZE IN BYTES BY Y SIZE
	BEQ	PICTUREBIAS	GO IF 256 ROWS HIGH
	STAA	TEMPB		SAVE MULTIPLICAND
	CLR	TEMPA
	STAB	MULTIPLIER	SAVE MULTIPLIER
	CLRA			ZERO THE PRODUCT
	CLRB
	ASR	MULTIPLIER	LOOK AT NEXT MULTIPLIER BIT
	BCC	PICTURESHIFT
PICTUREADD	EQU	*
	ADDB	TEMPB		MULTIPLIER BIT = 1, ADD MULTIPLICAND TO PRODUCT
	ADCA	TEMPA
PICTURESHIFT	EQU	*
	ASL	TEMPB		DOUBLE MULTIPLICAND TO COMPENSATE...
	ROL	TEMPA		FOR HALVING MULTIPLIER
	ASR	MULTIPLIER	EXAMINE NEXT MULTIPLIER BIT
	BCS	PICTUREADD
	BNE	PICTURESHIFT
PICTUREBIAS	EQU	*
	ADDB	#PICTBYT	ADD IN BIAS FOR PICTURE HEADER
	ADCA	#0
	STAA	TEMPA		SAVE SIZE OF PICTURE
	STAB	TEMPB
	RTS			AND EXIT
*	MAKESPACE -- ROLLS SCREEN UP AND LEFT FOR OBJECT
*	DIMENSIONS: (X) IS SIZE IN X ($1FF IS MAXIMUM!)
*	(A) IS SIZE IN Y (A=0 --> 256 LINES HIGH)
*	ADJUSTS CURSORX, CURSORY LEFT AND UP APPROPRIATELY...
*	IF SCREEN IS ROLLED
*
MAKESPACE	EQU	*
	STX	DELTAX		FOR CONVENIENCE TO THE CALLING ROUTINES
	STAA	DELTAY
	SEC			COMPUTE - # HORZ LINES LEFT BELOW OBJECT
	SBCA	CURSORY
	BLS	MAKESPACE1	GO IF ZERO OR MORE LINES LEFT
	ADDA	DOWNSHIFTDIST	OOPS, OBJECT WILL RUN OFF BOTTOM OF SCREEN
	JSR	MODIFYDOWNSHIFT	ADJUST THE SCREEN ROLL TO MAKE SPACE
*	NOW CLEAR THE SCREEN SPACE
MAKESPACE1	EQU	*
	LDAA	CURSORX		COMPUTE WHERE CURSORX WILL BE...
	LDAB	CURSORX+1	AFTER DRAWING IS COMPLETE
	ADDB	DELTAX+1	= CURSORX + DELTAX
	ADCA	DELTAX
	CMPA	#(SCREENWIDTH/256)-1	ENOUGH SPACE TO RIGHT ?
	BLS	MAKESPACE2	YES, STORE NEW VALUE FOR X AND QUIT
	SUBB	#(SCREENWIDTH-1)&$FF	COMPUTE LEFT SHIFT DELTA
	SBCA	#(SCREENWIDTH-1)/256
	ADDB	LEFTSHIFTDIST+1	COMPUTE NEW LEFT SHIFT DISTANCE
	ADCA	LEFTSHIFTDIST
	JSR	MODIFYLEFTSHIFT	NOW TELL THE HARDWARE ABOUT IT!
***	THEN CLEAR SCREEN SPACE SHIFTED IN FROM RIGHT SIDE ????
	LDX	#SCREENWIDTH-1
	STX	NEWX		THIS WILL BE CURSORX WHEN WE'RE DONE
	LDAA	#(SCREENWIDTH-1)/256	COMPUTE ADJUSTED CURSORX
	LDAB	#(SCREENWIDTH-1)&$FF	= SCREENWIDTH-1-DELTAX
	SUBB	DELTAX+1
	SBCA	DELTAX
	STAA	CURSORX		THIS IS REALLY CORRECT, DON'T FIDDLE!
	STAB	CURSORX+1
	RTS

MAKESPACE2	EQU	*
	STAA	NEWX		STORE NEW VALUE FOR CURSORX
	STAB	NEWX+1
	RTS
*	DRAWBLANKLINE -- DRAWS A BLANK HORIZONTAL LINE
*	TARGETLEFT POINTS TO 1ST SCREEN BYTE
*	LEFTEND CONTAINS LEFT HAND END OF LINE MASK
*	PWIDTH CONTAINS WIDTH OF LINE IN BYTES
*	RIGHTEND CONTAINS RIGHTMOST MOST BYTE OF LINE
*	IF ONLY ONE BYTE IS TO BE MODIFIED,
*	LEFTEND = BYTE CONTENTS...
*	AND RIGHTEND MUST BE ZERO
*
DRAWBLANKLINE	EQU	*
	LDX	TARGETLEFT	GET POINTER TO LEFTMOST BYTE OF LINE
	STX	TEMPX		SAVE POINTER TO SCREEN BYTE
	LDAA	PWIDTH		GET WIDTH OF HORIZONTAL LINE
	DECA			REMOVE ONE BYTE FOR RIGHT END
	BNE	DRAWBLANK1	B/ NOT SPECIAL ONE BYTE CASE
	INCA			MAKE US FALL THRU LOOP
DRAWBLANK1	EQU	*
	STAA	WIDTHCNT	SAVE IT WHERE WE CAN DECREMENT IT
	LDAA	LEFTEND		GET LEFTMOST BYTE OF LINE
	COMA			MAKE MASK OF BITS TO RETAIN
DRAWBLANKLOOP	EQU	*
	ANDA	0,X		COMBINE (A) WITH SCREEN BYTE
	STAA	0,X
	LDAA	TEMPX+1		NOW FIND NEXT SCREEN BYTE TO MODIFY
	TAB			(COPY TO B)
	INCB			= THIS SCREEN BYTE + 1...
	ANDB	#NBROW-1	IN THE SAME ROW
	ANDA	#-NBROW		RETAIN OLD ROW NUMBER
	ABA			GOT IT!
	STAA	TEMPX+1
	LDX	TEMPX		GET POINTER TO NEXT SCREEN BYTE
	CLRA			GET ALL 0'S PATTERN
	DEC	WIDTHCNT	ALL BYTES IN ROW HIT YET ?
	BNE	DRAWBLANKLOOP	NO
	LDAA	RIGHTEND	GET LAST BYTE BIT PATTERN
	COMA			MAKE MASK OF BITS TO RETAIN
	ANDA	0,X		AND DUMP INTO SCREEN MEMORY
	STAA	0,X
	RTS
	PAGE	BLANK REGION
RBLANKREGION	EQU	*
	JSR	GETXY		GET BOX X AND Y DIMENSIONS
BLANKREGION	EQU	*	(X) CONTAINS BOX X SIZE, (A) CONTAINS Y SIZE
	BSR	BOXEDGE		GO COMPUTE BOX EDGES AND POINTERS
	BEQ	BLANKREGIONXIT	IF X DIMENSION IS ZERO, GET OUT!
BLANKLOOP	EQU	*	DRAW ONE ROW OF A BLANK BOX
	JSR	DRAWBLANKLINE	GO DRAW A BLANK HORIZONTAL LINE
	LDAA	TARGETLEFT	NOW ADVANCE DOWN THE SCREEN A ROW
	LDAB	TARGETLEFT+1
	SUBB	#NBROW
	SBCA	#0
	BMI	BLANK1		GO IF STILL IN SCREEN MEMORY
	LDAA	#SCREENTOP/256	OOPS, GO TO TOP OF PHYSICAL SCREEN MEMORY
BLANK1	EQU	*
	STAA	TARGETLEFT	UPDATE THE BOX LINE LEFT SIDE POINTER
	STAB	TARGETLEFT+1
	DEC	DELTAY		MORE LINES OF BOX TO DRAW ?
	BNE	BLANKLOOP	YES, GO DO ANOTHER
	LDX	NEWX		NOW ADVANCE CURSORX PAST THE BOX
	STX	CURSORX
BLANKREGIONXIT	EQU	*
	RTS			AND QUIT
	PAGE	BOXEDGE SUBROUTINE

*	BOXEDGE -- SUBROUTINE TO COMPUTE POINTERS AND PIECES OF BOXES
*	USED BY SOLIDBOX AND EMPTYBOX
*
BOXEDGE	EQU	*
	JSR	MAKESPACE	MAKE ENOUGH ROOM ON SCREEN FOR BOX
	JSR	GENCURSORBYTE	FIND UPPER LEFT HAND CORNER OF BOX
	STX	TARGETLEFT	AND REMEMBER LEFT EDGE OF BOX
	PSHB			SAVE BEGINNING BIT NUMBER
	CLRA			COMPUTE LEFT MOST BYTE OF BOX
	ADDB	#BITMSK&$FF	= SPECIFIED BIT WITH ALL BITS TO ITS...
	ADCA	#BITMSK/256	RIGHT TURNED ON
	STAA	TEMPX		FETCH THE SPECIFIED BIT PATTERN
	STAB	TEMPX+1
	LDX	TEMPX
	LDAA	0,X
	STAA	LEFTCOLUMN	THIS IS FOR EMPTY BOX SIDES
	ASLA			TURN ON ALL BITS TO RIGHT OF SPECIFIED BIT
	DECA			NOW WE HAVE LEFTMOST BYTE OF SOLIDBOX
	STAA	LEFTEND		WE MAY NEED THIS A LOT...
	LDAB	NEWX+1		NOW WE MUST FIND RIGHTMOST BYTE OF EMPTYBOX
	DECB			SINCE BOX DOES NOT ACTUALLY EXTEND TO NEWX
	ADDB	LEFTSHIFTDIST+1	(COMPUTE BIT NUMBER...)
	ANDB	#7		(JUST LIKE GENCURSORBYTE DOES IT)
	CLRA			SIMILAR TO ABOVE SEQUENCE...
	ADDB	#BITMSK&$FF	BUT WE WANT ALL BITS TO LEFT...
	ADCA	#BITMSK/256	OF SPECIFIED BITS TO BE ON
	STAA	TEMPX
	STAB	TEMPX+1
	LDX	TEMPX
	LDAA	0,X
	STAA	RIGHTCOLUMN	THIS IS RIGHT SIDE OF EMPTYBOX
	DECA			TURN ON ALL THE BITS TO THE LEFT...
	COMA			OF THE SPECIFIED BIT
	STAA	RIGHTEND	NOW WE HAVE RIGHTMOST BYTE OF SOLIDBOX
	PULB			COMPUTE WIDTH OF BOX IN BYTES
	CLRA			= CEILING(DELTAX + LEFT BIT # )
	ADDB	#7		(CEILING WE GET BY ROUNDING UP)
	ADDB	DELTAX+1
	ADCA	DELTAX		(THIS CAN BE AS LARGE AS 512+7+7)
	LSRA			(DIVIDE NUMBER OF BITS BY 8)
	RORB
	LSRA
	RORB
	LSRB			(B) = WIDTH OF BOX IN BYTES
	STAB	PWIDTH		SAVE WIDTH OF BOX
	CMPB	#1		IS BOX ONE BYTE WIDE ?
	BNE	BOXIT		NO, DON'T HAVE TO MASK ENDS TOGETHER
	LDAA	LEFTEND		YES, BOX <= 1 BYTE WIDE...
	ANDA	RIGHTEND	SO LEFT AND RIGHT ENDS OVERLAP
	STAA	LEFTEND		SET LEFT END MASK TO BOX PATTERN
	CLR	RIGHTEND	PREVENT THIS GUY FROM MUCKING UP THE WORKS
BOXIT	TSTB			LOOK AT BYTE WIDTH OF BOX
	RTS			AND EXIT WITH CC BIT Z SET
	PAGE	DRAW SOLID BOX
RSOLIDBOX	EQU	*
	JSR	GETXY		GET BOX DIMENSIONS
SOLIDBOX	EQU	*	(X) CONTAINS BOX X SIZE, (A) CONTAINS Y SIZE
	BSR	BOXEDGE		GO COMPUTE BOX EDGES AND POINTERS
	BEQ	SOLIDBOXIT	IF X DIMENSION IS ZERO, GET OUT!
SOLIDLOOP	EQU	*	DRAW ONE ROW OF A SOLID BOX
	JSR	DRAWHORZLINE	GO DRAW A SOLID HORIZONTAL LINE
	LDAA	TARGETLEFT	NOW ADVANCE DOWN THE SCREEN A ROW
	LDAB	TARGETLEFT+1
	SUBB	#NBROW
	SBCA	#0
	BMI	SOLID1		GO IF STILL IN SCREEN MEMORY
	LDAA	#SCREENTOP/256	OOPS, GO TO TOP OF PHYSICAL SCREEN MEMORY
SOLID1	EQU	*
	STAA	TARGETLEFT	UPDATE THE BOX LINE LEFT SIDE POINTER
	STAB	TARGETLEFT+1
	DEC	DELTAY		MORE LINES OF BOX TO DRAW ?
	BNE	SOLIDLOOP	YES, GO DO ANOTHER
	LDX	NEWX		NOW ADVANCE CURSORX PAST THE BOX
	STX	CURSORX
SOLIDBOXIT	EQU	*
	RTS			AND QUIT
	PAGE	DRAW EMPTY BOX
REMPTYBOX	EQU	*
	JSR	GETXY		GET BOX DIMENSIONS
EMPTYBOX	EQU	*	(X) CONTAINS BOX X SIZE, (A) CONTAINS Y SIZE
	CPX	#1		A ONE BIT WIDE BOX ?
	BEQ	SOLIDBOX	SIGH... THIS WILL WORK BETTER AS SOLIDBOX
	JSR	BOXEDGE		COMPUTE BOX EDGES AND POINTERS
	BEQ	EMPTYBOXIT	QUIT IF BOX IS ZERO BYTES WIDE
	JSR	DRAWHORZLINE	GO DRAW TOP OF BOX
	DEC	DELTAY		IS BOX ONLY ONE ROW DEEP ?
	BEQ	EMPTYBOXIT	YES, GET OUT NOW !
EMPTYLOOP	EQU	*
	LDAA	TARGETLEFT	ADVANCE POINTER DOWN ONE SCREEN LINE
	LDAB	TARGETLEFT+1
	SUBB	#NBROW
	SBCA	#0
	BMI	EMPTY1		GO IF STILL IN SCREEN MEMORY
	LDAA	#SCREENTOP/256	NO, GO CIRCULARLY TO TOP OF SCREEN
EMPTY1	EQU	*
	STAA	TARGETLEFT
	STAB	TARGETLEFT+1
	DEC	DELTAY		ON LAST LINE OF BOX ?
	BEQ	EMPTYLAST	YES, GO DRAW A SOLID LINE
	STAA	TARGETRIGHT	NO, COMPUTE RIGHT END POINTER
	TBA			= LEFT END...
	ADDB	PWIDTH		+ PICTUREWIDTH - 1
	DECB			IN SAME ROW
	ANDB	#NBROW-1	KEEP DISPLACEMENT INTO ROW
	ANDA	#-NBROW		EXTRACT ROW BITS
	ABA			RE-COMBINE
	STAA	TARGETRIGHT+1
	LDX	TARGETLEFT	INSERT LEFT SIDE OF BOX
	LDAA	LEFTCOLUMN
	EORA	0,X
	STAA	0,X
	LDX	TARGETRIGHT	INSERT RIGHT SIDE OF BOX
	LDAA	RIGHTCOLUMN
	EORA	0,X
	STAA	0,X
	BRA	EMPTYLOOP
EMPTYLAST	EQU	*
	JSR	DRAWHORZLINE	GO DRAW A SOLID HORIZONTAL LINE
EMPTYBOXIT	EQU	*
	LDX	NEWX		NOW ADVANCE CURSORX PAST THE BOX
	STX	CURSORX
	RTS			AND QUIT
*	DRAWHORZLINE -- DRAWS A HORIZONTAL LINE
*	TARGETLEFT POINTS TO 1ST SCREEN BYTE
*	LEFTEND CONTAINS LEFT HAND END OF LINE MASK
*	PWIDTH CONTAINS WIDTH OF LINE IN BYTES
*	RIGHTEND CONTAINS RIGHTMOST MOST BYTE OF LINE
*	IF ONLY ONE BYTE IS TO BE MODIFIED,
*	LEFTEND = BYTE CONTENTS...
*	AND RIGHTEND MUST BE ZERO
*
DRAWHORZLINE	EQU	*
	LDX	TARGETLEFT	GET POINTER TO LEFTMOST BYTE OF LINE
	STX	TEMPX		SAVE POINTER TO SCREEN BYTE
	LDAA	PWIDTH		GET WIDTH OF HORIZONTAL LINE
	DECA			REMOVE ONE BYTE FOR RIGHT END
	BNE	DRAWHORZ1	B/ NOT SPECIAL ONE BYTE CASE
	INCA			MAKE US FALL THRU LOOP
DRAWHORZ1	EQU	*
	STAA	WIDTHCNT	SAVE IT WHERE WE CAN DECREMENT IT
	LDAA	LEFTEND		GET LEFTMOST BYTE OF LINE
DRAWHORZLOOP	EQU	*
	JSR	WRITEATOSCREEN	COMBINE (A) WITH SCREEN BYTE
	LDAA	TEMPX+1		NOW FIND NEXT SCREEN BYTE TO MODIFY
	TAB			(COPY TO B)
	INCB			= THIS SCREEN BYTE + 1...
	ANDB	#NBROW-1	IN THE SAME ROW
	ANDA	#-NBROW		RETAIN OLD ROW NUMBER
	ABA			GOT IT!
	STAA	TEMPX+1
	LDX	TEMPX		GET POINTER TO NEXT SCREEN BYTE
	LDAA	#%11111111	GET ALL 1'S PATTERN
	DEC	WIDTHCNT	ALL BYTES IN ROW HIT YET ?
	BNE	DRAWHORZLOOP	NO
	LDAA	RIGHTEND	GET LAST BYTE BIT PATTERN
	JSR	WRITEATOSCREEN	AND DUMP INTO SCREEN MEMORY
	RTS
	PAGE	READ CURSOR AND CROSSHAIRS
READCROSSHAIRS	EQU	*	LOCAL READ CROSS HAIRS
	LDAA	CROSSX		GET CROSSX AND ADJUST PROPERLY
	LDAB	CROSSX+1	(CROSSX IS ACTUALLY UPPER LEFT CORNER OF CROSSHAIRS PICTURE)
	ADDB	#CROSSXFUDGE
	ADCA	#0
	ANDA	#(SCREENWIDTH-1)/256
	STAA	TEMPX
	STAB	TEMPX+1
	LDX	TEMPX
	LDAA	CROSSY		ALSO GET CROSS Y POSTION
	ADDA	#CROSSYFUDGE
	RTS

RREADCROSSHAIRS	EQU	*	REMOTE READ CROSS HAIRS
	LDAA	CROSSX		GRAB CROSS X POSITION
	LDAB	CROSSX+1
	ADDB	#CROSSXFUDGE	ADJUST SO RESULTING VALUE IS CENTER DOT OF CROSSHAIRS
	ADCA	#0
	ANDA	#(SCREENWIDTH-1)/256
	BSR	SENDVALUE	AND OUTPUT TO REMOTE HOST
	JSR	SENDCOMMA	SEND ',' TO SEPERATE X FROM Y VALUE
	LDAB	CROSSY		NOW SEND Y POSITION
	ADDB	#CROSSYFUDGE
RREAD1	CLRA
	BSR	SENDVALUE	SEND Y VALUE TO HOST
	LDAA	#CR
	JSR	SENDCHAR
	RTS
	PAGE
RREADCURSORXY	EQU	*	REMOTE READ CURSOR
	LDAA	CURSORX		GET CURSOR X POSITION
	LDAB	CURSORX+1
	BSR	SENDVALUE
	JSR	SENDCOMMA	OUTPUT A SEPERATOR
	LDAB	CURSORY		GET CURSOR Y POSITION
	BRA	RREAD1		AND SEND TO HOST

READCURSORXY	EQU	*	LOCAL READ CURSOR
	LDX	CURSORX		GET CURSOR X POSITION
	LDAA	CURSORY		AND CURSOR Y POSTION
	RTS
	PAGE	SEND VALUE TO REMOTE HOST
SENDVALUE	EQU	*
	STAA	VALUE		STORE THE VALUE TO BE SENT
	STAB	VALUE+1
	LDX	#ACC		CONVERT TO BCD DIGIT STRING IN ACC
	CLR	0,X		ZERO OUT THE ACCUMULATOR
	CLR	1,X
	CLR	2,X
	LDAB	#16		16 BITS OF VALUE TO CONVERT
SENDCONVERT	EQU	*	FOR EACH BIT OF VALUE...
	ASL	VALUE+1		SHIFT BIT OUT OF VALUE
	ROL	VALUE
	LDAA	2,X		LET ACC=ACC*2+BIT FROM VALUE
	ADCA	2,X		BUT WE DOUBLE ACC AS A DECIMAL NUMBER!
	DAA
	STAA	2,X
	LDAA	1,X
	ADCA	1,X
	DAA
	STAA	1,X
	LDAA	0,X
	ADCA	0,X
	DAA
	STAA	0,X		NO OVERFLOW POSSIBLE SINCE VALUE<=65535
	DECB			ALL BITS PROCESSED YET ?
	BNE	SENDCONVERT	B/ NO, CONVERT ANOTHER
	LDAB	#5		YES, NOW OUTPUT THE DIGITS
	STAB	COUNT		(ALL 5 OF THEM!)
SENDDIGIT	EQU	*
	LDAA	0,X		GET DIGIT TO SEND
	ANDA	#$F		IGNORE DIGIT IN UPPER HALF
	ADDA	#'0		CONVERT TO ASCII
	JSR	SENDCHAR	SEND IT TO REMOTE HOST
	BSR	ASLACC		LEFT SHIFT ACCUMULATOR 1 BIT...
	BSR	ASLACC		4 TIMES...
	BSR	ASLACC		= MULTIPLY BY 10
	BSR	ASLACC
	DEC	COUNT		MORE DIGITS TO SEND ?
	BNE	SENDDIGIT	B/ YES
	RTS

ASLACC	LDX	#ACC		GET ADDRESS OF ACCUMULATOR
	ASL	2,X
	ROL	1,X
	ROL	0,X
	RTS
	PAGE	CROSSHAIR AND CURSOR PICTURES

CURSOR	EQU	*
	FDB	6		X SIZE
	FCB	8		Y SIZE
	FCB	%11111100	DOT MATRIX
	FCB	%11111100
	FCB	%11111100
	FCB	%11111100
	FCB	%11111100
	FCB	%11111100
	FCB	%11111100
	FCB	%11111100

CROSSHAIRS	EQU	*
	FDB	7		X SIZE
	FCB	5		Y SIZE
	FCB	%00010000	DOT MATRIX
	FCB	%00010000
	FCB	%11111110
	FCB	%00010000
	FCB	%00010000
	PAGE
ERASECURSOR	EQU	*
	LDAA	CURSORON	IS CURSOR DISPLAYED ?
	BEQ	ERASERTS	NO, SO EXIT
	CLR	CURSORON	YES, MARK AS UNDISPLAYED
	LDAA	#CURSORX/256	GET POINTER TO CURSORX AND Y...
	LDAB	#CURSORX&$FF	TO (A,B0)
	LDX	#CURSOR		AND GO DRAW IT ON TOP OF ITSELF
	BRA	SPLAT		WHICH ERASES IT

ERASERTS	EQU	*
	RTS

ERASECROSS	EQU	*
	LDAA	CROSSON		ARE CROSSHAIRS DISPLAYED ?
	BEQ	ERASERTS	NO, SO EXIT
	CLR	CROSSON		YES, MAKE AS UNDISPLAYED
	LDAA	#CROSSX/256	GET POINTER TO CROSS X AND Y...
	LDAB	#CROSSX&$FF	TO (A,B)
	LDX	#CROSSHAIRS	GET POINTER TO PICTURE OF CROSS HAIRS
SPLAT	EQU	*		DRAW PICTURE (X) IGNORING EDGES OF SCREEN
*				(A,B) POINT TO (X,Y) POSITION ON SCREEN
	STAA	TEMPA		SAVE POINTER TO (X,Y) POSITION
	STAB	TEMPB
	STX	PICTURE		SAVE POINTER TO PICTURE
	LDAA	PICTYSIZ,X	GET Y PICTURE SIZE
	STAA	DELTAY		AND SAVE IT
	LDX	PICTXSIZ,X	GET X PICTURE SIZE
	STX	DELTAX		AND SAVE IT
	BSR	SPLAT2		PUSH RETURN ADDRESS ON STACK FIRST
	STX	TARGETLEFT	TO BE COMPATIBLE WITH DRAWPICTURE
	JMP	SPLAT1		GO FINISH DRAWING THE PICTURE

SPLAT2	LDX	TEMPX		GET POINTER TO (X,Y) POSITION
	LDAA	PICTYSIZ,X	GET Y POSITION
	PSHA			PUSH FOR GENCURSORBYTE ENTRY
	LDAA	PICTXSIZ,X		GET X POSITION TO (A,B)
	LDAB	PICTXSIZ+1,X
	JMP	GENCURSOR1	GO COMPUTE SCREEN ADDRESS
	PAGE	DOWNLOAD FROM HOST

*	IF THIS ROUTINE IS NOT SMART ENOUGH,...
*	THE USER CAN DOWNLOAD HIS OWN LOADER.  NUFF SAID?

RDOWNLOAD	EQU	*	DOWN LOAD MIKBUG FORMAT RECORDS
	JSR	GETDB		GET A DATA BYTE
	CMPA	#'S		RECORD START ?
	BNE	RDOWNLOAD	NO, IGNORE IT
	JSR	GETDB		YES, GET RECORD TYPE
	CMPA	#'1		A DATA RECORD ?
	BNE	DOWNS9		NO, MUST BE END RECORD
	BSR	GETHEXBYTE	GET BYTE COUNT OF RECORD
	STAA	CKSUM		SAVE AS INITIAL CHECKSUM VALUE
	SUBA	#2		2 BYTES ARE USED FOR ADDRESS
	STAA	COUNT		USE REST AS DATA BYTES
	BSR	GETHEXBYTE	GET LEFT HAND ADDRESS BYTE
	STAA	ADDRESS
	BSR	GETHEXBYTE	GET RIGHT HAND ADDRESS BYTE
	STAA	ADDRESS+1
DOWNLOOP	EQU	*
	BSR	GETHEXBYTE	GET A (POTENTIAL) DATA BYTE
	DEC	COUNT		LAST BYTE OF RECORD ?
	BEQ	DOWNCK		YES, GO CHECK CHECKSUM
	LDX	ADDRESS		NO, GRAB ADDRESS
	STAA	0,X		AND STORE THE DATA BYTE
	INX			BUMP ADDRESS
	STX	ADDRESS		AND SAVE FOR NEXT ROUND
	BRA	DOWNLOOP

DOWNCK	EQU	*		PROCESS CHECKSUM BYTE
	ADDA	CKSUM		ADD CHECKSUM BYTE TO CHECKSUM
	INCA			THIS SHOULD BE ZERO
	BEQ	RDOWNLOAD	IT IS, GO LOAD ANOTHER RECORD
	LDAA	#'?		NOPE, GET A QUESTION MARK
	JSR	DISPLAYCHAR	AND POKE IT TO THE SCREEN
	BRA	RDOWNLOAD	GO LOAD ANOTHER RECORD ANYWAY
DOWNS9	RTS			I HOPE THE CHARACTER WAS A '9'
	PAGE
GETHEXBYTE	EQU	*	COLLECT 2 HEX DIGITS, COMBINE TO GET BYTE
	LDX	#RETURN		NO RECOVERY IF BAD CHARACTER
GETHEXBYTEX	EQU	*
	STX	PARAMETERERROR
	BSR	GETHEXDIG	GET 1ST HEX DIGIT
	ASLA			LEFT SHIFT 4 PLACES
	ASLA
	ASLA
	ASLA
	PSHA			AND SAVE
	BSR	GETHEXDIG	GET 2ND HEX DIGIT
	PULB			GET 1ST DIGIT SHIFT BACK
	ABA			COMBINE TO OBTAIN BYTE
	TAB			MAKE COPY
	ADDB	CKSUM		AND ADD TO CHECKSUM
	STAB	CKSUM
	RTS

GETHEXDIG	EQU	*	COLLECT NEXT CHARACTER AND CONVERT TO HEX
	JSR	GETDB		GET A DATA BYTE
	BSR	ISDIGIT		'0-'9 ?
	BCS	HEXRTS		B/ YES, EXIT WITH BINARY EQUIVALENT
	CMPA	#'A		NO, 'A-'F ?
	BCS	BADPARAMETER	B/ NO, ILLEGAL CHARACTER
	SUBA	#'A-10	MUST BE 'A-'F, DO CONVERSION
	CMPA	#$10		REASONABLE RESULT ?
	BCC	BADPARAMETER		B/ NO, NOT LEGAL HEX CHARACTER
HEXRTS	RTS
	PAGE
*	CHECKFORUSERRAM-- CHECK TO MAKE SURE PICTURE ASSOCIATION IS LEGAL
*
CHECKFORUSERRAM	EQU	*
	LDAB	#$AA		THIS CODE CAN'T BE STORED BY NONEXISTENT MEMORY
	STAB	0		WILL LOCATION ZERO STORE IT ?
	CMPB	0		... ?
	BEQ	HEXRTS		B/ YES, PICTURE ASSOCIATION COMMMAND IS LEGAL
BADPARAMETER	EQU	*	THIS IS WHERE TO GO IF...
	LDS	#DISPSTACK	RESET THE STACK TO A CLEAN STACK
	LDX	PARAMETERERROR	GET ADDRESS OF CLEAN-UP ROUTINE
	JSR	0,X		CALL ERROR CLEAN-UP
BADCOMMAND	EQU	*
	INC	CHARREJECT	BARF UP THE CHARACTER
	LDAA	#'?		BITCH ABOUT COMMAND AND SHOW REST OF IT
	JSR	DISPLAYCHAR
	JMP	REMOTELOOP	GIVE UP AND START ANOTHER COMMAND STREAM
*
*	ISDIGIT-- IS (A) A DIGIT ?
*	RETURNS BINARY EQUIVALENT IN (A) WITH CARRY SET IF YES
*	ELSE RETURNS CHARACTER UNTOUCHED WITH CARRY RESET
*
ISDIGIT	EQU	*
	CMPA	#'9+1		TOO BIG TO BE A LEGAL DIGIT ?
	BCC	ISRTS		B/ YES
	SUBA	#'0		NO, ASSUME A DIGIT AND DO CONVERSION
	BCS	ISNOT		B/ WE LOSE AGAIN, BUCKWHEAT...
	SEC			FLAG 'LEGAL DIGIT!'
	RTS
ISNOT	ADDA	#'0		CHANGE BACK TO ORIGINAL FORM
	CLC
ISRTS	RTS
	PAGE	GET INTEGER FROM INPUT STREAM
*	GETINTEGER-- CONVERT INPUT STREAM TO 16 BIT VALUE IN (A,B) AND (X)
*	EATS $SPACE ('+'/.EMPTY) <DIGIT> $<DIGIT> ('.' $<DIGIT> / .EMPTY)
*	IGNORES DECIMAL POINT AND FRACTION BITS
*	GETINTEGERX SETS UP PARAMETER ERROR CLEANUP ROUTINE

GETINTEGER	EQU	*
	LDX	#RETURN		SELECT DEFAULT "NO CLEANUP" AFTER ERROR
GETINTEGERX	EQU	*
	STX	PARAMETERERROR	POINTER TO ROUTINE TO CLEAN UP AFTER PARAMETER ERROR
	LDX	#0		ZERO OUT THE COLLECTED VALUE
	STX	VALUE
SKIPBLANKS	EQU	*
	JSR	GETDB		GET 1ST BYTE OF VALUE
	CMPA	#$20		A SPACE ?
	BEQ	SKIPBLANKS	OOOH, THAT FELT GOOD... DO IT AGAIN!
	CMPA	#'+		LEADING PLUS ?
	BNE	CHECKDIGIT	NO, MUST BE 1ST DIGIT
	JSR	GETDB		GET 1ST DIGIT
CHECKDIGIT	EQU	*
	BSR	ISDIGIT		MAKE SURE FIRST DIGIT REALLY IS A DIGIT
	BCS	PROCESSDIGIT	B/ IT IS, GO PROCESS
	BRA	BADPARAMETER

GETNEXTDIGIT	EQU	*
	JSR	GETDB		GET NEXT DIGIT OF VALUE
	BSR	ISDIGIT		IS IT A DIGIT ?
	BCC	SCANTAIL1	NO, MUST BE END OF NUMBER OR DECIMAL PART
PROCESSDIGIT	EQU	*
	PSHA			SAVE DIGIT
	LDX	#VALUE		CHEAT TO SAVE BYTES
	LDAA	0,X		MULTIPLY OLD VALUE BY 10
	LDAB	1,X
	ASL	1,X		*2
	ROL	0,X
	ASL	1,X		*4
	ROL	0,X
	ADDB	1,X		*5
	ADCA	0,X
	ASLB			*10
	ROLA
	TSX			+ DIGIT
	ADDB	0,X
	ADCA	#0
	INS			(POP DIGIT OFF STACK)
	STAA	VALUE
	STAB	VALUE+1
	BRA	GETNEXTDIGIT

SCANTAIL1	EQU	*	DID WE ENCOUNTER...
	CMPA	#'.		A DECIMAL POINT ?
	BNE	SCANTAIL2	B/ NO, END OF NUMBER
SCANTAIL	EQU	*
	JSR	GETDB		EAT REST OF DECIMALS PART
	BSR	ISDIGIT		NO, TRY FOR DIGIT
	BCS	SCANTAIL	B/ EAT THEM TOO
SCANTAIL2	EQU	*
	INC	CHARREJECT	THIS MIGHT BE NEXT COMMAND BYTE
	LDAA	VALUE		ALL DONE, GET THE VALUE
	LDAB	VALUE+1
	LDX	VALUE
	RTS
	PAGE	BACKGROUND AND BAUDRATE CONTROL

BACKTOGGLE	EQU	*	TOGLE BACKGROUND COLOR
	LDAA	DOWNSHIFTPIA	GET PIA CONTENTS
	EORA	#VIDEOINVERT	TOGGLE THE BACKGROUND BIT
	SK2			SKIP TO STAA
BACKBLACK	EQU	*	SET BACKGROUND TO BLACK
	LDAA	#BLACKBGD+DOWNSHIFTCW	GET THE PROPER COLOR
	SK2			AND SKIP TO STAA
BACKWHITE	EQU	*	SET BACKGROUND TO WHITE
	LDAA	#WHITEBGD+DOWNSHIFTCW	GET THE PROPER COLOR
	STAA	DOWNSHIFTPIA	STORE BACKGROUND COLOR INTO PIA
	RTS

RSETBAUDRATE	EQU	*	SET BAUD RATE TO 2ND CHARACTER
	JSR	GETDB		GET BAUD RATE INDEX NUMBER
SETBAUDRATE	EQU	*	SET BAUD RATE INDEX TO (A)
	ANDA	#JOYDDR		MASK TO 4 BITS
	STAA	BAUDRATE	STORE INTO PIA
	RTS
*
*	DUMPMODE -- DUMP CHARACTER STREAM TO CRT W/O EXECUTING GRAPHICS
*	CONTROL CHARACTERS DISPLAYED AS "^ LETTER"
*	EXIT VIA TERMINAL "RESET"
*
DUMPCHAR	EQU	*
	JSR	DISPLAYCHAR	PUT CHARACTER ON SCREEN
DUMPMODE	EQU	*
	JSR	GETDBLINK	BLINK CURSOR, LET CROSSHAIRS MOVE
	CMPA	#$1F		A CONTROL CHARACTER ?
	BHI	DUMPCHAR	B/ NO, GO DISPLAY AS IS
	PSHA			YES, SAVE IT
	LDAA	#'^		GET DISPLAY PREAMBLE
	JSR	DISPLAYCHAR	AND DISPLAY THE PREAMBLE
	PULA			GET CHARACTER BACK
	ADDA	#'A-1		CONTROL-A IS PRINTED AS "^A", ETC.
	BRA	DUMPCHAR	GO DISPLAY THE CONTROL CHARACTER
	PAGE	WRITE MODE SETUP ROUTINES
SELECTEORMODE	EQU	*
	BSR	SETUPWRITE	USE RETURN ADDRESS AS POINTER
	EORA	0,X		ACTUAL SUBROUTINE TO USE FOR "WRITEATOSCREEN"
	STAA	0,X
	RTS

SELECTIORMODE	EQU	*
	BSR	SETUPWRITE
	ORAA	0,X
	STAA	0,X
	RTS

SELECTANDMODE	EQU	*
	BSR	SETUPWRITE
	COMA
	ANDA	0,X
	STAA	0,X
	RTS

SETUPWRITE	EQU	*
	LDX	#WRITEATOSCREEN	PLACE TO COPY IN-LINE CODE TO..
	STX	ADDRESS
	TSX			POINTER TO SUBROUTINE CODE TO COPY
	LDX	0,X
	INS			POP RETURN ADDRESS OFF STACK
	INS
	LDAB	#SETUPWRITE-SELECTANDMODE-2
SETUPWRITEL	EQU	*
	LDAA	0,X		GET SUBROUTINE BYTE
	INX			BUMP POINTER TO SUBROUTINE BYTES
	STX	TEMPX
	LDX	ADDRESS		ADDRESS OF TARGET AREA FOR BYTE
	STAA	0,X		COPY THE BYTE
	INX			BUMP TARGET POINTER
	STX	ADDRESS
	LDX	TEMPX		IN CASE WE GO AROUND AGAIN
	DECB			# BYTES LEFT TO COPY
	BNE	SETUPWRITEL
	RTS			ALL DONE, GET OUT!
	PAGE	RESTART CODE

RESTART	EQU	*		THIS IS WHERE WE POWER UP!
	NOP			MAKE SURE INTERRUPTS ARE OFF!
	SEI
	LDS	#SSTK		GET A GOOD STACK POINTER
	LDAA	#DOWNSHIFTDDR	SET DOWNSHIFT PIA DATA DIRECTION BITS
	STAA	DOWNSHIFTDATA	(PIA IS IN DDRA MODE DUE TO RESET PULSE)
	LDAA	#BGDCA2+BLACKBGD+DATAACCESS+NMICA1	START REFRESH INTERRUPT *** NOW ***
	STAA	DOWNSHIFTPIA
	LDX	#SSTK-7+1	ZERO ALL THE WORLD (EXCEPT OUR STACK!)
RESTARL	DEX			FIND NEXT BYTE
	CLR	0,X		GOODBYE, BYTE!
	CPX	#SCREENBOTTOM	HIT BOTTOM OF SCREEN YET ?
	BNE	RESTARL		LOOP TILL EVERYBODY BUT LOC 0 IS HIT
	LDX	#INITLIST	GET ADDRESS OF BYTES TO INITIALIZE
REST1	STX	TEMPX		SAVE ADDRESS OF THIS LIST ENTRY
	LDAA	IDATA,X		GET DATA BYTE
	LDX	IADDR,X		AND ADDRESS TO STORE BYTE AT
	BEQ	REST3		OOPS, END OF SINGLE BYTE INIT LIST HIT...
	STAA	0,X		STORE DATA BYTE AT ADDRESS SPECIFIED
	LDX	TEMPX		GET LIST ENTRY ADDRESS
	INX			BUMP TO NEXT LIST ENTRY
	INX
	INX
	BRA	REST1		AND GO PROCESS IT

REST2	STX	TEMPX		SAVE ADDRESS OF THIS 2 BYTE LIST ENTRY
	LDAA	IDATA,X		GET 2 DATA BYTES TO INITIALIZE WITH
	LDAB	IDATA+1,X
	LDX	IADDR,X		GET ADDRESS WHERE DATA BYTES ARE TO GO
	BEQ	RESTGO		HIT END OF INIT LIST, GRAPHBOX IS READY!
	STAA	0,X		STORE 2 BYTES OF DATA AT ADDRESS
	STAB	1,X
REST3	LDX	TEMPX		GET ADDRESS OF LIST ENTRY
	INX			BUMP TO NEXT LIST ENTRY
	INX
	INX
	INX
	BRA	REST2		AND GO PROCESS NEXT LIST ENTRY
	PAGE
RESTGO	JSR	SELECTEORMODE	DEFAULT SCREEN WRITE MODE
	JSR	MODIFYDOWNSHIFT	MAKE HARDWARE DOWN- AND LEFTSHIFT REGISTERS...
	JSR	MODIFYLEFTSHIFT	MATCH THE LOGICAL LEFT AND DOWN SHIFT VALUES
	LDAA	KEYDATA		DITCH ANY POSSIBLE INTERRUPT REQUEST
	LDX	#BAUDREQUEST	OUTPUT MESSAGE ASKING FOR BAUD RATE
BAUDASKL	EQU	*
	LDAA	0,X		GET CHARACTER
	BEQ	RESTKW		B/ END OF MESSAGE, GO GET BAUD RATE
	INX			BUMP MESSAGE SCAN POINTER
	STX	ADDRESS		SAVE IT SOMEPLACE THAT'S UNBUSY NOW
	JSR	DISPLAYCHAR	SHOW IT TO OPERATOR
	LDX	ADDRESS		GRAB OUR ACE IN THE HOLE
	BRA	BAUDASKL	AND GO OUTPUT ANOTHER CARD

RESTKW	LDAA	KEYPIA		NOW WAIT FOR USER TO ENTER BAUD RATE
	BITA	#KEYSENSE	KEYBOARD RECEIVED ANY DATA ?
	BEQ	RESTKW		NO, LOOP UNTIL KEY HIT
	LDAA	KEYDATA		YES, GET KEY STROKE VALUE
	JSR	SETBAUDRATE	GO INSERT BAUDRATE INTO PIA
	JSR	NEWPAGE		DUMP THE BAUD RATE REQUEST
	LDAA	#XON		TELL THE HOST COMPUTER WE'RE ALIVE!
	JSR	SENDCHAR
	JMP	SCHEDULER

BAUDREQUEST	EQU	*
	FCC	"Enter Baud Rate: "
	FCB	0
	PAGE	INITIALIZATION LIST
INITLIST	EQU	*	USED TO INITIALIZE AFTER RESTART
*	FORMAT:  ADDRESS, DATABYTE
*		 ADDRESS=0 --> END OF SINGLE BYTE INIT LIST

	FDB	LEFTSHIFTPIA
	FCB	DDRACCESS
	FDB	LEFTSHIFTDATA
	FCB	LEFTSHIFTDDR
	FDB	LEFTSHIFTPIA
	FCB	LEFTSHIFTCW		THIS ZEROES MSB OF LEFTSHIFTDATA

	FDB	KEYPIA
	FCB	DDRACCESS
	FDB	KEYDATA
	FCB	KEYDDR
	FDB	KEYPIA
	FCB	KEYCW

	FDB	JOYBAUDPIA
	FCB	DDRACCESS
	FDB	JOYSTICK
	FCB	JOYDDR
	FDB	JOYBAUDPIA
	FCB	JOYBAUDCW

	FDB	ACIACTRL
	FCB	ACIARESET
	FDB	ACIACTRL
	FCB	ACIACW+ACIARTSD

	FDB	IOINT
	FCB	$7E	JMP OPCODE

	FDB	DEBUG		"JMP KEYSAVE"
	FCB	$7E		DEFAULT ^D IS JUST A CHARACTER

	FDB	XMITBUFREE
	FCB	XMITBUFSIZ
	
	FDB	KEYBUFREE
	FCB	KEYBUFSIZ

	FDB	DISPLAYBUFFREE
	FCB	DISPLAYBUFSIZ

	FDB	$0000	END OF SINGLE BYTE INIT LIST
	FDB	$0000	(DUMMY ENTRY IN DOUBLE BYTE INIT LIST)
	PAGE
*	DOUBLE BYTE INIT LIST
*	FORMAT:	ADDRESS, BYTE, BYTE
*		ADDRESS=0 --> END OF DOUBLE BYTE INIT LIST

	FDB	IOINT+1		COMPLETE THE I/O INTERRUPT...
	FDB	WHODIDIT	'JMP WHODIDIT' INSTRUCTION

	FDB	DEBUG+1		COMPLETE THE DEFAULT DEBUG CONTROL
	FDB	KEYSAVE

	FDB	PICTOP
	FDB	FREESPACE

	FDB	KEYBUFILL
	FDB	KEYBUFBAS
	FDB	KEYBUFMPT
	FDB	KEYBUFBAS

	FDB	DISPLAYBUFILL
	FDB	DISPLAYBUFBAS
	FDB	DISPLAYBUFMPT
	FDB	DISPLAYBUFBAS

	FDB	XMITBUFILL
	FDB	XMITBUFBAS
	FDB	XMITBUFMPT
	FDB	XMITBUFBAS

	FDB	TASKQUEUE
	FDB	DISPLAYTCB

	FDB	DISPLAYTCB+TCBLNK
	FDB	KEYTCB
	FDB	DISPLAYTCB+TCBSTK
	FDB	DISPSTACK-7-2	ZEROED CONTEXT BLOCK
	FDB	DISPLAYTCB+TCBCND	MARK TASK AS 'IN EXECUTION'
	FDB	EXECUTING
	FDB	DISPSTACK-7+PC	P COUNTER OF DISPLAY TASK
	FDB	REMOTELOOP	WELL... THIS WILL DO JUST FINE

	FDB	KEYTCB+TCBSTK
	FDB	KEYSTACK-7-2	ZEROED CONTEXT BLOCK
	FDB	KEYTCB+TCBCND	MARK TASK AS EXECUTING
	FDB	EXECUTING
	FDB	KEYSTACK-7+PC
	FDB	KEYTASK
	FDB	SCHEDSTK	SET UP DEFAULT SCHEDULER'S STACK POINTER
	FDB	SSTK

	FDB	$0000		END OF DOUBLE BYTE INIT LIST
	PAGE	I/O ROUTINES
*	GETDB -- GET DATA BYTE; DO NOT DISPLAY CURSOR OR CROSSHAIRS
*	ENTERED WITH BOTH CURSOR AND CROSSHAIRS ERASED
*	USED TO OBTAIN PARAMETER BYTES TO COMMANDS

GETDBWAIT	EQU	*
	LDX	#WAKEDISPLAY	GET ADDRESS OF WAKE-UP ROUTINE
	JSR	WAIT$		SLEEP UNTIL SOMETHING HAPPENS
GETDB	EQU	*
	LDAA	CHARREJECT	A REJECTED CHARACTER ?
	BNE	GETDBREJECTED	B/ YES, USE IT INSTEAD OF BUFFERED CHARACTERS
	LDAA	DISPLAYBUFFREE	IS BUFFER EMPTY ?
	CMPA	#DISPLAYBUFSIZ	...?
	BEQ	GETDBWAIT	YES, GO SLEEP
GETDB1	EQU	*
	CMPA	#DISPLAYBUFSIZ-DISPLAYBUFSIZ*1/3	IS BUFFER GOING TO BE ONLY 1/3 FULL ?
	BNE	GETDBGRAB		B/ NO, GET A CHARACTER AND GET OUT!
	LDAA	#XON			YES, TELL HOST TO SEND SOME MORE WORK
	STAA	PRIORITYXMIT
	LDAA	#ACIACW+ACIARTSI	ENABLE ACIA XMIT INTERRUPT
	STAA	ACIACTRL		IN CASE XMIT ROUTINES ARE ASLEEP
GETDBGRAB	EQU	*
	LDX	DISPLAYBUFMPT	GET BUFFER EMPTY OUT POINTER
	LDAA	0,X		NO, FETCH BYTE FROM BUFFER
	INX			BUMP THE EMPTY OUT POINTER
	CPX	#DISPLAYBUFTOP	AT PHYSICAL END OF CIRCULAR BUFFER ?
	BNE	GETDBSTX	NO
	LDX	#DISPLAYBUFBAS	YES, GET PHYSICAL BOTTOM OF BUFFER
GETDBSTX	EQU	*
	STX	DISPLAYBUFMPT	UPDATE THE DISPLAY BUFFER EMPTY POINTER
	INC	DISPLAYBUFFREE	REMEMBER # OF FREE BYTES
	STAA	LASTCHAR	SAVE LAST BYTE IN CASE IT GETS REJECTED
	RTS

GETDBREJECTED	EQU	*
	CLR	CHARREJECT	FLAG 'CHARACTER NOT REJECTED'
	LDAA	LASTCHAR	GET THE REJECTED CHARACTER
	RTS			AND GET OUT!

WAKEDISPLAY	EQU	*	WAKE UP DISPLAY ROUTINE FOR PARAMETER CHARACTER
	LDAA	DISPLAYBUFFREE	ANYTHING ARRIVE IN BUFFER YET ?
	SUBA	#DISPLAYBUFSIZ	(IS BUFFER EMPTY ?)
	RTS
	PAGE
WAKEBLINK	EQU	*	WAKE UP ROUTINE FOR THE DISPLAY TASK COMMAND CHARACTER
	LDAA	DISPLAYBUFFREE	ANYTHING ARRIVE IN BUFFER YET ?
	CMPA	#DISPLAYBUFSIZ	(...?)
	BNE	WAKEBLINK1	YES, GO WAKE THE TASK!
	LDAA	TIME+3		ONE SCREEN CYCLE GO BY ?
	TAB			SAVE CURRENT TIME (MOD 256)
	SUBA	LCTIME		HOW LONG AGO DID CROSS UPDATE RUN ?
	CMPA	#CROSSPTIME	(TIME TO PROCESS CURSOR/CROSSHAIRS AGAIN ?)
	BHI	WAKEBLINK2	YES, WAKE UP THE CROSSHAIR PHYSICS ROUTINE
	CLRA			NOTHING DOING, FLAG 'NOT READY'
	RTS			AND RETURN TO SCHEDULER

WAKEBLINK1	EQU	*
	LDAA	#WAKBUFCH	FLAG 'WOKEN BECAUSE OF BUFFER DATA'
	RTS

WAKEBLINK2	EQU	*
	STAB	LCTIME		SAVE CURRENT TIME AS LAST TIME CROSSHAIRS PROCESSED
	LDAA	#WAK60HZ	FLAG 'WOKEN BECAUSE OF ELAPSED TIME'
	RTS

*	GETDBLINK -- GET COMMAND DATA BYTE; BLINK CURSOR AND MOVE CROSSHAIRS
*	USED ONLY TO OBTAIN COMMAND BYTE OF COMMAND SEQUENCE
*	COMMAND PARAMETERS ARE FETCHED USING "GETDB" (ABOVE)

GETDBLINKWAIT	EQU	*	TIME TO GO TO SLEEP...
	LDX	#WAKEBLINK	WAKE US UP IF ANYTHING INTERESTING HAPPENS
	JSR	WAIT$		(LIKE CLOCK TICK OR DATA BYTE ARRIVAL)
	CMPA	#WAKBUFCH	NEW COMMAND BYTE ARRIVE ?
	BNE	SIXTYHZ		NO, MUST BE CLOCK TICK
	JSR	ERASECURSOR	YES, MAKE SURE THE CURSOR IS GONE
	JSR	ERASECROSS	DITCH THE CROSSHAIRS, TOO!
GETDBLINK	EQU	*	ENTERED WITH CURSOR, CROSS NOT DISPLAYED!
	LDAA	CHARREJECT	A REJECTED CHARACTER ?
	BNE	GETDBREJECTED	B/ YES, GET IT AND SPLIT
	LDAA	DISPLAYBUFFREE	IS BUFFER EMPTY ?
	CMPA	#DISPLAYBUFSIZ	...?
	BNE	GETDB1		NO, GO GET THE CHARACTER AND SPLIT!
	BRA	GETDBLINKWAIT	YES, GO SNOOZE AWHILE

	PAGE
SIXTYHZ	EQU	*	WE GET HERE ABOUT 60 TIMES PER SECOND
*	MOVE CROSSHAIRS IF NEEDED
	DEC	JOYDEADTIME	JOYSTICK DEAD ?
	BPL	CHECKCROSS	YES, GO PROCESS CROSS
	CLR	JOYDEADTIME	NO, KEEP IT ALIVE
	LDX	#UP		GET PARAMETER FOR JOYDIRECTION ROUTINE
	JSR	JOYDIRECTION	GO PROCESS VELOCITY CHANGE FOR UP SWITCH
	LDX	#DOWN		LIKEWISE FOR OTHER 3 DIRECTIONS...
	JSR	JOYDIRECTION
	LDX	#LEFT
	JSR	JOYDIRECTION
	LDX	#RIGHT
	JSR	JOYDIRECTION
CHECKCROSS	EQU	*	CHECK CROSS TO SEE IF DISPLAYED
	LDAA	CROSSON		IS CROSS DISPLAYED ?
	BEQ	MOVECROSS	B/ NO, IT MAY HAVE MOVED AND NEEDS RE-DISPLAY!
	LDAA	CROSSXVEL	ARE CROSS HAIRS MOVING ?
	ORAA	CROSSXVFRA
	ORAA	CROSSYVEL
	ORAA	CROSSYVFRA
	BEQ	BLINKTEST	NO, GO DEAL WITH CURSOR
	JSR	ERASECURSOR	YES, DITCH THE CURSOR WHEN CROSSHAIRS MOVE!
	JSR	ERASECROSS	ERASE THE CROSSHAIRS
MOVECROSS	EQU	*	ADJUST CROSS X,Y POSITION BY ADDING VELOCITY
	LDAA	CROSSXVEL	COMPUTE NEW CROSSHAIR POSITION
	BPL	SIXTYHZ1	IF VELOCITY IS > 0, SIGN BYTE = 0
	DEC	CROSSX		< 0, ADD SIGN BYTE OF -1
SIXTYHZ1	EQU	*
	LDAB	CROSSXVFRA	(A,B):= X VELOCITY
	ADDB	CROSSXFRA	X:=X+XVEL
	ADCA	CROSSX+1
	BCC	SIXTYHZ2	B/ NO CARRY TO UPPER BYTE
	INC	CROSSX		CARRY, DEAL WITH IT
SIXTYHZ2	EQU	*
	STAA	CROSSX+1	STORE NEW X POSITION
	STAB	CROSSXFRA
	LDAA	CROSSX		MASK CROSSX POSITION TO KEEP IT REASONABLE
	ANDA	#(SCREENWIDTH-1)/256
	STAA	CROSSX
	LDAA	CROSSYVEL	UPDATE CROSSY POSITION
	LDAB	CROSSYVFRA
	ADDB	CROSSYFRA
	ADCA	CROSSY
	STAA	CROSSY
	STAB	CROSSYFRA
	CMPA	#0-CROSSYFUDGE	NOW DECIDE IF WE SHOULD DISPLAY CROSS
	BNE	DRAWCROSS	B/ NOT AT SPECIAL HOME DISAPPEARING PLACE
	LDX	CROSSX		AT HOME IN Y, HOW 'BOUT X ?
	CPX	#(0-CROSSXFUDGE)&(SCREENWIDTH-1)	...?
	BEQ	BLINKTEST	YEP, SKIP THE DISPLAY STEP (CROSS IS INVISIBLE AT HOME)
DRAWCROSS	EQU	*	WE GET HERE IF WE MUST DISPLAY THE CROSS
	INC	CROSSON		REMEMBER THAT WE HAVE THE CROSS DISPLAYED
	LDAA	#CROSSX/256	(A,B):= ADDRESS OF (CROSSX,CROSSY)
	LDAB	#CROSSX&$FF
	LDX	#CROSSHAIRS	NOW DRAW THE CROSS HAIRS
	JSR	SPLAT
	JMP	GETDBLINKWAIT	AND IGNORE THE CURSOR
	PAGE
BLINKTEST	EQU	*	TEST TO SEE IF TIME TO TOGGLE CURSOR
	DEC	BLINKFUSE	TIME TO TOGGLE CURSOR ?
	BNE	JGETDBLINKWAIT	NO, GO BACK TO SLEEP
	LDAA	CURSORON	MAKE CURSOR BLINK, IS IT ON ?
	BNE	BLINKCURSOR	YES, GO TURN IT OFF
	LDX	CURSORX		NO, HAS IT BEEN POSITIONED...
	CPX	#SCREENWIDTH-1	TO UPPER RIGHT CORNER ?
	BNE	BLINKCURSOR	NO, GO MAKE IT BLINK
	LDAA	CURSORY		TOP LINE OF SCREEN ?
	CMPA	#SCREENHEIGHT-1	...?
	BEQ	JGETDBLINKWAIT	YES, DON'T DISPLAY CURSOR!
BLINKCURSOR	EQU	*
	COM	CURSORON	REMEMBER STATE OF CURSOR DISPLAY
	LDAA	JOYSTICK	RESET VERTICAL BLANKING SENSE BIT
	LDX	#WAITFORVERTICALBLANK
	JSR	WAIT$		COULD I SAY IT MORE SUCCINCTLY ?
	LDX	#CURSOR		GET POINTER TO CURSOR PICTURE
	LDAA	#CURSORX/256	GET POINTER TO CURSOR POSITION...
	LDAB	#CURSORX&$FF	TO (A,B)
	JSR	SPLAT		GO DUMP IT ON THE SCREEN
	LDAA	#CURSORBLINK	SET TIME BOMB...
	STAA	BLINKFUSE	TO GO OFF SOON
JGETDBLINKWAIT	JMP	GETDBLINKWAIT	NOW WAIT FOR A DATA BYTE TO APPEAR

WAITFORVERTICALBLANK	EQU	*
	LDAA	DISPLAYBUFFREE	IF CHARACTER ARRIVES, ALL BETS ARE OFF!
	SUBA	#DISPLAYBUFSIZ	(BUFFER EMPTY ?)
	BNE	WAITVERTICALRTS	B/ NO, GET OUT!
	LDAA	JOYBAUDPIA	GRAB SENSE BITS FOR BLANKING TIMES
	ANDA	#VERTBSENSE	MASK OFF ALL BUT THE VERTICAL BLANK SENSE BIT
WAITVERTICALRTS	EQU	*
	RTS
	PAGE
**********
	ORG	$F800
**********
*	JOYDIRECTION -- SUBROUTINE TO PROCESS JOYSTICK SWITCH...
*	AND UPDATE CROSS X AND Y VELOCITIES
*	PASSED A POINTER TO A CROSSHAIR VELOCITY CONTROL BLOCK IN (X)
*	DOES ACCELERATION AND INSTANT STOP WHEN ACCELERATOR RELEASED
*	DOES SINGLE STEP FOR SHORT TAPS OF ACCELERATOR
*
JOYDIRECTION	EQU	*
	LDAA	JOYDEADTIME	IN CASE INSTANT STOP ON ANOTHER KEY JUST KILLED JOYSTICK
	BNE	JOYRTS		B/ JOYSTICK IS DEAD, DO NADA
	LDAA	CROSSVINC,X	GRAB VELOCITY CONTROL BLOCK PARAMETERS
	LDAB	CROSSVINC+1,X
	STAA	TEMPA		TEMPA,TEMPB CONTAIN VELOCITY INCREMENT
	STAB	TEMPB
	LDAA	CROSSDIR,X	GET MASK TO SELECT WHICH SWITCH
	LDX	CROSSVPTR,X	GET POINTER TO VELOCITY (AND POSITION) WORD(S)
	LDAB	CROSSV+1,X	GRAB LOWER PART OF VELOCITY
	BITA	JOYSTICK	IS SELECTED JOYSTICK SWITCH ON ?
	BNE	JOYSWOFF	B/NO (1--> OFF)
	LDAA	CROSSV,X	GET UPPER HALF OF VELOCITY WORD
	BEQ	JOY0		B/ PERHAPS ALL VELOCITIES ARE ZERO
	BMI	JOYVLS		DOES SIGN OF VELOCITY...
JOYVGR	TST	TEMPA		MATCH SIGN OF VELOCITY INCREMENT ?
	BMI	JOYSTOP		NO, MUST BE INSTANT STOP!
	CMPA	#CROSSVMAX	YES, REACHED MAX VELOCITY YET ?
	BGE	JOYRTS		YES, LEAVE VELOCITY ALONE
JOYINCV	EQU	*		INCREMENT THE VELOCITY
	ADDB	TEMPB		ADD VELOCITY INCREMENT (ACCELERATION)...
	ADCA	TEMPA		TO VELOCITY
JOYSTORE	EQU	*
	STAA	CROSSV,X	STORE UPDATED VELOCITY
	STAB	CROSSV+1,X
JOYRTS	RTS			AND EXIT

JOYSWOFF	EQU	*	JOY STICK SWITCH IS OFF, DECELERATE
	LDAA	CROSSV,X	LOOK AT VELOCITY IN THIS DIRECTION
	BNE	JOYDECV		ITS NOT ZERO, GO DECELERATE
	TSTB			(LOWER HALF ZERO ?)
	BEQ	JOYRTS		(YES, LEAVE VELOCITY AT ZERO)
JOYDECV	EQU	*
	PSHA			DECELERATE ONLY IF THIS SWITCH HAS...
	EORA	TEMPA		JURISDICTION OVER CURRENT DIRECTION OF MOVEMENT
	PULA			(I.E., SIGNS OF VELOCITY AND ACCELERATION MATCH)
	BMI	JOYRTS		B/ SIGNS DON'T MATCH
JOYSTOP	EQU	*		INSTANT STOP REQUIRED
	CLRA			MAKE ZERO TO USE ON VELOCITIES
	STAA	CROSSXVEL	ZERO OUT X VELOCITY
	STAA	CROSSXVFRA
	STAA	CROSSYVEL	ZERO THE Y VELOCITY
	STAA	CROSSYVFRA
	LDAA	#JOYDEADSTOP	AND KILL THE JOYSTICK FOR AWHILE
	STAA	JOYDEADTIME
	RTS

JOY0	LDAA	CROSSXVEL	UPPER HALF OF VELOCITY = 0...
	ORAA	CROSSXVFRA	ARE BOTH X AND Y VELOCITIES ZERO ?
	ORAA	CROSSYVEL	...?
	ORAA	CROSSYVFRA	...?
	BEQ	JOYSS		B/ ALL VELOCITIES = 0: IS SINGLE STEP
	CLRA			UPPER HALF OF VELOCITY = 0, REMEMBER ?
	TSTB			LOWER HALF = 0, TOO ?
	BEQ	JOYINCV		B/ VELOCITY = 0, THIS CAN'T BE INSTANT STOP
	BRA	JOYVGR		NON-ZERO VELOCITY, MIGHT BE INSTANT STOP

JOYSS	LDAA	TEMPA		ALL VELOCITIES ARE ZERO, MUST BE SINGLE STEP
	LDAB	TEMPB		SET VELOCITY TO VELOCITY INCREMENT (<>0)
	ASLB			ACTUALLY, SET IT TO VELOCITY INCREMENT * 4
	ROLA
	ASLB
	ROLA
	STAA	CROSSV,X
	STAB	CROSSV+1,X
	LDAA	#$100/2		GET .5 UNIT
	STAA	CROSSXFRA	SET FRACTION PART TO .5
	STAA	CROSSYFRA	SO THAT POSITION+.5+ OR -1 IS ENDING POSITION OF CROSS
	LDAA	#JOYDEADSS	AND KILL JOYSTICK ACTIVITY...
	STAA	JOYDEADTIME	UNTIL CROSSHAIRS HAVE MOVED ONE UNIT
	RTS		THEN THEY WILL EITHER INSTANT STOP OR FURTHER ACCELERATE

JOYVLS	TST	TEMPA		VELOCITY SIGN IS MINUS
	BPL	JOYSTOP		B/ VELOCITY INCREMENT > 0, INSTANT STOP!
	CMPA	#-CROSSVMAX	VELOCITY SIGNS THE SAME, HIT MAX VELOCITY ?
	BGT	JOYINCV		NO, JUST INCREMENT THE VELOCITY
	RTS			YES, JUST GET OUT

*	VELOCITY CONTROL BLOCKS

UP	FCB	CROSSUP
	FDB	CROSSYVEL,CROSSACCEL

DOWN	FCB	CROSSDN
	FDB	CROSSYVEL,-CROSSACCEL

LEFT	FCB	CROSSLF
	FDB	CROSSXVEL,-CROSSACCEL

RIGHT	FCB	CROSSRT
	FDB	CROSSXVEL,CROSSACCEL
	PAGE	PROCESSCHAR/SENDCHAR
PROCESSCHAR	EQU	*	ASSUMES NO REMOTE HOST!
	LDX	DISPLAYBUFILL	PUT CHARACTER INTO DISPLAY TASK'S...
	STAA	0,X		INPUT STREAM
	INX
	CPX	#DISPLAYBUFTOP	HIT TOP OF CIRCULAR BUFFER ?
	BNE	PROCESSSTX
	LDX	#DISPLAYBUFBAS	YES, USE BOTTOM INSTEAD
PROCESSSTX	EQU	*
	STX	DISPLAYBUFILL	STORE BUMPED FILL POINTER
	LDX	#WAKEPROCESS	NOW WAIT FOR DISPLAY TASK...
	JMP	WAIT$		TO PROCESS THE DATA BEFORE RETURNING

WAKEPROCESS	EQU	*	WAKE USER TASK WHEN DISPLAY BUFFER IS EMPTY
	LDX	DISPLAYBUFILL
	CPX	DISPLAYBUFMPT	COMPARE FILL AND EMPTY POINTERS
	TPA			COPY "= COMPARE" BIT TO (A)
	ANDA	#4		<>0 --> BUFFER IS EMPTY
	RTS			RETURN BACK TO SCHEDULER

SENDCOMMA	EQU	*
	LDAA	#',
SENDCHAR	EQU	*	SEND (A) TO REMOTE HOST
	LDAB	XMITBUFREE	IS XMIT BUFFER FULL ?
	BNE	SEND1		NO, GO STUFF BYTE INTO OUTPUT BUFFER
	PSHA			YES, SAVE THE BYTE TO SEND
	LDX	#WAKESEND	WAKE ME UP...
	JSR	WAIT$		WHEN SOME BUFFER SPACE ARRIVES
	PULA			FREE BUFFER SLOT NOW AVAILABLE
SEND1	LDX	XMITBUFILL	GET BUFFER FILL POINTER
	STAA	0,X		STORE BYTE INTO BUFFER
	INX			BUMP BUFFER FILL POINTER
	CPX	#XMITBUFTOP	HIT END OF CIRCULAR BUFFER ?
	BNE	SENDSTX		NO, BUMPED POINTER IS JUST FINE
	LDX	#XMITBUFBAS	YES, USE BUFFER BOTTOM INSTEAD
SENDSTX	EQU	*
	STX	XMITBUFILL	STORE UPDATED BUFFER FILL POINTER
	DEC	XMITBUFREE	MARK BUFFER SLOT AS ALLOCATED
	LDAA	#ACIACW+ACIARTSI	FORCE ACIA TO CAUSE INTERRUPT...
	STAA	ACIACTRL	WHEN ITS XMIT REGISTER IS EMPTY!
	RTS

WAKESEND	EQU	*	WAKE UP SEND WHEN XMIT BUFFER SPACE IS AVAILABLE
	LDAA	XMITBUFREE	GET NUMBER OF FREE BYTES IN XMIT BUFFER
	RTS
	PAGE	KEYTASK

*	KEYTASK -- TASK TO COPY KEYSTROKES TO REMOTE HOST
*

KEYTASK	EQU	*
	LDX	#WAKEKEY	WAKE ME ON A KEYSTROKE
	JSR	WAIT$
	LDX	KEYBUFMPT	GET KEY BUFFER EMPTY POINTER
	LDAA	0,X		GET KEY BUFFER BYTE
	INX			BUMP TO NEXT BUFFER SLOT
	CPX	#KEYBUFTOP	HIT TOP OF CIRCULAR BUFFER ?
	BNE	KEYTSTX		NO, USE BUMPED VERSION
	LDX	#KEYBUFBAS	YES, USE BUFFER BOTTOM INSTEAD
KEYTSTX	EQU	*
	STX	KEYBUFMPT	UPDATE THE BUFFER EMPTY POINTER
	INC	KEYBUFREE	TELL INTERRUPT SOFTWARE ABOUT FREE SLOT
	BSR	SENDCHAR	SEND BYTE TO REMOTE HOST
	BRA	KEYTASK

WAKEKEY	EQU	*		WAKE KEY TASK WHEN KEYSTROKE ARRIVES
	LDAA	KEYBUFREE	IF # BYTES FREE...
	SUBA	#KEYBUFSIZ	<> # BYTES AVAILABLE IN BUFFER, THEN WAKE UP!
	RTS
	PAGE	INTERRUPT ROUTINES
KEYINT	EQU	*		KEYBOARD INTERRUPT SERVICING
	LDAA	KEYDATA		FETCH THE KEYSTROKE
	CMPA	#CTRLD		DEBUG INTERRUPT ?
	BEQ	JDEBUG		YES, GO TO THE DEBUGGER
	LDX	KEYBUFILL	NOW FILL IN THE BUFFER
	CMPA	#ESCAPE		ESCAPE CODE ?
	BNE	KEYSAVE		NO, JUST STICK THE CODE IN THE BUFFER
	INC	ESCCOUNT	YES, SET ESCAPE FLAG
KEYSAVE	EQU	*	SAVE KEY IN BUFFER, CHECK FOR BUFFER FULL
	LDAB	KEYBUFREE	GET BUFFER FREE COUNT
	BEQ	KEYLOSE		NONE FREE, KEYSTROKE LOST
	DECB			DOWN COUNT AVAILABLE BUFFER BYTES
	STAB	KEYBUFREE
KEYSTAA	EQU	*	STORE BYTE INTO BUFFER
	STAA	0,X
	INX			BUMP BUFFER FILL POINTER
	CPX	#KEYBUFTOP	CIRCULAR BUFFER TOP HIT ?
	BNE	KEYSTX		NO, GO UPDATE BUFFER FILL POINTER
	LDX	#KEYBUFBAS	YES, USE PHYSICAL BUFFER BASE
KEYSTX	EQU	*
	STX	KEYBUFILL	UPDATE BUFFER FILL POINTER
	LDAA	#CLICK		MAKE EACH SAVED KEYSTROKE NOISY
	STAA	DURATION
	JMP	FORCESCHEDULE

KEYLOSE	EQU	*		NO SPACE IN BUFFER FOR KEYSTROKE
	CMPA	#ESCAPE		TREAT ESCAPE SPECIAL...
	BNE	JIORTI		NOT AN ESCAPE, THROW IT AWAY!
	DEX			ELSE PUT ESCAPE CODE INTO LAST BYTE OF BUFFER
	CPX	#KEYBUFBAS-1	BACK UP THE BUFFER FILL POINTER BY ONE
	BNE	KEYESC		B/ DIDN'T RUN OFF BOTTOM OF BUFFER
	LDX	#KEYBUFTOP-1	SET FILL POINTER TO BUFFER TOP
KEYESC	EQU	*		ALREADY HAVE ESCAPE AT END OF BUFFER ?
	CMPA	0,X		...?
	BNE	KEYSTAA		NO, STORE THE ESCAPE CODE
	DEC	ESCCOUNT	YES, CAN'T COUNT THIS ESCAPE!
JIORTI	JMP	IORTI

JDEBUG	JMP	DEBUG
WHODIDIT	EQU	*	THIS IS WHERE WE END UP ON AN IO INTERRUPT
	LDAA	ACIASTATUS	ACIA HIT US ?
	BITA	#ACIARDRF	(RECEIVER JUST GET A CHARACTER ?)
	BNE	RCVINT		YES, DO SOMETHING QUICK!
	LDAB	KEYPIA		NO, COULD BE KEYSTROKE
	BMI	KEYINT		IS KEYSTROKE, GO PROCESS
	BITA	#ACIATDRE	MAYBE XMITTER IS READY...
	BEQ	IORTI		B/ NOT ANYBODY, I GUESS THIS IS OK ?
	PAGE	XMIT INTERRUPT HANDLER
XMTINT	LDAA	PRIORITYXMIT	HIGH PRIORITY CHARACTER TO SEND ?
	BNE	XMITINT3	B/ YES, GO SEND IT!
	LDAA	XMITBUFREE	ANY CHARACTERS TO TRANSMIT ?
	CMPA	#XMITBUFSIZ	(ARE ALL BUFFER SLOTS FREE?)
	BEQ	XMITINT2	(B/ ALL FREE, NOTHING TO XMIT)
	LDX	XMITBUFMPT	YES, GET BUFFER EMPTY POINTER
	LDAA	0,X		GRAB BYTE TO SEND
	STAA	ACIAXMITD	STORE INTO ACIA XMIT DATA REGISTER
	INX			NOW BUMP XMIT POINTER CIRCULARLY
	CPX	#XMITBUFTOP	HIT TOP OF BUFFER ?
	BNE	XMITINT1	NO, STORE NEW VALUE
	LDX	#XMITBUFBAS	YES, USE BUFFER BASE
XMITINT1	EQU	*
	STX	XMITBUFMPT	UPDATE THE XMIT POINTER
	INC	XMITBUFREE	TELL TASK THAT BUFFER SLOT IS AVAILABLE
	BRA	FORCESCHEDULE	AND WAKE THE SCHEDULER

XMITINT2	EQU	*	NO CHARACTERS TO XMIT
	LDAA	#ACIACW+ACIARTSD	DISABLE ACIA XMIT INTERRUPT
	STAA	ACIACTRL		SO ACIA WON'T CONTINUALLY PESTER US!
	BRA	IORTI

XMITINT3	EQU	*	SEND HIGH PRIORITY CHARACTER
	CLR	PRIORITYXMIT	FLAG 'DATA SENT'
	STAA	ACIAXMITD	AND SEND IT (USUALLY XON OR XOFF)
	BRA	IORTI		NO BUFFER SLOTS WERE FREED...
RCVINT	EQU	*		ACIA JUST RECEIVED A CHARACTER !
	LDAA	ACIARCVD	GET THE RECEIVED DATA AND ACK THE INTERRUPT
	LDAB	DISPLAYBUFFREE	GET NUMBER OF FREE SLOTS IN BUFFER
	BEQ	IORTI		B/ NO SLOTS, YOU LOSE THE CHARACTER, BUDDY!
	DEC	DISPLAYBUFFREE	ALLOCATE A SLOT
	CMPB	#DISPLAYBUFSIZ-DISPLAYBUFSIZ*9/10	BUFFER OVER 9/10 FULL ?
	BHI	RCVOK		B/ NO
	LDAB	#XOFF		YES, TURN ON THE SAFETY VALVE!
	STAB	PRIORITYXMIT	TELL HOST TO SHUT UP AWHILE
	LDAB	#ACIACW+ACIARTSI	ENABLE XMIT INTERRUPT...
	STAB	ACIACTRL	IN CASE THE XMIT ROUTINES ARE ASLEEP
RCVOK	EQU	*
	LDX	DISPLAYBUFILL	GRAB THE BUFFER FILL POINTER
	ANDA	#$7F		***** MASK TO 7 BITS ******
	STAA	0,X		STORE BYTE INTO BUFFER
	INX			BUMP FILL POINTER CIRCULARLY
	CPX	#DISPLAYBUFTOP	(AT PHYSICAL TOP OF BUFFER ?)
	BNE	RCVSTX		(NO)
	LDX	#DISPLAYBUFBAS	(YES, USE PHYSICAL BOTTOM OF BUFFER)
RCVSTX	EQU	*
	STX	DISPLAYBUFILL	UPDATE THE BUFFER POINTER
*	NOW FALL INTO FORCESCHEDULE
	PAGE	*** SCHEDULER ***
FORCESCHEDULE	EQU	*	FORCE SCHEDULER TO LOOK AROUND AFTER INTERRUPT
	INC	SURPRISE	SYSTEM STATE HAS CHANGED SIGNIFICANTLY
IORTI	DEC	DONTSTOPME	SCHEDULER OR INTERRUPT ROUTINE ALREADY ACTIVE ?
	BPL	FORCERTI	YES, GO BACK TO IT...
	LDAA	SURPRISE	DID THIS SET OF INTERRUPTS...
	BEQ	TASKRTI		CAUSE ANYTHING INTERESTING TO HAPPEN ?
	INC	DONTSTOPME	YES, WE MUST INVOKE THE SCHEDULER
SCHEDULER	EQU	*
	LDS	SCHEDSTK	SWITCH TO SCHEDULER'S STACK
	CLI			ALLOW INTERRUPTS IF NOT ALREADY ON
SCHEDTOP	EQU	*	START SCANNING TCB LIST
	CLR	SURPRISE	WE'RE STARTING OVER, NO BAD DECISIONS
	LDX	TASKQUEUE	GET POINTER TO LIST OF TASKS
SCHEDLOOP	EQU	*
	STX	CURRENTASK	SAVE POINTER TO THIS TCB IN CASE HE'S READY
	LDX	TCBCND,X	GET POINTER TO WAKE UP ROUTINE
	JSR	0,X		GO TEST WAKE UP CONDITION
	LDX	CURRENTASK	GET TCB ADDRESS
	TSTA			WAKEUP CODE <> 0 ?
	BNE	JUSTREADY	YES, TASK JUST WENT READY!
	LDX	TCBLNK,X	FOLLOW TO NEXT TCB
	BNE	SCHEDLOOP	LOOP IF NOT END OF LIST
	BRA	SCHEDTOP	ELSE START PROCESSING THE LIST AGAIN

JUSTREADY	EQU	*	TASK JUST BECAME READY
	LDAB	#EXECUTING/256	MARK TCB AS 'EXECUTING'
	STAB	TCBCND,X	SO INVOKING WAKE-UP ROUTINE...
	LDAB	#EXECUTING&$FF	WILL SPEED UP FURTHER DISPATCHES
	STAB	TCBCND+1,X
	LDX	TCBSTK,X	GET TASK'S STACK POINTER
	STAA	AREG+2,X	SET TASK'S A REGISTER TO WAKE UP CODE
EXECUTING	EQU	*	TASK WAS IN EXECUTION WHEN STOPPED
	NOP			FUCKING DESIGNERS OF CHIP, AGAIN...
	SEI			DISABLE INTERRUPTS
	LDAA	SURPRISE	DID WE GET SURPRISED ?
	BNE	SCHEDULER	YES, GO SCHEDULE AGAIN
	DEC	DONTSTOPME	FLAG 'RESCHEDULING IS OK'
TASKRTI	LDX	CURRENTASK	GET POINTER TO TASK CONTROL BLOCK AGAIN
	LDS	TCBSTK,X	GET TASK'S STACK POINTER
FORCERTI	EQU	*
	PULA			RESTORE TEMPX FROM TASK'S CONTEXT BLOCK
	PULB
	STAA	TEMPX
	STAB	TEMPX+1
	RTI			PASS CONTROL TO INTERRUPTED ROUTINE
*	WAIT$ -- MAKE TASK WAIT FOR WAKEUP CONDITION
*	(X) POINTS TO WAKE UP CONDITION TEST SUBROUTINE...
*	WHICH IS CONTINUALLY CALLED BY THE SCHEDULER
*	IF WAKE UP SUBROUTINE RETURNS ZERO IN (A)...
*	THEN TASK WHICH IS WAITING IS NOT WOKEN
*	(A) <> 0 --> TASK JUST WENT READY;
*	WAKE UP SUBROUTINE IS NOT CALLED ANYMORE.
*	WAIT$ RETURNS TO TASK; (A) REGISTER CONTAINS WAKE-UP CODE...
*	FROM THE WAKE-UP ROUTINE
*
WAIT$	EQU	*		(X) POINT TO WAIT SUBROUTINE FOR TASK
	STX	TEMPX		SAVE ADDRESS OF WAKE UP ROUTINE
	CLRA			MAKE A ZERO TO PUSH
	NOP			SIGH... SOMETIMES, YOU JUST HAVE TO TURN INTERRUPTS OFF!
	SEI
	PSHA			TURN RETURN ADDRESS INTO A CONTEXT BLOCK
	PSHA			BY PUSHING A ZEROED X REGISTER
	PSHA			A ZERO CONDITION CODE BYTE
	PSHA			ZERO FOR B REGISTER
	PSHA			ZERO FOR A REGISTER
	PSHA			AND ZERO FOR TEMPX
	PSHA
	INC	DONTSTOPME	FLAG 'IN SCHEDULER'
	LDX	CURRENTASK	REMEMBER ADDRESS OF WAKE UP SUBROUTINE
	LDAA	TEMPX		GET WAKE UP SUBROUTINE ADDRESS
	LDAB	TEMPX+1
	STAA	TCBCND,X
	STAB	TCBCND+1,X
	STS	TCBSTK,X	STORE TASK'S STACK IN HIS TCB
	BRA	SCHEDULER	THEN GO SCHEDULE SOME OTHER TASK
*	CAUSEINT$ -- (X) POINTS TO INTERRUPT ROUTINE ADDRESS
*	SIMULATES AN I/O INTERRUPT AND TRANSFERS CONTROL TO (X)
*	ALL REGISTERS DESTROYED
*	USED TO START AN INTERRUPT ROUTINE UP
*
CAUSEINT$	EQU	*
	STX	TEMPX		SAVE INTERRUPT ROUTINE ADDRESS
	CLRA			MAKE A ZERO TO PUSH
	NOP			DISABLE INTERRUPTS...
	SEI			WHILE WE PUSH A CONTEXT BLOCK
	PSHA			PUSH A ZEROED X REGISTER
	PSHA
	PSHA			PUSH A ZEROED B REGISTER
	PSHA			PUSH A ZEROED A REGISTER
	PSHA			ZERO CC BITS (ENABLING INTERRUPTS)
	PSHA			PUSH A ZEROED TEMPB
	PSHA			PUSH A ZEROED TEMPA
	INC	DONTSTOPME	FLAG 'IN AN INTERRUPT ROUTINE'
	LDX	CURRENTASK	LEAVE TCBCND = "EXECUTING"
	STS	TCBSTK,X	AND SWITCH TO THE INTERRUPT STACK
	LDS	SCHEDSTK	...
	LDX	TEMPX		GET INTERRRUPT ROUTINE ADDRESS
	JMP	0,X		AND GO THERE!
	PAGE	NON-MASKABLE INTERRUPT HANDLER

NMICHK	EQU	*	IO INTERRUPT GOES HERE FIRST!
	LDAA	NMISENSE	DID WE GET HERE BECAUSE OF SWI + NMI ?
	BMI	OMIGOD		YES, MUST DO SUPER FUNNY TO RECOVER
	LDAA	TEMPA		SAVE TEMPA AND TEMPB...
	LDAB	TEMPB		ON STACK
	PSHB
	PSHA
	INC	DONTSTOPME	BUMP NUMBER OF NESTED INTERRUPTS
	BNE	JIOINT		B/ ALREADY SWITCHED TO INTERRUPT STACK
	LDX	CURRENTASK	SWITCH TO SCHEDULER'S STACK
	STS	TCBSTK,X	SAVE TASK'S STACK IN HIS TCB
	LDS	SCHEDSTK	GET THE SCHEDULER'S STACK INSTEAD
JIOINT	JMP	IOINT		GO DEAL WITH I/O INTERRUPT

*	"IF A NMI AND A SWI OCCUR SIMULTANEOUSLY, AN..."
*	"I/O INTERRUPT OCCURS INSTEAD"
*	FUCKING DESIGNERS PISS ME OFF...
*
OMIGOD	EQU	*	WELL, IT HAPPENED
	TSX			BACK UP INTERRUPTED PC TO RE-DO THE SWI
	LDAA	PC+1,X		LSB OF PC = 0 ?
	BNE	DECPCLSB	NO, UPPER BYTE WON'T CHANGE
	DEC	PC,X		YES, DECREMENT UPPER BYTE
DECPCLSB	EQU	*
	DEC	PC+1,X		THEN DECREMENT THE LOWER BYTE

*	WE FINISH CLEANING UP THE MESS BY DOING WHAT NMI INTENDED

NMINT	EQU	*	NON-MASKABLE INTERRUPT ROUTINE
*	PERFORMS RAM REFRESH FUNCTION BY HITTING 128 CONSECUTIVE ADDRESSES
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDX	FETCH+((*-NMINT)/3)*2
	LDAA	NMIACK		! ACKNOWLEDGE THE INTERRUPT
	INC	TIME+3		! NOW BUMP THE TIMER
	BEQ	NMTIM1		! BRANCH IF WE MUST BUMP UPPER PART OF TIMER
NMDUR	LDAA	DURATION	! GET DURATION OF BEEPER TONE
	BNE	NMNOISE		! BRANCH IF BEEPER IS ON
NMIRTI	RTI			! ELSE EXIT WITH REFRESH TIME = 161 CYCLES
FETCH	EQU	*		! ALL MEMORY BETWEEN NMINT AND HERE IS EXECUTED

NMTIM1	INC	TIME+2		BUMP 2ND BYTE OF TIMER
	BNE	NMDUR		GO TEST BEEPER IF NO OVERFLOW
	LDX	TIME		BUMP TOP 16 BITS OF TIME
	INX
	STX	TIME
	BRA	NMDUR		(HAPPENS VERY, VERY RARELY)

NMNOISE	EQU	*	MAKE BEEPER NOISE
	DECA			DOWN COUNT BEEPER ON TIME
	STAA	DURATION
	RORA			GENERATE BEEPER TONE BIT
	RORA
	STAA	SPEAKER		(THIS OUTPUTS A SQUARE WAVE OF 250 HZ)
	JMP	NMIRTI		A JUMP HERE IS FASTER THAN A BRA
*
*
	IF	*>/GRAPHRAM-9
	? ROM ALLOCATION EXCEEDED ?
	FIN
*
*
*
UNUSEDROM	EQU	GRAPHRAM-9-*	THE 9 IS FOR INTERRUPT VECTORS
*
*	VERSION NUMBER

	ORG	GRAPHRAM-9
	FCB	VERSION

*	INTERRUPT VECTORS
*
	ORG	GRAPHRAM-8
	FDB	NMICHK		I/O INTERRUPT (APPARENTLY)
	FDB	SWINT		SOFTWARE INTERRUPT
	FDB	NMINT		NON-MASKABLE INTERRUPT
	FDB	RESTART		RESTART ROUTINE

	END
