Steve2/Apple_II_ROM.s

6915 lines
282 KiB
ArmAsm

include(`asm.m4h')
; --------------------------------
;
; Applesoft BASIC, V2
;
; Written by Marc McDonald and Randy Wigginton.
;
; Original copyright 1976 by Microsoft,
; 1977 by Apple Computer.
;
; Disassembled by (unknown).
; Fixed by Chris Mosher.
;
; For the cc65.org Assembler (ca65)
;
; Applesoft BASIC was first written by
; Marc McDonald, the first employee of Microsoft,
; in mid-1976. That version was bought by Apple
; and released (on casette) in Nov. 1977.
;
; Version 2 was written by Randy Wigginton and
; others at Apple in spring 1978. This version
; was released in several different forms. The
; one reproduced by this source assembly file is
; the main-board ROM form, which appeared in the
; Apple ][ plus ROM at $D000-$F7FF.
;
; --------------------------------
; --------------------------------
; ZERO PAGE LOCATIONS:
; --------------------------------
GOWARM = $00 ; GETS "JMP RESTART"
GOSTROUT = $03 ; GETS "JMP STROUT"
USR = $0A ; GETS "JMP <USER ADDR>"
; (INITIALLY $E199)
CHARAC = $0D ; ALTERNATE STRING TERMINATOR
ENDCHR = $0E ; STRING TERMINATOR
TKN_CNTR = $0F ; USED IN PARSE
EOL_PNTR = $0F ; USED IN NXLIN
NUMDIM = $0F ; USED IN ARRAY ROUTINES
DIMFLG = $10 ;
VALTYP = $11 ; $:VALTYP=$FF; %:VALTYP+1=$80
DATAFLG = $13 ; USED IN PARSE
GARFLG = $13 ; USED IN GARBAG
SUBFLG = $14 ;
INPUTFLG = $15 ; = $40 FOR GET, $98 FOR READ
CPRMASK = $16 ; RECEIVES CPRTYP IN FRMEVL
SIGNFLG = $16 ; FLAGS SIGN IN TAN
HGR_SHAPE = $1A ;
HGR_BITS = $1C ;
HGR_COUNT = $1D ;
MON_CH = $24 ;
MON_GBASL = $26 ;
MON_GBASH = $27 ;
MON_H2 = $2C ;
MON_V2 = $2D ;
MON_HMASK = $30 ;
MON_INVFLG = $32 ;
MON_PROMPT = $33 ;
MON_A1L = $3C ; USED BY TAPE I/O ROUTINES
MON_A1H = $3D ; "
MON_A2L = $3E ; "
MON_A2H = $3F ; "
LINNUM = $50 ; CONVERTED LINE #
TEMPPT = $52 ; LAST USED TEMP STRING DESC
LASTPT = $53 ; LAST USED TEMP STRING PNTR
TEMPST = $55 ; HOLDS UP TO 3 DESCRIPTORS
INDEX = $5E ;
DEST = $60 ;
RESULT = $62 ; RESULT OF LAST * OR /
TXTTAB = $67 ; START OF PROGRAM TEXT
VARTAB = $69 ; START OF VARIABLE STORAGE
ARYTAB = $6B ; START OF ARRAY STORAGE
STREND = $6D ; END OF ARRAY STORAGE
FRETOP = $6F ; START OF STRING STORAGE
FRESPC = $71 ; TEMP PNTR, STRING ROUTINES
MEMSIZ = $73 ; END OF STRING SPACE (HIMEM)
CURLIN = $75 ; CURRENT LINE NUMBER
; ( = $FFXX IF IN DIRECT MODE)
OLDLIN = $77 ; ADDR. OF LAST LINE EXECUTED
OLDTEXT = $79 ;
DATLIN = $7B ; LINE # OF CURRENT DATA STT.
DATPTR = $7D ; ADDR OF CURRENT DATA STT.
INPTR = $7F ;
VARNAM = $81 ; NAME OF VARIABLE
VARPNT = $83 ; ADDR OF VARIABLE
FORPNT = $85 ;
TXPSV = $87 ; USED IN INPUT
LASTOP = $87 ; SCRATCH FLAG USED IN FRMEVL
CPRTYP = $89 ; >,=,< FLAG IN FRMEVL
TEMP3 = $8A ;
FNCNAM = $8A ;
DSCPTR = $8C ;
DSCLEN = $8F ; USED IN GARBAG
JMPADRS = $90 ; GETS "JMP ...."
LENGTH = $91 ; USED IN GARBAG
ARG_EXTENSION = $92 ; FP EXTRA PRECISION
TEMP1 = $93 ; SAVE AREAS FOR FAC
ARYPNT = $94 ; USED IN GARBAG
HIGHDS = $94 ; PNTR FOR BLTU
HIGHTR = $96 ; PNTR FOR BLTU
TEMP2 = $98 ;
TMPEXP = $99 ; USED IN FIN (EVAL)
INDX = $99 ; USED BY ARRAY RTNS
EXPON = $9A ; "
DPFLG = $9B ; FLAGS DEC PNT IN FIN
LOWTR = $9B ;
EXPSGN = $9C ;
FAC = $9D ; MAIN FLT PT ACCUMULATOR
DSCTMP = $9D ;
VPNT = $A0 ; TEMP VAR PTR
FAC_SIGN = $A2 ; HOLDS UNPACKED SIGN
SERLEN = $A3 ; HOLDS LENGTH OF SERIES-1
SHIFT_SIGN_EXT = $A4 ; SIGN EXTENSION, RIGHT SHIFTS
ARG = $A5 ; SECONDARY FP ACC
ARG_SIGN = $AA ;
SGNCPR = $AB ; FLAGS OPP SIGN IN FP ROUT.
FAC_EXTENSION = $AC ; FAC EXTENSION BYTE
SERPNT = $AD ; PNTR TO SERIES DATA IN FP
STRNG1 = $AB ;
STRNG2 = $AD ;
PRGEND = $AF ;
CHRGET = $B1 ;
CHRGOT = $B7 ;
TXTPTR = $B8 ;
RNDSEED = $C9 ;
HGR_DX = $D0 ;
HGR_DY = $D2 ;
HGR_QUADRANT = $D3 ;
HGR_E = $D4 ;
LOCK = $D6 ; NO USER ACCESS IF > 127
ERRFLG = $D8 ; $80 IF ON ERR ACTIVE
ERRLIN = $DA ; LINE # WHERE ERROR OCCURRED
ERRPOS = $DC ; TXTPTR SAVE FOR HANDLERR
ERRNUM = $DE ; WHICH ERROR OCCURRED
ERRSTK = $DF ; STACK PNTR BEFORE ERROR
HGR_X = $E0 ;
HGR_Y = $E2 ;
HGR_COLOR = $E4 ;
HGR_HORIZ = $E5 ; BYTE INDEX FROM GBASH,L
HGR_PAGE = $E6 ; HGR=$20, HGR2=$40
HGR_SCALE = $E7 ;
HGR_SHAPE_PNTR = $E8 ;
HGR_COLLISIONS = $EA ;
FIRST = $F0 ;
SPEEDZ = $F1 ; OUTPUT SPEED
TRCFLG = $F2 ;
FLASH_BIT = $F3 ; = $40 FOR FLASH, ELSE =$00
TXTPSV = $F4 ;
CURLSV = $F6 ;
REMSTK = $F8 ; STACK PNTR BEFORE EACH STT.
HGR_ROTATION = $F9 ;
; $FF IS ALSO USED BY THE STRING OUT ROUTINES
; --------------------------------
STACK = $0100
INPUT_BUFFER = $0200
AMPERSAND_VECTOR = $03F5 ; - 3F7 GETS "JMP ...."
; --------------------------------
; I/O & SOFT SWITCHES
; --------------------------------
KEYBOARD = $C000
SW_TXTCLR = $C050
SW_MIXCLR = $C052
SW_MIXSET = $C053
SW_LOWSCR = $C054
SW_HISCR = $C055
SW_LORES = $C056
SW_HIRES = $C057
; --------------------------------
; MONITOR SUBROUTINES
; --------------------------------
MON_PLOT = $F800
MON_HLINE = $F819
MON_VLINE = $F828
MON_SETCOL = $F864
MON_SCRN = $F871
MON_PREAD = $FB1E
MON_SETTXT = $FB39
MON_SETGR = $FB40
MON_TABV = $FB5B
MON_HOME = $FC58
MON_WAIT = $FCA8
MON_RD2BIT = $FCFA
MON_RDKEY = $FD0C
MON_GETLN = $FD6A
MON_COUT = $FDED
MON_INPORT = $FE8B
MON_OUTPORT = $FE95
MON_WRITE = $FECD
MON_READ = $FEFD
MON_READ2 = $FF02
; --------------------------------
; --------------------------------
; APPLESOFT TOKENS
; --------------------------------
TOKEN_FOR = $81
TOKENDWTA = $83
TOKEN_POP = $A1
TOKEN_GOTO = $AB
TOKEN_GOSUB = $B0
TOKEN_REM = $B2
TOKEN_PRINT = $BA
TOKEN_TAB = $C0
TOKEN_TO = $C1
TOKEN_FN = $C2
TOKEN_SPC = $C3
TOKEN_THEN = $C4
TOKENDB = $C5
TOKEN_NOT = $C6
TOKEN_STEP = $C7
TOKEN_PLUS = $C8
TOKEN_MINUS = $C9
TOKEN_GREATER = $CF
TOKENEQUUAL = $D0
TOKEN_SGN = $D2
TOKEN_SCRN = $D7
TOKEN_LEFTSTR = $E8
; --------------------------------
; BRANCH TABLE FOR TOKENS
; --------------------------------
TOKEN_ADDRESS_TABLE ASM_ADDR(`ENDX-1') ; $80...128...END
ASM_ADDR(`FOR-1') ; $81...129...FOR
ASM_ADDR(`NEXT-1') ; $82...130...NEXT
ASM_ADDR(`DATA-1') ; $83...131..DWTA
ASM_ADDR(`INPUT-1') ; $84...132...INPUT
ASM_ADDR(`DEL-1') ; $85...133...DEL
ASM_ADDR(`DIM-1') ; $86...134...DIM
ASM_ADDR(`READ-1') ; $87...135...READ
ASM_ADDR(`GR-1') ; $88...136...GR
ASM_ADDR(`TEXT-1') ; $89...137...TEXT
ASM_ADDR(`PR_NUMBER-1') ; $8A...138...PR#
ASM_ADDR(`IN_NUMBER-1') ; $8B...139...IN#
ASM_ADDR(`CALL-1') ; $8C...140...CALL
ASM_ADDR(`PLOT-1') ; $8D...141...PLOT
ASM_ADDR(`HLIN-1') ; $8E...142...HLIN
ASM_ADDR(`VLIN-1') ; $8F...143...VLIN
ASM_ADDR(`HGR2-1') ; $90...144...HGR2
ASM_ADDR(`HGR-1') ; $91...145...HGR
ASM_ADDR(`HCOLOR-1') ; $92...146...HCOLOR=
ASM_ADDR(`HPLOT-1') ; $93...147...HPLOT
ASM_ADDR(`DRAW-1') ; $94...148...DRAW
ASM_ADDR(`XDRAW-1') ; $95...149...XDRAW
ASM_ADDR(`HTAB-1') ; $96...150...HTAB
ASM_ADDR(`MON_HOME-1') ; $97...151...HOME
ASM_ADDR(`ROT-1') ; $98...152...ROT=
ASM_ADDR(`SCALE-1') ; $99...153...SCALE=
ASM_ADDR(`SHLOAD-1') ; $9A...154...SHLOAD
ASM_ADDR(`TRACE-1') ; $9B...155...TRACE
ASM_ADDR(`NOTRACE-1') ; $9C...156...NOTRACE
ASM_ADDR(`NORMAL-1') ; $9D...157...NORMAL
ASM_ADDR(`INVERSE-1') ; $9E...158...INVERSE
ASM_ADDR(`FLASH-1') ; $9F...159...FLASH
ASM_ADDR(`COLOR-1') ; $A0...160...COLOR=
ASM_ADDR(`POP-1') ; $A1...161...POP
ASM_ADDR(`VTAB-1') ; $A2...162...VTAB
ASM_ADDR(`HIMEM-1') ; $A3...163...HIMEM:
ASM_ADDR(`LOMEM-1') ; $A4...164...LOMEM:
ASM_ADDR(`ONERR-1') ; $A5...165...ONERR
ASM_ADDR(`RESUME-1') ; $A6...166...RESUME
ASM_ADDR(`RECALL-1') ; $A7...167...RECALL
ASM_ADDR(`STORE-1') ; $A8...168...STORE
ASM_ADDR(`SPEED-1') ; $A9...169...SPEED=
ASM_ADDR(`LET-1') ; $AA...170...LET
ASM_ADDR(`GOTO-1') ; $AB...171...GOTO
ASM_ADDR(`RUN-1') ; $AC...172...RUN
ASM_ADDR(`IF-1') ; $AD...173...IF
ASM_ADDR(`RESTORE-1') ; $AE...174...RESTORE
ASM_ADDR(`AMPERSAND_VECTOR-1') ; $AF...175...&
ASM_ADDR(`GOSUB-1') ; $B0...176...GOSUB
ASM_ADDR(`POP-1') ; $B1...177...RETURN
ASM_ADDR(`REM-1') ; $B2...178...REM
ASM_ADDR(`STOP-1') ; $B3...179...STOP
ASM_ADDR(`ONGOTO-1') ; $B4...180...ON
ASM_ADDR(`WAIT-1') ; $B5...181...WAIT
ASM_ADDR(`LOAD-1') ; $B6...182...LOAD
ASM_ADDR(`SAVE-1') ; $B7...183...SAVE
ASM_ADDR(`DEF-1') ; $B8...184...DEF
ASM_ADDR(`POKE-1') ; $B9...185...POKE
ASM_ADDR(`PRINT-1') ; $BA...186...PRINT
ASM_ADDR(`CONT-1') ; $BB...187...CONT
ASM_ADDR(`LIST-1') ; $BC...188...LIST
ASM_ADDR(`CLEAR-1') ; $BD...189...CLEAR
ASM_ADDR(`GET-1') ; $BE...190...GET
ASM_ADDR(`NEW-1') ; $BF...191...NEW
; --------------------------------
UNFNC ASM_ADDR(`SGN') ; $D2...210...SGN
ASM_ADDR(`INT') ; $D3...211...INT
ASM_ADDR(`ABS') ; $D4...212...ABS
ASM_ADDR(`USR') ; $D5...213...USR
ASM_ADDR(`FRE') ; $D6...214...FRE
ASM_ADDR(`ERROR') ; $D7...215...SCRN(
ASM_ADDR(`PDL') ; $D8...216...PDL
ASM_ADDR(`POS') ; $D9...217...POS
ASM_ADDR(`SQR') ; $DA...218...SQR
ASM_ADDR(`RND') ; $DB...219...RND
ASM_ADDR(`LOG') ; $DC...220...LOG
ASM_ADDR(`EXP') ; $DD...221...EXP
ASM_ADDR(`COS') ; $DE...222...COS
ASM_ADDR(`SIN') ; $DF...223...SIN
ASM_ADDR(`TAN') ; $E0...224...TAN
ASM_ADDR(`ATN') ; $E1...225...ATN
ASM_ADDR(`PEEK') ; $E2...226...PEEK
ASM_ADDR(`LEN') ; $E3...227...LEN
ASM_ADDR(`STR') ; $E4...228...STR$
ASM_ADDR(`VAL') ; $E5...229...VAL
ASM_ADDR(`ASC') ; $E6...230...ASC
ASM_ADDR(`CHRSTR') ; $E7...231...CHR$
ASM_ADDR(`LEFTSTR') ; $E8...232...LEFT$
ASM_ADDR(`RIGHTSTR') ; $E9...233...RIGHT$
ASM_ADDR(`MIDSTR') ; $EA...234...MID$
; --------------------------------
; MATH OPERATOR BRANCH TABLE
;
; ONE-BYTE PRECEDENCE CODE
; TWO-BYTE ADDRESS
; --------------------------------
P_OR = $46 ; "OR" IS LOWEST PRECEDENCE
P_AND = $50 ;
P_REL = $64 ; RELATIONAL OPERATORS
P_ADD = $79 ; BINARY + AND -
P_MUL = $7B ; * AND /
P_PWR = $7D ; EXPONENTIATION
P_NEQ = $7F ; UNARY - AND COMPARISON =
; --------------------------------
MATHTBL ASM_DATA(`P_ADD')
ASM_ADDR(`FADDT-1') ; $C8...200...+
ASM_DATA(`P_ADD')
ASM_ADDR(`FSUBT-1') ; $C9...201...-
ASM_DATA(`P_MUL')
ASM_ADDR(`FMULTT-1') ; $CA...202...*
ASM_DATA(`P_MUL')
ASM_ADDR(`FDIVT-1') ; $CB...203.../
ASM_DATA(`P_PWR')
ASM_ADDR(`FPWRT-1') ; $CC...204...^
ASM_DATA(`P_AND')
ASM_ADDR(`ANDOP-1') ; $CD...205...AND
ASM_DATA(`P_OR')
ASM_ADDR(`OR-1') ; $CE...206...OR
M_NEG ASM_DATA(`P_NEQ')
ASM_ADDR(`NEGOP-1') ; $CF...207...>
MEQUU ASM_DATA(`P_NEQ')
ASM_ADDR(`EQUOP-1') ; $D0...208...=
M_REL ASM_DATA(`P_REL')
ASM_ADDR(`RELOPS-1') ; $D1...209...<
; --------------------------------
; TOKEN NAME TABLE
; --------------------------------
;
TOKEN_NAME_TABLE LHASCII(`END') ; $80...128
LHASCII(`FOR') ; $81...129
LHASCII(`NEXT') ; $82...130
LHASCII(`DATA') ; $83...131
LHASCII(`INPUT') ; $84...132
LHASCII(`DEL') ; $85...133
LHASCII(`DIM') ; $86...134
LHASCII(`READ') ; $87...135
LHASCII(`GR') ; $88...136
LHASCII(`TEXT') ; $89...137
LHASCII(`PR#') ; $8A...138
LHASCII(`IN#') ; $8B...139
LHASCII(`CALL') ; $8C...140
LHASCII(`PLOT') ; $8D...141
LHASCII(`HLIN') ; $8E...142
LHASCII(`VLIN') ; $8F...143
LHASCII(`HGR2') ; $90...144
LHASCII(`HGR') ; $91...145
LHASCII(`HCOLOR=') ; $92...146
LHASCII(`HPLOT') ; $93...147
LHASCII(`DRAW') ; $94...148
LHASCII(`XDRAW') ; $95...149
LHASCII(`HTAB') ; $96...150
LHASCII(`HOME') ; $97...151
LHASCII(`ROT=') ; $98...152
LHASCII(`SCALE=') ; $99...153
LHASCII(`SHLOAD') ; $9A...154
LHASCII(`TRACE') ; $9B...155
LHASCII(`NOTRACE') ; $9C...156
LHASCII(`NORMAL') ; $9D...157
LHASCII(`INVERSE') ; $9E...158
LHASCII(`FLASH') ; $9F...159
LHASCII(`COLOR=') ; $A0...160
LHASCII(`POP') ; $A1...161
LHASCII(`VTAB') ; $A2...162
LHASCII(`HIMEM:') ; $A3...163
LHASCII(`LOMEM:') ; $A4...164
LHASCII(`ONERR') ; $A5...165
LHASCII(`RESUME') ; $A6...166
LHASCII(`RECALL') ; $A7...167
LHASCII(`STORE') ; $A8...168
LHASCII(`SPEED=') ; $A9...169
LHASCII(`LET') ; $AA...170
LHASCII(`GOTO') ; $AB...171
LHASCII(`RUN') ; $AC...172
LHASCII(`IF') ; $AD...173
LHASCII(`RESTORE') ; $AE...174
LHASCII(`&') ; $AF...175
LHASCII(`GOSUB') ; $B0...176
LHASCII(`RETURN') ; $B1...177
LHASCII(`REM') ; $B2...178
LHASCII(`STOP') ; $B3...179
LHASCII(`ON') ; $B4...180
LHASCII(`WAIT') ; $B5...181
LHASCII(`LOAD') ; $B6...182
LHASCII(`SAVE') ; $B7...183
LHASCII(`DEF') ; $B8...184
LHASCII(`POKE') ; $B9...185
LHASCII(`PRINT') ; $BA...186
LHASCII(`CONT') ; $BB...187
LHASCII(`LIST') ; $BC...188
LHASCII(`CLEAR') ; $BD...189
LHASCII(`GET') ; $BE...190
LHASCII(`NEW') ; $BF...191
LOASCII(`TAB') ; $C0...192
ASM_DATA($A8)
LHASCII(`TO') ; $C1...193
LHASCII(`FN') ; $C2...194
LOASCII(`SPC') ; $C3...195
ASM_DATA($A8)
LHASCII(`THEN') ; $C4...196
LHASCII(`AT') ; $C5...197
LHASCII(`NOT') ; $C6...198
LHASCII(`STEP') ; $C7...199
LHASCII(`+') ; $C8...200
LHASCII(`-') ; $C9...201
LHASCII(`*') ; $CA...202
LHASCII(`/') ; $CB...203
ASM_DATA($DE)
; LHASCII(`^') ; $CC...204
LHASCII(`AND') ; $CD...205
LHASCII(`OR') ; $CE...206
LHASCII(`>') ; $CF...207
LHASCII(`=') ; $D0...208
LHASCII(`<') ; $D1...209
LHASCII(`SGN') ; $D2...210
LHASCII(`INT') ; $D3...211
LHASCII(`ABS') ; $D4...212
LHASCII(`USR') ; $D5...213
LHASCII(`FRE') ; $D6...214
LOASCII(`SCRN') ; $D7...215
ASM_DATA($A8)
LHASCII(`PDL') ; $D8...216
LHASCII(`POS') ; $D9...217
LHASCII(`SQR') ; $DA...218
LHASCII(`RND') ; $DB...219
LHASCII(`LOG') ; $DC...220
LHASCII(`EXP') ; $DD...221
LHASCII(`COS') ; $DE...222
LHASCII(`SIN') ; $DF...223
LHASCII(`TAN') ; $E0...224
LHASCII(`ATN') ; $E1...225
LHASCII(`PEEK') ; $E2...226
LHASCII(`LEN') ; $E3...227
LHASCII(`STR$') ; $E4...228
LHASCII(`VAL') ; $E5...229
LHASCII(`ASC') ; $E6...230
LHASCII(`CHR$') ; $E7...231
LHASCII(`LEFT$') ; $E8...232
LHASCII(`RIGHT$') ; $E9...233
LHASCII(`MID$') ; $EA...234
ASM_DATA(0) ; END OF TOKEN NAME TABLE
; --------------------------------
; --------------------------------
; ERROR MESSAGES
; --------------------------------
ERROR_MESSAGES
ERR_NOFOR = *-ERROR_MESSAGES
LHASCII(`NEXT WITHOUT FOR')
ERR_SYNTAX = *-ERROR_MESSAGES
LHASCII(`SYNTAX')
ERR_NOGOSUB = *-ERROR_MESSAGES
LHASCII(`RETURN WITHOUT GOSUB')
ERR_NODATA = *-ERROR_MESSAGES
LHASCII(`OUT OF DATA')
ERR_ILLQTY = *-ERROR_MESSAGES
LHASCII(`ILLEGAL QUANTITY')
ERR_OVERFLOW = *-ERROR_MESSAGES
LHASCII(`OVERFLOW')
ERR_MEMFULL = *-ERROR_MESSAGES
LHASCII(`OUT OF MEMORY')
ERR_UNDEFSTAT = *-ERROR_MESSAGES
LOASCII(`UNDEF')
ASM_DATA($27)
LHASCII(`D STATEMENT')
ERR_BADSUBS = *-ERROR_MESSAGES
LHASCII(`BAD SUBSCRIPT')
ERR_REDIMD = *-ERROR_MESSAGES
LOASCII(`REDIM')
ASM_DATA($27)
LHASCII(`D ARRAY')
ERR_ZERODIV = *-ERROR_MESSAGES
LHASCII(`DIVISION BY ZERO')
ERR_ILLDIR = *-ERROR_MESSAGES
LHASCII(`ILLEGAL DIRECT')
ERR_BADTYPE = *-ERROR_MESSAGES
LHASCII(`TYPE MISMATCH')
ERR_STRLONG = *-ERROR_MESSAGES
LHASCII(`STRING TOO LONG')
ERR_FRMCPX = *-ERROR_MESSAGES
LHASCII(`FORMULA TOO COMPLEX')
ERR_CANTCONT = *-ERROR_MESSAGES
LOASCII(`CAN')
ASM_DATA($27)
LHASCII(`T CONTINUE')
ERR_UNDEFFUNC = *-ERROR_MESSAGES
LOASCII(`UNDEF')
ASM_DATA($27)
LHASCII(`D FUNCTION')
; --------------------------------
QT_ERROR LOASCII(` ERROR')
ASM_DATA($07,0)
QT_IN LOASCII(` IN ')
ASM_DATA(0)
QT_BREAK ASM_DATA($0D)
LOASCII(`BREAK')
ASM_DATA($07,0)
; --------------------------------
; CALLED BY "NEXT" AND "FOR" TO SCAN THROUGH
; THE STACK FOR A FRAME WITH THE SAME VARIABLE.
;
; (FORPNT) = ADDRESS OF VARIABLE IF "FOR" OR "NEXT"
; = $XXFF IF CALLED FROM "RETURN"
; <<< BUG: SHOULD BE $FFXX >>>
;
; RETURNS .NE. IF VARIABLE NOT FOUND,
; (X) = STACK PNTR AFTER SKIPPING ALL FRAMES
;
; EQU. IF FOUND
; (X) = STACK PNTR OF FRAME FOUND
; --------------------------------
GTFORPNT
TSX
INX
INX
INX
INX
L_GTFORPNT_1 LDA STACK+1,X ; "FOR" FRAME HERE?
CMP #TOKEN_FOR ;
BNE L_GTFORPNT_4 ; NO
LDA FORPNT+1 ; YES -- "NEXT" WITH NO VARIABLE?
BNE L_GTFORPNT_2 ; NO, VARIABLE SPECIFIED
LDA STACK+2,X ; YES, SO USE THIS FRAME
STA FORPNT ;
LDA STACK+3,X ;
STA FORPNT+1 ;
L_GTFORPNT_2 CMP STACK+3,X ; IS VARIABLE IN THIS FRAME?
BNE L_GTFORPNT_3 ; NO
LDA FORPNT ; LOOK AT 2ND BYTE TOO
CMP STACK+2,X ; SAME VARIABLE?
BEQ L_GTFORPNT_4 ; YES
L_GTFORPNT_3 TXA ; NO, SO TRY NEXT FRAME (IF ANY)
CLC ; 18 BYTES PER FRAME
ADC #18 ;
TAX
BNE L_GTFORPNT_1 ; ...ALWAYS?
L_GTFORPNT_4 RTS
; --------------------------------
; MOVE BLOCK OF MEMORY UP
;
; ON ENTRY:
; (Y,A) = (HIGHDS) = DESTINATION END+1
; (LOWTR) = LOWEST ADDRESS OF SOURCE
; (HIGHTR) = HIGHEST SOURCE ADDRESS+1
; --------------------------------
BLTU JSR REASON ; BE SURE (Y,A) < FRETOP
STA STREND ; NEW TOP OF ARRAY STORAGE
STY STREND+1 ;
BLTU2 SEC ;
LDA HIGHTR ; COMPUTE # OF BYTES TO BE MOVED
SBC LOWTR ; (FROM LOWTR THRU HIGHTR-1)
STA INDEX ; PARTIAL PAGE AMOUNT
TAY ;
LDA HIGHTR+1 ;
SBC LOWTR+1 ;
TAX ; # OF WHOLE PAGES IN X-REG
INX ;
TYA ; # BYTES IN PARTIAL PAGE
BEQ L_BLTU2_4 ; NO PARTIAL PAGE
LDA HIGHTR ; BACK UP HIGHTR # BYTES IN PARTIAL PAGE
SEC ;
SBC INDEX ;
STA HIGHTR ;
BCS L_BLTU2_1 ;
DEC HIGHTR+1 ;
SEC ;
L_BLTU2_1 LDA HIGHDS ; BACK UP HIGHDS # BYTES IN PARTIAL PAGE
SBC INDEX ;
STA HIGHDS ;
BCS L_BLTU2_3 ;
DEC HIGHDS+1 ;
BCC L_BLTU2_3 ; ...ALWAYS
L_BLTU2_2 LDA (HIGHTR),Y ; MOVE THE BYTES
STA (HIGHDS),Y
L_BLTU2_3 DEY
BNE L_BLTU2_2 ; LOOP TO END OF THIS 256 BYTES
LDA (HIGHTR),Y ; MOVE ONE MORE BYTE
STA (HIGHDS),Y
L_BLTU2_4 DEC HIGHTR+1 ; DOWN TO NEXT BLOCK OF 256
DEC HIGHDS+1
DEX ; ANOTHER BLOCK OF 256 TO MOVE?
BNE L_BLTU2_3 ; YES
RTS ; NO, FINISHED
; --------------------------------
; CHECK IF ENOUGH ROOM LEFT ON STACK
; FOR "FOR", "GOSUB", OR EXPRESSION EVALUATION
; --------------------------------
CHKMEM ASL
ADC #54
BCS MEMERR ; ...MEM FULL ERR
STA INDEX
TSX
CPX INDEX
BCC MEMERR ; ...MEM FULL ERR
RTS
; --------------------------------
; CHECK IF ENOUGH ROOM BETWEEN ARRAYS AND STRINGS
; (Y,A) = ADDR ARRAYS NEED TO GROW TO
; --------------------------------
REASON CPY FRETOP+1 ; HIGH BYTE
BCC L_REASON_4 ; PLENTY OF ROOM
BNE L_REASON_1 ; NOT ENOUGH, TRY GARBAGE COLLECTION
CMP FRETOP ; LOW BYTE
BCC L_REASON_4 ; ENOUGH ROOM
; --------------------------------
L_REASON_1 PHA ; SAVE (Y,A), TEMP1, AND TEMP2
LDX #FAC-TEMP1-1
TYA
L_REASON_2 PHA
LDA TEMP1,X
DEX
BPL L_REASON_2
JSR GARBAG ; MAKE AS MUCH ROOM AS POSSIBLE
LDX #TEMP1+256-FAC+1 ; RESTORE TEMP1 AND TEMP2
L_REASON_3 PLA ; AND (Y,A)
STA FAC,X
INX
BMI L_REASON_3
PLA
TAY
PLA ; DID WE FIND ENOUGH ROOM?
CPY FRETOP+1 ; HIGH BYTE
BCC L_REASON_4 ; YES, AT LEAST A PAGE
BNE MEMERR ; NO, MEM FULL ERR
CMP FRETOP ; LOW BYTE
BCS MEMERR ; NO, MEM FULL ERR
L_REASON_4 RTS ; YES, RETURN
; --------------------------------
MEMERR LDX #ERR_MEMFULL
; --------------------------------
; HANDLE AN ERROR
;
; (X)=OFFSET IN ERROR MESSAGE TABLE
; (ERRFLG) > 128 IF "ON ERR" TURNED ON
; (CURLIN+1) = $FF IF IN DIRECT MODE
; --------------------------------
ERROR BIT ERRFLG ; "ON ERR" TURNED ON?
BPL L_ERROR_1 ; NO
JMP HANDLERR ; YES
L_ERROR_1 JSR CRDO ; PRINT <RETURN>
JSR OUTQUES ; PRINT "?"
L_ERROR_2 LDA ERROR_MESSAGES,X
PHA ; PRINT MESSAGE
JSR OUTDO
INX
PLA
BPL L_ERROR_2
JSR STKINI ; FIX STACK, ET AL
LDA #<QT_ERROR ; PRINT " ERROR" AND BELL
LDY #>QT_ERROR
; --------------------------------
; PRINT STRING AT (Y,A)
; PRINT CURRENT LINE # UNLESS IN DIRECT MODE
; FALL INTO WARM RESTART
; --------------------------------
PRINT_ERROR_LINNUM
JSR STROUT ; PRINT STRING AT (Y,A)
LDY CURLIN+1 ; RUNNING, OR DIRECT?
INY
BEQ RESTART ; WAS $FF, SO DIRECT MODE
JSR INPRT ; RUNNING, SO PRINT LINE NUMBER
; --------------------------------
; WARM RESTART ENTRY
;
; COME HERE FROM MONITOR BY CTL-C, 0G, 3D0G, OR E003G
; --------------------------------
RESTART
JSR CRDO ; PRINT <RETURN>
LDX #HICHAR(`]') ; PROMPT CHARACTER
JSR INLIN2 ; READ A LINE
STX TXTPTR ; SET UP CHRGET TO SCAN THE LINE
STY TXTPTR+1 ;
LSR ERRFLG ; CLEAR FLAG
JSR CHRGET ;
TAX ;
BEQ RESTART ; EMPTY LINE
LDX #$FF ; $FF IN HI-BYTE OF CURLIN MEANS
STX CURLIN+1 ; WE ARE IN DIRECT MODE
BCC NUMBERED_LINE ; CHRGET SAW DIGIT, NUMBERED LINE
JSR PARSE_INPUT_LINE ; NO NUMBER, SO PARSE IT
JMP TRACE_ ; AND TRY EXECUTING IT
; --------------------------------
; HANDLE NUMBERED LINE
; --------------------------------
NUMBERED_LINE
LDX PRGEND ; SQUASH VARIABLE TABLE
STX VARTAB
LDX PRGEND+1
STX VARTAB+1
JSR LINGET ; GET LINE #
JSR PARSE_INPUT_LINE ; AND PARSE THE INPUT LINE
STY EOL_PNTR ; SAVE INDEX TO INPUT BUFFER
JSR FNDLIN ; IS THIS LINE # ALREADY IN PROGRAM?
BCC PUT_NEW_LINE ; NO
LDY #1 ; YES, SO DELETE IT
LDA (LOWTR),Y ; LOWTR POINTS AT LINE
STA INDEX+1 ; GET HIGH BYTE OF FORWARD PNTR
LDA VARTAB
STA INDEX
LDA LOWTR+1
STA DEST+1
LDA LOWTR
DEY
SBC (LOWTR),Y
CLC
ADC VARTAB
STA VARTAB
STA DEST
LDA VARTAB+1
ADC #$FF
STA VARTAB+1
SBC LOWTR+1
TAX
SEC
LDA LOWTR
SBC VARTAB
TAY
BCS L_NUMBERED_LINE_1
INX
DEC DEST+1
L_NUMBERED_LINE_1 CLC
ADC INDEX
BCC L_NUMBERED_LINE_2
DEC INDEX+1
CLC
; --------------------------------
L_NUMBERED_LINE_2 LDA (INDEX),Y ; MOVE HIGHER LINES OF PROGRAM
STA (DEST),Y ; DOWN OVER THE DELETED LINE.
INY
BNE L_NUMBERED_LINE_2
INC INDEX+1
INC DEST+1
DEX
BNE L_NUMBERED_LINE_2
; --------------------------------
PUT_NEW_LINE
LDA INPUT_BUFFER ; ANY CHARACTERS AFTER LINE #?
BEQ FIX_LINKS ; NO, SO NOTHING TO INSERT.
LDA MEMSIZ ; YES, SO MAKE ROOM AND INSERT LINE
LDY MEMSIZ+1 ; WIPE STRING AREA CLEAN
STA FRETOP ;
STY FRETOP+1 ;
LDA VARTAB ; SET UP BLTU SUBROUTINE
STA HIGHTR ; INSERT NEW LINE.
ADC EOL_PNTR
STA HIGHDS
LDY VARTAB+1
STY HIGHTR+1
BCC L_PUT_NEW_LINE_1
INY
L_PUT_NEW_LINE_1 STY HIGHDS+1
JSR BLTU ; MAKE ROOM FOR THE LINE
LDA LINNUM ; PUT LINE NUMBER IN LINE IMAGE
LDY LINNUM+1
STA INPUT_BUFFER-2
STY INPUT_BUFFER-1
LDA STREND
LDY STREND+1
STA VARTAB
STY VARTAB+1
LDY EOL_PNTR
; ---COPY LINE INTO PROGRAM-------
L_PUT_NEW_LINE_2 LDA INPUT_BUFFER-5,Y
DEY
STA (LOWTR),Y
BNE L_PUT_NEW_LINE_2
; --------------------------------
; CLEAR ALL VARIABLES
; RE-ESTABLISH ALL FORWARD LINKS
; --------------------------------
FIX_LINKS
JSR SETPTRS ; CLEAR ALL VARIABLES
LDA TXTTAB ; POINT INDEX AT START OF PROGRAM
LDY TXTTAB+1
STA INDEX
STY INDEX+1
CLC
L_FIX_LINKS_1 LDY #1 ; HI-BYTE OF NEXT FORWARD PNTR
LDA (INDEX),Y ; END OF PROGRAM YET?
BNE L_FIX_LINKS_2 ; NO, KEEP GOING
LDA VARTAB ; YES
STA PRGEND
LDA VARTAB+1
STA PRGEND+1
JMP RESTART
L_FIX_LINKS_2 LDY #4 ; FIND END OF THIS LINE
L_FIX_LINKS_3 INY ; (NOTE MAXIMUM LENGTH < 256)
LDA (INDEX),Y ;
BNE L_FIX_LINKS_3 ;
INY ; COMPUTE ADDRESS OF NEXT LINE
TYA ;
ADC INDEX ;
TAX ;
LDY #0 ; STORE FORWARD PNTR IN THIS LINE
STA (INDEX),Y ;
LDA INDEX+1 ;
ADC #0 ; (NOTE: THIS CLEARS CARRY)
INY ;
STA (INDEX),Y ;
STX INDEX ;
STA INDEX+1 ;
BCC L_FIX_LINKS_1 ; ...ALWAYS
; --------------------------------
; --------------------------------
; READ A LINE, AND STRIP OFF SIGN BITS
; --------------------------------
INLIN LDX #$80 ; NULL PROMPT
INLIN2 STX MON_PROMPT
JSR MON_GETLN
CPX #239 ; MAXIMUM LINE LENGTH
BCC L_INLIN2_1
LDX #239 ; TRUNCATE AT 239 CHARS
L_INLIN2_1 LDA #0 ; MARK END OF LINE WITH $00 BYTE
STA INPUT_BUFFER,X
TXA
BEQ L_INLIN2_3 ; NULL INPUT LINE
L_INLIN2_2 LDA INPUT_BUFFER-1,X ; DROP SIGN BITS
AND #$7F
STA INPUT_BUFFER-1,X
DEX
BNE L_INLIN2_2
L_INLIN2_3 LDA #0 ; (Y,X) POINTS AT BUFFER-1
LDX #<(INPUT_BUFFER-1)
LDY #>(INPUT_BUFFER-1)
RTS
; --------------------------------
INCHR JSR MON_RDKEY ; *** OUGHT TO BE "BIT $C010" ***
AND #$7F
RTS
; --------------------------------
; TOKENIZE THE INPUT LINE
; --------------------------------
PARSE_INPUT_LINE
LDX TXTPTR ; INDEX INTO UNPARSED LINE
DEX ; PREPARE FOR INX AT "PARSE"
LDY #4 ; INDEX TO PARSED OUTPUT LINE
STY DATAFLG ; CLEAR SIGN-BIT OF DATAFLG
BIT LOCK ; IS THIS PROGRAM LOCKED?
BPL PARSE ; NO, GO AHEAD AND PARSE THE LINE
PLA ; YES, IGNORE INPUT AND "RUN"
PLA ; THE PROGRAM
JSR SETPTRS ; CLEAR ALL VARIABLES
JMP NEWSTT ; START RUNNING
; --------------------------------
PARSE INX ; NEXT INPUT CHARACTER
L_PARSE_1 LDA INPUT_BUFFER,X
BIT DATAFLG ; IN A "DATA" STATEMENT?
BVS L_PARSE_2 ; YES (DATAFLG = $49)
CMP #LOCHAR(` ') ; IGNORE BLANKS
BEQ PARSE ;
L_PARSE_2 STA ENDCHR ;
CMP #$22 ; START OF QUOTATION?
BEQ L_PARSE_13 ;
BVS L_PARSE_9 ; BRANCH IF IN "DATA" STATEMENT
CMP #LOCHAR(`?') ; SHORTHAND FOR "PRINT"?
BNE L_PARSE_3 ; NO
LDA #TOKEN_PRINT ; YES, REPLACE WITH "PRINT" TOKEN
BNE L_PARSE_9 ; ...ALWAYS
L_PARSE_3 CMP #LOCHAR(`0') ; IS IT A DIGIT, COLON, OR SEMI-COLON?
BCC L_PARSE_4 ; NO, PUNCTUATION !"#$%&'()*+,-./
CMP #LOCHAR(`;')+1
BCC L_PARSE_9 ; YES, NOT A TOKEN
; --------------------------------
; SEARCH TOKEN NAME TABLE FOR MATCH STARTING
; WITH CURRENT CHAR FROM INPUT LINE
; --------------------------------
L_PARSE_4 STY STRNG2 ; SAVE INDEX TO OUTPUT LINE
LDA #<(TOKEN_NAME_TABLE-$100)
STA FAC ; MAKE PNTR FOR SEARCH
LDA #>(TOKEN_NAME_TABLE-$100)
STA FAC+1
LDY #0 ; USE Y-REG WITH (FAC) TO ADDRESS TABLE
STY TKN_CNTR ; HOLDS CURRENT TOKEN-$80
DEY ; PREPARE FOR "INY" A FEW LINES DOWN
STX TXTPTR ; SAVE POSITION IN INPUT LINE
DEX ; PREPARE FOR "INX" A FEW LINES DOWN
L_PARSE_5 INY ; ADVANCE POINTER TO TOKEN TABLE
BNE L_PARSE_6 ; Y=Y+1 IS ENOUGH
INC FAC+1 ; ALSO NEED TO BUMP THE PAGE
L_PARSE_6 INX ; ADVANCE POINTER TO INPUT LINE
L_PARSE_7 LDA INPUT_BUFFER,X ; NEXT CHAR FROM INPUT LINE
CMP #LOCHAR(` ') ; THIS CHAR A BLANK?
BEQ L_PARSE_6 ; YES, IGNORE ALL BLANKS
SEC ; NO, COMPARE TO CHAR IN TABLE
SBC (FAC),Y ; SAME AS NEXT CHAR OF TOKEN NAME?
BEQ L_PARSE_5 ; YES, CONTINUE MATCHING
CMP #$80 ; MAYBE; WAS IT SAME EXCEPT FOR BIT 7?
BNE L_PARSE_14 ; NO, SKIP TO NEXT TOKEN
ORA TKN_CNTR ; YES, END OF TOKEN; GET TOKEN #
CMP #TOKENDB ; DID WE MATCH "AT"?
BNE L_PARSE_8 ; NO, SO NO AMBIGUITY
LDA INPUT_BUFFER+1,X ; "AT" COULD BE "ATN" OR "A TO"
CMP #LOCHAR(`N') ; "ATN" HAS PRECEDENCE OVER "AT"
BEQ L_PARSE_14 ; IT IS "ATN", FIND IT THE HARD WAY
CMP #LOCHAR(`O') ; "TO" HAS PRECEDENCE OVER "AT"
BEQ L_PARSE_14 ; IT IS "A TO", FIN IT THE HARD WAY
LDA #TOKENDB ; NOT "ATN" OR "A TO", SO USE "AT"
; --------------------------------
; STORE CHARACTER OR TOKEN IN OUTPUT LINE
; --------------------------------
L_PARSE_8 LDY STRNG2 ; GET INDEX TO OUTPUT LINE IN Y-REG
L_PARSE_9 INX ; ADVANCE INPUT INDEX
INY ; ADVANCE OUTPUT INDEX
STA INPUT_BUFFER-5,Y ; STORE CHAR OR TOKEN
LDA INPUT_BUFFER-5,Y ; TEST FOR EOL OR EOS
BEQ L_PARSE_17 ; END OF LINE
SEC ;
SBC #LOCHAR(`:') ; END OF STATEMENT?
BEQ L_PARSE_10 ; YES, CLEAR DATAFLG
CMP #TOKENDWTA+128-$BA ; "DATA" TOKEN?
BNE L_PARSE_11 ; NO, LEAVE DATAFLG ALONE
L_PARSE_10 STA DATAFLG ; DATAFLG = 0 OR $83-$3A = $49
L_PARSE_11 SEC ; IS IT A "REM" TOKEN?
SBC #TOKEN_REM+128-$BA
BNE L_PARSE_1 ; NO, CONTINUE PARSING LINE
STA ENDCHR ; YES, CLEAR LITERAL FLAG
; --------------------------------
; HANDLE LITERAL (BETWEEN QUOTES) OR REMARK,
; BY COPYING CHARS UP TO ENDCHR.
; --------------------------------
L_PARSE_12 LDA INPUT_BUFFER,X
BEQ L_PARSE_9 ; END OF LINE
CMP ENDCHR
BEQ L_PARSE_9 ; FOUND ENDCHR
L_PARSE_13 INY ; NEXT OUTPUT CHAR
STA INPUT_BUFFER-5,Y
INX ; NEXT INPUT CHAR
BNE L_PARSE_12 ; ...ALWAYS
; --------------------------------
; ADVANCE POINTER TO NEXT TOKEN NAME
; --------------------------------
L_PARSE_14 LDX TXTPTR ; GET POINTER TO INPUT LINE IN X-REG
INC TKN_CNTR ; BUMP (TOKEN # - $80)
L_PARSE_15 LDA (FAC),Y ; SCAN THROUGH TABLE FOR BIT7 = 1
INY ; NEXT TOKEN ONE BEYOND THAT
BNE L_PARSE_16 ; ...USUALLY ENOUGH TO BUMP Y-REG
INC FAC+1 ; NEXT SET OF 256 TOKEN CHARS
L_PARSE_16 ASL ; SEE IF SIGN BIT SET ON CHAR
BCC L_PARSE_15 ; NO, MORE IN THIS NAME
LDA (FAC),Y ; YES, AT NEXT NAME. END OF TABLE?
BNE L_PARSE_7 ; NO, NOT END OF TABLE
LDA INPUT_BUFFER,X ; YES, SO NOT A KEYWORD
BPL L_PARSE_8 ; ...ALWAYS, COPY CHAR AS IS
; ---END OF LINE------------------
L_PARSE_17 STA INPUT_BUFFER-3,Y ; STORE ANOTHER 00 ON END
DEC TXTPTR+1 ; SET TXTPTR = INPUT.BUFFER-1
LDA #<(INPUT_BUFFER-1)
STA TXTPTR
RTS
; --------------------------------
; SEARCH FOR LINE
;
; (LINNUM) = LINE # TO FIND
; IF NOT FOUND: CARRY = 0
; LOWTR POINTS AT NEXT LINE
; IF FOUND: CARRY = 1
; LOWTR POINTS AT LINE
; --------------------------------
FNDLIN LDA TXTTAB ; SEARCH FROM BEGINNING OF PROGRAM
LDX TXTTAB+1 ;
FL1 LDY #1 ; SEARCH FROM (X,A)
STA LOWTR ;
STX LOWTR+1 ;
LDA (LOWTR),Y ;
BEQ L_FL1_3 ; END OF PROGRAM, AND NOT FOUND
INY ;
INY ;
LDA LINNUM+1 ;
CMP (LOWTR),Y ;
BCC RTS_1 ; IF NOT FOUND
BEQ L_FL1_1 ;
DEY ;
BNE L_FL1_2 ;
L_FL1_1 LDA LINNUM ;
DEY ;
CMP (LOWTR),Y ;
BCC RTS_1 ; PAST LINE, NOT FOUND
BEQ RTS_1 ; IF FOUND
L_FL1_2 DEY ;
LDA (LOWTR),Y ;
TAX ;
DEY ;
LDA (LOWTR),Y ;
BCS FL1 ; ALWAYS
L_FL1_3 CLC ; RETURN CARRY = 0
RTS_1 RTS
; --------------------------------
; "NEW" STATEMENT
; --------------------------------
NEW BNE RTS_1 ; IGNORE IF MORE TO THE STATEMENT
SCRTCH LDA #0
STA LOCK
TAY
STA (TXTTAB),Y
INY
STA (TXTTAB),Y
LDA TXTTAB
ADC #2 ; (CARRY WASN'T CLEARED, SO "NEW" USUALLY
STA VARTAB ; ADDS 3, WHEREAS "FP" ADDS 2.)
STA PRGEND
LDA TXTTAB+1
ADC #0
STA VARTAB+1
STA PRGEND+1
; --------------------------------
SETPTRS
JSR STXTPT ; SET TXTPTR TO TXTTAB - 1
LDA #0 ; (THIS COULD HAVE BEEN ".HS 2C")
; --------------------------------
; "CLEAR" STATEMENT
; --------------------------------
CLEAR BNE RTS_2 ; IGNORE IF NOT AT END OF STATEMENT
CLEARC LDA MEMSIZ ; CLEAR STRING AREA
LDY MEMSIZ+1 ;
STA FRETOP ;
STY FRETOP+1 ;
LDA VARTAB ; CLEAR ARRAY AREA
LDY VARTAB+1 ;
STA ARYTAB ;
STY ARYTAB+1 ;
STA STREND ; LOW END OF FREE SPACE
STY STREND+1 ;
JSR RESTORE ; SET "DATA" POINTER TO BEGINNING
; --------------------------------
STKINI LDX #TEMPST
STX TEMPPT
PLA ; SAVE RETURN ADDRESS
TAY ;
PLA ;
LDX #$F8 ; START STACK AT $F8,
TXS ; LEAVING ROOM FOR PARSING LINES
PHA ; RESTORE RETURN ADDRESS
TYA
PHA
LDA #0
STA OLDTEXT+1
STA SUBFLG
RTS_2 RTS
; --------------------------------
; SET TXTPTR TO BEGINNING OF PROGRAM
; --------------------------------
STXTPT CLC ; TXTPTR = TXTTAB - 1
LDA TXTTAB
ADC #$FF
STA TXTPTR
LDA TXTTAB+1
ADC #$FF
STA TXTPTR+1
RTS
; --------------------------------
; "LIST" STATEMENT
; --------------------------------
LIST BCC L_LIST_1 ; NO LINE # SPECIFIED
BEQ L_LIST_1 ; ---DITTO---
CMP #TOKEN_MINUS ; IF DASH OR COMMA, START AT LINE 0
BEQ L_LIST_1 ; IS IS A DASH
CMP #LOCHAR(`,') ; COMMA?
BNE RTS_2 ; NO, ERROR
L_LIST_1 JSR LINGET ; CONVERT LINE NUMBER IF ANY
JSR FNDLIN ; POINT LOWTR TO 1ST LINE
JSR CHRGOT ; RANGE SPECIFIED?
BEQ L_LIST_3 ; NO
CMP #TOKEN_MINUS
BEQ L_LIST_2
CMP #LOCHAR(`,')
BNE RTS_1
L_LIST_2 JSR CHRGET ; GET NEXT CHAR
JSR LINGET ; CONVERT SECOND LINE #
BNE RTS_2 ; BRANCH IF SYNTAX ERR
L_LIST_3 PLA ; POP RETURN ADRESS
PLA ; (GET BACK BY "JMP NEWSTT")
LDA LINNUM ; IF NO SECOND NUMBER, USE $FFFF
ORA LINNUM+1 ;
BNE LIST_0 ; THERE WAS A SECOND NUMBER
LDA #$FF ; MAX END RANGE
STA LINNUM ;
STA LINNUM+1 ;
LIST_0 LDY #1 ;
LDA (LOWTR),Y ; HIGH BYTE OF LINK
BEQ LIST_3 ; END OF PROGRAM
JSR ISCNTC ; CHECK IF CONTROL-C HAS BEEN TYPED
JSR CRDO ; NO, PRINT <RETURN>
INY ;
LDA (LOWTR),Y ; GET LINE #, COMPARE WITH END RANGE
TAX ;
INY ;
LDA (LOWTR),Y ;
CMP LINNUM+1 ;
BNE L_LIST_0_5 ;
CPX LINNUM ;
BEQ L_LIST_0_6 ; ON LAST LINE OF RANGE
L_LIST_0_5 BCS LIST_3 ; FINISHED THE RANGE
; ---LIST ONE LINE----------------
L_LIST_0_6 STY FORPNT ;
JSR LINPRT ; PRINT LINE # FROM X,A
LDA #LOCHAR(` ') ; PRINT SPACE AFTER LINE #
LIST_1 LDY FORPNT ;
AND #$7F ;
LIST_2 JSR OUTDO ;
LDA MON_CH ; IF PAST COLUMN 33, START A NEW LINE
CMP #33 ;
BCC L_LIST_2_1 ; < 33
JSR CRDO ; PRINT <RETURN>
LDA #5 ; AND TAB OVER 5
STA MON_CH ;
L_LIST_2_1 INY ;
LDA (LOWTR),Y ;
BNE LIST_4 ; NOT END OF LINE YET
TAY ; END OF LINE
LDA (LOWTR),Y ; GET LINK TO NEXT LINE
TAX ;
INY ;
LDA (LOWTR),Y ;
STX LOWTR ; POINT TO NEXT LINE
STA LOWTR+1 ;
BNE LIST_0 ; BRANCH IF NOT END OF PROGRAM
LIST_3 LDA #$0D ; PRINT <RETURN>
JSR OUTDO ;
JMP NEWSTT ; TO NEXT STATEMENT
; --------------------------------
GETCHR INY ; PICK UP CHAR FROM TABLE
BNE L_GETCHR_1 ;
INC FAC+1 ;
L_GETCHR_1 LDA (FAC),Y ;
RTS ;
; --------------------------------
LIST_4 BPL LIST_2 ; BRANCH IF NOT A TOKEN
SEC ;
SBC #$7F ; CONVERT TOKEN TO INDEX
TAX ;
STY FORPNT ; SAVE LINE POINTER
LDY #<(TOKEN_NAME_TABLE-$100)
STY FAC ; POINT FAC TO TABLE
LDY #>(TOKEN_NAME_TABLE-$100)
STY FAC+1
LDY #$FF
L_LIST_4_1 DEX ; SKIP KEYWORDS UNTIL REACH THIS ONE
BEQ L_LIST_4_3 ;
L_LIST_4_2 JSR GETCHR ; BUMP Y, GET CHAR FROM TABLE
BPL L_LIST_4_2 ; NOT AT END OF KEYWORD YET
BMI L_LIST_4_1 ; END OF KEYWORD, ALWAYS BRANCHES
L_LIST_4_3 LDA #LOCHAR(` ') ; FOUND THE RIGHT KEYWORD
JSR OUTDO ; PRINT LEADING SPACE
L_LIST_4_4 JSR GETCHR ; PRINT THE KEYWORD
BMI L_LIST_4_5 ; LAST CHAR OF KEYWORD
JSR OUTDO ;
BNE L_LIST_4_4 ; ...ALWAYS
L_LIST_4_5 JSR OUTDO ; PRINT LAST CHAR OF KEYWORD
LDA #LOCHAR(` ') ; PRINT TRAILING SPACE
BNE LIST_1 ; ...ALWAYS, BACK TO ACTUAL LINE
; --------------------------------
; "FOR" STATEMENT
;
; FOR PUSHES 18 BYTES ON THE STACK:
; 2 -- TXTPTR
; 2 -- LINE NUMBER
; 5 -- INITIAL (CURRENT) FOR VARIABLE VALUE
; 1 -- STEP SIGN
; 5 -- STEP VALUE
; 2 -- ADDRESS OF FOR VARIABLE IN VARTAB
; 1 -- FOR TOKEN ($81)
; --------------------------------
FOR LDA #$80 ;
STA SUBFLG ; SUBSCRIPTS NOT ALLOWED
JSR LET ; DO <VAR> = <EXP>, STORE ADDR IN FORPNT
JSR GTFORPNT ; IS THIS FOR VARIABLE ACTIVE?
BNE L_FOR_1 ; NO
TXA ; YES, CANCEL IT AND ENCLOSED LOOPS
ADC #15 ; CARRY=1, THIS ADDS 16
TAX ; X WAS ALREADY S+2
TXS ;
L_FOR_1 PLA ; POP RETURN ADDRESS TOO
PLA ;
LDA #9 ; BE CERTAIN ENOUGH ROOM IN STACK
JSR CHKMEM ;
JSR DATAN ; SCAN AHEAD TO NEXT STATEMENT
CLC ; PUSH STATEMENT ADDRESS ON STACK
TYA ;
ADC TXTPTR ;
PHA ;
LDA TXTPTR+1 ;
ADC #0 ;
PHA ;
LDA CURLIN+1 ; PUSH LINE NUMBER ON STACK
PHA ;
LDA CURLIN ;
PHA ;
LDA #TOKEN_TO ;
JSR SYNCHR ; REQUIRE "TO"
JSR CHKNUM ; <VAR> = <EXP> MUST BE NUMERIC
JSR FRMNUM ; GET FINAL VALUE, MUST BE NUMERIC
LDA FAC_SIGN ; PUT SIGN INTO VALUE IN FAC
ORA #$7F ;
AND FAC+1 ;
STA FAC+1 ;
LDA #<STEP ; SET UP FOR RETURN
LDY #>STEP ; TO STEP
STA INDEX
STY INDEX+1
JMP FRM_STACK_3 ; RETURNS BY "JMP (INDEX)"
; --------------------------------
; "STEP" PHRASE OF "FOR" STATEMENT
; --------------------------------
STEP LDA #<CON_ONE ; STEP DEFAULT=1
LDY #>CON_ONE
JSR LOAD_FAC_FROM_YA
JSR CHRGOT
CMP #TOKEN_STEP
BNE L_STEP_1 ; USE DEFAULT VALUE OF 1.0
JSR CHRGET ; STEP SPECIFIED, GET IT
JSR FRMNUM
L_STEP_1 JSR SIGN
JSR FRM_STACK_2
LDA FORPNT+1
PHA
LDA FORPNT
PHA
LDA #TOKEN_FOR
PHA
; --------------------------------
; PERFORM NEXT STATEMENT
; --------------------------------
NEWSTT TSX ; REMEMBER THE STACK POSITION
STX REMSTK ;
JSR ISCNTC ; SEE IF CONTROL-C HAS BEEN TYPED
LDA TXTPTR ; NO, KEEP EXECUTING
LDY TXTPTR+1 ;
LDX CURLIN+1 ; =$FF IF IN DIRECT MODE
INX ; $FF TURNS INTO $00
BEQ L_NEWSTT_1 ; IN DIRECT MODE
STA OLDTEXT ; IN RUNNING MODE
STY OLDTEXT+1 ;
L_NEWSTT_1 LDY #0 ;
LDA (TXTPTR),Y ; END OF LINE YET?
BNE COLON_ ; NO
LDY #2 ; YES, SEE IF END OF PROGRAM
LDA (TXTPTR),Y ;
CLC ;
BEQ GOEND ; YES, END OF PROGRAM
INY ;
LDA (TXTPTR),Y ; GET LINE # OF NEXT LINE
STA CURLIN ;
INY ;
LDA (TXTPTR),Y ;
STA CURLIN+1 ;
TYA ; ADJUST TXTPTR TO START
ADC TXTPTR ; OF NEW LINE
STA TXTPTR
BCC L_NEWSTT_2
INC TXTPTR+1
L_NEWSTT_2
; --------------------------------
TRACE_ BIT TRCFLG ; IS TRACE ON?
BPL L_TRACE__1 ; NO
LDX CURLIN+1 ; YES, ARE WE RUNNING?
INX ;
BEQ L_TRACE__1 ; NOT RUNNING, SO DON'T TRACE
LDA #LOCHAR(`#') ; PRINT "#"
JSR OUTDO ;
LDX CURLIN ;
LDA CURLIN+1 ;
JSR LINPRT ; PRINT LINE NUMBER
JSR OUTSP ; PRINT TRAILING SPACE
L_TRACE__1 JSR CHRGET ; GET FIRST CHR OF STATEMENT
JSR EXECUTE_STATEMENT ; AND START PROCESSING
JMP NEWSTT ; BACK FOR MORE
; --------------------------------
GOEND BEQ END4
; --------------------------------
; EXECUTE A STATEMENT
;
; (A) IS FIRST CHAR OF STATEMENT
; CARRY IS SET
; --------------------------------
EXECUTE_STATEMENT
BEQ RTS_3 ; END OF LINE, NULL STATEMENT
EXECUTE_STATEMENT_1 ;
SBC #$80 ; FIRST CHAR A TOKEN?
BCC L_EXECUTE_STATEMENT_1_1 ; NOT TOKEN, MUST BE "LET"
CMP #$40 ; STATEMENT-TYPE TOKEN?
BCS SYNERR_1 ; NO, SYNTAX ERROR
ASL ; DOUBLE TO GET INDEX
TAY ; INTO ADDRESS TABLE
LDA TOKEN_ADDRESS_TABLE+1,Y
PHA ; PUT ADDRESS ON STACK
LDA TOKEN_ADDRESS_TABLE,Y
PHA
JMP CHRGET ; GET NEXT CHR & RTS TO ROUTINE
; --------------------------------
L_EXECUTE_STATEMENT_1_1 JMP LET ; MUST BE <VAR> = <EXP>
; --------------------------------
COLON_ CMP #LOCHAR(`:')
BEQ TRACE_
SYNERR_1 JMP SYNERR
; --------------------------------
; "RESTORE" STATEMENT
; --------------------------------
RESTORE
SEC ; SET DATPTR TO BEGINNING OF PROGRAM
LDA TXTTAB
SBC #1
LDY TXTTAB+1
BCS SETDA
DEY
; ---SET DATPTR TO Y,A------------
SETDA STA DATPTR
STY DATPTR+1
RTS_3 RTS
; --------------------------------
; SEE IF CONTROL-C TYPED
; --------------------------------
ISCNTC LDA KEYBOARD
CMP #$83
BEQ L_ISCNTC_1
RTS
L_ISCNTC_1 JSR INCHR ; <<< SHOULD BE "BIT $C010" >>>
CONTROL_C_TYPED
LDX #$FF ; CONTROL C ATTEMPTED
BIT ERRFLG ; "ON ERR" ENABLED?
BPL L_CONTROL_C_TYPED_2 ; NO
JMP HANDLERR ; YES, RETURN ERR CODE = 255
L_CONTROL_C_TYPED_2 CMP #3 ; SINCE IT IS CTRL-C, SET Z AND C BITS
; --------------------------------
; "STOP" STATEMENT
; --------------------------------
STOP BCS END2 ; CARRY=1 TO FORCE PRINTING "BREAK AT.."
; --------------------------------
; "END" STATEMENT
; --------------------------------
ENDX CLC ; CARRY=0 TO AVOID PRINTING MESSAGE
END2 BNE RTS_4 ; IF NOT END OF STATEMENT, DO NOTHING
LDA TXTPTR
LDY TXTPTR+1
LDX CURLIN+1
INX ; RUNNING?
BEQ L_END2_1 ; NO, DIRECT MODE
STA OLDTEXT
STY OLDTEXT+1
LDA CURLIN
LDY CURLIN+1
STA OLDLIN
STY OLDLIN+1
L_END2_1 PLA
PLA
END4 LDA #<QT_BREAK ; " BREAK" AND BELL
LDY #>QT_BREAK
BCC L_END4_1
JMP PRINT_ERROR_LINNUM
L_END4_1 JMP RESTART
; --------------------------------
; "CONT" COMMAND
; --------------------------------
CONT BNE RTS_4 ; IF NOT END OF STATEMENT, DO NOTHING
LDX #ERR_CANTCONT
LDY OLDTEXT+1 ; MEANINGFUL RE-ENTRY?
BNE L_CONT_1 ; YES
JMP ERROR ; NO
L_CONT_1 LDA OLDTEXT ; RESTORE TXTPTR
STA TXTPTR ;
STY TXTPTR+1 ;
LDA OLDLIN ; RESTORE LINE NUMBER
LDY OLDLIN+1
STA CURLIN
STY CURLIN+1
RTS_4 RTS
; --------------------------------
; "SAVE" COMMAND
; WRITES PROGRAM ON CASSETTE TAPE
; --------------------------------
SAVE SEC
LDA PRGEND ; COMPUTE PROGRAM LENGTH
SBC TXTTAB
STA LINNUM
LDA PRGEND+1
SBC TXTTAB+1
STA LINNUM+1
JSR VARTIO ; SET UP TO WRITE 3 BYTE HEADER
JSR MON_WRITE ; WRITE 'EM
JSR PROGIO ; SET UP TO WRITE THE PROGRAM
JMP MON_WRITE ; WRITE IT
; --------------------------------
; "LOAD" COMMAND
; READS A PROGRAM FROM CASSETTE TAPE
; --------------------------------
LOAD JSR VARTIO ; SET UP TO READ 3 BYTE HEADER
JSR MON_READ ; READ LENGTH, LOCK BYTE
CLC ;
LDA TXTTAB ; COMPUTE END ADDRESS
ADC LINNUM ;
STA VARTAB ;
LDA TXTTAB+1 ;
ADC LINNUM+1 ;
STA VARTAB+1 ;
LDA TEMPPT ; LOCK BYTE
STA LOCK ;
JSR PROGIO ; SET UP TO READ PROGRAM
JSR MON_READ ; READ IT
BIT LOCK ; IF LOCKED, START RUNNING NOW
BPL L_LOAD_1 ; NOT LOCKED
JMP SETPTRS ; LOCKED, START RUNNING
L_LOAD_1 JMP FIX_LINKS ; JUST FIX FORWARD POINTERS
; --------------------------------
VARTIO LDA #LINNUM ; SET UP TO READ/WRITE 3 BYTE HEADER
LDY #0
STA MON_A1L
STY MON_A1H
LDA #TEMPPT
STA MON_A2L
STY MON_A2H
STY LOCK
RTS
; --------------------------------
PROGIO LDA TXTTAB ; SET UP TO READ/WRITE PROGRAM
LDY TXTTAB+1
STA MON_A1L
STY MON_A1H
LDA VARTAB
LDY VARTAB+1
STA MON_A2L
STY MON_A2H
RTS
; --------------------------------
; --------------------------------
; "RUN" COMMAND
; --------------------------------
RUN PHP ; SAVE STATUS WHILE SUBTRACTING
DEC CURLIN+1 ; IF WAS $FF (MEANING DIRECT MODE)
; MAKE IT "RUNNING MODE"
PLP ; GET STATUS AGAIN (FROM CHRGET)
BNE L_RUN_1 ; PROBABLY A LINE NUMBER
JMP SETPTRS ; START AT BEGINNING OF PROGRAM
L_RUN_1 JSR CLEARC ; CLEAR VARIABLES
JMP GO_TO_LINE ; JOIN GOSUB STATEMENT
; --------------------------------
; "GOSUB" STATEMENT
;
; LEAVES 7 BYTES ON STACK:
; 2 -- RETURN ADDRESS (NEWSTT)
; 2 -- TXTPTR
; 2 -- LINE #
; 1 -- GOSUB TOKEN ($B0)
; --------------------------------
GOSUB LDA #3 ; BE SURE ENOUGH ROOM ON STACK
JSR CHKMEM
LDA TXTPTR+1
PHA
LDA TXTPTR
PHA
LDA CURLIN+1
PHA
LDA CURLIN
PHA
LDA #TOKEN_GOSUB
PHA
GO_TO_LINE
JSR CHRGOT
JSR GOTO
JMP NEWSTT
; --------------------------------
; "GOTO" STATEMENT
; ALSO USED BY "RUN" AND "GOSUB"
; --------------------------------
GOTO JSR LINGET ; GET GOTO LINE
JSR REMN ; POINT Y TO EOL
LDA CURLIN+1 ; IS CURRENT PAGE < GOTO PAGE?
CMP LINNUM+1 ;
BCS L_GOTO_1 ; SEARCH FROM PROG START IF NOT
TYA ; OTHERWISE SEARCH FROM NEXT LINE
SEC ;
ADC TXTPTR ;
LDX TXTPTR+1 ;
BCC L_GOTO_2 ;
INX ;
BCS L_GOTO_2 ;
L_GOTO_1 LDA TXTTAB ; GET PROGRAM BEGINNING
LDX TXTTAB+1 ;
L_GOTO_2 JSR FL1 ; SEARCH FOR GOTO LINE
BCC UNDERR ; ERROR IF NOT THERE
LDA LOWTR ; TXTPTR = START OF THE DESTINATION LINE
SBC #1 ;
STA TXTPTR ;
LDA LOWTR+1 ;
SBC #0 ;
STA TXTPTR+1 ;
RTS_5 RTS ; RETURN TO NEWSTT OR GOSUB
; --------------------------------
; "POP" AND "RETURN" STATEMENTS
; --------------------------------
POP BNE RTS_5
LDA #$FF
STA FORPNT ; <<< BUG: SHOULD BE FORPNT+1 >>>
; <<< SEE "ALL ABOUT APPLESOFT", PAGES 100,101 >>>
JSR GTFORPNT ; TO CANCEL FOR/NEXT IN SUB
TXS
CMP #TOKEN_GOSUB ; LAST GOSUB FOUND?
BEQ RETURN
LDX #ERR_NOGOSUB
ASM_DATA($2C) ; FAKE
UNDERR LDX #ERR_UNDEFSTAT
JMP ERROR
; --------------------------------
SYNERR_2 JMP SYNERR
; --------------------------------
RETURN PLA ; DISCARD GOSUB TOKEN
PLA
CPY #<(TOKEN_POP*2)
BEQ PULL3 ; BRANCH IF A POP
STA CURLIN ; PULL LINE #
PLA
STA CURLIN+1
PLA
STA TXTPTR ; PULL TXTPTR
PLA
STA TXTPTR+1
; --------------------------------
; "DATA" STATEMENT
; EXECUTED BY SKIPPING TO NEXT COLON OR EOL
; --------------------------------
DATA JSR DATAN ; MOVE TO NEXT STATEMENT
; --------------------------------
; ADD (Y) TO TXTPTR
; --------------------------------
ADDON TYA
CLC
ADC TXTPTR
STA TXTPTR
BCC L_ADDON_1
INC TXTPTR+1
L_ADDON_1
RTS_6 RTS
; --------------------------------
; SCAN AHEAD TO NEXT ":" OR EOL
; --------------------------------
DATAN LDX #LOCHAR(`:') ; GET OFFSET IN Y TO EOL OR ":"
ASM_DATA($2C) ; FAKE
; --------------------------------
REMN LDX #0 ; TO EOL ONLY
STX CHARAC
LDY #0
STY ENDCHR
L_REMN_1 LDA ENDCHR ; TRICK TO COUNT QUOTE PARITY
LDX CHARAC
STA CHARAC
STX ENDCHR
L_REMN_2 LDA (TXTPTR),Y
BEQ RTS_6 ; END OF LINE
CMP ENDCHR
BEQ RTS_6 ; COLON IF LOOKING FOR COLONS
INY
CMP #$22
BNE L_REMN_2
BEQ L_REMN_1 ; ...ALWAYS
; --------------------------------
PULL3 PLA
PLA
PLA
RTS
; --------------------------------
; "IF" STATEMENT
; --------------------------------
IF JSR FRMEVL
JSR CHRGOT
CMP #TOKEN_GOTO
BEQ L_IF_1
LDA #TOKEN_THEN
JSR SYNCHR
L_IF_1 LDA FAC ; CONDITION TRUE OR FALSE?
BNE IF_TRUE ; BRANCH IF TRUE
; --------------------------------
; "REM" STATEMENT, OR FALSE "IF" STATEMENT
; --------------------------------
REM JSR REMN ; SKIP REST OF LINE
BEQ ADDON ; ...ALWAYS
; --------------------------------
IF_TRUE
JSR CHRGOT ; COMMAND OR NUMBER?
BCS L_IF_TRUE_1 ; COMMAND
JMP GOTO ; NUMBER
L_IF_TRUE_1 JMP EXECUTE_STATEMENT
; --------------------------------
; "ON" STATEMENT
;
; ON <EXP> GOTO <LIST>
; ON <EXP> GOSUB <LIST>
; --------------------------------
ONGOTO JSR GETBYT ; EVALUATE <EXP>, AS BYTE IN FAC+4
PHA ; SAVE NEXT CHAR ON STACK
CMP #TOKEN_GOSUB
BEQ ON_2
ON_1 CMP #TOKEN_GOTO
BNE SYNERR_2
ON_2 DEC FAC+4 ; COUNTED TO RIGHT ONE YET?
BNE L_ON_2_3 ; NO, KEEP LOOKING
PLA ; YES, RETRIEVE CMD
JMP EXECUTE_STATEMENT_1 ; AND GO.
L_ON_2_3 JSR CHRGET ; PRIME CONVERT SUBROUTINE
JSR LINGET ; CONVERT LINE #
CMP #LOCHAR(`,') ; TERMINATE WITH COMMA?
BEQ ON_2 ; YES
PLA ; NO, END OF LIST, SO IGNORE
RTS_7 RTS
; --------------------------------
; CONVERT LINE NUMBER
; --------------------------------
LINGET LDX #0 ; ASC # TO HEX ADDRESS
STX LINNUM ; IN LINNUM.
STX LINNUM+1 ;
L_LINGET_1 BCS RTS_7 ; NOT A DIGIT
SBC #LOCHAR(`0')-1 ; CONVERT DIGIT TO BINARY
STA CHARAC ; SAVE THE DIGIT
LDA LINNUM+1 ; CHECK RANGE
STA INDEX ;
CMP #>6400 ; LINE # TOO LARGE?
BCS ON_1 ; YES, > 63999, GO INDIRECTLY TO
; "SYNTAX ERROR".
; <<<<<DANGEROUS CODE>>>>>
; NOTE THAT IF (A) = $AB ON THE LINE ABOVE,
; ON_1 WILL COMPARE = AND CAUSE A CATASTROPHIC
; JUMP TO $22D9 (FOR GOTO), OR OTHER LOCATIONS
; FOR OTHER CALLS TO LINGET.
;
; YOU CAN SEE THIS IS YOU FIRST PUT "BRK" IN $22D9,
; THEN TYPE "GO TO 437761".
;
; ANY VALUE FROM 437760 THROUGH 440319 WILL CAUSE
; THE PROBLEM. ($AB00 - $ABFF)
; <<<<<DANGEROUS CODE>>>>>
LDA LINNUM ; MULTIPLY BY TEN
ASL
ROL INDEX
ASL
ROL INDEX
ADC LINNUM
STA LINNUM
LDA INDEX
ADC LINNUM+1
STA LINNUM+1
ASL LINNUM
ROL LINNUM+1
LDA LINNUM
ADC CHARAC ; ADD DIGIT
STA LINNUM
BCC L_LINGET_2
INC LINNUM+1
L_LINGET_2 JSR CHRGET ; GET NEXT CHAR
JMP L_LINGET_1 ; MORE CONVERTING
; --------------------------------
; "LET" STATEMENT
;
; LET <VAR> = <EXP>
; <VAR> = <EXP>
; --------------------------------
LET JSR PTRGET ; GET <VAR>
STA FORPNT
STY FORPNT+1
LDA #TOKENEQUUAL
JSR SYNCHR
LDA VALTYP+1 ; SAVE VARIABLE TYPE
PHA
LDA VALTYP
PHA
JSR FRMEVL ; EVALUATE <EXP>
PLA
ROL
JSR CHKVAL
BNE LET_STRING
PLA
; --------------------------------
LET2 BPL L_LET2_1 ; REAL VARIABLE
JSR ROUND_FAC ; INTEGER VAR: ROUND TO 32 BITS
JSR AYINT ; TRUNCATE TO 16-BITS
LDY #0
LDA FAC+3
STA (FORPNT),Y
INY
LDA FAC+4
STA (FORPNT),Y
RTS
; --------------------------------
; REAL VARIABLE = EXPRESSION
; --------------------------------
L_LET2_1 JMP SETFOR
; --------------------------------
LET_STRING
PLA
; --------------------------------
; INSTALL STRING, DESCRIPTOR ADDRESS IS AT FAC+3,4
; --------------------------------
PUTSTR LDY #2 ; STRING DATA ALREADY IN STRING AREA?
LDA (FAC+3),Y ; (STRING AREA IS BTWN FRETOP
CMP FRETOP+1 ; HIMEM)
BCC L_PUTSTR_2 ; YES, DATA ALREADY UP THERE
BNE L_PUTSTR_1 ; NO
DEY ; MAYBE, TEST LOW BYTE OF POINTER
LDA (FAC+3),Y ;
CMP FRETOP ;
BCC L_PUTSTR_2 ; YES, ALREADY THERE
L_PUTSTR_1 LDY FAC+4 ; NO. DESCRIPTOR ALREADY AMONG VARIABLES?
CPY VARTAB+1 ;
BCC L_PUTSTR_2 ; NO
BNE L_PUTSTR_3 ; YES
LDA FAC+3 ; MAYBE, COMPARE LO-BYTE
CMP VARTAB ;
BCS L_PUTSTR_3 ; YES, DESCRIPTOR IS AMONG VARIABLES
L_PUTSTR_2 LDA FAC+3 ; EITHER STRING ALREADY ON TOP, OR
LDY FAC+4 ; DESCRIPTOR IS NOT A VARIABLE
JMP L_PUTSTR_4 ; SO JUST STORE THE DESCRIPTOR
; --------------------------------
; STRING NOT YET IN STRING AREA,
; AND DESCRIPTOR IS A VARIABLE
; --------------------------------
L_PUTSTR_3 LDY #0 ; POINT AT LENGTH IN DESCRIPTOR
LDA (FAC+3),Y ; GET LENGTH
JSR STRINI ; MAKE A STRING THAT LONG UP ABOVE
LDA DSCPTR ; SET UP SOURCE PNTR FOR MONINS
LDY DSCPTR+1 ;
STA STRNG1 ;
STY STRNG1+1 ;
JSR MOVINS ; MOVE STRING DATA TO NEW AREA
LDA #<FAC ; ADDRESS OF DESCRIPTOR IS IN FAC
LDY #>FAC ;
L_PUTSTR_4 STA DSCPTR ;
STY DSCPTR+1 ;
JSR FRETMS ; DISCARD DESCRIPTOR IF 'TWAS TEMPORARY
LDY #0 ; COPY STRING DESCRIPTOR
LDA (DSCPTR),Y
STA (FORPNT),Y
INY
LDA (DSCPTR),Y
STA (FORPNT),Y
INY
LDA (DSCPTR),Y
STA (FORPNT),Y
RTS
; --------------------------------
PR_STRING
JSR STRPRT
JSR CHRGOT
; --------------------------------
; "PRINT" STATEMENT
; --------------------------------
PRINT BEQ CRDO ; NO MORE LIST, PRINT <RETURN>
; --------------------------------
PRINT2 BEQ RTS_8 ; NO MORE LIST, DON'T PRINT <RETURN>
CMP #TOKEN_TAB
BEQ PR_TAB_OR_SPC ; C=1 FOR TAB(
CMP #TOKEN_SPC
CLC
BEQ PR_TAB_OR_SPC ; C=0 FOR SPC(
CMP #LOCHAR(`,')
CLC ; <<< NO PURPOSE TO THIS >>>
BEQ PR_COMMA ;
CMP #LOCHAR(`;')
BEQ PR_NEXT_CHAR ;
JSR FRMEVL ; EVALUATE EXPRESSION
BIT VALTYP ; STRING OR FP VALUE?
BMI PR_STRING ; STRING
JSR FOUT ; FP: CONVERT INTO BUFFER
JSR STRLIT ; MAKE BUFFER INTO STRING
JMP PR_STRING ; PRINT THE STRING
; --------------------------------
CRDO LDA #$0D ; PRINT <RETURN>
JSR OUTDO
NEGATE EOR #$FF ; <<< WHY??? >>>
RTS_8 RTS
; --------------------------------
; TAB TO NEXT COMMA COLUMN
; <<< NOTE BUG IF WIDTH OF WINDOW LESS THAN 33 >>>
PR_COMMA
LDA MON_CH
CMP #24 ; <<< BUG: IT SHOULD BE 32 >>>
BCC L_PR_COMMA_1 ; NEXT COLUMN, SAME LINE
JSR CRDO ; FIRST COLUMN, NEXT LINT
BNE PR_NEXT_CHAR ; ...ALWAYS
L_PR_COMMA_1 ADC #16
AND #$F0 ; ROUND TO 16 OR 32
STA MON_CH
BCC PR_NEXT_CHAR ; ...ALWAYS
; --------------------------------
PR_TAB_OR_SPC
PHP ; C=0 FOR SPC(, C=1 FOR TAB(
JSR GTBYTC ; GET VALUE
CMP #LOCHAR(`)') ; TRAILING PARENTHESIS
BEQ L_PR_TAB_OR_SPC_1 ; GOOD
JMP SYNERR ; NO, SYNTAX ERROR
L_PR_TAB_OR_SPC_1 PLP ; TAB( OR SPC(
BCC L_PR_TAB_OR_SPC_2 ; SPC(
DEX ; TAB(
TXA ; CALCULATE SPACES NEEDED FOR TAB(
SBC MON_CH
BCC PR_NEXT_CHAR ; ALREADY PAST THAT COLUMN
TAX ; NOW DO A SPC( TO THE SPECIFIED COLUMN
L_PR_TAB_OR_SPC_2 INX
NXSPC DEX
BNE DOSPC ; MORE SPACES TO PRINT
; --------------------------------
PR_NEXT_CHAR
JSR CHRGET
JMP PRINT2 ; CONTINUE PARSING PRINT LIST
; --------------------------------
DOSPC JSR OUTSP
BNE NXSPC ; ...ALWAYS
; --------------------------------
; PRINT STRING AT (Y,A)
STROUT JSR STRLIT ; MAKE (Y,A) PRINTABLE
; --------------------------------
; PRINT STRING AT (FACMO,FACLO)
; --------------------------------
STRPRT JSR FREFAC ; GET ADDRESS INTO INDEX, (A)=LENGTH
TAX ; USE X-REG FOR COUNTER
LDY #0 ; USE Y-REG FOR SCANNER
INX ;
L_STRPRT_1 DEX ;
BEQ RTS_8 ; FINISHED
LDA (INDEX),Y ; NEXT CHAR FROM STRING
JSR OUTDO ; PRINT THE CHAR
INY ;
; <<< NEXT THREE LINES ARE USELESS >>>
CMP #$0D ; WAS IT <RETURN>?
BNE L_STRPRT_1 ; NO
JSR NEGATE ; EOR #$FF WOULD DO IT, BUT WHY?
; <<< ABOVE THREE LINES ARE USELESS >>>
JMP L_STRPRT_1
; --------------------------------
OUTSP LDA #LOCHAR(` ') ; PRINT A SPACE
ASM_DATA($2C) ; SKIP OVER NEXT LINE
OUTQUES LDA #LOCHAR(`?') ; PRINT QUESTION MARK
; --------------------------------
; PRINT CHAR FROM (A)
;
; NOTE: POKE 243,32 ($20 IN $F3) WILL CONVERT
; OUTPUT TO LOWER CASE. THIS CAN BE CANCELLED
; BY NORMAL, INVERSE, OR FLASH OR POKE 243,0.
; --------------------------------
OUTDO ORA #$80 ; PRINT (A)
CMP #$A0 ; CONTROL CHR?
BCC L_OUTDO_1 ; SKIP IF SO
ORA FLASH_BIT ; =$40 FOR FLASH, ELSE $00
L_OUTDO_1 JSR MON_COUT ; "AND"S WITH $3F (INVERSE), $7F (FLASH)
AND #$7F ;
PHA ;
LDA SPEEDZ ; COMPLEMENT OF SPEED #
JSR MON_WAIT ; SO SPEED=255 BECOMES (A)=1
PLA
RTS
; --------------------------------
; INPUT CONVERSION ERROR: ILLEGAL CHARACTER
; IN NUMERIC FIELD. MUST DISTINGUISH
; BETWEEN INPUT, READ, AND GET
; --------------------------------
INPUTERR
LDA INPUTFLG
BEQ RESPERR ; TAKEN IF INPUT
BMI READERR ; TAKEN IF READ
LDY #$FF ; FROM A GET
BNE ERLIN ; ...ALWAYS
; --------------------------------
READERR
LDA DATLIN ; TELL WHERE THE "DATA" IS, RATHER
LDY DATLIN+1 ; THAN THE "READ"
; --------------------------------
ERLIN STA CURLIN
STY CURLIN+1
JMP SYNERR
; --------------------------------
INPERR PLA
; --------------------------------
RESPERR
BIT ERRFLG ; "ON ERR" TURNED ON?
BPL L_RESPERR_1 ; NO, GIVE REENTRY A TRY
LDX #254 ; ERROR CODE = 254
JMP HANDLERR
L_RESPERR_1 LDA #<ERR_REENTRY ; "?REENTER"
LDY #>ERR_REENTRY
JSR STROUT
LDA OLDTEXT ; RE-EXECUTE THE WHOLE INPUT STATEMENT
LDY OLDTEXT+1
STA TXTPTR
STY TXTPTR+1
RTS
; --------------------------------
; "GET" STATEMENT
; --------------------------------
GET JSR ERRDIR ; ILLEGAL IF IN DIRECT MODE
LDX #<(INPUT_BUFFER+1) ; SIMULATE INPUT
LDY #>(INPUT_BUFFER+1)
LDA #0
STA INPUT_BUFFER+1
LDA #$40 ; SET UP INPUTFLG
JSR PROCESS_INPUT_LIST ; <<< CAN SAVE 1 BYTE HERE>>>
RTS ; <<<BY "JMP PROCESS.INPUT.LIST">>>
; --------------------------------
; "INPUT" STATEMENT
; --------------------------------
INPUT CMP #$22 ; CHECK FOR OPTIONAL PROMPT STRING
BNE L_INPUT_1 ; NO, PRINT "?" PROMPT
JSR STRTXT ; MAKE A PRINTABLE STRING OUT OF IT
LDA #LOCHAR(`;') ; MUST HAVE ; NOW
JSR SYNCHR ;
JSR STRPRT ; PRINT THE STRING
JMP L_INPUT_2 ;
L_INPUT_1 JSR OUTQUES ; NO STRING, PRINT "?"
L_INPUT_2 JSR ERRDIR ; ILLEGAL IF IN DIRECT MODE
LDA #LOCHAR(`,') ; PRIME THE BUFFER
STA INPUT_BUFFER-1
JSR INLIN
LDA INPUT_BUFFER
CMP #$03 ; CONTROL C?
BNE INPUT_FLAG_ZERO ; NO
JMP CONTROL_C_TYPED
; --------------------------------
NXIN JSR OUTQUES ; PRINT "?"
JMP INLIN
; --------------------------------
; "READ" STATEMENT
; --------------------------------
READ LDX DATPTR ; Y,X POINTS AT NEXT DATA STATEMENT
LDY DATPTR+1 ;
LDA #$98 ; SET INPUTFLG = $98
ASM_DATA($2C) ; TRICK TO PROCESS.INPUT.LIST
; --------------------------------
INPUT_FLAG_ZERO LDA #0 ; SET INPUTFLG = $00
; --------------------------------
; PROCESS INPUT LIST
;
; (Y,X) IS ADDRESS OF INPUT DATA STRING
; (A) = VALUE FOR INPUTFLG: $00 FOR INPUT
; $40 FOR GET
; $98 FOR READ
; --------------------------------
PROCESS_INPUT_LIST STA INPUTFLG
STX INPTR ; ADDRESS OF INPUT STRING
STY INPTR+1
; --------------------------------
PROCESS_INPUT_ITEM JSR PTRGET ; GET ADDRESS OF VARIABLE
STA FORPNT ;
STY FORPNT+1 ;
LDA TXTPTR ; SAVE CURRENT TXTPTR,
LDY TXTPTR+1 ; WHICH POINTS INTO PROGRAM
STA TXPSV ;
STY TXPSV+1 ;
LDX INPTR ; SET TXTPTR TO POINT AT INPUT BUFFER
LDY INPTR+1 ; OR "DATA" LINE
STX TXTPTR ;
STY TXTPTR+1 ;
JSR CHRGOT ; GET CHAR AT PNTR
BNE INSTART ; NOT END OF LINE OR COLON
BIT INPUTFLG ; DOING A "GET"?
BVC L_PROCESS_INPUT_ITEM_1 ; NO
JSR MON_RDKEY ; YES, GET CHAR
AND #$7F
STA INPUT_BUFFER
LDX #<(INPUT_BUFFER-1)
LDY #>(INPUT_BUFFER-1)
BNE L_PROCESS_INPUT_ITEM_2 ; ...ALWAYS
; --------------------------------
L_PROCESS_INPUT_ITEM_1 BMI FINDATA ; DOING A "READ"
JSR OUTQUES ; DOING AN "INPUT", PRINT "?"
JSR NXIN ; PRINT ANOTHER "?", AND INPUT A LINE
L_PROCESS_INPUT_ITEM_2 STX TXTPTR
STY TXTPTR+1
; --------------------------------
INSTART
JSR CHRGET ; GET NEXT INPUT CHAR
BIT VALTYP ; STRING OR NUMERIC?
BPL L_INSTART_5 ; NUMERIC
BIT INPUTFLG ; STRING -- NOW WHAT INPUT TYPE?
BVC L_INSTART_1 ; NOT A "GET"
INX ; "GET"
STX TXTPTR
LDA #0
STA CHARAC ; NO OTHER TERMINATORS THAN $00
BEQ L_INSTART_2 ; ...ALWAYS
; --------------------------------
L_INSTART_1 STA CHARAC
CMP #$22 ; TERMINATE ON $00 OR QUOTE
BEQ L_INSTART_3
LDA #LOCHAR(`:') ; TERMINATE ON $00, COLON, OR COMMA
STA CHARAC
LDA #LOCHAR(`,')
L_INSTART_2 CLC
L_INSTART_3 STA ENDCHR
LDA TXTPTR
LDY TXTPTR+1
ADC #0 ; SKIP OVER QUOTATION MARK, IF
BCC L_INSTART_4 ; THERE WAS ONE
INY
L_INSTART_4 JSR STRLT2 ; BUILD STRING STARTING AT (Y,A)
; TERMINATED BY $00, (CHARAC), OR (ENDCHR)
JSR POINT ; SET TXTPTR TO POINT AT STRING
JSR PUTSTR ; STORE STRING IN VARIABLE
JMP INPUT_MORE
; --------------------------------
L_INSTART_5 PHA
LDA INPUT_BUFFER ; ANYTHING IN BUFFER?
BEQ INPFIN ; NO, SEE IF READ OR INPUT
; --------------------------------
INPUTDWTA
PLA ; "READ"
JSR FIN ; GET FP NUMBER AT TXTPTR
LDA VALTYP+1 ;
JSR LET2 ; STORE RESULT IN VARIABLE
; --------------------------------
INPUT_MORE
JSR CHRGOT
BEQ L_INPUT_MORE_1 ; END OF LINE OR COLON
CMP #LOCHAR(`,') ; COMMA IN INPUT?
BEQ L_INPUT_MORE_1 ; YES
JMP INPUTERR ; NOTHING ELSE WILL DO
L_INPUT_MORE_1 LDA TXTPTR ; SAVE POSITION IN INPUT BUFFER
LDY TXTPTR+1 ;
STA INPTR ;
STY INPTR+1 ;
LDA TXPSV ; RESTORE PROGRAM POINTER
LDY TXPSV+1 ;
STA TXTPTR ;
STY TXTPTR+1 ;
JSR CHRGOT ; NEXT CHAR FROM PROGRAM
BEQ INPDONE ; END OF STATEMENT
JSR CHKCOM ; BETTER BE A COMMA THEN
JMP PROCESS_INPUT_ITEM
; --------------------------------
INPFIN LDA INPUTFLG ; "INPUT" OR "READ"
BNE INPUTDWTA ; "READ"
JMP INPERR
; --------------------------------
FINDATA
JSR DATAN ; GET OFFSET TO NEXT COLON OR EOL
INY ; TO FIRST CHAR OF NEXT LINE
TAX ; WHICH: EOL OR COLON?
BNE L_FINDATA_1 ; COLON
LDX #ERR_NODATA ; EOL: MIGHT BE OUT OF DATA
INY ; CHECK HI-BYTE OF FORWARD PNTR
LDA (TXTPTR),Y ; END OF PROGRAM?
BEQ GERR ; YES, WE ARE OUT OF DATA
INY ; PICK UP THE LINE #
LDA (TXTPTR),Y
STA DATLIN
INY
LDA (TXTPTR),Y
INY ; POINT AT FIRST TEXT CHAR IN LINE
STA DATLIN+1
L_FINDATA_1 LDA (TXTPTR),Y ; GET 1ST TOKEN OF STATEMENT
TAX ; SAVE TOKEN IN X-REG
JSR ADDON ; ADD (Y) TO TXTPTR
CPX #TOKENDWTA ; DID WE FIND A "DATA" STATEMENT?
BNE FINDATA ; NOT YET
JMP INSTART ; YES, READ IT
; ---NO MORE INPUT REQUESTED------
INPDONE
LDA INPTR ; GET POINTER IN CASE IT WAS "READ"
LDY INPTR+1
LDX INPUTFLG ; "READ" OR "INPUT"?
BPL L_INPDONE_1 ; "INPUT"
JMP SETDA ; "DATA", SO STORE (Y,X) AT DATPTR
L_INPDONE_1 LDY #0 ; "INPUT": ANY MORE CHARS ON LINE?
LDA (INPTR),Y
BEQ L_INPDONE_2 ; NO, ALL IS WELL
LDA #<ERR_EXTRA ; YES, ERROR
LDY #>ERR_EXTRA ; "EXTRA IGNORED"
JMP STROUT
L_INPDONE_2 RTS
; --------------------------------
ERR_EXTRA LOASCII(`?EXTRA IGNORED')
ASM_DATA($0D,0)
ERR_REENTRY LOASCII(`?REENTER')
ASM_DATA($0D,0)
; --------------------------------
; --------------------------------
; "NEXT" STATEMENT
; --------------------------------
NEXT BNE NEXT_1 ; VARIABLE AFTER "NEXT"
LDY #0 ; FLAG BY SETTING FORPNT+1 = 0
BEQ NEXT_2 ; ...ALWAYS
; --------------------------------
NEXT_1 JSR PTRGET ; GET PNTR TO VARIABLE IN (Y,A)
NEXT_2 STA FORPNT
STY FORPNT+1
JSR GTFORPNT ; FIND FOR-FRAME FOR THIS VARIABLE
BEQ NEXT_3 ; FOUND IT
LDX #ERR_NOFOR ; NOT THERE, ABORT
GERR BEQ JERROR ; ...ALWAYS
NEXT_3 TXS ; SET STACK PTR TO POINT TO THIS FRAME,
INX ; WHICH TRIMS OFF ANY INNER LOOPS
INX
INX
INX
TXA ; LOW BYTE OF ADRS OF STEP VALUE
INX
INX
INX
INX
INX
INX
STX DEST ; LOW BYTE ADRS OF FOR VAR VALUE
LDY #>STACK ; (Y,A) IS ADDRESS OF STEP VALUE
JSR LOAD_FAC_FROM_YA ; STEP TO FAC
TSX
LDA STACK+9,X
STA FAC_SIGN
LDA FORPNT
LDY FORPNT+1
JSR FADD ; ADD TO FOR VALUE
JSR SETFOR ; PUT NEW VALUE BACK
LDY #>STACK ; (Y,A) IS ADDRESS OF END VALUE
JSR FCOMP2 ; COMPARE TO END VALUE
TSX
SEC
SBC STACK+9,X ; SIGN OF STEP
BEQ L_NEXT_3_2 ; BRANCH IF FOR COMPLETE
LDA STACK+15,X ; OTHERWISE SET UP
STA CURLIN ; FOR LINE #
LDA STACK+16,X
STA CURLIN+1
LDA STACK+18,X ; AND SET TXTPTR TO JUST
STA TXTPTR ; AFTER FOR STATEMENT
LDA STACK+17,X
STA TXTPTR+1
L_NEXT_3_1 JMP NEWSTT
L_NEXT_3_2 TXA ; POP OFF FOR-FRAME, LOOP IS DONE
ADC #17 ; CARRY IS SET, SO ADDS 18
TAX
TXS
JSR CHRGOT ; CHAR AFTER VARIABLE
CMP #LOCHAR(`,') ; ANOTHER VARIABLE IN NEXT_
BNE L_NEXT_3_1 ; NO, GO TO NEXT STATEMENT
JSR CHRGET ; YES, PRIME FOR NEXT VARIABLE
JSR NEXT_1 ; (DOES NOT RETURN)
; --------------------------------
; EVALUATE EXPRESSION, MAKE SURE IT IS NUMERIC
; --------------------------------
FRMNUM JSR FRMEVL
; --------------------------------
; MAKE SURE (FAC) IS NUMERIC
; --------------------------------
CHKNUM CLC
ASM_DATA($24) ; DUMMY FOR SKIP
; --------------------------------
; MAKE SURE (FAC) IS STRING
; --------------------------------
CHKSTR SEC
; --------------------------------
; MAKE SURE (FAC) IS CORRECT TYPE
; IF C=0, TYPE MUST BE NUMERIC
; IF C=1, TYPE MUST BE STRING
; --------------------------------
CHKVAL BIT VALTYP ; $00 IF NUMERIC, $FF IF STRING
BMI L_CHKVAL_2 ; TYPE IS STRING
BCS L_CHKVAL_3 ; NOT STRING, BUT WE NEED STRING
L_CHKVAL_1 RTS ; TYPE IS CORRECT
L_CHKVAL_2 BCS L_CHKVAL_1 ; IS STRING AND WE WANTED STRING
L_CHKVAL_3 LDX #ERR_BADTYPE ; TYPE MISMATCH
JERROR JMP ERROR
; --------------------------------
; EVALUATE THE EXPRESSION AT TXTPTR, LEAVING THE
; RESULT IN FAC. WORKS FOR BOTH STRING AND NUMERIC
; EXPRESSIONS.
; --------------------------------
FRMEVL LDX TXTPTR ; DECREMENT TXTPTR
BNE L_FRMEVL_1
DEC TXTPTR+1
L_FRMEVL_1 DEC TXTPTR
LDX #0 ; START WITH PRECEDENCE = 0
ASM_DATA($24) ; TRICK TO SKIP FOLLOWING "PHA"
; --------------------------------
FRMEVL_1
PHA ; PUSH RELOPS FLAGS
TXA ;
PHA ; SAVE LAST PRECEDENCE
LDA #1 ;
JSR CHKMEM ; CHECK IF ENOUGH ROOM ON STACK
JSR FRM_ELEMENT ; GET AN ELEMENT
LDA #0
STA CPRTYP ; CLEAR COMPARISON OPERATOR FLAGS
; --------------------------------
FRMEVL_2
JSR CHRGOT ; CHECK FOR RELATIONAL OPERATORS
L_FRMEVL_2_1 SEC ; > IS $CF, = IS $D0, < IS $D1
SBC #TOKEN_GREATER ; > IS 0, = IS 1, < IS 2
BCC L_FRMEVL_2_2 ; NOT RELATIONAL OPERATOR
CMP #3 ;
BCS L_FRMEVL_2_2 ; NOT RELATIONAL OPERATOR
CMP #1 ; SET CARRY IF "=" OR "<"
ROL ; NOW > IS 0, = IS 3, < IS 5
EOR #1 ; NOW > IS 1, = IS 2, < IS 4
EOR CPRTYP ; SET BITS OF CPRTYP: 00000<=>
CMP CPRTYP ; CHECK FOR ILLEGAL COMBINATIONS
BCC SNTXERR ; IF LESS THAN, A RELOP WAS REPEATED
STA CPRTYP ;
JSR CHRGET ; ANOTHER OPERATOR?
JMP L_FRMEVL_2_1 ; CHECK FOR <,=,> AGAIN
; --------------------------------
L_FRMEVL_2_2 LDX CPRTYP ; DID WE FIND A RELATIONAL OPERATOR?
BNE FRM_RELATIONAL ; YES
BCS NOTMATH ; NO, AND NEXT TOKEN IS > $D1
ADC #$CF-TOKEN_PLUS ; NO, AND NEXT TOKEN < $CF
BCC NOTMATH ; IF NEXT TOKEN < "+"
ADC VALTYP ; + AND LAST RESULT A STRING?
BNE L_FRMEVL_2_3 ; BRANCH IF NOT
JMP CAT ; CONCATENATE IF SO.
; --------------------------------
L_FRMEVL_2_3 ADC #$FF ; +-*/ IS 0123
STA INDEX
ASL ; MULTIPLY BY 3
ADC INDEX ; +-*/ IS 0,3,6,9
TAY
; --------------------------------
FRM_PRECEDENCE_TEST
PLA ; GET LAST PRECEDENCE
CMP MATHTBL,Y
BCS FRM_PERFORM_1 ; DO NOW IF HIGHER PRECEDENCE
JSR CHKNUM ; WAS LAST RESULT A #?
NXOP PHA ; YES, SAVE PRECEDENCE ON STACK
SAVOP JSR FRM_RECURSE ; SAVE REST, CALL FRMEVL RECURSIVELY
PLA
LDY LASTOP
BPL PREFNC
TAX
BEQ GOEX ; EXIT IF NO MATH IN EXPRESSION
BNE FRM_PERFORM_2 ; ...ALWAYS
; --------------------------------
; FOUND ONE OR MORE RELATIONAL OPERATORS <,=,>
; --------------------------------
FRM_RELATIONAL
LSR VALTYP ; (VALTYP) = 0 (NUMERIC), = $FF (STRING)
TXA ; SET CPRTYP TO 0000<=>C
ROL ; WHERE C=0 IF #, C=1 IF STRING
LDX TXTPTR ; BACK UP TXTPTR
BNE L_FRM_RELATIONAL_1
DEC TXTPTR+1
L_FRM_RELATIONAL_1 DEC TXTPTR
LDY #M_REL-MATHTBL ; POINT AT RELOPS ENTRY
STA CPRTYP
BNE FRM_PRECEDENCE_TEST ; ...ALWAYS
; --------------------------------
PREFNC CMP MATHTBL,Y
BCS FRM_PERFORM_2 ; DO NOW IF HIGHER PRECEDENCE
BCC NXOP ; ...ALWAYS
; --------------------------------
; STACK THIS OPERATION AND CALL FRMEVL FOR
; ANOTHER ONE
; --------------------------------
FRM_RECURSE
LDA MATHTBL+2,Y
PHA ; PUSH ADDRESS OF OPERATION PERFORMER
LDA MATHTBL+1,Y
PHA
JSR FRM_STACK_1 ; STACK FAC.SIGN AND FAC
LDA CPRTYP ; A=RELOP FLAGS, X=PRECEDENCE BYTE
JMP FRMEVL_1 ; RECURSIVELY CALL FRMEVL
; --------------------------------
SNTXERR JMP SYNERR
; --------------------------------
; STACK (FAC)
;
; THREE ENTRY POINTS:
; L_SNTXERR_1, FROM FRMEVL
; L_SNTXERR_2, FROM "STEP"
; L_SNTXERR_3, FROM "FOR"
; --------------------------------
FRM_STACK_1
LDA FAC_SIGN ; GET FAC.SIGN TO PUSH IT
; Note: XA65 assembler (Andre Fachat) requires ! here when asm with "xa -R -bt 0" for some reason:
LDX !MATHTBL,Y ; PRECEDENCE BYTE FROM MATHTBL
; --------------------------------
; ENTER HERE FROM "STEP", TO PUSH STEP SIGN AND VALUE
; --------------------------------
FRM_STACK_2
TAY ; FAC.SIGN OR SGN(STEP VALUE)
PLA ; PULL RETURN ADDRESS AND ADD 1
STA INDEX ; <<< ASSUMES NOT ON PAGE BOUNDARY! >>>
INC INDEX ; PLACE BUMPED RETURN ADDRESS IN
PLA ; INDEX,INDEX+1
STA INDEX+1 ;
TYA ; FAC.SIGN OR SGN(STEP VALUE)
PHA ; PUSH FAC.SIGN OR SGN(STEP VALUE)
; --------------------------------
; ENTER HERE FROM "FOR", WITH (INDEX) = STEP,
; TO PUSH INITIAL VALUE OF "FOR" VARIABLE
; --------------------------------
FRM_STACK_3
JSR ROUND_FAC ; ROUND TO 32 BITS
LDA FAC+4 ; PUSH (FAC)
PHA
LDA FAC+3
PHA
LDA FAC+2
PHA
LDA FAC+1
PHA
LDA FAC
PHA
JMP (INDEX) ; DO RTS FUNNY WAY
; --------------------------------
;
; --------------------------------
NOTMATH LDY #$FF ; SET UP TO EXIT ROUTINE
PLA
GOEX BEQ EXIT ; EXIT IF NO MATH TO DO
; --------------------------------
; PERFORM STACKED OPERATION
;
; (A) = PRECEDENCE BYTE
; STACK: 1 -- CPRMASK
; 5 -- (ARG)
; 2 -- ADDR OF PERFORMER
; --------------------------------
FRM_PERFORM_1
CMP #P_REL ; WAS IT RELATIONAL OPERATOR?
BEQ L_FRM_PERFORM_1_1 ; YES, ALLOW STRING COMPARE
JSR CHKNUM ; MUST BE NUMERIC VALUE
L_FRM_PERFORM_1_1 STY LASTOP ;
; --------------------------------
FRM_PERFORM_2 ;
PLA ; GET 0000<=>C FROM STACK
LSR ; SHIFT TO 00000<=> FORM
STA CPRMASK ; 00000<=>
PLA ;
STA ARG ; GET FLOATING POINT VALUE OFF STACK,
PLA ; AND PUT IT IN ARG
STA ARG+1 ;
PLA ;
STA ARG+2 ;
PLA ;
STA ARG+3 ;
PLA ;
STA ARG+4 ;
PLA ;
STA ARG+5 ;
EOR FAC_SIGN ; SAVE EOR OF SIGNS OF THE OPERANDS,
STA SGNCPR ; IN CASE OF MULTIPLY OR DIVIDE
EXIT LDA FAC ; FAC EXPONENT IN A-REG
RTS ; STATUS EQU. IF (FAC)=0
; RTS GOES TO PERFORM OPERATION
; --------------------------------
; GET ELEMENT IN EXPRESSION
;
; GET VALUE OF VARIABLE OR NUMBER AT TXTPNT, OR POINT
; TO STRING DESCRIPTOR IF A STRING, AND PUT IN FAC.
; --------------------------------
FRM_ELEMENT ;
LDA #0 ; ASSUME NUMERIC
STA VALTYP ;
L_FRM_ELEMENT_1 JSR CHRGET ;
BCS L_FRM_ELEMENT_3 ; NOT A DIGIT
L_FRM_ELEMENT_2 JMP FIN ; NUMERIC CONSTANT
L_FRM_ELEMENT_3 JSR ISLETC ; VARIABLE NAME?
BCS FRM_VARIABLE ; YES
CMP #LOCHAR(`.') ; DECIMAL POINT
BEQ L_FRM_ELEMENT_2 ; YES, NUMERIC CONSTANT
CMP #TOKEN_MINUS ; UNARY MINUS?
BEQ MIN ; YES
CMP #TOKEN_PLUS ; UNARY PLUS
BEQ L_FRM_ELEMENT_1 ; YES
CMP #$22 ; STRING CONSTANT?
BNE NOT_ ; NO
; --------------------------------
; STRING CONSTANT ELEMENT
;
; SET Y,A = (TXTPTR)+CARRY
; --------------------------------
STRTXT LDA TXTPTR ; ADD (CARRY) TO GET ADDRESS OF 1ST CHAR
LDY TXTPTR+1 ; OF STRING IN Y,A
ADC #0 ;
BCC L_STRTXT_1 ;
INY ;
L_STRTXT_1 JSR STRLIT ; BUILD DESCRIPTOR TO STRING
; GET ADDRESS OF DESCRIPTOR IN FAC
JMP POINT ; POINT TXTPTR AFTER TRAILING QUOTE
; --------------------------------
; "NOT" FUNCTION
; IF FAC=0, RETURN FAC=1
; IF FAC<>0, RETURN FAC=0
; --------------------------------
NOT_ CMP #TOKEN_NOT
BNE FN_ ; NOT "NOT", TRY "FN"
LDY #MEQUU-MATHTBL ; POINT AT = COMPARISON
BNE EQUL ; ...ALWAYS
; --------------------------------
; COMPARISON FOR EQUALITY (= OPERATOR)
; ALSO USED TO EVALUATE "NOT" FUNCTION
; --------------------------------
EQUOP LDA FAC ; SET "TRUE" IF (FAC) = ZERO
BNE L_EQUOP_1 ; FALSE
LDY #1 ; TRUE
ASM_DATA($2C) ; TRICK TO SKIP NEXT 2 BYTES
L_EQUOP_1 LDY #0 ; FALSE
JMP SNGFLT ;
; --------------------------------
FN_ CMP #TOKEN_FN
BNE SGN_
JMP FUNCT
; --------------------------------
SGN_ CMP #TOKEN_SGN
BCC PARCHK
JMP UNARY
; --------------------------------
; EVALUATE "(EXPRESSION)"
; --------------------------------
PARCHK JSR CHKOPN ; IS THERE A '(' AT TXTPTR?
JSR FRMEVL ; YES, EVALUATE EXPRESSION
; --------------------------------
CHKCLS LDA #$29 ; CHECK FOR ')'
ASM_DATA($2C) ; TRICK
; --------------------------------
CHKOPN LDA #$28 ;
ASM_DATA($2C) ; TRICK
; --------------------------------
CHKCOM LDA #LOCHAR(`,') ; COMMA AT TXTPTR?
; --------------------------------
; UNLESS CHAR AT TXTPTR = (A), SYNTAX ERROR
; --------------------------------
SYNCHR LDY #0
CMP (TXTPTR),Y
BNE SYNERR
JMP CHRGET ; MATCH, GET NEXT CHAR & RETURN
; --------------------------------
SYNERR LDX #ERR_SYNTAX
JMP ERROR
; --------------------------------
MIN LDY #M_NEG-MATHTBL ; POINT AT UNARY MINUS
EQUL PLA
PLA
JMP SAVOP
; --------------------------------
FRM_VARIABLE
JSR PTRGET
FRM_VARIABLE_CALL = *-1 ; SO PTRGET CAN TELL WE CALLED
STA VPNT ; ADDRESS OF VARIABLE
STY VPNT+1 ;
LDX VALTYP ; NUMERIC OR STRING?
BEQ L_FRM_VARIABLE_CALL_1 ; NUMERIC
LDX #0 ; STRING
STX STRNG1+1 ;
RTS ;
L_FRM_VARIABLE_CALL_1 LDX VALTYP+1 ; NUMERIC, WHICH TYPE?
BPL L_FRM_VARIABLE_CALL_2 ; FLOATING POINT
LDY #0 ; INTEGER
LDA (VPNT),Y ;
TAX ; GET VALUE IN A,Y
INY ;
LDA (VPNT),Y ;
TAY ;
TXA ;
JMP GIVAYF ; CONVERT A,Y TO FLOATING POINT
L_FRM_VARIABLE_CALL_2 JMP LOAD_FAC_FROM_YA
; --------------------------------
; --------------------------------
; "SCRN(" FUNCTION
; --------------------------------
SCREEN JSR CHRGET
JSR PLOTFNS ; GET COLUMN AND ROW
TXA ; ROW
LDY FIRST ; COLUMN
JSR MON_SCRN ; GET 4-BIT COLOR THERE
TAY ;
JSR SNGFLT ; CONVERT (Y) TO REAL IN FAC
JMP CHKCLS ; REQUIRE ")"
; --------------------------------
; PROCESS UNARY OPERATORS (FUNCTIONS)
; --------------------------------
UNARY CMP #TOKEN_SCRN ; NOT UNARY, DO SPECIAL
BEQ SCREEN
ASL ; DOUBLE TOKEN TO GET INDEX
PHA
TAX
JSR CHRGET
CPX #<(TOKEN_LEFTSTR*2-1) ; LEFT$, RIGHT$, AND MID$
BCC L_UNARY_1 ; NOT ONE OF THE STRING FUNCTIONS
JSR CHKOPN ; STRING FUNCTION, NEED "("
JSR FRMEVL ; EVALUATE EXPRESSION FOR STRING
JSR CHKCOM ; REQUIRE A COMMA
JSR CHKSTR ; MAKE SURE EXPRESSION IS A STRING
PLA ;
TAX ; RETRIEVE ROUTINE POINTER
LDA VPNT+1 ; STACK ADDRESS OF STRING
PHA ;
LDA VPNT ;
PHA ;
TXA ;
PHA ; STACK DOUBLED TOKEN
JSR GETBYT ; CONVERT NEXT EXPRESSION TO BYTE IN X-REG
PLA ; GET DOUBLED TOKEN OFF STACK
TAY ; USE AS INDEX TO BRANCH
TXA ; VALUE OF SECOND PARAMETER
PHA ; PUSH 2ND PARAM
JMP L_UNARY_2 ; JOIN UNARY FUNCTIONS
L_UNARY_1 JSR PARCHK ; REQUIRE "(EXPRESSION)"
PLA
TAY ; INDEX INTO FUNCTION ADDRESS TABLE
L_UNARY_2 LDA UNFNC-TOKEN_SGN-TOKEN_SGN+$100,Y
STA JMPADRS+1 ; PREPARE TO JSR TO ADDRESS
LDA UNFNC-TOKEN_SGN-TOKEN_SGN+$101,Y
STA JMPADRS+2
JSR JMPADRS ; DOES NOT RETURN FOR
; CHR$, LEFT$, RIGHT$, OR MID$
JMP CHKNUM ; REQUIRE NUMERIC RESULT
; --------------------------------
OR LDA ARG ; "OR" OPERATOR
ORA FAC ; IF RESULT NONZERO, IT IS TRUE
BNE TRUE ;
; --------------------------------
ANDOP LDA ARG ; "AND" OPERATOR
BEQ FALSE ; IF EITHER IS ZERO, RESULT IS FALSE
LDA FAC ;
BNE TRUE ;
; --------------------------------
FALSE LDY #0 ; RETURN FAC=0
ASM_DATA($2C) ; TRICK
; --------------------------------
TRUE LDY #1 ; RETURN FAC=1
JMP SNGFLT ;
; --------------------------------
; PERFORM RELATIONAL OPERATIONS
; --------------------------------
RELOPS JSR CHKVAL ; MAKE SURE FAC IS CORRECT TYPE
BCS STRCMP ; TYPE MATCHES, BRANCH IF STRINGS
LDA ARG_SIGN ; NUMERIC COMPARISON
ORA #$7F ; RE-PACK VALUE IN ARG FOR FCOMP
AND ARG+1 ;
STA ARG+1 ;
LDA #<ARG ;
LDY #>ARG ;
JSR FCOMP ; RETURN A-REG = -1,0,1
TAX ; AS ARG <,=,> FAC
JMP NUMCMP ;
; --------------------------------
; STRING COMPARISON
; --------------------------------
STRCMP LDA #0 ; SET RESULT TYPE TO NUMERIC
STA VALTYP ;
DEC CPRTYP ; MAKE CPRTYP 0000<=>0
JSR FREFAC ;
STA FAC ; STRING LENGTH
STX FAC+1
STY FAC+2
LDA ARG+3
LDY ARG+4
JSR FRETMP
STX ARG+3
STY ARG+4
TAX ; LEN (ARG) STRING
SEC ;
SBC FAC ; SET X TO SMALLER LEN
BEQ L_STRCMP_1 ;
LDA #1 ;
BCC L_STRCMP_1 ;
LDX FAC ;
LDA #$FF ;
L_STRCMP_1 STA FAC_SIGN ; FLAG WHICH SHORTER
LDY #$FF ;
INX ;
STRCMP_1 ;
INY ;
DEX ;
BNE STRCMP_2 ; MORE CHARS IN BOTH STRINGS
LDX FAC_SIGN ; IF = SO FAR, DECIDE BY LENGTH
; --------------------------------
NUMCMP BMI CMPDONE ;
CLC ;
BCC CMPDONE ; ...ALWAYS
; --------------------------------
STRCMP_2 ;
LDA (ARG+3),Y ;
CMP (FAC+1),Y ;
BEQ STRCMP_1 ; SAME, KEEP COMPARING
LDX #$FF ; IN CASE ARG GREATER
BCS CMPDONE ; IT IS
LDX #1 ; FAC GREATER
; --------------------------------
CMPDONE ;
INX ; CONVERT FF,0,1 TO 0,1,2
TXA ;
ROL ; AND TO 0,2,4 IF C=0, ELSE 1,2,5
AND CPRMASK ; 00000<=>
BEQ L_CMPDONE_1 ; IF NO MATCH: FALSE
LDA #1 ; AT LEAST ONE MATCH: TRUE
L_CMPDONE_1 JMP FLOAT ;
; --------------------------------
; "PDL" FUNCTION
; <<< NOTE: ARG<4 IS NOT CHECKED >>>
; --------------------------------
PDL JSR CONINT ; GET # IN X
JSR MON_PREAD ; READ PADDLE
JMP SNGFLT ; FLOAT RESULT
; --------------------------------
; "DIM" STATEMENT
; --------------------------------
NXDIM JSR CHKCOM ; SEPARATED BY COMMAS
DIM TAX ; NON-ZERO, FLAGS PTRGET DIM CALLED
JSR PTRGET2 ; ALLOCATE THE ARRAY
JSR CHRGOT ; NEXT CHAR
BNE NXDIM ; NOT END OF STATEMENT
RTS ;
; --------------------------------
; PTRGET -- GENERAL VARIABLE SCAN
;
; SCANS VARIABLE NAME AT TXTPTR, AND SEARCHES THE
; VARTAB AND ARYTAB FOR THE NAME.
; IF NOT FOUND, CREATE VARIABLE OF APPROPRIATE TYPE.
; RETURN WITH ADDRESS IN VARPNT AND Y,A
;
; ACTUAL ACTIVITY CONTROLLED SOMEWHAT BY TWO FLAGS:
; DIMFLG -- NONZERO IF CALLED FROM "DIM"
; ELSE = 0
;
; SUBFLG -- = $00
; = $40 IF CALLED FROM "GETARYPT"
; = $80 IF CALLED FROM "DEF FN"
; = $C1-DA IF CALLED FROM "FN"
; --------------------------------
PTRGET LDX #0 ;
JSR CHRGOT ; GET FIRST CHAR OF VARIABLE NAME
; --------------------------------
PTRGET2 ;
STX DIMFLG ; X IS NONZERO IF FROM DIM
; --------------------------------
PTRGET3 ;
STA VARNAM ;
JSR CHRGOT ;
JSR ISLETC ; IS IT A LETTER?
BCS NAMOK ; YES, OKAY SO FAR
BADNAM JMP SYNERR ; NO, SYNTAX ERROR
NAMOK LDX #0 ;
STX VALTYP ;
STX VALTYP+1 ;
JMP PTRGET4 ; TO BRANCH ACROSS $E000 VECTORS
; --------------------------------
; DOS AND MONITOR CALL BASIC AT $E000 AND $E003
; --------------------------------
BASIC JMP COLD_START
BASIC2 JMP RESTART
BRK ; <<< WASTED BYTE >>>
; --------------------------------
PTRGET4
JSR CHRGET ; SECOND CHAR OF VARIABLE NAME
BCC L_PTRGET4_1 ; NUMERIC
JSR ISLETC ; LETTER?
BCC L_PTRGET4_3 ; NO, END OF NAME
L_PTRGET4_1 TAX ; SAVE SECOND CHAR OF NAME IN X
L_PTRGET4_2 JSR CHRGET ; SCAN TO END OF VARIABLE NAME
BCC L_PTRGET4_2 ; NUMERIC
JSR ISLETC ;
BCS L_PTRGET4_2 ; ALPHA
L_PTRGET4_3 CMP #LOCHAR(`$') ; STRING?
BNE L_PTRGET4_4 ; NO
LDA #$FF ;
STA VALTYP ;
BNE L_PTRGET4_5 ; ...ALWAYS
L_PTRGET4_4 CMP #LOCHAR(`%') ; INTEGER?
BNE L_PTRGET4_6 ; NO
LDA SUBFLG ; YES; INTEGER VARIABLE ALLOWED?
BMI BADNAM ; NO, SYNTAX ERROR
LDA #$80 ; YES
STA VALTYP+1 ; FLAG INTEGER MODE
ORA VARNAM ;
STA VARNAM ; SET SIGN BIT ON VARNAME
L_PTRGET4_5 TXA ; SECOND CHAR OF NAME
ORA #$80 ; SET SIGN
TAX ;
JSR CHRGET ; GET TERMINATING CHAR
L_PTRGET4_6 STX VARNAM+1 ; STORE SECOND CHAR OF NAME
SEC ;
ORA SUBFLG ; $00 OR $40 IF SUBSCRIPTS OK, ELSE $80
SBC #$28 ; IF SUBFLG=$00 AND CHAR="("...
BNE L_PTRGET4_8 ; NOPE
L_PTRGET4_7 JMP ARRAY ; YES
L_PTRGET4_8 BIT SUBFLG ; CHECK TOP TWO BITS OF SUBFLG
BMI L_PTRGET4_9 ; $80
BVS L_PTRGET4_7 ; $40, CALLED FROM GETARYPT
L_PTRGET4_9 LDA #0 ; CLEAR SUBFLG
STA SUBFLG ;
LDA VARTAB ; START LOWTR AT SIMPLE VARIABLE TABLE
LDX VARTAB+1 ;
LDY #0 ;
L_PTRGET4_10 STX LOWTR+1 ;
L_PTRGET4_11 STA LOWTR ;
CPX ARYTAB+1 ; END OF SIMPLE VARIABLES?
BNE L_PTRGET4_12 ; NO, GO ON
CMP ARYTAB ; YES; END OF ARRAYS?
BEQ NAME_NOT_FOUND ; YES, MAKE ONE
L_PTRGET4_12 LDA VARNAM ; SAME FIRST LETTER?
CMP (LOWTR),Y ;
BNE L_PTRGET4_13 ; NOT SAME FIRST LETTER
LDA VARNAM+1 ; SAME SECOND LETTER?
INY
CMP (LOWTR),Y
BEQ SET_VARPNT_AND_YA ; YES, SAME VARIABLE NAME
DEY ; NO, BUMP TO NEXT NAME
L_PTRGET4_13 CLC
LDA LOWTR
ADC #7
BCC L_PTRGET4_11
INX
BNE L_PTRGET4_10 ; ...ALWAYS
; --------------------------------
; CHECK IF (A) IS ASCII LETTER A-Z
;
; RETURN CARRY = 1 IF A-Z
; = 0 IF NOT
;
; <<<NOTE FASTER AND SHORTER CODE: >>>
; <<< CMP #LOCHAR(`Z')+1 COMPARE HI END
; <<< BCS L_PTRGET4_1 ABOVE A-Z
; <<< CMP #LOCHAR(`A') COMPARE LO END
; <<< RTS C=0 IF LO, C=1 IF A-Z
; <<<L_PTRGET4_1 CLC C=0 IF HI
; <<< RTS
; --------------------------------
ISLETC CMP #LOCHAR(`A') ; COMPARE LO END
BCC L_ISLETC_1 ; C=0 IF LOW
SBC #LOCHAR(`Z')+1 ; PREPARE HI END TEST
SEC ; TEST HI END, RESTORING (A)
SBC #255-'Z' ; C=0 IF LO, C=1 IF A-Z
L_ISLETC_1 RTS
; --------------------------------
; VARIABLE NOT FOUND, SO MAKE ONE
; --------------------------------
NAME_NOT_FOUND
PLA ; LOOK AT RETURN ADDRESS ON STACK TO
PHA ; SEE IF CALLED FROM FRM.VARIABLE
CMP #<FRM_VARIABLE_CALL
BNE MAKE_NEW_VARIABLE ; NO
TSX
LDA STACK+2,X
CMP #>FRM_VARIABLE_CALL
BNE MAKE_NEW_VARIABLE ; NO
LDA #<C_ZERO ; YES, CALLED FROM FRM.VARIABLE
LDY #>C_ZERO ; POINT TO A CONSTANT ZERO
RTS ; NEW VARIABLE USED IN EXPRESSION = 0
; --------------------------------
C_ZERO ASM_DATA(00,00) ; INTEGER OR REAL ZERO, OR NULL STRING
; --------------------------------
; MAKE A NEW SIMPLE VARIABLE
;
; MOVE ARRAYS UP 7 BYTES TO MAKE ROOM FOR NEW VARIABLE
; ENTER 7-BYTE VARIABLE DATA IN THE HOLE
; --------------------------------
MAKE_NEW_VARIABLE
LDA ARYTAB ; SET UP CALL TO BLTU TO
LDY ARYTAB+1 ; TO MOVE FROM ARYTAB THRU STREND-1
STA LOWTR ; 7 BYTES HIGHER
STY LOWTR+1 ;
LDA STREND ;
LDY STREND+1 ;
STA HIGHTR ;
STY HIGHTR+1 ;
CLC ;
ADC #7 ;
BCC L_MAKE_NEW_VARIABLE_1 ;
INY ;
L_MAKE_NEW_VARIABLE_1 STA ARYPNT ;
STY ARYPNT+1 ;
JSR BLTU ; MOVE ARRAY BLOCK UP
LDA ARYPNT ; STORE NEW START OF ARRAYS
LDY ARYPNT+1 ;
INY ;
STA ARYTAB ;
STY ARYTAB+1 ;
LDY #0 ;
LDA VARNAM ; FIRST CHAR OF NAME
STA (LOWTR),Y ;
INY ;
LDA VARNAM+1 ; SECOND CHAR OF NAME
STA (LOWTR),Y ;
LDA #0 ; SET FIVE-BYTE VALUE TO 0
INY ;
STA (LOWTR),Y ;
INY ;
STA (LOWTR),Y ;
INY ;
STA (LOWTR),Y ;
INY ;
STA (LOWTR),Y ;
INY ;
STA (LOWTR),Y ;
; --------------------------------
; PUT ADDRESS OF VALUE OF VARIABLE IN VARPNT AND Y,A
; --------------------------------
SET_VARPNT_AND_YA ;
LDA LOWTR ; LOWTR POINTS AT NAME OF VARIABLE,
CLC ; SO ADD 2 TO GET TO VALUE
ADC #2 ;
LDY LOWTR+1 ;
BCC L_SET_VARPNT_AND_YA_1 ;
INY ;
L_SET_VARPNT_AND_YA_1 STA VARPNT ; ADDRESS IN VARPNT AND Y,A
STY VARPNT+1 ;
RTS ;
; --------------------------------
; COMPUTE ADDRESS OF FIRST VALUE IN ARRAY
; ARYPNT = (LOWTR) + #DIMS*2 + 5
; --------------------------------
GETARY LDA NUMDIM ; GET # OF DIMENSIONS
; --------------------------------
GETARY2 ;
ASL ; #DIMS*2 (SIZE OF EACH DIM IN 2 BYTES)
ADC #5 ; + 5 (2 FOR NAME, 2 FOR OFFSET TO NEXT
; ARRAY, AND 1 FOR #DIMS
ADC LOWTR ; ADDRESS OF TH IS ARRAY IN ARYTAB
LDY LOWTR+1 ;
BCC L_GETARY2_1 ;
INY ;
L_GETARY2_1 STA ARYPNT ; ADDRESS OF FIRST VALUE IN ARRAY
STY ARYPNT+1 ;
RTS ;
; --------------------------------
NEG32768 ASM_DATA($90,$80,$00,$00) ; -32768.00049 IN FLOATING POINT
; <<< MEANT TO BE -32768, WHICH WOULD BE 9080000000 >>>
; <<< 1 BYTE SHORT, SO PICKS UP $20 FROM NEXT INSTRUCTION
; --------------------------------
; EVALUATE NUMERIC FORMULA AT TXTPTR
; CONVERTING RESULT TO INTEGER 0 <= X <= 32767
; IN FAC+3,4
; --------------------------------
MAKINT JSR CHRGET
JSR FRMNUM
; --------------------------------
; CONVERT FAC TO INTEGER
; MUST BE POSITIVE AND LESS THAN 32768
; --------------------------------
MKINT LDA FAC_SIGN ; ERROR IF -
BMI MI1
; --------------------------------
; CONVERT FAC TO INTEGER
; MUST BE -32767 <= FAC <= 32767
; --------------------------------
AYINT LDA FAC ; EXPONENT OF VALUE IN FAC
CMP #$90 ; ABS(VALUE) < 32768?
BCC MI2 ; YES, OK FOR INTEGER
LDA #<NEG32768 ; NO; NEXT FEW LINES ARE SUPPOSED TO
LDY #>NEG32768 ; ALLOW -32768 ($8000), BUT DO NOT!
JSR FCOMP ; BECAUSE COMPARED TO -32768.00049
; <<< BUG: A=-32768.00049:A%=A IS ACCEPTED >>>
; <<< BUT PRINT A,A% SHOWS THAT >>>
; <<< A=-32768.0005 (OK), A%=32767 >>>
; <<< WRONG! WRONG! WRONG! >>>
; --------------------------------
MI1 BNE IQERR ; ILLEGAL QUANTITY
MI2 JMP QINT ; CONVERT TO INTEGER
; --------------------------------
; LOCATE ARRAY ELEMENT OR CREATE AN ARRAY
; --------------------------------
ARRAY LDA SUBFLG ; SUBSCRIPTS GIVEN?
BNE L_ARRAY_2 ; NO
; --------------------------------
; PARSE THE SUBSCRIPT LIST
; --------------------------------
LDA DIMFLG ; YES
ORA VALTYP+1 ; SET HIGH BIT IF %
PHA ; SAVE VALTYP AND DIMFLG ON STACK
LDA VALTYP ;
PHA ;
LDY #0 ; COUNT # DIMENSIONS IN Y-REG
L_ARRAY_1 TYA ; SAVE #DIMS ON STACK
PHA ;
LDA VARNAM+1 ; SAVE VARIABLE NAME ON STACK
PHA ;
LDA VARNAM ;
PHA ;
JSR MAKINT ; EVALUATE SUBSCRIPT AS INTEGER
PLA ; RESTORE VARIABLE NAME
STA VARNAM ;
PLA ;
STA VARNAM+1 ;
PLA ; RESTORE # DIMS TO Y-REG
TAY ;
TSX ; COPY VALTYP AND DIMFLG ON STACK
LDA STACK+2,X ; TO LEAVE ROOM FOR THE SUBSCRIPT
PHA ;
LDA STACK+1,X ;
PHA ;
LDA FAC+3 ; GET SUBSCRIPT VALUE AND PLACE IN THE
STA STACK+2,X ; STACK WHERE VALTYP & DIMFLG WERE
LDA FAC+4 ;
STA STACK+1,X ;
INY ; COUNT THE SUBSCRIPT
JSR CHRGOT ; NEXT CHAR
CMP #LOCHAR(`,') ;
BEQ L_ARRAY_1 ; COMMA, PARSE ANOTHER SUBSCRIPT
STY NUMDIM ; NO MORE SUBSCRIPTS, SAVE #
JSR CHKCLS ; NOW NEED ")"
PLA ; RESTORE VALTYPE AND DIMFLG
STA VALTYP ;
PLA ;
STA VALTYP+1 ;
AND #$7F ; ISOLATE DIMFLG
STA DIMFLG ;
; --------------------------------
; SEARCH ARRAY TABLE FOR THIS ARRAY NAME
; --------------------------------
L_ARRAY_2 LDX ARYTAB ; (A,X) = START OF ARRAY TABLE
LDA ARYTAB+1 ;
L_ARRAY_3 STX LOWTR ; USE LOWTR FOR RUNNING POINTER
STA LOWTR+1 ;
CMP STREND+1 ; DID WE REACH THE END OF ARRAYS YET?
BNE L_ARRAY_4 ; NO, KEEP SEARCHING
CPX STREND ;
BEQ MAKE_NEW_ARRAY ; YES, THIS IS A NEW ARRAY NAME
L_ARRAY_4 LDY #0 ; POINT AT 1ST CHAR OF ARRAY NAME
LDA (LOWTR),Y ; GET 1ST CHAR OF NAME
INY ; POINT AT 2ND CHAR
CMP VARNAM ; 1ST CHAR SAME?
BNE L_ARRAY_5 ; NO, MOVE TO NEXT ARRAY
LDA VARNAM+1 ; YES, TRY 2ND CHAR
CMP (LOWTR),Y ; SAME?
BEQ USE_OLD_ARRAY ; YES, ARRAY FOUND
L_ARRAY_5 INY ; POINT AT OFFSET TO NEXT ARRAY
LDA (LOWTR),Y ; ADD OFFSET TO RUNNING POINTER
CLC
ADC LOWTR
TAX
INY
LDA (LOWTR),Y
ADC LOWTR+1
BCC L_ARRAY_3 ; ...ALWAYS
; --------------------------------
; ERROR: BAD SUBSCRIPTS
; --------------------------------
SUBERR LDX #ERR_BADSUBS
ASM_DATA($2C) ; TRICK TO SKIP NEXT LINE
; --------------------------------
; ERROR: ILLEGAL QUANTITY
; --------------------------------
IQERR LDX #ERR_ILLQTY
JER JMP ERROR
; --------------------------------
; FOUND THE ARRAY
; --------------------------------
USE_OLD_ARRAY
LDX #ERR_REDIMD ; SET UP FOR REDIM'D ARRAY ERROR
LDA DIMFLG ; CALLED FROM "DIM" STATEMENT?
BNE JER ; YES, ERROR
LDA SUBFLG ; NO, CHECK IF ANY SUBSCRIPTS
BEQ L_USE_OLD_ARRAY_1 ; YES, NEED TO CHECK THE NUMBER
SEC ; NO, SIGNAL ARRAY FOUND
RTS
; --------------------------------
L_USE_OLD_ARRAY_1 JSR GETARY ; SET (ARYPNT) = ADDR OF FIRST ELEMENT
LDA NUMDIM ; COMPARE NUMBER OF DIMENSIONS
LDY #4
CMP (LOWTR),Y
BNE SUBERR ; NOT SAME, SUBSCRIPT ERROR
JMP FIND_ARRAY_ELEMENT
; --------------------------------
; --------------------------------
; CREATE A NEW ARRAY, UNLESS CALLED FROM GETARYPT
; --------------------------------
MAKE_NEW_ARRAY
LDA SUBFLG ; CALLED FROM GETARYPT?
BEQ L_MAKE_NEW_ARRAY_1 ; NO
LDX #ERR_NODATA ; YES, GIVE "OUT OF DATA" ERROR
JMP ERROR
L_MAKE_NEW_ARRAY_1 JSR GETARY ; PUT ADDR OF 1ST ELEMENT IN ARYPNT
JSR REASON ; MAKE SURE ENOUGH MEMORY LEFT
; --------------------------------
; <<< NEXT 3 LINES COULD BE WRITTEN: >>>
; LDY #0
; STY STRNG2+1
; --------------------------------
LDA #0 ; POINT Y-REG AT VARIABLE NAME SLOT
TAY ;
STA STRNG2+1 ; START SIZE COMPUTATION
LDX #5 ; ASSUME 5-BYTES PER ELEMENT
LDA VARNAM ; STUFF VARIABLE NAME IN ARRAY
STA (LOWTR),Y ;
BPL L_MAKE_NEW_ARRAY_2 ; NOT INTEGER ARRAY
DEX ; INTEGER ARRAY, DECR. SIZE TO 4-BYTES
L_MAKE_NEW_ARRAY_2 INY ; POINT Y-REG AT NEXT CHAR OF NAME
LDA VARNAM+1 ; REST OF ARRAY NAME
STA (LOWTR),Y ;
BPL L_MAKE_NEW_ARRAY_3 ; REAL ARRAY, STICK WITH SIZE = 5 BYTES
DEX ; INTEGER OR STRING ARRAY, ADJUST SIZE
DEX ; TO INTEGER=3, STRING=2 BYTES
L_MAKE_NEW_ARRAY_3 STX STRNG2 ; STORE LOW-BYTE OF ARRAY ELEMENT SIZE
LDA NUMDIM ; STORE NUMBER OF DIMENSIONS
INY ; IN 5TH BYTE OF ARRAY
INY ;
INY ;
STA (LOWTR),Y ;
L_MAKE_NEW_ARRAY_4 LDX #11 ; DEFAULT DIMENSION = 11 ELEMENTS
LDA #0 ; FOR HI-BYTE OF DIMENSION IF DEFAULT
BIT DIMFLG ; DIMENSIONED ARRAY?
BVC L_MAKE_NEW_ARRAY_5 ; NO, USE DEFAULT VALUE
PLA ; GET SPECIFIED DIM IN A,X
CLC ; # ELEMENTS IS 1 LARGER THAN
ADC #1 ; DIMENSION VALUE
TAX ;
PLA ;
ADC #0 ;
L_MAKE_NEW_ARRAY_5 INY ; ADD THIS DIMENSION TO ARRAY DESCRIPTOR
STA (LOWTR),Y
INY
TXA
STA (LOWTR),Y
JSR MULTIPLY_SUBSCRIPT ; MULTIPLY THIS
; DIMENSION BY RUNNING SIZE
; ((LOWTR)) * (STRNG2) --> A,X
STX STRNG2 ; STORE RUNNING SIZE IN STRNG2
STA STRNG2+1 ;
LDY INDEX ; RETRIEVE Y SAVED BY MULTIPLY.SUBSCRIPT
DEC NUMDIM ; COUNT DOWN # DIMS
BNE L_MAKE_NEW_ARRAY_4 ; LOOP TILL DONE
; --------------------------------
; NOW A,X HAS TOTAL # BYTES OF ARRAY ELEMENTS
; --------------------------------
ADC ARYPNT+1 ; COMPUTE ADDRESS OF END OF THIS ARRAY
BCS GME ; ...TOO LARGE, ERROR
STA ARYPNT+1 ;
TAY ;
TXA ;
ADC ARYPNT ;
BCC L_MAKE_NEW_ARRAY_6 ;
INY ;
BEQ GME ; ...TOO LARGE, ERROR
L_MAKE_NEW_ARRAY_6 JSR REASON ; MAKE SURE THERE IS ROOM UP TO Y,A
STA STREND ; THERE IS ROOM SO SAVE NEW END OF TABLE
STY STREND+1 ; AND ZERO THE ARRAY
LDA #0 ;
INC STRNG2+1 ; PREPARE FOR FAST ZEROING LOOP
LDY STRNG2 ; # BYTES MOD 256
BEQ L_MAKE_NEW_ARRAY_8 ; FULL PAGE
L_MAKE_NEW_ARRAY_7 DEY ; CLEAR PAGE FULL
STA (ARYPNT),Y
BNE L_MAKE_NEW_ARRAY_7
L_MAKE_NEW_ARRAY_8 DEC ARYPNT+1 ; POINT TO NEXT PAGE
DEC STRNG2+1 ; COUNT THE PAGES
BNE L_MAKE_NEW_ARRAY_7 ; STILL MORE TO CLEAR
INC ARYPNT+1 ; RECOVER LAST DEC, POINT AT 1ST ELEMENT
SEC ;
LDA STREND ; COMPUTE OFFSET TO END OF ARRAYS
SBC LOWTR ; AND STORE IN ARRAY DESCRIPTOR
LDY #2 ;
STA (LOWTR),Y ;
LDA STREND+1 ;
INY ;
SBC LOWTR+1 ;
STA (LOWTR),Y ;
LDA DIMFLG ; WAS THIS CALLED FROM "DIM" STATEMENT?
BNE RTS_9 ; YES, WE ARE FINISHED
INY ; NO, NOW NEED TO FIND THE ELEMENT
; --------------------------------
; FIND SPECIFIED ARRAY ELEMENT
;
; (LOWTR),Y POINTS AT # OF DIMS IN ARRAY DESCRIPTOR
; THE SUBSCRIPTS ARE ALL ON THE STACK AS INTEGERS
; --------------------------------
FIND_ARRAY_ELEMENT
LDA (LOWTR),Y ; GET # OF DIMENSIONS
STA NUMDIM ;
LDA #0 ; ZERO SUBSCRIPT ACCUMULATOR
STA STRNG2 ;
FAE_1 STA STRNG2+1 ;
INY ;
PLA ; PULL NEXT SUBSCRIPT FROM STACK
TAX ; SAVE IN FAC+3,4
STA FAC+3 ; AND COMPARE WITH DIMENSIONED SIZE
PLA ;
STA FAC+4 ;
CMP (LOWTR),Y ;
BCC FAE_2 ; SUBSCRIPT NOT TOO LARGE
BNE GSE ; SUBSCRIPT IS TOO LARGE
INY ; CHECK LOW-BYTE OF SUBSCRIPT
TXA ;
CMP (LOWTR),Y ;
BCC FAE_3 ; NOT TOO LARGE
; --------------------------------
GSE JMP SUBERR ; BAD SUBSCRIPTS ERROR
GME JMP MEMERR ; MEM FULL ERROR
; --------------------------------
FAE_2 INY ; BUMP POINTER INTO DESCRIPTOR
FAE_3 LDA STRNG2+1 ; BYPASS MULTIPLICATION IF VALUE SO
ORA STRNG2 ; FAR = 0
CLC ;
BEQ L_FAE_3_1 ; IT IS ZERO SO FAR
JSR MULTIPLY_SUBSCRIPT ; NOT ZERO, SO MULTIPLY
TXA ; ADD CURRENT SUBSCRIPT
ADC FAC+3 ;
TAX ;
TYA ;
LDY INDEX ; RETRIEVE Y SAVED BY MULTIPLY.SUBSCRIPT
L_FAE_3_1 ADC FAC+4 ; FINISH ADDING CURRENT SUBSCRIPT
STX STRNG2 ; STORE ACCUMULATED OFFSET
DEC NUMDIM ; LAST SUBSCRIPT YET?
BNE FAE_1 ; NO, LOOP TILL DONE
STA STRNG2+1 ; YES, NOW MULTIPLY BE ELEMENT SIZE
LDX #5 ; START WITH SIZE = 5
LDA VARNAM ; DETERMINE VARIABLE TYPE
BPL L_FAE_3_2 ; NOT INTEGER
DEX ; INTEGER, BACK DOWN SIZE TO 4 BYTES
L_FAE_3_2 LDA VARNAM+1 ; DISCRIMINATE BETWEEN REAL AND STR
BPL L_FAE_3_3 ; IT IS REAL
DEX ; SIZE = 3 IF STRING, =2 IF INTEGER
DEX ;
L_FAE_3_3 STX RESULT+2 ; SET UP MULTIPLIER
LDA #0 ; HI-BYTE OF MULTIPLIER
JSR MULTIPLY_SUBS_1 ; (STRNG2) BY ELEMENT SIZE
TXA ; ADD ACCUMULATED OFFSET
ADC ARYPNT ; TO ADDRESS OF 1ST ELEMENT
STA VARPNT ; TO GET ADDRESS OF SPECIFIED ELEMENT
TYA ;
ADC ARYPNT+1 ;
STA VARPNT+1 ;
TAY ; RETURN WITH ADDR IN VARPNT
LDA VARPNT ; AND IN Y,A
RTS_9 RTS ;
; --------------------------------
; MULTIPLY (STRNG2) BY ((LOWTR),Y)
; LEAVING PRODUCT IN A,X. (HI-BYTE ALSO IN Y.)
; USED ONLY BY ARRAY SUBSCRIPT ROUTINES
; --------------------------------
MULTIPLY_SUBSCRIPT
STY INDEX ; SAVE Y-REG
LDA (LOWTR),Y ; GET MULTIPLIER
STA RESULT+2 ; SAVE IN RESULT+2,3
DEY ;
LDA (LOWTR),Y ;
; --------------------------------
MULTIPLY_SUBS_1 ;
STA RESULT+3 ; LOW BYTE OF MULTIPLIER
LDA #16 ; MULTIPLY 16 BITS
STA INDX ;
LDX #0 ; PRODUCT = 0 INITIALLY
LDY #0 ;
L_MULTIPLY_SUBS_1_1 TXA ; DOUBLE PRODUCT
ASL ; LOW BYTE
TAX ;
TYA ; HIGH BYTE
ROL ; IF TOO LARGE, SET CARRY
TAY ;
BCS GME ; TOO LARGE, "MEM FULL ERROR"
ASL STRNG2 ; NEXT BIT OF MUTLPLICAND
ROL STRNG2+1 ; INTO CARRY
BCC L_MULTIPLY_SUBS_1_2 ; BIT=0, DON'T NEED TO ADD
CLC ; BIT=1, ADD INTO PARTIAL PRODUCT
TXA ;
ADC RESULT+2 ;
TAX ;
TYA ;
ADC RESULT+3 ;
TAY ;
BCS GME ; TOO LARGE, "MEM FULL ERROR"
L_MULTIPLY_SUBS_1_2 DEC INDX ; 16-BITS YET?
BNE L_MULTIPLY_SUBS_1_1 ; NO, KEEP SHUFFLING
RTS ; YES, PRODUCT IN Y,X AND A,X
; --------------------------------
; "FRE" FUNCTION
;
; COLLECTS GARBAGE AND RETURNS # BYTES OF MEMORY LEFT
; --------------------------------
FRE LDA VALTYP ; LOOK AT VALUE OF ARGUMENT
BEQ L_FRE_1 ; =0 MEANS REAL, =$FF MEANS STRING
JSR FREFAC ; STRING, SO SET IT FREE IS TEMP
L_FRE_1 JSR GARBAG ; COLLECT ALL THE GARBAGE IN SIGHT
SEC ; COMPUTE SPACE BETWEEN ARRAYS AND
LDA FRETOP ; STRING TEMP AREA
SBC STREND ;
TAY ;
LDA FRETOP+1 ;
SBC STREND+1 ; FREE SPACE IN Y,A
; FALL INTO GIVAYF TO FLOAT THE VALUE
; NOTE THAT VALUES OVER 32767 WILL RETURN AS NEGATIVE
; --------------------------------
; FLOAT THE SIGNED INTEGER IN A,Y
; --------------------------------
GIVAYF LDX #0 ; MARK FAC VALUE TYPE REAL
STX VALTYP ;
STA FAC+1 ; SAVE VALUE FROM A,Y IN MANTISSA
STY FAC+2 ;
LDX #$90 ; SET EXPONENT TO 2^16
JMP FLOAT_1 ; CONVERT TO SIGNED FP
; --------------------------------
; "POS" FUNCTION
;
; RETURNS CURRENT LINE POSITION FROM MON.CH
; --------------------------------
POS LDY MON_CH ; GET A,Y = (MON.CH, GO TO GIVAYF
; --------------------------------
; FLOAT (Y) INTO FAC, GIVING VALUE 0-255
; --------------------------------
SNGFLT LDA #0 ; MSB = 0
SEC ; <<< NO PURPOSE WHATSOEVER >>>
BEQ GIVAYF ; ...ALWAYS
; --------------------------------
; CHECK FOR DIRECT OR RUNNING MODE
; GIVING ERROR IF DIRECT MODE
; --------------------------------
ERRDIR LDX CURLIN+1 ; =$FF IF DIRECT MODE
INX ; MAKES $FF INTO ZERO
BNE RTS_9 ; RETURN IF RUNNING MODE
LDX #ERR_ILLDIR ; DIRECT MODE, GIVE ERROR
ASM_DATA($2C) ; TRICK TO SKIP NEXT 2 BYTES
; --------------------------------
UNDFNC LDX #ERR_UNDEFFUNC ; UNDEFINDED FUNCTION ERROR
JMP ERROR
; --------------------------------
; "DEF" STATEMENT
; --------------------------------
DEF JSR FNC_ ; PARSE "FN", FUNCTION NAME
JSR ERRDIR ; ERROR IF IN DIRECT MODE
JSR CHKOPN ; NEED "("
LDA #$80 ; FLAG PTRGET THAT CALLED FROM "DEF FN"
STA SUBFLG ; ALLOW ONLY SIMPLE FP VARIABLE FOR ARG
JSR PTRGET ; GET PNTR TO ARGUMENT
JSR CHKNUM ; MUST BE NUMERIC
JSR CHKCLS ; MUST HAVE ")" NOW
LDA #TOKENEQUUAL ; NOW NEED "="
JSR SYNCHR ; OR ELSE SYNTAX ERROR
PHA ; SAVE CHAR AFTER "="
LDA VARPNT+1 ; SAVE PNTR TO ARGUMENT
PHA
LDA VARPNT
PHA
LDA TXTPTR+1 ; SAVE TXTPTR
PHA
LDA TXTPTR
PHA
JSR DATA ; SCAN TO NEXT STATEMENT
JMP FNCDATA ; STORE ABOVE 5 BYTES IN "VALUE"
; --------------------------------
; COMMON ROUTINE FOR "DEFFN" AND "FN", TO
; PARSE "FN" AND THE FUNCTION NAME
; --------------------------------
FNC_ LDA #TOKEN_FN ; MUST NOW SEE "FN" TOKEN
JSR SYNCHR ; OR ELSE SYNTAX ERROR
ORA #$80 ; SET SIGN BIT ON 1ST CHAR OF NAME,
STA SUBFLG ; MAKING $C0 < SUBFLG < $DB
JSR PTRGET3 ; WHICH TELLS PTRGET WHO CALLED
STA FNCNAM ; FOUND VALID FUNCTION NAME, SO
STY FNCNAM+1 ; SAVE ADDRESS
JMP CHKNUM ; MUST BE NUMERIC
; --------------------------------
; "FN" FUNCTION CALL
; --------------------------------
FUNCT JSR FNC_ ; PARSE "FN", FUNCTION NAME
LDA FNCNAM+1 ; STACK FUNCTION ADDRESS
PHA ; IN CASE OF A NESTED FN CALL
LDA FNCNAM ;
PHA ;
JSR PARCHK ; MUST NOW HAVE "(EXPRESSION)"
JSR CHKNUM ; MUST BE NUMERIC EXPRESSION
PLA ; GET FUNCTION ADDRESS BACK
STA FNCNAM ;
PLA ;
STA FNCNAM+1 ;
LDY #2 ; POINT AT ADD OF ARGUMENT VARIABLE
LDA (FNCNAM),Y
STA VARPNT
TAX
INY
LDA (FNCNAM),Y
BEQ UNDFNC ; UNDEFINED FUNCTION
STA VARPNT+1
INY ; Y=4 NOW
L_FUNCT_1 LDA (VARPNT),Y ; SAVE OLD VALUE OF ARGUMENT VARIABLE
PHA ; ON STACK, IN CASE ALSO USED AS
DEY ; A NORMAL VARIABLE!
BPL L_FUNCT_1
LDY VARPNT+1 ; (Y,X)= ADDRESS, STORE FAC IN VARIABLE
JSR STORE_FACDB_YX_ROUNDED
LDA TXTPTR+1 ; REMEMBER TXTPTR AFTER FN CALL
PHA
LDA TXTPTR
PHA
LDA (FNCNAM),Y ; Y=0 FROM MOVMF
STA TXTPTR ; POINT TO FUNCTION DEF'N
INY
LDA (FNCNAM),Y
STA TXTPTR+1
LDA VARPNT+1 ; SAVE ADDRESS OF ARGUMENT VARIABLE
PHA ;
LDA VARPNT ;
PHA ;
JSR FRMNUM ; EVALUATE THE FUNCTION EXPRESSION
PLA ; GET ADDRESS OF ARGUMENT VARIABLE
STA FNCNAM ; AND SAVE IT
PLA ;
STA FNCNAM+1 ;
JSR CHRGOT ; MUST BE AT ":" OR EOL
BEQ L_FUNCT_2 ; WE ARE
JMP SYNERR ; WE ARE NOT, SLYNTAX ERROR
L_FUNCT_2 PLA ; RETRIEVE TXTPTR AFTER "FN" CALL
STA TXTPTR
PLA
STA TXTPTR+1
; STACK NOW HAS 5-BYTE VALUE
; OF THE ARGUMENT VARIABLE,
; AND FNCNAM POINTS AT THE VARIABLE
; --------------------------------
; STORE FIVE BYTES FROM STACK AT (FNCNAM)
; --------------------------------
FNCDATA
LDY #0
PLA
STA (FNCNAM),Y
PLA
INY
STA (FNCNAM),Y
PLA
INY
STA (FNCNAM),Y
PLA
INY
STA (FNCNAM),Y
PLA
INY
STA (FNCNAM),Y
RTS
; --------------------------------
; "STR$" FUNCTION
; --------------------------------
STR JSR CHKNUM ; EXPRESSION MUST BE NUMERIC
LDY #0 ; START STRING AT STACK-1 ($00FF)
; SO STRLIT CAN DIFFRENTIATE STR$ CALLS
JSR FOUT_1 ; CONVERT FAC TO STRING
PLA ; POP RETURN OFF STACK
PLA ;
LDA #<STACK-1 ; POINT TO STACK-1
LDY #>STACK-1 ; (WHICH=0)
BEQ STRLIT ; ...ALWAYS, CREATE DESC & MOVE STRING
; --------------------------------
; GET SPACE AND MAKE DESCRIPTOR FOR STRING WHOSE
; ADDRESS IS IN FAC+3,4 AND WHOSE LENGTH IS IN A-REG
; --------------------------------
STRINI LDX FAC+3 ; Y,X = STRING ADDRESS
LDY FAC+4 ;
STX DSCPTR ;
STY DSCPTR+1 ;
; --------------------------------
; GET SPACE AND MAKE DESCRIPTOR FOR STRING WHOSE
; ADDRESS IS IN Y,X AND WHOSE LENGTH IS IN A-REG
; --------------------------------
STRSPA JSR GETSPA ; A HOLDS LENGTH
STX FAC+1 ; SAVE DESCRIPTOR IN FAC
STY FAC+2 ; ---FAC--- --FAC+1-- --FAC+2--
STA FAC ; <LENGTH> <ADDR-LO> <ADDR-HI>
RTS ;
; --------------------------------
; BUILD A DESCRIPTOR FOR STRING STARTING AT Y,A
; AND TERMINATED BY $00 OR QUOTATION MARK
; RETURN WITH DESCRIPTOR IN A TEMPORARY
; AND ADDRESS OF DESCRIPTOR IN FAC+3,4
; --------------------------------
STRLIT LDX #$22 ; SET UP LITERAL SCAN TO STOP ON
STX CHARAC ; QUOTATION MARK OR $00
STX ENDCHR ;
; --------------------------------
; BUILD A DESCRIPTOR FOR STRING STARTING AT Y,A
; AND TERMINATED BY $00, (CHARAC), OR (ENDCHR)
;
; RETURN WITH DESCRIPTOR IN A TEMPORARY
; AND ADDRESS OF DESCRIPTOR IN FAC+3,4
; --------------------------------
STRLT2 STA STRNG1 ; SAVE ADDRESS OF STRING
STY STRNG1+1 ;
STA FAC+1 ; ...AGAIN
STY FAC+2 ;
LDY #$FF ;
L_STRLT2_1 INY ; FIND END OF STRING
LDA (STRNG1),Y ; NEXT STRING CHAR
BEQ L_STRLT2_3 ; END OF STRING
CMP CHARAC ; ALTERNATE TERMINATOR # 1?
BEQ L_STRLT2_2 ; YES
CMP ENDCHR ; ALTERNATE TERMINATOR # 2?
BNE L_STRLT2_1 ; NO, KEEP SCANNING
L_STRLT2_2 CMP #$22 ; IS STRING ENDED WITH QUOTE MARK?
BEQ L_STRLT2_4 ; YES, C=1 TO INCLUDE " IN STRING
L_STRLT2_3 CLC ;
L_STRLT2_4 STY FAC ; SAVE LENGTH
TYA ;
ADC STRNG1 ; COMPUTE ADDRESS OF END OF STRING
STA STRNG2 ; (OF 00 BYTE, OR JUST AFTER ")
LDX STRNG1+1 ;
BCC L_STRLT2_5 ;
INX ;
L_STRLT2_5 STX STRNG2+1 ;
LDA STRNG1+1 ; WHERE DOES THE STRING START?
BEQ L_STRLT2_6 ; PAGE 0, MUST BE FROM STR$ FUNCTION
CMP #2 ; PAGE 2?
BNE PUTNEW ; NO, NOT PAGE 0 OR 2
L_STRLT2_6 TYA ; LENGTH OF STRING
JSR STRINI ; MAKE SPACE FOR STRING
LDX STRNG1 ;
LDY STRNG1+1 ;
JSR MOVSTR ; MOVE IT IN
; --------------------------------
; STORE DESCRIPTOR IN TEMPORARY DESCRIPTOR STACK
;
; THE DESCRIPTOR IS NOW IN FAC, FAC+1, FAC+2
; PUT ADDRESS OF TEMP DESCRIPTOR IN FAC+3,4
; --------------------------------
PUTNEW LDX TEMPPT ; POINTER TO NEXT TEMP STRING SLOT
CPX #TEMPST+9 ; MAX OF 3 TEMP STRINGS
BNE PUTEMP ; ROOM FOR ANOTHER ONE
LDX #ERR_FRMCPX ; TOO MANY, FORMULA TOO COMPLEX
JERR JMP ERROR
; --------------------------------
PUTEMP LDA FAC ; COPY TEMP DESCRIPTOR INTO TEMP STACK
STA 0,X
LDA FAC+1
STA 1,X
LDA FAC+2
STA 2,X
LDY #0
STX FAC+3 ; ADDRESS OF TEMP DESCRIPTOR
STY FAC+4 ; IN Y,X AND FAC+3,4
DEY ; Y=$FF
STY VALTYP ; FLAG (FAC ) AS STRING
STX LASTPT ; INDEX OF LAST POINTER
INX ; UPDATE FOR NEXT TEMP ENTRY
INX
INX
STX TEMPPT
RTS
; --------------------------------
; MAKE SPACE FOR STRING AT BOTTOM OF STRING SPACE
; (A)=# BYTES SPACE TO MAKE
;
; RETURN WITH (A) SAME,
; AND Y,X = ADDRESS OF SPACE ALLOCATED
; --------------------------------
GETSPA LSR GARFLG ; CLEAR SIGNBIT OF FLAG
L_GETSPA_1 PHA ; A HOLDS LENGTH
EOR #$FF ; GET -LENGTH
SEC ;
ADC FRETOP ; COMPUTE STARTING ADDRESS OF SPACE
LDY FRETOP+1 ; FOR THE STRING
BCS L_GETSPA_2 ;
DEY ;
L_GETSPA_2 CPY STREND+1 ; SEE IF FITS IN REMAINING MEMORY
BCC L_GETSPA_4 ; NO, TRY GARBAGE
BNE L_GETSPA_3 ; YES, IT FITS
CMP STREND ; HAVE TO CHECK LOWER BYTES
BCC L_GETSPA_4 ; NOT ENUF ROOM YET
L_GETSPA_3 STA FRETOP ; THERE IS ROOM SO SAVE NEW FRETOP
STY FRETOP+1 ;
STA FRESPC ;
STY FRESPC+1 ;
TAX ; ADDR IN Y,X
PLA ; LENGTH IN A
RTS
L_GETSPA_4 LDX #ERR_MEMFULL
LDA GARFLG ; GARBAGE DONE YET?
BMI JERR ; YES, MEMORY IS REALLY FULL
JSR GARBAG ; NO, TRY COLLECTING NOW
LDA #$80 ; FLAG THAT COLLECTED GARBAGE ALREADY
STA GARFLG ;
PLA ; GET STRING LENGTH AGAIN
BNE L_GETSPA_1 ; ...ALWAYS
; --------------------------------
; SHOVE ALL REFERENCED STRINGS AS HIGH AS POSSIBLE
; IN MEMORY (AGAINST HIMEM), FREEING UP SPACE
; BELOW STRING AREA DOWN TO STREND.
; --------------------------------
GARBAG LDX MEMSIZ ; COLLECT FROM TOP DOWN
LDA MEMSIZ+1 ;
FIND_HIGHEST_STRING ;
STX FRETOP ; ONE PASS THROUGH ALL VARS
STA FRETOP+1 ; FOR EACH ACTIVE STRING!
LDY #0 ;
STY FNCNAM+1 ; FLAG IN CASE NO STRINGS TO COLLECT
LDA STREND ;
LDX STREND+1 ;
STA LOWTR ;
STX LOWTR+1 ;
; --------------------------------
; START BY COLLECTING TEMPORARIES
; --------------------------------
LDA #<TEMPST ;
LDX #>TEMPST ;
STA INDEX ;
STX INDEX+1 ;
L_FIND_HIGHEST_STRING_1 CMP TEMPPT ; FINISHED WITH TEMPS YET?
BEQ L_FIND_HIGHEST_STRING_2 ; YES, NOW DO SIMPLE VARIABLES
JSR CHECK_VARIABLE ; DO A TEMP
BEQ L_FIND_HIGHEST_STRING_1 ; ...ALWAYS
; --------------------------------
; NOW COLLECT SIMPLE VARIABLES
; --------------------------------
L_FIND_HIGHEST_STRING_2 LDA #7 ; LENGTH OF EACH VARIABLE IS 7 BYTES
STA DSCLEN ;
LDA VARTAB ; START AT BEGINNING OF VARTAB
LDX VARTAB+1
STA INDEX
STX INDEX+1
L_FIND_HIGHEST_STRING_3 CPX ARYTAB+1 ; FINISHED WITH SIMPLE VARIABLES?
BNE L_FIND_HIGHEST_STRING_4 ; NO
CMP ARYTAB ; MAYBE, CHECK LO-BYTE
BEQ L_FIND_HIGHEST_STRING_5 ; YES, NOW DO ARRAYS
L_FIND_HIGHEST_STRING_4 JSR CHECK_SIMPLE_VARIABLE
BEQ L_FIND_HIGHEST_STRING_3 ; ...ALWAYS
; --------------------------------
; NOW COLLECT ARRAY VARIABLES
; --------------------------------
L_FIND_HIGHEST_STRING_5 STA ARYPNT
STX ARYPNT+1
LDA #3 ; DESCRIPTORS IN ARRAYS ARE 3-BYTES EACH
STA DSCLEN ;
L_FIND_HIGHEST_STRING_6 LDA ARYPNT ; COMPARE TO END OF ARRAYS
LDX ARYPNT+1 ;
L_FIND_HIGHEST_STRING_7 CPX STREND+1 ; FINISHED WITH ARRAYS YET?
BNE L_FIND_HIGHEST_STRING_8 ; NOT YET
CMP STREND ; MAYBE, CHECK LO-BYTE
BNE L_FIND_HIGHEST_STRING_8 ; NOT FINISHED YET
JMP MOVE_HIGHEST_STRING_TO_TOP ; FINISHED
L_FIND_HIGHEST_STRING_8 STA INDEX ; SET UP PNTR TO START OF ARRAY
STX INDEX+1 ;
LDY #0 ; POINT AT NAME OF ARRAY
LDA (INDEX),Y ;
TAX ; 1ST LETTER OF NAME IN X-REG
INY ;
LDA (INDEX),Y ;
PHP ; STATUS FROM SECOND LETTER OF NAME
INY ;
LDA (INDEX),Y ; OFFSET TO NEXT ARRAY
ADC ARYPNT ; (CARRY ALWAYS CLEAR)
STA ARYPNT ; CALCULATE START OF NEXT ARRAY
INY ;
LDA (INDEX),Y ; HI-BYTE OF OFFSET
ADC ARYPNT+1 ;
STA ARYPNT+1 ;
PLP ; GET STATUS FROM 2ND CHAR OF NAME
BPL L_FIND_HIGHEST_STRING_6 ; NOT A STRING ARRAY
TXA ; SET STATUS WITH 1ST CHAR OF NAME
BMI L_FIND_HIGHEST_STRING_6 ; NOT A STRING ARRAY
INY ;
LDA (INDEX),Y ; # OF DIMENSIONS FOR THIS ARRAY
LDY #0 ;
ASL ; PREAMBLE SIZE = 2*#DIMS + 5
ADC #5 ;
ADC INDEX ; MAKE INDEX POINT AT FIRST ELEMENT
STA INDEX ; IN THE ARRAY
BCC L_FIND_HIGHEST_STRING_9 ;
INC INDEX+1 ;
L_FIND_HIGHEST_STRING_9 ;
LDX INDEX+1 ; STEP THRU EACH STRING IN THIS ARRAY
L_FIND_HIGHEST_STRING_10 CPX ARYPNT+1 ; ARRAY DONE?
BNE L_FIND_HIGHEST_STRING_11 ; NO, PROCESS NEXT ELEMENT
CMP ARYPNT ; MAYBE, CHECK LO-BYTE
BEQ L_FIND_HIGHEST_STRING_7 ; YES, MOVE TO NEXT ARRAY
L_FIND_HIGHEST_STRING_11 JSR CHECK_VARIABLE ; PROCESS THE ARRAY
BEQ L_FIND_HIGHEST_STRING_10 ; ...ALWAYS
; --------------------------------
; PROCESS A SIMPLE VARIABLE
; --------------------------------
CHECK_SIMPLE_VARIABLE
LDA (INDEX),Y ; LOOK AT 1ST CHAR OF NAME
BMI CHECK_BUMP ; NOT A STRING VARIABLE
INY ;
LDA (INDEX),Y ; LOOK AT 2ND CHAR OF NAME
BPL CHECK_BUMP ; NOT A STRING VARIABLE
INY ;
; --------------------------------
; IF STRING IS NOT EMPTY, CHECK IF IT IS HIGHEST
; --------------------------------
CHECK_VARIABLE ;
LDA (INDEX),Y ; GET LENGTH OF STRING
BEQ CHECK_BUMP ; IGNORE STRING IF LENGTH IS ZERO
INY ;
LDA (INDEX),Y ; GET ADDRESS OF STRING
TAX ;
INY ;
LDA (INDEX),Y ;
CMP FRETOP+1 ; CHECK IF ALREADY COLLECTED
BCC L_CHECK_VARIABLE_1 ; NO, BELOW FRETOP
BNE CHECK_BUMP ; YES, ABOVE FRETOP
CPX FRETOP ; MAYBE, CHECK LO-BYTE
BCS CHECK_BUMP ; YES, ABOVE FRETOP
L_CHECK_VARIABLE_1 CMP LOWTR+1 ; ABOVE HIGHEST STRING FOUND?
BCC CHECK_BUMP ; NO, IGNORE FOR NOW
BNE L_CHECK_VARIABLE_2 ; YES, THIS IS THE NEW HIGHEST
CPX LOWTR ; MAYBE, TRY LO-BYTE
BCC CHECK_BUMP ; NO, IGNORE FOR NOW
L_CHECK_VARIABLE_2 STX LOWTR ; MAKE THIS THE HIGHEST STRING
STA LOWTR+1
LDA INDEX ; SAVE ADDRESS OF DESCRIPTOR TOO
LDX INDEX+1
STA FNCNAM
STX FNCNAM+1
LDA DSCLEN
STA LENGTH
; --------------------------------
; ADD (DSCLEN) TO PNTR IN INDEX
; RETURN WITH Y=0, PNTR ALSO IN X,A
; --------------------------------
CHECK_BUMP
LDA DSCLEN ; BUMP TO NEXT VARIABLE
CLC
ADC INDEX
STA INDEX
BCC CHECK_EXIT
INC INDEX+1
; --------------------------------
CHECK_EXIT
LDX INDEX+1
LDY #0
RTS
; --------------------------------
; FOUND HIGHEST NON-EMPTY STRING, SO MOVE IT
; TO TOP AND GO BACK FOR ANOTHER
; --------------------------------
MOVE_HIGHEST_STRING_TO_TOP
LDX FNCNAM+1 ; ANY STRING FOUND?
BEQ CHECK_EXIT ; NO, RETURN
LDA LENGTH ; GET LENGTH OF VARIABLE ELEMENT
AND #4 ; WAS 7 OR 3, MAKE 4 OR 0
LSR ; 2 0R 0; IN SIMPLE VARIABLES,
TAY ; NAME PRECEDES DESCRIPTOR
STA LENGTH ; 2 OR 0
LDA (FNCNAM),Y ; GET LENGTH FROM DESCRIPTOR
ADC LOWTR ; CARRY ALREADY CLEARED BY LSR
STA HIGHTR ; STRING IS BTWN (LOWTR) AND (HIGHTR)
LDA LOWTR+1 ;
ADC #0 ;
STA HIGHTR+1 ;
LDA FRETOP ; HIGH END DESTINATION
LDX FRETOP+1 ;
STA HIGHDS ;
STX HIGHDS+1 ;
JSR BLTU2 ; MOVE STRING UP
LDY LENGTH ; FIX ITS DESCRIPTOR
INY ; POINT AT ADDRESS IN DESCRIPTOR
LDA HIGHDS ; STORE NEW ADDRESS
STA (FNCNAM),Y
TAX
INC HIGHDS+1 ; CORRECT BLTU'S OVERSHOOT
LDA HIGHDS+1
INY
STA (FNCNAM),Y
JMP FIND_HIGHEST_STRING
; --------------------------------
; --------------------------------
; CONCATENATE TWO STRINGS
; --------------------------------
CAT LDA FAC+4 ; SAVE ADDRESS OF FIRST DESCRIPTOR
PHA
LDA FAC+3
PHA
JSR FRM_ELEMENT ; GET SECOND STRING ELEMENT
JSR CHKSTR ; MUST BE A STRING
PLA ; RECOVER ADDRES OF 1ST DESCRIPTOR
STA STRNG1
PLA
STA STRNG1+1
LDY #0
LDA (STRNG1),Y ; ADD LENGTHS, GET CONCATENATED SIZE
CLC
ADC (FAC+3),Y
BCC L_CAT_1 ; OK IF < $100
LDX #ERR_STRLONG
JMP ERROR
L_CAT_1 JSR STRINI ; GET SPACE FOR CONCATENATED STRINGS
JSR MOVINS ; MOVE 1ST STRING
LDA DSCPTR ;
LDY DSCPTR+1 ;
JSR FRETMP ;
JSR MOVSTR_1 ; MOVE 2ND STRING
LDA STRNG1 ;
LDY STRNG1+1 ;
JSR FRETMP ;
JSR PUTNEW ; SET UP DESCRIPTOR
JMP FRMEVL_2 ; FINISH EXPRESSION
; --------------------------------
; GET STRING DESCRIPTOR POINTED AT BY (STRNG1)
; AND MOVE DESCRIBED STRING TO (FRESPC)
; --------------------------------
MOVINS LDY #0
LDA (STRNG1),Y
PHA ; LENGTH
INY
LDA (STRNG1),Y
TAX ; PUT STRING POINTER IN X,Y
INY
LDA (STRNG1),Y
TAY
PLA ; RETRIEVE LENGTH
; --------------------------------
; MOVE STRING AT (Y,X) WITH LENGTH (A)
; TO DESTINATION WHOSE ADDRESS IS IN FRESPC,FRESPC+1
; --------------------------------
MOVSTR STX INDEX ; PUT POINTER IN INDEX
STY INDEX+1 ;
MOVSTR_1 ;
TAY ; LENGTH TO Y-REG
BEQ L_MOVSTR_1_2 ; IF LENGTH IS ZERO, FINISHED
PHA ; SAVE LENGTH ON STACK
L_MOVSTR_1_1 DEY ; MOVE BYTES FROM (INDEX) TO (FRESPC)
LDA (INDEX),Y
STA (FRESPC),Y
TYA ; TEST IF ANY LEFT TO MOVE
BNE L_MOVSTR_1_1 ; YES, KEEP MOVING
PLA ; NO, FINISHED. GET LENGTH
L_MOVSTR_1_2 CLC ; AND ADD TO FRESPC, SO
ADC FRESPC ; FRESPC POINTS TO NEXT HIGHER
STA FRESPC ; BYTE. (USED BY CONCATENATION)
BCC L_MOVSTR_1_3
INC FRESPC+1
L_MOVSTR_1_3 RTS
; --------------------------------
; IF (FAC) IS A TEMPORARY STRING, RELEASE DESCRIPTOR
; --------------------------------
FRESTR JSR CHKSTR ; LAST RESULT A STRING?
; --------------------------------
; IF STRING DESCRIPTOR POINTED TO BY FAC+3,4 IS
; A TEMPORARY STRING, RELEASE IT.
; --------------------------------
FREFAC LDA FAC+3 ; GET DESCRIPTOR POINTER
LDY FAC+4
; --------------------------------
; IF STRING DESCRIPTOR WHOSE ADDRESS IS IN Y,A IS
; A TEMPORARY STRING, RELEASE IT.
; --------------------------------
FRETMP STA INDEX ; SAVE THE ADDRESS OF THE DESCRIPTOR
STY INDEX+1 ;
JSR FRETMS ; FREE DESCRIPTOR IF IT IS TEMPORARY
PHP ; REMEMBER IF TEMP
LDY #0 ; POINT AT LENGTH OF STRING
LDA (INDEX),Y ;
PHA ; SAVE LENGTH ON STACK
INY ;
LDA (INDEX),Y ;
TAX ; GET ADDRESS OF STRING IN Y,X
INY ;
LDA (INDEX),Y ;
TAY ;
PLA ; LENGTH IN A
PLP ; RETRIEVE STATUS, Z=1 IF TEMP
BNE L_FRETMP_2 ; NOT A TEMPORARY STRING
CPY FRETOP+1 ; IS IT THE LOWEST STRING?
BNE L_FRETMP_2 ; NO
CPX FRETOP ;
BNE L_FRETMP_2 ; NO
PHA ; YES, PUSH LENGTH AGAIN
CLC ; RECOVER THE SPACE USED BY
ADC FRETOP ; THE STRING
STA FRETOP ;
BCC L_FRETMP_1 ;
INC FRETOP+1 ;
L_FRETMP_1 PLA ; RETRIEVE LENGTH AGAIN
L_FRETMP_2 STX INDEX ; ADDRESS OF STRING IN Y,X
STY INDEX+1 ; LENGTH OF STRING IN A-REG
RTS ;
; --------------------------------
; RELEASE TEMPORARY DESCRIPTOR IF Y,A = LASTPT
; --------------------------------
FRETMS CPY LASTPT+1 ; COMPARE Y,A TO LATEST TEMP
BNE L_FRETMS_1 ; NOT SAME ONE, CANNOT RELEASE
CMP LASTPT ;
BNE L_FRETMS_1 ; NOT SAME ONE, CANNOT RELEASE
STA TEMPPT ; UPDATE TEMPT FOR NEXT TEMP
SBC #3 ; BACK OFF LASTPT
STA LASTPT ;
LDY #0 ; NOW Y,A POINTS TO TOP TEMP
L_FRETMS_1 RTS ; Z=0 IF NOT TEMP, Z=1 IF TEMP
; --------------------------------
; "CHR$" FUNCTION
; --------------------------------
CHRSTR JSR CONINT ; CONVERT ARGUMENT TO BYTE IN X
TXA ;
PHA ; SAVE IT
LDA #1 ; GET SPACE FOR STRING OF LENGTH 1
JSR STRSPA ;
PLA ; RECALL THE CHARACTER
LDY #0 ; PUT IN STRING
STA (FAC+1),Y ;
PLA ; POP RETURN ADDRESS
PLA ;
JMP PUTNEW ; MAKE IT A TEMPORARY STRING
; --------------------------------
; "LEFT$" FUNCTION
; --------------------------------
LEFTSTR
JSR SUBSTRING_SETUP
CMP (DSCPTR),Y ; COMPARE 1ST PARAMETER TO LENGTH
TYA ; Y=A=0
SUBSTRING_1 ;
BCC L_SUBSTRING_1_1 ; 1ST PARAMETER SMALLER, USE IT
LDA (DSCPTR),Y ; 1ST IS LONGER, USE STRING LENGTH
TAX ; IN X-REG
TYA ; Y=A=0 AGAIN
L_SUBSTRING_1_1 PHA ; PUSH LEFT END OF SUBSTRING
SUBSTRING_2 ;
TXA ;
SUBSTRING_3 ;
PHA ; PUSH LENGTH OF SUBSTRING
JSR STRSPA ; MAKE ROOM FOR STRING OF (A) BYTES
LDA DSCPTR ; RELEASE PARAMETER STRING IF TEMP
LDY DSCPTR+1 ;
JSR FRETMP ;
PLA ; GET LENGTH OF SUBSTRING
TAY ; IN Y-REG
PLA ; GET LEFT END OF SUBSTRING
CLC ; ADD TO POINTER TO STRING
ADC INDEX ;
STA INDEX ;
BCC L_SUBSTRING_3_1 ;
INC INDEX+1 ;
L_SUBSTRING_3_1 TYA ; LENGTH
JSR MOVSTR_1 ; COPY STRING INTO SPACE
JMP PUTNEW ; ADD TO TEMPS
; --------------------------------
; "RIGHT$" FUNCTION
; --------------------------------
RIGHTSTR
JSR SUBSTRING_SETUP
CLC ; COMPUTE LENGTH-WIDTH OF SUBSTRING
SBC (DSCPTR),Y ; TO GET STARTING POINT IN STRING
EOR #$FF
JMP SUBSTRING_1 ; JOIN LEFT$
; --------------------------------
; "MID$" FUNCTION
; --------------------------------
MIDSTR LDA #$FF ; FLAG WHETHER 2ND PARAMETER
STA FAC+4 ;
JSR CHRGOT ; SEE IF ")" YET
CMP #LOCHAR(`)') ;
BEQ L_MIDSTR_1 ; YES, NO 2ND PARAMETER
JSR CHKCOM ; NO, MUST HAVE COMMA
JSR GETBYT ; GET 2ND PARAM IN X-REG
L_MIDSTR_1 JSR SUBSTRING_SETUP
DEX ; 1ST PARAMETER - 1
TXA
PHA
CLC
LDX #0
SBC (DSCPTR),Y
BCS SUBSTRING_2
EOR #$FF
CMP FAC+4 ; USE SMALLER OF TWO
BCC SUBSTRING_3
LDA FAC+4
BCS SUBSTRING_3 ; ...ALWAYS
; --------------------------------
; COMMON SETUP ROUTINE FOR LEFT$, RIGHT$, MID$:
; REQUIRE ")"; POP RETURN ADRS, GET DESCRIPTOR
; ADDRESS, GET 1ST PARAMETER OF COMMAND
; --------------------------------
SUBSTRING_SETUP
JSR CHKCLS ; REQUIRE ")"
PLA ; SAVE RETURN ADDRESS
TAY ; IN Y-REG AND LENGTH
PLA ;
STA LENGTH ;
PLA ; POP PREVIOUS RETURN ADDRESS
PLA ; (FROM GOROUT).
PLA ; RETRIEVE 1ST PARAMETER
TAX ;
PLA ; GET ADDRESS OF STRING DESCRIPTOR
STA DSCPTR ;
PLA ;
STA DSCPTR+1 ;
LDA LENGTH ; RESTORE RETURN ADDRESS
PHA ;
TYA ;
PHA ;
LDY #0 ;
TXA ; GET 1ST PARAMETER IN A-REG
BEQ GOIQ ; ERROR IF 0
RTS
; --------------------------------
; "LEN" FUNCTION
; --------------------------------
LEN JSR GETSTR ; GET LENTGH IN Y-REG, MAKE FAC NUMERIC
JMP SNGFLT ; FLOAT Y-REG INTO FAC
; --------------------------------
; IF LAST RESULT IS A TEMPORARY STRING, FREE IT
; MAKE VALTYP NUMERIC, RETURN LENGTH IN Y-REG
; --------------------------------
GETSTR JSR FRESTR ; IF LAST RESULT IS A STRING, FREE IT
LDX #0 ; MAKE VALTYP NUMERIC
STX VALTYP ;
TAY ; LENGTH OF STRING TO Y-REG
RTS
; --------------------------------
; "ASC" FUNCTION
; --------------------------------
ASC JSR GETSTR ; GET STRING, GET LENGTH IN Y-REG
BEQ GOIQ ; ERROR IF LENGTH 0
LDY #0 ;
LDA (INDEX),Y ; GET 1ST CHAR OF STRING
TAY ;
JMP SNGFLT ; FLOAT Y-REG INTO FAC
; --------------------------------
GOIQ JMP IQERR ; ILLEGAL QUANTITY ERROR
; --------------------------------
; SCAN TO NEXT CHARACTER AND CONVERT EXPRESSION
; TO SINGLE BYTE IN X-REG
; --------------------------------
GTBYTC JSR CHRGET
; --------------------------------
; EVALUATE EXPRESSION AT TXTPTR, AND
; CONVERT IT TO SINGLE BYTE IN X-REG
; --------------------------------
GETBYT JSR FRMNUM
; --------------------------------
; CONVERT (FAC) TO SINGLE BYTE INTEGER IN X-REG
; --------------------------------
CONINT JSR MKINT ; CONVERT IF IN RANGE -32767 TO +32767
LDX FAC+3 ; HI-BYTE MUST BE ZERO
BNE GOIQ ; VALUE > 255, ERROR
LDX FAC+4 ; VALUE IN X-REG
JMP CHRGOT ; GET NEXT CHAR IN A-REG
; --------------------------------
; "VAL" FUNCTION
; --------------------------------
VAL JSR GETSTR ; GET POINTER TO STRING IN INDEX
BNE L_VAL_1 ; LENGTH NON-ZERO
JMP ZERO_FAC ; RETURN 0 IF LENGTH=0
L_VAL_1 LDX TXTPTR ; SAVE CURRENT TXTPTR
LDY TXTPTR+1 ;
STX STRNG2 ;
STY STRNG2+1 ;
LDX INDEX ;
STX TXTPTR ; POINT TXTPTR TO START OF STRING
CLC ;
ADC INDEX ; ADD LENGTH
STA DEST ; POINT DEST TO END OF STRING + 1
LDX INDEX+1 ;
STX TXTPTR+1 ;
BCC L_VAL_2 ;
INX ;
L_VAL_2 STX DEST+1 ;
LDY #0 ; SAVE BYTE THAT FOLLOWS STRING
LDA (DEST),Y ; ON STACK
PHA ;
LDA #0 ; AND STORE $00 IN ITS PLACE
STA (DEST),Y ;
; <<< THAT CAUSES A BUG IF HIMEM = $BFFF, >>>
; <<< BECAUSE STORING $00 AT $C000 IS NO >>>
; <<< USE; $C000 WILL ALWAYS BE LAST CHAR >>>
; <<< TYPED, SO FIN WON'T TERMINATE UNTIL >>>
; <<< IT SEES A ZERO AT $C010! >>>
JSR CHRGOT ; PRIME THE PUMP
JSR FIN ; EVALUATE STRING
PLA ; GET BYTE THAT SHOULD FOLLOW STRING
LDY #0 ; AND PUT IT BACK
STA (DEST),Y ;
; RESTORE TXTPTR
; --------------------------------
; COPY STRNG2 INTO TXTPTR
; --------------------------------
POINT LDX STRNG2 ;
LDY STRNG2+1 ;
STX TXTPTR ;
STY TXTPTR+1 ;
RTS ;
; --------------------------------
; EVALUATE "EXP1,EXP2"
;
; CONVERT EXP1 TO 16-BIT NUMBER IN LINNUM
; CONVERT EXP2 TO 8-BIT NUMBER IN X-REG
; --------------------------------
GTNUM JSR FRMNUM ;
JSR GETADR ;
; --------------------------------
; EVALUATE ",EXPRESSION"
; CONVERT EXPRESSION TO SINGLE BYTE IN X-REG
; --------------------------------
COMBYTE ;
JSR CHKCOM ; MUST HAVE COMMA FIRST
JMP GETBYT ; CONVERT EXPRESSION TO BYTE IN X-REG
; --------------------------------
; CONVERT (FAC) TO A 16-BIT VALUE IN LINNUM
; --------------------------------
GETADR LDA FAC ; FAC < 2^16?
CMP #$91 ;
BCS GOIQ ; NO, ILLEGAL QUANTITY
JSR QINT ; CONVERT TO INTEGER
LDA FAC+3 ; COPY IT INTO LINNUM
LDY FAC+4 ;
STY LINNUM ; TO LINNUM
STA LINNUM+1 ;
RTS ;
; --------------------------------
; "PEEK" FUNCTION
; --------------------------------
PEEK LDA LINNUM ; SAVE (LINNUM) ON STACK DURING PEEK
PHA ;
LDA LINNUM+1 ;
PHA ;
JSR GETADR ; GET ADDRESS PEEKING AT
LDY #0
LDA (LINNUM),Y ; TAKE A QUICK LOOK
TAY ; VALUE IN Y-REG
PLA ; RESTORE LINNUM FROM STACK
STA LINNUM+1 ;
PLA ;
STA LINNUM ;
JMP SNGFLT ; FLOAT Y-REG INTO FAC
; --------------------------------
; "POKE" STATEMENT
; --------------------------------
POKE JSR GTNUM ; GET THE ADDRESS AND VALUE
TXA ; VALUE IN A,
LDY #0 ;
STA (LINNUM),Y ; STORE IT AWAY,
RTS ; AND THAT'S ALL FOR TODAY
; --------------------------------
; "WAIT" STATEMENT
; --------------------------------
WAIT JSR GTNUM ; GET ADDRESS IN LINNUM, MASK IN X
STX FORPNT ; SAVE MASK
LDX #0 ;
JSR CHRGOT ; ANOTHER PARAMETER?
BEQ L_WAIT_1 ; NO, USE $00 FOR EXCLUSIVE-OR
JSR COMBYTE ; GET XOR-MASK
L_WAIT_1 STX FORPNT+1 ; SAVE XOR-MASK HERE
LDY #0
L_WAIT_2 LDA (LINNUM),Y ; GET BYTE AT ADDRESS
EOR FORPNT+1 ; INVERT SPECIFIED BITS
AND FORPNT ; SELECT SPECIFIED BITS
BEQ L_WAIT_2 ; LOOP TILL NOT 0
RTS_10 RTS
; --------------------------------
; ADD 0L_RTS_10_5 TO FAC
; --------------------------------
FADDH LDA #<CON_HALF ; FAC+1/2 -> FAC
LDY #>CON_HALF
JMP FADD
; --------------------------------
; FAC = (Y,A) - FAC
; --------------------------------
FSUB JSR LOAD_ARG_FROM_YA
; --------------------------------
; FAC = ARG - FAC
; --------------------------------
FSUBT LDA FAC_SIGN ; COMPLEMENT FAC AND ADD
EOR #$FF ;
STA FAC_SIGN ;
EOR ARG_SIGN ; FIX SGNCPR TOO
STA SGNCPR ;
LDA FAC ; MAKE STATUS SHOW FAC EXPONENT
JMP FADDT ; JOIN FADD
; --------------------------------
; SHIFT SMALLER ARGUMENT MORE THAN 7 BITS
; --------------------------------
FADD_1 JSR SHIFT_RIGHT ; ALIGN RADIX BY SHIFTING
BCC FADD_3 ; ...ALWAYS
; --------------------------------
; FAC = (Y,A) + FAC
; --------------------------------
FADD JSR LOAD_ARG_FROM_YA
; --------------------------------
; FAC = ARG + FAC
; --------------------------------
FADDT BNE L_FADDT_1 ; FAC IS NON-ZERO
JMP COPY_ARG_TO_FAC ; FAC = 0 + ARG
L_FADDT_1 LDX FAC_EXTENSION
STX ARG_EXTENSION
LDX #ARG ; SET UP TO SHIFT ARG
LDA ARG ; EXPONENT
; --------------------------------
FADD_2 TAY
BEQ RTS_10 ; IF ARG=0, WE ARE FINISHED
SEC ;
SBC FAC ; GET DIFFNCE OF EXP
BEQ FADD_3 ; GO ADD IF SAME EXP
BCC L_FADD_2_1 ; ARG HAS SMALLER EXPONENT
STY FAC ; EXP HAS SMALLER EXPONENT
LDY ARG_SIGN ;
STY FAC_SIGN ;
EOR #$FF ; COMPLEMENT SHIFT COUNT
ADC #0 ; CARRY WAS SET
LDY #0
STY ARG_EXTENSION
LDX #FAC ; SET UP TO SHIFT FAC
BNE L_FADD_2_2 ; ...ALWAYS
L_FADD_2_1 LDY #0
STY FAC_EXTENSION
L_FADD_2_2 CMP #$F9 ; SHIFT MORE THAN 7 BITS?
BMI FADD_1 ; YES
TAY ; INDEX TO # OF SHIFTS
LDA FAC_EXTENSION
LSR 1,X ; START SHIFTING...
JSR SHIFT_RIGHT_4 ; ...COMPLETE SHIFTING
FADD_3 BIT SGNCPR ; DO FAC AND ARG HAVE SAME SIGNS?
BPL FADD_4 ; YES, ADD THE MANTISSAS
LDY #FAC ; NO, SUBTRACT SMALLER FROM LARGER
CPX #ARG ; WHICH WAS ADJUSTED?
BEQ L_FADD_3_1 ; IF ARG, DO FAC-ARG
LDY #ARG ; IF FAC, DO ARG-FAC
L_FADD_3_1 SEC ; SUBTRACT SMALLER FROM LARGER (WE HOPE)
EOR #$FF ; (IF EXPONENTS WERE EQUAL, WE MIGHT BE
ADC ARG_EXTENSION ; SUBTRACTING LARGER FROM SMALLER)
STA FAC_EXTENSION
LDA 4,Y
SBC 4,X
STA FAC+4
LDA 3,Y
SBC 3,X
STA FAC+3
LDA 2,Y
SBC 2,X
STA FAC+2
LDA 1,Y
SBC 1,X
STA FAC+1
; --------------------------------
; NORMALIZE VALUE IN FAC
; --------------------------------
NORMALIZE_FAC_1
BCS NORMALIZE_FAC_2
JSR COMPLEMENT_FAC
; --------------------------------
NORMALIZE_FAC_2
LDY #0 ; SHIFT UP SIGNIF DIGIT
TYA ; START A=0, COUNT SHIFTS IN A-REG
CLC
L_NORMALIZE_FAC_2_1 LDX FAC+1 ; LOOK AT MOST SIGNIFICANT BYTE
BNE NORMALIZE_FAC_4 ; SOME 1-BITS HERE
LDX FAC+2 ; HI-BYTE OF MANTISSA STILL ZERO,
STX FAC+1 ; SO DO A FAST 8-BIT SHUFFLE
LDX FAC+3
STX FAC+2
LDX FAC+4
STX FAC+3
LDX FAC_EXTENSION
STX FAC+4
STY FAC_EXTENSION ; ZERO EXTENSION BYTE
ADC #8 ; BUMP SHIFT COUNT
CMP #32 ; DONE 4 TIMES YET?
BNE L_NORMALIZE_FAC_2_1 ; NO, STILL MIGHT BE SOME 1'S
; YES, VALUE OF FAC IS ZERO
; --------------------------------
; SET FAC = 0
; (ONLY NECESSARY TO ZERO EXPONENT AND SIGN CELLS)
; --------------------------------
ZERO_FAC
LDA #0
; --------------------------------
STA_IN_FAC_SIGN_AND_EXP
STA FAC
; --------------------------------
STA_IN_FAC_SIGN
STA FAC_SIGN
RTS
; --------------------------------
; ADD MANTISSAS OF FAC AND ARG INTO FAC
; --------------------------------
FADD_4 ADC ARG_EXTENSION
STA FAC_EXTENSION
LDA FAC+4
ADC ARG+4
STA FAC+4
LDA FAC+3
ADC ARG+3
STA FAC+3
LDA FAC+2
ADC ARG+2
STA FAC+2
LDA FAC+1
ADC ARG+1
STA FAC+1
JMP NORMALIZE_FAC_5
; --------------------------------
; FINISH NORMALIZING FAC
; --------------------------------
NORMALIZE_FAC_3
ADC #1 ; COUNT BITS SHIFTED
ASL FAC_EXTENSION
ROL FAC+4
ROL FAC+3
ROL FAC+2
ROL FAC+1
; --------------------------------
NORMALIZE_FAC_4
BPL NORMALIZE_FAC_3 ; UNTIL TOP BIT = 1
SEC
SBC FAC ; ADJUST EXPONENT BY BITS SHIFTED
BCS ZERO_FAC ; UNDERFLOW, RETURN ZERO
EOR #$FF ;
ADC #1 ; 2'S COMPLEMENT
STA FAC ; CARRY=0 NOW
; --------------------------------
NORMALIZE_FAC_5 ;
BCC RTS_11 ; UNLESS MANTISSA CARRIED
; --------------------------------
NORMALIZE_FAC_6 ;
INC FAC ; MANTISSA CARRIED, SO SHIFT RIGHT
BEQ OVERFLOW ; OVERFLOW IF EXPONENT TOO BIG
ROR FAC+1
ROR FAC+2
ROR FAC+3
ROR FAC+4
ROR FAC_EXTENSION
RTS_11 RTS
; --------------------------------
; 2'S COMPLEMENT OF FAC
; --------------------------------
COMPLEMENT_FAC
LDA FAC_SIGN
EOR #$FF
STA FAC_SIGN
; --------------------------------
; 2'S COMPLEMENT OF FAC MANTISSA ONLY
; --------------------------------
COMPLEMENT_FAC_MANTISSA
LDA FAC+1
EOR #$FF
STA FAC+1
LDA FAC+2
EOR #$FF
STA FAC+2
LDA FAC+3
EOR #$FF
STA FAC+3
LDA FAC+4
EOR #$FF
STA FAC+4
LDA FAC_EXTENSION
EOR #$FF
STA FAC_EXTENSION
INC FAC_EXTENSION ; START INCREMENTING MANTISSA
BNE RTS_12
; --------------------------------
; INCREMENT FAC MANTISSA
; --------------------------------
INCREMENT_FAC_MANTISSA
INC FAC+4 ; ADD CARRY FROM EXTRA
BNE RTS_12
INC FAC+3
BNE RTS_12
INC FAC+2
BNE RTS_12
INC FAC+1
RTS_12 RTS
; --------------------------------
OVERFLOW
LDX #ERR_OVERFLOW
JMP ERROR
; --------------------------------
; SHIFT 1,X THRU 5,X RIGHT
; (A) = NEGATIVE OF SHIFT COUNT
; (X) = POINTER TO BYTES TO BE SHIFTED
;
; RETURN WITH (Y)=0, CARRY=0, EXTENSION BITS IN A-REG
; --------------------------------
SHIFT_RIGHT_1
LDX #RESULT-1 ; SHIFT RESULT RIGHT
SHIFT_RIGHT_2 ;
LDY 4,X ; SHIFT 8 BITS RIGHT
STY FAC_EXTENSION ;
LDY 3,X ;
STY 4,X ;
LDY 2,X ;
STY 3,X ;
LDY 1,X ;
STY 2,X ;
LDY SHIFT_SIGN_EXT ; $00 IF +, $FF IF -
STY 1,X
; --------------------------------
; MAIN ENTRY TO RIGHT SHIFT SUBROUTINE
; --------------------------------
SHIFT_RIGHT
ADC #8
BMI SHIFT_RIGHT_2 ; STILL MORE THAN 8 BITS TO GO
BEQ SHIFT_RIGHT_2 ; EXACTLY 8 MORE BITS TO GO
SBC #8 ; UNDO ADC ABOVE
TAY ; REMAINING SHIFT COUNT
LDA FAC_EXTENSION ;
BCS SHIFT_RIGHT_5 ; FINISHED SHIFTING
SHIFT_RIGHT_3 ;
L ASL 1,X ; SIGN -> CARRY (SIGN EXTENSION)
BCC L_L_1 ; SIGN +
INC 1,X ; PUT SIGN IN LSB
L_L_1 ROR 1,X ; RESTORE VALUE, SIGN STILL IN CARRY
ROR 1,X ; START RIGHT SHIFT, INSERTING SIGN
; --------------------------------
; ENTER HERE FOR SHORT SHIFTS WITH NO SIGN EXTENSION
; --------------------------------
SHIFT_RIGHT_4
ROR 2,X
ROR 3,X
ROR 4,X
ROR ; EXTENSION
INY ; COUNT THE SHIFT
BNE SHIFT_RIGHT_3 ;
SHIFT_RIGHT_5 ;
CLC ; RETURN WITH CARRY CLEAR
RTS
; --------------------------------
; --------------------------------
CON_ONE ASM_DATA($81,$00,$00,$00,$00)
; --------------------------------
POLY_LOG ASM_DATA(3) ; # OF COEFFICIENTS - 1
ASM_DATA($7F,$5E,$56,$CB,$79) ; * X^7 +
ASM_DATA($80,$13,$9B,$0B,$64) ; * X^5 +
ASM_DATA($80,$76,$38,$93,$16) ; * X^3 +
ASM_DATA($82,$38,$AA,$3B,$20) ; * X
; --------------------------------
CON_SQR_HALF ASM_DATA($80,$35,$04,$F3,$34)
CON_SQR_TWO ASM_DATA($81,$35,$04,$F3,$34)
CON_NEG_HALF ASM_DATA($80,$80,$00,$00,$00)
CON_LOG_TWO ASM_DATA($80,$31,$72,$17,$F8)
; --------------------------------
; "LOG" FUNCTION
; --------------------------------
LOG JSR SIGN ; GET -1,0,+1 IN A-REG FOR FAC
BEQ GIQ ; LOG (0) IS ILLEGAL
BPL LOG_2 ; >0 IS OK
GIQ JMP IQERR ; <= 0 IS NO GOOD
LOG_2 LDA FAC ; FIRST GET LOG BASE 2
SBC #$7F ; SAVE UNBIASED EXPONENT
PHA ;
LDA #$80 ; NORMALIZE BETWEEN L_LOG_2_5 AND 1
STA FAC
LDA #<CON_SQR_HALF
LDY #>CON_SQR_HALF
JSR FADD ; COMPUTE VIA SERIES OF ODD
LDA #<CON_SQR_TWO ; POWERS OF
LDY #>CON_SQR_TWO ; (SQR(2)X-1)/(SQR(2)X+1)
JSR FDIV
LDA #<CON_ONE
LDY #>CON_ONE
JSR FSUB
LDA #<POLY_LOG
LDY #>POLY_LOG
JSR POLYNOMIAL_ODD
LDA #<CON_NEG_HALF
LDY #>CON_NEG_HALF
JSR FADD
PLA
JSR ADDACC ; ADD ORIGINAL EXPONENT
LDA #<CON_LOG_TWO ; MULTIPLY BY LOG(2) TO FORM
LDY #>CON_LOG_TWO ; NATURAL LOG OF X
; --------------------------------
; FAC = (Y,A) * FAC
; --------------------------------
FMULT JSR LOAD_ARG_FROM_YA
; --------------------------------
; FAC = ARG * FAC
; --------------------------------
FMULTT BNE L_FMULTT_1 ; FAC .NE. ZERO
JMP RTS_13 ; FAC = 0 * ARG = 0
; <<< WHY IS LINE ABOVE JUST "RTS"? >>>
; --------------------------------
;
; --------------------------------
L_FMULTT_1 JSR ADD_EXPONENTS
LDA #0
STA RESULT ; INIT PRODUCT = 0
STA RESULT+1
STA RESULT+2
STA RESULT+3
LDA FAC_EXTENSION
JSR MULTIPLY_1
LDA FAC+4
JSR MULTIPLY_1
LDA FAC+3
JSR MULTIPLY_1
LDA FAC+2
JSR MULTIPLY_1
LDA FAC+1
JSR MULTIPLY_2
JMP COPY_RESULT_INTO_FAC
; --------------------------------
; MULTIPLY ARG BY (A) INTO RESULT
; --------------------------------
MULTIPLY_1
BNE MULTIPLY_2 ; THIS BYTE NON-ZERO
JMP SHIFT_RIGHT_1 ; (A)=0, JUST SHIFT ARG RIGHT 8
; --------------------------------
MULTIPLY_2 ;
LSR ; SHIFT BIT INTO CARRY
ORA #$80 ; SUPPLY SENTINEL BIT
L_MULTIPLY_2_1 TAY ; REMAINING MULTIPLIER TO Y
BCC L_MULTIPLY_2_2 ; THIS MULTIPLIER BIT = 0
CLC ; = 1, SO ADD ARG TO RESULT
LDA RESULT+3
ADC ARG+4
STA RESULT+3
LDA RESULT+2
ADC ARG+3
STA RESULT+2
LDA RESULT+1
ADC ARG+2
STA RESULT+1
LDA RESULT
ADC ARG+1
STA RESULT
L_MULTIPLY_2_2 ROR RESULT ; SHIFT RESULT RIGHT 1
ROR RESULT+1 ;
ROR RESULT+2 ;
ROR RESULT+3 ;
ROR FAC_EXTENSION ;
TYA ; REMAINING MULTIPLIER
LSR ; LSB INTO CARRY
BNE L_MULTIPLY_2_1 ; IF SENTINEL STILL HERE, MULTIPLY
RTS_13 RTS ; 8 X 32 COMPLETED
; --------------------------------
; UNPACK NUMBER AT (Y,A) INTO ARG
; --------------------------------
LOAD_ARG_FROM_YA
STA INDEX ; USE INDEX FOR PNTR
STY INDEX+1 ;
LDY #4 ; FIVE BYTES TO MOVE
LDA (INDEX),Y ;
STA ARG+4 ;
DEY ;
LDA (INDEX),Y ;
STA ARG+3 ;
DEY ;
LDA (INDEX),Y ;
STA ARG+2 ;
DEY ;
LDA (INDEX),Y ;
STA ARG_SIGN ;
EOR FAC_SIGN ; SET COMBINED SIGN FOR MULT/DIV
STA SGNCPR ;
LDA ARG_SIGN ; TURN ON NORMALIZED INVISIBLE BIT
ORA #$80 ; TO COMPLETE MANTISSA
STA ARG+1 ;
DEY ;
LDA (INDEX),Y ;
STA ARG ; EXPONENT
LDA FAC ; SET STATUS BITS ON FAC EXPONENT
RTS ;
; --------------------------------
; ADD EXPONENTS OF ARG AND FAC
; (CALLED BY FMULT AND FDIV)
;
; ALSO CHECK FOR OVERFLOW, AND SET RESULT SIGN
; --------------------------------
ADD_EXPONENTS
LDA ARG
; --------------------------------
ADD_EXPONENTS_1
BEQ ZERO ; IF ARG=0, RESULT IS ZERO
CLC ;
ADC FAC ;
BCC L_ADD_EXPONENTS_1_1 ; IN RANGE
BMI JOV ; OVERFLOW
CLC ;
ASM_DATA($2C) ; TRICK TO SKIP
L_ADD_EXPONENTS_1_1 BPL ZERO ; OVERFLOW
ADC #$80 ; RE-BIAS
STA FAC ; RESULT
BNE L_ADD_EXPONENTS_1_2
JMP STA_IN_FAC_SIGN ; RESULT IS ZERO
; <<< CRAZY TO JUMP WAY BACK THERE! >>>
; <<< SAME IDENTICAL CODE IS BELOW! >>>
; <<< INSTEAD OF BNE L_ADD_EXPONENTS_1_2, JMP STA.IN.FAC.SIGN >>>
; <<< ONLY NEEDED BEQ L_ADD_EXPONENTS_1_3 >>>
L_ADD_EXPONENTS_1_2 LDA SGNCPR ; SET SIGN OF RESULT
L_ADD_EXPONENTS_1_3 STA FAC_SIGN
RTS
; --------------------------------
; IF (FAC) IS POSITIVE, GIVE "OVERFLOW" ERROR
; IF (FAC) IS NEGATIVE, SET FAC=0, POP ONE RETURN, AND RTS
; CALLED FROM "EXP" FUNCTION
; --------------------------------
OUTOFRNG
LDA FAC_SIGN
EOR #$FF
BMI JOV ; ERROR IF POSITIVE #
; --------------------------------
; POP RETURN ADDRESS AND SET FAC=0
; --------------------------------
ZERO PLA
PLA
JMP ZERO_FAC
; --------------------------------
JOV JMP OVERFLOW
; --------------------------------
; MULTIPLY FAC BY 10
; --------------------------------
MUL10 JSR COPY_FAC_TO_ARG_ROUNDED
TAX ; TEXT FAC EXPONENT
BEQ L_MUL10_1 ; FINISHED IF FAC=0
CLC ;
ADC #2 ; ADD 2 TO EXPONENT GIVES (FAC)*4
BCS JOV ; OVERFLOW
LDX #0 ;
STX SGNCPR ;
JSR FADD_2 ; MAKES (FAC)*5
INC FAC ; *2, MAKES (FAC)*10
BEQ JOV ; OVERFLOW
L_MUL10_1 RTS
; --------------------------------
CON_TEN ASM_DATA($84,$20,$00,$00,$00)
; --------------------------------
; DIVIDE FAC BY 10
; --------------------------------
DIV10 JSR COPY_FAC_TO_ARG_ROUNDED
LDA #<CON_TEN ; SET UP TO PUT
LDY #>CON_TEN ; 10 IN FAC
LDX #0
; --------------------------------
; FAC = ARG / (Y,A)
; --------------------------------
DIV STX SGNCPR
JSR LOAD_FAC_FROM_YA
JMP FDIVT ; DIVIDE ARG BY FAC
; --------------------------------
; FAC = (Y,A) / FAC
; --------------------------------
FDIV JSR LOAD_ARG_FROM_YA
; --------------------------------
; FAC = ARG / FAC
; --------------------------------
FDIVT BEQ L_FDIVT_8 ; FAC = 0, DIVIDE BY ZERO ERROR
JSR ROUND_FAC ;
LDA #0 ; NEGATE FAC EXPONENT, SO
SEC ; ADD.EXPONENTS FORMS DIFFERENCE
SBC FAC
STA FAC
JSR ADD_EXPONENTS
INC FAC
BEQ JOV ; OVERFLOW
LDX #$FC ; INDEX FOR RESULT
LDA #1 ; SENTINEL
L_FDIVT_1 LDY ARG+1 ; SEE IF FAC CAN BE SUBTRACTED
CPY FAC+1
BNE L_FDIVT_2
LDY ARG+2
CPY FAC+2
BNE L_FDIVT_2
LDY ARG+3
CPY FAC+3
BNE L_FDIVT_2
LDY ARG+4
CPY FAC+4
L_FDIVT_2 PHP ; SAVE THE ANSWER, AND ALSO ROLL THE
ROL ; BIT INTO THE QUOTIENT, SENTINEL OUT
BCC L_FDIVT_3 ; NO SENTINEL, STILL NOT 8 TRIPS
INX ; 8 TRIPS, STORE BYTE OF QUOTIENT
STA RESULT+3,X
BEQ L_FDIVT_6 ; 32-BITS COMPLETED
BPL L_FDIVT_7 ; FINAL EXIT WHEN X=1
LDA #1 ; RE-START SENTINEL
L_FDIVT_3 PLP ; GET ANSWER, CAN FAC BE SUBTRACTED?
BCS L_FDIVT_5 ; YES, DO IT
L_FDIVT_4 ASL ARG+4 ; NO, SHIFT ARG LEFT
ROL ARG+3 ;
ROL ARG+2 ;
ROL ARG+1 ;
BCS L_FDIVT_2 ; ANOTHER TRIP
BMI L_FDIVT_1 ; HAVE TO COMPARE FIRST
BPL L_FDIVT_2 ; ...ALWAYS
L_FDIVT_5 TAY ; SAVE QUOTIENT/SENTINEL BYTE
LDA ARG+4 ; SUBTRACT FAC FROM ARG ONCE
SBC FAC+4 ;
STA ARG+4 ;
LDA ARG+3 ;
SBC FAC+3 ;
STA ARG+3 ;
LDA ARG+2 ;
SBC FAC+2 ;
STA ARG+2 ;
LDA ARG+1 ;
SBC FAC+1 ;
STA ARG+1 ;
TYA ; RESTORE QUOTIENT/SENTINEL BYTE
JMP L_FDIVT_4 ; GO TO SHIFT ARG AND CONTINUE
; --------------------------------
L_FDIVT_6 LDA #$40 ; DO A FEW EXTENSION BITS
BNE L_FDIVT_3 ; ...ALWAYS
; --------------------------------
L_FDIVT_7 ASL ; LEFT JUSTIFY THE EXTENSION BITS WE DID
ASL
ASL
ASL
ASL
ASL
STA FAC_EXTENSION
PLP
JMP COPY_RESULT_INTO_FAC
; --------------------------------
L_FDIVT_8 LDX #ERR_ZERODIV
JMP ERROR
; --------------------------------
; COPY RESULT INTO FAC MANTISSA, AND NORMALIZE
; --------------------------------
COPY_RESULT_INTO_FAC
LDA RESULT
STA FAC+1
LDA RESULT+1
STA FAC+2
LDA RESULT+2
STA FAC+3
LDA RESULT+3
STA FAC+4
JMP NORMALIZE_FAC_2
; --------------------------------
; UNPACK (Y,A) INTO FAC
; --------------------------------
LOAD_FAC_FROM_YA
STA INDEX ; USE INDEX FOR PNTR
STY INDEX+1 ;
LDY #4 ; PICK UP 5 BYTES
LDA (INDEX),Y ;
STA FAC+4 ;
DEY ;
LDA (INDEX),Y ;
STA FAC+3 ;
DEY ;
LDA (INDEX),Y ;
STA FAC+2 ;
DEY ;
LDA (INDEX),Y ;
STA FAC_SIGN ; FIRST BIT IS SIGN
ORA #$80 ; SET NORMALIZED INVISIBLE BIT
STA FAC+1 ;
DEY ;
LDA (INDEX),Y ;
STA FAC ; EXPONENT
STY FAC_EXTENSION ; Y=0
RTS
; --------------------------------
; ROUND FAC, STORE IN TEMP2
; --------------------------------
STORE_FAC_IN_TEMP2_ROUNDED
LDX #TEMP2 ; PACK FAC INTO TEMP2
ASM_DATA($2C) ; TRICK TO BRANCH
; --------------------------------
; ROUND FAC, STORE IN TEMP1
; --------------------------------
STORE_FAC_IN_TEMP1_ROUNDED
LDX #<TEMP1 ; PACK FAC INTO TEMP1
LDY #>TEMP1 ; HI-BYTE OF TEMP1 SAME AS TEMP2
BEQ STORE_FACDB_YX_ROUNDED ; ...ALWAYS
; --------------------------------
; ROUND FAC, AND STORE WHERE FORPNT POINTS
; --------------------------------
SETFOR LDX FORPNT
LDY FORPNT+1
; --------------------------------
; ROUND FAC, AND STORE AT (Y,X)
; --------------------------------
STORE_FACDB_YX_ROUNDED
JSR ROUND_FAC ; ROUND VALUE IN FAC USING EXTENSION
STX INDEX ; USE INDEX FOR PNTR
STY INDEX+1 ;
LDY #4 ; STORING 5 PACKED BYTES
LDA FAC+4 ;
STA (INDEX),Y ;
DEY ;
LDA FAC+3 ;
STA (INDEX),Y ;
DEY ;
LDA FAC+2 ;
STA (INDEX),Y ;
DEY ;
LDA FAC_SIGN ; PACK SIGN IN TOP BIT OF MANTISSA
ORA #$7F ;
AND FAC+1 ;
STA (INDEX),Y ;
DEY ;
LDA FAC ; EXPONENT
STA (INDEX),Y ;
STY FAC_EXTENSION ; ZERO THE EXTENSION
RTS
; --------------------------------
; COPY ARG INTO FAC
; --------------------------------
COPY_ARG_TO_FAC
LDA ARG_SIGN ; COPY SIGN
MFA STA FAC_SIGN ;
LDX #5 ; MOVE 5 BYTES
L_MFA_1 LDA ARG-1,X ;
STA FAC-1,X ;
DEX ;
BNE L_MFA_1 ;
STX FAC_EXTENSION ; ZERO EXTENSION
RTS ;
; --------------------------------
; ROUND FAC AND COPY TO ARG
; --------------------------------
COPY_FAC_TO_ARG_ROUNDED
JSR ROUND_FAC ; ROUND FAC USING EXTENSION
MAF LDX #6 ; COPY 6 BYTES, INCLUDES SIGN
L_MAF_1 LDA FAC-1,X ;
STA ARG-1,X ;
DEX ;
BNE L_MAF_1 ;
STX FAC_EXTENSION ; ZERO FAC EXTENSION
RTS_14 RTS ;
; --------------------------------
; ROUND FAC USING EXTENSION BYTE
; --------------------------------
ROUND_FAC
LDA FAC
BEQ RTS_14 ; FAC = 0, RETURN
ASL FAC_EXTENSION ; IS FAC.EXTENSION >= 128?
BCC RTS_14 ; NO, FINISHED
; --------------------------------
; INCREMENT MANTISSA AND RE-NORMALIZE IF CARRY
; --------------------------------
INCREMENT_MANTISSA
JSR INCREMENT_FAC_MANTISSA ; YES, INCREMENT FAC
BNE RTS_14 ; HIGH BYTE HAS BITS, FINISHED
JMP NORMALIZE_FAC_6 ; HI-BYTE=0, SO SHIFT LEFT
; --------------------------------
; TEST FAC FOR ZERO AND SIGN
;
; FAC > 0, RETURN +1
; FAC = 0, RETURN 0
; FAC < 0, RETURN -1
; --------------------------------
SIGN LDA FAC ; CHECK SIGN OF FAC AND
BEQ RTS_15 ; RETURN -1,0,1 IN A-REG
; --------------------------------
SIGN1 LDA FAC_SIGN ;
; --------------------------------
SIGN2 ROL ; MSBIT TO CARRY
LDA #$FF ; -1
BCS RTS_15 ; MSBIT = 1
LDA #1 ; +1
RTS_15 RTS ;
; --------------------------------
; "SGN" FUNCTION
; --------------------------------
SGN JSR SIGN ; CONVERT FAC TO -1,0,1
; --------------------------------
; CONVERT (A) INTO FAC, AS SIGNED VALUE -128 TO +127
; --------------------------------
FLOAT STA FAC+1 ; PUT IN HIGH BYTE OF MANTISSA
LDA #0 ; CLEAR 2ND BYTE OF MANTISSA
STA FAC+2 ;
LDX #$88 ; USE EXPONENT 2^9
; --------------------------------
; FLOAT UNSIGNED VALUE IN FAC+1,2
; (X) = EXPONENT
; --------------------------------
FLOAT_1 ;
LDA FAC+1 ; MSBIT=0, SET CARRY; =1, CLEAR CARRY
EOR #$FF ;
ROL ;
; --------------------------------
; FLOAT UNSIGNED VALUE IN FAC+1,2
; (X) = EXPONENT
; C=0 TO MAKE VALUE NEGATIVE
; C=1 TO MAKE VALUE POSITIVE
; --------------------------------
FLOAT_2 ;
LDA #0 ; CLEAR LOWER 16-BITS OF MANTISSA
STA FAC+4 ;
STA FAC+3 ;
STX FAC ; STORE EXPONENT
STA FAC_EXTENSION ; CLEAR EXTENSION
STA FAC_SIGN ; MAKE SIGN POSITIVE
JMP NORMALIZE_FAC_1 ; IF C=0, WILL NEGATE FAC
; --------------------------------
; "ABS" FUNCTION
; --------------------------------
ABS LSR FAC_SIGN ; CHANGE SIGN TO +
RTS
; --------------------------------
; COMPARE FAC WITH PACKED # AT (Y,A)
; RETURN A=1,0,-1 AS (Y,A) IS <,=,> FAC
; --------------------------------
FCOMP STA DEST ; USE DEST FOR PNTR
; --------------------------------
; SPECIAL ENTRY FROM "NEXT" PROCESSOR
; "DEST" ALREADY SET UP
; --------------------------------
FCOMP2 STY DEST+1 ;
LDY #0 ; GET EXPONENT OF COMPARAND
LDA (DEST),Y ;
INY ; POINT AT NEXT BYTE
TAX ; EXPONENT TO X-REG
BEQ SIGN ; IF COMPARAND=0, "SIGN" COMPARES FAC
LDA (DEST),Y ; GET HI-BYTE OF MANTISSA
EOR FAC_SIGN ; COMPARE WITH FAC SIGN
BMI SIGN1 ; DIFFERENT SIGNS, "SIGN" GIVES ANSWER
CPX FAC ; SAME SIGN, SO COMPARE EXPONENTS
BNE L_FCOMP2_1 ; DIFFERENT, SO SUFFICIENT TEST
LDA (DEST),Y ; SAME EXPONENT, COMPARE MANTISSA
ORA #$80 ; SET INVISIBLE NORMALIZED BIT
CMP FAC+1 ;
BNE L_FCOMP2_1 ; NOT SAME, SO SUFFICIENT
INY ; SAME, COMPARE MORE MANTISSA
LDA (DEST),Y ;
CMP FAC+2 ;
BNE L_FCOMP2_1 ; NOT SAME, SO SUFFICIENT
INY ; SAME, COMPARE MORE MANTISSA
LDA (DEST),Y ;
CMP FAC+3 ;
BNE L_FCOMP2_1 ; NOT SAME, SO SUFFICIENT
INY ; SAME, COMPARE REST OF MANTISSA
LDA #$7F ; ARTIFICIAL EXTENSION BYTE FOR COMPARAND
CMP FAC_EXTENSION
LDA (DEST),Y
SBC FAC+4
BEQ RTS_16 ; NUMBERS ARE EQUAL, RETURN (A)=0
L_FCOMP2_1 LDA FAC_SIGN ; NUMBERS ARE DIFFERENT
BCC L_FCOMP2_2 ; FAC IS LARGER MAGNITUDE
EOR #$FF ; FAC IS SMALLER MAGNITUDE
; <<< NOTE THAT ABOVE THREE LINES CAN BE SHORTENED: >>>
; <<< L_FCOMP2_1 ROR PUT CARRY INTO SIGN BIT >>>
; <<< EOR FAC.SIGN TOGGLE WITH SIGN OF FAC >>>
L_FCOMP2_2 JMP SIGN2 ; CONVERT +1 OR -1
; --------------------------------
; QUICK INTEGER FUNCTION
;
; CONVERTS FP VALUE IN FAC TO INTEGER VALUE
; IN FAC+1...FAC+4, BY SHIFTING RIGHT WITH SIGN
; EXTENSION UNTIL FRACTIONAL BITS ARE OUT.
;
; THIS SUBROUTINE ASSUMES THE EXPONENT < 32.
; --------------------------------
QINT LDA FAC ; LOOK AT FAC EXPONENT
BEQ QINT_3 ; FAC=0, SO FINISHED
SEC ; GET -(NUMBER OF FRACTIONAL BITS)
SBC #$A0 ; IN A-REG FOR SHIFT COUNT
BIT FAC_SIGN ; CHECK SIGN OF FAC
BPL L_QINT_1 ; POSITIVE, CONTINUE
TAX ; NEGATIVE, SO COMPLEMENT MANTISSA
LDA #$FF ; AND SET SIGN EXTENSION FOR SHIFT
STA SHIFT_SIGN_EXT
JSR COMPLEMENT_FAC_MANTISSA
TXA ; RESTORE BIT COUNT TO A-REG
L_QINT_1 LDX #FAC ; POINT SHIFT SUBROUTINE AT FAC
CMP #$F9 ; MORE THAN 7 BITS TO SHIFT?
BPL QINT_2 ; NO, SHORT SHIFT
JSR SHIFT_RIGHT ; YES, USE GENERAL ROUTINE
STY SHIFT_SIGN_EXT ; Y=0, CLEAR SIGN EXTENSION
RTS_16 RTS
; --------------------------------
QINT_2 TAY ; SAVE SHIFT COUNT
LDA FAC_SIGN ; GET SIGN BIT
AND #$80 ;
LSR FAC+1 ; START RIGHT SHIFT
ORA FAC+1 ; AND MERGE WITH SIGN
STA FAC+1
JSR SHIFT_RIGHT_4 ; JUMP INTO MIDDLE OF SHIFTER
STY SHIFT_SIGN_EXT ; Y=0, CLEAR SIGN EXTENSION
RTS
; --------------------------------
; "INT" FUNCTION
;
; USES QINT TO CONVERT (FAC) TO INTEGER FORM,
; AND THEN REFLOATS THE INTEGER.
; <<< A FASTER APPROACH WOULD SIMPLY CLEAR >>>
; <<< THE FRACTIONAL BITS BY ZEROING THEM >>>
; --------------------------------
INT LDA FAC ; CHECK IF EXPONENT < 32
CMP #$A0 ; BECAUSE IF > 31 THERE IS NO FRACTION
BCS RTS_17 ; NO FRACTION, WE ARE FINISHED
JSR QINT ; USE GENERAL INTEGER CONVERSION
STY FAC_EXTENSION ; Y=0, CLEAR EXTENSION
LDA FAC_SIGN ; GET SIGN OF VALUE
STY FAC_SIGN ; Y=0, CLEAR SIGN
EOR #$80 ; TOGGLE ACTUAL SIGN
ROL ; AND SAVE IN CARRY
LDA #$A0 ; SET EXPONENT TO 32
STA FAC ; BECAUSE 4-BYTE INTEGER NOW
LDA FAC+4 ; SAVE LOW 8-BITS OF INTEGER FORM
STA CHARAC ; FOR EXP AND POWER
JMP NORMALIZE_FAC_1 ; NORMALIZE TO FINISH CONVERSION
; --------------------------------
QINT_3 STA FAC+1 ; FAC=0, SO CLEAR ALL 4 BYTES FOR
STA FAC+2 ; INTEGER VERSION
STA FAC+3 ;
STA FAC+4 ;
TAY ; Y=0 TOO
RTS_17 RTS ;
; --------------------------------
; CONVERT STRING TO FP VALUE IN FAC
;
; STRING POINTED TO BY TXTPTR
; FIRST CHAR ALREADY SCANNED BY CHRGET
; (A) = FIRST CHAR, C=0 IF DIGIT.
; --------------------------------
FIN LDY #0 ; CLEAR WORKING AREA ($99...$A3)
LDX #10 ; TMPEXP, EXPON, DPFLG, EXPSGN, FAC, SERLEN
L_FIN_1 STY TMPEXP,X
DEX
BPL L_FIN_1
; --------------------------------
BCC FIN_2 ; FIRST CHAR IS A DIGIT
CMP #LOCHAR(`-') ; CHECK FOR LEADING SIGN
BNE L_FIN_2 ; NOT MINUS
STX SERLEN ; MINUS, SET SERLEN = $FF FOR FLAG
BEQ FIN_1 ; ...ALWAYS
L_FIN_2 CMP #LOCHAR(`+') ; MIGHT BE PLUS
BNE FIN_3 ; NOT PLUS EITHER, CHECK DECIMAL POINT
; --------------------------------
FIN_1 JSR CHRGET ; GET NEXT CHAR OF STRING
; --------------------------------
FIN_2 BCC FIN_9 ; INSERT THIS DIGIT
; --------------------------------
FIN_3 CMP #LOCHAR(`.') ; CHECK FOR DECIMAL POINT
BEQ FIN_10 ; YES
CMP #LOCHAR(`E') ; CHECK FOR EXPONENT PART
BNE FIN_7 ; NO, END OF NUMBER
JSR CHRGET ; YES, START CONVERTING EXPONENT
BCC FIN_5 ; EXPONENT DIGIT
CMP #TOKEN_MINUS ; NEGATIVE EXPONENT?
BEQ L_FIN_3_1 ; YES
CMP #LOCHAR(`-') ; MIGHT NOT BE TOKENIZED YET
BEQ L_FIN_3_1 ; YES, IT IS NEGATIVE
CMP #TOKEN_PLUS ; OPTIONAL "+"
BEQ FIN_4 ; YES
CMP #LOCHAR(`+') ; MIGHT NOT BE TOKENIZED YET
BEQ FIN_4 ; YES, FOUND "+"
BNE FIN_6 ; ...ALWAYS, NUMBER COMPLETED
L_FIN_3_1 ROR EXPSGN ; C=1, SET FLAG NEGATIVE
; --------------------------------
FIN_4 JSR CHRGET ; GET NEXT DIGIT OF EXPONENT
; --------------------------------
FIN_5 BCC GETEXP ; CHAR IS A DIGIT OF EXPONENT
; --------------------------------
FIN_6 BIT EXPSGN ; END OF NUMBER, CHECK EXP SIGN
BPL FIN_7 ; POSITIVE EXPONENT
LDA #0 ; NEGATIVE EXPONENT
SEC ; MAKE 2'S COMPLEMENT OF EXPONENT
SBC EXPON ;
JMP FIN_8 ;
; --------------------------------
; FOUND A DECIMAL POINT
; --------------------------------
FIN_10 ROR DPFLG ; C=1, SET DPFLG FOR DECIMAL POINT
BIT DPFLG ; CHECK IF PREVIOUS DEC. PT.
BVC FIN_1 ; NO PREVIOUS DECIMAL POINT
; A SECOND DECIMAL POINT IS TAKEN AS A TERMINATOR
; TO THE NUMERIC STRING.
; "A=11..22" WILL GIVE A SYNTAX ERROR, BECAUSE
; IT IS TWO NUMBERS WITH NO OPERATOR BETWEEN.
; "PRINT 11..22" GIVES NO ERROR, BECAUSE IT IS
; JUST THE CONCATENATION OF TWO NUMBERS.
; --------------------------------
; NUMBER TERMINATED, ADJUST EXPONENT NOW
; --------------------------------
FIN_7 LDA EXPON ; E-VALUE
FIN_8 SEC ; MODIFY WITH COUNT OF DIGITS
SBC TMPEXP ; AFTER THE DECIMAL POINT
STA EXPON ; COMPLETE CURRENT EXPONENT
BEQ L_FIN_8_15 ; NO ADJUST NEEDED IF EXP=0
BPL L_FIN_8_14 ; EXP>0, MULTIPLY BY TEN
L_FIN_8_13 JSR DIV10 ; EXP<0, DIVIDE BY TEN
INC EXPON ; UNTIL EXP=0
BNE L_FIN_8_13 ;
BEQ L_FIN_8_15 ; ...ALWAYS, WE ARE FINISHED
L_FIN_8_14 JSR MUL10 ; EXP>0, MULTIPLY BKY TEN
DEC EXPON ; UNTIL EXP=0
BNE L_FIN_8_14 ;
L_FIN_8_15 LDA SERLEN ; IS WHOLE NUMBER NEGATIVE?
BMI L_FIN_8_16 ; YES
RTS ; NO, RETURN, WHOLE JOB DONE!
L_FIN_8_16 JMP NEGOP ; NEGATIVE NUMBER, SO NEGATE FAC
; --------------------------------
; ACCUMULATE A DIGIT INTO FAC
; --------------------------------
FIN_9 PHA ; SAVE DIGIT
BIT DPFLG ; SEEN A DECIMAL POINT YET?
BPL L_FIN_9_1 ; NO, STILL IN INTEGER PART
INC TMPEXP ; YES, COUNT THE FRACTIONAL DIGIT
L_FIN_9_1 JSR MUL10 ; FAC = FAC * 10
PLA ; CURRENT DIGIT
SEC ; <<<SHORTER HERE TO JUST "AND #$0F">>>
SBC #LOCHAR(`0') ; <<<TO CONVERT ASCII TO BINARY FORM>>>
JSR ADDACC ; ADD THE DIGIT
JMP FIN_1 ; GO BACK FOR MORE
; --------------------------------
; ADD (A) TO FAC
; --------------------------------
ADDACC PHA ; SAVE ADDEND
JSR COPY_FAC_TO_ARG_ROUNDED
PLA ; GET ADDEND AGAIN
JSR FLOAT ; CONVERT TO FP VALUE IN FAC
LDA ARG_SIGN ;
EOR FAC_SIGN ;
STA SGNCPR ;
LDX FAC ; TO SIGNAL IF FAC=0
JMP FADDT ; PERFORM THE ADDITION
; --------------------------------
; ACCUMULATE DIGIT OF EXPONENT
; --------------------------------
GETEXP LDA EXPON ; CHECK CURRENT VALUE
CMP #10 ; FOR MORE THAN 2 DIGITS
BCC L_GETEXP_1 ; NO, THIS IS 1ST OR 2ND DIGIT
LDA #100 ; EXPONENT TOO BIG
BIT EXPSGN ; UNLESS IT IS NEGATIVE
BMI L_GETEXP_2 ; LARGE NEGATIVE EXPONENT MAKES FAC=0
JMP OVERFLOW ; LARGE POSITIVE EXPONENT IS ERROR
L_GETEXP_1 ASL ; EXPONENT TIMES 10
ASL ;
CLC ;
ADC EXPON ;
ASL ;
CLC ; <<< ASL ALREADY DID THIS! >>>
LDY #0 ; ADD THE NEW DIGIT
ADC (TXTPTR),Y ; BUT THIS IS IN ASCII,
SEC ; SO ADJUST BACK TO BINARY
SBC #LOCHAR(`0')
L_GETEXP_2 STA EXPON ; NEW VALUE
JMP FIN_4 ; BACK FOR MORE
; --------------------------------
; --------------------------------
CON_99999999P9 ASM_DATA($9B,$3E,$BC,$1F,$FD) ; 99,999,999.9
CON_999999999 ASM_DATA($9E,$6E,$6B,$27,$FD) ; 999,999,999
CON_BILLION ASM_DATA($9E,$6E,$6B,$28,$00) ; 1,000,000,000
; --------------------------------
; PRINT "IN <LINE #>"
; --------------------------------
INPRT LDA #<QT_IN ; PRINT " IN "
LDY #>QT_IN
JSR GO_STROUT
LDA CURLIN+1
LDX CURLIN
; --------------------------------
; PRINT A,X AS DECIMAL INTEGER
; --------------------------------
LINPRT STA FAC+1 ; PRINT A,X IN DECIMAL
STX FAC+2 ;
LDX #$90 ; EXPONENT = 2^16
SEC ; CONVERT UNSIGNED
JSR FLOAT_2 ; CONVERT LINE # TO FP
; --------------------------------
; CONVERT (FAC) TO STRING, AND PRINT IT
; --------------------------------
PRINT_FAC ;
JSR FOUT ; CONVERT (FAC) TO STRING AT STACK
; --------------------------------
; PRINT STRING STARTING AT Y,A
; --------------------------------
GO_STROUT ;
JMP STROUT ; PRINT STRING AT A,Y
; --------------------------------
; CONVERT (FAC) TO STRING STARTING AT STACK
; RETURN WITH (Y,A) POINTING AT STRING
; --------------------------------
FOUT LDY #1 ; NORMAL ENTRY PUTS STRING AT STACK...
; --------------------------------
; "STR$" FUNCTION ENTERS HERE, WITH (Y)=0
; SO THAT RESULT STRING STARTS AT STACK-1
; (THIS IS USED AS A FLAG)
; --------------------------------
FOUT_1 LDA #LOCHAR(`-') ; IN CASE VALUE NEGATIVE
DEY ; BACK UP PNTR
BIT FAC_SIGN ;
BPL L_FOUT_1_1 ; VALUE IS +
INY ; VALUE IS -
STA STACK-1,Y ; EMIT "-"
L_FOUT_1_1 STA FAC_SIGN ; MAKE FAC.SIGN POSITIVE ($2D)
STY STRNG2 ; SAVE STRING PNTR
INY ;
LDA #LOCHAR(`0') ; IN CASE (FAC)=0
LDX FAC ; NUMBER=0?
BNE L_FOUT_1_2 ; NO, (FAC) NOT ZERO
JMP FOUT_4 ; YES, FINISHED
; --------------------------------
L_FOUT_1_2 LDA #0 ; STARTING VALUE FOR TMPEXP
CPX #$80 ; ANY INTEGER PART?
BEQ L_FOUT_1_3 ; NO, BTWN L_FOUT_1_5 AND L_FOUT_1_999999999
BCS L_FOUT_1_4 ; YES
; --------------------------------
L_FOUT_1_3 LDA #<CON_BILLION ; MULTIPLY BY 1E9
LDY #>CON_BILLION ; TO GIVE ADJUSTMENT A HEAD START
JSR FMULT ;
LDA #$100-9 ; EXPONENT ADJUSTMENT
L_FOUT_1_4 STA TMPEXP ; 0 OR -9
; --------------------------------
; ADJUST UNTIL 1E8 <= (FAC) <1E9
; --------------------------------
L_FOUT_1_5 LDA #<CON_999999999
LDY #>CON_999999999
JSR FCOMP ; COMPARE TO 1E9-1
BEQ L_FOUT_1_10 ; (FAC) = 1E9-1
BPL L_FOUT_1_8 ; TOO LARGE, DIVIDE BY TEN
L_FOUT_1_6 LDA #<CON_99999999P9 ; COMPARE TO 1E8-L_FOUT_1_1
LDY #>CON_99999999P9
JSR FCOMP ; COMPARE TO 1E8-L_FOUT_1_1
BEQ L_FOUT_1_7 ; (FAC) = 1E8-L_FOUT_1_1
BPL L_FOUT_1_9 ; IN RANGE, ADJUSTMENT FINISHED
L_FOUT_1_7 JSR MUL10 ; TOO SMALL, MULTIPLY BY TEN
DEC TMPEXP ; KEEP TRACK OF MULTIPLIES
BNE L_FOUT_1_6 ; ...ALWAYS
L_FOUT_1_8 JSR DIV10 ; TOO LARGE, DIVIDE BY TEN
INC TMPEXP ; KEEP TRACK OF DIVISIONS
BNE L_FOUT_1_5 ; ...ALWAYS
; --------------------------------
L_FOUT_1_9 JSR FADDH ; ROUND ADJUSTED RESULT
L_FOUT_1_10 JSR QINT ; CONVERT ADJUSTED VALUE TO 32-BIT INTEGER
; --------------------------------
; FAC+1...FAC+4 IS NOW IN INTEGER FORM
; WITH POWER OF TEN ADJUSTMENT IN TMPEXP
;
; IF -10 < TMPEXP > 1, PRINT IN DECIMAL FORM
; OTHERWISE, PRINT IN EXPONENTIAL FORM
; --------------------------------
FOUT_2 LDX #1 ; ASSUME 1 DIGIT BEFORE "."
LDA TMPEXP ; CHECK RANGE
CLC ;
ADC #10 ;
BMI L_FOUT_2_1 ; < .01, USE EXPONENTIAL FORM
CMP #11 ;
BCS L_FOUT_2_2 ; >= 1E10, USE EXPONENTIAL FORM
ADC #$FF ; LESS 1 GIVES INDEX FOR "."
TAX ;
LDA #2 ; SET REMAINING EXPONENT = 0
L_FOUT_2_1 SEC ; COMPUTE REMAINING EXPONENT
L_FOUT_2_2 SBC #2 ;
STA EXPON ; VALUE FOR "E+XX" OR "E-XX"
STX TMPEXP ; INDEX FOR DECIMAL POINT
TXA ; SEE IF "." COMES FIRST
BEQ L_FOUT_2_3 ; YES
BPL L_FOUT_2_5 ; NO, LATER
L_FOUT_2_3 LDY STRNG2 ; GET INDEX INTO STRING BEING BUILT
LDA #LOCHAR(`.') ; STORE A DECIMAL POINT
INY ;
STA STACK-1,Y ;
TXA ; SEE IF NEED ".0"
BEQ L_FOUT_2_4 ; NO
LDA #LOCHAR(`0') ; YES, STORE "0"
INY ;
STA STACK-1,Y ;
L_FOUT_2_4 STY STRNG2 ; SAVE OUTPUT INDEX AGAIN
; --------------------------------
; NOW DIVIDE BY POWERS OF TEN TO GET SUCCESSIVE DIGITS
; --------------------------------
L_FOUT_2_5 LDY #0 ; INDEX TO TABLE OF POWERS OF TEN
LDX #$80 ; STARTING VALUE FOR DIGIT WITH DIRECTION
L_FOUT_2_6 LDA FAC+4 ; START BY ADDING -100000000 UNTIL
CLC ; OVERSHOOT. THEN ADD +10000000,
ADC DECTBL+3,Y ; THEN ADD -1000000, THEN ADD
STA FAC+4 ; +100000, AND SO ON.
LDA FAC+3 ; THE # OF TIMES EACH POWER IS ADDED
ADC DECTBL+2,Y ; IS 1 MORE THAN CORRESPONDING DIGIT
STA FAC+3
LDA FAC+2
ADC DECTBL+1,Y
STA FAC+2
LDA FAC+1
ADC DECTBL,Y
STA FAC+1
INX ; COUNT THE ADD
BCS L_FOUT_2_7 ; IF C=1 AND X NEGATIVE, KEEP ADDING
BPL L_FOUT_2_6 ; IF C=0 AND X POSITIVE, KEEP ADDING
BMI L_FOUT_2_8 ; IF C=0 AND X NEGATIVE, WE OVERSHOT
L_FOUT_2_7 BMI L_FOUT_2_6 ; IF C=1 AND X POSITIVE, WE OVERSHOT
L_FOUT_2_8 TXA ; OVERSHOT, SO MAKE X INTO A DIGIT
BCC L_FOUT_2_9 ; HOW DEPENDS ON DIRECTION WE WERE GOING
EOR #$FF ; DIGIT = 9-X
ADC #10 ;
L_FOUT_2_9 ADC #LOCHAR(`0')-1 ; MAKE DIGIT INTO ASCII
INY ; ADVANCE TO NEXT SMALLER POWER OF TEN
INY ;
INY ;
INY ;
STY VARPNT ; SAVE PNTR TO POWERS
LDY STRNG2 ; GET OUTPUT PNTR
INY ; STORE THE DIGIT
TAX ; SAVE DIGIT, HI-BIT IS DIRECTION
AND #$7F ; MAKE SURE $30...$39 FOR STRING
STA STACK-1,Y ;
DEC TMPEXP ; COUNT THE DIGIT
BNE L_FOUT_2_10 ; NOT TIME FOR "." YET
LDA #LOCHAR(`.') ; TIME, SO STORE THE DECIMAL POINT
INY ;
STA STACK-1,Y ;
L_FOUT_2_10 STY STRNG2 ; SAVE OUTPUT PNTR AGAIN
LDY VARPNT ; GET PNTR TO POWERS
TXA ; GET DIGIT WITH HI-BIT = DIRECTION
EOR #$FF ; CHANGE DIRECTION
AND #$80 ; $00 IF ADDING, $80 IF SUBTRACTING
TAX
CPY #DECTBL_END-DECTBL
BNE L_FOUT_2_6 ; NOT FINISHED YET
; --------------------------------
; NINE DIGITS HAVE BEEN STORED IN STRING. NOW LOOK
; BACK AND LOP OFF TRAILING ZEROES AND A TRAILING
; DECIMAL POINT.
; --------------------------------
FOUT_3 LDY STRNG2 ; POINTS AT LAST STORED CHAR
L_FOUT_3_1 LDA STACK-1,Y ; SEE IF LOPPABLE
DEY ;
CMP #LOCHAR(`0') ; SUPPRESS TRAILING ZEROES
BEQ L_FOUT_3_1 ; YES, KEEP LOOPING
CMP #LOCHAR(`.') ; SUPPRESS TRAILING DECIMAL POINT
BEQ L_FOUT_3_2 ; ".", SO WRITE OVER IT
INY ; NOT ".", SO INCLUDE IN STRING AGAIN
L_FOUT_3_2 LDA #LOCHAR(`+') ; PREPARE FOR POSITIVE EXPONENT "E+XX"
LDX EXPON ; SEE IF ANY E-VALUE
BEQ FOUT_5 ; NO, JUST MARK END OF STRING
BPL L_FOUT_3_3 ; YES, AND IT IS POSITIVE
LDA #0 ; YES, AND IT IS NEGATIVE
SEC ; COMPLEMENT THE VALUE
SBC EXPON ;
TAX ; GET MAGNITUDE IN X
LDA #LOCHAR(`-') ; E SIGN
L_FOUT_3_3 STA STACK+1,Y ; STORE SIGN IN STRING
LDA #LOCHAR(`E') ; STORE "E" IN STRING BEFORE SIGN
STA STACK,Y ;
TXA ; EXPONENT MAGNITUDE IN A-REG
LDX #LOCHAR(`0')-1 ; SEED FOR EXPONENT DIGIT
SEC ; CONVERT TO DECIMAL
L_FOUT_3_4 INX ; COUNT THE SUBTRACTION
SBC #10 ; TEN'S DIGIT
BCS L_FOUT_3_4 ; MORE TENS TO SUBTRACT
ADC #LOCHAR(`0')+10 ; CONVERT REMAINDER TO ONE'S DIGIT
STA STACK+3,Y ; STORE ONE'S DIGIT
TXA ;
STA STACK+2,Y ; STORE TEN'S DIGIT
LDA #0 ; MARK END OF STRING WITH $00
STA STACK+4,Y ;
BEQ FOUT_6 ; ...ALWAYS
FOUT_4 STA STACK-1,Y ; STORE "0" IN ASCII
FOUT_5 LDA #0 ; STORE $00 ON END OF STRING
STA STACK,Y ;
FOUT_6 LDA #<STACK ; POINT Y,A AT BEGINNING OF STRING
LDY #>STACK ; (STR$ STARTED STRING AT STACK-1, BUT
RTS ; STR$ DOESN'T USE Y,A ANYWAY.)
; --------------------------------
CON_HALF ASM_DATA($80,$00,$00,$00,$00) ; FP CONSTANT 0L_CON_HALF_5
; --------------------------------
; POWERS OF 10 FROM 1E8 DOWN TO 1,
; AS 32-BIT INTEGERS, WITH ALTERNATING SIGNS
; --------------------------------
DECTBL ASM_DATA($FA,$0A,$1F,$00) ; -100000000
ASM_DATA($00,$98,$96,$80) ; 10000000
ASM_DATA($FF,$F0,$BD,$C0) ; -1000000
ASM_DATA($00,$01,$86,$A0) ; 100000
ASM_DATA($FF,$FF,$D8,$F0) ; -10000
ASM_DATA($00,$00,$03,$E8) ; 1000
ASM_DATA($FF,$FF,$FF,$9C) ; -100
ASM_DATA($00,$00,$00,$0A) ; 10
ASM_DATA($FF,$FF,$FF,$FF) ; -1
DECTBL_END
; --------------------------------
; --------------------------------
; "SQR" FUNCTION
;
; <<< UNFORTUNATELY, RATHER THAN A NEWTON-RAPHSON >>>
; <<< ITERATION, APPLESOFT USES EXPONENTIATION >>>
; <<< SQR(X) = X^L_DECTBL_END_5 >>>
; --------------------------------
SQR JSR COPY_FAC_TO_ARG_ROUNDED
LDA #<CON_HALF ; SET UP POWER OF 0L_SQR_5
LDY #>CON_HALF
JSR LOAD_FAC_FROM_YA
; --------------------------------
; EXPONENTIATION OPERATION
;
; ARG ^ FAC = EXP( LOG(ARG) * FAC )
; --------------------------------
FPWRT BEQ EXP ; IF FAC=0, ARG^FAC=EXP(0)
LDA ARG ; IF ARG=0, ARG^FAC=0
BNE L_FPWRT_1 ; NEITHER IS ZERO
JMP STA_IN_FAC_SIGN_AND_EXP ; SET FAC = 0
L_FPWRT_1 LDX #TEMP3 ; SAVE FAC IN TEMP3
LDY #0
JSR STORE_FACDB_YX_ROUNDED
LDA ARG_SIGN ; NORMALLY, ARG MUST BE POSITIVE
BPL L_FPWRT_2 ; IT IS POSITIVE, SO ALL IS WELL
JSR INT ; NEGATIVE, BUT OK IF INTEGRAL POWER
LDA #TEMP3 ; SEE IF INT(FAC)=FAC
LDY #0 ;
JSR FCOMP ; IS IT AN INTEGER POWER?
BNE L_FPWRT_2 ; NOT INTEGRAL, WILL CAUSE ERROR LATER
TYA ; MAKE ARG SIGN + AS IT IS MOVED TO FAC
LDY CHARAC ; INTEGRAL, SO ALLOW NEGATIVE ARG
L_FPWRT_2 JSR MFA ; MOVE ARGUMENT TO FAC
TYA ; SAVE FLAG FOR NEGATIVE ARG (0=+)
PHA ;
JSR LOG ; GET LOG(ARG)
LDA #TEMP3 ; MULTIPLY BY POWER
LDY #0 ;
JSR FMULT ;
JSR EXP ; E ^ LOG(FAC)
PLA ; GET FLAG FOR NEGATIVE ARG
LSR ; <<<LSR,BCC COULD BE MERELY BPL>>>
BCC RTS_18 ; NOT NEGATIVE, FINISHED
; NEGATIVE ARG, SO NEGATE RESULT
; --------------------------------
; NEGATE VALUE IN FAC
; --------------------------------
NEGOP LDA FAC ; IF FAC=0, NO NEED TO COMPLEMENT
BEQ RTS_18 ; YES, FAC=0
LDA FAC_SIGN ; NO, SO TOGGLE SIGN
EOR #$FF
STA FAC_SIGN
RTS_18 RTS
; --------------------------------
CON_LOG_E ASM_DATA($81,$38,$AA,$3B,$29) ; LOG(E) TO BASE 2
; --------------------------------
POLY_EXP ASM_DATA(7) ; ( # OF TERMS IN POLYNOMIAL) - 1
ASM_DATA($71,$34,$58,$3E,$56) ; (LOG(2)^7)/8!
ASM_DATA($74,$16,$7E,$B3,$1B) ; (LOG(2)^6)/7!
ASM_DATA($77,$2F,$EE,$E3,$85) ; (LOG(2)^5)/6!
ASM_DATA($7A,$1D,$84,$1C,$2A) ; (LOG(2)^4)/5!
ASM_DATA($7C,$63,$59,$58,$0A) ; (LOG(2)^3)/4!
ASM_DATA($7E,$75,$FD,$E7,$C6) ; (LOG(2)^2)/3!
ASM_DATA($80,$31,$72,$18,$10) ; LOG(2)/2!
ASM_DATA($81,$00,$00,$00,$00) ; 1
; --------------------------------
; "EXP" FUNCTION
;
; FAC = E ^ FAC
; --------------------------------
EXP LDA #<CON_LOG_E ; CONVERT TO POWER OF TWO PROBLEM
LDY #>CON_LOG_E ; E^X = 2^(LOG2(E)*X)
JSR FMULT ;
LDA FAC_EXTENSION ; NON-STANDARD ROUNDING HERE
ADC #$50 ; ROUND UP IF EXTENSION > $AF
BCC L_EXP_1 ; NO, DON'T ROUND UP
JSR INCREMENT_MANTISSA
L_EXP_1 STA ARG_EXTENSION ; STRANGE VALUE
JSR MAF ; COPY FAC INTO ARG
LDA FAC ; MAXIMUM EXPONENT IS < 128
CMP #$88 ; WITHIN RANGE?
BCC L_EXP_3 ; YES
L_EXP_2 JSR OUTOFRNG ; OVERFLOW IF +, RETURN 0.0 IF -
L_EXP_3 JSR INT ; GET INT(FAC)
LDA CHARAC ; THIS IS THE INETGRAL PART OF THE POWER
CLC ; ADD TO EXPONENT BIAS + 1
ADC #$81 ;
BEQ L_EXP_2 ; OVERFLOW
SEC ; BACK OFF TO NORMAL BIAS
SBC #1 ;
PHA ; SAVE EXPONENT
; --------------------------------
LDX #5 ; SWAP ARG AND FAC
L_EXP_4 LDA ARG,X ; <<< WHY SWAP? IT IS DOING >>>
LDY FAC,X ; <<< -(A-B) WHEN (B-A) IS THE >>>
STA FAC,X ; <<< SAME THING! >>>
STY ARG,X
DEX
BPL L_EXP_4
LDA ARG_EXTENSION
STA FAC_EXTENSION
JSR FSUBT ; POWER-INT(POWER) --> FRACTIONAL PART
JSR NEGOP
LDA #<POLY_EXP
LDY #>POLY_EXP
JSR POLYNOMIAL ; COMPUTE F(X) ON FRACTIONAL PART
LDA #0
STA SGNCPR
PLA ; GET EXPONENT
JSR ADD_EXPONENTS_1
RTS ; <<< WASTED BYTE HERE, COULD HAVE >>>
; <<< JUST USED "JMP ADD.EXPO..." >>>
; --------------------------------
; ODD POLYNOMIAL SUBROUTINE
;
; F(X) = X * P(X^2)
;
; WHERE: X IS VALUE IN FAC
; Y,A POINTS AT COEFFICIENT TABLE
; FIRST BYTE OF COEFF. TABLE IS N
; COEFFICIENTS FOLLOW, HIGHEST POWER FIRST
;
; P(X^2) COMPUTED USING NORMAL POLYNOMIAL SUBROUTINE
;
; --------------------------------
POLYNOMIAL_ODD
STA SERPNT ; SAVE ADDRESS OF COEFFICIENT TABLE
STY SERPNT+1
JSR STORE_FAC_IN_TEMP1_ROUNDED
LDA #TEMP1 ; Y=0 ALREADY, SO Y,A POINTS AT TEMP1
JSR FMULT ; FORM X^2
JSR SERMAIN ; DO SERIES IN X^2
LDA #<TEMP1 ; GET X AGAIN
LDY #>TEMP1 ;
JMP FMULT ; MULTIPLY X BY P(X^2) AND EXIT
; --------------------------------
; NORMAL POLYNOMIAL SUBROUTINE
;
; P(X) = C(0)*X^N + C(1)*X^(N-1) + ... + C(N)
;
; WHERE: X IS VALUE IN FAC
; Y,A POINTS AT COEFFICIENT TABLE
; FIRST BYTE OF COEFF. TABLE IS N
; COEFFICIENTS FOLLOW, HIGHEST POWER FIRST
;
; --------------------------------
POLYNOMIAL
STA SERPNT ; POINTER TO COEFFICIENT TABLE
STY SERPNT+1
; --------------------------------
SERMAIN
JSR STORE_FAC_IN_TEMP2_ROUNDED
LDA (SERPNT),Y ; GET N
STA SERLEN ; SAVE N
LDY SERPNT ; BUMP PNTR TO HIGHEST COEFFICIENT
INY ; AND GET PNTR INTO Y,A
TYA
BNE L_SERMAIN_1
INC SERPNT+1
L_SERMAIN_1 STA SERPNT
LDY SERPNT+1
L_SERMAIN_2 JSR FMULT ; ACCUMULATE SERIES TERMS
LDA SERPNT ; BUMP PNTR TO NEXT COEFFICIENT
LDY SERPNT+1
CLC
ADC #5
BCC L_SERMAIN_3
INY
L_SERMAIN_3 STA SERPNT
STY SERPNT+1
JSR FADD ; ADD NEXT COEFFICIENT
LDA #TEMP2 ; POINT AT X AGAIN
LDY #0 ;
DEC SERLEN ; IF SERIES NOT FINISHED,
BNE L_SERMAIN_2 ; THEN ADD ANOTHER TERM
RTS_19 RTS ; FINISHED
; --------------------------------
CON_RND_1 ASM_DATA($98,$35,$44,$7A) ; <<< THESE ARE MISSING ONE BYTE >>>
CON_RND_2 ASM_DATA($68,$28,$B1,$46) ; <<< FOR FP VALUES >>>
; --------------------------------
; "RND" FUNCTION
; --------------------------------
RND JSR SIGN ; REDUCE ARGUMENT TO -1, 0, OR +1
TAX ; SAVE ARGUMENT
BMI L_RND_1 ; = -1, USE CURRENT ARGUMENT FOR SEED
LDA #<RNDSEED ; USE CURRENT SEED
LDY #>RNDSEED
JSR LOAD_FAC_FROM_YA
TXA ; RECALL SIGN OF ARGUMENT
BEQ RTS_19 ; =0, RETURN SEED UNCHANGED
LDA #<CON_RND_1 ; VERY POOR RND ALGORITHM
LDY #>CON_RND_1
JSR FMULT
LDA #<CON_RND_2 ; ALSO, CONSTANTS ARE TRUNCATED
LDY #>CON_RND_2 ; <<<THIS DOES NOTHING, DUE TO >>>
; <<<SMALL EXPONENT >>>
JSR FADD
L_RND_1 LDX FAC+4 ; SHUFFLE HI AND LO BYTES
LDA FAC+1 ; TO SUPPOSEDLY MAKE IT MORE RANDOM
STA FAC+4 ;
STX FAC+1 ;
LDA #0 ; MAKE IT POSITIVE
STA FAC_SIGN ;
LDA FAC ; A SOMEWHAT RANDOM EXTENSION
STA FAC_EXTENSION
LDA #$80 ; EXPONENT TO MAKE VALUE < 1.0
STA FAC
JSR NORMALIZE_FAC_2
LDX #<RNDSEED ; MOVE FAC TO RND SEED
LDY #>RNDSEED
GO_MOVMF JMP STORE_FACDB_YX_ROUNDED
; --------------------------------
; --------------------------------
; "COS" FUNCTION
; --------------------------------
COS LDA #<CON_PI_HALF ; COS(X)=SIN(X + PI/2)
LDY #>CON_PI_HALF
JSR FADD
; --------------------------------
; "SIN" FUNCTION
; --------------------------------
SIN JSR COPY_FAC_TO_ARG_ROUNDED
LDA #<CON_PI_DOUB ; REMOVE MULTIPLES OF 2*PI
LDY #>CON_PI_DOUB ; BY DIVIDING AND SAVING
LDX ARG_SIGN ; THE FRACTIONAL PART
JSR DIV ; USE SIGN OF ARGUMENT
JSR COPY_FAC_TO_ARG_ROUNDED
JSR INT ; TAKE INTEGER PART
LDA #0 ; <<< WASTED LINES, BECAUSE FSUBT >>>
STA SGNCPR ; <<< CHANGES SGNCPR AGAIN >>>
JSR FSUBT ; SUBTRACT TO GET FRACTIONAL PART
; --------------------------------
; (FAC) = ANGLE AS A FRACTION OF A FULL CIRCLE
;
; NOW FOLD THE RANGE INTO A QUARTER CIRCLE
;
; <<< THERE ARE MUCH SIMPLER WAYS TO DO THIS >>>
; --------------------------------
LDA #<QUARTER ; 1/4 - FRACTION MAKES
LDY #>QUARTER ; -3/4 <= FRACTION < 1/4
JSR FSUB ;
LDA FAC_SIGN ; TEST SIGN OF RESULT
PHA ; SAVE SIGN FOR LATER UNFOLDING
BPL SIN_1 ; ALREADY 0...1/4
JSR FADDH ; ADD 1/2 TO SHIFT TO -1/4...1/2
LDA FAC_SIGN ; TEST SIGN
BMI SIN_2 ; -1/4...0
; 0...1/2
LDA SIGNFLG ; SIGNFLG INITIALIZED = 0 IN "TAN"
EOR #$FF ; FUNCTION
STA SIGNFLG ; "TAN" IS ONLY USER OF SIGNFLG TOO
; --------------------------------
; IF FALL THRU, RANGE IS 0...1/2
; IF BRANCH HERE, RANGE IS 0...1/4
; --------------------------------
SIN_1 JSR NEGOP
; --------------------------------
; IF FALL THRU, RANGE IS -1/2...0
; IF BRANCH HERE, RANGE IS -1/4...0
; --------------------------------
SIN_2 LDA #<QUARTER ; ADD 1/4 TO SHIFT RANGE
LDY #>QUARTER ; TO -1/4...1/4
JSR FADD ;
PLA ; GET SAVED SIGN FROM ABOVE
BPL L_SIN_2_1 ;
JSR NEGOP ; MAKE RANGE 0...1/4
L_SIN_2_1 LDA #<POLY_SIN ; DO STANDARD SIN SERIES
LDY #>POLY_SIN ;
JMP POLYNOMIAL_ODD ;
; --------------------------------
; "TAN" FUNCTION
;
; COMPUTE TAN(X) = SIN(X) / COS(X)
; --------------------------------
TAN JSR STORE_FAC_IN_TEMP1_ROUNDED
LDA #0 ; SIGNFLG WILL BE TOGGLED IF 2ND OR 3RD
STA SIGNFLG ; QUADRANT
JSR SIN ; GET SIN(X)
LDX #<TEMP3 ; SAVE SIN(X) IN TEMP3
LDY #>TEMP3 ;
JSR GO_MOVMF ; <<<FUNNY WAY TO CALL MOVMF! >>>
LDA #<TEMP1 ; RETRIEVE X
LDY #>TEMP1 ;
JSR LOAD_FAC_FROM_YA
LDA #0 ; AND COMPUTE COS(X)
STA FAC_SIGN ;
LDA SIGNFLG ;
JSR TAN_1 ; WEIRD & DANGEROUS WAY TO GET INTO SIN
LDA #<TEMP3 ; NOW FORM SIN/COS
LDY #>TEMP3 ;
JMP FDIV ;
; --------------------------------
TAN_1 PHA ; SHAME, SHAME!
JMP SIN_1
; --------------------------------
CON_PI_HALF ASM_DATA($81,$49,$0F,$DA,$A2)
CON_PI_DOUB ASM_DATA($83,$49,$0F,$DA,$A2)
QUARTER ASM_DATA($7F,$00,$00,$00,$00)
; --------------------------------
POLY_SIN ASM_DATA(5) ; POWER OF POLYNOMIAL
ASM_DATA($84,$E6,$1A,$2D,$1B) ; (2PI)^11/11!
ASM_DATA($86,$28,$07,$FB,$F8) ; (2PI)^9/9!
ASM_DATA($87,$99,$68,$89,$01) ; (2PI)^7/7!
ASM_DATA($87,$23,$35,$DF,$E1) ; (2PI)^5/5!
ASM_DATA($86,$A5,$5D,$E7,$28) ; (2PI)^3/3!
ASM_DATA($83,$49,$0F,$DA,$A2) ; 2PI
; --------------------------------
; <<< NEXT TEN BYTES ARE NEVER REFERENCED >>>
; OBFUSCATED "MICROSOFT!" BY BILL GATES
; (REVERSED, HIGH BIT SET, XOR 7)
; --------------------------------
define(`GATES_OBFUSCATE',
`STR_FORCHAR(__,STR_REVERSE($1),`ASM_DATA(HICHAR(__)^7) NL()')')
GATES_OBFUSCATE(`MICROSOFT!')
; --------------------------------
; "ATN" FUNCTION
; --------------------------------
ATN LDA FAC_SIGN ; FOLD THE ARGUMENT RANGE FIRST
PHA ; SAVE SIGN FOR LATER UNFOLDING
BPL L_ATN_1 ; .GE. 0
JSR NEGOP ; .LT. 0, SO COMPLEMENT
L_ATN_1 LDA FAC ; IF .GE. 1, FORM RECIPROCAL
PHA ; SAVE FOR LATER UNFOLDING
CMP #$81 ; (EXPONENT FOR .GE. 1
BCC L_ATN_2 ; X < 1
LDA #<CON_ONE ; FORM 1/X
LDY #>CON_ONE
JSR FDIV
; --------------------------------
; 0 <= X <= 1
; 0 <= ATN(X) <= PI/8
; --------------------------------
L_ATN_2 LDA #<POLY_ATN ; COMPUTE POLYNOMIAL APPROXIMATION
LDY #>POLY_ATN
JSR POLYNOMIAL_ODD
PLA ; START TO UNFOLD
CMP #$81 ; WAS IT .GE. 1?
BCC L_ATN_3 ; NO
LDA #<CON_PI_HALF ; YES, SUBTRACT FROM PI/2
LDY #>CON_PI_HALF ;
JSR FSUB ;
L_ATN_3 PLA ; WAS IT NEGATIVE?
BPL RTS_20 ; NO
JMP NEGOP ; YES, COMPLEMENT
RTS_20 RTS
; --------------------------------
POLY_ATN ASM_DATA(11) ; POWER OF POLYNOMIAL
ASM_DATA($76,$B3,$83,$BD,$D3)
ASM_DATA($79,$1E,$F4,$A6,$F5)
ASM_DATA($7B,$83,$FC,$B0,$10)
ASM_DATA($7C,$0C,$1F,$67,$CA)
ASM_DATA($7C,$DE,$53,$CB,$C1)
ASM_DATA($7D,$14,$64,$70,$4C)
ASM_DATA($7D,$B7,$EA,$51,$7A)
ASM_DATA($7D,$63,$30,$88,$7E)
ASM_DATA($7E,$92,$44,$99,$3A)
ASM_DATA($7E,$4C,$CC,$91,$C7)
ASM_DATA($7F,$AA,$AA,$AA,$13)
ASM_DATA($81,$00,$00,$00,$00)
; --------------------------------
; GENERIC COPY OF CHRGET SUBROUTINE, WHICH
; IS COPIED INTO $00B1...$00C8 DURING INITIALIZATION
;
; CORNELIS BONGERS DESCRIBED SEVERAL IMPROVEMENTS
; TO CHRGET IN MICRO MAGAZINE OR CALL A.P.P.L.E.
; (I DON'T REMEMBER WHICH OR EXACTLY WHEN)
; --------------------------------
GENERIC_CHRGET
INC TXTPTR
BNE L_GENERIC_CHRGET_1
INC TXTPTR+1
L_GENERIC_CHRGET_1 LDA $EA60 ; <<< ACTUAL ADDRESS FILLED IN LATER >>>
CMP #LOCHAR(`:') ; EOS, ALSO TOP OF NUMERIC RANGE
BCS L_GENERIC_CHRGET_2 ; NOT NUMBER, MIGHT BE EOS
CMP #LOCHAR(` ') ; IGNORE BLANKS
BEQ GENERIC_CHRGET
SEC ; TEST FOR NUMERIC RANGE IN WAY THAT
SBC #LOCHAR(`0') ; CLEARS CARRY IF CHAR IS DIGIT
SEC ; AND LEAVES CHAR IN A-REG
SBC #$D0
L_GENERIC_CHRGET_2 RTS
; --------------------------------
; INITIAL VALUE FOR RANDOM NUMBER, ALSO COPIED
; IN ALONG WITH CHRGET, BUT ERRONEOUSLY:
; <<< THE LAST BYTE IS NOT COPIED >>>
; --------------------------------
ASM_DATA($80,$4F,$C7,$52,$58) ; APPROX. = L_GENERIC_CHRGET_811635157
GENERIC_END
; --------------------------------
COLD_START
LDX #$FF ; SET DIRECT MODE FLAG
STX CURLIN+1 ;
LDX #$FB ; SET STACK POINTER, LEAVING ROOM FOR
TXS ; LINE BUFFER DURING PARSING
LDA #<COLD_START ; SET RESTART TO COLD.START
LDY #>COLD_START ; UNTIL COLDSTART IS COMPLETED
STA GOWARM+1 ;
STY GOWARM+2 ;
STA GOSTROUT+1 ; ALSO SECOND USER VECTOR...
STY GOSTROUT+2 ; ..WE SIMPLY MUST FINISH COLD.START!
JSR NORMAL ; SET NORMAL DISPLAY MODE
LDA #$4C ; "JMP" OPCODE FOR 4 VECTORS
STA GOWARM ; WARM START
STA GOSTROUT ; ANYONE EVER USE THIS ONE?
STA JMPADRS ; USED BY FUNCTIONS (JSR JMPADRS)
STA USR ; "USR" FUNCTION VECTOR
LDA #<IQERR ; POINT "USR" TO ILLEGAL QUANTITY
LDY #>IQERR ; ERROR, UNTIL USER SETS IT UP
STA USR+1
STY USR+2
; --------------------------------
; MOVE GENERIC CHRGET AND RANDOM SEED INTO PLACE
;
; <<< NOTE THAT LOOP VALUE IS WRONG! >>>
; <<< THE LAST BYTE OF THE RANDOM SEED IS NOT >>>
; <<< COPIED INTO PAGE ZERO! >>>
; --------------------------------
LDX #GENERIC_END-GENERIC_CHRGET-1
L_COLD_START_1 LDA GENERIC_CHRGET-1,X
STA CHRGET-1,X
STX SPEEDZ ; ON LAST PASS STORES $01)
DEX
BNE L_COLD_START_1
; --------------------------------
STX TRCFLG ; X=0, TURN OFF TRACING
TXA ; A=0
STA SHIFT_SIGN_EXT
STA LASTPT+1
PHA ; PUT $00 ON STACK (WHAT FOR?)
LDA #3 ; SET LENGTH OF TEMP. STRING DESCRIPTORS
STA DSCLEN ; FOR GARBAGE COLLECTION SUBROUTINE
JSR CRDO ; PRINT <RETURN>
LDA #1 ; SET UP FAKE FORWARD LINK
STA INPUT_BUFFER-3
STA INPUT_BUFFER-4
LDX #TEMPST ; INIT INDEX TO TEMP STRING DESCRIPTORS
STX TEMPPT
; --------------------------------
; FIND HIGH END OF RAM
; --------------------------------
LDA #<$0800 ; SET UP POINTER TO LOW END OF RAM
LDY #>$0800
STA LINNUM
STY LINNUM+1
LDY #0
L_COLD_START_2 INC LINNUM+1 ; TEST FIRST BYTE OF EACH PAGE
LDA (LINNUM),Y ; BY COMPLEMENTING IT AND WATCHING
EOR #$FF ; IT CHANGE THE SAME WAY
STA (LINNUM),Y ;
CMP (LINNUM),Y ; ROM OR EMPTY SOCKETS WON'T TRACK
BNE L_COLD_START_3 ; NOT RAM HERE
EOR #$FF ; RESTORE ORIGINAL VALUE
STA (LINNUM),Y ;
CMP (LINNUM),Y ; DID IT TRACK AGAIN?
BEQ L_COLD_START_2 ; YES, STILL IN RAM
L_COLD_START_3 LDY LINNUM ; NO, END OF RAM
LDA LINNUM+1 ;
AND #$F0 ; FORCE A MULTIPLE OF 4096 BYTES
STY MEMSIZ ; (BAD RAM MAY HAVE YIELDED NON-MULTIPLE)
STA MEMSIZ+1 ;
STY FRETOP ; SET HIMEM AND BOTTOM OF STRINGS
STA FRETOP+1 ;
LDX #<$0800 ; SET PROGRAM POINTER TO $0800
LDY #>$0800 ;
STX TXTTAB ;
STY TXTTAB+1 ;
LDY #0 ; TURN OFF SEMI-SECRET LOCK FLAG
STY LOCK ;
TYA ; A=0 TOO
STA (TXTTAB),Y ; FIRST BYTE IN PROGRAM SPACE = 0
INC TXTTAB ; ADVANCE PAST THE $00
BNE L_COLD_START_4 ;
INC TXTTAB+1 ;
L_COLD_START_4 LDA TXTTAB ;
LDY TXTTAB+1 ;
JSR REASON ; SET REST OF POINTERS UP
JSR SCRTCH ; MORE POINTERS
LDA #<STROUT ; PUT CORRECT ADDRESSES IN TWO
LDY #>STROUT ; USER VECTORS
STA GOSTROUT+1
STY GOSTROUT+2
LDA #<RESTART
LDY #>RESTART
STA GOWARM+1
STY GOWARM+2
JMP (GOWARM+1) ; SILLY, WHY NOT JUST "JMP RESTART"
; --------------------------------
; --------------------------------
; "CALL" STATEMENT
;
; EFFECTIVELY PERFORMS A "JSR" TO THE SPECIFIED
; ADDRESS, WITH THE FOLLOWING REGISTER CONTENTS:
; (A,Y) = CALL ADDRESS
; (X) = $9D
;
; THE CALLED ROUTINE CAN RETURN WITH "RTS",
; AND APPLESOFT WILL CONTINUE WITH THE NEXT
; STATEMENT.
; --------------------------------
CALL JSR FRMNUM ; EVALUATE EXPRESSION FOR CALL ADDRESS
JSR GETADR ; CONVERT EXPRESSION TO 16-BIT INTEGER
JMP (LINNUM) ; IN LINNUM, AND JUMP THERE.
; --------------------------------
; "IN#" STATEMENT
;
; NOTE: NO CHECK FOR VALID SLOT #, AS LONG
; AS VALUE IS < 256 IT IS ACCEPTED.
; MONITOR MASKS VALUE TO 4 BITS (0-15).
; --------------------------------
IN_NUMBER
JSR GETBYT ; GET SLOT NUMBER IN X-REG
TXA ; MONITOR WILL INSTALL IN VECTOR
JMP MON_INPORT ; AT $38,39.
; --------------------------------
; "PR#" STATEMENT
;
; NOTE: NO CHECK FOR VALID SLOT #, AS LONG
; AS VALUE IS < 256 IT IS ACCEPTED.
; MONITOR MASKS VALUE TO 4 BITS (0-15).
; --------------------------------
PR_NUMBER
JSR GETBYT ; GET SLOT NUMBER IN X-REG
TXA ; MONITOR WILL INSTALL IN VECTOR
JMP MON_OUTPORT ; AT $36,37
; --------------------------------
; GET TWO VALUES < 48, WITH COMMA SEPARATOR
;
; CALLED FOR "PLOT X,Y"
; AND "HLIN A,B AT Y"
; AND "VLIN A,B AT X"
;
; --------------------------------
PLOTFNS
JSR GETBYT ; GET FIRST VALUE IN X-REG
CPX #48 ; MUST BE < 48
BCS GOERR ; TOO LARGE
STX FIRST ; SAVE FIRST VALUE
LDA #LOCHAR(`,') ; MUST HAVE A COMMA
JSR SYNCHR ;
JSR GETBYT ; GET SECOND VALUE IN X-REG
CPX #48 ; MUST BE < 48
BCS GOERR ; TOO LARGE
STX MON_H2 ; SAVE SECOND VALUE
STX MON_V2 ;
RTS ; SECOND VALUE STILL IN X-REG
; --------------------------------
GOERR JMP IQERR ; ILLEGAL QUANTITY ERROR
; --------------------------------
; GET "A,B AT C" VALUES FOR "HLIN" AND "VLIN"
;
; PUT SMALLER OF (A,B) IN FIRST,
; AND LARGER OF (A,B) IN H2 AND V2.
; RETURN WITH (X) = C-VALUE.
; --------------------------------
LINCOOR
JSR PLOTFNS ; GET A,B VALUES
CPX FIRST ; IS A < B?
BCS L_LINCOOR_1 ; YES, IN RIGHT ORDER
LDA FIRST ; NO, INTERCHANGE THEM
STA MON_H2 ;
STA MON_V2 ;
STX FIRST ;
L_LINCOOR_1 LDA #TOKENDB ; MUST HAVE "AT" NEXT
JSR SYNCHR ;
JSR GETBYT ; GET C-VALUE IN X-REG
CPX #48 ; MUST BE < 48
BCS GOERR ; TOO LARGE
RTS ; C-VALUE IN X-REG
; --------------------------------
; "PLOT" STATEMENT
; --------------------------------
PLOT JSR PLOTFNS ; GET X,Y VALUES
TXA ; Y-COORD TO A-REG FOR MONITOR
LDY FIRST ; X-COORD TO Y-YEG FOR MONITOR
CPY #40 ; X-COORD MUST BE < 40
BCS GOERR ; X-COORD IS TOO LARGE
JMP MON_PLOT ; PLOT!
; --------------------------------
; "HLIN" STATEMENT
; --------------------------------
HLIN JSR LINCOOR ; GET "A,B AT C"
TXA ; Y-COORD IN A-REG
LDY MON_H2 ; RIGHT END OF LINE
CPY #40 ; MUST BE < 40
BCS GOERR ; TOO LARGE
LDY FIRST ; LEFT END OF LINE IN Y-REG
JMP MON_HLINE ; LET MONITOR DRAW LINE
; --------------------------------
; "VLIN" STATEMENT
; --------------------------------
VLIN JSR LINCOOR ; GET "A,B AT C"
TXA ; X-COORD IN Y-REG
TAY ;
CPY #40 ; X-COORD MUST BE < 40
BCS GOERR ; TOO LARGE
LDA FIRST ; TOP END OF LINE IN A-REG
JMP MON_VLINE ; LET MONITOR DRAW LINE
; --------------------------------
; "COLOR=" STATEMENT
; --------------------------------
COLOR JSR GETBYT ; GET COLOR VALUE IN X-REG
TXA ;
JMP MON_SETCOL ; LET MONITOR STORE COLOR
; --------------------------------
; "VTAB" STATEMENT
; --------------------------------
VTAB JSR GETBYT ; GET LINE # IN X-REG
DEX ; CONVERT TO ZERO BASE
TXA ;
CMP #24 ; MUST BE 0-23
BCS GOERR ; TOO LARGE, OR WAS "VTAB 0"
JMP MON_TABV ; LET MONITOR COMPUTE BASE
; --------------------------------
; "SPEED=" STATEMENT
; --------------------------------
SPEED JSR GETBYT ; GET SPEED SETTING IN X-REG
TXA ; SPEEDZ = $100-SPEED
EOR #$FF ; SO "SPEED=255" IS FASTEST
TAX ;
INX ;
STX SPEEDZ ;
RTS ;
; --------------------------------
; "TRACE" STATEMENT
; SET SIGN BIT IN TRCFLG
; --------------------------------
TRACE SEC ;
ASM_DATA($90) ; FAKE BCC TO SKIP NEXT OPCODE
; --------------------------------
; "NOTRACE" STATEMENT
; CLEAR SIGN BIT IN TRCFLG
; --------------------------------
NOTRACE ;
CLC ;
ROR TRCFLG ; SHIFT CARRY INTO TRCFLG
RTS ;
; --------------------------------
; "NORMAL" STATEMENT
; --------------------------------
NORMAL LDA #$FF ; SET INVFLG = $FF
BNE N_I_ ; AND FLASH.BIT = $00
; --------------------------------
; "INVERSE" STATEMENT
; --------------------------------
INVERSE ;
LDA #$3F ; SET INVFLG = $3F
N_I_ LDX #0 ; AND FLASH.BIT = $00
N_I_F_ STA MON_INVFLG
STX FLASH_BIT
RTS
; --------------------------------
; "FLASH" STATEMENT
; --------------------------------
FLASH LDA #$7F ; SET INVFLG = $7F
LDX #$40 ; AND FLASH.BIT = $40
BNE N_I_F_ ; ...ALWAYS
; --------------------------------
; "HIMEM:" STATEMENT
; --------------------------------
HIMEM JSR FRMNUM ; GET VALUE SPECIFIED FOR HIMEM
JSR GETADR ; AS 16-BIT INTEGER
LDA LINNUM ; MUST BE ABOVE VARIABLES AND ARRAYS
CMP STREND ;
LDA LINNUM+1 ;
SBC STREND+1 ;
BCS SETHI ; IT IS ABOVE THEM
JMM JMP MEMERR ; NOT ENOUGH MEMORY
SETHI LDA LINNUM ; STORE NEW HIMEM: VALUE
STA MEMSIZ ;
STA FRETOP ; <<<NOTE THAT "HIMEM:" DOES NOT>>>
LDA LINNUM+1 ; <<<CLEAR STRING VARIABLES. >>>
STA MEMSIZ+1 ; <<<THIS COULD BE DISASTROUS. >>>
STA FRETOP+1 ;
RTS ;
; --------------------------------
; "LOMEM:" STATEMENT
; --------------------------------
LOMEM JSR FRMNUM ; GET VALUE SPECIFIED FOR LOMEM
JSR GETADR ; AS 16-BIT INTEGER IN LINNUM
LDA LINNUM ; MUST BE BELOW HIMEM
CMP MEMSIZ ;
LDA LINNUM+1 ;
SBC MEMSIZ+1 ;
BCS JMM ; ABOVE HIMEM, MEMORY ERROR
LDA LINNUM ; MUST BE ABOVE PROGRAM
CMP VARTAB ;
LDA LINNUM+1 ;
SBC VARTAB+1 ;
BCC JMM ; NOT ABOVE PROGRAM, ERROR
LDA LINNUM ; STORE NEW LOMEM VALUE
STA VARTAB ;
LDA LINNUM+1 ;
STA VARTAB+1 ;
JMP CLEARC ; LOMEM CLEARS VARIABLES AND ARRAYS
; --------------------------------
; "ON ERR GO TO" STATEMENT
; --------------------------------
ONERR LDA #TOKEN_GOTO ; MUST BE "GOTO" NEXT
JSR SYNCHR
LDA TXTPTR ; SAVE TXTPTR FOR HANDLERR
STA TXTPSV ;
LDA TXTPTR+1 ;
STA TXTPSV+1 ;
SEC ; SET SIGN BIT OF ERRFLG
ROR ERRFLG ;
LDA CURLIN ; SAVE LINE # OF CURRENT LINE
STA CURLSV ;
LDA CURLIN+1 ;
STA CURLSV+1 ;
JSR REMN ; IGNORE REST OF LINE <<<WHY?>>>
JMP ADDON ; CONTINUE PROGRAM
; --------------------------------
; ROUTINE TO HANDLE ERRORS IF ONERR GOTO ACTIVE
; --------------------------------
HANDLERR ;
STX ERRNUM ; SAVE ERROR CODE NUMBER
LDX REMSTK ; GET STACK PNTR SAVED AT NEWSTT
STX ERRSTK ; REMEMBER IT
; <<<COULD ALSO HAVE DONE TXS >>>
; <<<HERE; SEE ONERR CORRECTION>>>
; <<<IN APPLESOFT MANUAL. >>>
LDA CURLIN ; GET LINE # OF OFFENDING STATEMENT
STA ERRLIN ; SO USER CAN SEE IT IF DESIRED
LDA CURLIN+1 ;
STA ERRLIN+1 ;
LDA OLDTEXT ; ALSO THE POSITION IN THE LINE
STA ERRPOS ; IN CASE USER WANTS TO "RESUME"
LDA OLDTEXT+1 ;
STA ERRPOS+1 ;
LDA TXTPSV ; SET UP TXTPTR TO READ TARGET LINE #
STA TXTPTR ; IN "ON ERR GO TO XXXX"
LDA TXTPSV+1 ;
STA TXTPTR+1 ;
LDA CURLSV ;
STA CURLIN ; LINE # OF "ON ERR" STATEMENT
LDA CURLSV+1 ;
STA CURLIN+1 ;
JSR CHRGOT ; START CONVERSION
JSR GOTO ; GOTO SPECIFIED ONERR LINE
JMP NEWSTT ;
; --------------------------------
; "RESUME" STATEMENT
; --------------------------------
RESUME LDA ERRLIN ; RESTORE LINE # AND TXTPTR
STA CURLIN ; TO RE-TRY OFFENDING LINE
LDA ERRLIN+1 ;
STA CURLIN+1 ;
LDA ERRPOS ;
STA TXTPTR ;
LDA ERRPOS+1 ;
STA TXTPTR+1 ;
; <<< ONERR CORRECTION IN MANUAL IS EASILY >>>
; <<< BY "CALL -3288", WHICH IS $F328 HERE >>>
LDX ERRSTK ; RETRIEVE STACK PNTR AS IT WAS
TXS ; BEFORE STATEMENT SCANNED
JMP NEWSTT ; DO STATEMENT AGAIN
; --------------------------------
JSYN JMP SYNERR ;
; --------------------------------
; "DEL" STATEMENT
; --------------------------------
DEL BCS JSYN ; ERROR IF # NOT SPECIFIED
LDX PRGEND ;
STX VARTAB ;
LDX PRGEND+1 ;
STX VARTAB+1 ;
JSR LINGET ; GET BEGINNING OF RANGE
JSR FNDLIN ; FIND THIS LINE OR NEXT
LDA LOWTR ; UPPER PORTION OF PROGRAM WILL
STA DEST ; BE MOVED DOWN TO HERE
LDA LOWTR+1 ;
STA DEST+1 ;
LDA #LOCHAR(`,') ; MUST HAVE A COMMA NEXT
JSR SYNCHR ;
JSR LINGET ; GET END RANGE
; (DOES NOTHING IF END RANGE
; IS NOT SPECIFIED)
INC LINNUM ; POINT ONE PAST IT
BNE L_DEL_1 ;
INC LINNUM+1 ;
L_DEL_1 JSR FNDLIN ; FIND START LINE AFTER SPECIFIED LINE
LDA LOWTR ; WHICH IS BEGINNING OF PORTION
CMP DEST ; TO BE MOVED DOWN
LDA LOWTR+1 ; IT MUST BE ABOVE THE TARGET
SBC DEST+1 ;
BCS L_DEL_2 ; IT IS OKAY
RTS ; NOTHING TO DELETE
L_DEL_2 LDY #0 ; MOVE UPPER PORTION DOWN NOW
L_DEL_3 LDA (LOWTR),Y ; SOURCE . . .
STA (DEST),Y ; ...TO DESTINATION
INC LOWTR ; BUMP SOURCE PNTR
BNE L_DEL_4 ;
INC LOWTR+1 ;
L_DEL_4 INC DEST ; BUMP DESTINATION PNTR
BNE L_DEL_5 ;
INC DEST+1 ;
L_DEL_5 LDA VARTAB ; REACHED END OF PROGRAM YET?
CMP LOWTR ;
LDA VARTAB+1 ;
SBC LOWTR+1 ;
BCS L_DEL_3 ; NO, KEEP MOVING
LDX DEST+1 ; STORE NEW END OF PROGRAM
LDY DEST ; MUST SUBTRACT 1 FIRST
BNE L_DEL_6 ;
DEX ;
L_DEL_6 DEY ;
STX VARTAB+1 ;
STY VARTAB ;
JMP FIX_LINKS ; RESET LINKS AFTER A DELETE
; --------------------------------
; "GR" STATEMENT
; --------------------------------
GR LDA SW_LORES
LDA SW_MIXSET
JMP MON_SETGR
; --------------------------------
; "TEXT" STATEMENT
; --------------------------------
TEXT LDA SW_LOWSCR ; JMP $FB36 WOULD HAVE
JMP MON_SETTXT ; DONE BOTH OF THESE
; <<< BETTER CODE WOULD BE: >>>
; <<< LDA SW.MIXSET >>>
; <<< JMP $FB33 >>>
; --------------------------------
; "STORE" STATEMENT
; --------------------------------
STORE JSR GETARYPT ; GET ADDRESS OF ARRAY TO BE SAVED
LDY #3 ; FORWARD OFFSET - 1 IS SIZE OF
LDA (LOWTR),Y ; THIS ARRAY
TAX
DEY
LDA (LOWTR),Y
SBC #1
BCS L_STORE_1
DEX
L_STORE_1 STA LINNUM
STX LINNUM+1
JSR MON_WRITE
JSR TAPEPNT
JMP MON_WRITE
; --------------------------------
; "RECALL" STATEMENT
; --------------------------------
RECALL JSR GETARYPT ; FIND ARRAY IN MEMORY
JSR MON_READ ; READ HEADER
LDY #2 ; MAKE SURE THE NEW DATA FITS
LDA (LOWTR),Y ;
CMP LINNUM ;
INY ;
LDA (LOWTR),Y ;
SBC LINNUM+1 ;
BCS L_RECALL_1 ; IT FITS
JMP MEMERR ; DOESN'T FIT
L_RECALL_1 JSR TAPEPNT ; READ THE DATA
JMP MON_READ ;
; --------------------------------
; "HGR" AND "HGR2" STATEMENTS
; --------------------------------
HGR2 BIT SW_HISCR ; SELECT PAGE 2 ($4000-5FFF)
BIT SW_MIXCLR ; DEFAULT TO FULL SCREEN
LDA #>$4000 ; SET STARTING PAGE FOR HIRES
BNE SETHPG ; ...ALWAYS
HGR LDA #>$2000 ; SET STARTING PAGE FOR HIRES
BIT SW_LOWSCR ; SELECT PAGE 1 ($2000-3FFF)
BIT SW_MIXSET ; DEFAULT TO MIXED SCREEN
SETHPG STA HGR_PAGE ; BASE PAGE OF HIRES BUFFER
LDA SW_HIRES ; TURN ON HIRES
LDA SW_TXTCLR ; TURN ON GRAPHICS
; --------------------------------
; CLEAR SCREEN
; --------------------------------
HCLR LDA #0 ; SET FOR BLACK BACKGROUND
STA HGR_BITS
; --------------------------------
; FILL SCREEN WITH (HGR.BITS)
; --------------------------------
BKGND LDA HGR_PAGE ; PUT BUFFER ADDRESS IN HGR.SHAPE
STA HGR_SHAPE+1
LDY #0
STY HGR_SHAPE
L_BKGND_1 LDA HGR_BITS ; COLOR BYTE
STA (HGR_SHAPE),Y ; CLEAR HIRES TO HGR.BITS
JSR COLOR_SHIFT ; CORRECT FOR COLOR SHIFT
INY ; (SLOWS CLEAR BY FACTOR OF 2)
BNE L_BKGND_1
INC HGR_SHAPE+1
LDA HGR_SHAPE+1
AND #$1F ; DONE? ($40 OR$60)
BNE L_BKGND_1 ; NO
RTS ; YES, RETURN
; --------------------------------
; SET THE HIRES CURSOR POSITION
;
; (Y,X) = HORIZONTAL COORDINATE (0-279)
; (A) = VERTICAL COORDINATE (0-191)
; --------------------------------
HPOSN STA HGR_Y ; SAVE Y- AND X-POSITIONS
STX HGR_X ;
STY HGR_X+1 ;
PHA ; Y-POS ALSO ON STACK
AND #$C0 ; CALCULATE BASE ADDRESS FOR Y-POS
STA MON_GBASL ; FOR Y=ABCDEFGH
LSR ; GBASL=ABAB0000
LSR ;
ORA MON_GBASL ;
STA MON_GBASL ;
PLA ; (A) (GBASH) (GBASL)
STA MON_GBASH ; ?-ABCDEFGH ABCDEFGH ABAB0000
ASL ; A-BCDEFGH0 ABCDEFGH ABAB0000
ASL ; B-CDEFGH00 ABCDEFGH ABAB0000
ASL ; C-DEFGH000 ABCDEFGH ABAB0000
ROL MON_GBASH ; A-DEFGH000 BCDEFGHC ABAB0000
ASL ; D-EFGH0000 BCDEFGHC ABAB0000
ROL MON_GBASH ; B-EFGH0000 CDEFGHCD ABAB0000
ASL ; E-FGH00000 CDEFGHCD ABAB0000
ROR MON_GBASL ; 0-FGH00000 CDEFGHCD EABAB000
LDA MON_GBASH ; 0-CDEFGHCD CDEFGHCD EABAB000
AND #$1F ; 0-000FGHCD CDEFGHCD EABAB000
ORA HGR_PAGE ; 0-PPPFGHCD CDEFGHCD EABAB000
STA MON_GBASH ; 0-PPPFGHCD PPPFGHCD EABAB000
TXA ; DIVIDE X-POS BY 7 FOR INDEX FROM BASE
CPY #0 ; IS X-POS < 256?
BEQ L_HPOSN_2 ; YES
LDY #35 ; NO: 256/7 = 36 REM 4
; CARRY=1, SO ADC #4 IS TOO LARGE;
; HOWEVER, ADC #4 CLEARS CARRY
; WHICH MAKES SBC #7 ONLY -6
; BALANCING IT OUT.
ADC #4 ; FOLLOWING INY MAKES Y=36
L_HPOSN_1 INY
L_HPOSN_2 SBC #7
BCS L_HPOSN_1
STY HGR_HORIZ ; HORIZONTAL INDEX
TAX ; USE REMAINDER-7 TO LOOK UP THE
LDA MSKTBL-$100+7,X ; BIT MASK
STA MON_HMASK
TYA ; QUOTIENT GIVES BYTE INDEX
LSR ; ODD OR EVEN COLUMN?
LDA HGR_COLOR ; IF ON ODD BYTE (CARRY SET)
STA HGR_BITS ; THEN ROTATE BITS
BCS COLOR_SHIFT ; ODD COLUMN
RTS ; EVEN COLUMN
; --------------------------------
; PLOT A DOT
;
; (Y,X) = HORIZONTAL POSITION
; (A) = VERTICAL POSITION
; --------------------------------
HPLOT0 JSR HPOSN
LDA HGR_BITS ; CALCULATE BIT POSN IN GBAS,
EOR (MON_GBASL),Y ; HGR.HORIZ, AND HMASK FROM
AND MON_HMASK ; Y-COOR IN A-REG,
EOR (MON_GBASL),Y ; X-COOR IN X,Y REGS.
STA (MON_GBASL),Y ; FOR ANY 1-BITS, SUBSTITUTE
RTS ; CORRESPONDING BIT OF HGR.BITS
; --------------------------------
; MOVE LEFT OR RIGHT ONE PIXEL
;
; IF STATUS IS +, MOVE RIGHT; IF -, MOVE LEFT
; IF ALREADY AT LEFT OR RIGHT EDGE, WRAP AROUND
;
; REMEMBER BITS IN HI-RES BYTE ARE BACKWARDS ORDER:
; BYTE N BYTE N+1
; S7654321 SEDCBA98
; --------------------------------
MOVE_LEFT_OR_RIGHT
BPL MOVE_RIGHT ; + MOVE RIGHT, - MOVE LEFT
LDA MON_HMASK ; MOVE LEFT ONE PIXEL
LSR ; SHIFT MASK RIGHT, MOVES DOT LEFT
BCS LR_2 ; ...DOT MOVED TO NEXT BYTE
EOR #$C0 ; MOVE SIGN BIT BACK WHERE IT WAS
LR_1 STA MON_HMASK ; NEW MASK VALUE
RTS ;
LR_2 DEY ; MOVED TO NEXT BYTE, SO DECR INDEX
BPL LR_3 ; STILL NOT PAST EDGE
LDY #39 ; OFF LEFT EDGE, SO WRAP AROUND SCREEN
LR_3 LDA #$C0 ; NEW HMASK, RIGHTMOST BIT ON SCREEN
LR_4 STA MON_HMASK ; NEW MASK AND INDEX
STY HGR_HORIZ ;
LDA HGR_BITS ; ALSO NEED TO ROTATE COLOR
; --------------------------------
COLOR_SHIFT
ASL ; ROTATE LOW-ORDER 7 BITS
CMP #$C0 ; OF HGR.BITS ONE BIT POSN.
BPL L_COLOR_SHIFT_1
LDA HGR_BITS
EOR #$7F
STA HGR_BITS
L_COLOR_SHIFT_1 RTS
; --------------------------------
; MOVE RIGHT ONE PIXEL
; IF ALREADY AT RIGHT EDGE, WRAP AROUND
; --------------------------------
MOVE_RIGHT
LDA MON_HMASK
ASL ; SHIFTING BYTE LEFT MOVES PIXEL RIGHT
EOR #$80 ;
; ORIGINAL: C0 A0 90 88 84 82 81
; SHIFTED: 80 40 20 10 08 02 01
; EOR #$80: 00 C0 A0 90 88 84 82
BMI LR_1 ; FINISHED
LDA #$81 ; NEW MASK VALUE
INY ; MOVE TO NEXT BYTE RIGHT
CPY #40 ; UNLESS THAT IS TOO FAR
BCC LR_4 ; NOT TOO FAR
LDY #0 ; TOO FAR, SO WRAP AROUND
BCS LR_4 ; ...ALWAYS
; --------------------------------
; --------------------------------
; "XDRAW" ONE BIT
; --------------------------------
LRUDX1 CLC ; C=0 MEANS NO 90 DEGREE ROTATION
LRUDX2 LDA HGR_DX+1 ; C=1 MEANS ROTATE 90 DEGREES
AND #4 ; IF BIT2=0 THEN DON'T PLOT
BEQ LRUD4 ; YES, DO NOT PLOT
LDA #$7F ; NO, LOOK AT WHAT IS ALREADY THERE
AND MON_HMASK
AND (MON_GBASL),Y ; SCREEN BIT = 1?
BNE LRUD3 ; YES, GO CLEAR IT
INC HGR_COLLISIONS ; NO, COUNT THE COLLISION
LDA #$7F ; AND TURN THE BIT ON
AND MON_HMASK ;
BPL LRUD3 ; ...ALWAYS
; --------------------------------
; "DRAW" ONE BIT
; --------------------------------
LRUD1 CLC ; C=0 MEANS NO 90 DEGREE ROTATION
LRUD2 LDA HGR_DX+1 ; C=1 MEANS ROTATE
AND #4 ; IF BIT2=0 THEN DO NOT PLOT
BEQ LRUD4 ; DO NOT PLOT
LDA (MON_GBASL),Y
EOR HGR_BITS ; 1'S WHERE ANY BITS NOT IN COLOR
AND MON_HMASK ; LOOK AT JUST THIS BIT POSITION
BNE LRUD3 ; THE BIT WAS ZERO, SO PLOT IT
INC HGR_COLLISIONS ; BIT IS ALREADY 1; COUNT COLLSN
; --------------------------------
; TOGGLE BIT ON SCREEN WITH (A)
; --------------------------------
LRUD3 EOR (MON_GBASL),Y
STA (MON_GBASL),Y
; --------------------------------
; DETERMINE WHERE NEXT POINT WILL BE, AND MOVE THERE
; C=0 IF NO 90 DEGREE ROTATION
; C=1 ROTATES 90 DEGREES
; --------------------------------
LRUD4 LDA HGR_DX+1 ; CALCULATE THE DIRECTION TO MOVE
ADC HGR_QUADRANT
AND #3 ; WRAP AROUND THE CIRCLE
CON_03 = *-1 ; (( A CONSTANT ))
;
; 00 -- UP
; 01 -- DOWN
; 10 -- RIGHT
; 11 -- LEFT
;
CMP #2 ; C=0 IF 0 OR 1, C=1 IF 2 OR 3
ROR ; PUT C INTO SIGN, ODD/EVEN INTO C
BCS MOVE_LEFT_OR_RIGHT
; --------------------------------
MOVE_UP_OR_DOWN
BMI MOVE_DOWN ; SIGN FOR UP/DOWN SELECT_
; --------------------------------
; MOVE UP ONE PIXEL
; IF ALREADY AT TOP, GO TO BOTTOM
;
; REMEMBER: Y-COORD GBASH GBASL
; ABCDEFGH PPPFGHCD EABAB000
; --------------------------------
CLC ; MOVE UP
LDA MON_GBASH ; CALC. BASE ADDRESS OF PREV. LINE
BIT CON_1C ; LOOK AT BITS 000FGH00 IN GBASH
BNE L_MOVE_UP_OR_DOWN_5 ; SIMPLE, JUST FGH=FGH-1
; GBASH=PPP000CD, GBASL=EABAB000
ASL MON_GBASL ; WHAT IS "E"?
BCS L_MOVE_UP_OR_DOWN_3 ; E=1, THEN EFGH=EFGH-1
BIT CON_03 ; LOOK AT 000000CD IN GBASH
BEQ L_MOVE_UP_OR_DOWN_1 ; Y-POS IS AB000000 FORM
ADC #$1F ; CD <> 0, SO CDEFGH=CDEFGH-1
SEC ;
BCS L_MOVE_UP_OR_DOWN_4 ; ...ALWAYS
L_MOVE_UP_OR_DOWN_1 ADC #$23 ; ENOUGH TO MAKE GBASH=PPP11111 LATER
PHA ; SAVE FOR LATER
LDA MON_GBASL ; GBASL IS NOW ABAB0000 (AB=00,01,10)
ADC #$B0 ; 0000+1011=1011 AND CARRY CLEAR
; OR 0101+1011=0000 AND CARRY SET
; OR 1010+1011=0101 AND CARRY SET
BCS L_MOVE_UP_OR_DOWN_2 ; NO WRAP-AROUND NEEDED
ADC #$F0 ; CHANGE 1011 TO 1010 (WRAP-AROUND)
L_MOVE_UP_OR_DOWN_2 STA MON_GBASL ; FORM IS NOW STILL ABAB0000
PLA ; PARTIALLY MODIFIED GBASH
BCS L_MOVE_UP_OR_DOWN_4 ; ...ALWAYS
L_MOVE_UP_OR_DOWN_3 ADC #$1F ;
L_MOVE_UP_OR_DOWN_4 ROR MON_GBASL ; SHIFT IN E, TO GET EABAB000 FORM
L_MOVE_UP_OR_DOWN_5 ADC #$FC ; FINISH GBASH MODS
UD_1 STA MON_GBASH ;
RTS
; --------------------------------
CLC ; <<<NEVER USED>>>
; --------------------------------
; MOVE DOWN ONE PIXEL
; IF ALREADY AT BOTTOM, GO TO TOP
;
; REMEMBER: Y-COORD GBASH GBASL
; ABCDEFGH PPPFGHCD EABAB000
; --------------------------------
MOVE_DOWN
LDA MON_GBASH ; TRY IT FIRST, BY FGH=FGH+1
ADC #4 ; GBASH = PPPFGHCD
CON_04 = *-1 ; (( CONSTANT ))
BIT CON_1C ; IS FGH FIELD NOW ZERO?
BNE UD_1 ; NO, SO WE ARE FINISHED
; YES, RIPPLE THE CARRY AS HIGH
; AS NECESSARY
ASL MON_GBASL ; LOOK AT "E" BIT
BCC L_CON_04_2 ; NOW ZERO; MAKE IT 1 AND LEAVE
ADC #$E0 ; CARRY = 1, SO ADDS $E1
CLC ; IS "CD" NOT ZERO?
BIT CON_04 ; TESTS BIT 2 FOR CARRY OUT OF "CD"
BEQ L_CON_04_3 ; NO CARRY, FINISHED
; INCREMENT "AB" THEN
; 0000 --> 0101
; 0101 --> 1010
; 1010 --> WRAP AROUND TO LINE 0
LDA MON_GBASL ; 0000 0101 1010
ADC #$50 ; 0101 1010 1111
EOR #$F0 ; 1010 0101 0000
BEQ L_CON_04_1 ;
EOR #$F0 ; 0101 1010
L_CON_04_1 STA MON_GBASL ; NEW ABAB0000
LDA HGR_PAGE ; WRAP AROUND TO LINE ZERO OF GROUP
BCC L_CON_04_3 ; ...ALWAYS
L_CON_04_2 ADC #$E0
L_CON_04_3 ROR MON_GBASL
BCC UD_1 ; ...ALWAYS
; --------------------------------
; HLINRL IS NEVER CALLED BY APPLESOFT
;
; ENTER WITH: (A,X) = DX FROM CURRENT POINT
; (Y) = DY FROM CURRENT POINT
; --------------------------------
HLINRL PHA ; SAVE (A)
LDA #0 ; CLEAR CURRENT POINT SO HGLIN WILL
STA HGR_X ; ACT RELATIVELY
STA HGR_X+1 ;
STA HGR_Y ;
PLA ; RESTORE (A)
; --------------------------------
; DRAW LINE FROM LAST PLOTTED POINT TO (A,X),(Y)
;
; ENTER WITH: (A,X) = X OF TARGET POINT
; (Y) = Y OF TARGET POINT
; --------------------------------
HGLIN PHA ; COMPUTE DX = X- X0
SEC
SBC HGR_X
PHA
TXA
SBC HGR_X+1
STA HGR_QUADRANT ; SAVE DX SIGN (+ = RIGHT, - = LEFT)
BCS L_HGLIN_1 ; NOW FIND ABS (DX)
PLA ; FORMS 2'S COMPLEMENT
EOR #$FF
ADC #1
PHA
LDA #0
SBC HGR_QUADRANT
L_HGLIN_1 STA HGR_DX+1
STA HGR_E+1 ; INIT HGR.E TO ABS(X-X0)
PLA
STA HGR_DX
STA HGR_E
PLA
STA HGR_X ; TARGET X POINT
STX HGR_X+1 ;
TYA ; TARGET Y POINT
CLC ; COMPUTE DY = Y-HGR.Y
SBC HGR_Y ; AND SAVE -ABS(Y-HGR.Y)-1 IN HGR.DY
BCC L_HGLIN_2 ; (SO + MEANS UP, - MEANS DOWN)
EOR #$FF ; 2'S COMPLEMENT OF DY
ADC #$FE ;
L_HGLIN_2 STA HGR_DY ;
STY HGR_Y ; TARGET Y POINT
ROR HGR_QUADRANT ; SHIFT Y-DIRECTION INTO QUADRANT
SEC ; COUNT = DX -(-DY) = # OF DOTS NEEDED
SBC HGR_DX ;
TAX ; COUNTL IS IN X-REG
LDA #$FF
SBC HGR_DX+1
STA HGR_COUNT
LDY HGR_HORIZ ; HORIZONTAL INDEX
BCS MOVEX2 ; ...ALWAYS
; --------------------------------
; MOVE LEFT OR RIGHT ONE PIXEL
; (A) BIT 6 HAS DIRECTION
; --------------------------------
MOVEX ASL ; PUT BIT 6 INTO SIGN POSITION
JSR MOVE_LEFT_OR_RIGHT
SEC
; --------------------------------
; DRAW LINE NOW
; --------------------------------
MOVEX2 LDA HGR_E ; CARRY IS SET
ADC HGR_DY ; E = E-DELTY
STA HGR_E ; NOTE: DY IS (-DELTA Y)-1
LDA HGR_E+1 ; CARRY CLR IF HGR.E GOES NEGATIVE
SBC #0
L_MOVEX2_1 STA HGR_E+1
LDA (MON_GBASL),Y
EOR HGR_BITS ; PLOT A DOT
AND MON_HMASK
EOR (MON_GBASL),Y
STA (MON_GBASL),Y
INX ; FINISHED ALL THE DOTS?
BNE L_MOVEX2_2 ; NO
INC HGR_COUNT ; TEST REST OF COUNT
BEQ RTS_22 ; YES, FINISHED.
L_MOVEX2_2 LDA HGR_QUADRANT ; TEST DIRECTION
BCS MOVEX ; NEXT MOVE IS IN THE X DIRECTION
JSR MOVE_UP_OR_DOWN ; IF CLR, NEG, MOVE
CLC ; E = E+DX
LDA HGR_E
ADC HGR_DX
STA HGR_E
LDA HGR_E+1
ADC HGR_DX+1
BVC L_MOVEX2_1 ; ...ALWAYS
; --------------------------------
MSKTBL ASM_DATA(%10000001)
ASM_DATA(%10000010)
ASM_DATA(%10000100)
ASM_DATA(%10001000)
ASM_DATA(%10010000)
ASM_DATA(%10100000)
ASM_DATA(%11000000)
; --------------------------------
CON_1C ASM_DATA(%00011100) ; MASK FOR "FGH" BITS
; --------------------------------
; --------------------------------
; TABLE OF COS(90*X/16 DEGREES)*$100 - 1
; WITH ONE BYTE PRECISION, X=0 TO 16:
; --------------------------------
COSINE_TABLE ASM_DATA($FF,$FE,$FA,$F4,$EC,$E1,$D4,$C5)
ASM_DATA($B4,$A1,$8D,$78,$61,$49,$31,$18)
ASM_DATA($FF)
; --------------------------------
; HFIND -- CALCULATES CURRENT POSITION OF HI-RES CURSOR
; (NOT CALLED BY ANY APPLESOFT ROUTINE)
;
; CALCULATE Y-COORD FROM GBASL,H
; AND X-COORD FROM HORIZ AND HMASK
; --------------------------------
HFIND LDA MON_GBASL ; GBASL = EABAB000
ASL ; E INTO CARRY
LDA MON_GBASH ; GBASH = PPPFGHCD
AND #3 ; 000000CD
ROL ; 00000CDE
ORA MON_GBASL ; EABABCDE
ASL ; ABABCDE0
ASL ; BABCDE00
ASL ; ABCDE000
STA HGR_Y ; ALL BUT FGH
LDA MON_GBASH ; PPPFGHCD
LSR ; 0PPPFGHC
LSR ; 00PPPFGH
AND #7 ; 00000FGH
ORA HGR_Y ; ABCDEFGH
STA HGR_Y ; THAT TAKES CARE OF Y-COORDINATE!
LDA HGR_HORIZ ; X = 7*HORIZ + BIT POS. IN HMASK
ASL ; MULTIPLY BY 7
ADC HGR_HORIZ ; 3* SO FAR
ASL ; 6*
TAX ; SINCE 7* MIGHT NOT FIT IN 1 BYTE,
; WAIT TILL LATER FOR LAST ADD
DEX ;
LDA MON_HMASK ; NOW FIND BIT POSITION IN HMASK
AND #$7F ; ONLY LOOK AT LOW SEVEN
L_HFIND_1 INX ; COUNT A SHIFT
LSR ;
BNE L_HFIND_1 ; STILL IN THERE
STA HGR_X+1 ; ZERO TO HI-BYTE
TXA ; 6*HORIZ+LOG2(HMASK)
CLC ; ADD HORIZ ONE MORE TIME
ADC HGR_HORIZ ; 7*HORIZ+LOG2(HMASK)
BCC L_HFIND_2 ; UPPER BYTE = 0
INC HGR_X+1 ; UPPER BYTE = 1
L_HFIND_2 STA HGR_X ; STORE LOWER BYTE
RTS_22 RTS
; --------------------------------
; DRAW A SHAPE
;
; (Y,X) = SHAPE STARTING ADDRESS
; (A) = ROTATION (0-3F)
; --------------------------------
; APPLESOFT DOES NOT CALL DRAW0
; --------------------------------
DRAW0 STX HGR_SHAPE ; SAVE SHAPE ADDRESS
STY HGR_SHAPE+1
; --------------------------------
; APPLESOFT ENTERS HERE
; --------------------------------
DRAW1 TAX ; SAVE ROTATION (0-$3F)
LSR ; DIVIDE ROTATION BY 16 TO GET
LSR ; QUADRANT (0=UP, 1=RT, 2=DWN, 3=LFT)
LSR
LSR
STA HGR_QUADRANT
TXA ; USE LOW 4 BITS OF ROTATION TO INDEX
AND #$0F ; THE TRIG TABLE
TAX
LDY COSINE_TABLE,X ; SAVE COSINE IN HGR.DX
STY HGR_DX ;
EOR #$F ; AND SINE IN DY
TAX
LDY COSINE_TABLE+1,X
INY
STY HGR_DY
LDY HGR_HORIZ ; INDEX FROM GBASL,H TO BYTE WE'RE IN
LDX #0
STX HGR_COLLISIONS ; CLEAR COLLISION COUNTER
LDA (HGR_SHAPE,X) ; GET FIRST BYTE OF SHAPE DEFN
L_DRAW1_1 STA HGR_DX+1 ; KEEP SHAPE BYTE IN HGR.DX+1
LDX #$80 ; INITIAL VALUES FOR FRACTIONAL VECTORS
STX HGR_E ; L_DRAW1_5 IN COSINE COMPONENT
STX HGR_E+1 ; L_DRAW1_5 IN SINE COMPONENT
LDX HGR_SCALE ; SCALE FACTOR
L_DRAW1_2 LDA HGR_E ; ADD COSINE VALUE TO X-VALUE
SEC ; IF >= 1, THEN DRAW
ADC HGR_DX ;
STA HGR_E ; ONLY SAVE FRACTIONAL PART
BCC L_DRAW1_3 ; NO INTEGRAL PART
JSR LRUD1 ; TIME TO PLOT COSINE COMPONENT
CLC ;
L_DRAW1_3 LDA HGR_E+1 ; ADD SINE VALUE TO Y-VALUE
ADC HGR_DY ; IF >= 1, THEN DRAW
STA HGR_E+1 ; ONLY SAVE FRACTIONAL PART
BCC L_DRAW1_4 ; NO INTEGRAL PART
JSR LRUD2 ; TIME TO PLOT SINE COMPONENT
L_DRAW1_4 DEX ; LOOP ON SCALE FACTOR.
BNE L_DRAW1_2 ; STILL ON SAME SHAPE ITEM
LDA HGR_DX+1 ; GET NEXT SHAPE ITEM
LSR ; NEXT 3 BIT VECTOR
LSR ;
LSR ;
BNE L_DRAW1_1 ; MORE IN THIS SHAPE BYTE
INC HGR_SHAPE ; GO TO NEXT SHAPE BYTE
BNE L_DRAW1_5
INC HGR_SHAPE+1
L_DRAW1_5 LDA (HGR_SHAPE,X) ; NEXT BYTE OF SHAPE DEFINITION
BNE L_DRAW1_1 ; PROCESS IF NOT ZERO
RTS ; FINISHED
; --------------------------------
; XDRAW A SHAPE (SAME AS DRAW, EXCEPT TOGGLES SCREEN)
;
; (Y,X) = SHAPE STARTING ADDRESS
; (A) = ROTATION (0-3F)
; --------------------------------
; APPLESOFT DOES NOT CALL XDRAW0
; --------------------------------
XDRAW0 STX HGR_SHAPE ; SAVE SHAPE ADDRESS
STY HGR_SHAPE+1
; --------------------------------
; APPLESOFT ENTERS HERE
; --------------------------------
XDRAW1 TAX ; SAVE ROTATION (0-$3F)
LSR ; DIVIDE ROTATION BY 16 TO GET
LSR ; QUADRANT (0=UP, 1=RT, 2=DWN, 3=LFT)
LSR
LSR
STA HGR_QUADRANT
TXA ; USE LOW 4 BITS OF ROTATION TO INDEX
AND #$0F ; THE TRIG TABLE
TAX
LDY COSINE_TABLE,X ; SAVE COSINE IN HGR.DX
STY HGR_DX ;
EOR #$F ; AND SINE IN DY
TAX
LDY COSINE_TABLE+1,X
INY
STY HGR_DY
LDY HGR_HORIZ ; INDEX FROM GBASL,H TO BYTE WE'RE IN
LDX #0
STX HGR_COLLISIONS ; CLEAR COLLISION COUNTER
LDA (HGR_SHAPE,X) ; GET FIRST BYTE OF SHAPE DEFN
L_XDRAW1_1 STA HGR_DX+1 ; KEEP SHAPE BYTE IN HGR.DX+1
LDX #$80 ; INITIAL VALUES FOR FRACTIONAL VECTORS
STX HGR_E ; L_XDRAW1_5 IN COSINE COMPONENT
STX HGR_E+1 ; L_XDRAW1_5 IN SINE COMPONENT
LDX HGR_SCALE ; SCALE FACTOR
L_XDRAW1_2 LDA HGR_E ; ADD COSINE VALUE TO X-VALUE
SEC ; IF >= 1, THEN DRAW
ADC HGR_DX ;
STA HGR_E ; ONLY SAVE FRACTIONAL PART
BCC L_XDRAW1_3 ; NO INTEGRAL PART
JSR LRUDX1 ; TIME TO PLOT COSINE COMPONENT
CLC ;
L_XDRAW1_3 LDA HGR_E+1 ; ADD SINE VALUE TO Y-VALUE
ADC HGR_DY ; IF >= 1, THEN DRAW
STA HGR_E+1 ; ONLY SAVE FRACTIONAL PART
BCC L_XDRAW1_4 ; NO INTEGRAL PART
JSR LRUDX2 ; TIME TO PLOT SINE COMPONENT
L_XDRAW1_4 DEX ; LOOP ON SCALE FACTOR.
BNE L_XDRAW1_2 ; STILL ON SAME SHAPE ITEM
LDA HGR_DX+1 ; GET NEXT SHAPE ITEM
LSR ; NEXT 3 BIT VECTOR
LSR ;
LSR ;
BNE L_XDRAW1_1 ; MORE IN THIS SHAPE BYTE
INC HGR_SHAPE ; GO TO NEXT SHAPE BYTE
BNE L_XDRAW1_5
INC HGR_SHAPE+1
L_XDRAW1_5 LDA (HGR_SHAPE,X) ; NEXT BYTE OF SHAPE DEFINITION
BNE L_XDRAW1_1 ; PROCESS IF NOT ZERO
RTS ; FINISHED
; --------------------------------
; GET HI-RES PLOTTING COORDINATES (0-279,0-191) FROM
; TXTPTR. LEAVE REGISTERS SET UP FOR HPOSN:
; (Y,X)=X-COORD
; (A) =Y-COORD
; --------------------------------
HFNS JSR FRMNUM ; EVALUATE EXPRESSION, MUST BE NUMERIC
JSR GETADR ; CONVERT TO 2-BYTE INTEGER IN LINNUM
LDY LINNUM+1 ; GET HORIZ COOR IN X,Y
LDX LINNUM ;
CPY #>280 ; MAKE SURE IT IS < 280
BCC L_HFNS_1 ; IN RANGE
BNE GGERR ;
CPX #<280 ;
BCS GGERR ;
L_HFNS_1 TXA ; SAVE HORIZ COOR ON STACK
PHA ;
TYA ;
PHA ;
LDA #LOCHAR(`,') ; REQUIRE A COMMA
JSR SYNCHR ;
JSR GETBYT ; EVAL EXP TO SINGLE BYTE IN X-REG
CPX #192 ; CHECK FOR RANGE
BCS GGERR ; TOO BIG
STX FAC ; SAVE Y-COORD
PLA ; RETRIEVE HORIZONTAL COORDINATE
TAY ;
PLA ;
TAX ;
LDA FAC ; AND VERTICAL COORDINATE
RTS ;
; --------------------------------
GGERR JMP GOERR ; ILLEGAL QUANTITY ERROR
; --------------------------------
; "HCOLOR=" STATEMENT
; --------------------------------
HCOLOR JSR GETBYT ; EVAL EXP TO SINGLE BYTE IN X
CPX #8 ; VALUE MUST BE 0-7
BCS GGERR ; TOO BIG
LDA COLORTBL,X ; GET COLOR PATTERN
STA HGR_COLOR
RTS_23 RTS
; --------------------------------
COLORTBL ASM_DATA(%00000000)
ASM_DATA(%00101010)
ASM_DATA(%01010101)
ASM_DATA(%01111111)
ASM_DATA(%00000000 | %10000000)
ASM_DATA(%00101010 | %10000000)
ASM_DATA(%01010101 | %10000000)
ASM_DATA(%01111111 | %10000000)
; --------------------------------
; "HPLOT" STATEMENT
;
; HPLOT X,Y
; HPLOT TO X,Y
; HPLOT X1,Y1 TO X2,Y2
; --------------------------------
HPLOT CMP #TOKEN_TO ; "PLOT TO" FORM?
BEQ L_HPLOT_2 ; YES, START FROM CURRENT LOCATION
JSR HFNS ; NO, GET STARTING POINT OF LINE
JSR HPLOT0 ; PLOT THE POINT, AND SET UP FOR
; DRAWING A LINE FROM THAT POINT
L_HPLOT_1 JSR CHRGOT ; CHARACTER AT END OF EXPRESSION
CMP #TOKEN_TO ; IS A LINE SPECIFIED?
BNE RTS_23 ; NO, EXIT
L_HPLOT_2 JSR SYNCHR ; YES. ADV. TXTPTR (WHY NOT CHRGET)
JSR HFNS ; GET COORDINATES OF LINE END
STY DSCTMP ; SET UP FOR LINE
TAY ;
TXA ;
LDX DSCTMP ;
JSR HGLIN ; PLOT LINE
JMP L_HPLOT_1 ; LOOP TILL NO MORE "TO" PHRASES
; --------------------------------
; "ROT=" STATEMENT
; --------------------------------
ROT JSR GETBYT ; EVAL EXP TO A BYTE IN X-REG
STX HGR_ROTATION
RTS
; --------------------------------
; "SCALE=" STATEMENT
; --------------------------------
SCALE JSR GETBYT ; EVAL EXP TO A BYTE IN X-REG
STX HGR_SCALE
RTS
; --------------------------------
; SET UP FOR DRAW AND XDRAW
; --------------------------------
DRWPNT JSR GETBYT ; GET SHAPE NUMBER IN X-REG
LDA HGR_SHAPE_PNTR ; SEARCH FOR THAT SHAPE
STA HGR_SHAPE ; SET UP PNTR TO BEGINNING OF TABLE
LDA HGR_SHAPE_PNTR+1
STA HGR_SHAPE+1
TXA
LDX #0
CMP (HGR_SHAPE,X) ; COMPARE TO # OF SHAPES IN TABLE
BEQ L_DRWPNT_1 ; LAST SHAPE IN TABLE
BCS GGERR ; SHAPE # TOO LARGE
L_DRWPNT_1 ASL ; DOUBLE SHAPE# TO MAKE AN INDEX
BCC L_DRWPNT_2 ; ADD 256 IF SHAPE # > 127
INC HGR_SHAPE+1
CLC
L_DRWPNT_2 TAY ; USE INDEX TO LOOK UP OFFSET FOR SHAPE
LDA (HGR_SHAPE),Y ; IN OFFSET TABLE
ADC HGR_SHAPE
TAX
INY
LDA (HGR_SHAPE),Y
ADC HGR_SHAPE_PNTR+1
STA HGR_SHAPE+1 ; SAVE ADDRESS OF SHAPE
STX HGR_SHAPE
JSR CHRGOT ; IS THERE ANY "AT" PHRASE?
CMP #TOKENDB ;
BNE L_DRWPNT_3 ; NO, DRAW RIGHT WHERE WE ARE
JSR SYNCHR ; SCAN OVER "AT"
JSR HFNS ; GET X- AND Y-COORDS TO START DRAWING AT
JSR HPOSN ; SET UP CURSOR THERE
L_DRWPNT_3 LDA HGR_ROTATION ; ROTATION VALUE
RTS
; --------------------------------
; "DRAW" STATEMENT
; --------------------------------
DRAW JSR DRWPNT
JMP DRAW1
; --------------------------------
; "XDRAW" STATEMENT
; --------------------------------
XDRAW JSR DRWPNT
JMP XDRAW1
; --------------------------------
; "SHLOAD" STATEMENT
;
; READS A SHAPE TABLE FROM CASSETTE TAPE
; TO A POSITION JUST BELOW HIMEM.
; HIMEM IS THEN MOVED TO JUST BELOW THE TABLE
; --------------------------------
SHLOAD LDA #>LINNUM ; SET UP TO READ TWO BYTES
STA MON_A1H ; INTO LINNUM,LINNUM+1
STA MON_A2H ;
LDY #LINNUM ;
STY MON_A1L ;
INY ; LINNUM+1
STY MON_A2L ;
JSR MON_READ ; READ TAPE
CLC ; SETUP TO READ (LINNUM) BYTES
LDA MEMSIZ ; ENDING AT HIMEM-1
TAX ;
DEX ; FORMING HIMEM-1
STX MON_A2L ;
SBC LINNUM ; FORMING HIMEM-(LINNUM)
PHA ;
LDA MEMSIZ+1 ;
TAY ;
INX ; SEE IF HIMEM LOW-BYTE WAS ZERO
BNE L_SHLOAD_1 ; NO
DEY ; YES, HAVE TO DECREMENT HIGH BYTE
L_SHLOAD_1 STY MON_A2H ;
SBC LINNUM+1 ;
CMP STREND+1 ; RUNNING INTO VARIABLES?
BCC L_SHLOAD_2 ; YES, OUT OF MEMORY
BNE L_SHLOAD_3 ; NO, STILL ROOM
L_SHLOAD_2 JMP MEMERR ; MEM FULL ERR
L_SHLOAD_3 STA MEMSIZ+1 ;
STA FRETOP+1 ; CLEAR STRING SPACE
STA MON_A1H ; (BUT NAMES ARE STILL IN VARTBL!)
STA HGR_SHAPE_PNTR+1
PLA
STA HGR_SHAPE_PNTR
STA MEMSIZ
STA FRETOP
STA MON_A1L
JSR MON_RD2BIT ; READ TO TAPE TRANSITIONS
LDA #3 ; SHORT DELAY FOR INTERMEDIATE HEADER
JMP MON_READ2 ; READ SHAPES
; --------------------------------
; CALLED FROM STORE AND RECALL
; --------------------------------
TAPEPNT
CLC
LDA LOWTR
ADC LINNUM
STA MON_A2L
LDA LOWTR+1
ADC LINNUM+1
STA MON_A2H
LDY #4
LDA (LOWTR),Y
JSR GETARY2
LDA HIGHDS
STA MON_A1L
LDA HIGHDS+1
STA MON_A1H
RTS
; --------------------------------
; CALLED FROM STORE AND RECALL
; --------------------------------
GETARYPT
LDA #$40
STA SUBFLG
JSR PTRGET
LDA #0
STA SUBFLG
JMP VARTIO
; --------------------------------
; "HTAB" STATEMENT
;
; NOTE THAT IF WNDLEFT IS NOT 0, HTAB CAN PRINT
; OUTSIDE THE SCREEN (EG., IN THE PROGRAM)
; --------------------------------
HTAB JSR GETBYT
DEX
TXA
L_HTAB_1 CMP #40
BCC L_HTAB_2
SBC #40
PHA
JSR CRDO
PLA
JMP L_HTAB_1
L_HTAB_2 STA MON_CH
RTS
; --------------------------------
HIASCII(`KRW') ; UNKNOWN