z80-python-emulator/roms/basic.asm

4342 lines
197 KiB
NASM
Raw Normal View History

2014-08-29 23:48:33 +02:00
;==================================================================================
; The updates to the original BASIC within this file are copyright Grant Searle
;
; You have permission to use this for NON COMMERCIAL USE ONLY
; If you wish to use it elsewhere, please include an acknowledgement to myself.
;
; http://searle.hostei.com/grant/index.html
;
; eMail: home.micros01@btinternet.com
;
; If the above don't work, please perform an Internet search to see if I have
; updated the web page hosting service.
;
;==================================================================================
; NASCOM ROM BASIC Ver 4.7, (C) 1978 Microsoft
; Scanned from source published in 80-BUS NEWS from Vol 2, Issue 3
; (May-June 1983) to Vol 3, Issue 3 (May-June 1984)
; Adapted for the freeware Zilog Macro Assembler 2.10 to produce
; the original ROM code (checksum A934H). PA
; GENERAL EQUATES
CTRLC .EQU 03H ; Control "C"
CTRLG .EQU 07H ; Control "G"
BKSP .EQU 08H ; Back space
LF .EQU 0AH ; Line feed
CS .EQU 0CH ; Clear screen
CR .EQU 0DH ; Carriage return
CTRLO .EQU 0FH ; Control "O"
CTRLQ .EQU 11H ; Control "Q"
CTRLR .EQU 12H ; Control "R"
CTRLS .EQU 13H ; Control "S"
CTRLU .EQU 15H ; Control "U"
ESC .EQU 1BH ; Escape
DEL .EQU 7FH ; Delete
; BASIC WORK SPACE LOCATIONS
WRKSPC .EQU 2045H ; BASIC Work space
USR .EQU WRKSPC+3H ; "USR (x)" jump
OUTSUB .EQU WRKSPC+6H ; "OUT p,n"
OTPORT .EQU WRKSPC+7H ; Port (p)
DIVSUP .EQU WRKSPC+9H ; Division support routine
DIV1 .EQU WRKSPC+0AH ; <- Values
DIV2 .EQU WRKSPC+0EH ; <- to
DIV3 .EQU WRKSPC+12H ; <- be
DIV4 .EQU WRKSPC+15H ; <-inserted
SEED .EQU WRKSPC+17H ; Random number seed
LSTRND .EQU WRKSPC+3AH ; Last random number
INPSUB .EQU WRKSPC+3EH ; #INP (x)" Routine
INPORT .EQU WRKSPC+3FH ; PORT (x)
NULLS .EQU WRKSPC+41H ; Number of nulls
LWIDTH .EQU WRKSPC+42H ; Terminal width
COMMAN .EQU WRKSPC+43H ; Width for commas
NULFLG .EQU WRKSPC+44H ; Null after input byte flag
CTLOFG .EQU WRKSPC+45H ; Control "O" flag
LINESC .EQU WRKSPC+46H ; Lines counter
LINESN .EQU WRKSPC+48H ; Lines number
CHKSUM .EQU WRKSPC+4AH ; Array load/save check sum
NMIFLG .EQU WRKSPC+4CH ; Flag for NMI break routine
BRKFLG .EQU WRKSPC+4DH ; Break flag
RINPUT .EQU WRKSPC+4EH ; Input reflection
POINT .EQU WRKSPC+51H ; "POINT" reflection (unused)
PSET .EQU WRKSPC+54H ; "SET" reflection
RESET .EQU WRKSPC+57H ; "RESET" reflection
STRSPC .EQU WRKSPC+5AH ; Bottom of string space
LINEAT .EQU WRKSPC+5CH ; Current line number
BASTXT .EQU WRKSPC+5EH ; Pointer to start of program
BUFFER .EQU WRKSPC+61H ; Input buffer
STACK .EQU WRKSPC+66H ; Initial stack
CURPOS .EQU WRKSPC+0ABH ; Character position on line
LCRFLG .EQU WRKSPC+0ACH ; Locate/Create flag
TYPE .EQU WRKSPC+0ADH ; Data type flag
DATFLG .EQU WRKSPC+0AEH ; Literal statement flag
LSTRAM .EQU WRKSPC+0AFH ; Last available RAM
TMSTPT .EQU WRKSPC+0B1H ; Temporary string pointer
TMSTPL .EQU WRKSPC+0B3H ; Temporary string pool
TMPSTR .EQU WRKSPC+0BFH ; Temporary string
STRBOT .EQU WRKSPC+0C3H ; Bottom of string space
CUROPR .EQU WRKSPC+0C5H ; Current operator in EVAL
LOOPST .EQU WRKSPC+0C7H ; First statement of loop
DATLIN .EQU WRKSPC+0C9H ; Line of current DATA item
FORFLG .EQU WRKSPC+0CBH ; "FOR" loop flag
LSTBIN .EQU WRKSPC+0CCH ; Last byte entered
READFG .EQU WRKSPC+0CDH ; Read/Input flag
BRKLIN .EQU WRKSPC+0CEH ; Line of break
NXTOPR .EQU WRKSPC+0D0H ; Next operator in EVAL
ERRLIN .EQU WRKSPC+0D2H ; Line of error
CONTAD .EQU WRKSPC+0D4H ; Where to CONTinue
PROGND .EQU WRKSPC+0D6H ; End of program
VAREND .EQU WRKSPC+0D8H ; End of variables
ARREND .EQU WRKSPC+0DAH ; End of arrays
NXTDAT .EQU WRKSPC+0DCH ; Next data item
FNRGNM .EQU WRKSPC+0DEH ; Name of FN argument
FNARG .EQU WRKSPC+0E0H ; FN argument value
FPREG .EQU WRKSPC+0E4H ; Floating point register
FPEXP .EQU FPREG+3 ; Floating point exponent
SGNRES .EQU WRKSPC+0E8H ; Sign of result
PBUFF .EQU WRKSPC+0E9H ; Number print buffer
MULVAL .EQU WRKSPC+0F6H ; Multiplier
PROGST .EQU WRKSPC+0F9H ; Start of program text area
STLOOK .EQU WRKSPC+15DH ; Start of memory test
; BASIC ERROR CODE VALUES
NF .EQU 00H ; NEXT without FOR
SN .EQU 02H ; Syntax error
RG .EQU 04H ; RETURN without GOSUB
OD .EQU 06H ; Out of DATA
FC .EQU 08H ; Function call error
OV .EQU 0AH ; Overflow
OM .EQU 0CH ; Out of memory
UL .EQU 0EH ; Undefined line number
BS .EQU 10H ; Bad subscript
DD .EQU 12H ; Re-DIMensioned array
DZ .EQU 14H ; Division by zero (/0)
ID .EQU 16H ; Illegal direct
TM .EQU 18H ; Type miss-match
OS .EQU 1AH ; Out of string space
LS .EQU 1CH ; String too long
ST .EQU 1EH ; String formula too complex
CN .EQU 20H ; Can't CONTinue
UF .EQU 22H ; UnDEFined FN function
MO .EQU 24H ; Missing operand
HX .EQU 26H ; HEX error
BN .EQU 28H ; BIN error
.ORG 00150H
COLD: JP STARTB ; Jump for cold start
WARM: JP WARMST ; Jump for warm start
STARTB:
LD IX,0 ; Flag cold start
JP CSTART ; Jump to initialise
.WORD DEINT ; Get integer -32768 to 32767
.WORD ABPASS ; Return integer in AB
CSTART: LD HL,WRKSPC ; Start of workspace RAM
LD SP,HL ; Set up a temporary stack
JP INITST ; Go to initialise
INIT: LD DE,INITAB ; Initialise workspace
LD B,INITBE-INITAB+3; Bytes to copy
LD HL,WRKSPC ; Into workspace RAM
COPY: LD A,(DE) ; Get source
LD (HL),A ; To destination
INC HL ; Next destination
INC DE ; Next source
DEC B ; Count bytes
JP NZ,COPY ; More to move
LD SP,HL ; Temporary stack
CALL CLREG ; Clear registers and stack
CALL PRNTCRLF ; Output CRLF
LD (BUFFER+72+1),A ; Mark end of buffer
LD (PROGST),A ; Initialise program area
MSIZE: LD HL,MEMMSG ; Point to message
CALL PRS ; Output "Memory size"
CALL PROMPT ; Get input with '?'
CALL GETCHR ; Get next character
OR A ; Set flags
JP NZ,TSTMEM ; If number - Test if RAM there
LD HL,STLOOK ; Point to start of RAM
MLOOP: INC HL ; Next byte
LD A,H ; Above address FFFF ?
OR L
JP Z,SETTOP ; Yes - 64K RAM
LD A,(HL) ; Get contents
LD B,A ; Save it
CPL ; Flip all bits
LD (HL),A ; Put it back
CP (HL) ; RAM there if same
LD (HL),B ; Restore old contents
JP Z,MLOOP ; If RAM - test next byte
JP SETTOP ; Top of RAM found
TSTMEM: CALL ATOH ; Get high memory into DE
OR A ; Set flags on last byte
JP NZ,SNERR ; ?SN Error if bad character
EX DE,HL ; Address into HL
DEC HL ; Back one byte
LD A,11011001B ; Test byte
LD B,(HL) ; Get old contents
LD (HL),A ; Load test byte
CP (HL) ; RAM there if same
LD (HL),B ; Restore old contents
JP NZ,MSIZE ; Ask again if no RAM
SETTOP: DEC HL ; Back one byte
LD DE,STLOOK-1 ; See if enough RAM
CALL CPDEHL ; Compare DE with HL
JP C,MSIZE ; Ask again if not enough RAM
LD DE,0-50 ; 50 Bytes string space
LD (LSTRAM),HL ; Save last available RAM
ADD HL,DE ; Allocate string space
LD (STRSPC),HL ; Save string space
CALL CLRPTR ; Clear program area
LD HL,(STRSPC) ; Get end of memory
LD DE,0-17 ; Offset for free bytes
ADD HL,DE ; Adjust HL
LD DE,PROGST ; Start of program text
LD A,L ; Get LSB
SUB E ; Adjust it
LD L,A ; Re-save
LD A,H ; Get MSB
SBC A,D ; Adjust it
LD H,A ; Re-save
PUSH HL ; Save bytes free
LD HL,SIGNON ; Sign-on message
CALL PRS ; Output string
POP HL ; Get bytes free back
CALL PRNTHL ; Output amount of free memory
LD HL,BFREE ; " Bytes free" message
CALL PRS ; Output string
WARMST: LD SP,STACK ; Temporary stack
BRKRET: CALL CLREG ; Clear registers and stack
JP PRNTOK ; Go to get command line
BFREE: .BYTE " Bytes free",CR,LF,0,0
SIGNON: .BYTE "Z80 BASIC Ver 4.7b",CR,LF
.BYTE "Copyright ",40,"C",41
.BYTE " 1978 by Microsoft",CR,LF,0,0
MEMMSG: .BYTE "Memory top",0
; FUNCTION ADDRESS TABLE
FNCTAB: .WORD SGN
.WORD INT
.WORD ABS
.WORD USR
.WORD FRE
.WORD INP
.WORD POS
.WORD SQR
.WORD RND
.WORD LOG
.WORD EXP
.WORD COS
.WORD SIN
.WORD TAN
.WORD ATN
.WORD PEEK
.WORD DEEK
.WORD POINT
.WORD LEN
.WORD STR
.WORD VAL
.WORD ASC
.WORD CHR
.WORD HEX
.WORD BIN
.WORD LEFT
.WORD RIGHT
.WORD MID
; RESERVED WORD LIST
WORDS: .BYTE 'E'+80H,"ND"
.BYTE 'F'+80H,"OR"
.BYTE 'N'+80H,"EXT"
.BYTE 'D'+80H,"ATA"
.BYTE 'I'+80H,"NPUT"
.BYTE 'D'+80H,"IM"
.BYTE 'R'+80H,"EAD"
.BYTE 'L'+80H,"ET"
.BYTE 'G'+80H,"OTO"
.BYTE 'R'+80H,"UN"
.BYTE 'I'+80H,"F"
.BYTE 'R'+80H,"ESTORE"
.BYTE 'G'+80H,"OSUB"
.BYTE 'R'+80H,"ETURN"
.BYTE 'R'+80H,"EM"
.BYTE 'S'+80H,"TOP"
.BYTE 'O'+80H,"UT"
.BYTE 'O'+80H,"N"
.BYTE 'N'+80H,"ULL"
.BYTE 'W'+80H,"AIT"
.BYTE 'D'+80H,"EF"
.BYTE 'P'+80H,"OKE"
.BYTE 'D'+80H,"OKE"
.BYTE 'S'+80H,"CREEN"
.BYTE 'L'+80H,"INES"
.BYTE 'C'+80H,"LS"
.BYTE 'W'+80H,"IDTH"
.BYTE 'M'+80H,"ONITOR"
.BYTE 'S'+80H,"ET"
.BYTE 'R'+80H,"ESET"
.BYTE 'P'+80H,"RINT"
.BYTE 'C'+80H,"ONT"
.BYTE 'L'+80H,"IST"
.BYTE 'C'+80H,"LEAR"
.BYTE 'C'+80H,"LOAD"
.BYTE 'C'+80H,"SAVE"
.BYTE 'N'+80H,"EW"
.BYTE 'T'+80H,"AB("
.BYTE 'T'+80H,"O"
.BYTE 'F'+80H,"N"
.BYTE 'S'+80H,"PC("
.BYTE 'T'+80H,"HEN"
.BYTE 'N'+80H,"OT"
.BYTE 'S'+80H,"TEP"
.BYTE '+'+80H
.BYTE '-'+80H
.BYTE '*'+80H
.BYTE '/'+80H
.BYTE '^'+80H
.BYTE 'A'+80H,"ND"
.BYTE 'O'+80H,"R"
.BYTE '>'+80H
.BYTE '='+80H
.BYTE '<'+80H
.BYTE 'S'+80H,"GN"
.BYTE 'I'+80H,"NT"
.BYTE 'A'+80H,"BS"
.BYTE 'U'+80H,"SR"
.BYTE 'F'+80H,"RE"
.BYTE 'I'+80H,"NP"
.BYTE 'P'+80H,"OS"
.BYTE 'S'+80H,"QR"
.BYTE 'R'+80H,"ND"
.BYTE 'L'+80H,"OG"
.BYTE 'E'+80H,"XP"
.BYTE 'C'+80H,"OS"
.BYTE 'S'+80H,"IN"
.BYTE 'T'+80H,"AN"
.BYTE 'A'+80H,"TN"
.BYTE 'P'+80H,"EEK"
.BYTE 'D'+80H,"EEK"
.BYTE 'P'+80H,"OINT"
.BYTE 'L'+80H,"EN"
.BYTE 'S'+80H,"TR$"
.BYTE 'V'+80H,"AL"
.BYTE 'A'+80H,"SC"
.BYTE 'C'+80H,"HR$"
.BYTE 'H'+80H,"EX$"
.BYTE 'B'+80H,"IN$"
.BYTE 'L'+80H,"EFT$"
.BYTE 'R'+80H,"IGHT$"
.BYTE 'M'+80H,"ID$"
.BYTE 80H ; End of list marker
; KEYWORD ADDRESS TABLE
WORDTB: .WORD PEND
.WORD FOR
.WORD NEXT
.WORD DATA
.WORD INPUT
.WORD DIM
.WORD READ
.WORD LET
.WORD GOTO
.WORD RUN
.WORD IF
.WORD RESTOR
.WORD GOSUB
.WORD RETURN
.WORD REM
.WORD STOP
.WORD POUT
.WORD ON
.WORD NULL
.WORD WAIT
.WORD DEF
.WORD POKE
.WORD DOKE
.WORD REM
.WORD LINES
.WORD CLS
.WORD WIDTH
.WORD MONITR
.WORD PSET
.WORD RESET
.WORD PRINT
.WORD CONT
.WORD LIST
.WORD CLEAR
.WORD REM
.WORD REM
.WORD NEW
; RESERVED WORD TOKEN VALUES
ZEND .EQU 080H ; END
ZFOR .EQU 081H ; FOR
ZDATA .EQU 083H ; DATA
ZGOTO .EQU 088H ; GOTO
ZGOSUB .EQU 08CH ; GOSUB
ZREM .EQU 08EH ; REM
ZPRINT .EQU 09EH ; PRINT
ZNEW .EQU 0A4H ; NEW
ZTAB .EQU 0A5H ; TAB
ZTO .EQU 0A6H ; TO
ZFN .EQU 0A7H ; FN
ZSPC .EQU 0A8H ; SPC
ZTHEN .EQU 0A9H ; THEN
ZNOT .EQU 0AAH ; NOT
ZSTEP .EQU 0ABH ; STEP
ZPLUS .EQU 0ACH ; +
ZMINUS .EQU 0ADH ; -
ZTIMES .EQU 0AEH ; *
ZDIV .EQU 0AFH ; /
ZOR .EQU 0B2H ; OR
ZGTR .EQU 0B3H ; >
ZEQUAL .EQU 0B4H ; M
ZLTH .EQU 0B5H ; <
ZSGN .EQU 0B6H ; SGN
ZPOINT .EQU 0C7H ; POINT
ZLEFT .EQU 0CDH +2 ; LEFT$
; ARITHMETIC PRECEDENCE TABLE
PRITAB: .BYTE 79H ; Precedence value
.WORD PADD ; FPREG = <last> + FPREG
.BYTE 79H ; Precedence value
.WORD PSUB ; FPREG = <last> - FPREG
.BYTE 7CH ; Precedence value
.WORD MULT ; PPREG = <last> * FPREG
.BYTE 7CH ; Precedence value
.WORD DIV ; FPREG = <last> / FPREG
.BYTE 7FH ; Precedence value
.WORD POWER ; FPREG = <last> ^ FPREG
.BYTE 50H ; Precedence value
.WORD PAND ; FPREG = <last> AND FPREG
.BYTE 46H ; Precedence value
.WORD POR ; FPREG = <last> OR FPREG
; BASIC ERROR CODE LIST
ERRORS: .BYTE "NF" ; NEXT without FOR
.BYTE "SN" ; Syntax error
.BYTE "RG" ; RETURN without GOSUB
.BYTE "OD" ; Out of DATA
.BYTE "FC" ; Illegal function call
.BYTE "OV" ; Overflow error
.BYTE "OM" ; Out of memory
.BYTE "UL" ; Undefined line
.BYTE "BS" ; Bad subscript
.BYTE "DD" ; Re-DIMensioned array
.BYTE "/0" ; Division by zero
.BYTE "ID" ; Illegal direct
.BYTE "TM" ; Type mis-match
.BYTE "OS" ; Out of string space
.BYTE "LS" ; String too long
.BYTE "ST" ; String formula too complex
.BYTE "CN" ; Can't CONTinue
.BYTE "UF" ; Undefined FN function
.BYTE "MO" ; Missing operand
.BYTE "HX" ; HEX error
.BYTE "BN" ; BIN error
; INITIALISATION TABLE -------------------------------------------------------
INITAB: JP WARMST ; Warm start jump
JP FCERR ; "USR (X)" jump (Set to Error)
OUT (0),A ; "OUT p,n" skeleton
RET
SUB 0 ; Division support routine
LD L,A
LD A,H
SBC A,0
LD H,A
LD A,B
SBC A,0
LD B,A
LD A,0
RET
.BYTE 0,0,0 ; Random number seed table used by RND
.BYTE 035H,04AH,0CAH,099H ;-2.65145E+07
.BYTE 039H,01CH,076H,098H ; 1.61291E+07
.BYTE 022H,095H,0B3H,098H ;-1.17691E+07
.BYTE 00AH,0DDH,047H,098H ; 1.30983E+07
.BYTE 053H,0D1H,099H,099H ;-2-01612E+07
.BYTE 00AH,01AH,09FH,098H ;-1.04269E+07
.BYTE 065H,0BCH,0CDH,098H ;-1.34831E+07
.BYTE 0D6H,077H,03EH,098H ; 1.24825E+07
.BYTE 052H,0C7H,04FH,080H ; Last random number
IN A,(0) ; INP (x) skeleton
RET
.BYTE 1 ; POS (x) number (1)
.BYTE 255 ; Terminal width (255 = no auto CRLF)
.BYTE 28 ; Width for commas (3 columns)
.BYTE 0 ; No nulls after input bytes
.BYTE 0 ; Output enabled (^O off)
.WORD 20 ; Initial lines counter
.WORD 20 ; Initial lines number
.WORD 0 ; Array load/save check sum
.BYTE 0 ; Break not by NMI
.BYTE 0 ; Break flag
JP TTYLIN ; Input reflection (set to TTY)
JP $0000 ; POINT reflection unused
JP $0000 ; SET reflection
JP $0000 ; RESET reflection
.WORD STLOOK ; Temp string space
.WORD -2 ; Current line number (cold)
.WORD PROGST+1 ; Start of program text
INITBE:
; END OF INITIALISATION TABLE ---------------------------------------------------
ERRMSG: .BYTE " Error",0
INMSG: .BYTE " in ",0
ZERBYT .EQU $-1 ; A zero byte
OKMSG: .BYTE "Ok",CR,LF,0,0
BRKMSG: .BYTE "Break",0
BAKSTK: LD HL,4 ; Look for "FOR" block with
ADD HL,SP ; same index as specified
LOKFOR: LD A,(HL) ; Get block ID
INC HL ; Point to index address
CP ZFOR ; Is it a "FOR" token
RET NZ ; No - exit
LD C,(HL) ; BC = Address of "FOR" index
INC HL
LD B,(HL)
INC HL ; Point to sign of STEP
PUSH HL ; Save pointer to sign
LD L,C ; HL = address of "FOR" index
LD H,B
LD A,D ; See if an index was specified
OR E ; DE = 0 if no index specified
EX DE,HL ; Specified index into HL
JP Z,INDFND ; Skip if no index given
EX DE,HL ; Index back into DE
CALL CPDEHL ; Compare index with one given
INDFND: LD BC,16-3 ; Offset to next block
POP HL ; Restore pointer to sign
RET Z ; Return if block found
ADD HL,BC ; Point to next block
JP LOKFOR ; Keep on looking
MOVUP: CALL ENFMEM ; See if enough memory
MOVSTR: PUSH BC ; Save end of source
EX (SP),HL ; Swap source and dest" end
POP BC ; Get end of destination
MOVLP: CALL CPDEHL ; See if list moved
LD A,(HL) ; Get byte
LD (BC),A ; Move it
RET Z ; Exit if all done
DEC BC ; Next byte to move to
DEC HL ; Next byte to move
JP MOVLP ; Loop until all bytes moved
CHKSTK: PUSH HL ; Save code string address
LD HL,(ARREND) ; Lowest free memory
LD B,0 ; BC = Number of levels to test
ADD HL,BC ; 2 Bytes for each level
ADD HL,BC
.BYTE 3EH ; Skip "PUSH HL"
ENFMEM: PUSH HL ; Save code string address
LD A,0D0H ;LOW -48 ; 48 Bytes minimum RAM
SUB L
LD L,A
LD A,0FFH; HIGH (-48) ; 48 Bytes minimum RAM
SBC A,H
JP C,OMERR ; Not enough - ?OM Error
LD H,A
ADD HL,SP ; Test if stack is overflowed
POP HL ; Restore code string address
RET C ; Return if enough mmory
OMERR: LD E,OM ; ?OM Error
JP ERROR
DATSNR: LD HL,(DATLIN) ; Get line of current DATA item
LD (LINEAT),HL ; Save as current line
SNERR: LD E,SN ; ?SN Error
.BYTE 01H ; Skip "LD E,DZ"
DZERR: LD E,DZ ; ?/0 Error
.BYTE 01H ; Skip "LD E,NF"
NFERR: LD E,NF ; ?NF Error
.BYTE 01H ; Skip "LD E,DD"
DDERR: LD E,DD ; ?DD Error
.BYTE 01H ; Skip "LD E,UF"
UFERR: LD E,UF ; ?UF Error
.BYTE 01H ; Skip "LD E,OV
OVERR: LD E,OV ; ?OV Error
.BYTE 01H ; Skip "LD E,TM"
TMERR: LD E,TM ; ?TM Error
ERROR: CALL CLREG ; Clear registers and stack
LD (CTLOFG),A ; Enable output (A is 0)
CALL STTLIN ; Start new line
LD HL,ERRORS ; Point to error codes
LD D,A ; D = 0 (A is 0)
LD A,'?'
CALL OUTC ; Output '?'
ADD HL,DE ; Offset to correct error code
LD A,(HL) ; First character
CALL OUTC ; Output it
CALL GETCHR ; Get next character
CALL OUTC ; Output it
LD HL,ERRMSG ; "Error" message
ERRIN: CALL PRS ; Output message
LD HL,(LINEAT) ; Get line of error
LD DE,-2 ; Cold start error if -2
CALL CPDEHL ; See if cold start error
JP Z,CSTART ; Cold start error - Restart
LD A,H ; Was it a direct error?
AND L ; Line = -1 if direct error
INC A
CALL NZ,LINEIN ; No - output line of error
.BYTE 3EH ; Skip "POP BC"
POPNOK: POP BC ; Drop address in input buffer
PRNTOK: XOR A ; Output "Ok" and get command
LD (CTLOFG),A ; Enable output
CALL STTLIN ; Start new line
LD HL,OKMSG ; "Ok" message
CALL PRS ; Output "Ok"
GETCMD: LD HL,-1 ; Flag direct mode
LD (LINEAT),HL ; Save as current line
CALL GETLIN ; Get an input line
JP C,GETCMD ; Get line again if break
CALL GETCHR ; Get first character
INC A ; Test if end of line
DEC A ; Without affecting Carry
JP Z,GETCMD ; Nothing entered - Get another
PUSH AF ; Save Carry status
CALL ATOH ; Get line number into DE
PUSH DE ; Save line number
CALL CRUNCH ; Tokenise rest of line
LD B,A ; Length of tokenised line
POP DE ; Restore line number
POP AF ; Restore Carry
JP NC,EXCUTE ; No line number - Direct mode
PUSH DE ; Save line number
PUSH BC ; Save length of tokenised line
XOR A
LD (LSTBIN),A ; Clear last byte input
CALL GETCHR ; Get next character
OR A ; Set flags
PUSH AF ; And save them
CALL SRCHLN ; Search for line number in DE
JP C,LINFND ; Jump if line found
POP AF ; Get status
PUSH AF ; And re-save
JP Z,ULERR ; Nothing after number - Error
OR A ; Clear Carry
LINFND: PUSH BC ; Save address of line in prog
JP NC,INEWLN ; Line not found - Insert new
EX DE,HL ; Next line address in DE
LD HL,(PROGND) ; End of program
SFTPRG: LD A,(DE) ; Shift rest of program down
LD (BC),A
INC BC ; Next destination
INC DE ; Next source
CALL CPDEHL ; All done?
JP NZ,SFTPRG ; More to do
LD H,B ; HL - New end of program
LD L,C
LD (PROGND),HL ; Update end of program
INEWLN: POP DE ; Get address of line,
POP AF ; Get status
JP Z,SETPTR ; No text - Set up pointers
LD HL,(PROGND) ; Get end of program
EX (SP),HL ; Get length of input line
POP BC ; End of program to BC
ADD HL,BC ; Find new end
PUSH HL ; Save new end
CALL MOVUP ; Make space for line
POP HL ; Restore new end
LD (PROGND),HL ; Update end of program pointer
EX DE,HL ; Get line to move up in HL
LD (HL),H ; Save MSB
POP DE ; Get new line number
INC HL ; Skip pointer
INC HL
LD (HL),E ; Save LSB of line number
INC HL
LD (HL),D ; Save MSB of line number
INC HL ; To first byte in line
LD DE,BUFFER ; Copy buffer to program
MOVBUF: LD A,(DE) ; Get source
LD (HL),A ; Save destinations
INC HL ; Next source
INC DE ; Next destination
OR A ; Done?
JP NZ,MOVBUF ; No - Repeat
SETPTR: CALL RUNFST ; Set line pointers
INC HL ; To LSB of pointer
EX DE,HL ; Address to DE
PTRLP: LD H,D ; Address to HL
LD L,E
LD A,(HL) ; Get LSB of pointer
INC HL ; To MSB of pointer
OR (HL) ; Compare with MSB pointer
JP Z,GETCMD ; Get command line if end
INC HL ; To LSB of line number
INC HL ; Skip line number
INC HL ; Point to first byte in line
XOR A ; Looking for 00 byte
FNDEND: CP (HL) ; Found end of line?
INC HL ; Move to next byte
JP NZ,FNDEND ; No - Keep looking
EX DE,HL ; Next line address to HL
LD (HL),E ; Save LSB of pointer
INC HL
LD (HL),D ; Save MSB of pointer
JP PTRLP ; Do next line
SRCHLN: LD HL,(BASTXT) ; Start of program text
SRCHLP: LD B,H ; BC = Address to look at
LD C,L
LD A,(HL) ; Get address of next line
INC HL
OR (HL) ; End of program found?
DEC HL
RET Z ; Yes - Line not found
INC HL
INC HL
LD A,(HL) ; Get LSB of line number
INC HL
LD H,(HL) ; Get MSB of line number
LD L,A
CALL CPDEHL ; Compare with line in DE
LD H,B ; HL = Start of this line
LD L,C
LD A,(HL) ; Get LSB of next line address
INC HL
LD H,(HL) ; Get MSB of next line address
LD L,A ; Next line to HL
CCF
RET Z ; Lines found - Exit
CCF
RET NC ; Line not found,at line after
JP SRCHLP ; Keep looking
NEW: RET NZ ; Return if any more on line
CLRPTR: LD HL,(BASTXT) ; Point to start of program
XOR A ; Set program area to empty
LD (HL),A ; Save LSB = 00
INC HL
LD (HL),A ; Save MSB = 00
INC HL
LD (PROGND),HL ; Set program end
RUNFST: LD HL,(BASTXT) ; Clear all variables
DEC HL
INTVAR: LD (BRKLIN),HL ; Initialise RUN variables
LD HL,(LSTRAM) ; Get end of RAM
LD (STRBOT),HL ; Clear string space
XOR A
CALL RESTOR ; Reset DATA pointers
LD HL,(PROGND) ; Get end of program
LD (VAREND),HL ; Clear variables
LD (ARREND),HL ; Clear arrays
CLREG: POP BC ; Save return address
LD HL,(STRSPC) ; Get end of working RAN
LD SP,HL ; Set stack
LD HL,TMSTPL ; Temporary string pool
LD (TMSTPT),HL ; Reset temporary string ptr
XOR A ; A = 00
LD L,A ; HL = 0000
LD H,A
LD (CONTAD),HL ; No CONTinue
LD (FORFLG),A ; Clear FOR flag
LD (FNRGNM),HL ; Clear FN argument
PUSH HL ; HL = 0000
PUSH BC ; Put back return
DOAGN: LD HL,(BRKLIN) ; Get address of code to RUN
RET ; Return to execution driver
PROMPT: LD A,'?' ; '?'
CALL OUTC ; Output character
LD A,' ' ; Space
CALL OUTC ; Output character
JP RINPUT ; Get input line
CRUNCH: XOR A ; Tokenise line @ HL to BUFFER
LD (DATFLG),A ; Reset literal flag
LD C,2+3 ; 2 byte number and 3 nulls
LD DE,BUFFER ; Start of input buffer
CRNCLP: LD A,(HL) ; Get byte
CP ' ' ; Is it a space?
JP Z,MOVDIR ; Yes - Copy direct
LD B,A ; Save character
CP '"' ; Is it a quote?
JP Z,CPYLIT ; Yes - Copy literal string
OR A ; Is it end of buffer?
JP Z,ENDBUF ; Yes - End buffer
LD A,(DATFLG) ; Get data type
OR A ; Literal?
LD A,(HL) ; Get byte to copy
JP NZ,MOVDIR ; Literal - Copy direct
CP '?' ; Is it '?' short for PRINT
LD A,ZPRINT ; "PRINT" token
JP Z,MOVDIR ; Yes - replace it
LD A,(HL) ; Get byte again
CP '0' ; Is it less than '0'
JP C,FNDWRD ; Yes - Look for reserved words
CP 60; ";"+1 ; Is it "0123456789:;" ?
JP C,MOVDIR ; Yes - copy it direct
FNDWRD: PUSH DE ; Look for reserved words
LD DE,WORDS-1 ; Point to table
PUSH BC ; Save count
LD BC,RETNAD ; Where to return to
PUSH BC ; Save return address
LD B,ZEND-1 ; First token value -1
LD A,(HL) ; Get byte
CP 'a' ; Less than 'a' ?
JP C,SEARCH ; Yes - search for words
CP 'z'+1 ; Greater than 'z' ?
JP NC,SEARCH ; Yes - search for words
AND 01011111B ; Force upper case
LD (HL),A ; Replace byte
SEARCH: LD C,(HL) ; Search for a word
EX DE,HL
GETNXT: INC HL ; Get next reserved word
OR (HL) ; Start of word?
JP P,GETNXT ; No - move on
INC B ; Increment token value
LD A, (HL) ; Get byte from table
AND 01111111B ; Strip bit 7
RET Z ; Return if end of list
CP C ; Same character as in buffer?
JP NZ,GETNXT ; No - get next word
EX DE,HL
PUSH HL ; Save start of word
NXTBYT: INC DE ; Look through rest of word
LD A,(DE) ; Get byte from table
OR A ; End of word ?
JP M,MATCH ; Yes - Match found
LD C,A ; Save it
LD A,B ; Get token value
CP ZGOTO ; Is it "GOTO" token ?
JP NZ,NOSPC ; No - Don't allow spaces
CALL GETCHR ; Get next character
DEC HL ; Cancel increment from GETCHR
NOSPC: INC HL ; Next byte
LD A,(HL) ; Get byte
CP 'a' ; Less than 'a' ?
JP C,NOCHNG ; Yes - don't change
AND 01011111B ; Make upper case
NOCHNG: CP C ; Same as in buffer ?
JP Z,NXTBYT ; Yes - keep testing
POP HL ; Get back start of word
JP SEARCH ; Look at next word
MATCH: LD C,B ; Word found - Save token value
POP AF ; Throw away return
EX DE,HL
RET ; Return to "RETNAD"
RETNAD: EX DE,HL ; Get address in string
LD A,C ; Get token value
POP BC ; Restore buffer length
POP DE ; Get destination address
MOVDIR: INC HL ; Next source in buffer
LD (DE),A ; Put byte in buffer
INC DE ; Move up buffer
INC C ; Increment length of buffer
SUB ':' ; End of statement?
JP Z,SETLIT ; Jump if multi-statement line
CP ZDATA-3AH ; Is it DATA statement ?
JP NZ,TSTREM ; No - see if REM
SETLIT: LD (DATFLG),A ; Set literal flag
TSTREM: SUB ZREM-3AH ; Is it REM?
JP NZ,CRNCLP ; No - Leave flag
LD B,A ; Copy rest of buffer
NXTCHR: LD A,(HL) ; Get byte
OR A ; End of line ?
JP Z,ENDBUF ; Yes - Terminate buffer
CP B ; End of statement ?
JP Z,MOVDIR ; Yes - Get next one
CPYLIT: INC HL ; Move up source string
LD (DE),A ; Save in destination
INC C ; Increment length
INC DE ; Move up destination
JP NXTCHR ; Repeat
ENDBUF: LD HL,BUFFER-1 ; Point to start of buffer
LD (DE),A ; Mark end of buffer (A = 00)
INC DE
LD (DE),A ; A = 00
INC DE
LD (DE),A ; A = 00
RET
DODEL: LD A,(NULFLG) ; Get null flag status
OR A ; Is it zero?
LD A,0 ; Zero A - Leave flags
LD (NULFLG),A ; Zero null flag
JP NZ,ECHDEL ; Set - Echo it
DEC B ; Decrement length
JP Z,GETLIN ; Get line again if empty
CALL OUTC ; Output null character
.BYTE 3EH ; Skip "DEC B"
ECHDEL: DEC B ; Count bytes in buffer
DEC HL ; Back space buffer
JP Z,OTKLN ; No buffer - Try again
LD A,(HL) ; Get deleted byte
CALL OUTC ; Echo it
JP MORINP ; Get more input
DELCHR: DEC B ; Count bytes in buffer
DEC HL ; Back space buffer
CALL OUTC ; Output character in A
JP NZ,MORINP ; Not end - Get more
OTKLN: CALL OUTC ; Output character in A
KILIN: CALL PRNTCRLF ; Output CRLF
JP TTYLIN ; Get line again
GETLIN:
TTYLIN: LD HL,BUFFER ; Get a line by character
LD B,1 ; Set buffer as empty
XOR A
LD (NULFLG),A ; Clear null flag
MORINP: CALL CLOTST ; Get character and test ^O
LD C,A ; Save character in C
CP DEL ; Delete character?
JP Z,DODEL ; Yes - Process it
LD A,(NULFLG) ; Get null flag
OR A ; Test null flag status
JP Z,PROCES ; Reset - Process character
LD A,0 ; Set a null
CALL OUTC ; Output null
XOR A ; Clear A
LD (NULFLG),A ; Reset null flag
PROCES: LD A,C ; Get character
CP CTRLG ; Bell?
JP Z,PUTCTL ; Yes - Save it
CP CTRLC ; Is it control "C"?
CALL Z,PRNTCRLF ; Yes - Output CRLF
SCF ; Flag break
RET Z ; Return if control "C"
CP CR ; Is it enter?
JP Z,ENDINP ; Yes - Terminate input
CP CTRLU ; Is it control "U"?
JP Z,KILIN ; Yes - Get another line
CP '@' ; Is it "kill line"?
JP Z,OTKLN ; Yes - Kill line
CP '_' ; Is it delete?
JP Z,DELCHR ; Yes - Delete character
CP BKSP ; Is it backspace?
JP Z,DELCHR ; Yes - Delete character
CP CTRLR ; Is it control "R"?
JP NZ,PUTBUF ; No - Put in buffer
PUSH BC ; Save buffer length
PUSH DE ; Save DE
PUSH HL ; Save buffer address
LD (HL),0 ; Mark end of buffer
CALL OUTNCR ; Output and do CRLF
LD HL,BUFFER ; Point to buffer start
CALL PRS ; Output buffer
POP HL ; Restore buffer address
POP DE ; Restore DE
POP BC ; Restore buffer length
JP MORINP ; Get another character
PUTBUF: CP ' ' ; Is it a control code?
JP C,MORINP ; Yes - Ignore
PUTCTL: LD A,B ; Get number of bytes in buffer
CP 72+1 ; Test for line overflow
LD A,CTRLG ; Set a bell
JP NC,OUTNBS ; Ring bell if buffer full
LD A,C ; Get character
LD (HL),C ; Save in buffer
LD (LSTBIN),A ; Save last input byte
INC HL ; Move up buffer
INC B ; Increment length
OUTIT: CALL OUTC ; Output the character entered
JP MORINP ; Get another character
OUTNBS: CALL OUTC ; Output bell and back over it
LD A,BKSP ; Set back space
JP OUTIT ; Output it and get more
CPDEHL: LD A,H ; Get H
SUB D ; Compare with D
RET NZ ; Different - Exit
LD A,L ; Get L
SUB E ; Compare with E
RET ; Return status
CHKSYN: LD A,(HL) ; Check syntax of character
EX (SP),HL ; Address of test byte
CP (HL) ; Same as in code string?
INC HL ; Return address
EX (SP),HL ; Put it back
JP Z,GETCHR ; Yes - Get next character
JP SNERR ; Different - ?SN Error
OUTC: PUSH AF ; Save character
LD A,(CTLOFG) ; Get control "O" flag
OR A ; Is it set?
JP NZ,POPAF ; Yes - don't output
POP AF ; Restore character
PUSH BC ; Save buffer length
PUSH AF ; Save character
CP ' ' ; Is it a control code?
JP C,DINPOS ; Yes - Don't INC POS(X)
LD A,(LWIDTH) ; Get line width
LD B,A ; To B
LD A,(CURPOS) ; Get cursor position
INC B ; Width 255?
JP Z,INCLEN ; Yes - No width limit
DEC B ; Restore width
CP B ; At end of line?
CALL Z,PRNTCRLF ; Yes - output CRLF
INCLEN: INC A ; Move on one character
LD (CURPOS),A ; Save new position
DINPOS: POP AF ; Restore character
POP BC ; Restore buffer length
CALL MONOUT ; Send it
RET
CLOTST: CALL GETINP ; Get input character
AND 01111111B ; Strip bit 7
CP CTRLO ; Is it control "O"?
RET NZ ; No don't flip flag
LD A,(CTLOFG) ; Get flag
CPL ; Flip it
LD (CTLOFG),A ; Put it back
XOR A ; Null character
RET
LIST: CALL ATOH ; ASCII number to DE
RET NZ ; Return if anything extra
POP BC ; Rubbish - Not needed
CALL SRCHLN ; Search for line number in DE
PUSH BC ; Save address of line
CALL SETLIN ; Set up lines counter
LISTLP: POP HL ; Restore address of line
LD C,(HL) ; Get LSB of next line
INC HL
LD B,(HL) ; Get MSB of next line
INC HL
LD A,B ; BC = 0 (End of program)?
OR C
JP Z,PRNTOK ; Yes - Go to command mode
CALL COUNT ; Count lines
CALL TSTBRK ; Test for break key
PUSH BC ; Save address of next line
CALL PRNTCRLF ; Output CRLF
LD E,(HL) ; Get LSB of line number
INC HL
LD D,(HL) ; Get MSB of line number
INC HL
PUSH HL ; Save address of line start
EX DE,HL ; Line number to HL
CALL PRNTHL ; Output line number in decimal
LD A,' ' ; Space after line number
POP HL ; Restore start of line address
LSTLP2: CALL OUTC ; Output character in A
LSTLP3: LD A,(HL) ; Get next byte in line
OR A ; End of line?
INC HL ; To next byte in line
JP Z,LISTLP ; Yes - get next line
JP P,LSTLP2 ; No token - output it
SUB ZEND-1 ; Find and output word
LD C,A ; Token offset+1 to C
LD DE,WORDS ; Reserved word list
FNDTOK: LD A,(DE) ; Get character in list
INC DE ; Move on to next
OR A ; Is it start of word?
JP P,FNDTOK ; No - Keep looking for word
DEC C ; Count words
JP NZ,FNDTOK ; Not there - keep looking
OUTWRD: AND 01111111B ; Strip bit 7
CALL OUTC ; Output first character
LD A,(DE) ; Get next character
INC DE ; Move on to next
OR A ; Is it end of word?
JP P,OUTWRD ; No - output the rest
JP LSTLP3 ; Next byte in line
SETLIN: PUSH HL ; Set up LINES counter
LD HL,(LINESN) ; Get LINES number
LD (LINESC),HL ; Save in LINES counter
POP HL
RET
COUNT: PUSH HL ; Save code string address
PUSH DE
LD HL,(LINESC) ; Get LINES counter
LD DE,-1
ADC HL,DE ; Decrement
LD (LINESC),HL ; Put it back
POP DE
POP HL ; Restore code string address
RET P ; Return if more lines to go
PUSH HL ; Save code string address
LD HL,(LINESN) ; Get LINES number
LD (LINESC),HL ; Reset LINES counter
CALL GETINP ; Get input character
CP CTRLC ; Is it control "C"?
JP Z,RSLNBK ; Yes - Reset LINES and break
POP HL ; Restore code string address
JP COUNT ; Keep on counting
RSLNBK: LD HL,(LINESN) ; Get LINES number
LD (LINESC),HL ; Reset LINES counter
JP BRKRET ; Go and output "Break"
FOR: LD A,64H ; Flag "FOR" assignment
LD (FORFLG),A ; Save "FOR" flag
CALL LET ; Set up initial index
POP BC ; Drop RETurn address
PUSH HL ; Save code string address
CALL DATA ; Get next statement address
LD (LOOPST),HL ; Save it for start of loop
LD HL,2 ; Offset for "FOR" block
ADD HL,SP ; Point to it
FORSLP: CALL LOKFOR ; Look for existing "FOR" block
POP DE ; Get code string address
JP NZ,FORFND ; No nesting found
ADD HL,BC ; Move into "FOR" block
PUSH DE ; Save code string address
DEC HL
LD D,(HL) ; Get MSB of loop statement
DEC HL
LD E,(HL) ; Get LSB of loop statement
INC HL
INC HL
PUSH HL ; Save block address
LD HL,(LOOPST) ; Get address of loop statement
CALL CPDEHL ; Compare the FOR loops
POP HL ; Restore block address
JP NZ,FORSLP ; Different FORs - Find another
POP DE ; Restore code string address
LD SP,HL ; Remove all nested loops
FORFND: EX DE,HL ; Code string address to HL
LD C,8
CALL CHKSTK ; Check for 8 levels of stack
PUSH HL ; Save code string address
LD HL,(LOOPST) ; Get first statement of loop
EX (SP),HL ; Save and restore code string
PUSH HL ; Re-save code string address
LD HL,(LINEAT) ; Get current line number
EX (SP),HL ; Save and restore code string
CALL TSTNUM ; Make sure it's a number
CALL CHKSYN ; Make sure "TO" is next
.BYTE ZTO ; "TO" token
CALL GETNUM ; Get "TO" expression value
PUSH HL ; Save code string address
CALL BCDEFP ; Move "TO" value to BCDE
POP HL ; Restore code string address
PUSH BC ; Save "TO" value in block
PUSH DE
LD BC,8100H ; BCDE - 1 (default STEP)
LD D,C ; C=0
LD E,D ; D=0
LD A,(HL) ; Get next byte in code string
CP ZSTEP ; See if "STEP" is stated
LD A,1 ; Sign of step = 1
JP NZ,SAVSTP ; No STEP given - Default to 1
CALL GETCHR ; Jump over "STEP" token
CALL GETNUM ; Get step value
PUSH HL ; Save code string address
CALL BCDEFP ; Move STEP to BCDE
CALL TSTSGN ; Test sign of FPREG
POP HL ; Restore code string address
SAVSTP: PUSH BC ; Save the STEP value in block
PUSH DE
PUSH AF ; Save sign of STEP
INC SP ; Don't save flags
PUSH HL ; Save code string address
LD HL,(BRKLIN) ; Get address of index variable
EX (SP),HL ; Save and restore code string
PUTFID: LD B,ZFOR ; "FOR" block marker
PUSH BC ; Save it
INC SP ; Don't save C
RUNCNT: CALL TSTBRK ; Execution driver - Test break
LD (BRKLIN),HL ; Save code address for break
LD A,(HL) ; Get next byte in code string
CP ':' ; Multi statement line?
JP Z,EXCUTE ; Yes - Execute it
OR A ; End of line?
JP NZ,SNERR ; No - Syntax error
INC HL ; Point to address of next line
LD A,(HL) ; Get LSB of line pointer
INC HL
OR (HL) ; Is it zero (End of prog)?
JP Z,ENDPRG ; Yes - Terminate execution
INC HL ; Point to line number
LD E,(HL) ; Get LSB of line number
INC HL
LD D,(HL) ; Get MSB of line number
EX DE,HL ; Line number to HL
LD (LINEAT),HL ; Save as current line number
EX DE,HL ; Line number back to DE
EXCUTE: CALL GETCHR ; Get key word
LD DE,RUNCNT ; Where to RETurn to
PUSH DE ; Save for RETurn
IFJMP: RET Z ; Go to RUNCNT if end of STMT
ONJMP: SUB ZEND ; Is it a token?
JP C,LET ; No - try to assign it
CP ZNEW+1-ZEND ; END to NEW ?
JP NC,SNERR ; Not a key word - ?SN Error
RLCA ; Double it
LD C,A ; BC = Offset into table
LD B,0
EX DE,HL ; Save code string address
LD HL,WORDTB ; Keyword address table
ADD HL,BC ; Point to routine address
LD C,(HL) ; Get LSB of routine address
INC HL
LD B,(HL) ; Get MSB of routine address
PUSH BC ; Save routine address
EX DE,HL ; Restore code string address
GETCHR: INC HL ; Point to next character
LD A,(HL) ; Get next code string byte
CP ':' ; Z if ':'
RET NC ; NC if > "9"
CP ' '
JP Z,GETCHR ; Skip over spaces
CP '0'
CCF ; NC if < '0'
INC A ; Test for zero - Leave carry
DEC A ; Z if Null
RET
RESTOR: EX DE,HL ; Save code string address
LD HL,(BASTXT) ; Point to start of program
JP Z,RESTNL ; Just RESTORE - reset pointer
EX DE,HL ; Restore code string address
CALL ATOH ; Get line number to DE
PUSH HL ; Save code string address
CALL SRCHLN ; Search for line number in DE
LD H,B ; HL = Address of line
LD L,C
POP DE ; Restore code string address
JP NC,ULERR ; ?UL Error if not found
RESTNL: DEC HL ; Byte before DATA statement
UPDATA: LD (NXTDAT),HL ; Update DATA pointer
EX DE,HL ; Restore code string address
RET
TSTBRK: RST 18H ; Check input status
RET Z ; No key, go back
RST 10H ; Get the key into A
CP ESC ; Escape key?
JR Z,BRK ; Yes, break
CP CTRLC ; <Ctrl-C>
JR Z,BRK ; Yes, break
CP CTRLS ; Stop scrolling?
RET NZ ; Other key, ignore
STALL: RST 10H ; Wait for key
CP CTRLQ ; Resume scrolling?
RET Z ; Release the chokehold
CP CTRLC ; Second break?
JR Z,STOP ; Break during hold exits prog
JR STALL ; Loop until <Ctrl-Q> or <brk>
BRK LD A,$FF ; Set BRKFLG
LD (BRKFLG),A ; Store it
STOP: RET NZ ; Exit if anything else
.BYTE 0F6H ; Flag "STOP"
PEND: RET NZ ; Exit if anything else
LD (BRKLIN),HL ; Save point of break
.BYTE 21H ; Skip "OR 11111111B"
INPBRK: OR 11111111B ; Flag "Break" wanted
POP BC ; Return not needed and more
ENDPRG: LD HL,(LINEAT) ; Get current line number
PUSH AF ; Save STOP / END status
LD A,L ; Is it direct break?
AND H
INC A ; Line is -1 if direct break
JP Z,NOLIN ; Yes - No line number
LD (ERRLIN),HL ; Save line of break
LD HL,(BRKLIN) ; Get point of break
LD (CONTAD),HL ; Save point to CONTinue
NOLIN: XOR A
LD (CTLOFG),A ; Enable output
CALL STTLIN ; Start a new line
POP AF ; Restore STOP / END status
LD HL,BRKMSG ; "Break" message
JP NZ,ERRIN ; "in line" wanted?
JP PRNTOK ; Go to command mode
CONT: LD HL,(CONTAD) ; Get CONTinue address
LD A,H ; Is it zero?
OR L
LD E,CN ; ?CN Error
JP Z,ERROR ; Yes - output "?CN Error"
EX DE,HL ; Save code string address
LD HL,(ERRLIN) ; Get line of last break
LD (LINEAT),HL ; Set up current line number
EX DE,HL ; Restore code string address
RET ; CONTinue where left off
NULL: CALL GETINT ; Get integer 0-255
RET NZ ; Return if bad value
LD (NULLS),A ; Set nulls number
RET
ACCSUM: PUSH HL ; Save address in array
LD HL,(CHKSUM) ; Get check sum
LD B,0 ; BC - Value of byte
LD C,A
ADD HL,BC ; Add byte to check sum
LD (CHKSUM),HL ; Re-save check sum
POP HL ; Restore address in array
RET
CHKLTR: LD A,(HL) ; Get byte
CP 'A' ; < 'a' ?
RET C ; Carry set if not letter
CP 'Z'+1 ; > 'z' ?
CCF
RET ; Carry set if not letter
FPSINT: CALL GETCHR ; Get next character
POSINT: CALL GETNUM ; Get integer 0 to 32767
DEPINT: CALL TSTSGN ; Test sign of FPREG
JP M,FCERR ; Negative - ?FC Error
DEINT: LD A,(FPEXP) ; Get integer value to DE
CP 80H+16 ; Exponent in range (16 bits)?
JP C,FPINT ; Yes - convert it
LD BC,9080H ; BCDE = -32768
LD DE,0000
PUSH HL ; Save code string address
CALL CMPNUM ; Compare FPREG with BCDE
POP HL ; Restore code string address
LD D,C ; MSB to D
RET Z ; Return if in range
FCERR: LD E,FC ; ?FC Error
JP ERROR ; Output error-
ATOH: DEC HL ; ASCII number to DE binary
GETLN: LD DE,0 ; Get number to DE
GTLNLP: CALL GETCHR ; Get next character
RET NC ; Exit if not a digit
PUSH HL ; Save code string address
PUSH AF ; Save digit
LD HL,65529/10 ; Largest number 65529
CALL CPDEHL ; Number in range?
JP C,SNERR ; No - ?SN Error
LD H,D ; HL = Number
LD L,E
ADD HL,DE ; Times 2
ADD HL,HL ; Times 4
ADD HL,DE ; Times 5
ADD HL,HL ; Times 10
POP AF ; Restore digit
SUB '0' ; Make it 0 to 9
LD E,A ; DE = Value of digit
LD D,0
ADD HL,DE ; Add to number
EX DE,HL ; Number to DE
POP HL ; Restore code string address
JP GTLNLP ; Go to next character
CLEAR: JP Z,INTVAR ; Just "CLEAR" Keep parameters
CALL POSINT ; Get integer 0 to 32767 to DE
DEC HL ; Cancel increment
CALL GETCHR ; Get next character
PUSH HL ; Save code string address
LD HL,(LSTRAM) ; Get end of RAM
JP Z,STORED ; No value given - Use stored
POP HL ; Restore code string address
CALL CHKSYN ; Check for comma
.BYTE ','
PUSH DE ; Save number
CALL POSINT ; Get integer 0 to 32767
DEC HL ; Cancel increment
CALL GETCHR ; Get next character
JP NZ,SNERR ; ?SN Error if more on line
EX (SP),HL ; Save code string address
EX DE,HL ; Number to DE
STORED: LD A,L ; Get LSB of new RAM top
SUB E ; Subtract LSB of string space
LD E,A ; Save LSB
LD A,H ; Get MSB of new RAM top
SBC A,D ; Subtract MSB of string space
LD D,A ; Save MSB
JP C,OMERR ; ?OM Error if not enough mem
PUSH HL ; Save RAM top
LD HL,(PROGND) ; Get program end
LD BC,40 ; 40 Bytes minimum working RAM
ADD HL,BC ; Get lowest address
CALL CPDEHL ; Enough memory?
JP NC,OMERR ; No - ?OM Error
EX DE,HL ; RAM top to HL
LD (STRSPC),HL ; Set new string space
POP HL ; End of memory to use
LD (LSTRAM),HL ; Set new top of RAM
POP HL ; Restore code string address
JP INTVAR ; Initialise variables
RUN: JP Z,RUNFST ; RUN from start if just RUN
CALL INTVAR ; Initialise variables
LD BC,RUNCNT ; Execution driver loop
JP RUNLIN ; RUN from line number
GOSUB: LD C,3 ; 3 Levels of stack needed
CALL CHKSTK ; Check for 3 levels of stack
POP BC ; Get return address
PUSH HL ; Save code string for RETURN
PUSH HL ; And for GOSUB routine
LD HL,(LINEAT) ; Get current line
EX (SP),HL ; Into stack - Code string out
LD A,ZGOSUB ; "GOSUB" token
PUSH AF ; Save token
INC SP ; Don't save flags
RUNLIN: PUSH BC ; Save return address
GOTO: CALL ATOH ; ASCII number to DE binary
CALL REM ; Get end of line
PUSH HL ; Save end of line
LD HL,(LINEAT) ; Get current line
CALL CPDEHL ; Line after current?
POP HL ; Restore end of line
INC HL ; Start of next line
CALL C,SRCHLP ; Line is after current line
CALL NC,SRCHLN ; Line is before current line
LD H,B ; Set up code string address
LD L,C
DEC HL ; Incremented after
RET C ; Line found
ULERR: LD E,UL ; ?UL Error
JP ERROR ; Output error message
RETURN: RET NZ ; Return if not just RETURN
LD D,-1 ; Flag "GOSUB" search
CALL BAKSTK ; Look "GOSUB" block
LD SP,HL ; Kill all FORs in subroutine
CP ZGOSUB ; Test for "GOSUB" token
LD E,RG ; ?RG Error
JP NZ,ERROR ; Error if no "GOSUB" found
POP HL ; Get RETURN line number
LD (LINEAT),HL ; Save as current
INC HL ; Was it from direct statement?
LD A,H
OR L ; Return to line
JP NZ,RETLIN ; No - Return to line
LD A,(LSTBIN) ; Any INPUT in subroutine?
OR A ; If so buffer is corrupted
JP NZ,POPNOK ; Yes - Go to command mode
RETLIN: LD HL,RUNCNT ; Execution driver loop
EX (SP),HL ; Into stack - Code string out
.BYTE 3EH ; Skip "POP HL"
NXTDTA: POP HL ; Restore code string address
DATA: .BYTE 01H,3AH ; ':' End of statement
REM: LD C,0 ; 00 End of statement
LD B,0
NXTSTL: LD A,C ; Statement and byte
LD C,B
LD B,A ; Statement end byte
NXTSTT: LD A,(HL) ; Get byte
OR A ; End of line?
RET Z ; Yes - Exit
CP B ; End of statement?
RET Z ; Yes - Exit
INC HL ; Next byte
CP '"' ; Literal string?
JP Z,NXTSTL ; Yes - Look for another '"'
JP NXTSTT ; Keep looking
LET: CALL GETVAR ; Get variable name
CALL CHKSYN ; Make sure "=" follows
.BYTE ZEQUAL ; "=" token
PUSH DE ; Save address of variable
LD A,(TYPE) ; Get data type
PUSH AF ; Save type
CALL EVAL ; Evaluate expression
POP AF ; Restore type
EX (SP),HL ; Save code - Get var addr
LD (BRKLIN),HL ; Save address of variable
RRA ; Adjust type
CALL CHKTYP ; Check types are the same
JP Z,LETNUM ; Numeric - Move value
LETSTR: PUSH HL ; Save address of string var
LD HL,(FPREG) ; Pointer to string entry
PUSH HL ; Save it on stack
INC HL ; Skip over length
INC HL
LD E,(HL) ; LSB of string address
INC HL
LD D,(HL) ; MSB of string address
LD HL,(BASTXT) ; Point to start of program
CALL CPDEHL ; Is string before program?
JP NC,CRESTR ; Yes - Create string entry
LD HL,(STRSPC) ; Point to string space
CALL CPDEHL ; Is string literal in program?
POP DE ; Restore address of string
JP NC,MVSTPT ; Yes - Set up pointer
LD HL,TMPSTR ; Temporary string pool
CALL CPDEHL ; Is string in temporary pool?
JP NC,MVSTPT ; No - Set up pointer
.BYTE 3EH ; Skip "POP DE"
CRESTR: POP DE ; Restore address of string
CALL BAKTMP ; Back to last tmp-str entry
EX DE,HL ; Address of string entry
CALL SAVSTR ; Save string in string area
MVSTPT: CALL BAKTMP ; Back to last tmp-str entry
POP HL ; Get string pointer
CALL DETHL4 ; Move string pointer to var
POP HL ; Restore code string address
RET
LETNUM: PUSH HL ; Save address of variable
CALL FPTHL ; Move value to variable
POP DE ; Restore address of variable
POP HL ; Restore code string address
RET
ON: CALL GETINT ; Get integer 0-255
LD A,(HL) ; Get "GOTO" or "GOSUB" token
LD B,A ; Save in B
CP ZGOSUB ; "GOSUB" token?
JP Z,ONGO ; Yes - Find line number
CALL CHKSYN ; Make sure it's "GOTO"
.BYTE ZGOTO ; "GOTO" token
DEC HL ; Cancel increment
ONGO: LD C,E ; Integer of branch value
ONGOLP: DEC C ; Count branches
LD A,B ; Get "GOTO" or "GOSUB" token
JP Z,ONJMP ; Go to that line if right one
CALL GETLN ; Get line number to DE
CP ',' ; Another line number?
RET NZ ; No - Drop through
JP ONGOLP ; Yes - loop
IF: CALL EVAL ; Evaluate expression
LD A,(HL) ; Get token
CP ZGOTO ; "GOTO" token?
JP Z,IFGO ; Yes - Get line
CALL CHKSYN ; Make sure it's "THEN"
.BYTE ZTHEN ; "THEN" token
DEC HL ; Cancel increment
IFGO: CALL TSTNUM ; Make sure it's numeric
CALL TSTSGN ; Test state of expression
JP Z,REM ; False - Drop through
CALL GETCHR ; Get next character
JP C,GOTO ; Number - GOTO that line
JP IFJMP ; Otherwise do statement
MRPRNT: DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
PRINT: JP Z,PRNTCRLF ; CRLF if just PRINT
PRNTLP: RET Z ; End of list - Exit
CP ZTAB ; "TAB(" token?
JP Z,DOTAB ; Yes - Do TAB routine
CP ZSPC ; "SPC(" token?
JP Z,DOTAB ; Yes - Do SPC routine
PUSH HL ; Save code string address
CP ',' ; Comma?
JP Z,DOCOM ; Yes - Move to next zone
CP 59 ;";" ; Semi-colon?
JP Z,NEXITM ; Do semi-colon routine
POP BC ; Code string address to BC
CALL EVAL ; Evaluate expression
PUSH HL ; Save code string address
LD A,(TYPE) ; Get variable type
OR A ; Is it a string variable?
JP NZ,PRNTST ; Yes - Output string contents
CALL NUMASC ; Convert number to text
CALL CRTST ; Create temporary string
LD (HL),' ' ; Followed by a space
LD HL,(FPREG) ; Get length of output
INC (HL) ; Plus 1 for the space
LD HL,(FPREG) ; < Not needed >
LD A,(LWIDTH) ; Get width of line
LD B,A ; To B
INC B ; Width 255 (No limit)?
JP Z,PRNTNB ; Yes - Output number string
INC B ; Adjust it
LD A,(CURPOS) ; Get cursor position
ADD A,(HL) ; Add length of string
DEC A ; Adjust it
CP B ; Will output fit on this line?
CALL NC,PRNTCRLF ; No - CRLF first
PRNTNB: CALL PRS1 ; Output string at (HL)
XOR A ; Skip CALL by setting 'z' flag
PRNTST: CALL NZ,PRS1 ; Output string at (HL)
POP HL ; Restore code string address
JP MRPRNT ; See if more to PRINT
STTLIN: LD A,(CURPOS) ; Make sure on new line
OR A ; Already at start?
RET Z ; Yes - Do nothing
JP PRNTCRLF ; Start a new line
ENDINP: LD (HL),0 ; Mark end of buffer
LD HL,BUFFER-1 ; Point to buffer
PRNTCRLF: LD A,CR ; Load a CR
CALL OUTC ; Output character
LD A,LF ; Load a LF
CALL OUTC ; Output character
DONULL: XOR A ; Set to position 0
LD (CURPOS),A ; Store it
LD A,(NULLS) ; Get number of nulls
NULLP: DEC A ; Count them
RET Z ; Return if done
PUSH AF ; Save count
XOR A ; Load a null
CALL OUTC ; Output it
POP AF ; Restore count
JP NULLP ; Keep counting
DOCOM: LD A,(COMMAN) ; Get comma width
LD B,A ; Save in B
LD A,(CURPOS) ; Get current position
CP B ; Within the limit?
CALL NC,PRNTCRLF ; No - output CRLF
JP NC,NEXITM ; Get next item
ZONELP: SUB 14 ; Next zone of 14 characters
JP NC,ZONELP ; Repeat if more zones
CPL ; Number of spaces to output
JP ASPCS ; Output them
DOTAB: PUSH AF ; Save token
CALL FNDNUM ; Evaluate expression
CALL CHKSYN ; Make sure ")" follows
.BYTE ")"
DEC HL ; Back space on to ")"
POP AF ; Restore token
SUB ZSPC ; Was it "SPC(" ?
PUSH HL ; Save code string address
JP Z,DOSPC ; Yes - Do 'E' spaces
LD A,(CURPOS) ; Get current position
DOSPC: CPL ; Number of spaces to print to
ADD A,E ; Total number to print
JP NC,NEXITM ; TAB < Current POS(X)
ASPCS: INC A ; Output A spaces
LD B,A ; Save number to print
LD A,' ' ; Space
SPCLP: CALL OUTC ; Output character in A
DEC B ; Count them
JP NZ,SPCLP ; Repeat if more
NEXITM: POP HL ; Restore code string address
CALL GETCHR ; Get next character
JP PRNTLP ; More to print
REDO: .BYTE "?Redo from start",CR,LF,0
BADINP: LD A,(READFG) ; READ or INPUT?
OR A
JP NZ,DATSNR ; READ - ?SN Error
POP BC ; Throw away code string addr
LD HL,REDO ; "Redo from start" message
CALL PRS ; Output string
JP DOAGN ; Do last INPUT again
INPUT: CALL IDTEST ; Test for illegal direct
LD A,(HL) ; Get character after "INPUT"
CP '"' ; Is there a prompt string?
LD A,0 ; Clear A and leave flags
LD (CTLOFG),A ; Enable output
JP NZ,NOPMPT ; No prompt - get input
CALL QTSTR ; Get string terminated by '"'
CALL CHKSYN ; Check for ';' after prompt
.BYTE ';'
PUSH HL ; Save code string address
CALL PRS1 ; Output prompt string
.BYTE 3EH ; Skip "PUSH HL"
NOPMPT: PUSH HL ; Save code string address
CALL PROMPT ; Get input with "? " prompt
POP BC ; Restore code string address
JP C,INPBRK ; Break pressed - Exit
INC HL ; Next byte
LD A,(HL) ; Get it
OR A ; End of line?
DEC HL ; Back again
PUSH BC ; Re-save code string address
JP Z,NXTDTA ; Yes - Find next DATA stmt
LD (HL),',' ; Store comma as separator
JP NXTITM ; Get next item
READ: PUSH HL ; Save code string address
LD HL,(NXTDAT) ; Next DATA statement
.BYTE 0F6H ; Flag "READ"
NXTITM: XOR A ; Flag "INPUT"
LD (READFG),A ; Save "READ"/"INPUT" flag
EX (SP),HL ; Get code str' , Save pointer
JP GTVLUS ; Get values
NEDMOR: CALL CHKSYN ; Check for comma between items
.BYTE ','
GTVLUS: CALL GETVAR ; Get variable name
EX (SP),HL ; Save code str" , Get pointer
PUSH DE ; Save variable address
LD A,(HL) ; Get next "INPUT"/"DATA" byte
CP ',' ; Comma?
JP Z,ANTVLU ; Yes - Get another value
LD A,(READFG) ; Is it READ?
OR A
JP NZ,FDTLP ; Yes - Find next DATA stmt
LD A,'?' ; More INPUT needed
CALL OUTC ; Output character
CALL PROMPT ; Get INPUT with prompt
POP DE ; Variable address
POP BC ; Code string address
JP C,INPBRK ; Break pressed
INC HL ; Point to next DATA byte
LD A,(HL) ; Get byte
OR A ; Is it zero (No input) ?
DEC HL ; Back space INPUT pointer
PUSH BC ; Save code string address
JP Z,NXTDTA ; Find end of buffer
PUSH DE ; Save variable address
ANTVLU: LD A,(TYPE) ; Check data type
OR A ; Is it numeric?
JP Z,INPBIN ; Yes - Convert to binary
CALL GETCHR ; Get next character
LD D,A ; Save input character
LD B,A ; Again
CP '"' ; Start of literal sting?
JP Z,STRENT ; Yes - Create string entry
LD A,(READFG) ; "READ" or "INPUT" ?
OR A
LD D,A ; Save 00 if "INPUT"
JP Z,ITMSEP ; "INPUT" - End with 00
LD D,':' ; "DATA" - End with 00 or ':'
ITMSEP: LD B,',' ; Item separator
DEC HL ; Back space for DTSTR
STRENT: CALL DTSTR ; Get string terminated by D
EX DE,HL ; String address to DE
LD HL,LTSTND ; Where to go after LETSTR
EX (SP),HL ; Save HL , get input pointer
PUSH DE ; Save address of string
JP LETSTR ; Assign string to variable
INPBIN: CALL GETCHR ; Get next character
CALL ASCTFP ; Convert ASCII to FP number
EX (SP),HL ; Save input ptr, Get var addr
CALL FPTHL ; Move FPREG to variable
POP HL ; Restore input pointer
LTSTND: DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
JP Z,MORDT ; End of line - More needed?
CP ',' ; Another value?
JP NZ,BADINP ; No - Bad input
MORDT: EX (SP),HL ; Get code string address
DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
JP NZ,NEDMOR ; More needed - Get it
POP DE ; Restore DATA pointer
LD A,(READFG) ; "READ" or "INPUT" ?
OR A
EX DE,HL ; DATA pointer to HL
JP NZ,UPDATA ; Update DATA pointer if "READ"
PUSH DE ; Save code string address
OR (HL) ; More input given?
LD HL,EXTIG ; "?Extra ignored" message
CALL NZ,PRS ; Output string if extra given
POP HL ; Restore code string address
RET
EXTIG: .BYTE "?Extra ignored",CR,LF,0
FDTLP: CALL DATA ; Get next statement
OR A ; End of line?
JP NZ,FANDT ; No - See if DATA statement
INC HL
LD A,(HL) ; End of program?
INC HL
OR (HL) ; 00 00 Ends program
LD E,OD ; ?OD Error
JP Z,ERROR ; Yes - Out of DATA
INC HL
LD E,(HL) ; LSB of line number
INC HL
LD D,(HL) ; MSB of line number
EX DE,HL
LD (DATLIN),HL ; Set line of current DATA item
EX DE,HL
FANDT: CALL GETCHR ; Get next character
CP ZDATA ; "DATA" token
JP NZ,FDTLP ; No "DATA" - Keep looking
JP ANTVLU ; Found - Convert input
NEXT: LD DE,0 ; In case no index given
NEXT1: CALL NZ,GETVAR ; Get index address
LD (BRKLIN),HL ; Save code string address
CALL BAKSTK ; Look for "FOR" block
JP NZ,NFERR ; No "FOR" - ?NF Error
LD SP,HL ; Clear nested loops
PUSH DE ; Save index address
LD A,(HL) ; Get sign of STEP
INC HL
PUSH AF ; Save sign of STEP
PUSH DE ; Save index address
CALL PHLTFP ; Move index value to FPREG
EX (SP),HL ; Save address of TO value
PUSH HL ; Save address of index
CALL ADDPHL ; Add STEP to index value
POP HL ; Restore address of index
CALL FPTHL ; Move value to index variable
POP HL ; Restore address of TO value
CALL LOADFP ; Move TO value to BCDE
PUSH HL ; Save address of line of FOR
CALL CMPNUM ; Compare index with TO value
POP HL ; Restore address of line num
POP BC ; Address of sign of STEP
SUB B ; Compare with expected sign
CALL LOADFP ; BC = Loop stmt,DE = Line num
JP Z,KILFOR ; Loop finished - Terminate it
EX DE,HL ; Loop statement line number
LD (LINEAT),HL ; Set loop line number
LD L,C ; Set code string to loop
LD H,B
JP PUTFID ; Put back "FOR" and continue
KILFOR: LD SP,HL ; Remove "FOR" block
LD HL,(BRKLIN) ; Code string after "NEXT"
LD A,(HL) ; Get next byte in code string
CP ',' ; More NEXTs ?
JP NZ,RUNCNT ; No - Do next statement
CALL GETCHR ; Position to index name
CALL NEXT1 ; Re-enter NEXT routine
; < will not RETurn to here , Exit to RUNCNT or Loop >
GETNUM: CALL EVAL ; Get a numeric expression
TSTNUM: .BYTE 0F6H ; Clear carry (numeric)
TSTSTR: SCF ; Set carry (string)
CHKTYP: LD A,(TYPE) ; Check types match
ADC A,A ; Expected + actual
OR A ; Clear carry , set parity
RET PE ; Even parity - Types match
JP TMERR ; Different types - Error
OPNPAR: CALL CHKSYN ; Make sure "(" follows
.BYTE "("
EVAL: DEC HL ; Evaluate expression & save
LD D,0 ; Precedence value
EVAL1: PUSH DE ; Save precedence
LD C,1
CALL CHKSTK ; Check for 1 level of stack
CALL OPRND ; Get next expression value
EVAL2: LD (NXTOPR),HL ; Save address of next operator
EVAL3: LD HL,(NXTOPR) ; Restore address of next opr
POP BC ; Precedence value and operator
LD A,B ; Get precedence value
CP 78H ; "AND" or "OR" ?
CALL NC,TSTNUM ; No - Make sure it's a number
LD A,(HL) ; Get next operator / function
LD D,0 ; Clear Last relation
RLTLP: SUB ZGTR ; ">" Token
JP C,FOPRND ; + - * / ^ AND OR - Test it
CP ZLTH+1-ZGTR ; < = >
JP NC,FOPRND ; Function - Call it
CP ZEQUAL-ZGTR ; "="
RLA ; <- Test for legal
XOR D ; <- combinations of < = >
CP D ; <- by combining last token
LD D,A ; <- with current one
JP C,SNERR ; Error if "<<' '==" or ">>"
LD (CUROPR),HL ; Save address of current token
CALL GETCHR ; Get next character
JP RLTLP ; Treat the two as one
FOPRND: LD A,D ; < = > found ?
OR A
JP NZ,TSTRED ; Yes - Test for reduction
LD A,(HL) ; Get operator token
LD (CUROPR),HL ; Save operator address
SUB ZPLUS ; Operator or function?
RET C ; Neither - Exit
CP ZOR+1-ZPLUS ; Is it + - * / ^ AND OR ?
RET NC ; No - Exit
LD E,A ; Coded operator
LD A,(TYPE) ; Get data type
DEC A ; FF = numeric , 00 = string
OR E ; Combine with coded operator
LD A,E ; Get coded operator
JP Z,CONCAT ; String concatenation
RLCA ; Times 2
ADD A,E ; Times 3
LD E,A ; To DE (D is 0)
LD HL,PRITAB ; Precedence table
ADD HL,DE ; To the operator concerned
LD A,B ; Last operator precedence
LD D,(HL) ; Get evaluation precedence
CP D ; Compare with eval precedence
RET NC ; Exit if higher precedence
INC HL ; Point to routine address
CALL TSTNUM ; Make sure it's a number
STKTHS: PUSH BC ; Save last precedence & token
LD BC,EVAL3 ; Where to go on prec' break
PUSH BC ; Save on stack for return
LD B,E ; Save operator
LD C,D ; Save precedence
CALL STAKFP ; Move value to stack
LD E,B ; Restore operator
LD D,C ; Restore precedence
LD C,(HL) ; Get LSB of routine address
INC HL
LD B,(HL) ; Get MSB of routine address
INC HL
PUSH BC ; Save routine address
LD HL,(CUROPR) ; Address of current operator
JP EVAL1 ; Loop until prec' break
OPRND: XOR A ; Get operand routine
LD (TYPE),A ; Set numeric expected
CALL GETCHR ; Get next character
LD E,MO ; ?MO Error
JP Z,ERROR ; No operand - Error
JP C,ASCTFP ; Number - Get value
CALL CHKLTR ; See if a letter
JP NC,CONVAR ; Letter - Find variable
CP '&' ; &H = HEX, &B = BINARY
JR NZ, NOTAMP
CALL GETCHR ; Get next character
CP 'H' ; Hex number indicated? [function added]
JP Z,HEXTFP ; Convert Hex to FPREG
CP 'B' ; Binary number indicated? [function added]
JP Z,BINTFP ; Convert Bin to FPREG
LD E,SN ; If neither then a ?SN Error
JP Z,ERROR ;
NOTAMP: CP ZPLUS ; '+' Token ?
JP Z,OPRND ; Yes - Look for operand
CP '.' ; '.' ?
JP Z,ASCTFP ; Yes - Create FP number
CP ZMINUS ; '-' Token ?
JP Z,MINUS ; Yes - Do minus
CP '"' ; Literal string ?
JP Z,QTSTR ; Get string terminated by '"'
CP ZNOT ; "NOT" Token ?
JP Z,EVNOT ; Yes - Eval NOT expression
CP ZFN ; "FN" Token ?
JP Z,DOFN ; Yes - Do FN routine
SUB ZSGN ; Is it a function?
JP NC,FNOFST ; Yes - Evaluate function
EVLPAR: CALL OPNPAR ; Evaluate expression in "()"
CALL CHKSYN ; Make sure ")" follows
.BYTE ")"
RET
MINUS: LD D,7DH ; '-' precedence
CALL EVAL1 ; Evaluate until prec' break
LD HL,(NXTOPR) ; Get next operator address
PUSH HL ; Save next operator address
CALL INVSGN ; Negate value
RETNUM: CALL TSTNUM ; Make sure it's a number
POP HL ; Restore next operator address
RET
CONVAR: CALL GETVAR ; Get variable address to DE
FRMEVL: PUSH HL ; Save code string address
EX DE,HL ; Variable address to HL
LD (FPREG),HL ; Save address of variable
LD A,(TYPE) ; Get type
OR A ; Numeric?
CALL Z,PHLTFP ; Yes - Move contents to FPREG
POP HL ; Restore code string address
RET
FNOFST: LD B,0 ; Get address of function
RLCA ; Double function offset
LD C,A ; BC = Offset in function table
PUSH BC ; Save adjusted token value
CALL GETCHR ; Get next character
LD A,C ; Get adjusted token value
CP 2*(ZLEFT-ZSGN)-1; Adj' LEFT$,RIGHT$ or MID$ ?
JP C,FNVAL ; No - Do function
CALL OPNPAR ; Evaluate expression (X,...
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
CALL TSTSTR ; Make sure it's a string
EX DE,HL ; Save code string address
LD HL,(FPREG) ; Get address of string
EX (SP),HL ; Save address of string
PUSH HL ; Save adjusted token value
EX DE,HL ; Restore code string address
CALL GETINT ; Get integer 0-255
EX DE,HL ; Save code string address
EX (SP),HL ; Save integer,HL = adj' token
JP GOFUNC ; Jump to string function
FNVAL: CALL EVLPAR ; Evaluate expression
EX (SP),HL ; HL = Adjusted token value
LD DE,RETNUM ; Return number from function
PUSH DE ; Save on stack
GOFUNC: LD BC,FNCTAB ; Function routine addresses
ADD HL,BC ; Point to right address
LD C,(HL) ; Get LSB of address
INC HL ;
LD H,(HL) ; Get MSB of address
LD L,C ; Address to HL
JP (HL) ; Jump to function
SGNEXP: DEC D ; Dee to flag negative exponent
CP ZMINUS ; '-' token ?
RET Z ; Yes - Return
CP '-' ; '-' ASCII ?
RET Z ; Yes - Return
INC D ; Inc to flag positive exponent
CP '+' ; '+' ASCII ?
RET Z ; Yes - Return
CP ZPLUS ; '+' token ?
RET Z ; Yes - Return
DEC HL ; DEC 'cos GETCHR INCs
RET ; Return "NZ"
POR: .BYTE 0F6H ; Flag "OR"
PAND: XOR A ; Flag "AND"
PUSH AF ; Save "AND" / "OR" flag
CALL TSTNUM ; Make sure it's a number
CALL DEINT ; Get integer -32768 to 32767
POP AF ; Restore "AND" / "OR" flag
EX DE,HL ; <- Get last
POP BC ; <- value
EX (SP),HL ; <- from
EX DE,HL ; <- stack
CALL FPBCDE ; Move last value to FPREG
PUSH AF ; Save "AND" / "OR" flag
CALL DEINT ; Get integer -32768 to 32767
POP AF ; Restore "AND" / "OR" flag
POP BC ; Get value
LD A,C ; Get LSB
LD HL,ACPASS ; Address of save AC as current
JP NZ,POR1 ; Jump if OR
AND E ; "AND" LSBs
LD C,A ; Save LSB
LD A,B ; Get MBS
AND D ; "AND" MSBs
JP (HL) ; Save AC as current (ACPASS)
POR1: OR E ; "OR" LSBs
LD C,A ; Save LSB
LD A,B ; Get MSB
OR D ; "OR" MSBs
JP (HL) ; Save AC as current (ACPASS)
TSTRED: LD HL,CMPLOG ; Logical compare routine
LD A,(TYPE) ; Get data type
RRA ; Carry set = string
LD A,D ; Get last precedence value
RLA ; Times 2 plus carry
LD E,A ; To E
LD D,64H ; Relational precedence
LD A,B ; Get current precedence
CP D ; Compare with last
RET NC ; Eval if last was rel' or log'
JP STKTHS ; Stack this one and get next
CMPLOG: .WORD CMPLG1 ; Compare two values / strings
CMPLG1: LD A,C ; Get data type
OR A
RRA
POP BC ; Get last expression to BCDE
POP DE
PUSH AF ; Save status
CALL CHKTYP ; Check that types match
LD HL,CMPRES ; Result to comparison
PUSH HL ; Save for RETurn
JP Z,CMPNUM ; Compare values if numeric
XOR A ; Compare two strings
LD (TYPE),A ; Set type to numeric
PUSH DE ; Save string name
CALL GSTRCU ; Get current string
LD A,(HL) ; Get length of string
INC HL
INC HL
LD C,(HL) ; Get LSB of address
INC HL
LD B,(HL) ; Get MSB of address
POP DE ; Restore string name
PUSH BC ; Save address of string
PUSH AF ; Save length of string
CALL GSTRDE ; Get second string
CALL LOADFP ; Get address of second string
POP AF ; Restore length of string 1
LD D,A ; Length to D
POP HL ; Restore address of string 1
CMPSTR: LD A,E ; Bytes of string 2 to do
OR D ; Bytes of string 1 to do
RET Z ; Exit if all bytes compared
LD A,D ; Get bytes of string 1 to do
SUB 1
RET C ; Exit if end of string 1
XOR A
CP E ; Bytes of string 2 to do
INC A
RET NC ; Exit if end of string 2
DEC D ; Count bytes in string 1
DEC E ; Count bytes in string 2
LD A,(BC) ; Byte in string 2
CP (HL) ; Compare to byte in string 1
INC HL ; Move up string 1
INC BC ; Move up string 2
JP Z,CMPSTR ; Same - Try next bytes
CCF ; Flag difference (">" or "<")
JP FLGDIF ; "<" gives -1 , ">" gives +1
CMPRES: INC A ; Increment current value
ADC A,A ; Double plus carry
POP BC ; Get other value
AND B ; Combine them
ADD A,-1 ; Carry set if different
SBC A,A ; 00 - Equal , FF - Different
JP FLGREL ; Set current value & continue
EVNOT: LD D,5AH ; Precedence value for "NOT"
CALL EVAL1 ; Eval until precedence break
CALL TSTNUM ; Make sure it's a number
CALL DEINT ; Get integer -32768 - 32767
LD A,E ; Get LSB
CPL ; Invert LSB
LD C,A ; Save "NOT" of LSB
LD A,D ; Get MSB
CPL ; Invert MSB
CALL ACPASS ; Save AC as current
POP BC ; Clean up stack
JP EVAL3 ; Continue evaluation
DIMRET: DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
RET Z ; End of DIM statement
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
DIM: LD BC,DIMRET ; Return to "DIMRET"
PUSH BC ; Save on stack
.BYTE 0F6H ; Flag "Create" variable
GETVAR: XOR A ; Find variable address,to DE
LD (LCRFLG),A ; Set locate / create flag
LD B,(HL) ; Get First byte of name
GTFNAM: CALL CHKLTR ; See if a letter
JP C,SNERR ; ?SN Error if not a letter
XOR A
LD C,A ; Clear second byte of name
LD (TYPE),A ; Set type to numeric
CALL GETCHR ; Get next character
JP C,SVNAM2 ; Numeric - Save in name
CALL CHKLTR ; See if a letter
JP C,CHARTY ; Not a letter - Check type
SVNAM2: LD C,A ; Save second byte of name
ENDNAM: CALL GETCHR ; Get next character
JP C,ENDNAM ; Numeric - Get another
CALL CHKLTR ; See if a letter
JP NC,ENDNAM ; Letter - Get another
CHARTY: SUB '$' ; String variable?
JP NZ,NOTSTR ; No - Numeric variable
INC A ; A = 1 (string type)
LD (TYPE),A ; Set type to string
RRCA ; A = 80H , Flag for string
ADD A,C ; 2nd byte of name has bit 7 on
LD C,A ; Resave second byte on name
CALL GETCHR ; Get next character
NOTSTR: LD A,(FORFLG) ; Array name needed ?
DEC A
JP Z,ARLDSV ; Yes - Get array name
JP P,NSCFOR ; No array with "FOR" or "FN"
LD A,(HL) ; Get byte again
SUB '(' ; Subscripted variable?
JP Z,SBSCPT ; Yes - Sort out subscript
NSCFOR: XOR A ; Simple variable
LD (FORFLG),A ; Clear "FOR" flag
PUSH HL ; Save code string address
LD D,B ; DE = Variable name to find
LD E,C
LD HL,(FNRGNM) ; FN argument name
CALL CPDEHL ; Is it the FN argument?
LD DE,FNARG ; Point to argument value
JP Z,POPHRT ; Yes - Return FN argument value
LD HL,(VAREND) ; End of variables
EX DE,HL ; Address of end of search
LD HL,(PROGND) ; Start of variables address
FNDVAR: CALL CPDEHL ; End of variable list table?
JP Z,CFEVAL ; Yes - Called from EVAL?
LD A,C ; Get second byte of name
SUB (HL) ; Compare with name in list
INC HL ; Move on to first byte
JP NZ,FNTHR ; Different - Find another
LD A,B ; Get first byte of name
SUB (HL) ; Compare with name in list
FNTHR: INC HL ; Move on to LSB of value
JP Z,RETADR ; Found - Return address
INC HL ; <- Skip
INC HL ; <- over
INC HL ; <- F.P.
INC HL ; <- value
JP FNDVAR ; Keep looking
CFEVAL: POP HL ; Restore code string address
EX (SP),HL ; Get return address
PUSH DE ; Save address of variable
LD DE,FRMEVL ; Return address in EVAL
CALL CPDEHL ; Called from EVAL ?
POP DE ; Restore address of variable
JP Z,RETNUL ; Yes - Return null variable
EX (SP),HL ; Put back return
PUSH HL ; Save code string address
PUSH BC ; Save variable name
LD BC,6 ; 2 byte name plus 4 byte data
LD HL,(ARREND) ; End of arrays
PUSH HL ; Save end of arrays
ADD HL,BC ; Move up 6 bytes
POP BC ; Source address in BC
PUSH HL ; Save new end address
CALL MOVUP ; Move arrays up
POP HL ; Restore new end address
LD (ARREND),HL ; Set new end address
LD H,B ; End of variables to HL
LD L,C
LD (VAREND),HL ; Set new end address
ZEROLP: DEC HL ; Back through to zero variable
LD (HL),0 ; Zero byte in variable
CALL CPDEHL ; Done them all?
JP NZ,ZEROLP ; No - Keep on going
POP DE ; Get variable name
LD (HL),E ; Store second character
INC HL
LD (HL),D ; Store first character
INC HL
RETADR: EX DE,HL ; Address of variable in DE
POP HL ; Restore code string address
RET
RETNUL: LD (FPEXP),A ; Set result to zero
LD HL,ZERBYT ; Also set a null string
LD (FPREG),HL ; Save for EVAL
POP HL ; Restore code string address
RET
SBSCPT: PUSH HL ; Save code string address
LD HL,(LCRFLG) ; Locate/Create and Type
EX (SP),HL ; Save and get code string
LD D,A ; Zero number of dimensions
SCPTLP: PUSH DE ; Save number of dimensions
PUSH BC ; Save array name
CALL FPSINT ; Get subscript (0-32767)
POP BC ; Restore array name
POP AF ; Get number of dimensions
EX DE,HL
EX (SP),HL ; Save subscript value
PUSH HL ; Save LCRFLG and TYPE
EX DE,HL
INC A ; Count dimensions
LD D,A ; Save in D
LD A,(HL) ; Get next byte in code string
CP ',' ; Comma (more to come)?
JP Z,SCPTLP ; Yes - More subscripts
CALL CHKSYN ; Make sure ")" follows
.BYTE ")"
LD (NXTOPR),HL ; Save code string address
POP HL ; Get LCRFLG and TYPE
LD (LCRFLG),HL ; Restore Locate/create & type
LD E,0 ; Flag not CSAVE* or CLOAD*
PUSH DE ; Save number of dimensions (D)
.BYTE 11H ; Skip "PUSH HL" and "PUSH AF'
ARLDSV: PUSH HL ; Save code string address
PUSH AF ; A = 00 , Flags set = Z,N
LD HL,(VAREND) ; Start of arrays
.BYTE 3EH ; Skip "ADD HL,DE"
FNDARY: ADD HL,DE ; Move to next array start
EX DE,HL
LD HL,(ARREND) ; End of arrays
EX DE,HL ; Current array pointer
CALL CPDEHL ; End of arrays found?
JP Z,CREARY ; Yes - Create array
LD A,(HL) ; Get second byte of name
CP C ; Compare with name given
INC HL ; Move on
JP NZ,NXTARY ; Different - Find next array
LD A,(HL) ; Get first byte of name
CP B ; Compare with name given
NXTARY: INC HL ; Move on
LD E,(HL) ; Get LSB of next array address
INC HL
LD D,(HL) ; Get MSB of next array address
INC HL
JP NZ,FNDARY ; Not found - Keep looking
LD A,(LCRFLG) ; Found Locate or Create it?
OR A
JP NZ,DDERR ; Create - ?DD Error
POP AF ; Locate - Get number of dim'ns
LD B,H ; BC Points to array dim'ns
LD C,L
JP Z,POPHRT ; Jump if array load/save
SUB (HL) ; Same number of dimensions?
JP Z,FINDEL ; Yes - Find element
BSERR: LD E,BS ; ?BS Error
JP ERROR ; Output error
CREARY: LD DE,4 ; 4 Bytes per entry
POP AF ; Array to save or 0 dim'ns?
JP Z,FCERR ; Yes - ?FC Error
LD (HL),C ; Save second byte of name
INC HL
LD (HL),B ; Save first byte of name
INC HL
LD C,A ; Number of dimensions to C
CALL CHKSTK ; Check if enough memory
INC HL ; Point to number of dimensions
INC HL
LD (CUROPR),HL ; Save address of pointer
LD (HL),C ; Set number of dimensions
INC HL
LD A,(LCRFLG) ; Locate of Create?
RLA ; Carry set = Create
LD A,C ; Get number of dimensions
CRARLP: LD BC,10+1 ; Default dimension size 10
JP NC,DEFSIZ ; Locate - Set default size
POP BC ; Get specified dimension size
INC BC ; Include zero element
DEFSIZ: LD (HL),C ; Save LSB of dimension size
INC HL
LD (HL),B ; Save MSB of dimension size
INC HL
PUSH AF ; Save num' of dim'ns an status
PUSH HL ; Save address of dim'n size
CALL MLDEBC ; Multiply DE by BC to find
EX DE,HL ; amount of mem needed (to DE)
POP HL ; Restore address of dimension
POP AF ; Restore number of dimensions
DEC A ; Count them
JP NZ,CRARLP ; Do next dimension if more
PUSH AF ; Save locate/create flag
LD B,D ; MSB of memory needed
LD C,E ; LSB of memory needed
EX DE,HL
ADD HL,DE ; Add bytes to array start
JP C,OMERR ; Too big - Error
CALL ENFMEM ; See if enough memory
LD (ARREND),HL ; Save new end of array
ZERARY: DEC HL ; Back through array data
LD (HL),0 ; Set array element to zero
CALL CPDEHL ; All elements zeroed?
JP NZ,ZERARY ; No - Keep on going
INC BC ; Number of bytes + 1
LD D,A ; A=0
LD HL,(CUROPR) ; Get address of array
LD E,(HL) ; Number of dimensions
EX DE,HL ; To HL
ADD HL,HL ; Two bytes per dimension size
ADD HL,BC ; Add number of bytes
EX DE,HL ; Bytes needed to DE
DEC HL
DEC HL
LD (HL),E ; Save LSB of bytes needed
INC HL
LD (HL),D ; Save MSB of bytes needed
INC HL
POP AF ; Locate / Create?
JP C,ENDDIM ; A is 0 , End if create
FINDEL: LD B,A ; Find array element
LD C,A
LD A,(HL) ; Number of dimensions
INC HL
.BYTE 16H ; Skip "POP HL"
FNDELP: POP HL ; Address of next dim' size
LD E,(HL) ; Get LSB of dim'n size
INC HL
LD D,(HL) ; Get MSB of dim'n size
INC HL
EX (SP),HL ; Save address - Get index
PUSH AF ; Save number of dim'ns
CALL CPDEHL ; Dimension too large?
JP NC,BSERR ; Yes - ?BS Error
PUSH HL ; Save index
CALL MLDEBC ; Multiply previous by size
POP DE ; Index supplied to DE
ADD HL,DE ; Add index to pointer
POP AF ; Number of dimensions
DEC A ; Count them
LD B,H ; MSB of pointer
LD C,L ; LSB of pointer
JP NZ,FNDELP ; More - Keep going
ADD HL,HL ; 4 Bytes per element
ADD HL,HL
POP BC ; Start of array
ADD HL,BC ; Point to element
EX DE,HL ; Address of element to DE
ENDDIM: LD HL,(NXTOPR) ; Got code string address
RET
FRE: LD HL,(ARREND) ; Start of free memory
EX DE,HL ; To DE
LD HL,0 ; End of free memory
ADD HL,SP ; Current stack value
LD A,(TYPE) ; Dummy argument type
OR A
JP Z,FRENUM ; Numeric - Free variable space
CALL GSTRCU ; Current string to pool
CALL GARBGE ; Garbage collection
LD HL,(STRSPC) ; Bottom of string space in use
EX DE,HL ; To DE
LD HL,(STRBOT) ; Bottom of string space
FRENUM: LD A,L ; Get LSB of end
SUB E ; Subtract LSB of beginning
LD C,A ; Save difference if C
LD A,H ; Get MSB of end
SBC A,D ; Subtract MSB of beginning
ACPASS: LD B,C ; Return integer AC
ABPASS: LD D,B ; Return integer AB
LD E,0
LD HL,TYPE ; Point to type
LD (HL),E ; Set type to numeric
LD B,80H+16 ; 16 bit integer
JP RETINT ; Return the integr
POS: LD A,(CURPOS) ; Get cursor position
PASSA: LD B,A ; Put A into AB
XOR A ; Zero A
JP ABPASS ; Return integer AB
DEF: CALL CHEKFN ; Get "FN" and name
CALL IDTEST ; Test for illegal direct
LD BC,DATA ; To get next statement
PUSH BC ; Save address for RETurn
PUSH DE ; Save address of function ptr
CALL CHKSYN ; Make sure "(" follows
.BYTE "("
CALL GETVAR ; Get argument variable name
PUSH HL ; Save code string address
EX DE,HL ; Argument address to HL
DEC HL
LD D,(HL) ; Get first byte of arg name
DEC HL
LD E,(HL) ; Get second byte of arg name
POP HL ; Restore code string address
CALL TSTNUM ; Make sure numeric argument
CALL CHKSYN ; Make sure ")" follows
.BYTE ")"
CALL CHKSYN ; Make sure "=" follows
.BYTE ZEQUAL ; "=" token
LD B,H ; Code string address to BC
LD C,L
EX (SP),HL ; Save code str , Get FN ptr
LD (HL),C ; Save LSB of FN code string
INC HL
LD (HL),B ; Save MSB of FN code string
JP SVSTAD ; Save address and do function
DOFN: CALL CHEKFN ; Make sure FN follows
PUSH DE ; Save function pointer address
CALL EVLPAR ; Evaluate expression in "()"
CALL TSTNUM ; Make sure numeric result
EX (SP),HL ; Save code str , Get FN ptr
LD E,(HL) ; Get LSB of FN code string
INC HL
LD D,(HL) ; Get MSB of FN code string
INC HL
LD A,D ; And function DEFined?
OR E
JP Z,UFERR ; No - ?UF Error
LD A,(HL) ; Get LSB of argument address
INC HL
LD H,(HL) ; Get MSB of argument address
LD L,A ; HL = Arg variable address
PUSH HL ; Save it
LD HL,(FNRGNM) ; Get old argument name
EX (SP),HL ; ; Save old , Get new
LD (FNRGNM),HL ; Set new argument name
LD HL,(FNARG+2) ; Get LSB,NLSB of old arg value
PUSH HL ; Save it
LD HL,(FNARG) ; Get MSB,EXP of old arg value
PUSH HL ; Save it
LD HL,FNARG ; HL = Value of argument
PUSH DE ; Save FN code string address
CALL FPTHL ; Move FPREG to argument
POP HL ; Get FN code string address
CALL GETNUM ; Get value from function
DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
JP NZ,SNERR ; Bad character in FN - Error
POP HL ; Get MSB,EXP of old arg
LD (FNARG),HL ; Restore it
POP HL ; Get LSB,NLSB of old arg
LD (FNARG+2),HL ; Restore it
POP HL ; Get name of old arg
LD (FNRGNM),HL ; Restore it
POP HL ; Restore code string address
RET
IDTEST: PUSH HL ; Save code string address
LD HL,(LINEAT) ; Get current line number
INC HL ; -1 means direct statement
LD A,H
OR L
POP HL ; Restore code string address
RET NZ ; Return if in program
LD E,ID ; ?ID Error
JP ERROR
CHEKFN: CALL CHKSYN ; Make sure FN follows
.BYTE ZFN ; "FN" token
LD A,80H
LD (FORFLG),A ; Flag FN name to find
OR (HL) ; FN name has bit 7 set
LD B,A ; in first byte of name
CALL GTFNAM ; Get FN name
JP TSTNUM ; Make sure numeric function
STR: CALL TSTNUM ; Make sure it's a number
CALL NUMASC ; Turn number into text
STR1: CALL CRTST ; Create string entry for it
CALL GSTRCU ; Current string to pool
LD BC,TOPOOL ; Save in string pool
PUSH BC ; Save address on stack
SAVSTR: LD A,(HL) ; Get string length
INC HL
INC HL
PUSH HL ; Save pointer to string
CALL TESTR ; See if enough string space
POP HL ; Restore pointer to string
LD C,(HL) ; Get LSB of address
INC HL
LD B,(HL) ; Get MSB of address
CALL CRTMST ; Create string entry
PUSH HL ; Save pointer to MSB of addr
LD L,A ; Length of string
CALL TOSTRA ; Move to string area
POP DE ; Restore pointer to MSB
RET
MKTMST: CALL TESTR ; See if enough string space
CRTMST: LD HL,TMPSTR ; Temporary string
PUSH HL ; Save it
LD (HL),A ; Save length of string
INC HL
SVSTAD: INC HL
LD (HL),E ; Save LSB of address
INC HL
LD (HL),D ; Save MSB of address
POP HL ; Restore pointer
RET
CRTST: DEC HL ; DEC - INCed after
QTSTR: LD B,'"' ; Terminating quote
LD D,B ; Quote to D
DTSTR: PUSH HL ; Save start
LD C,-1 ; Set counter to -1
QTSTLP: INC HL ; Move on
LD A,(HL) ; Get byte
INC C ; Count bytes
OR A ; End of line?
JP Z,CRTSTE ; Yes - Create string entry
CP D ; Terminator D found?
JP Z,CRTSTE ; Yes - Create string entry
CP B ; Terminator B found?
JP NZ,QTSTLP ; No - Keep looking
CRTSTE: CP '"' ; End with '"'?
CALL Z,GETCHR ; Yes - Get next character
EX (SP),HL ; Starting quote
INC HL ; First byte of string
EX DE,HL ; To DE
LD A,C ; Get length
CALL CRTMST ; Create string entry
TSTOPL: LD DE,TMPSTR ; Temporary string
LD HL,(TMSTPT) ; Temporary string pool pointer
LD (FPREG),HL ; Save address of string ptr
LD A,1
LD (TYPE),A ; Set type to string
CALL DETHL4 ; Move string to pool
CALL CPDEHL ; Out of string pool?
LD (TMSTPT),HL ; Save new pointer
POP HL ; Restore code string address
LD A,(HL) ; Get next code byte
RET NZ ; Return if pool OK
LD E,ST ; ?ST Error
JP ERROR ; String pool overflow
PRNUMS: INC HL ; Skip leading space
PRS: CALL CRTST ; Create string entry for it
PRS1: CALL GSTRCU ; Current string to pool
CALL LOADFP ; Move string block to BCDE
INC E ; Length + 1
PRSLP: DEC E ; Count characters
RET Z ; End of string
LD A,(BC) ; Get byte to output
CALL OUTC ; Output character in A
CP CR ; Return?
CALL Z,DONULL ; Yes - Do nulls
INC BC ; Next byte in string
JP PRSLP ; More characters to output
TESTR: OR A ; Test if enough room
.BYTE 0EH ; No garbage collection done
GRBDON: POP AF ; Garbage collection done
PUSH AF ; Save status
LD HL,(STRSPC) ; Bottom of string space in use
EX DE,HL ; To DE
LD HL,(STRBOT) ; Bottom of string area
CPL ; Negate length (Top down)
LD C,A ; -Length to BC
LD B,-1 ; BC = -ve length of string
ADD HL,BC ; Add to bottom of space in use
INC HL ; Plus one for 2's complement
CALL CPDEHL ; Below string RAM area?
JP C,TESTOS ; Tidy up if not done else err
LD (STRBOT),HL ; Save new bottom of area
INC HL ; Point to first byte of string
EX DE,HL ; Address to DE
POPAF: POP AF ; Throw away status push
RET
TESTOS: POP AF ; Garbage collect been done?
LD E,OS ; ?OS Error
JP Z,ERROR ; Yes - Not enough string apace
CP A ; Flag garbage collect done
PUSH AF ; Save status
LD BC,GRBDON ; Garbage collection done
PUSH BC ; Save for RETurn
GARBGE: LD HL,(LSTRAM) ; Get end of RAM pointer
GARBLP: LD (STRBOT),HL ; Reset string pointer
LD HL,0
PUSH HL ; Flag no string found
LD HL,(STRSPC) ; Get bottom of string space
PUSH HL ; Save bottom of string space
LD HL,TMSTPL ; Temporary string pool
GRBLP: EX DE,HL
LD HL,(TMSTPT) ; Temporary string pool pointer
EX DE,HL
CALL CPDEHL ; Temporary string pool done?
LD BC,GRBLP ; Loop until string pool done
JP NZ,STPOOL ; No - See if in string area
LD HL,(PROGND) ; Start of simple variables
SMPVAR: EX DE,HL
LD HL,(VAREND) ; End of simple variables
EX DE,HL
CALL CPDEHL ; All simple strings done?
JP Z,ARRLP ; Yes - Do string arrays
LD A,(HL) ; Get type of variable
INC HL
INC HL
OR A ; "S" flag set if string
CALL STRADD ; See if string in string area
JP SMPVAR ; Loop until simple ones done
GNXARY: POP BC ; Scrap address of this array
ARRLP: EX DE,HL
LD HL,(ARREND) ; End of string arrays
EX DE,HL
CALL CPDEHL ; All string arrays done?
JP Z,SCNEND ; Yes - Move string if found
CALL LOADFP ; Get array name to BCDE
LD A,E ; Get type of array
PUSH HL ; Save address of num of dim'ns
ADD HL,BC ; Start of next array
OR A ; Test type of array
JP P,GNXARY ; Numeric array - Ignore it
LD (CUROPR),HL ; Save address of next array
POP HL ; Get address of num of dim'ns
LD C,(HL) ; BC = Number of dimensions
LD B,0
ADD HL,BC ; Two bytes per dimension size
ADD HL,BC
INC HL ; Plus one for number of dim'ns
GRBARY: EX DE,HL
LD HL,(CUROPR) ; Get address of next array
EX DE,HL
CALL CPDEHL ; Is this array finished?
JP Z,ARRLP ; Yes - Get next one
LD BC,GRBARY ; Loop until array all done
STPOOL: PUSH BC ; Save return address
OR 80H ; Flag string type
STRADD: LD A,(HL) ; Get string length
INC HL
INC HL
LD E,(HL) ; Get LSB of string address
INC HL
LD D,(HL) ; Get MSB of string address
INC HL
RET P ; Not a string - Return
OR A ; Set flags on string length
RET Z ; Null string - Return
LD B,H ; Save variable pointer
LD C,L
LD HL,(STRBOT) ; Bottom of new area
CALL CPDEHL ; String been done?
LD H,B ; Restore variable pointer
LD L,C
RET C ; String done - Ignore
POP HL ; Return address
EX (SP),HL ; Lowest available string area
CALL CPDEHL ; String within string area?
EX (SP),HL ; Lowest available string area
PUSH HL ; Re-save return address
LD H,B ; Restore variable pointer
LD L,C
RET NC ; Outside string area - Ignore
POP BC ; Get return , Throw 2 away
POP AF ;
POP AF ;
PUSH HL ; Save variable pointer
PUSH DE ; Save address of current
PUSH BC ; Put back return address
RET ; Go to it
SCNEND: POP DE ; Addresses of strings
POP HL ;
LD A,L ; HL = 0 if no more to do
OR H
RET Z ; No more to do - Return
DEC HL
LD B,(HL) ; MSB of address of string
DEC HL
LD C,(HL) ; LSB of address of string
PUSH HL ; Save variable address
DEC HL
DEC HL
LD L,(HL) ; HL = Length of string
LD H,0
ADD HL,BC ; Address of end of string+1
LD D,B ; String address to DE
LD E,C
DEC HL ; Last byte in string
LD B,H ; Address to BC
LD C,L
LD HL,(STRBOT) ; Current bottom of string area
CALL MOVSTR ; Move string to new address
POP HL ; Restore variable address
LD (HL),C ; Save new LSB of address
INC HL
LD (HL),B ; Save new MSB of address
LD L,C ; Next string area+1 to HL
LD H,B
DEC HL ; Next string area address
JP GARBLP ; Look for more strings
CONCAT: PUSH BC ; Save prec' opr & code string
PUSH HL ;
LD HL,(FPREG) ; Get first string
EX (SP),HL ; Save first string
CALL OPRND ; Get second string
EX (SP),HL ; Restore first string
CALL TSTSTR ; Make sure it's a string
LD A,(HL) ; Get length of second string
PUSH HL ; Save first string
LD HL,(FPREG) ; Get second string
PUSH HL ; Save second string
ADD A,(HL) ; Add length of second string
LD E,LS ; ?LS Error
JP C,ERROR ; String too long - Error
CALL MKTMST ; Make temporary string
POP DE ; Get second string to DE
CALL GSTRDE ; Move to string pool if needed
EX (SP),HL ; Get first string
CALL GSTRHL ; Move to string pool if needed
PUSH HL ; Save first string
LD HL,(TMPSTR+2) ; Temporary string address
EX DE,HL ; To DE
CALL SSTSA ; First string to string area
CALL SSTSA ; Second string to string area
LD HL,EVAL2 ; Return to evaluation loop
EX (SP),HL ; Save return,get code string
PUSH HL ; Save code string address
JP TSTOPL ; To temporary string to pool
SSTSA: POP HL ; Return address
EX (SP),HL ; Get string block,save return
LD A,(HL) ; Get length of string
INC HL
INC HL
LD C,(HL) ; Get LSB of string address
INC HL
LD B,(HL) ; Get MSB of string address
LD L,A ; Length to L
TOSTRA: INC L ; INC - DECed after
TSALP: DEC L ; Count bytes moved
RET Z ; End of string - Return
LD A,(BC) ; Get source
LD (DE),A ; Save destination
INC BC ; Next source
INC DE ; Next destination
JP TSALP ; Loop until string moved
GETSTR: CALL TSTSTR ; Make sure it's a string
GSTRCU: LD HL,(FPREG) ; Get current string
GSTRHL: EX DE,HL ; Save DE
GSTRDE: CALL BAKTMP ; Was it last tmp-str?
EX DE,HL ; Restore DE
RET NZ ; No - Return
PUSH DE ; Save string
LD D,B ; String block address to DE
LD E,C
DEC DE ; Point to length
LD C,(HL) ; Get string length
LD HL,(STRBOT) ; Current bottom of string area
CALL CPDEHL ; Last one in string area?
JP NZ,POPHL ; No - Return
LD B,A ; Clear B (A=0)
ADD HL,BC ; Remove string from str' area
LD (STRBOT),HL ; Save new bottom of str' area
POPHL: POP HL ; Restore string
RET
BAKTMP: LD HL,(TMSTPT) ; Get temporary string pool top
DEC HL ; Back
LD B,(HL) ; Get MSB of address
DEC HL ; Back
LD C,(HL) ; Get LSB of address
DEC HL ; Back
DEC HL ; Back
CALL CPDEHL ; String last in string pool?
RET NZ ; Yes - Leave it
LD (TMSTPT),HL ; Save new string pool top
RET
LEN: LD BC,PASSA ; To return integer A
PUSH BC ; Save address
GETLEN: CALL GETSTR ; Get string and its length
XOR A
LD D,A ; Clear D
LD (TYPE),A ; Set type to numeric
LD A,(HL) ; Get length of string
OR A ; Set status flags
RET
ASC: LD BC,PASSA ; To return integer A
PUSH BC ; Save address
GTFLNM: CALL GETLEN ; Get length of string
JP Z,FCERR ; Null string - Error
INC HL
INC HL
LD E,(HL) ; Get LSB of address
INC HL
LD D,(HL) ; Get MSB of address
LD A,(DE) ; Get first byte of string
RET
CHR: LD A,1 ; One character string
CALL MKTMST ; Make a temporary string
CALL MAKINT ; Make it integer A
LD HL,(TMPSTR+2) ; Get address of string
LD (HL),E ; Save character
TOPOOL: POP BC ; Clean up stack
JP TSTOPL ; Temporary string to pool
LEFT: CALL LFRGNM ; Get number and ending ")"
XOR A ; Start at first byte in string
RIGHT1: EX (SP),HL ; Save code string,Get string
LD C,A ; Starting position in string
MID1: PUSH HL ; Save string block address
LD A,(HL) ; Get length of string
CP B ; Compare with number given
JP C,ALLFOL ; All following bytes required
LD A,B ; Get new length
.BYTE 11H ; Skip "LD C,0"
ALLFOL: LD C,0 ; First byte of string
PUSH BC ; Save position in string
CALL TESTR ; See if enough string space
POP BC ; Get position in string
POP HL ; Restore string block address
PUSH HL ; And re-save it
INC HL
INC HL
LD B,(HL) ; Get LSB of address
INC HL
LD H,(HL) ; Get MSB of address
LD L,B ; HL = address of string
LD B,0 ; BC = starting address
ADD HL,BC ; Point to that byte
LD B,H ; BC = source string
LD C,L
CALL CRTMST ; Create a string entry
LD L,A ; Length of new string
CALL TOSTRA ; Move string to string area
POP DE ; Clear stack
CALL GSTRDE ; Move to string pool if needed
JP TSTOPL ; Temporary string to pool
RIGHT: CALL LFRGNM ; Get number and ending ")"
POP DE ; Get string length
PUSH DE ; And re-save
LD A,(DE) ; Get length
SUB B ; Move back N bytes
JP RIGHT1 ; Go and get sub-string
MID: EX DE,HL ; Get code string address
LD A,(HL) ; Get next byte ',' or ")"
CALL MIDNUM ; Get number supplied
INC B ; Is it character zero?
DEC B
JP Z,FCERR ; Yes - Error
PUSH BC ; Save starting position
LD E,255 ; All of string
CP ')' ; Any length given?
JP Z,RSTSTR ; No - Rest of string
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
CALL GETINT ; Get integer 0-255
RSTSTR: CALL CHKSYN ; Make sure ")" follows
.BYTE ")"
POP AF ; Restore starting position
EX (SP),HL ; Get string,8ave code string
LD BC,MID1 ; Continuation of MID$ routine
PUSH BC ; Save for return
DEC A ; Starting position-1
CP (HL) ; Compare with length
LD B,0 ; Zero bytes length
RET NC ; Null string if start past end
LD C,A ; Save starting position-1
LD A,(HL) ; Get length of string
SUB C ; Subtract start
CP E ; Enough string for it?
LD B,A ; Save maximum length available
RET C ; Truncate string if needed
LD B,E ; Set specified length
RET ; Go and create string
VAL: CALL GETLEN ; Get length of string
JP Z,RESZER ; Result zero
LD E,A ; Save length
INC HL
INC HL
LD A,(HL) ; Get LSB of address
INC HL
LD H,(HL) ; Get MSB of address
LD L,A ; HL = String address
PUSH HL ; Save string address
ADD HL,DE
LD B,(HL) ; Get end of string+1 byte
LD (HL),D ; Zero it to terminate
EX (SP),HL ; Save string end,get start
PUSH BC ; Save end+1 byte
LD A,(HL) ; Get starting byte
CP '$' ; Hex number indicated? [function added]
JP NZ,VAL1
CALL HEXTFP ; Convert Hex to FPREG
JR VAL3
VAL1: CP '%' ; Binary number indicated? [function added]
JP NZ,VAL2
CALL BINTFP ; Convert Bin to FPREG
JR VAL3
VAL2: CALL ASCTFP ; Convert ASCII string to FP
VAL3: POP BC ; Restore end+1 byte
POP HL ; Restore end+1 address
LD (HL),B ; Put back original byte
RET
LFRGNM: EX DE,HL ; Code string address to HL
CALL CHKSYN ; Make sure ")" follows
.BYTE ")"
MIDNUM: POP BC ; Get return address
POP DE ; Get number supplied
PUSH BC ; Re-save return address
LD B,E ; Number to B
RET
INP: CALL MAKINT ; Make it integer A
LD (INPORT),A ; Set input port
CALL INPSUB ; Get input from port
JP PASSA ; Return integer A
POUT: CALL SETIO ; Set up port number
JP OUTSUB ; Output data and return
WAIT: CALL SETIO ; Set up port number
PUSH AF ; Save AND mask
LD E,0 ; Assume zero if none given
DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
JP Z,NOXOR ; No XOR byte given
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
CALL GETINT ; Get integer 0-255 to XOR with
NOXOR: POP BC ; Restore AND mask
WAITLP: CALL INPSUB ; Get input
XOR E ; Flip selected bits
AND B ; Result non-zero?
JP Z,WAITLP ; No = keep waiting
RET
SETIO: CALL GETINT ; Get integer 0-255
LD (INPORT),A ; Set input port
LD (OTPORT),A ; Set output port
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
JP GETINT ; Get integer 0-255 and return
FNDNUM: CALL GETCHR ; Get next character
GETINT: CALL GETNUM ; Get a number from 0 to 255
MAKINT: CALL DEPINT ; Make sure value 0 - 255
LD A,D ; Get MSB of number
OR A ; Zero?
JP NZ,FCERR ; No - Error
DEC HL ; DEC 'cos GETCHR INCs
CALL GETCHR ; Get next character
LD A,E ; Get number to A
RET
PEEK: CALL DEINT ; Get memory address
LD A,(DE) ; Get byte in memory
JP PASSA ; Return integer A
POKE: CALL GETNUM ; Get memory address
CALL DEINT ; Get integer -32768 to 3276
PUSH DE ; Save memory address
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
CALL GETINT ; Get integer 0-255
POP DE ; Restore memory address
LD (DE),A ; Load it into memory
RET
ROUND: LD HL,HALF ; Add 0.5 to FPREG
ADDPHL: CALL LOADFP ; Load FP at (HL) to BCDE
JP FPADD ; Add BCDE to FPREG
SUBPHL: CALL LOADFP ; FPREG = -FPREG + number at HL
.BYTE 21H ; Skip "POP BC" and "POP DE"
PSUB: POP BC ; Get FP number from stack
POP DE
SUBCDE: CALL INVSGN ; Negate FPREG
FPADD: LD A,B ; Get FP exponent
OR A ; Is number zero?
RET Z ; Yes - Nothing to add
LD A,(FPEXP) ; Get FPREG exponent
OR A ; Is this number zero?
JP Z,FPBCDE ; Yes - Move BCDE to FPREG
SUB B ; BCDE number larger?
JP NC,NOSWAP ; No - Don't swap them
CPL ; Two's complement
INC A ; FP exponent
EX DE,HL
CALL STAKFP ; Put FPREG on stack
EX DE,HL
CALL FPBCDE ; Move BCDE to FPREG
POP BC ; Restore number from stack
POP DE
NOSWAP: CP 24+1 ; Second number insignificant?
RET NC ; Yes - First number is result
PUSH AF ; Save number of bits to scale
CALL SIGNS ; Set MSBs & sign of result
LD H,A ; Save sign of result
POP AF ; Restore scaling factor
CALL SCALE ; Scale BCDE to same exponent
OR H ; Result to be positive?
LD HL,FPREG ; Point to FPREG
JP P,MINCDE ; No - Subtract FPREG from CDE
CALL PLUCDE ; Add FPREG to CDE
JP NC,RONDUP ; No overflow - Round it up
INC HL ; Point to exponent
INC (HL) ; Increment it
JP Z,OVERR ; Number overflowed - Error
LD L,1 ; 1 bit to shift right
CALL SHRT1 ; Shift result right
JP RONDUP ; Round it up
MINCDE: XOR A ; Clear A and carry
SUB B ; Negate exponent
LD B,A ; Re-save exponent
LD A,(HL) ; Get LSB of FPREG
SBC A, E ; Subtract LSB of BCDE
LD E,A ; Save LSB of BCDE
INC HL
LD A,(HL) ; Get NMSB of FPREG
SBC A,D ; Subtract NMSB of BCDE
LD D,A ; Save NMSB of BCDE
INC HL
LD A,(HL) ; Get MSB of FPREG
SBC A,C ; Subtract MSB of BCDE
LD C,A ; Save MSB of BCDE
CONPOS: CALL C,COMPL ; Overflow - Make it positive
BNORM: LD L,B ; L = Exponent
LD H,E ; H = LSB
XOR A
BNRMLP: LD B,A ; Save bit count
LD A,C ; Get MSB
OR A ; Is it zero?
JP NZ,PNORM ; No - Do it bit at a time
LD C,D ; MSB = NMSB
LD D,H ; NMSB= LSB
LD H,L ; LSB = VLSB
LD L,A ; VLSB= 0
LD A,B ; Get exponent
SUB 8 ; Count 8 bits
CP -24-8 ; Was number zero?
JP NZ,BNRMLP ; No - Keep normalising
RESZER: XOR A ; Result is zero
SAVEXP: LD (FPEXP),A ; Save result as zero
RET
NORMAL: DEC B ; Count bits
ADD HL,HL ; Shift HL left
LD A,D ; Get NMSB
RLA ; Shift left with last bit
LD D,A ; Save NMSB
LD A,C ; Get MSB
ADC A,A ; Shift left with last bit
LD C,A ; Save MSB
PNORM: JP P,NORMAL ; Not done - Keep going
LD A,B ; Number of bits shifted
LD E,H ; Save HL in EB
LD B,L
OR A ; Any shifting done?
JP Z,RONDUP ; No - Round it up
LD HL,FPEXP ; Point to exponent
ADD A,(HL) ; Add shifted bits
LD (HL),A ; Re-save exponent
JP NC,RESZER ; Underflow - Result is zero
RET Z ; Result is zero
RONDUP: LD A,B ; Get VLSB of number
RONDB: LD HL,FPEXP ; Point to exponent
OR A ; Any rounding?
CALL M,FPROND ; Yes - Round number up
LD B,(HL) ; B = Exponent
INC HL
LD A,(HL) ; Get sign of result
AND 10000000B ; Only bit 7 needed
XOR C ; Set correct sign
LD C,A ; Save correct sign in number
JP FPBCDE ; Move BCDE to FPREG
FPROND: INC E ; Round LSB
RET NZ ; Return if ok
INC D ; Round NMSB
RET NZ ; Return if ok
INC C ; Round MSB
RET NZ ; Return if ok
LD C,80H ; Set normal value
INC (HL) ; Increment exponent
RET NZ ; Return if ok
JP OVERR ; Overflow error
PLUCDE: LD A,(HL) ; Get LSB of FPREG
ADD A,E ; Add LSB of BCDE
LD E,A ; Save LSB of BCDE
INC HL
LD A,(HL) ; Get NMSB of FPREG
ADC A,D ; Add NMSB of BCDE
LD D,A ; Save NMSB of BCDE
INC HL
LD A,(HL) ; Get MSB of FPREG
ADC A,C ; Add MSB of BCDE
LD C,A ; Save MSB of BCDE
RET
COMPL: LD HL,SGNRES ; Sign of result
LD A,(HL) ; Get sign of result
CPL ; Negate it
LD (HL),A ; Put it back
XOR A
LD L,A ; Set L to zero
SUB B ; Negate exponent,set carry
LD B,A ; Re-save exponent
LD A,L ; Load zero
SBC A,E ; Negate LSB
LD E,A ; Re-save LSB
LD A,L ; Load zero
SBC A,D ; Negate NMSB
LD D,A ; Re-save NMSB
LD A,L ; Load zero
SBC A,C ; Negate MSB
LD C,A ; Re-save MSB
RET
SCALE: LD B,0 ; Clear underflow
SCALLP: SUB 8 ; 8 bits (a whole byte)?
JP C,SHRITE ; No - Shift right A bits
LD B,E ; <- Shift
LD E,D ; <- right
LD D,C ; <- eight
LD C,0 ; <- bits
JP SCALLP ; More bits to shift
SHRITE: ADD A,8+1 ; Adjust count
LD L,A ; Save bits to shift
SHRLP: XOR A ; Flag for all done
DEC L ; All shifting done?
RET Z ; Yes - Return
LD A,C ; Get MSB
SHRT1: RRA ; Shift it right
LD C,A ; Re-save
LD A,D ; Get NMSB
RRA ; Shift right with last bit
LD D,A ; Re-save it
LD A,E ; Get LSB
RRA ; Shift right with last bit
LD E,A ; Re-save it
LD A,B ; Get underflow
RRA ; Shift right with last bit
LD B,A ; Re-save underflow
JP SHRLP ; More bits to do
UNITY: .BYTE 000H,000H,000H,081H ; 1.00000
LOGTAB: .BYTE 3 ; Table used by LOG
.BYTE 0AAH,056H,019H,080H ; 0.59898
.BYTE 0F1H,022H,076H,080H ; 0.96147
.BYTE 045H,0AAH,038H,082H ; 2.88539
LOG: CALL TSTSGN ; Test sign of value
OR A
JP PE,FCERR ; ?FC Error if <= zero
LD HL,FPEXP ; Point to exponent
LD A,(HL) ; Get exponent
LD BC,8035H ; BCDE = SQR(1/2)
LD DE,04F3H
SUB B ; Scale value to be < 1
PUSH AF ; Save scale factor
LD (HL),B ; Save new exponent
PUSH DE ; Save SQR(1/2)
PUSH BC
CALL FPADD ; Add SQR(1/2) to value
POP BC ; Restore SQR(1/2)
POP DE
INC B ; Make it SQR(2)
CALL DVBCDE ; Divide by SQR(2)
LD HL,UNITY ; Point to 1.
CALL SUBPHL ; Subtract FPREG from 1
LD HL,LOGTAB ; Coefficient table
CALL SUMSER ; Evaluate sum of series
LD BC,8080H ; BCDE = -0.5
LD DE,0000H
CALL FPADD ; Subtract 0.5 from FPREG
POP AF ; Restore scale factor
CALL RSCALE ; Re-scale number
MULLN2: LD BC,8031H ; BCDE = Ln(2)
LD DE,7218H
.BYTE 21H ; Skip "POP BC" and "POP DE"
MULT: POP BC ; Get number from stack
POP DE
FPMULT: CALL TSTSGN ; Test sign of FPREG
RET Z ; Return zero if zero
LD L,0 ; Flag add exponents
CALL ADDEXP ; Add exponents
LD A,C ; Get MSB of multiplier
LD (MULVAL),A ; Save MSB of multiplier
EX DE,HL
LD (MULVAL+1),HL ; Save rest of multiplier
LD BC,0 ; Partial product (BCDE) = zero
LD D,B
LD E,B
LD HL,BNORM ; Address of normalise
PUSH HL ; Save for return
LD HL,MULT8 ; Address of 8 bit multiply
PUSH HL ; Save for NMSB,MSB
PUSH HL ;
LD HL,FPREG ; Point to number
MULT8: LD A,(HL) ; Get LSB of number
INC HL ; Point to NMSB
OR A ; Test LSB
JP Z,BYTSFT ; Zero - shift to next byte
PUSH HL ; Save address of number
LD L,8 ; 8 bits to multiply by
MUL8LP: RRA ; Shift LSB right
LD H,A ; Save LSB
LD A,C ; Get MSB
JP NC,NOMADD ; Bit was zero - Don't add
PUSH HL ; Save LSB and count
LD HL,(MULVAL+1) ; Get LSB and NMSB
ADD HL,DE ; Add NMSB and LSB
EX DE,HL ; Leave sum in DE
POP HL ; Restore MSB and count
LD A,(MULVAL) ; Get MSB of multiplier
ADC A,C ; Add MSB
NOMADD: RRA ; Shift MSB right
LD C,A ; Re-save MSB
LD A,D ; Get NMSB
RRA ; Shift NMSB right
LD D,A ; Re-save NMSB
LD A,E ; Get LSB
RRA ; Shift LSB right
LD E,A ; Re-save LSB
LD A,B ; Get VLSB
RRA ; Shift VLSB right
LD B,A ; Re-save VLSB
DEC L ; Count bits multiplied
LD A,H ; Get LSB of multiplier
JP NZ,MUL8LP ; More - Do it
POPHRT: POP HL ; Restore address of number
RET
BYTSFT: LD B,E ; Shift partial product left
LD E,D
LD D,C
LD C,A
RET
DIV10: CALL STAKFP ; Save FPREG on stack
LD BC,8420H ; BCDE = 10.
LD DE,0000H
CALL FPBCDE ; Move 10 to FPREG
DIV: POP BC ; Get number from stack
POP DE
DVBCDE: CALL TSTSGN ; Test sign of FPREG
JP Z,DZERR ; Error if division by zero
LD L,-1 ; Flag subtract exponents
CALL ADDEXP ; Subtract exponents
INC (HL) ; Add 2 to exponent to adjust
INC (HL)
DEC HL ; Point to MSB
LD A,(HL) ; Get MSB of dividend
LD (DIV3),A ; Save for subtraction
DEC HL
LD A,(HL) ; Get NMSB of dividend
LD (DIV2),A ; Save for subtraction
DEC HL
LD A,(HL) ; Get MSB of dividend
LD (DIV1),A ; Save for subtraction
LD B,C ; Get MSB
EX DE,HL ; NMSB,LSB to HL
XOR A
LD C,A ; Clear MSB of quotient
LD D,A ; Clear NMSB of quotient
LD E,A ; Clear LSB of quotient
LD (DIV4),A ; Clear overflow count
DIVLP: PUSH HL ; Save divisor
PUSH BC
LD A,L ; Get LSB of number
CALL DIVSUP ; Subt' divisor from dividend
SBC A,0 ; Count for overflows
CCF
JP NC,RESDIV ; Restore divisor if borrow
LD (DIV4),A ; Re-save overflow count
POP AF ; Scrap divisor
POP AF
SCF ; Set carry to
.BYTE 0D2H ; Skip "POP BC" and "POP HL"
RESDIV: POP BC ; Restore divisor
POP HL
LD A,C ; Get MSB of quotient
INC A
DEC A
RRA ; Bit 0 to bit 7
JP M,RONDB ; Done - Normalise result
RLA ; Restore carry
LD A,E ; Get LSB of quotient
RLA ; Double it
LD E,A ; Put it back
LD A,D ; Get NMSB of quotient
RLA ; Double it
LD D,A ; Put it back
LD A,C ; Get MSB of quotient
RLA ; Double it
LD C,A ; Put it back
ADD HL,HL ; Double NMSB,LSB of divisor
LD A,B ; Get MSB of divisor
RLA ; Double it
LD B,A ; Put it back
LD A,(DIV4) ; Get VLSB of quotient
RLA ; Double it
LD (DIV4),A ; Put it back
LD A,C ; Get MSB of quotient
OR D ; Merge NMSB
OR E ; Merge LSB
JP NZ,DIVLP ; Not done - Keep dividing
PUSH HL ; Save divisor
LD HL,FPEXP ; Point to exponent
DEC (HL) ; Divide by 2
POP HL ; Restore divisor
JP NZ,DIVLP ; Ok - Keep going
JP OVERR ; Overflow error
ADDEXP: LD A,B ; Get exponent of dividend
OR A ; Test it
JP Z,OVTST3 ; Zero - Result zero
LD A,L ; Get add/subtract flag
LD HL,FPEXP ; Point to exponent
XOR (HL) ; Add or subtract it
ADD A,B ; Add the other exponent
LD B,A ; Save new exponent
RRA ; Test exponent for overflow
XOR B
LD A,B ; Get exponent
JP P,OVTST2 ; Positive - Test for overflow
ADD A,80H ; Add excess 128
LD (HL),A ; Save new exponent
JP Z,POPHRT ; Zero - Result zero
CALL SIGNS ; Set MSBs and sign of result
LD (HL),A ; Save new exponent
DEC HL ; Point to MSB
RET
OVTST1: CALL TSTSGN ; Test sign of FPREG
CPL ; Invert sign
POP HL ; Clean up stack
OVTST2: OR A ; Test if new exponent zero
OVTST3: POP HL ; Clear off return address
JP P,RESZER ; Result zero
JP OVERR ; Overflow error
MLSP10: CALL BCDEFP ; Move FPREG to BCDE
LD A,B ; Get exponent
OR A ; Is it zero?
RET Z ; Yes - Result is zero
ADD A,2 ; Multiply by 4
JP C,OVERR ; Overflow - ?OV Error
LD B,A ; Re-save exponent
CALL FPADD ; Add BCDE to FPREG (Times 5)
LD HL,FPEXP ; Point to exponent
INC (HL) ; Double number (Times 10)
RET NZ ; Ok - Return
JP OVERR ; Overflow error
TSTSGN: LD A,(FPEXP) ; Get sign of FPREG
OR A
RET Z ; RETurn if number is zero
LD A,(FPREG+2) ; Get MSB of FPREG
.BYTE 0FEH ; Test sign
RETREL: CPL ; Invert sign
RLA ; Sign bit to carry
FLGDIF: SBC A,A ; Carry to all bits of A
RET NZ ; Return -1 if negative
INC A ; Bump to +1
RET ; Positive - Return +1
SGN: CALL TSTSGN ; Test sign of FPREG
FLGREL: LD B,80H+8 ; 8 bit integer in exponent
LD DE,0 ; Zero NMSB and LSB
RETINT: LD HL,FPEXP ; Point to exponent
LD C,A ; CDE = MSB,NMSB and LSB
LD (HL),B ; Save exponent
LD B,0 ; CDE = integer to normalise
INC HL ; Point to sign of result
LD (HL),80H ; Set sign of result
RLA ; Carry = sign of integer
JP CONPOS ; Set sign of result
ABS: CALL TSTSGN ; Test sign of FPREG
RET P ; Return if positive
INVSGN: LD HL,FPREG+2 ; Point to MSB
LD A,(HL) ; Get sign of mantissa
XOR 80H ; Invert sign of mantissa
LD (HL),A ; Re-save sign of mantissa
RET
STAKFP: EX DE,HL ; Save code string address
LD HL,(FPREG) ; LSB,NLSB of FPREG
EX (SP),HL ; Stack them,get return
PUSH HL ; Re-save return
LD HL,(FPREG+2) ; MSB and exponent of FPREG
EX (SP),HL ; Stack them,get return
PUSH HL ; Re-save return
EX DE,HL ; Restore code string address
RET
PHLTFP: CALL LOADFP ; Number at HL to BCDE
FPBCDE: EX DE,HL ; Save code string address
LD (FPREG),HL ; Save LSB,NLSB of number
LD H,B ; Exponent of number
LD L,C ; MSB of number
LD (FPREG+2),HL ; Save MSB and exponent
EX DE,HL ; Restore code string address
RET
BCDEFP: LD HL,FPREG ; Point to FPREG
LOADFP: LD E,(HL) ; Get LSB of number
INC HL
LD D,(HL) ; Get NMSB of number
INC HL
LD C,(HL) ; Get MSB of number
INC HL
LD B,(HL) ; Get exponent of number
INCHL: INC HL ; Used for conditional "INC HL"
RET
FPTHL: LD DE,FPREG ; Point to FPREG
DETHL4: LD B,4 ; 4 bytes to move
DETHLB: LD A,(DE) ; Get source
LD (HL),A ; Save destination
INC DE ; Next source
INC HL ; Next destination
DEC B ; Count bytes
JP NZ,DETHLB ; Loop if more
RET
SIGNS: LD HL,FPREG+2 ; Point to MSB of FPREG
LD A,(HL) ; Get MSB
RLCA ; Old sign to carry
SCF ; Set MSBit
RRA ; Set MSBit of MSB
LD (HL),A ; Save new MSB
CCF ; Complement sign
RRA ; Old sign to carry
INC HL
INC HL
LD (HL),A ; Set sign of result
LD A,C ; Get MSB
RLCA ; Old sign to carry
SCF ; Set MSBit
RRA ; Set MSBit of MSB
LD C,A ; Save MSB
RRA
XOR (HL) ; New sign of result
RET
CMPNUM: LD A,B ; Get exponent of number
OR A
JP Z,TSTSGN ; Zero - Test sign of FPREG
LD HL,RETREL ; Return relation routine
PUSH HL ; Save for return
CALL TSTSGN ; Test sign of FPREG
LD A,C ; Get MSB of number
RET Z ; FPREG zero - Number's MSB
LD HL,FPREG+2 ; MSB of FPREG
XOR (HL) ; Combine signs
LD A,C ; Get MSB of number
RET M ; Exit if signs different
CALL CMPFP ; Compare FP numbers
RRA ; Get carry to sign
XOR C ; Combine with MSB of number
RET
CMPFP: INC HL ; Point to exponent
LD A,B ; Get exponent
CP (HL) ; Compare exponents
RET NZ ; Different
DEC HL ; Point to MBS
LD A,C ; Get MSB
CP (HL) ; Compare MSBs
RET NZ ; Different
DEC HL ; Point to NMSB
LD A,D ; Get NMSB
CP (HL) ; Compare NMSBs
RET NZ ; Different
DEC HL ; Point to LSB
LD A,E ; Get LSB
SUB (HL) ; Compare LSBs
RET NZ ; Different
POP HL ; Drop RETurn
POP HL ; Drop another RETurn
RET
FPINT: LD B,A ; <- Move
LD C,A ; <- exponent
LD D,A ; <- to all
LD E,A ; <- bits
OR A ; Test exponent
RET Z ; Zero - Return zero
PUSH HL ; Save pointer to number
CALL BCDEFP ; Move FPREG to BCDE
CALL SIGNS ; Set MSBs & sign of result
XOR (HL) ; Combine with sign of FPREG
LD H,A ; Save combined signs
CALL M,DCBCDE ; Negative - Decrement BCDE
LD A,80H+24 ; 24 bits
SUB B ; Bits to shift
CALL SCALE ; Shift BCDE
LD A,H ; Get combined sign
RLA ; Sign to carry
CALL C,FPROND ; Negative - Round number up
LD B,0 ; Zero exponent
CALL C,COMPL ; If negative make positive
POP HL ; Restore pointer to number
RET
DCBCDE: DEC DE ; Decrement BCDE
LD A,D ; Test LSBs
AND E
INC A
RET NZ ; Exit if LSBs not FFFF
DEC BC ; Decrement MSBs
RET
INT: LD HL,FPEXP ; Point to exponent
LD A,(HL) ; Get exponent
CP 80H+24 ; Integer accuracy only?
LD A,(FPREG) ; Get LSB
RET NC ; Yes - Already integer
LD A,(HL) ; Get exponent
CALL FPINT ; F.P to integer
LD (HL),80H+24 ; Save 24 bit integer
LD A,E ; Get LSB of number
PUSH AF ; Save LSB
LD A,C ; Get MSB of number
RLA ; Sign to carry
CALL CONPOS ; Set sign of result
POP AF ; Restore LSB of number
RET
MLDEBC: LD HL,0 ; Clear partial product
LD A,B ; Test multiplier
OR C
RET Z ; Return zero if zero
LD A,16 ; 16 bits
MLDBLP: ADD HL,HL ; Shift P.P left
JP C,BSERR ; ?BS Error if overflow
EX DE,HL
ADD HL,HL ; Shift multiplier left
EX DE,HL
JP NC,NOMLAD ; Bit was zero - No add
ADD HL,BC ; Add multiplicand
JP C,BSERR ; ?BS Error if overflow
NOMLAD: DEC A ; Count bits
JP NZ,MLDBLP ; More
RET
ASCTFP: CP '-' ; Negative?
PUSH AF ; Save it and flags
JP Z,CNVNUM ; Yes - Convert number
CP '+' ; Positive?
JP Z,CNVNUM ; Yes - Convert number
DEC HL ; DEC 'cos GETCHR INCs
CNVNUM: CALL RESZER ; Set result to zero
LD B,A ; Digits after point counter
LD D,A ; Sign of exponent
LD E,A ; Exponent of ten
CPL
LD C,A ; Before or after point flag
MANLP: CALL GETCHR ; Get next character
JP C,ADDIG ; Digit - Add to number
CP '.'
JP Z,DPOINT ; '.' - Flag point
CP 'E'
JP NZ,CONEXP ; Not 'E' - Scale number
CALL GETCHR ; Get next character
CALL SGNEXP ; Get sign of exponent
EXPLP: CALL GETCHR ; Get next character
JP C,EDIGIT ; Digit - Add to exponent
INC D ; Is sign negative?
JP NZ,CONEXP ; No - Scale number
XOR A
SUB E ; Negate exponent
LD E,A ; And re-save it
INC C ; Flag end of number
DPOINT: INC C ; Flag point passed
JP Z,MANLP ; Zero - Get another digit
CONEXP: PUSH HL ; Save code string address
LD A,E ; Get exponent
SUB B ; Subtract digits after point
SCALMI: CALL P,SCALPL ; Positive - Multiply number
JP P,ENDCON ; Positive - All done
PUSH AF ; Save number of times to /10
CALL DIV10 ; Divide by 10
POP AF ; Restore count
INC A ; Count divides
ENDCON: JP NZ,SCALMI ; More to do
POP DE ; Restore code string address
POP AF ; Restore sign of number
CALL Z,INVSGN ; Negative - Negate number
EX DE,HL ; Code string address to HL
RET
SCALPL: RET Z ; Exit if no scaling needed
MULTEN: PUSH AF ; Save count
CALL MLSP10 ; Multiply number by 10
POP AF ; Restore count
DEC A ; Count multiplies
RET
ADDIG: PUSH DE ; Save sign of exponent
LD D,A ; Save digit
LD A,B ; Get digits after point
ADC A,C ; Add one if after point
LD B,A ; Re-save counter
PUSH BC ; Save point flags
PUSH HL ; Save code string address
PUSH DE ; Save digit
CALL MLSP10 ; Multiply number by 10
POP AF ; Restore digit
SUB '0' ; Make it absolute
CALL RSCALE ; Re-scale number
POP HL ; Restore code string address
POP BC ; Restore point flags
POP DE ; Restore sign of exponent
JP MANLP ; Get another digit
RSCALE: CALL STAKFP ; Put number on stack
CALL FLGREL ; Digit to add to FPREG
PADD: POP BC ; Restore number
POP DE
JP FPADD ; Add BCDE to FPREG and return
EDIGIT: LD A,E ; Get digit
RLCA ; Times 2
RLCA ; Times 4
ADD A,E ; Times 5
RLCA ; Times 10
ADD A,(HL) ; Add next digit
SUB '0' ; Make it absolute
LD E,A ; Save new digit
JP EXPLP ; Look for another digit
LINEIN: PUSH HL ; Save code string address
LD HL,INMSG ; Output " in "
CALL PRS ; Output string at HL
POP HL ; Restore code string address
PRNTHL: EX DE,HL ; Code string address to DE
XOR A
LD B,80H+24 ; 24 bits
CALL RETINT ; Return the integer
LD HL,PRNUMS ; Print number string
PUSH HL ; Save for return
NUMASC: LD HL,PBUFF ; Convert number to ASCII
PUSH HL ; Save for return
CALL TSTSGN ; Test sign of FPREG
LD (HL),' ' ; Space at start
JP P,SPCFST ; Positive - Space to start
LD (HL),'-' ; '-' sign at start
SPCFST: INC HL ; First byte of number
LD (HL),'0' ; '0' if zero
JP Z,JSTZER ; Return '0' if zero
PUSH HL ; Save buffer address
CALL M,INVSGN ; Negate FPREG if negative
XOR A ; Zero A
PUSH AF ; Save it
CALL RNGTST ; Test number is in range
SIXDIG: LD BC,9143H ; BCDE - 99999.9
LD DE,4FF8H
CALL CMPNUM ; Compare numbers
OR A
JP PO,INRNG ; > 99999.9 - Sort it out
POP AF ; Restore count
CALL MULTEN ; Multiply by ten
PUSH AF ; Re-save count
JP SIXDIG ; Test it again
GTSIXD: CALL DIV10 ; Divide by 10
POP AF ; Get count
INC A ; Count divides
PUSH AF ; Re-save count
CALL RNGTST ; Test number is in range
INRNG: CALL ROUND ; Add 0.5 to FPREG
INC A
CALL FPINT ; F.P to integer
CALL FPBCDE ; Move BCDE to FPREG
LD BC,0306H ; 1E+06 to 1E-03 range
POP AF ; Restore count
ADD A,C ; 6 digits before point
INC A ; Add one
JP M,MAKNUM ; Do it in 'E' form if < 1E-02
CP 6+1+1 ; More than 999999 ?
JP NC,MAKNUM ; Yes - Do it in 'E' form
INC A ; Adjust for exponent
LD B,A ; Exponent of number
LD A,2 ; Make it zero after
MAKNUM: DEC A ; Adjust for digits to do
DEC A
POP HL ; Restore buffer address
PUSH AF ; Save count
LD DE,POWERS ; Powers of ten
DEC B ; Count digits before point
JP NZ,DIGTXT ; Not zero - Do number
LD (HL),'.' ; Save point
INC HL ; Move on
LD (HL),'0' ; Save zero
INC HL ; Move on
DIGTXT: DEC B ; Count digits before point
LD (HL),'.' ; Save point in case
CALL Z,INCHL ; Last digit - move on
PUSH BC ; Save digits before point
PUSH HL ; Save buffer address
PUSH DE ; Save powers of ten
CALL BCDEFP ; Move FPREG to BCDE
POP HL ; Powers of ten table
LD B, '0'-1 ; ASCII '0' - 1
TRYAGN: INC B ; Count subtractions
LD A,E ; Get LSB
SUB (HL) ; Subtract LSB
LD E,A ; Save LSB
INC HL
LD A,D ; Get NMSB
SBC A,(HL) ; Subtract NMSB
LD D,A ; Save NMSB
INC HL
LD A,C ; Get MSB
SBC A,(HL) ; Subtract MSB
LD C,A ; Save MSB
DEC HL ; Point back to start
DEC HL
JP NC,TRYAGN ; No overflow - Try again
CALL PLUCDE ; Restore number
INC HL ; Start of next number
CALL FPBCDE ; Move BCDE to FPREG
EX DE,HL ; Save point in table
POP HL ; Restore buffer address
LD (HL),B ; Save digit in buffer
INC HL ; And move on
POP BC ; Restore digit count
DEC C ; Count digits
JP NZ,DIGTXT ; More - Do them
DEC B ; Any decimal part?
JP Z,DOEBIT ; No - Do 'E' bit
SUPTLZ: DEC HL ; Move back through buffer
LD A,(HL) ; Get character
CP '0' ; '0' character?
JP Z,SUPTLZ ; Yes - Look back for more
CP '.' ; A decimal point?
CALL NZ,INCHL ; Move back over digit
DOEBIT: POP AF ; Get 'E' flag
JP Z,NOENED ; No 'E' needed - End buffer
LD (HL),'E' ; Put 'E' in buffer
INC HL ; And move on
LD (HL),'+' ; Put '+' in buffer
JP P,OUTEXP ; Positive - Output exponent
LD (HL),'-' ; Put '-' in buffer
CPL ; Negate exponent
INC A
OUTEXP: LD B,'0'-1 ; ASCII '0' - 1
EXPTEN: INC B ; Count subtractions
SUB 10 ; Tens digit
JP NC,EXPTEN ; More to do
ADD A,'0'+10 ; Restore and make ASCII
INC HL ; Move on
LD (HL),B ; Save MSB of exponent
JSTZER: INC HL ;
LD (HL),A ; Save LSB of exponent
INC HL
NOENED: LD (HL),C ; Mark end of buffer
POP HL ; Restore code string address
RET
RNGTST: LD BC,9474H ; BCDE = 999999.
LD DE,23F7H
CALL CMPNUM ; Compare numbers
OR A
POP HL ; Return address to HL
JP PO,GTSIXD ; Too big - Divide by ten
JP (HL) ; Otherwise return to caller
HALF: .BYTE 00H,00H,00H,80H ; 0.5
POWERS: .BYTE 0A0H,086H,001H ; 100000
.BYTE 010H,027H,000H ; 10000
.BYTE 0E8H,003H,000H ; 1000
.BYTE 064H,000H,000H ; 100
.BYTE 00AH,000H,000H ; 10
.BYTE 001H,000H,000H ; 1
NEGAFT: LD HL,INVSGN ; Negate result
EX (SP),HL ; To be done after caller
JP (HL) ; Return to caller
SQR: CALL STAKFP ; Put value on stack
LD HL,HALF ; Set power to 1/2
CALL PHLTFP ; Move 1/2 to FPREG
POWER: POP BC ; Get base
POP DE
CALL TSTSGN ; Test sign of power
LD A,B ; Get exponent of base
JP Z,EXP ; Make result 1 if zero
JP P,POWER1 ; Positive base - Ok
OR A ; Zero to negative power?
JP Z,DZERR ; Yes - ?/0 Error
POWER1: OR A ; Base zero?
JP Z,SAVEXP ; Yes - Return zero
PUSH DE ; Save base
PUSH BC
LD A,C ; Get MSB of base
OR 01111111B ; Get sign status
CALL BCDEFP ; Move power to BCDE
JP P,POWER2 ; Positive base - Ok
PUSH DE ; Save power
PUSH BC
CALL INT ; Get integer of power
POP BC ; Restore power
POP DE
PUSH AF ; MSB of base
CALL CMPNUM ; Power an integer?
POP HL ; Restore MSB of base
LD A,H ; but don't affect flags
RRA ; Exponent odd or even?
POWER2: POP HL ; Restore MSB and exponent
LD (FPREG+2),HL ; Save base in FPREG
POP HL ; LSBs of base
LD (FPREG),HL ; Save in FPREG
CALL C,NEGAFT ; Odd power - Negate result
CALL Z,INVSGN ; Negative base - Negate it
PUSH DE ; Save power
PUSH BC
CALL LOG ; Get LOG of base
POP BC ; Restore power
POP DE
CALL FPMULT ; Multiply LOG by power
EXP: CALL STAKFP ; Put value on stack
LD BC,08138H ; BCDE = 1/Ln(2)
LD DE,0AA3BH
CALL FPMULT ; Multiply value by 1/LN(2)
LD A,(FPEXP) ; Get exponent
CP 80H+8 ; Is it in range?
JP NC,OVTST1 ; No - Test for overflow
CALL INT ; Get INT of FPREG
ADD A,80H ; For excess 128
ADD A,2 ; Exponent > 126?
JP C,OVTST1 ; Yes - Test for overflow
PUSH AF ; Save scaling factor
LD HL,UNITY ; Point to 1.
CALL ADDPHL ; Add 1 to FPREG
CALL MULLN2 ; Multiply by LN(2)
POP AF ; Restore scaling factor
POP BC ; Restore exponent
POP DE
PUSH AF ; Save scaling factor
CALL SUBCDE ; Subtract exponent from FPREG
CALL INVSGN ; Negate result
LD HL,EXPTAB ; Coefficient table
CALL SMSER1 ; Sum the series
LD DE,0 ; Zero LSBs
POP BC ; Scaling factor
LD C,D ; Zero MSB
JP FPMULT ; Scale result to correct value
EXPTAB: .BYTE 8 ; Table used by EXP
.BYTE 040H,02EH,094H,074H ; -1/7! (-1/5040)
.BYTE 070H,04FH,02EH,077H ; 1/6! ( 1/720)
.BYTE 06EH,002H,088H,07AH ; -1/5! (-1/120)
.BYTE 0E6H,0A0H,02AH,07CH ; 1/4! ( 1/24)
.BYTE 050H,0AAH,0AAH,07EH ; -1/3! (-1/6)
.BYTE 0FFH,0FFH,07FH,07FH ; 1/2! ( 1/2)
.BYTE 000H,000H,080H,081H ; -1/1! (-1/1)
.BYTE 000H,000H,000H,081H ; 1/0! ( 1/1)
SUMSER: CALL STAKFP ; Put FPREG on stack
LD DE,MULT ; Multiply by "X"
PUSH DE ; To be done after
PUSH HL ; Save address of table
CALL BCDEFP ; Move FPREG to BCDE
CALL FPMULT ; Square the value
POP HL ; Restore address of table
SMSER1: CALL STAKFP ; Put value on stack
LD A,(HL) ; Get number of coefficients
INC HL ; Point to start of table
CALL PHLTFP ; Move coefficient to FPREG
.BYTE 06H ; Skip "POP AF"
SUMLP: POP AF ; Restore count
POP BC ; Restore number
POP DE
DEC A ; Cont coefficients
RET Z ; All done
PUSH DE ; Save number
PUSH BC
PUSH AF ; Save count
PUSH HL ; Save address in table
CALL FPMULT ; Multiply FPREG by BCDE
POP HL ; Restore address in table
CALL LOADFP ; Number at HL to BCDE
PUSH HL ; Save address in table
CALL FPADD ; Add coefficient to FPREG
POP HL ; Restore address in table
JP SUMLP ; More coefficients
RND: CALL TSTSGN ; Test sign of FPREG
LD HL,SEED+2 ; Random number seed
JP M,RESEED ; Negative - Re-seed
LD HL,LSTRND ; Last random number
CALL PHLTFP ; Move last RND to FPREG
LD HL,SEED+2 ; Random number seed
RET Z ; Return if RND(0)
ADD A,(HL) ; Add (SEED)+2)
AND 00000111B ; 0 to 7
LD B,0
LD (HL),A ; Re-save seed
INC HL ; Move to coefficient table
ADD A,A ; 4 bytes
ADD A,A ; per entry
LD C,A ; BC = Offset into table
ADD HL,BC ; Point to coefficient
CALL LOADFP ; Coefficient to BCDE
CALL FPMULT ; ; Multiply FPREG by coefficient
LD A,(SEED+1) ; Get (SEED+1)
INC A ; Add 1
AND 00000011B ; 0 to 3
LD B,0
CP 1 ; Is it zero?
ADC A,B ; Yes - Make it 1
LD (SEED+1),A ; Re-save seed
LD HL,RNDTAB-4 ; Addition table
ADD A,A ; 4 bytes
ADD A,A ; per entry
LD C,A ; BC = Offset into table
ADD HL,BC ; Point to value
CALL ADDPHL ; Add value to FPREG
RND1: CALL BCDEFP ; Move FPREG to BCDE
LD A,E ; Get LSB
LD E,C ; LSB = MSB
XOR 01001111B ; Fiddle around
LD C,A ; New MSB
LD (HL),80H ; Set exponent
DEC HL ; Point to MSB
LD B,(HL) ; Get MSB
LD (HL),80H ; Make value -0.5
LD HL,SEED ; Random number seed
INC (HL) ; Count seed
LD A,(HL) ; Get seed
SUB 171 ; Do it modulo 171
JP NZ,RND2 ; Non-zero - Ok
LD (HL),A ; Zero seed
INC C ; Fillde about
DEC D ; with the
INC E ; number
RND2: CALL BNORM ; Normalise number
LD HL,LSTRND ; Save random number
JP FPTHL ; Move FPREG to last and return
RESEED: LD (HL),A ; Re-seed random numbers
DEC HL
LD (HL),A
DEC HL
LD (HL),A
JP RND1 ; Return RND seed
RNDTAB: .BYTE 068H,0B1H,046H,068H ; Table used by RND
.BYTE 099H,0E9H,092H,069H
.BYTE 010H,0D1H,075H,068H
COS: LD HL,HALFPI ; Point to PI/2
CALL ADDPHL ; Add it to PPREG
SIN: CALL STAKFP ; Put angle on stack
LD BC,8349H ; BCDE = 2 PI
LD DE,0FDBH
CALL FPBCDE ; Move 2 PI to FPREG
POP BC ; Restore angle
POP DE
CALL DVBCDE ; Divide angle by 2 PI
CALL STAKFP ; Put it on stack
CALL INT ; Get INT of result
POP BC ; Restore number
POP DE
CALL SUBCDE ; Make it 0 <= value < 1
LD HL,QUARTR ; Point to 0.25
CALL SUBPHL ; Subtract value from 0.25
CALL TSTSGN ; Test sign of value
SCF ; Flag positive
JP P,SIN1 ; Positive - Ok
CALL ROUND ; Add 0.5 to value
CALL TSTSGN ; Test sign of value
OR A ; Flag negative
SIN1: PUSH AF ; Save sign
CALL P,INVSGN ; Negate value if positive
LD HL,QUARTR ; Point to 0.25
CALL ADDPHL ; Add 0.25 to value
POP AF ; Restore sign
CALL NC,INVSGN ; Negative - Make positive
LD HL,SINTAB ; Coefficient table
JP SUMSER ; Evaluate sum of series
HALFPI: .BYTE 0DBH,00FH,049H,081H ; 1.5708 (PI/2)
QUARTR: .BYTE 000H,000H,000H,07FH ; 0.25
SINTAB: .BYTE 5 ; Table used by SIN
.BYTE 0BAH,0D7H,01EH,086H ; 39.711
.BYTE 064H,026H,099H,087H ;-76.575
.BYTE 058H,034H,023H,087H ; 81.602
.BYTE 0E0H,05DH,0A5H,086H ;-41.342
.BYTE 0DAH,00FH,049H,083H ; 6.2832
TAN: CALL STAKFP ; Put angle on stack
CALL SIN ; Get SIN of angle
POP BC ; Restore angle
POP HL
CALL STAKFP ; Save SIN of angle
EX DE,HL ; BCDE = Angle
CALL FPBCDE ; Angle to FPREG
CALL COS ; Get COS of angle
JP DIV ; TAN = SIN / COS
ATN: CALL TSTSGN ; Test sign of value
CALL M,NEGAFT ; Negate result after if -ve
CALL M,INVSGN ; Negate value if -ve
LD A,(FPEXP) ; Get exponent
CP 81H ; Number less than 1?
JP C,ATN1 ; Yes - Get arc tangnt
LD BC,8100H ; BCDE = 1
LD D,C
LD E,C
CALL DVBCDE ; Get reciprocal of number
LD HL,SUBPHL ; Sub angle from PI/2
PUSH HL ; Save for angle > 1
ATN1: LD HL,ATNTAB ; Coefficient table
CALL SUMSER ; Evaluate sum of series
LD HL,HALFPI ; PI/2 - angle in case > 1
RET ; Number > 1 - Sub from PI/2
ATNTAB: .BYTE 9 ; Table used by ATN
.BYTE 04AH,0D7H,03BH,078H ; 1/17
.BYTE 002H,06EH,084H,07BH ;-1/15
.BYTE 0FEH,0C1H,02FH,07CH ; 1/13
.BYTE 074H,031H,09AH,07DH ;-1/11
.BYTE 084H,03DH,05AH,07DH ; 1/9
.BYTE 0C8H,07FH,091H,07EH ;-1/7
.BYTE 0E4H,0BBH,04CH,07EH ; 1/5
.BYTE 06CH,0AAH,0AAH,07FH ;-1/3
.BYTE 000H,000H,000H,081H ; 1/1
ARET: RET ; A RETurn instruction
GETINP: RST 10H ;input a character
RET
CLS:
LD A,CS ; ASCII Clear screen
JP MONOUT ; Output character
WIDTH: CALL GETINT ; Get integer 0-255
LD A,E ; Width to A
LD (LWIDTH),A ; Set width
RET
LINES: CALL GETNUM ; Get a number
CALL DEINT ; Get integer -32768 to 32767
LD (LINESC),DE ; Set lines counter
LD (LINESN),DE ; Set lines number
RET
DEEK: CALL DEINT ; Get integer -32768 to 32767
PUSH DE ; Save number
POP HL ; Number to HL
LD B,(HL) ; Get LSB of contents
INC HL
LD A,(HL) ; Get MSB of contents
JP ABPASS ; Return integer AB
DOKE: CALL GETNUM ; Get a number
CALL DEINT ; Get integer -32768 to 32767
PUSH DE ; Save address
CALL CHKSYN ; Make sure ',' follows
.BYTE ','
CALL GETNUM ; Get a number
CALL DEINT ; Get integer -32768 to 32767
EX (SP),HL ; Save value,get address
LD (HL),E ; Save LSB of value
INC HL
LD (HL),D ; Save MSB of value
POP HL ; Restore code string address
RET
; HEX$(nn) Convert 16 bit number to Hexadecimal string
HEX: CALL TSTNUM ; Verify it's a number
CALL DEINT ; Get integer -32768 to 32767
PUSH BC ; Save contents of BC
LD HL,PBUFF
LD A,D ; Get high order into A
CP $0
JR Z,HEX2 ; Skip output if both high digits are zero
CALL BYT2ASC ; Convert D to ASCII
LD A,B
CP '0'
JR Z,HEX1 ; Don't store high digit if zero
LD (HL),B ; Store it to PBUFF
INC HL ; Next location
HEX1: LD (HL),C ; Store C to PBUFF+1
INC HL ; Next location
HEX2: LD A,E ; Get lower byte
CALL BYT2ASC ; Convert E to ASCII
LD A,D
CP $0
JR NZ,HEX3 ; If upper byte was not zero then always print lower byte
LD A,B
CP '0' ; If high digit of lower byte is zero then don't print
JR Z,HEX4
HEX3: LD (HL),B ; to PBUFF+2
INC HL ; Next location
HEX4: LD (HL),C ; to PBUFF+3
INC HL ; PBUFF+4 to zero
XOR A ; Terminating character
LD (HL),A ; Store zero to terminate
INC HL ; Make sure PBUFF is terminated
LD (HL),A ; Store the double zero there
POP BC ; Get BC back
LD HL,PBUFF ; Reset to start of PBUFF
JP STR1 ; Convert the PBUFF to a string and return it
BYT2ASC LD B,A ; Save original value
AND $0F ; Strip off upper nybble
CP $0A ; 0-9?
JR C,ADD30 ; If A-F, add 7 more
ADD A,$07 ; Bring value up to ASCII A-F
ADD30 ADD A,$30 ; And make ASCII
LD C,A ; Save converted char to C
LD A,B ; Retrieve original value
RRCA ; and Rotate it right
RRCA
RRCA
RRCA
AND $0F ; Mask off upper nybble
CP $0A ; 0-9? < A hex?
JR C,ADD301 ; Skip Add 7
ADD A,$07 ; Bring it up to ASCII A-F
ADD301 ADD A,$30 ; And make it full ASCII
LD B,A ; Store high order byte
RET
; Convert "&Hnnnn" to FPREG
; Gets a character from (HL) checks for Hexadecimal ASCII numbers "&Hnnnn"
; Char is in A, NC if char is ;<=>?@ A-z, CY is set if 0-9
HEXTFP EX DE,HL ; Move code string pointer to DE
LD HL,$0000 ; Zero out the value
CALL GETHEX ; Check the number for valid hex
JP C,HXERR ; First value wasn't hex, HX error
JR HEXLP1 ; Convert first character
HEXLP CALL GETHEX ; Get second and addtional characters
JR C,HEXIT ; Exit if not a hex character
HEXLP1 ADD HL,HL ; Rotate 4 bits to the left
ADD HL,HL
ADD HL,HL
ADD HL,HL
OR L ; Add in D0-D3 into L
LD L,A ; Save new value
JR HEXLP ; And continue until all hex characters are in
GETHEX INC DE ; Next location
LD A,(DE) ; Load character at pointer
CP ' '
JP Z,GETHEX ; Skip spaces
SUB $30 ; Get absolute value
RET C ; < "0", error
CP $0A
JR C,NOSUB7 ; Is already in the range 0-9
SUB $07 ; Reduce to A-F
CP $0A ; Value should be $0A-$0F at this point
RET C ; CY set if was : ; < = > ? @
NOSUB7 CP $10 ; > Greater than "F"?
CCF
RET ; CY set if it wasn't valid hex
HEXIT EX DE,HL ; Value into DE, Code string into HL
LD A,D ; Load DE into AC
LD C,E ; For prep to
PUSH HL
CALL ACPASS ; ACPASS to set AC as integer into FPREG
POP HL
RET
HXERR: LD E,HX ; ?HEX Error
JP ERROR
; BIN$(NN) Convert integer to a 1-16 char binary string
BIN: CALL TSTNUM ; Verify it's a number
CALL DEINT ; Get integer -32768 to 32767
BIN2: PUSH BC ; Save contents of BC
LD HL,PBUFF
LD B,17 ; One higher than max char count
ZEROSUP: ; Suppress leading zeros
DEC B ; Max 16 chars
LD A,B
CP $01
JR Z,BITOUT ; Always output at least one character
RL E
RL D
JR NC,ZEROSUP
JR BITOUT2
BITOUT:
RL E
RL D ; Top bit now in carry
BITOUT2:
LD A,'0' ; Char for '0'
ADC A,0 ; If carry set then '0' --> '1'
LD (HL),A
INC HL
DEC B
JR NZ,BITOUT
XOR A ; Terminating character
LD (HL),A ; Store zero to terminate
INC HL ; Make sure PBUFF is terminated
LD (HL),A ; Store the double zero there
POP BC
LD HL,PBUFF
JP STR1
; Convert "&Bnnnn" to FPREG
; Gets a character from (HL) checks for Binary ASCII numbers "&Bnnnn"
BINTFP: EX DE,HL ; Move code string pointer to DE
LD HL,$0000 ; Zero out the value
CALL CHKBIN ; Check the number for valid bin
JP C,BINERR ; First value wasn't bin, HX error
BINIT: SUB '0'
ADD HL,HL ; Rotate HL left
OR L
LD L,A
CALL CHKBIN ; Get second and addtional characters
JR NC,BINIT ; Process if a bin character
EX DE,HL ; Value into DE, Code string into HL
LD A,D ; Load DE into AC
LD C,E ; For prep to
PUSH HL
CALL ACPASS ; ACPASS to set AC as integer into FPREG
POP HL
RET
; Char is in A, NC if char is 0 or 1
CHKBIN: INC DE
LD A,(DE)
CP ' '
JP Z,CHKBIN ; Skip spaces
CP '0' ; Set C if < '0'
RET C
CP '2'
CCF ; Set C if > '1'
RET
BINERR: LD E,BN ; ?BIN Error
JP ERROR
JJUMP1:
LD IX,-1 ; Flag cold start
JP CSTART ; Go and initialise
MONOUT:
JP $0008 ; output a char
MONITR:
JP $0000 ; Restart (Normally Monitor Start)
INITST: LD A,0 ; Clear break flag
LD (BRKFLG),A
JP INIT
ARETN: RETN ; Return from NMI
TSTBIT: PUSH AF ; Save bit mask
AND B ; Get common bits
POP BC ; Restore bit mask
CP B ; Same bit set?
LD A,0 ; Return 0 in A
RET
OUTNCR: CALL OUTC ; Output character in A
JP PRNTCRLF ; Output CRLF
.end