Line 1... |
Line 1... |
;**************************************************************
|
!**************************************************************
|
;*
|
!*
|
;* TINY BASIC FOR INTEL 8080
|
!* tiny basic for intel 8080
|
;* VERSION 1.0
|
!* version 1.0
|
;* BY LI-CHEN WANG
|
!* by li-chen wang
|
;* 10 JUNE, 1976
|
!* 10 june, 1976
|
;* @COPYLEFT
|
!* @copyleft
|
;* ALL WRONGS RESERVED
|
!* all wrongs reserved
|
;*
|
!*
|
;**************************************************************
|
!**************************************************************
|
;*
|
!*
|
;* ;*** ZERO PAGE SUBROUTINES ***
|
!* !*** zero page subroutines ***
|
;*
|
!*
|
;* THE 8080 INSTRUCTION SET LETS YOU HAVE 8 ROUTINES IN LOW
|
!* the 8080 instruction set lets you have 8 routines in low
|
;* MEMORY THAT MAY BE CALLED BY RST N, N BEING 0 THROUGH 7.
|
!* memory that may be called by rst n, n being 0 through 7.
|
;* THIS IS A ONE BYTE INSTRUCTION AND HAS THE SAME POWER AS
|
!* this is a one byte instruction and has the same power as
|
;* THE THREE BYTE INSTRUCTION CALL LLHH. TINY BASIC WILL
|
!* the three byte instruction call llhh. tiny basic will
|
;* USE RST 0 AS START AND RST 1 THROUGH RST 7 FOR
|
!* use rst 0 as start and rst 1 through rst 7 for
|
;* THE SEVEN MOST FREQUENTLY USED SUBROUTINES.
|
!* the seven most frequently used subroutines.
|
;* TWO OTHER SUBROUTINES (CRLF AND TSTNUM) ARE ALSO IN THIS
|
!* two other subroutines (crlf and tstnum) are also in this
|
;* SECTION. THEY CAN BE REACHED ONLY BY 3-BYTE CALLS.
|
!* section. they can be reached only by 3-byte calls.
|
;* IN ORDER TO CONFIGURE THE SYSTEM FOR USE WITH CPM I HAVE
|
!
|
;* MOVED SOME OF THE ROUTINES AROUND. START WILL NOW BE AT
|
! Note: this version was extensively damaged to adapt to CP/M,
|
;* LOCATION 100H AND THIS SECTION WILL END AT LOCATION 3FH
|
! I am attempting to find other copies to reference to in order
|
;* WITH A JUMP TO 108H.
|
! to correct it.
|
;*
|
!
|
; ORG 8H
|
!*
|
; XTHL ;*** TSTC OR RST 1 ***
|
jmp ninit ! go main start
|
; RST 5 ;IGNORE BLANKS AND
|
alignp 8
|
; CMP M ;TEST CHARACTER
|
*
|
; JMP TC1 ;REST OF THIS IS AT TC1
|
xthl !*** tstc or rst 1 ***
|
;*
|
rst 5 !ignore blanks and
|
;CRLF MVI A,0DH ;*** CRLF ***
|
cmp m !test character
|
;*
|
jmp tc1 !rest of this is at tc1
|
; PUSH PSW ;*** OUTC OR RST 2 ***
|
*
|
; LDA OCSW ;PRINT CHARACTER ONLY
|
crlf: mvi a,0dh !*** crlf ***
|
; ORA A ;IFF OCSW SWITCH IS ON
|
*
|
; JMP OC2 ;REST OF THIS IS AT OC2
|
push psw !*** outc or rst 2 ***
|
;*
|
lda ocsw !print character only
|
; CALL EXPR2 ;*** EXPR OR RST 3 ***
|
ora a !iff ocsw switch is on
|
; PUSH H ;EVALUATE AN EXPRESION
|
jmp oc2 !rest of this is at oc2
|
; JMP EXPR1 ;REST OF IT IS AT EXPR1
|
*
|
; DB 'W'
|
call expr2 !*** expr or rst 3 ***
|
;*
|
push h !evaluate an expresion
|
; MOV A,H ;*** COMP OR RST 4 ***
|
jmp expr1 !rest of it is at expr1
|
; CMP D ;COMPARE HL WITH DE
|
defb 'w'
|
; RNZ ;RETURN CORRECT C AND
|
*
|
; MOV A,L ;Z FLAGS
|
mov a,h !*** comp or rst 4 ***
|
; CMP E ;BUT OLD A IS LOST
|
cmp d !compare hl with de
|
; RET
|
rnz !return correct c and
|
; DB 'AN'
|
mov a,l !z flags
|
;*
|
cmp e !but old a is lost
|
;SS1 LDAX D ;*** IGNBLK/RST 5 ***
|
ret
|
; CPI 40Q ;IGNORE BLANKS
|
defb 'an'
|
; RNZ ;IN TEXT (WHERE DE->)
|
*
|
; INX D ;AND RETURN THE FIRST
|
ss1: ldax d !*** ignblk/rst 5 ***
|
; JMP SS1 ;NON-BLANK CHAR. IN A
|
cpi 40q !ignore blanks
|
;*
|
rnz !in text (where de->)
|
; POP PSW ;*** FINISH/RST 6 ***
|
inx d !and return the first
|
; CALL FIN ;CHECK END OF COMMAND
|
jmp ss1 !non-blank char. in a
|
; JMP QWHAT ;PRINT "WHAT?" IFF WRONG
|
*
|
; DB 'G'
|
pop psw !*** finish/rst 6 ***
|
;*
|
call fin !check end of command
|
; RST 5 ;*** TSTV OR RST 7 ***
|
jmp qwhat !print "what?" iff wrong
|
; SUI 100Q ;TEST VARIABLES
|
defb 'g'
|
; RC ;C:NOT A VARIABLE
|
*
|
; JMP TSTV1 ;JUMP AROUND RESERVED AREA
|
rst 5 !*** tstv or rst 7 ***
|
ORG 100H ;OF CPM.
|
sui 100q !test variables
|
START JMP NINIT ;GO TO INITIALIZATION ROUTINE. JIF
|
rc !c:not a variable
|
TSTV1 JNZ TV1 ;NOT "@" ARRAY
|
*
|
INX D ;IT IS THE "@" ARRAY
|
tstv1: jnz tv1 !not "@" array
|
CALL PARN ;@ SHOULD BE FOLLOWED
|
inx d !it is the "@" array
|
DAD H ;BY (EXPR) AS ITS INDEX
|
call parn !@ should be followed
|
JC QHOW ;IS INDEX TOO BIG?
|
dad h !by (expr) as its index
|
PUSH D ;WILL IT OVERWRITE
|
jc qhow !is index too big?
|
XCHG ;TEXT?
|
push d !will it overwrite
|
CALL SIZE ;FIND SIZE OF FREE
|
xchg !text?
|
RST 4 ;AND CHECK THAT
|
call size !find size of free
|
JC ASORRY ;IFF SO, SAY "SORRY"
|
rst 4 !and check that
|
SS1A LXI H,VARBGN ;IFF NOT, GET ADDRESS
|
jc asorry !iff so, say "sorry"
|
CALL SUBDE ;OF @(EXPR) AND PUT IT
|
ss1a: lxi h,varbgn !iff not, get address
|
POP D ;IN HL
|
call subde !of @(expr) and put it
|
RET ;C FLAG IS CLEARED
|
pop d !in hl
|
TV1 CPI 33Q ;NOT @, IS IT A TO Z?
|
ret !c flag is cleared
|
CMC ;IFF NOT RETURN C FLAG
|
tv1: cpi 33q !not @, is it a to z?
|
RC
|
cmc !iff not return c flag
|
INX D ;IFF A THROUGH Z
|
rc
|
TV1A LXI H,VARBGN ;COMPUTE ADDRESS OF
|
inx d !iff a through z
|
RLC ;THAT VARIABLE
|
tv1a: lxi h,varbgn !compute address of
|
ADD L ;AND RETURN IT IN HL
|
rlc !that variable
|
MOV L,A ;WITH C FLAG CLEARED
|
add l !and return it in hl
|
MVI A,0
|
mov l,a !with c flag cleared
|
ADC H
|
mvi a,0
|
MOV H,A
|
adc h
|
RET
|
mov h,a
|
;*
|
ret
|
;* TSTC XCH HL,(SP) ;*** TSTC OR RST 1 ***
|
!*
|
;* IGNBLK THIS IS AT LOC. 8
|
!* tstc xch hl,(sp) !*** tstc or rst 1 ***
|
;* CMP M AND THEN JMP HERE
|
!* ignblk this is at loc. 8
|
TC1 INX H ;COMPARE THE BYTE THAT
|
!* cmp m and then jmp here
|
JZ TC2 ;FOLLOWS THE RST INST.
|
tc1: inx h !compare the byte that
|
PUSH B ;WITH THE TEXT (DE->)
|
jz tc2 !follows the rst inst.
|
MOV C,M ;IFF NOT =, ADD THE 2ND
|
push b !with the text (de->)
|
MVI B,0 ;BYTE THAT FOLLOWS THE
|
mov c,m !iff not =, add the 2nd
|
DAD B ;RST TO THE OLD PC
|
mvi b,0 !byte that follows the
|
POP B ;I.E., DO A RELATIVE
|
dad b !rst to the old pc
|
DCX D ;JUMP IFF NOT =
|
pop b !i.e., do a relative
|
TC2 INX D ;IFF =, SKIP THOSE BYTES
|
dcx d !jump iff not =
|
INX H ;AND CONTINUE
|
tc2: inx d !iff =, skip those bytes
|
XTHL
|
inx h !and continue
|
RET
|
xthl
|
;*
|
ret
|
TSTNUM LXI H,0 ;*** TSTNUM ***
|
!*
|
MOV B,H ;TEST IFF THE TEXT IS
|
tstnum:lxi h,0 !*** tstnum ***
|
RST 5 ;A NUMBER
|
mov b,h !test iff the text is
|
TN1 CPI 60Q ;IFF NOT, RETURN 0 IN
|
rst 5 !a number
|
RC ;B AND HL
|
tn1: cpi 60q !iff not, return 0 in
|
CPI 72Q ;IFF NUMBERS, CONVERT
|
rc !b and hl
|
RNC ;TO BINARY IN HL AND
|
cpi 72q !iff numbers, convert
|
MVI A,360Q ;SET A TO # OF DIGITS
|
rnc !to binary in hl and
|
ANA H ;IFF H>255, THERE IS NO
|
mvi a,360q !set a to # of digits
|
JNZ QHOW ;ROOM FOR NEXT DIGIT
|
ana h !iff h>255, there is no
|
INR B ;B COUNTS # OF DIGITS
|
jnz qhow !room for next digit
|
PUSH B
|
inr b !b counts # of digits
|
MOV B,H ;HL=10;*HL+(NEW DIGIT)
|
push b
|
MOV C,L
|
mov b,h !hl=10!*hl+(new digit)
|
DAD H ;WHERE 10;* IS DONE BY
|
mov c,l
|
DAD H ;SHIFT AND ADD
|
dad h !where 10!* is done by
|
DAD B
|
dad h !shift and add
|
DAD H
|
dad b
|
LDAX D ;AND (DIGIT) IS FROM
|
dad h
|
INX D ;STRIPPING THE ASCII
|
ldax d !and (digit) is from
|
ANI 17Q ;CODE
|
inx d !stripping the ascii
|
ADD L
|
ani 17q !code
|
MOV L,A
|
add l
|
MVI A,0
|
mov l,a
|
ADC H
|
mvi a,0
|
MOV H,A
|
adc h
|
POP B
|
mov h,a
|
LDAX D ;DO THIS DIGIT AFTER
|
pop b
|
JP TN1 ;DIGIT. S SAYS OVERFLOW
|
ldax d !do this digit after
|
QHOW PUSH D ;*** ERROR: "HOW?" ***
|
jp tn1 !digit. s says overflow
|
AHOW LXI D,HOW
|
qhow: push d !*** error: "how?" ***
|
JMP ERROR
|
ahow: lxi d,how
|
HOW DB 'HOW?',0DH
|
jmp error
|
OK DB 'OK',0DH
|
how: defb 'how?',0dh
|
WHAT DB 'WHAT?',0DH
|
ok: defb 'ok',0dh
|
SORRY DB 'SORRY',0DH
|
what: defb 'what?',0dh
|
;*
|
sorry: defb 'sorry',0dh
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** MAIN ***
|
!*
|
;*
|
!* *** main ***
|
;* THIS IS THE MAIN LOOP THAT COLLECTS THE TINY BASIC PROGRAM
|
!*
|
;* AND STORES IT IN THE MEMORY.
|
!* this is the main loop that collects the tiny basic program
|
;*
|
!* and stores it in the memory.
|
;* AT START, IT PRINTS OUT "(CR)OK(CR)", AND INITIALIZES THE
|
!*
|
;* STACK AND SOME OTHER INTERNAL VARIABLES. THEN IT PROMPTS
|
!* at start, it prints out "(cr)ok(cr)", and initializes the
|
;* ">" AND READS A LINE. IFF THE LINE STARTS WITH A NON-ZERO
|
!* stack and some other internal variables. then it prompts
|
;* NUMBER, THIS NUMBER IS THE LINE NUMBER. THE LINE NUMBER
|
!* ">" and reads a line. iff the line starts with a non-zero
|
;* (IN 16 BIT BINARY) AND THE REST OF THE LINE (INCLUDING CR)
|
!* number, this number is the line number. the line number
|
;* IS STORED IN THE MEMORY. IFF A LINE WITH THE SAME LINE
|
!* (in 16 bit binary) and the rest of the line (including cr)
|
;* NUMBER IS ALREDY THERE, IT IS REPLACED BY THE NEW ONE. IF
|
!* is stored in the memory. iff a line with the same line
|
;* THE REST OF THE LINE CONSISTS OF A 0DHONLY, IT IS NOT STORED
|
!* number is alredy there, it is replaced by the new one. if
|
;* AND ANY EXISTING LINE WITH THE SAME LINE NUMBER IS DELETED.
|
!* the rest of the line consists of a 0dhonly, it is not stored
|
;*
|
!* and any existing line with the same line number is deleted.
|
;* AFTER A LINE ISs INSERTED, REPLACED, OR DELETED, THE PROGRAM
|
!*
|
;* LOOPS BACK AND ASK FOR ANOTHER LINE. THIS LOOP WILL BE
|
!* after a line iss inserted, replaced, or deleted, the program
|
;* TERMINATED WHEN IT READS A LINE WITH ZERO OR NO LINE
|
!* loops back and ask for another line. this loop will be
|
;* NUMBER; AND CONTROL IS TRANSFERED TO "DIRCT".
|
!* terminated when it reads a line with zero or no line
|
;*
|
!* number! and control is transfered to "dirct".
|
;* TINY BASIC PROGRAM SAVE AREA STARTS AT THE MEMORY LOCATION
|
!*
|
;* LABELED "TXTBGN" AND ENDED AT "TXTEND". WE ALWAYS FILL THIS
|
!* tiny basic program save area starts at the memory location
|
;* AREA STARTING AT "TXTBGN", THE UNFILLED PORTION IS POINTED
|
!* labeled "txtbgn" and ended at "txtend". we always fill this
|
;* BY THE CONTENT OF A MEMORY LOCATION LABELED "TXTUNF".
|
!* area starting at "txtbgn", the unfilled portion is pointed
|
;*
|
!* by the content of a memory location labeled "txtunf".
|
;* THE MEMORY LOCATION "CURRNT" POINTS TO THE LINE NUMBER
|
!*
|
;* THAT IS CURRENTLY BEING INTERPRETED. WHILE WE ARE IN
|
!* the memory location "currnt" points to the line number
|
;* THIS LOOP OR WHILE WE ARE INTERPRETING A DIRECT COMMAND
|
!* that is currently being interpreted. while we are in
|
;* (SEE NEXT SECTION), "CURRNT" SHOULD POINT TO A 0.
|
!* this loop or while we are interpreting a direct command
|
;*
|
!* (see next section), "currnt" should point to a 0.
|
RSTART LXI SP,STACK ;SET STACK POINTER
|
!*
|
ST1 CALL CRLF ;AND JUMP TO HERE
|
rstart:lxi sp,stack !set stack pointer
|
LXI D,OK ;DE->STRING
|
st1: call crlf !and jump to here
|
SUB A ;A=0
|
lxi d,ok !de->string
|
CALL PRTSTG ;PRINT STRING UNTIL 0DH
|
sub a !a=0
|
LXI H,ST2+1 ;LITERAL 0
|
call prtstg !print string until 0dh
|
SHLD CURRNT ;CURRNT->LINE # = 0
|
lxi h,st2+1 !literal 0
|
ST2 LXI H,0
|
shld currnt !currnt->line # = 0
|
SHLD LOPVAR
|
st2: lxi h,0
|
SHLD STKGOS
|
shld lopvar
|
ST3 MVI A,76Q ;PROMPT '>' AND
|
shld stkgos
|
CALL GETLN ;READ A LINE
|
st3: mvi a,76q !prompt '>' and
|
PUSH D ;DE->END OF LINE
|
call getln !read a line
|
ST3A LXI D,BUFFER ;DE->BEGINNING OF LINE
|
push d !de->end of line
|
CALL TSTNUM ;TESt IFF IT IS A NUMBER
|
st3a: lxi d,buffer !de->beginning of line
|
RST 5
|
call tstnum !test iff it is a number
|
MOV A,H ;HL=VALUE OF THE # OR
|
rst 5
|
ORA L ;0 IFF NO # WAS FOUND
|
mov a,h !hl=value of the # or
|
POP B ;BC->END OF LINE
|
ora l !0 iff no # was found
|
JZ DIRECT
|
pop b !bc->end of line
|
DCX D ;BACKUP DE AND SAVE
|
jz direct
|
MOV A,H ;VALUE OF LINE # THERE
|
dcx d !backup de and save
|
STAX D
|
mov a,h !value of line # there
|
DCX D
|
stax d
|
MOV A,L
|
dcx d
|
STAX D
|
mov a,l
|
PUSH B ;BC,DE->BEGIN, END
|
stax d
|
PUSH D
|
push b !bc,de->begin, end
|
MOV A,C
|
push d
|
SUB E
|
mov a,c
|
PUSH PSW ;A=# OF BYTES IN LINE
|
sub e
|
CALL FNDLN ;FIND THIS LINE IN SAVE
|
push psw !a=# of bytes in line
|
PUSH D ;AREA, DE->SAVE AREA
|
call fndln !find this line in save
|
JNZ ST4 ;NZ:NOT FOUND, INSERT
|
push d !area, de->save area
|
PUSH D ;Z:FOUND, DELETE IT
|
jnz st4 !nz:not found, insert
|
CALL FNDNXT ;FIND NEXT LINE
|
push d !z:found, delete it
|
;* DE->NEXT LINE
|
call fndnxt !find next line
|
POP B ;BC->LINE TO BE DELETED
|
!* de->next line
|
LHLD TXTUNF ;HL->UNFILLED SAVE AREA
|
pop b !bc->line to be deleted
|
CALL MVUP ;MOVE UP TO DELETE
|
lhld txtunf !hl->unfilled save area
|
MOV H,B ;TXTUNF->UNFILLED AREA
|
call mvup !move up to delete
|
MOV L,C
|
mov h,b !txtunf->unfilled area
|
SHLD TXTUNF ;UPDATE
|
mov l,c
|
ST4 POP B ;GET READY TO INSERT
|
shld txtunf !update
|
LHLD TXTUNF ;BUT FIRT CHECK IF
|
st4: pop b !get ready to insert
|
POP PSW ;THE LENGTH OF NEW LINE
|
lhld txtunf !but firt check if
|
PUSH H ;IS 3 (LINE # AND CR)
|
pop psw !the length of new line
|
CPI 3 ;THEN DO NOT INSERT
|
push h !is 3 (line # and cr)
|
JZ RSTART ;MUST CLEAR THE STACK
|
cpi 3 !then do not insert
|
ADD L ;COMPUTE NEW TXTUNF
|
jz rstart !must clear the stack
|
MOV L,A
|
add l !compute new txtunf
|
MVI A,0
|
mov l,a
|
ADC H
|
mvi a,0
|
MOV H,A ;HL->NEW UNFILLED AREA
|
adc h
|
ST4A LXI D,TXTEND ;CHECK TO SEE IF THERE
|
mov h,a !hl->new unfilled area
|
RST 4 ;IS ENOUGH SPACE
|
st4a: lxi d,txtend !check to see if there
|
JNC QSORRY ;SORRY, NO ROOM FOR IT
|
rst 4 !is enough space
|
SHLD TXTUNF ;OK, UPDATE TXTUNF
|
jnc qsorry !sorry, no room for it
|
POP D ;DE->OLD UNFILLED AREA
|
shld txtunf !ok, update txtunf
|
CALL MVDOWN
|
pop d !de->old unfilled area
|
POP D ;DE->BEGIN, HL->END
|
call mvdown
|
POP H
|
pop d !de->begin, hl->end
|
CALL MVUP ;MOVE NEW LINE TO SAVE
|
pop h
|
JMP ST3 ;AREA
|
call mvup !move new line to save
|
;*
|
jmp st3 !area
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** TABLES *** DIRECT *** & EXEC ***
|
!*
|
;*
|
!* *** tables *** direct *** & exec ***
|
;* THIS SECTION OF THE CODE TESTS A STRING AGAINST A TABLE.
|
!*
|
;* WHEN A MATCH IS FOUND, CONTROL IS TRANSFERED TO THE SECTION
|
!* this section of the code tests a string against a table.
|
;* OF CODE ACCORDING TO THE TABLE.
|
!* when a match is found, control is transfered to the section
|
;*
|
!* of code according to the table.
|
;* AT 'EXEC', DE SHOULD POINT TO THE STRING AD HL SHOULD POINT
|
!*
|
;* TO THE TABLE-1. AT 'DIRECT', DE SHOULD POINT TO THE STRING,
|
!* at 'exec', de should point to the string ad hl should point
|
;* HL WILL BE SET UP TO POINT TO TAB1-1, WHICH IS THE TABLE OF
|
!* to the table-1. at 'direct', de should point to the string,
|
;* ALL DIRECT AND STATEMENT COMMANDS.
|
!* hl will be set up to point to tab1-1, which is the table of
|
;*
|
!* all direct and statement commands.
|
;* A '.' IN THE STRING WILL TERMINATE THE TEST AND THE PARTIAL
|
!*
|
;* MATCH WILL BE CONSIDERED AS A MATCH. E.G., 'P.', 'PR.',
|
!* a '.' in the string will terminate the test and the partial
|
;* 'PRI.', 'PRIN.', OR 'PRINT' WILL ALL MATCH 'PRINT'.
|
!* match will be considered as a match. e.g., 'p.', 'pr.',
|
;*
|
!* 'pri.', 'prin.', or 'print' will all match 'print'.
|
;* THE TABLE CONSISTS OF ANY NUMBER OF ITEMS. EACH ITEM
|
!*
|
;* IS A STRING OF CHARACTERS WITH BIT 7 SET TO 0 AND
|
!* the table consists of any number of items. each item
|
;* A JUMP ADDRESS STORED HI-LOW WITH BIT 7 OF THE HIGH
|
!* is a string of characters with bit 7 set to 0 and
|
;* BYTE SET TO 1.
|
!* a jump address stored hi-low with bit 7 of the high
|
;*
|
!* byte set to 1.
|
;* END OF TABLE IS AN ITEM WITH A JUMP ADDRESS ONLY. IFF THE
|
!*
|
;* STRING DOES NOT MATCH ANY OF THE OTHER ITEMS, IT WILL
|
!* end of table is an item with a jump address only. iff the
|
;* MATCH THIS NULL ITEM AS DEFAULT.
|
!* string does not match any of the other items, it will
|
;*
|
!* match this null item as default.
|
TAB1 EQU $ ;DIRECT COMMANDS
|
!*
|
DB 'LIST'
|
tab1: equ $ !direct commands
|
DB LIST SHR 8 + 128,LIST AND 0FFH
|
defb 'list'
|
DB 'RUN'
|
defb list shr 8 + 128,list and 0ffh
|
DB RUN SHR 8 + 128,RUN AND 255
|
defb 'run'
|
DB 'NEW'
|
defb run shr 8 + 128,run and 255
|
DB NEW SHR 8 + 128,NEW AND 255
|
defb 'new'
|
DB 'LOAD'
|
defb new shr 8 + 128,new and 255
|
DB DLOAD SHR 8 + 128,DLOAD AND 255
|
defb 'load'
|
DB 'SAVE'
|
defb dload shr 8 + 128,dload and 255
|
DB DSAVE SHR 8 + 128,DSAVE AND 255
|
defb 'save'
|
DB 'BYE',80H,0H ;GO BACK TO CPM
|
defb dsave shr 8 + 128,dsave and 255
|
TAB2 EQU $ ;DIRECT/TATEMENT
|
defb 'bye',80h,0h !go back to cpm
|
DB 'NEXT'
|
tab2: equ $ !direct/tatement
|
DB NEXT SHR 8 + 128,NEXT AND 255
|
defb 'next'
|
DB 'LET'
|
defb next shr 8 + 128,next and 255
|
DB LET SHR 8 + 128,LET AND 255
|
defb 'let'
|
DB 'OUT'
|
defb let shr 8 + 128,let and 255
|
DB OUTCMD SHR 8 + 128,OUTCMD AND 255
|
defb 'out'
|
DB 'POKE'
|
defb outcmd shr 8 + 128,outcmd and 255
|
DB POKE SHR 8 + 128,POKE AND 255
|
defb 'poke'
|
DB 'WAIT'
|
defb poke shr 8 + 128,poke and 255
|
DB WAITCM SHR 8 + 128,WAITCM AND 255
|
defb 'wait'
|
DB 'IF'
|
defb waitcm shr 8 + 128,waitcm and 255
|
DB IFF SHR 8 + 128,IFF AND 255
|
defb 'if'
|
DB 'GOTO'
|
defb iff shr 8 + 128,iff and 255
|
DB GOTO SHR 8 + 128,GOTO AND 255
|
defb 'goto'
|
DB 'GOSUB'
|
defb goto shr 8 + 128,goto and 255
|
DB GOSUB SHR 8 + 128,GOSUB AND 255
|
defb 'gosub'
|
DB 'RETURN'
|
defb gosub shr 8 + 128,gosub and 255
|
DB RETURN SHR 8 + 128,RETURN AND 255
|
defb 'return'
|
DB 'REM'
|
defb return shr 8 + 128,return and 255
|
DB REM SHR 8 + 128,REM AND 255
|
defb 'rem'
|
DB 'FOR'
|
defb rem shr 8 + 128,rem and 255
|
DB FOR SHR 8 + 128,FOR AND 255
|
defb 'for'
|
DB 'INPUT'
|
defb for shr 8 + 128,for and 255
|
DB INPUT SHR 8 + 128,INPUT AND 255
|
defb 'input'
|
DB 'PRINT'
|
defb input shr 8 + 128,input and 255
|
DB PRINT SHR 8 + 128,PRINT AND 255
|
defb 'print'
|
DB 'STOP'
|
defb print shr 8 + 128,print and 255
|
DB STOP SHR 8 + 128,STOP AND 255
|
defb 'stop'
|
DB DEFLT SHR 8 + 128,DEFLT AND 255
|
defb stop shr 8 + 128,stop and 255
|
DB 'YOU CAN ADD MORE' ;COMMANDS BUT
|
defb deflt shr 8 + 128,deflt and 255
|
;REMEMBER TO MOVE DEFAULT DOWN.
|
defb 'you can add more' !commands but
|
TAB4 EQU $ ;FUNCTIONS
|
!remember to move default down.
|
DB 'RND'
|
tab4: equ $ !functions
|
DB RND SHR 8 + 128,RND AND 255
|
defb 'rnd'
|
DB 'INP'
|
defb rnd shr 8 + 128,rnd and 255
|
DB INP SHR 8 + 128,INP AND 255
|
defb 'inp'
|
DB 'PEEK'
|
defb inp shr 8 + 128,inp and 255
|
DB PEEK SHR 8 + 128,PEEK AND 255
|
defb 'peek'
|
DB 'USR'
|
defb peek shr 8 + 128,peek and 255
|
DB USR SHR 8 + 128,USR AND 255
|
defb 'usr'
|
DB 'ABS'
|
defb usr shr 8 + 128,usr and 255
|
DB ABS SHR 8 + 128,ABS AND 255
|
defb 'abs'
|
DB 'SIZE'
|
defb abs shr 8 + 128,abs and 255
|
DB SIZE SHR 8 + 128,SIZE AND 255
|
defb 'size'
|
DB XP40 SHR 8 + 128,XP40 AND 255
|
defb size shr 8 + 128,size and 255
|
DB 'YOU CAN ADD MORE' ;FUNCTIONS BUT REMEMBER
|
defb xp40 shr 8 + 128,xp40 and 255
|
;TO MOVE XP40 DOWN
|
defb 'you can add more' !functions but remember
|
TAB5 EQU $ ;"TO" IN "FOR"
|
!to move xp40 down
|
DB 'TO'
|
tab5: equ $ !"to" in "for"
|
DB FR1 SHR 8 + 128,FR1 AND 255
|
defb 'to'
|
DB QWHAT SHR 8 + 128,QWHAT AND 255
|
defb fr1 shr 8 + 128,fr1 and 255
|
TAB6 EQU $ ;"STEP" IN "FOR"
|
defb qwhat shr 8 + 128,qwhat and 255
|
DB 'STEP'
|
tab6: equ $ !"step" in "for"
|
DB FR2 SHR 8 + 128,FR2 AND 255
|
defb 'step'
|
DB FR3 SHR 8 + 128,FR3 AND 255
|
defb fr2 shr 8 + 128,fr2 and 255
|
TAB8 EQU $ ;RELATION OPERATORS
|
defb fr3 shr 8 + 128,fr3 and 255
|
DB '>='
|
tab8: equ $ !relation operators
|
DB XP11 SHR 8 + 128,XP11 AND 255
|
defb '>='
|
DB '#'
|
defb xp11 shr 8 + 128,xp11 and 255
|
DB XP12 SHR 8 + 128,XP12 AND 255
|
defb '#'
|
DB '>'
|
defb xp12 shr 8 + 128,xp12 and 255
|
DB XP13 SHR 8 + 128,XP13 AND 255
|
defb '>'
|
DB '='
|
defb xp13 shr 8 + 128,xp13 and 255
|
DB XP15 SHR 8 + 128,XP15 AND 255
|
defb '='
|
DB '<='
|
defb xp15 shr 8 + 128,xp15 and 255
|
DB XP14 SHR 8 + 128,XP14 AND 255
|
defb '<='
|
DB '<'
|
defb xp14 shr 8 + 128,xp14 and 255
|
DB XP16 SHR 8 + 128,XP16 AND 255
|
defb '<'
|
DB XP17 SHR 8 + 128,XP17 AND 255
|
defb xp16 shr 8 + 128,xp16 and 255
|
;*
|
defb xp17 shr 8 + 128,xp17 and 255
|
DIRECT LXI H,TAB1-1 ;*** DIRECT ***
|
!*
|
;*
|
direct:lxi h,tab1-1 !*** direct ***
|
EXEC EQU $ ;*** EXEC ***
|
!*
|
EX0 RST 5 ;IGNORE LEADING BLANKS
|
exec: equ $ !*** exec ***
|
PUSH D ;SAVE POINTER
|
ex0: rst 5 !ignore leading blanks
|
EX1 LDAX D ;IFF FOUND '.' IN STRING
|
push d !save pointer
|
INX D ;BEFORE ANY MISMATCH
|
ex1: ldax d !iff found '.' in string
|
CPI 56Q ;WE DECLARE A MATCH
|
inx d !before any mismatch
|
JZ EX3
|
cpi 56q !we declare a match
|
INX H ;HL->TABLE
|
jz ex3
|
CMP M ;IFF MATCH, TEST NEXT
|
inx h !hl->table
|
JZ EX1
|
cmp m !iff match, test next
|
MVI A,177Q ;ELSE, SEE IFF BIT 7
|
jz ex1
|
DCX D ;OF TABLEIS SET, WHICH
|
mvi a,177q !else, see iff bit 7
|
CMP M ;IS THE JUMP ADDR. (HI)
|
dcx d !of tableis set, which
|
JC EX5 ;C:YES, MATCHED
|
cmp m !is the jump addr. (hi)
|
EX2 INX H ;NC:NO, FIND JUMP ADDR.
|
jc ex5 !c:yes, matched
|
CMP M
|
ex2: inx h !nc:no, find jump addr.
|
JNC EX2
|
cmp m
|
INX H ;BUMP TO NEXT TAB. ITEM
|
jnc ex2
|
POP D ;RESTORE STRING POINTER
|
inx h !bump to next tab. item
|
JMP EX0 ;TEST AGAINST NEXT ITEM
|
pop d !restore string pointer
|
EX3 MVI A,177Q ;PARTIAL MATCH, FIND
|
jmp ex0 !test against next item
|
EX4 INX H ;JUMP ADDR., WHICH IS
|
ex3: mvi a,177q !partial match, find
|
CMP M ;FLAGGED BY BIT 7
|
ex4: inx h !jump addr., which is
|
JNC EX4
|
cmp m !flagged by bit 7
|
EX5 MOV A,M ;LOAD HL WITH THE JUMP
|
jnc ex4
|
INX H ;ADDRESS FROM THE TABLE
|
ex5: mov a,m !load hl with the jump
|
MOV L,M
|
inx h !address from the table
|
ANI 177Q ;MASK OFF BIT 7
|
mov l,m
|
MOV H,A
|
ani 177q !mask off bit 7
|
POP PSW ;CLEAN UP THE GABAGE
|
mov h,a
|
PCHL ;AND WE GO DO IT
|
pop psw !clean up the gabage
|
;*
|
pchl !and we go do it
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* WHAT FOLLOWS IS THE CODE TO EXECUTE DIRECT AND STATEMENT
|
!*
|
;* COMMANDS. CONTROL IS TRANSFERED TO THESE POINTS VIA THE
|
!* what follows is the code to execute direct and statement
|
;* COMMAND TABLE LOOKUP CODE OF 'DIRECT' AND 'EXEC' IN LAST
|
!* commands. control is transfered to these points via the
|
;* SECTION. AFTER THE COMMAND IS EXECUTED, CONTROL IS
|
!* command table lookup code of 'direct' and 'exec' in last
|
;* TANSFERED TO OTHER SECTIONS AS FOLLOWS:
|
!* section. after the command is executed, control is
|
;*
|
!* tansfered to other sections as follows:
|
;* FOR 'LIST', 'NEW', AND 'STOP': GO BACK TO 'RSTART'
|
!*
|
;* FOR 'RUN': GO EXECUTE THE FIRST STORED LINE IFF ANY; ELSE
|
!* for 'list', 'new', and 'stop': go back to 'rstart'
|
;* GO BACK TO 'RSTART'.
|
!* for 'run': go execute the first stored line iff any! else
|
;* FOR 'GOTO' AND 'GOSUB': GO EXECUTE THE TARGET LINE.
|
!* go back to 'rstart'.
|
;* FOR 'RETURN' AND 'NEXT': GO BACK TO SAVED RETURN LINE.
|
!* for 'goto' and 'gosub': go execute the target line.
|
;* FOR ALL OTHERS: IFF 'CURRNT' -> 0, GO TO 'RSTART', ELSE
|
!* for 'return' and 'next': go back to saved return line.
|
;* GO EXECUTE NEXT COMMAND. (THIS IS DONE IN 'FINISH'.)
|
!* for all others: iff 'currnt' -> 0, go to 'rstart', else
|
;*
|
!* go execute next command. (this is done in 'finish'.)
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** NEW *** STOP *** RUN (& FRIENDS) *** & GOTO ***
|
!*
|
;*
|
!* *** new *** stop *** run (& friends) *** & goto ***
|
;* 'NEW(CR)' SETS 'TXTUNF' TO POINT TO 'TXTBGN'
|
!*
|
;*
|
!* 'new(cr)' sets 'txtunf' to point to 'txtbgn'
|
;* 'STOP(CR)' GOES BACK TO 'RSTART'
|
!*
|
;*
|
!* 'stop(cr)' goes back to 'rstart'
|
;* 'RUN(CR)' FINDS THE FIRST STORED LINE, STORE ITS ADDRESS (IN
|
!*
|
;* 'CURRNT'), AND START EXECUTE IT. NOTE THAT ONLY THOSE
|
!* 'run(cr)' finds the first stored line, store its address (in
|
;* COMMANDS IN TAB2 ARE LEGAL FOR STORED PROGRAM.
|
!* 'currnt'), and start execute it. note that only those
|
;*
|
!* commands in tab2 are legal for stored program.
|
;* THERE ARE 3 MORE ENTRIES IN 'RUN':
|
!*
|
;* 'RUNNXL' FINDS NEXT LINE, STORES ITS ADDR. AND EXECUTES IT.
|
!* there are 3 more entries in 'run':
|
;* 'RUNTSL' STORES THE ADDRESS OF THIS LINE AND EXECUTES IT.
|
!* 'runnxl' finds next line, stores its addr. and executes it.
|
;* 'RUNSML' CONTINUES THE EXECUTION ON SAME LINE.
|
!* 'runtsl' stores the address of this line and executes it.
|
;*
|
!* 'runsml' continues the execution on same line.
|
;* 'GOTO EXPR(CR)' EVALUATES THE EXPRESSION, FIND THE TARGET
|
!*
|
;* LINE, AND JUMP TO 'RUNTSL' TO DO IT.
|
!* 'goto expr(cr)' evaluates the expression, find the target
|
;* 'DLOAD' LOADS A NAMED PROGRAM FROM DISK.
|
!* line, and jump to 'runtsl' to do it.
|
;* 'DSAVE' SAVES A NAMED PROGRAM ON DISK.
|
!* 'dload' loads a named program from disk.
|
;* 'FCBSET' SETS UP THE FILE CONTROL BLOCK FOR SUBSEQUENT DISK I/O.
|
!* 'dsave' saves a named program on disk.
|
;*
|
!* 'fcbset' sets up the file control block for subsequent disk i/o.
|
NEW CALL ENDCHK ;*** NEW(CR) ***
|
!*
|
LXI H,TXTBGN
|
new: call endchk !*** new(cr) ***
|
SHLD TXTUNF
|
lxi h,txtbgn
|
;*
|
shld txtunf
|
STOP CALL ENDCHK ;*** STOP(CR) ***
|
!*
|
JMP RSTART
|
stop: call endchk !*** stop(cr) ***
|
;*
|
jmp rstart
|
RUN CALL ENDCHK ;*** RUN(CR) ***
|
!*
|
LXI D,TXTBGN ;FIRST SAVED LINE
|
run: call endchk !*** run(cr) ***
|
;*
|
lxi d,txtbgn !first saved line
|
RUNNXL LXI H,0 ;*** RUNNXL ***
|
!*
|
CALL FNDLNP ;FIND WHATEVER LINE #
|
runnxl:lxi h,0 !*** runnxl ***
|
JC RSTART ;C:PASSED TXTUNF, QUIT
|
call fndlnp !find whatever line #
|
;*
|
jc rstart !c:passed txtunf, quit
|
RUNTSL XCHG ;*** RUNTSL ***
|
!*
|
SHLD CURRNT ;SET 'CURRNT'->LINE #
|
runtsl:xchg !*** runtsl ***
|
XCHG
|
shld currnt !set 'currnt'->line #
|
INX D ;BUMP PASS LINE #
|
xchg
|
INX D
|
inx d !bump pass line #
|
;*
|
inx d
|
RUNSML CALL CHKIO ;*** RUNSML ***
|
!*
|
LXI H,TAB2-1 ;FIND COMMAND IN TAB2
|
runsml:call chkio !*** runsml ***
|
JMP EXEC ;AND EXECUTE IT
|
lxi h,tab2-1 !find command in tab2
|
;*
|
jmp exec !and execute it
|
GOTO RST 3 ;*** GOTO EXPR ***
|
!*
|
PUSH D ;SAVE FOR ERROR ROUTINE
|
goto: rst 3 !*** goto expr ***
|
CALL ENDCHK ;MUST FIND A 0DH
|
push d !save for error routine
|
CALL FNDLN ;FIND THE TARGET LINE
|
call endchk !must find a 0dh
|
JNZ AHOW ;NO SUCH LINE #
|
call fndln !find the target line
|
POP PSW ;CLEAR THE "PUSH DE"
|
jnz ahow !no such line #
|
JMP RUNTSL ;GO DO IT
|
pop psw !clear the "push de"
|
CPM EQU 5 ;DISK PARAMETERS
|
jmp runtsl !go do it
|
FCB EQU 5CH
|
cpm: equ 5 !disk parameters
|
SETDMA EQU 26
|
fcb: equ 5ch
|
OPEN EQU 15
|
setdma:equ 26
|
READD EQU 20
|
open: equ 15
|
WRITED EQU 21
|
readd: equ 20
|
CLOSE EQU 16
|
writed:equ 21
|
MAKE EQU 22
|
close: equ 16
|
DELETE EQU 19
|
make: equ 22
|
;*
|
delete:equ 19
|
DLOAD RST 5 ;IGNORE BLANKS
|
!*
|
PUSH H ;SAVE H
|
dload: rst 5 !ignore blanks
|
CALL FCBSET ;SET UP FILE CONTROL BLOCK
|
push h !save h
|
PUSH D ;SAVE THE REST
|
call fcbset !set up file control block
|
PUSH B
|
push d !save the rest
|
LXI D,FCB ;GET FCB ADDRESS
|
push b
|
MVI C,OPEN ;PREPARE TO OPEN FILE
|
lxi d,fcb !get fcb address
|
CALL CPM ;OPEN IT
|
mvi c,open !prepare to open file
|
CPI 0FFH ;IS IT THERE?
|
call cpm !open it
|
JZ QHOW ;NO, SEND ERROR
|
cpi 0ffh !is it there?
|
XRA A ;CLEAR A
|
jz qhow !no, send error
|
STA FCB+32 ;START AT RECORD 0
|
xra a !clear a
|
LXI D,TXTUNF ;GET BEGINNING
|
sta fcb+32 !start at record 0
|
LOAD PUSH D ;SAVE DMA ADDRESS
|
lxi d,txtunf !get beginning
|
MVI C,SETDMA ;
|
load: push d !save dma address
|
CALL CPM ;SET DMA ADDRESS
|
mvi c,setdma !
|
MVI C,READD ;
|
call cpm !set dma address
|
LXI D,FCB
|
mvi c,readd !
|
CALL CPM ;READ SECTOR
|
lxi d,fcb
|
CPI 1 ;DONE?
|
call cpm !read sector
|
JC RDMORE ;NO, READ MORE
|
cpi 1 !done?
|
JNZ QHOW ;BAD READ
|
jc rdmore !no, read more
|
MVI C,CLOSE
|
jnz qhow !bad read
|
LXI D,FCB
|
mvi c,close
|
CALL CPM ;CLOSE FILE
|
lxi d,fcb
|
POP D ;THROW AWAY DMA ADD.
|
call cpm !close file
|
POP B ;GET OLD REGISTERS BACK
|
pop d !throw away dma add.
|
POP D
|
pop b !get old registers back
|
POP H
|
pop d
|
RST 6 ;FINISH
|
pop h
|
RDMORE POP D ;GET DMA ADDRESS
|
rst 6 !finish
|
LXI H,80H ;GET 128
|
rdmore:pop d !get dma address
|
DAD D ;ADD 128 TO DMA ADD.
|
lxi h,80h !get 128
|
XCHG ;PUT IT BACK IN D
|
dad d !add 128 to dma add.
|
JMP LOAD ;AND READ SOME MORE
|
xchg !put it back in d
|
;*
|
jmp load !and read some more
|
DSAVE RST 5 ;IGNORE BLANKS
|
!*
|
PUSH H ;SAVE H
|
dsave: rst 5 !ignore blanks
|
CALL FCBSET ;SETUP FCB
|
push h !save h
|
PUSH D
|
call fcbset !setup fcb
|
PUSH B ;SAVE OTHERS
|
push d
|
LXI D,FCB
|
push b !save others
|
MVI C,DELETE
|
lxi d,fcb
|
CALL CPM ;ERASE FILE IF IT EXISTS
|
mvi c,delete
|
LXI D,FCB
|
call cpm !erase file if it exists
|
MVI C,MAKE
|
lxi d,fcb
|
CALL CPM ;MAKE A NEW ONE
|
mvi c,make
|
CPI 0FFH ;IS THERE SPACE?
|
call cpm !make a new one
|
JZ QHOW ;NO, ERROR
|
cpi 0ffh !is there space?
|
XRA A ;CLEAR A
|
jz qhow !no, error
|
STA FCB+32 ;START AT RECORD 0
|
xra a !clear a
|
LXI D,TXTUNF ;GET BEGINNING
|
sta fcb+32 !start at record 0
|
SAVE PUSH D ;SAVE DMA ADDRESS
|
lxi d,txtunf !get beginning
|
MVI C,SETDMA ;
|
save: push d !save dma address
|
CALL CPM ;SET DMA ADDRESS
|
mvi c,setdma !
|
MVI C,WRITED
|
call cpm !set dma address
|
LXI D,FCB
|
mvi c,writed
|
CALL CPM ;WRITE SECTOR
|
lxi d,fcb
|
ORA A ;SET FLAGS
|
call cpm !write sector
|
JNZ QHOW ;IF NOT ZERO, ERROR
|
ora a !set flags
|
POP D ;GET DMA ADD. BACK
|
jnz qhow !if not zero, error
|
LDA TXTUNF+1 ;AND MSB OF LAST ADD.
|
pop d !get dma add. back
|
CMP D ;IS D SMALLER?
|
lda txtunf+1 !and msb of last add.
|
JC SAVDON ;YES, DONE
|
cmp d !is d smaller?
|
JNZ WRITMOR ;DONT TEST E IF NOT EQUAL
|
jc savdon !yes, done
|
LDA TXTUNF ;IS E SMALLER?
|
jnz writmor !dont test e if not equal
|
CMP E
|
lda txtunf !is e smaller?
|
JC SAVDON ;YES, DONE
|
cmp e
|
WRITMOR LXI H,80H
|
jc savdon !yes, done
|
DAD D ;ADD 128 TO DMA ADD.
|
writmor:lxi h,80h
|
XCHG ;GET IT BACK IN D
|
dad d !add 128 to dma add.
|
JMP SAVE ;WRITE SOME MORE
|
xchg !get it back in d
|
SAVDON MVI C,CLOSE
|
jmp save !write some more
|
LXI D,FCB
|
savdon:mvi c,close
|
CALL CPM ;CLOSE FILE
|
lxi d,fcb
|
POP B ;GET REGISTERS BACK
|
call cpm !close file
|
POP D
|
pop b !get registers back
|
POP H
|
pop d
|
RST 6 ;FINISH
|
pop h
|
;*
|
rst 6 !finish
|
FCBSET LXI H,FCB ;GET FILE CONTROL BLOCK ADDRESS
|
!*
|
MVI M,0 ;CLEAR ENTRY TYPE
|
fcbset:lxi h,fcb !get file control block address
|
FNCLR INX H ;NEXT LOCATION
|
mvi m,0 !clear entry type
|
MVI M,' ' ;CLEAR TO SPACE
|
fnclr: inx h !next location
|
MVI A,FCB+8 AND 255
|
mvi m,' ' !clear to space
|
CMP L ;DONE?
|
mvi a,fcb+8 and 255
|
JNZ FNCLR ;NO, DO IT AGAIN
|
cmp l !done?
|
INX H ;NEXT
|
jnz fnclr !no, do it again
|
MVI M,'T' ;SET FILE TYPE TO 'TBI'
|
inx h !next
|
INX H
|
mvi m,'t' !set file type to 'tbi'
|
MVI M,'B'
|
inx h
|
INX H
|
mvi m,'b'
|
MVI M,'I'
|
inx h
|
EXRC INX H ;CLEAR REST OF FCB
|
mvi m,'i'
|
MVI M,0
|
exrc: inx h !clear rest of fcb
|
MVI A,FCB+15 AND 255
|
mvi m,0
|
CMP L ;DONE?
|
mvi a,fcb+15 and 255
|
JNZ EXRC ;NO, CONTINUE
|
cmp l !done?
|
LXI H,FCB+1 ;GET FILENAME START
|
jnz exrc !no, continue
|
FN LDAX D ;GET CHARACTER
|
lxi h,fcb+1 !get filename start
|
CPI 0DH ;IS IT A 'CR'
|
fn: ldax d !get character
|
RZ ;YES, DONE
|
cpi 0dh !is it a 'cr'
|
CPI '!' ;LEGAL CHARACTER?
|
rz !yes, done
|
JC QWHAT ;NO, SEND ERROR
|
cpi '!' !legal character?
|
CPI '[' ;AGAIN
|
jc qwhat !no, send error
|
JNC QWHAT ;DITTO
|
cpi '[' !again
|
MOV M,A ;SAVE IT IN FCB
|
jnc qwhat !ditto
|
INX H ;NEXT
|
mov m,a !save it in fcb
|
INX D
|
inx h !next
|
MVI A,FCB+9 AND 255
|
inx d
|
CMP L ;LAST?
|
mvi a,fcb+9 and 255
|
JNZ FN ;NO, CONTINUE
|
cmp l !last?
|
RET ;TRUNCATE AT 8 CHARACTERS
|
jnz fn !no, continue
|
;*
|
ret !truncate at 8 characters
|
;*************************************************************
|
!*
|
;*
|
!*************************************************************
|
;* *** LIST *** & PRINT ***
|
!*
|
;*
|
!* *** list *** & print ***
|
;* LIST HAS TWO FORMS:
|
!*
|
;* 'LIST(CR)' LISTS ALL SAVED LINES
|
!* list has two forms:
|
;* 'LIST #(CR)' START LIST AT THIS LINE #
|
!* 'list(cr)' lists all saved lines
|
;* YOU CAN STOP THE LISTING BY CONTROL C KEY
|
!* 'list #(cr)' start list at this line #
|
;*
|
!* you can stop the listing by control c key
|
;* PRINT COMMAND IS 'PRINT ....;' OR 'PRINT ....(CR)'
|
!*
|
;* WHERE '....' IS A LIST OF EXPRESIONS, FORMATS, BACK-
|
!* print command is 'print ....!' or 'print ....(cr)'
|
;* ARROWS, AND STRINGS. THESE ITEMS ARE SEPERATED BY COMMAS.
|
!* where '....' is a list of expresions, formats, back-
|
;*
|
!* arrows, and strings. these items are seperated by commas.
|
;* A FORMAT IS A POUND SIGN FOLLOWED BY A NUMBER. IT CONTROLSs
|
!*
|
;* THE NUMBER OF SPACES THE VALUE OF A EXPRESION IS GOING TO
|
!* a format is a pound sign followed by a number. it controlss
|
;* BE PRINTED. IT STAYS EFFECTIVE FOR THE REST OF THE PRINT
|
!* the number of spaces the value of a expresion is going to
|
;* COMMAND UNLESS CHANGED BY ANOTHER FORMAT. IFF NO FORMAT IS
|
!* be printed. it stays effective for the rest of the print
|
;* SPECIFIED, 6 POSITIONS WILL BE USED.
|
!* command unless changed by another format. iff no format is
|
;*
|
!* specified, 6 positions will be used.
|
;* A STRING IS QUOTED IN A PAIR OF SINGLE QUOTES OR A PAIR OF
|
!*
|
;* DOUBLE QUOTES.
|
!* a string is quoted in a pair of single quotes or a pair of
|
;*
|
!* double quotes.
|
;* A BACK-ARROW MEANS GENERATE A (CR) WITHOUT (LF)
|
!*
|
;*
|
!* a back-arrow means generate a (cr) without (lf)
|
;* A (CRLF) IS GENERATED AFTER THE ENTIRE LIST HAS BEEN
|
!*
|
;* PRINTED OR IFF THE LIST IS A NULL LIST. HOWEVER IFF THE LIST
|
!* a (crlf) is generated after the entire list has been
|
;* ENDED WITH A COMMA, NO (CRL) IS GENERATED.
|
!* printed or iff the list is a null list. however iff the list
|
;*
|
!* ended with a comma, no (crl) is generated.
|
LIST CALL TSTNUM ;TEST IFF THERE IS A #
|
!*
|
CALL ENDCHK ;IFF NO # WE GET A 0
|
list: call tstnum !test iff there is a #
|
CALL FNDLN ;FIND THIS OR NEXT LINE
|
call endchk !iff no # we get a 0
|
LS1 JC RSTART ;C:PASSED TXTUNF
|
call fndln !find this or next line
|
CALL PRTLN ;PRINT THE LINE
|
ls1: jc rstart !c:passed txtunf
|
CALL CHKIO ;STOP IFF HIT CONTROL-C
|
call prtln !print the line
|
CALL FNDLNP ;FIND NEXT LINE
|
call chkio !stop iff hit control-c
|
JMP LS1 ;AND LOOP BACK
|
call fndlnp !find next line
|
;*
|
jmp ls1 !and loop back
|
PRINT MVI C,6 ;C = # OF SPACES
|
!*
|
RST 1 ;IFF NULL LIST & ";"
|
print: mvi c,6 !c = # of spaces
|
DB 73Q
|
rst 1 !iff null list & "!"
|
DB 6Q
|
defb 73q
|
CALL CRLF ;GIVE CR-LF AND
|
defb 6q
|
JMP RUNSML ;CONTINUE SAME LINE
|
call crlf !give cr-lf and
|
PR2 RST 1 ;IFF NULL LIST (CR)
|
jmp runsml !continue same line
|
DB 0DH
|
pr2: rst 1 !iff null list (cr)
|
DB 6Q
|
defb 0dh
|
CALL CRLF ;ALSO GIVE CR-LF AND
|
defb 6q
|
JMP RUNNXL ;GO TO NEXT LINE
|
call crlf !also give cr-lf and
|
PR0 RST 1 ;ELSE IS IT FORMAT?
|
jmp runnxl !go to next line
|
DB '#'
|
pr0: rst 1 !else is it format?
|
DB 5Q
|
defb '#'
|
RST 3 ;YES, EVALUATE EXPR.
|
defb 5q
|
MOV C,L ;AND SAVE IT IN C
|
rst 3 !yes, evaluate expr.
|
JMP PR3 ;LOOK FOR MORE TO PRINT
|
mov c,l !and save it in c
|
PR1 CALL QTSTG ;OR IS IT A STRING?
|
jmp pr3 !look for more to print
|
JMP PR8 ;IFF NOT, MUST BE EXPR.
|
pr1: call qtstg !or is it a string?
|
PR3 RST 1 ;IFF ",", GO FIND NEXT
|
jmp pr8 !iff not, must be expr.
|
DB ','
|
pr3: rst 1 !iff ",", go find next
|
DB 6Q
|
defb ','
|
CALL FIN ;IN THE LIST.
|
defb 6q
|
JMP PR0 ;LIST CONTINUES
|
call fin !in the list.
|
PR6 CALL CRLF ;LIST ENDS
|
jmp pr0 !list continues
|
RST 6
|
pr6: call crlf !list ends
|
PR8 RST 3 ;EVALUATE THE EXPR
|
rst 6
|
PUSH B
|
pr8: rst 3 !evaluate the expr
|
CALL PRTNUM ;PRINT THE VALUE
|
push b
|
POP B
|
call prtnum !print the value
|
JMP PR3 ;MORE TO PRINT?
|
pop b
|
;*
|
jmp pr3 !more to print?
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** GOSUB *** & RETURN ***
|
!*
|
;*
|
!* *** gosub *** & return ***
|
;* 'GOSUB EXPR;' OR 'GOSUB EXPR (CR)' IS LIKE THE 'GOTO'
|
!*
|
;* COMMAND, EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER
|
!* 'gosub expr!' or 'gosub expr (cr)' is like the 'goto'
|
;* ETC. ARE SAVE SO THAT EXECUTION CAN BE CONTINUED AFTER THE
|
!* command, except that the current text pointer, stack pointer
|
;* SUBROUTINE 'RETURN'. IN ORDER THAT 'GOSUB' CAN BE NESTED
|
!* etc. are save so that execution can be continued after the
|
;* (AND EVEN RECURSIVE), THE SAVE AREA MUST BE STACKED.
|
!* subroutine 'return'. in order that 'gosub' can be nested
|
;* THE STACK POINTER IS SAVED IN 'STKGOS'. THE OLD 'STKGOS' IS
|
!* (and even recursive), the save area must be stacked.
|
;* SAVED IN THE STACK. IFF WE ARE IN THE MAIN ROUTINE, 'STKGOS'
|
!* the stack pointer is saved in 'stkgos'. the old 'stkgos' is
|
;* IS ZERO (THIS WAS DONE BY THE "MAIN" SECTION OF THE CODE),
|
!* saved in the stack. iff we are in the main routine, 'stkgos'
|
;* BUT WE STILL SAVE IT AS A FLAG FORr NO FURTHER 'RETURN'S.
|
!* is zero (this was done by the "main" section of the code),
|
;*
|
!* but we still save it as a flag forr no further 'return's.
|
;* 'RETURN(CR)' UNDOS EVERYHING THAT 'GOSUB' DID, AND THUS
|
!*
|
;* RETURN THE EXCUTION TO THE COMMAND AFTER THE MOST RECENT
|
!* 'return(cr)' undos everyhing that 'gosub' did, and thus
|
;* 'GOSUB'. IFF 'STKGOS' IS ZERO, IT INDICATES THAT WE
|
!* return the excution to the command after the most recent
|
;* NEVER HAD A 'GOSUB' AND IS THUS AN ERROR.
|
!* 'gosub'. iff 'stkgos' is zero, it indicates that we
|
;*
|
!* never had a 'gosub' and is thus an error.
|
GOSUB CALL PUSHA ;SAVE THE CURRENT "FOR"
|
!*
|
RST 3 ;PARAMETERS
|
gosub: call pusha !save the current "for"
|
PUSH D ;AND TEXT POINTER
|
rst 3 !parameters
|
CALL FNDLN ;FIND THE TARGET LINE
|
push d !and text pointer
|
JNZ AHOW ;NOT THERE. SAY "HOW?"
|
call fndln !find the target line
|
LHLD CURRNT ;FOUND IT, SAVE OLD
|
jnz ahow !not there. say "how?"
|
PUSH H ;'CURRNT' OLD 'STKGOS'
|
lhld currnt !found it, save old
|
LHLD STKGOS
|
push h !'currnt' old 'stkgos'
|
PUSH H
|
lhld stkgos
|
LXI H,0 ;AND LOAD NEW ONES
|
push h
|
SHLD LOPVAR
|
lxi h,0 !and load new ones
|
DAD SP
|
shld lopvar
|
SHLD STKGOS
|
dad sp
|
JMP RUNTSL ;THEN RUN THAT LINE
|
shld stkgos
|
RETURN CALL ENDCHK ;THERE MUST BE A 0DH
|
jmp runtsl !then run that line
|
LHLD STKGOS ;OLD STACK POINTER
|
return:call endchk !there must be a 0dh
|
MOV A,H ;0 MEANS NOT EXIST
|
lhld stkgos !old stack pointer
|
ORA L
|
mov a,h !0 means not exist
|
JZ QWHAT ;SO, WE SAY: "WHAT?"
|
ora l
|
SPHL ;ELSE, RESTORE IT
|
jz qwhat !so, we say: "what?"
|
POP H
|
sphl !else, restore it
|
SHLD STKGOS ;AND THE OLD 'STKGOS'
|
pop h
|
POP H
|
shld stkgos !and the old 'stkgos'
|
SHLD CURRNT ;AND THE OLD 'CURRNT'
|
pop h
|
POP D ;OLD TEXT POINTER
|
shld currnt !and the old 'currnt'
|
CALL POPA ;OLD "FOR" PARAMETERS
|
pop d !old text pointer
|
RST 6 ;AND WE ARE BACK HOME
|
call popa !old "for" parameters
|
;*
|
rst 6 !and we are back home
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** FOR *** & NEXT ***
|
!*
|
;*
|
!* *** for *** & next ***
|
;* 'FOR' HAS TWO FORMS:
|
!*
|
;* 'FOR VAR=EXP1 TO EXP2 STEP EXP1' AND 'FOR VAR=EXP1 TO EXP2'
|
!* 'for' has two forms:
|
;* THE SECOND FORM MEANS THE SAME THING AS THE FIRST FORM WITH
|
!* 'for var=exp1 to exp2 step exp1' and 'for var=exp1 to exp2'
|
;* EXP1=1. (I.E., WITH A STEP OF +1.)
|
!* the second form means the same thing as the first form with
|
;* TBI WILL FIND THE VARIABLE VAR. AND SET ITS VALUE TO THE
|
!* exp1=1. (i.e., with a step of +1.)
|
;* CURRENT VALUE OF EXP1. IT ALSO EVALUATES EXPR2 AND EXP1
|
!* tbi will find the variable var. and set its value to the
|
;* AND SAVE ALL THESE TOGETHER WITH THE TEXT POINTERr ETC. IN
|
!* current value of exp1. it also evaluates expr2 and exp1
|
;* THE 'FOR' SAVE AREA, WHICH CONSISTS OF 'LOPVAR', 'LOPINC',
|
!* and save all these together with the text pointerr etc. in
|
;* 'LOPLMT', 'LOPLN', AND 'LOPPT'. IFF THERE IS ALREADY SOME-
|
!* the 'for' save area, which consists of 'lopvar', 'lopinc',
|
;* THING IN THE SAVE AREA (THIS IS INDICATED BY A NON-ZERO
|
!* 'loplmt', 'lopln', and 'loppt'. iff there is already some-
|
;* 'LOPVAR'), THEN THE OLD SAVE AREA IS SAVED IN THE STACK
|
!* thing in the save area (this is indicated by a non-zero
|
;* BEFORE THE NEW ONE OVERWRITES IT.
|
!* 'lopvar'), then the old save area is saved in the stack
|
;* TBI WILL THEN DIG IN THE STACK AND FIND OUT IFF THIS SAME
|
!* before the new one overwrites it.
|
;* VARIABLE WAS USED IN ANOTHER CURRENTLY ACTIVE 'FOR' LOOP.
|
!* tbi will then dig in the stack and find out iff this same
|
;* IFF THAT IS THE CASE THEN THE OLD 'FOR' LOOP IS DEACTIVATED.
|
!* variable was used in another currently active 'for' loop.
|
;* (PURGED FROM THE STACK..)
|
!* iff that is the case then the old 'for' loop is deactivated.
|
;*
|
!* (purged from the stack..)
|
;* 'NEXT VAR' SERVES AS THE LOGICAL (NOT NECESSARILLY PHYSICAL)
|
!*
|
;* END OF THE 'FOR' LOOP. THE CONTROL VARIABLE VAR. IS CHECKED
|
!* 'next var' serves as the logical (not necessarilly physical)
|
;* WITH THE 'LOPVAR'. IFF THEY ARE NOT THE SAME, TBI DIGS IN
|
!* end of the 'for' loop. the control variable var. is checked
|
;* THE STACK TO FIND THE RIGHTt ONE AND PURGES ALL THOSE THAT
|
!* with the 'lopvar'. iff they are not the same, tbi digs in
|
;* DID NOT MATCH. EITHER WAY, TBI THEN ADDS THE 'STEP' TO
|
!* the stack to find the rightt one and purges all those that
|
;* THAT VARIABLE AND CHECK THE RESULT WITH THE LIMIT. IFF IT
|
!* did not match. either way, tbi then adds the 'step' to
|
;* IS WITHIN THE LIMIT, CONTROL LOOPS BACK TO THE COMMAND
|
!* that variable and check the result with the limit. iff it
|
;* FOLLOWING THE 'FOR'. IFF OUTSIDE THE LIMIT, THE SAVE ARER
|
!* is within the limit, control loops back to the command
|
;* IS PURGED AND EXECUTION CONTINUES.
|
!* following the 'for'. iff outside the limit, the save arer
|
;*
|
!* is purged and execution continues.
|
FOR CALL PUSHA ;SAVE THE OLD SAVE AREA
|
!*
|
CALL SETVAL ;SET THE CONTROL VAR.
|
for: call pusha !save the old save area
|
DCX H ;HL IS ITS ADDRESS
|
call setval !set the control var.
|
SHLD LOPVAR ;SAVE THAT
|
dcx h !hl is its address
|
LXI H,TAB5-1 ;USE 'EXEC' TO LOOK
|
shld lopvar !save that
|
JMP EXEC ;FOR THE WORD 'TO'
|
lxi h,tab5-1 !use 'exec' to look
|
FR1 RST 3 ;EVALUATE THE LIMIT
|
jmp exec !for the word 'to'
|
SHLD LOPLMT ;SAVE THAT
|
fr1: rst 3 !evaluate the limit
|
LXI H,TAB6-1 ;USE 'EXEC' TO LOOK
|
shld loplmt !save that
|
JMP EXEC ;FOR THE WORD 'STEP'
|
lxi h,tab6-1 !use 'exec' to look
|
FR2 RST 3 ;FOUND IT, GET STEP
|
jmp exec !for the word 'step'
|
JMP FR4
|
fr2: rst 3 !found it, get step
|
FR3 LXI H,1Q ;NOT FOUND, SET TO 1
|
jmp fr4
|
FR4 SHLD LOPINC ;SAVE THAT TOO
|
fr3: lxi h,1q !not found, set to 1
|
FR5 LHLD CURRNT ;SAVE CURRENT LINE #
|
fr4: shld lopinc !save that too
|
SHLD LOPLN
|
fr5: lhld currnt !save current line #
|
XCHG ;AND TEXT POINTER
|
shld lopln
|
SHLD LOPPT
|
xchg !and text pointer
|
LXI B,12Q ;DIG INTO STACK TO
|
shld loppt
|
LHLD LOPVAR ;FIND 'LOPVAR'
|
lxi b,12q !dig into stack to
|
XCHG
|
lhld lopvar !find 'lopvar'
|
MOV H,B
|
xchg
|
MOV L,B ;HL=0 NOW
|
mov h,b
|
DAD SP ;HERE IS THE STACK
|
mov l,b !hl=0 now
|
DB 76Q
|
dad sp !here is the stack
|
FR7 DAD B ;EACH LEVEL IS 10 DEEP
|
defb 76q
|
MOV A,M ;GET THAT OLD 'LOPVAR'
|
fr7: dad b !each level is 10 deep
|
INX H
|
mov a,m !get that old 'lopvar'
|
ORA M
|
inx h
|
JZ FR8 ;0 SAYS NO MORE IN IT
|
ora m
|
MOV A,M
|
jz fr8 !0 says no more in it
|
DCX H
|
mov a,m
|
CMP D ;SAME AS THIS ONE?
|
dcx h
|
JNZ FR7
|
cmp d !same as this one?
|
MOV A,M ;THE OTHER HALF?
|
jnz fr7
|
CMP E
|
mov a,m !the other half?
|
JNZ FR7
|
cmp e
|
XCHG ;YES, FOUND ONE
|
jnz fr7
|
LXI H,0Q
|
xchg !yes, found one
|
DAD SP ;TRY TO MOVE SP
|
lxi h,0q
|
MOV B,H
|
dad sp !try to move sp
|
MOV C,L
|
mov b,h
|
LXI H,12Q
|
mov c,l
|
DAD D
|
lxi h,12q
|
CALL MVDOWN ;AND PURGE 10 WORDS
|
dad d
|
SPHL ;IN THE STACK
|
call mvdown !and purge 10 words
|
FR8 LHLD LOPPT ;JOB DONE, RESTORE DE
|
sphl !in the stack
|
XCHG
|
fr8: lhld loppt !job done, restore de
|
RST 6 ;AND CONTINUE
|
xchg
|
;*
|
rst 6 !and continue
|
NEXT RST 7 ;GET ADDRESS OF VAR.
|
!*
|
JC QWHAT ;NO VARIABLE, "WHAT?"
|
next: rst 7 !get address of var.
|
SHLD VARNXT ;YES, SAVE IT
|
jc qwhat !no variable, "what?"
|
NX0 PUSH D ;SAVE TEXT POINTER
|
shld varnxt !yes, save it
|
XCHG
|
nx0: push d !save text pointer
|
LHLD LOPVAR ;GET VAR. IN 'FOR'
|
xchg
|
MOV A,H
|
lhld lopvar !get var. in 'for'
|
ORA L ;0 SAYS NEVER HAD ONE
|
mov a,h
|
JZ AWHAT ;SO WE ASK: "WHAT?"
|
ora l !0 says never had one
|
RST 4 ;ELSE WE CHECK THEM
|
jz awhat !so we ask: "what?"
|
JZ NX3 ;OK, THEY AGREE
|
rst 4 !else we check them
|
POP D ;NO, LET'S SEE
|
jz nx3 !ok, they agree
|
CALL POPA ;PURGE CURRENT LOOP
|
pop d !no, let's see
|
LHLD VARNXT ;AND POP ONE LEVEL
|
call popa !purge current loop
|
JMP NX0 ;GO CHECK AGAIN
|
lhld varnxt !and pop one level
|
NX3 MOV E,M ;COME HERE WHEN AGREED
|
jmp nx0 !go check again
|
INX H
|
nx3: mov e,m !come here when agreed
|
MOV D,M ;DE=VALUE OF VAR.
|
inx h
|
LHLD LOPINC
|
mov d,m !de=value of var.
|
PUSH H
|
lhld lopinc
|
DAD D ;ADD ONE STEP
|
push h
|
XCHG
|
dad d !add one step
|
LHLD LOPVAR ;PUT IT BACK
|
xchg
|
MOV M,E
|
lhld lopvar !put it back
|
INX H
|
mov m,e
|
MOV M,D
|
inx h
|
LHLD LOPLMT ;HL->LIMIT
|
mov m,d
|
POP PSW ;OLD HL
|
lhld loplmt !hl->limit
|
ORA A
|
pop psw !old hl
|
JP NX1 ;STEP > 0
|
ora a
|
XCHG
|
jp nx1 !step > 0
|
NX1 CALL CKHLDE ;COMPARE WITH LIMIT
|
xchg
|
POP D ;RESTORE TEXT POINTER
|
nx1: call ckhlde !compare with limit
|
JC NX2 ;OUTSIDE LIMIT
|
pop d !restore text pointer
|
LHLD LOPLN ;WITHIN LIMIT, GO
|
jc nx2 !outside limit
|
SHLD CURRNT ;BACK TO THE SAVED
|
lhld lopln !within limit, go
|
LHLD LOPPT ;'CURRNT' AND TEXT
|
shld currnt !back to the saved
|
XCHG ;POINTER
|
lhld loppt !'currnt' and text
|
RST 6
|
xchg !pointer
|
NX2 CALL POPA ;PURGE THIS LOOP
|
rst 6
|
RST 6
|
nx2: call popa !purge this loop
|
;*
|
rst 6
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** REM *** IFF *** INPUT *** & LET (& DEFLT) ***
|
!*
|
;*
|
!* *** rem *** iff *** input *** & let (& deflt) ***
|
;* 'REM' CAN BE FOLLOWED BY ANYTHING AND IS IGNORED BY TBI.
|
!*
|
;* TBI TREATS IT LIKE AN 'IF' WITH A FALSE CONDITION.
|
!* 'rem' can be followed by anything and is ignored by tbi.
|
;*
|
!* tbi treats it like an 'if' with a false condition.
|
;* 'IF' IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE
|
!*
|
;* COMMANDS (INCLUDING OUTHER 'IF'S) SEPERATED BY SEMI-COLONS.
|
!* 'if' is followed by an expr. as a condition and one or more
|
;* NOTE THAT THE WORD 'THEN' IS NOT USED. TBI EVALUATES THE
|
!* commands (including outher 'if's) seperated by semi-colons.
|
;* EXPR. IFF IT IS NON-ZERO, EXECUTION CONTINUES. IFF THE
|
!* note that the word 'then' is not used. tbi evaluates the
|
;* EXPR. IS ZERO, THE COMMANDS THAT FOLLOWS ARE IGNORED AND
|
!* expr. iff it is non-zero, execution continues. iff the
|
;* EXECUTION CONTINUES AT THE NEXT LINE.
|
!* expr. is zero, the commands that follows are ignored and
|
;*
|
!* execution continues at the next line.
|
;* 'IPUT' COMMAND IS LIKE THE 'PRINT' COMMAND, AND IS FOLLOWED
|
!*
|
;* BY A LIST OF ITEMS. IFF THE ITEM IS A STRING IN SINGLE OR
|
!* 'iput' command is like the 'print' command, and is followed
|
;* DOUBLE QUOTES, OR IS A BACK-ARROW, IT HAS THE SAME EFFECT AS
|
!* by a list of items. iff the item is a string in single or
|
;* IN 'PRINT'. IFF AN ITEM IS A VARIABLE, THIS VARIABLE NAME IS
|
!* double quotes, or is a back-arrow, it has the same effect as
|
;* PRINTED OUT FOLLOWED BY A COLON. THEN TBI WAITS FOR AN
|
!* in 'print'. iff an item is a variable, this variable name is
|
;* EXPR. TO BE TYPED IN. THE VARIABLE ISs THEN SET TO THE
|
!* printed out followed by a colon. then tbi waits for an
|
;* VALUE OF THIS EXPR. IFF THE VARIABLE IS PROCEDED BY A STRING
|
!* expr. to be typed in. the variable iss then set to the
|
;* (AGAIN IN SINGLE OR DOUBLE QUOTES), THE STRING WILL BE
|
!* value of this expr. iff the variable is proceded by a string
|
;* PRINTED FOLLOWED BY A COLON. TBI THEN WAITS FOR INPUT EXPR.
|
!* (again in single or double quotes), the string will be
|
;* AND SET THE VARIABLE TO THE VALUE OF THE EXPR.
|
!* printed followed by a colon. tbi then waits for input expr.
|
;*
|
!* and set the variable to the value of the expr.
|
;* IFF THE INPUT EXPR. IS INVALID, TBI WILL PRINT "WHAT?",
|
!*
|
;* "HOW?" OR "SORRY" AND REPRINT THE PROMPT AND REDO THE INPUT.
|
!* iff the input expr. is invalid, tbi will print "what?",
|
;* THE EXECUTION WILL NOT TERMINATE UNLESS YOU TYPE CONTROL-C.
|
!* "how?" or "sorry" and reprint the prompt and redo the input.
|
;* THIS IS HANDLED IN 'INPERR'.
|
!* the execution will not terminate unless you type control-c.
|
;*
|
!* this is handled in 'inperr'.
|
;* 'LET' IS FOLLOWED BY A LIST OF ITEMS SEPERATED BY COMMAS.
|
!*
|
;* EACH ITEM CONSISTS OF A VARIABLE, AN EQUAL SIGN, AND AN EXPR.
|
!* 'let' is followed by a list of items seperated by commas.
|
;* TBI EVALUATES THE EXPR. AND SET THE VARIBLE TO THAT VALUE.
|
!* each item consists of a variable, an equal sign, and an expr.
|
;* TB WILL ALSO HANDLE 'LET' COMMAND WITHOUT THE WORD 'LET'.
|
!* tbi evaluates the expr. and set the varible to that value.
|
;* THIS IS DONE BY 'DEFLT'.
|
!* tb will also handle 'let' command without the word 'let'.
|
;*
|
!* this is done by 'deflt'.
|
REM LXI H,0Q ;*** REM ***
|
!*
|
DB 76Q
|
rem: lxi h,0q !*** rem ***
|
;*
|
defb 76q
|
IFF RST 3 ;*** IFF ***
|
!*
|
MOV A,H ;IS THE EXPR.=0?
|
iff: rst 3 !*** iff ***
|
ORA L
|
mov a,h !is the expr.=0?
|
JNZ RUNSML ;NO, CONTINUE
|
ora l
|
CALL FNDSKP ;YES, SKIP REST OF LINE
|
jnz runsml !no, continue
|
JNC RUNTSL
|
call fndskp !yes, skip rest of line
|
JMP RSTART
|
jnc runtsl
|
;*
|
jmp rstart
|
INPERR LHLD STKINP ;*** INPERR ***
|
!*
|
SPHL ;RESTORE OLD SP
|
inperr:lhld stkinp !*** inperr ***
|
POP H ;AND OLD 'CURRNT'
|
sphl !restore old sp
|
SHLD CURRNT
|
pop h !and old 'currnt'
|
POP D ;AND OLD TEXT POINTER
|
shld currnt
|
POP D ;REDO INPUT
|
pop d !and old text pointer
|
;*
|
pop d !redo input
|
INPUT EQU $ ;*** INPUT ***
|
!*
|
IP1 PUSH D ;SAVE IN CASE OF ERROR
|
input: equ $ !*** input ***
|
CALL QTSTG ;IS NEXT ITEM A STRING?
|
ip1: push d !save in case of error
|
JMP IP2 ;NO
|
call qtstg !is next item a string?
|
RST 7 ;YES. BUT FOLLOWED BY A
|
jmp ip2 !no
|
JC IP4 ;VARIABLE? NO.
|
rst 7 !yes. but followed by a
|
JMP IP3 ;YES. INPUT VARIABLE
|
jc ip4 !variable? no.
|
IP2 PUSH D ;SAVE FOR 'PRTSTG'
|
jmp ip3 !yes. input variable
|
RST 7 ;MUST BE VARIABLE NOW
|
ip2: push d !save for 'prtstg'
|
JC QWHAT ;"WHAT?" IT IS NOT?
|
rst 7 !must be variable now
|
LDAX D ;GET READY FOR 'RTSTG'
|
jc qwhat !"what?" it is not?
|
MOV C,A
|
ldax d !get ready for 'rtstg'
|
SUB A
|
mov c,a
|
STAX D
|
sub a
|
POP D
|
stax d
|
CALL PRTSTG ;PRINT STRING AS PROMPT
|
pop d
|
MOV A,C ;RESTORE TEXT
|
call prtstg !print string as prompt
|
DCX D
|
mov a,c !restore text
|
STAX D
|
dcx d
|
IP3 PUSH D ;SAVE IN CASE OF ERROR
|
stax d
|
XCHG
|
ip3: push d !save in case of error
|
LHLD CURRNT ;ALSO SAVE 'CURRNT'
|
xchg
|
PUSH H
|
lhld currnt !also save 'currnt'
|
LXI H,IP1 ;A NEGATIVE NUMBER
|
push h
|
SHLD CURRNT ;AS A FLAG
|
lxi h,ip1 !a negative number
|
LXI H,0Q ;SAVE SP TOO
|
shld currnt !as a flag
|
DAD SP
|
lxi h,0q !save sp too
|
SHLD STKINP
|
dad sp
|
PUSH D ;OLD HL
|
shld stkinp
|
MVI A,72Q ;PRINT THIS TOO
|
push d !old hl
|
CALL GETLN ;AND GET A LINE
|
mvi a,72q !print this too
|
IP3A LXI D,BUFFER ;POINTS TO BUFFER
|
call getln !and get a line
|
RST 3 ;EVALUATE INPUT
|
ip3a: lxi d,buffer !points to buffer
|
NOP ;CAN BE 'CALL ENDCHK'
|
rst 3 !evaluate input
|
NOP
|
nop !can be 'call endchk'
|
NOP
|
nop
|
POP D ;OK, GET OLD HL
|
nop
|
XCHG
|
pop d !ok, get old hl
|
MOV M,E ;SAVE VALUE IN VAR.
|
xchg
|
INX H
|
mov m,e !save value in var.
|
MOV M,D
|
inx h
|
POP H ;GET OLD 'CURRNT'
|
mov m,d
|
SHLD CURRNT
|
pop h !get old 'currnt'
|
POP D ;AND OLD TEXT POINTER
|
shld currnt
|
IP4 POP PSW ;PURGE JUNK IN STACK
|
pop d !and old text pointer
|
RST 1 ;IS NEXT CH. ','?
|
ip4: pop psw !purge junk in stack
|
DB ','
|
rst 1 !is next ch. ','?
|
DB 3Q
|
defb ','
|
JMP IP1 ;YES, MORE ITEMS.
|
defb 3q
|
IP5 RST 6
|
jmp ip1 !yes, more items.
|
;*
|
ip5: rst 6
|
DEFLT LDAX D ;*** DEFLT ***
|
!*
|
CPI 0DH ;EMPTY LINE IS OK
|
deflt: ldax d !*** deflt ***
|
JZ LT1 ;ELSE IT IS 'LET'
|
cpi 0dh !empty line is ok
|
;*
|
jz lt1 !else it is 'let'
|
LET CALL SETVAL ;*** LET ***
|
!*
|
RST 1 ;SET VALUE TO VAR.
|
let: call setval !*** let ***
|
DB ','
|
rst 1 !set value to var.
|
DB 3Q
|
defb ','
|
JMP LET ;ITEM BY ITEM
|
defb 3q
|
LT1 RST 6 ;UNTIL FINISH
|
jmp let !item by item
|
;*
|
lt1: rst 6 !until finish
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** EXPR ***
|
!*
|
;*
|
!* *** expr ***
|
;* 'EXPR' EVALUATES ARITHMETICAL OR LOGICAL EXPRESSIONS.
|
!*
|
;* ::=
|
!* 'expr' evaluates arithmetical or logical expressions.
|
;*
|
!* ::=
|
;* WHERE IS ONE OF THE OPERATORSs IN TAB8 AND THE
|
!*
|
;* RESULT OF THESE OPERATIONS IS 1 IFF TRUE AND 0 IFF FALSE.
|
!* where is one of the operatorss in tab8 and the
|
;* ::=(+ OR -)(+ OR -)(....)
|
!* result of these operations is 1 iff true and 0 iff false.
|
;* WHERE () ARE OPTIONAL AND (....) ARE OPTIONAL REPEATS.
|
!* ::=(+ or -)(+ or -)(....)
|
;* ::=(<* OR />)(....)
|
!* where () are optional and (....) are optional repeats.
|
;* ::=
|
!* ::=(<* or />)(....)
|
;*
|
!* ::=
|
;* ()
|
!*
|
;* IS RECURSIVE SO THAT VARIABLE '@' CAN HAVE AN
|
!* ()
|
;* AS INDEX, FNCTIONS CAN HAVE AN AS ARGUMENTS, AND
|
!* is recursive so that variable '@' can have an
|
;* CAN BE AN IN PARANTHESE.
|
!* as index, fnctions can have an as arguments, and
|
;*
|
!* can be an in paranthese.
|
;* EXPR CALL EXPR2 THIS IS AT LOC. 18
|
!*
|
;* PUSH HL SAVE VALUE
|
!* expr call expr2 this is at loc. 18
|
EXPR1 LXI H,TAB8-1 ;LOOKUP REL.OP.
|
!* push hl save value
|
JMP EXEC ;GO DO IT
|
expr1: lxi h,tab8-1 !lookup rel.op.
|
XP11 CALL XP18 ;REL.OP.">="
|
jmp exec !go do it
|
RC ;NO, RETURN HL=0
|
xp11: call xp18 !rel.op.">="
|
MOV L,A ;YES, RETURN HL=1
|
rc !no, return hl=0
|
RET
|
mov l,a !yes, return hl=1
|
XP12 CALL XP18 ;REL.OP."#"
|
ret
|
RZ ;FALSE, RETURN HL=0
|
xp12: call xp18 !rel.op."#"
|
MOV L,A ;TRUE, RETURN HL=1
|
rz !false, return hl=0
|
RET
|
mov l,a !true, return hl=1
|
XP13 CALL XP18 ;REL.OP.">"
|
ret
|
RZ ;FALSE
|
xp13: call xp18 !rel.op.">"
|
RC ;ALSO FALSE, HL=0
|
rz !false
|
MOV L,A ;TRUE, HL=1
|
rc !also false, hl=0
|
RET
|
mov l,a !true, hl=1
|
XP14 CALL XP18 ;REL.OP."<="
|
ret
|
MOV L,A ;SET HL=1
|
xp14: call xp18 !rel.op."<="
|
RZ ;REL. TRUE, RETURN
|
mov l,a !set hl=1
|
RC
|
rz !rel. true, return
|
MOV L,H ;ELSE SET HL=0
|
rc
|
RET
|
mov l,h !else set hl=0
|
XP15 CALL XP18 ;REL.OP."="
|
ret
|
RNZ ;FALSE, RETRUN HL=0
|
xp15: call xp18 !rel.op."="
|
MOV L,A ;ELSE SET HL=1
|
rnz !false, retrun hl=0
|
RET
|
mov l,a !else set hl=1
|
XP16 CALL XP18 ;REL.OP."<"
|
ret
|
RNC ;FALSE, RETURN HL=0
|
xp16: call xp18 !rel.op."<"
|
MOV L,A ;ELSE SET HL=1
|
rnc !false, return hl=0
|
RET
|
mov l,a !else set hl=1
|
XP17 POP H ;NOT REL.OP.
|
ret
|
RET ;RETURN HL=
|
xp17: pop h !not rel.op.
|
XP18 MOV A,C ;SUBROUTINE FOR ALL
|
ret !return hl=
|
POP H ;REL.OP.'S
|
xp18: mov a,c !subroutine for all
|
POP B
|
pop h !rel.op.'s
|
PUSH H ;REVERSE TOP OF STACK
|
pop b
|
PUSH B
|
push h !reverse top of stack
|
MOV C,A
|
push b
|
CALL EXPR2 ;GET 2ND
|
mov c,a
|
XCHG ;VALUE IN DE NOW
|
call expr2 !get 2nd
|
XTHL ;1ST IN HL
|
xchg !value in de now
|
CALL CKHLDE ;COMPARE 1ST WITH 2ND
|
xthl !1st in hl
|
POP D ;RESTORE TEXT POINTER
|
call ckhlde !compare 1st with 2nd
|
LXI H,0Q ;SET HL=0, A=1
|
pop d !restore text pointer
|
MVI A,1
|
lxi h,0q !set hl=0, a=1
|
RET
|
mvi a,1
|
;*
|
ret
|
EXPR2 RST 1 ;NEGATIVE SIGN?
|
!*
|
DB '-'
|
expr2: rst 1 !negative sign?
|
DB 6Q
|
defb '-'
|
LXI H,0Q ;YES, FAKE '0-'
|
defb 6q
|
JMP XP26 ;TREAT LIKE SUBTRACT
|
lxi h,0q !yes, fake '0-'
|
XP21 RST 1 ;POSITIVE SIGN? IGNORE
|
jmp xp26 !treat like subtract
|
DB '+'
|
xp21: rst 1 !positive sign? ignore
|
DB 0Q
|
defb '+'
|
XP22 CALL EXPR3 ;1ST
|
defb 0q
|
XP23 RST 1 ;ADD?
|
xp22: call expr3 !1st
|
DB '+'
|
xp23: rst 1 !add?
|
DB 25Q
|
defb '+'
|
PUSH H ;YES, SAVE VALUE
|
defb 25q
|
CALL EXPR3 ;GET 2ND
|
push h !yes, save value
|
XP24 XCHG ;2ND IN DE
|
call expr3 !get 2nd
|
XTHL ;1ST IN HL
|
xp24: xchg !2nd in de
|
MOV A,H ;COMPARE SIGN
|
xthl !1st in hl
|
XRA D
|
mov a,h !compare sign
|
MOV A,D
|
xra d
|
DAD D
|
mov a,d
|
POP D ;RESTORE TEXT POINTER
|
dad d
|
JM XP23 ;1ST 2ND SIGN DIFFER
|
pop d !restore text pointer
|
XRA H ;1ST 2ND SIGN EQUAL
|
jm xp23 !1st 2nd sign differ
|
JP XP23 ;SO ISp RESULT
|
xra h !1st 2nd sign equal
|
JMP QHOW ;ELSE WE HAVE OVERFLOW
|
jp xp23 !so isp result
|
XP25 RST 1 ;SUBTRACT?
|
jmp qhow !else we have overflow
|
DB '-'
|
xp25: rst 1 !subtract?
|
DB 203Q
|
defb '-'
|
XP26 PUSH H ;YES, SAVE 1ST
|
defb 203q
|
CALL EXPR3 ;GET 2ND
|
xp26: push h !yes, save 1st
|
CALL CHGSGN ;NEGATE
|
call expr3 !get 2nd
|
JMP XP24 ;AND ADD THEM
|
call chgsgn !negate
|
;*
|
jmp xp24 !and add them
|
EXPR3 CALL EXPR4 ;GET 1ST
|
!*
|
XP31 RST 1 ;MULTIPLY?
|
expr3: call expr4 !get 1st
|
DB '*'
|
xp31: rst 1 !multiply?
|
DB 54Q
|
defb '*'
|
PUSH H ;YES, SAVE 1ST
|
defb 54q
|
CALL EXPR4 ;AND GET 2ND
|
push h !yes, save 1st
|
MVI B,0Q ;CLEAR B FOR SIGN
|
call expr4 !and get 2nd
|
CALL CHKSGN ;CHECK SIGN
|
mvi b,0q !clear b for sign
|
XCHG ;2ND IN DE NOW
|
call chksgn !check sign
|
XTHL ;1ST IN HL
|
xchg !2nd in de now
|
CALL CHKSGN ;CHECK SIGN OF 1ST
|
xthl !1st in hl
|
MOV A,H ;IS HL > 255 ?
|
call chksgn !check sign of 1st
|
ORA A
|
mov a,h !is hl > 255 ?
|
JZ XP32 ;NO
|
ora a
|
MOV A,D ;YES, HOW ABOUT DE
|
jz xp32 !no
|
ORA D
|
mov a,d !yes, how about de
|
XCHG ;PUT SMALLER IN HL
|
ora d
|
JNZ AHOW ;ALSO >, WILL OVERFLOW
|
xchg !put smaller in hl
|
XP32 MOV A,L ;THIS IS DUMB
|
jnz ahow !also >, will overflow
|
LXI H,0Q ;CLEAR RESULT
|
xp32: mov a,l !this is dumb
|
ORA A ;ADD AND COUNT
|
lxi h,0q !clear result
|
JZ XP35
|
ora a !add and count
|
XP33 DAD D
|
jz xp35
|
JC AHOW ;OVERFLOW
|
xp33: dad d
|
DCR A
|
jc ahow !overflow
|
JNZ XP33
|
dcr a
|
JMP XP35 ;FINISHED
|
jnz xp33
|
XP34 RST 1 ;DIVIDE?
|
jmp xp35 !finished
|
DB '/'
|
xp34: rst 1 !divide?
|
DB 104Q
|
defb '/'
|
PUSH H ;YES, SAVE 1ST
|
defb 104q
|
CALL EXPR4 ;AND GET 2ND ONE
|
push h !yes, save 1st
|
MVI B,0Q ;CLEAR B FOR SIGN
|
call expr4 !and get 2nd one
|
CALL CHKSGN ;CHECK SIGN OF 2ND
|
mvi b,0q !clear b for sign
|
XCHG ;PUT 2ND IN DE
|
call chksgn !check sign of 2nd
|
XTHL ;GET 1ST IN HL
|
xchg !put 2nd in de
|
CALL CHKSGN ;CHECK SIGN OF 1ST
|
xthl !get 1st in hl
|
MOV A,D ;DIVIDE BY 0?
|
call chksgn !check sign of 1st
|
ORA E
|
mov a,d !divide by 0?
|
JZ AHOW ;SAY "HOW?"
|
ora e
|
PUSH B ;ELSE SAVE SIGN
|
jz ahow !say "how?"
|
CALL DIVIDE ;USE SUBROUTINE
|
push b !else save sign
|
MOV H,B ;RESULT IN HL NOW
|
call divide !use subroutine
|
MOV L,C
|
mov h,b !result in hl now
|
POP B ;GET SIGN BACK
|
mov l,c
|
XP35 POP D ;AND TEXT POINTER
|
pop b !get sign back
|
MOV A,H ;HL MUST BE +
|
xp35: pop d !and text pointer
|
ORA A
|
mov a,h !hl must be +
|
JM QHOW ;ELSE IT IS OVERFLOW
|
ora a
|
MOV A,B
|
jm qhow !else it is overflow
|
ORA A
|
mov a,b
|
CM CHGSGN ;CHANGE SIGN IFF NEEDED
|
ora a
|
JMP XP31 ;LOOK OR MORE TERMS
|
cm chgsgn !change sign iff needed
|
;*
|
jmp xp31 !look or more terms
|
EXPR4 LXI H,TAB4-1 ;FIND FUNCTION IN TAB4
|
!*
|
JMP EXEC ;AND GO DO IT
|
expr4: lxi h,tab4-1 !find function in tab4
|
XP40 RST 7 ;NO, NOT A FUNCTION
|
jmp exec !and go do it
|
JC XP41 ;NOR A VARIABLE
|
xp40: rst 7 !no, not a function
|
MOV A,M ;VARIABLE
|
jc xp41 !nor a variable
|
INX H
|
mov a,m !variable
|
MOV H,M ;VALUE IN HL
|
inx h
|
MOV L,A
|
mov h,m !value in hl
|
RET
|
mov l,a
|
XP41 CALL TSTNUM ;OR IS IT A NUMBER
|
ret
|
MOV A,B ;# OF DIGIT
|
xp41: call tstnum !or is it a number
|
ORA A
|
mov a,b !# of digit
|
RNZ ;OK
|
ora a
|
PARN RST 1 ;NO DIGIT, MUST BE
|
rnz !ok
|
DB '('
|
parn: rst 1 !no digit, must be
|
DB 5Q
|
defb '('
|
RST 3 ;"(EXPR)"
|
defb 5q
|
RST 1
|
rst 3 !"(expr)"
|
DB ')'
|
rst 1
|
DB 1Q
|
defb ')'
|
XP42 RET
|
defb 1q
|
XP43 JMP QWHAT ;ELSE SAY: "WHAT?"
|
xp42: ret
|
;*
|
xp43: jmp qwhat !else say: "what?"
|
RND CALL PARN ;*** RND(EXPR) ***
|
!*
|
MOV A,H ;EXPR MUST BE +
|
rnd: call parn !*** rnd(expr) ***
|
ORA A
|
mov a,h !expr must be +
|
JM QHOW
|
ora a
|
ORA L ;AND NON-ZERO
|
jm qhow
|
JZ QHOW
|
ora l !and non-zero
|
PUSH D ;SAVE BOTH
|
jz qhow
|
PUSH H
|
push d !save both
|
LHLD RANPNT ;GET MEMORY AS RANDOM
|
push h
|
LXI D,LSTROM ;NUMBER
|
lhld ranpnt !get memory as random
|
RST 4
|
lxi d,lstrom !number
|
JC RA1 ;WRAP AROUND IFF LAST
|
rst 4
|
LXI H,START
|
jc ra1 !wrap around iff last
|
RA1 MOV E,M
|
lxi h,start
|
INX H
|
ra1: mov e,m
|
MOV D,M
|
inx h
|
SHLD RANPNT
|
mov d,m
|
POP H
|
shld ranpnt
|
XCHG
|
pop h
|
PUSH B
|
xchg
|
CALL DIVIDE ;RND(N)=MOD(M,N)+1
|
push b
|
POP B
|
call divide !rnd(n)=mod(m,n)+1
|
POP D
|
pop b
|
INX H
|
pop d
|
RET
|
inx h
|
;*
|
ret
|
ABS CALL PARN ;*** ABS(EXPR) ***
|
!*
|
CALL CHKSGN ;CHECK SIGN
|
abs: call parn !*** abs(expr) ***
|
MOV A,H ;NOTE THAT -32768
|
call chksgn !check sign
|
ORA H ;CANNOT CHANGE SIGN
|
mov a,h !note that -32768
|
JM QHOW ;SO SAY: "HOW?"
|
ora h !cannot change sign
|
RET
|
jm qhow !so say: "how?"
|
SIZE LHLD TXTUNF ;*** SIZE ***
|
ret
|
PUSH D ;GET THE NUMBER OF FREE
|
size: lhld txtunf !*** size ***
|
XCHG ;BYTES BETWEEN 'TXTUNF'
|
push d !get the number of free
|
SIZEA LXI H,VARBGN ;AND 'VARBGN'
|
xchg !bytes between 'txtunf'
|
CALL SUBDE
|
sizea: lxi h,varbgn !and 'varbgn'
|
POP D
|
call subde
|
RET
|
pop d
|
;*
|
ret
|
;*********************************************************
|
!*
|
;*
|
!*********************************************************
|
;* *** OUT *** INP *** WAIT *** POKE *** PEEK *** & USR
|
!*
|
;*
|
!* *** out *** inp *** wait *** poke *** peek *** & usr
|
;* OUT I,J(,K,L)
|
!*
|
;*
|
!* out i,j(,k,l)
|
;* OUTPUTS EXPRESSION 'J' TO PORT 'I', AND MAY BE REPEATED
|
!*
|
;* AS IN DATA 'L' TO PORT 'K' AS MANY TIMES AS NEEDED
|
!* outputs expression 'j' to port 'i', and may be repeated
|
;* THIS COMMAND MODIFIES ;* THIS COMMAND MODIFIES
|
!* as in data 'l' to port 'k' as many times as needed
|
;* THIS COMMAND MODIFY'S A SMALL SECTION OF CODE LOCATED
|
!* this command modifies !* this command modifies
|
;* JUST ABOVE ADDRESS 2K
|
!* this command modify's a small section of code located
|
;*
|
!* just above address 2k
|
;* INP (I)
|
!*
|
;*
|
!* inp (i)
|
;* THIS FUNCTION RETURNS DATA READ FROM INPUT PORT 'I' AS
|
!*
|
;* IT'S VALUE.
|
!* this function returns data read from input port 'i' as
|
;* IT ALSO MODIFIES CODE JUST ABOVE 2K.
|
!* it's value.
|
;*
|
!* it also modifies code just above 2k.
|
;* WAIT I,J,K
|
!*
|
;*
|
!* wait i,j,k
|
;* THIS COMMAND READS THE STATUS OF PORT 'I', EXCLUSIVE OR'S
|
!*
|
;* THE RESULT WITH 'K' IF THERE IS ONE, OR IF NOT WITH 0,
|
!* this command reads the status of port 'i', exclusive or's
|
;* AND'S WITH 'J' AND RETURNS WHEN THE RESULT IS NONZERO.
|
!* the result with 'k' if there is one, or if not with 0,
|
;* ITS MODIFIED CODE IS ALSO ABOVE 2K.
|
!* and's with 'j' and returns when the result is nonzero.
|
;*
|
!* its modified code is also above 2k.
|
;* POKE I,J(,K,L)
|
!*
|
;*
|
!* poke i,j(,k,l)
|
;* THIS COMMAND WORKS LIKE OUT EXCEPT THAT IT PUTS DATA 'J'
|
!*
|
;* INTO MEMORY LOCATION 'I'.
|
!* this command works like out except that it puts data 'j'
|
;*
|
!* into memory location 'i'.
|
;* PEEK (I)
|
!*
|
;*
|
!* peek (i)
|
;* THIS FUNCTION WORKS LIKE INP EXCEPT IT GETS IT'S VALUE
|
!*
|
;* FROM MEMORY LOCATION 'I'.
|
!* this function works like inp except it gets it's value
|
;*
|
!* from memory location 'i'.
|
;* USR (I(,J))
|
!*
|
;*
|
!* usr (i(,j))
|
;* USR CALLS A MACHINE LANGUAGE SUBROUTINE AT LOCATION 'I'
|
!*
|
;* IF THE OPTIONAL PARAMETER 'J' IS USED ITS VALUE IS PASSED
|
!* usr calls a machine language subroutine at location 'i'
|
;* IN H&L. THE VALUE OF THE FUNCTION SHOULD BE RETURNED IN H&L.
|
!* if the optional parameter 'j' is used its value is passed
|
;*
|
!* in h&l. the value of the function should be returned in h&l.
|
;************************************************************
|
!*
|
;*
|
!************************************************************
|
OUTCMD RST 3
|
!*
|
MOV A,L
|
outcmd:rst 3
|
STA OUTIO + 1
|
mov a,l
|
RST 1
|
sta outio + 1
|
DB ','
|
rst 1
|
DB 2FH
|
defb ','
|
RST 3
|
defb 2fh
|
MOV A,L
|
rst 3
|
CALL OUTIO
|
mov a,l
|
RST 1
|
call outio
|
DB ','
|
rst 1
|
DB 03H
|
defb ','
|
JMP OUTCMD
|
defb 03h
|
RST 6
|
jmp outcmd
|
WAITCM RST 3
|
rst 6
|
MOV A,L
|
waitcm:rst 3
|
STA WAITIO + 1
|
mov a,l
|
RST 1
|
sta waitio + 1
|
DB ','
|
rst 1
|
DB 1BH
|
defb ','
|
RST 3
|
defb 1bh
|
PUSH H
|
rst 3
|
RST 1
|
push h
|
DB ','
|
rst 1
|
DB 7H
|
defb ','
|
RST 3
|
defb 7h
|
MOV A,L
|
rst 3
|
POP H
|
mov a,l
|
MOV H,A
|
pop h
|
JMP $ + 2
|
mov h,a
|
MVI H,0
|
jmp $ + 2
|
JMP WAITIO
|
mvi h,0
|
INP CALL PARN
|
jmp waitio
|
MOV A,L
|
inp: call parn
|
STA INPIO + 1
|
mov a,l
|
MVI H,0
|
sta inpio + 1
|
JMP INPIO
|
mvi h,0
|
JMP QWHAT
|
jmp inpio
|
POKE RST 3
|
jmp qwhat
|
PUSH H
|
poke: rst 3
|
RST 1
|
push h
|
DB ','
|
rst 1
|
DB 12H
|
defb ','
|
RST 3
|
defb 12h
|
MOV A,L
|
rst 3
|
POP H
|
mov a,l
|
MOV M,A
|
pop h
|
RST 1
|
mov m,a
|
DB ',',03H
|
rst 1
|
JMP POKE
|
defb ',',03h
|
RST 6
|
jmp poke
|
PEEK CALL PARN
|
rst 6
|
MOV L,M
|
peek: call parn
|
MVI H,0
|
mov l,m
|
RET
|
mvi h,0
|
JMP QWHAT
|
ret
|
USR PUSH B
|
jmp qwhat
|
RST 1
|
usr: push b
|
DB '(',28D ;QWHAT
|
rst 1
|
RST 3 ;EXPR
|
defb '(',28d !qwhat
|
RST 1
|
rst 3 !expr
|
DB ')',7 ;PASPARM
|
rst 1
|
PUSH D
|
defb ')',7 !pasparm
|
LXI D,USRET
|
push d
|
PUSH D
|
lxi d,usret
|
PUSH H
|
push d
|
RET ;CALL USR ROUTINE
|
push h
|
PASPRM RST 1
|
ret !call usr routine
|
DB ',',14D
|
pasprm:rst 1
|
PUSH H
|
defb ',',14d
|
RST 3
|
push h
|
RST 1
|
rst 3
|
DB ')',9
|
rst 1
|
POP B
|
defb ')',9
|
PUSH D
|
pop b
|
LXI D,USRET
|
push d
|
PUSH D
|
lxi d,usret
|
PUSH B
|
push d
|
RET ;CALL USR ROUTINE
|
push b
|
USRET POP D
|
ret !call usr routine
|
POP B
|
usret: pop d
|
RET
|
pop b
|
JMP QWHAT
|
ret
|
;*
|
jmp qwhat
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE ***
|
!*
|
;*
|
!* *** divide *** subde *** chksgn *** chgsgn *** & ckhlde ***
|
;* 'DIVIDE' DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL
|
!*
|
;*
|
!* 'divide' divides hl by de, result in bc, remainder in hl
|
;* 'SUBDE' SUBTRACTS DE FROM HL
|
!*
|
;*
|
!* 'subde' subtracts de from hl
|
;* 'CHKSGN' CHECKS SIGN OF HL. IFF +, NO CHANGE. IFF -, CHANGE
|
!*
|
;* SIGN AND FLIP SIGN OF B.
|
!* 'chksgn' checks sign of hl. iff +, no change. iff -, change
|
;*
|
!* sign and flip sign of b.
|
;* 'CHGSGN' CHNGES SIGN OF HL AND B UNCONDITIONALLY.
|
!*
|
;*
|
!* 'chgsgn' chnges sign of hl and b unconditionally.
|
;* 'CKHLE' CHECKS SIGN OF HL AND DE. IFF DIFFERENT, HL AND DE
|
!*
|
;* ARE INTERCHANGED. IFF SAME SIGN, NOT INTERCHANGED. EITHER
|
!* 'ckhle' checks sign of hl and de. iff different, hl and de
|
;* CASE, HL DE ARE THEN COMPARED TO SET THE FLAGS.
|
!* are interchanged. iff same sign, not interchanged. either
|
;*
|
!* case, hl de are then compared to set the flags.
|
DIVIDE PUSH H ;*** DIVIDE ***
|
!*
|
MOV L,H ;DIVIDE H BY DE
|
divide:push h !*** divide ***
|
MVI H,0
|
mov l,h !divide h by de
|
CALL DV1
|
mvi h,0
|
MOV B,C ;SAVE RESULT IN B
|
call dv1
|
MOV A,L ;(REMAINDER+L)/DE
|
mov b,c !save result in b
|
POP H
|
mov a,l !(remainder+l)/de
|
MOV H,A
|
pop h
|
DV1 MVI C,377Q ;RESULT IN C
|
mov h,a
|
DV2 INR C ;DUMB ROUTINE
|
dv1: mvi c,377q !result in c
|
CALL SUBDE ;DIVIDE BY SUBTRACT
|
dv2: inr c !dumb routine
|
JNC DV2 ;AND COUNT
|
call subde !divide by subtract
|
DAD D
|
jnc dv2 !and count
|
RET
|
dad d
|
;*
|
ret
|
SUBDE MOV A,L ;*** SUBDE ***
|
!*
|
SUB E ;SUBTRACT DE FROM
|
subde: mov a,l !*** subde ***
|
MOV L,A ;HL
|
sub e !subtract de from
|
MOV A,H
|
mov l,a !hl
|
SBB D
|
mov a,h
|
MOV H,A
|
sbb d
|
RET
|
mov h,a
|
;*
|
ret
|
CHKSGN MOV A,H ;*** CHKSGN ***
|
!*
|
ORA A ;CHECK SIGN OF HL
|
chksgn:mov a,h !*** chksgn ***
|
RP ;IFF -, CHANGE SIGN
|
ora a !check sign of hl
|
;*
|
rp !iff -, change sign
|
CHGSGN MOV A,H ;*** CHGSGN ***
|
!*
|
CMA ;CHANGE SIGN OF HL
|
chgsgn:mov a,h !*** chgsgn ***
|
MOV H,A
|
cma !change sign of hl
|
MOV A,L
|
mov h,a
|
CMA
|
mov a,l
|
MOV L,A
|
cma
|
INX H
|
mov l,a
|
MOV A,B ;AND ALSO FLIP B
|
inx h
|
XRI 200Q
|
mov a,b !and also flip b
|
MOV B,A
|
xri 200q
|
RET
|
mov b,a
|
;*
|
ret
|
CKHLDE MOV A,H
|
!*
|
XRA D ;SAME SIGN?
|
ckhlde:mov a,h
|
JP CK1 ;YES, COMPARE
|
xra d !same sign?
|
XCHG ;NO, XCH AND COMP
|
jp ck1 !yes, compare
|
CK1 RST 4
|
xchg !no, xch and comp
|
RET
|
ck1: rst 4
|
;*
|
ret
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** SETVAL *** FIN *** ENDCHK *** & ERROR (& FRIENDS) ***
|
!*
|
;*
|
!* *** setval *** fin *** endchk *** & error (& friends) ***
|
;* "SETVAL" EXPECTS A VARIABLE, FOLLOWED BY AN EQUAL SIGN AND
|
!*
|
;* THEN AN EXPR. IT EVALUATES THE EXPR. AND SET THE VARIABLE
|
!* "setval" expects a variable, followed by an equal sign and
|
;* TO THAT VALUE.
|
!* then an expr. it evaluates the expr. and set the variable
|
;*
|
!* to that value.
|
;* "FIN" CHECKS THE END OF A COMMAND. IFF IT ENDED WITH ";",
|
!*
|
;* EXECUTION CONTINUES. IFF IT ENDED WITH A CR, IT FINDS THE
|
!* "fin" checks the end of a command. iff it ended with "!",
|
;* NEXT LINE AND CONTINUE FROM THERE.
|
!* execution continues. iff it ended with a cr, it finds the
|
;*
|
!* next line and continue from there.
|
;* "ENDCHK" CHECKS IFF A COMMAND IS ENDED WITH CR. THIS IS
|
!*
|
;* REQUIRED IN CERTAIN COMMANDS. (GOTO, RETURN, AND STOP ETC.)
|
!* "endchk" checks iff a command is ended with cr. this is
|
;*
|
!* required in certain commands. (goto, return, and stop etc.)
|
;* "ERROR" PRINTS THE STRING POINTED BY DE (AND ENDS WITH CR).
|
!*
|
;* IT THEN PRINTS THE LINE POINTED BY 'CURRNT' WITH A "?"
|
!* "error" prints the string pointed by de (and ends with cr).
|
;* INSERTED AT WHERE THE OLD TEXT POINTER (SHOULD BE ON TOP
|
!* it then prints the line pointed by 'currnt' with a "?"
|
;* O THE STACK) POINTS TO. EXECUTION OF TB IS STOPPED
|
!* inserted at where the old text pointer (should be on top
|
;* AND TBI IS RESTARTED. HOWEVER, IFF 'CURRNT' -> ZERO
|
!* o the stack) points to. execution of tb is stopped
|
;* (INDICATING A DIRECT COMMAND), THE DIRECT COMMAND IS NOT
|
!* and tbi is restarted. however, iff 'currnt' -> zero
|
;* PRINTED. AND IFF 'CURRNT' -> NEGATIVE # (INDICATING 'INPUT'
|
!* (indicating a direct command), the direct command is not
|
;* COMMAND, THE INPUT LINE IS NOT PRINTED AND EXECUTION IS
|
!* printed. and iff 'currnt' -> negative # (indicating 'input'
|
;* NOT TERMINATED BUT CONTINUED AT 'INPERR'.
|
!* command, the input line is not printed and execution is
|
;*
|
!* not terminated but continued at 'inperr'.
|
;* RELATED TO 'ERROR' ARE THE FOLLOWING:
|
!*
|
;* 'QWHAT' SAVES TEXT POINTER IN STACK AND GET MESSAGE "WHAT?"
|
!* related to 'error' are the following:
|
;* 'AWHAT' JUST GET MESSAGE "WHAT?" AND JUMP TO 'ERROR'.
|
!* 'qwhat' saves text pointer in stack and get message "what?"
|
;* 'QSORRY' AND 'ASORRY' DO SAME KIND OF THING.
|
!* 'awhat' just get message "what?" and jump to 'error'.
|
;* 'QHOW' AND 'AHOW' IN THE ZERO PAGE SECTION ALSO DO THIS
|
!* 'qsorry' and 'asorry' do same kind of thing.
|
;*
|
!* 'qhow' and 'ahow' in the zero page section also do this
|
SETVAL RST 7 ;*** SETVAL ***
|
!*
|
JC QWHAT ;"WHAT?" NO VARIABLE
|
setval:rst 7 !*** setval ***
|
PUSH H ;SAVE ADDRESS OF VAR.
|
jc qwhat !"what?" no variable
|
RST 1 ;PASS "=" SIGN
|
push h !save address of var.
|
DB '='
|
rst 1 !pass "=" sign
|
DB 10Q
|
defb '='
|
RST 3 ;EVALUATE EXPR.
|
defb 10q
|
MOV B,H ;VALUE IN BC NOW
|
rst 3 !evaluate expr.
|
MOV C,L
|
mov b,h !value in bc now
|
POP H ;GET ADDRESS
|
mov c,l
|
MOV M,C ;SAVE VALUE
|
pop h !get address
|
INX H
|
mov m,c !save value
|
MOV M,B
|
inx h
|
RET
|
mov m,b
|
SV1 JMP QWHAT ;NO "=" SIGN
|
ret
|
;*
|
sv1: jmp qwhat !no "=" sign
|
FIN RST 1 ;*** FIN ***
|
!*
|
DB 73Q
|
fin: rst 1 !*** fin ***
|
DB 4Q
|
defb 73q
|
POP PSW ;";", PURGE RET ADDR.
|
defb 4q
|
JMP RUNSML ;CONTINUE SAME LINE
|
pop psw !"!", purge ret addr.
|
FI1 RST 1 ;NOT ";", IS IT CR?
|
jmp runsml !continue same line
|
DB 0DH
|
fi1: rst 1 !not "!", is it cr?
|
DB 4Q
|
defb 0dh
|
POP PSW ;YES, PURGE RET ADDR.
|
defb 4q
|
JMP RUNNXL ;RUN NEXT LINE
|
pop psw !yes, purge ret addr.
|
FI2 RET ;ELSE RETURN TO CALLER
|
jmp runnxl !run next line
|
;*
|
fi2: ret !else return to caller
|
ENDCHK RST 5 ;*** ENDCHK ***
|
!*
|
CPI 0DH ;END WITH CR?
|
endchk:rst 5 !*** endchk ***
|
RZ ;OK, ELSE SAY: "WHAT?"
|
cpi 0dh !end with cr?
|
;*
|
rz !ok, else say: "what?"
|
QWHAT PUSH D ;*** QWHAT ***
|
!*
|
AWHAT LXI D,WHAT ;*** AWHAT ***
|
qwhat: push d !*** qwhat ***
|
ERROR SUB A ;*** ERROR ***
|
awhat: lxi d,what !*** awhat ***
|
CALL PRTSTG ;PRINT 'WHAT?', 'HOW?'
|
error: sub a !*** error ***
|
POP D ;OR 'SORRY'
|
call prtstg !print 'what?', 'how?'
|
LDAX D ;SAVE THE CHARACTER
|
pop d !or 'sorry'
|
PUSH PSW ;AT WHERE OLD DE ->
|
ldax d !save the character
|
SUB A ;AND PUT A 0 THERE
|
push psw !at where old de ->
|
STAX D
|
sub a !and put a 0 there
|
LHLD CURRNT ;GET CURRENT LINE #
|
stax d
|
PUSH H
|
lhld currnt !get current line #
|
MOV A,M ;CHECK THE VALUE
|
push h
|
INX H
|
mov a,m !check the value
|
ORA M
|
inx h
|
POP D
|
ora m
|
JZ RSTART ;IFF ZERO, JUST RERSTART
|
pop d
|
MOV A,M ;IFF NEGATIVE,
|
jz rstart !iff zero, just rerstart
|
ORA A
|
mov a,m !iff negative,
|
JM INPERR ;REDO INPUT
|
ora a
|
CALL PRTLN ;ELSE PRINT THE LINE
|
jm inperr !redo input
|
DCX D ;UPTO WHERE THE 0 IS
|
call prtln !else print the line
|
POP PSW ;RESTORE THE CHARACTER
|
dcx d !upto where the 0 is
|
STAX D
|
pop psw !restore the character
|
MVI A,77Q ;PRINTt A "?"
|
stax d
|
RST 2
|
mvi a,77q !printt a "?"
|
SUB A ;AND THE REST OF THE
|
rst 2
|
CALL PRTSTG ;LINE
|
sub a !and the rest of the
|
JMP RSTART
|
call prtstg !line
|
QSORRY PUSH D ;*** QSORRY ***
|
jmp rstart
|
ASORRY LXI D,SORRY ;*** ASORRY ***
|
qsorry:push d !*** qsorry ***
|
JMP ERROR
|
asorry:lxi d,sorry !*** asorry ***
|
;*
|
jmp error
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** GETLN *** FNDLN (& FRIENDS) ***
|
!*
|
;*
|
!* *** getln *** fndln (& friends) ***
|
;* 'GETLN' READS A INPUT LINE INTO 'BUFFER'. IT FIRST PROMPT
|
!*
|
;* THE CHARACTER IN A (GIVEN BY THE CALLER), THEN IT FILLS THE
|
!* 'getln' reads a input line into 'buffer'. it first prompt
|
;* THE BUFFER AND ECHOS. IT IGNORES LF'S AND NULLS, BUT STILL
|
!* the character in a (given by the caller), then it fills the
|
;* ECHOS THEM BACK. RUB-OUT IS USED TO CAUSE IT TO DELETE
|
!* the buffer and echos. it ignores lf's and nulls, but still
|
;* THE LAST CHARATER (IFF THERE IS ONE), AND ALT-MOD IS USED TO
|
!* echos them back. rub-out is used to cause it to delete
|
;* CAUSE IT TO DELETE THE WHOLE LINE AND START IT ALL OVER.
|
!* the last charater (iff there is one), and alt-mod is used to
|
;* 0DHSIGNALS THE END OF A LINE, AND CAUE 'GETLN' TO RETURN.
|
!* cause it to delete the whole line and start it all over.
|
;*
|
!* 0dhsignals the end of a line, and caue 'getln' to return.
|
;* 'FNDLN' FINDS A LINE WITH A GIVEN LINE # (IN HL) IN THE
|
!*
|
;* TEXT SAVE AREA. DE IS USED AS THE TEXT POINTER. IFF THE
|
!* 'fndln' finds a line with a given line # (in hl) in the
|
;* LINE IS FOUND, DE WILL POINT TO THE BEGINNING OF THAT LINE
|
!* text save area. de is used as the text pointer. iff the
|
;* (I.E., THE LOW BYTE OF THE LINE #), AND FLAGS ARE NC & Z.
|
!* line is found, de will point to the beginning of that line
|
;* IFF THAT LINE IS NOT THERE AND A LINE WITH A HIGHER LINE #
|
!* (i.e., the low byte of the line #), and flags are nc & z.
|
;* IS FOUND, DE POINTS TO THERE AND FLAGS ARE NC & NZ. IFF
|
!* iff that line is not there and a line with a higher line #
|
;* WE REACHED THE END OF TEXT SAVE ARE AND CANNOT FIND THE
|
!* is found, de points to there and flags are nc & nz. iff
|
;* LINE, FLAGS ARE C & NZ.
|
!* we reached the end of text save are and cannot find the
|
;* 'FNDLN' WILL INITIALIZE DE TO THE BEGINNING OF THE TEXT SAVE
|
!* line, flags are c & nz.
|
;* AREA TO START THE SEARCH. SOME OTHER ENTRIES OF THIS
|
!* 'fndln' will initialize de to the beginning of the text save
|
;* ROUTINE WILL NOT INITIALIZE DE AND DO THE SEARCH.
|
!* area to start the search. some other entries of this
|
;* 'FNDLNP' WILL START WITH DE AND SEARCH FOR THE LINE #.
|
!* routine will not initialize de and do the search.
|
;* 'FNDNXT' WILL BUMP DE BY 2, FIND A 0DHAND THEN START SEARCH.
|
!* 'fndlnp' will start with de and search for the line #.
|
;* 'FNDSKP' USE DE TO FIND A CR, AND THEN STRART SEARCH.
|
!* 'fndnxt' will bump de by 2, find a 0dhand then start search.
|
;*
|
!* 'fndskp' use de to find a cr, and then strart search.
|
GETLN RST 2 ;*** GETLN ***
|
!*
|
LXI D,BUFFER ;PROMPT AND INIT
|
getln: rst 2 !*** getln ***
|
GL1 CALL CHKIO ;CHECK KEYBOARD
|
lxi d,buffer !prompt and init
|
JZ GL1 ;NO INPUT, WAIT
|
gl1: call chkio !check keyboard
|
CPI 177Q ;DELETE LST CHARACTER?
|
jz gl1 !no input, wait
|
JZ GL3 ;YES
|
cpi 177q !delete lst character?
|
CPI 12Q ;IGNORE LF
|
jz gl3 !yes
|
JZ GL1
|
cpi 12q !ignore lf
|
ORA A ;IGNORE NULL
|
jz gl1
|
JZ GL1
|
ora a !ignore null
|
CPI 134Q ;DELETE THE WHOLE LINE?
|
jz gl1
|
JZ GL4 ;YES
|
cpi 134q !delete the whole line?
|
STAX D ;ELSE, SAVE INPUT
|
jz gl4 !yes
|
INX D ;AND BUMP POINTER
|
stax d !else, save input
|
CPI 15Q ;WAS IT CR?
|
inx d !and bump pointer
|
JNZ GL2 ;NO
|
cpi 15q !was it cr?
|
MVI A,12Q ;YES, GET LINE FEED
|
jnz gl2 !no
|
RST 2 ;CALL OUTC AND LINE FEED
|
mvi a,12q !yes, get line feed
|
RET ;WE'VE GOT A LINE
|
rst 2 !call outc and line feed
|
GL2 MOV A,E ;MORE FREE ROOM?
|
ret !we've got a line
|
CPI BUFEND AND 0FFH
|
gl2: mov a,e !more free room?
|
JNZ GL1 ;YES, GET NEXT INPUT
|
cpi bufend and 0ffh
|
GL3 MOV A,E ;DELETE LAST CHARACTER
|
jnz gl1 !yes, get next input
|
CPI BUFFER AND 0FFH ;BUT DO WE HAVE ANY?
|
gl3: mov a,e !delete last character
|
JZ GL4 ;NO, REDO WHOLE LINE
|
cpi buffer and 0ffh !but do we have any?
|
DCX D ;YES, BACKUP POINTER
|
jz gl4 !no, redo whole line
|
MVI A,'_' ;AND ECHO A BACK-SPACE
|
dcx d !yes, backup pointer
|
RST 2
|
mvi a,'_' !and echo a back-space
|
JMP GL1 ;GO GET NEXT INPUT
|
rst 2
|
GL4 CALL CRLF ;REDO ENTIRE LINE
|
jmp gl1 !go get next input
|
MVI A,136Q ;CR, LF AND UP-ARROW
|
gl4: call crlf !redo entire line
|
JMP GETLN
|
mvi a,136q !cr, lf and up-arrow
|
;*
|
jmp getln
|
FNDLN MOV A,H ;*** FNDLN ***
|
!*
|
ORA A ;CHECK SIGN OF HL
|
fndln: mov a,h !*** fndln ***
|
JM QHOW ;IT CANNT BE -
|
ora a !check sign of hl
|
LXI D,TXTBGN ;INIT. TEXT POINTER
|
jm qhow !it cannt be -
|
;*
|
lxi d,txtbgn !init. text pointer
|
FNDLNP EQU $ ;*** FNDLNP ***
|
!*
|
FL1 PUSH H ;SAVE LINE #
|
fndlnp:equ $ !*** fndlnp ***
|
LHLD TXTUNF ;CHECK IFF WE PASSED END
|
fl1: push h !save line #
|
DCX H
|
lhld txtunf !check iff we passed end
|
RST 4
|
dcx h
|
POP H ;GET LINE # BACK
|
rst 4
|
RC ;C,NZ PASSED END
|
pop h !get line # back
|
LDAX D ;WE DID NOT, GET BYTE 1
|
rc !c,nz passed end
|
SUB L ;IS THIS THE LINE?
|
ldax d !we did not, get byte 1
|
MOV B,A ;COMPARE LOW ORDER
|
sub l !is this the line?
|
INX D
|
mov b,a !compare low order
|
LDAX D ;GET BYTE 2
|
inx d
|
SBB H ;COMPARE HIGH ORDER
|
ldax d !get byte 2
|
JC FL2 ;NO, NOT THERE YET
|
sbb h !compare high order
|
DCX D ;ELSE WE EITHER FOUND
|
jc fl2 !no, not there yet
|
ORA B ;IT, OR IT IS NOT THERE
|
dcx d !else we either found
|
RET ;NC,Z:FOUND; NC,NZ:NO
|
ora b !it, or it is not there
|
;*
|
ret !nc,z:found! nc,nz:no
|
FNDNXT EQU $ ;*** FNDNXT ***
|
!*
|
INX D ;FIND NEXT LINE
|
fndnxt:equ $ !*** fndnxt ***
|
FL2 INX D ;JUST PASSED BYTE 1 & 2
|
inx d !find next line
|
;*
|
fl2: inx d !just passed byte 1 & 2
|
FNDSKP LDAX D ;*** FNDSKP ***
|
!*
|
CPI 0DH ;TRY TO FIND 0DH
|
fndskp:ldax d !*** fndskp ***
|
JNZ FL2 ;KEEP LOOKING
|
cpi 0dh !try to find 0dh
|
INX D ;FOUND CR, SKIP OVER
|
jnz fl2 !keep looking
|
JMP FL1 ;CHECK IFF END OF TEXT
|
inx d !found cr, skip over
|
;*
|
jmp fl1 !check iff end of text
|
;*************************************************************
|
!*
|
;*
|
!*************************************************************
|
;* *** PRTSTG *** QTSTG *** PRTNUM *** & PRTLN ***
|
!*
|
;*
|
!* *** prtstg *** qtstg *** prtnum *** & prtln ***
|
;* 'PRTSTG' PRINTS A STRING POINTED BY DE. IT STOPS PRINTING
|
!*
|
;* AND RETURNS TO CALÌER WHEN EITHER A 0DHIS PRINTED OR WHEN
|
!* 'prtstg' prints a string pointed by de. it stops printing
|
;* THE NEXT BYTE IS THE SAME AS WHAT WAS IN A (GIVEN BY THE
|
!* and returns to calìer when either a 0dhis printed or when
|
;* CALLER). OLD A IS STORED IN B, OLD B IS LOST.
|
!* the next byte is the same as what was in a (given by the
|
;*
|
!* caller). old a is stored in b, old b is lost.
|
;* 'QTSTG' LOOKS FOR A BACK-ARROW, SINGLE QUOTE, OR DOUBLE
|
!*
|
;* QUOTE. IFF NONE OF THESE, RETURN TO CALLER. IFF BACK-ARROW,
|
!* 'qtstg' looks for a back-arrow, single quote, or double
|
;* OUTPUT A 0DHWITHOUT A LF. IFF SINGLE OR DOUBLE QUOTE, PRINT
|
!* quote. iff none of these, return to caller. iff back-arrow,
|
;* THE STRING IN THE QUOTE AND DEMANDS A MATCHING UNQUOTE.
|
!* output a 0dhwithout a lf. iff single or double quote, print
|
;* AFTER THE PRINTING THE NEXT 3 BYTES OF THE CALLER IS SKIPPED
|
!* the string in the quote and demands a matching unquote.
|
;* OVER (USUALLY A JUMP INSTRUCTION).
|
!* after the printing the next 3 bytes of the caller is skipped
|
;*
|
!* over (usually a jump instruction).
|
;* 'PRTNUM' PRINTS THE NUMBER IN HL. LEADING BLANKS ARE ADDED
|
!*
|
;* IFF NEEDED TO PAD THE NUMBER OF SPACES TO THE NUMBER IN C.
|
!* 'prtnum' prints the number in hl. leading blanks are added
|
;* HOWEVER, IFF THE NUMBER OF DIGITS IS LARGER THAN THE # IN
|
!* iff needed to pad the number of spaces to the number in c.
|
;* C, ALL DIGITS ARE PRINTED ANYWAY. NEGATIVE SIGN IS ALSO
|
!* however, iff the number of digits is larger than the # in
|
;* PRINTED AND COUNTED IN, POSITIVE SIGN IS NOT.
|
!* c, all digits are printed anyway. negative sign is also
|
;*
|
!* printed and counted in, positive sign is not.
|
;* 'PRTLN' PRINSrA SAVED TEXT LINE WITH LINE # AND ALL.
|
!*
|
;*
|
!* 'prtln' prinsra saved text line with line # and all.
|
PRTSTG MOV B,A ;*** PRTSTG ***
|
!*
|
PS1 LDAX D ;GET A CHARACTERr
|
prtstg:mov b,a !*** prtstg ***
|
INX D ;BUMP POINTER
|
ps1: ldax d !get a characterr
|
CMP B ;SAME AS OLD A?
|
inx d !bump pointer
|
RZ ;YES, RETURN
|
cmp b !same as old a?
|
RST 2 ;ELSE PRINT IT
|
rz !yes, return
|
CPI 0DH ;WAS IT A CR?
|
rst 2 !else print it
|
JNZ PS1 ;NO, NEXT
|
cpi 0dh !was it a cr?
|
RET ;YES, RETURN
|
jnz ps1 !no, next
|
;*
|
ret !yes, return
|
QTSTG RST 1 ;*** QTSTG ***
|
!*
|
DB '"'
|
qtstg: rst 1 !*** qtstg ***
|
DB 17Q
|
defb '"'
|
MVI A,42Q ;IT IS A "
|
defb 17q
|
QT1 CALL PRTSTG ;PRINT UNTIL ANOTHER
|
mvi a,42q !it is a "
|
CPI 0DH ;WAS LAST ONE A CR?
|
qt1: call prtstg !print until another
|
POP H ;RETURN ADDRESS
|
cpi 0dh !was last one a cr?
|
JZ RUNNXL ;WAS CR, RUN NEXT LINE
|
pop h !return address
|
QT2 INX H ;SKIP 3 BYTES ON RETURN
|
jz runnxl !was cr, run next line
|
INX H
|
qt2: inx h !skip 3 bytes on return
|
INX H
|
inx h
|
PCHL ;RETURN
|
inx h
|
QT3 RST 1 ;IS IT A ' ?
|
pchl !return
|
DB 47Q
|
qt3: rst 1 !is it a ' ?
|
DB 5Q
|
defb 47q
|
MVI A,47Q ;YES, DO SAME
|
defb 5q
|
JMP QT1 ;AS IN "
|
mvi a,47q !yes, do same
|
QT4 RST 1 ;IS IT BACK-ARROW?
|
jmp qt1 !as in "
|
DB 137Q
|
qt4: rst 1 !is it back-arrow?
|
DB 10Q
|
defb 137q
|
MVI A,215Q ;YES, 0DHWITHOUT LF!!
|
defb 10q
|
RST 2 ;DO IT TWICE TO GIVE
|
mvi a,215q !yes, 0dhwithout lf!!
|
RST 2 ;TTY ENOUGH TIME
|
rst 2 !do it twice to give
|
POP H ;RETURN ADDRESS
|
rst 2 !tty enough time
|
JMP QT2
|
pop h !return address
|
QT5 RET ;NONE OF ABOVE
|
jmp qt2
|
;*
|
qt5: ret !none of above
|
PRTNUM PUSH D ;*** PRTNUM ***
|
!*
|
LXI D,12Q ;DECIMAL
|
prtnum push d !*** prtnum ***
|
PUSH D ;SAVE AS A FLAG
|
lxi d,12q !decimal
|
MOV B,D ;B=SIGN
|
push d !save as a flag
|
DCR C ;C=SPACES
|
mov b,d !b=sign
|
CALL CHKSGN ;CHECK SIGN
|
dcr c !c=spaces
|
JP PN1 ;NO SIGN
|
call chksgn !check sign
|
MVI B,55Q ;B=SIGN
|
jp pn1 !no sign
|
DCR C ;'-' TAKES SPACE
|
mvi b,55q !b=sign
|
PN1 PUSH B ;SAVE SIGN & SPACE
|
dcr c !'-' takes space
|
PN2 CALL DIVIDE ;DEVIDE HL BY 10
|
pn1: push b !save sign & space
|
MOV A,B ;RESULT 0?
|
pn2: call divide !devide hl by 10
|
ORA C
|
mov a,b !result 0?
|
JZ PN3 ;YES, WE GOT ALL
|
ora c
|
XTHL ;NO, SAVE REMAINDER
|
jz pn3 !yes, we got all
|
DCR L ;AND COUNT SPACE
|
xthl !no, save remainder
|
PUSH H ;HL IS OLD BC
|
dcr l !and count space
|
MOV H,B ;MOVE RESULT TO BC
|
push h !hl is old bc
|
MOV L,C
|
mov h,b !move result to bc
|
JMP PN2 ;AND DIVIDE BY 10
|
mov l,c
|
PN3 POP B ;WE GOT ALL DIGITS IN
|
jmp pn2 !and divide by 10
|
PN4 DCR C ;THE STACK
|
pn3: pop b !we got all digits in
|
MOV A,C ;LOOK AT SPACE COUNT
|
pn4: dcr c !the stack
|
ORA A
|
mov a,c !look at space count
|
JM PN5 ;NO LEADING BLANKS
|
ora a
|
MVI A,40Q ;LEADING BLANKS
|
jm pn5 !no leading blanks
|
RST 2
|
mvi a,40q !leading blanks
|
JMP PN4 ;MORE?
|
rst 2
|
PN5 MOV A,B ;PRINT SIGN
|
jmp pn4 !more?
|
RST 2 ;MAYBE - OR NULL
|
pn5: mov a,b !print sign
|
MOV E,L ;LAST REMAINDER IN E
|
rst 2 !maybe - or null
|
PN6 MOV A,E ;CHECK DIGIT IN E
|
mov e,l !last remainder in e
|
CPI 12Q ;10 IS FLAG FOR NO MORE
|
pn6: mov a,e !check digit in e
|
POP D
|
cpi 12q !10 is flag for no more
|
RZ ;IFF SO, RETURN
|
pop d
|
ADI 60Q ;ELSE CONVERT TO ASCII
|
rz !iff so, return
|
RST 2 ;AND PRINT THE DIGIT
|
adi 60q !else convert to ascii
|
JMP PN6 ;GO BACK FOR MORE
|
rst 2 !and print the digit
|
;*
|
jmp pn6 !go back for more
|
PRTLN LDAX D ;*** PRTLN ***
|
!*
|
MOV L,A ;LOW ORDER LINE #
|
prtln: ldax d !*** prtln ***
|
INX D
|
mov l,a !low order line #
|
LDAX D ;HIGH ORDER
|
inx d
|
MOV H,A
|
ldax d !high order
|
INX D
|
mov h,a
|
MVI C,4Q ;PRINT 4 DIGIT LINE #
|
inx d
|
CALL PRTNUM
|
mvi c,4q !print 4 digit line #
|
MVI A,40Q ;FOLLOWED BY A BLANK
|
call prtnum
|
RST 2
|
mvi a,40q !followed by a blank
|
SUB A ;AND THEN THE TEXT
|
rst 2
|
CALL PRTSTG
|
sub a !and then the text
|
RET
|
call prtstg
|
;*
|
ret
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** MVUP *** MVDOWN *** POPA *** & PUSHA ***
|
!*
|
;*
|
!* *** mvup *** mvdown *** popa *** & pusha ***
|
;* 'MVUP' MOVES A BLOCK UP FROM HERE DE-> TO WHERE BC-> UNTIL
|
!*
|
;* DE = HL
|
!* 'mvup' moves a block up from here de-> to where bc-> until
|
;*
|
!* de = hl
|
;* 'MVDOWN' MOVES A BLOCK DOWN FROM WHERE DE-> TO WHERE HL->
|
!*
|
;* UNTIL DE = BC
|
!* 'mvdown' moves a block down from where de-> to where hl->
|
;*
|
!* until de = bc
|
;* 'POPA' RESTORES THE 'FOR' LOOP VARIABLE SAVE AREA FROM THE
|
!*
|
;* STACK
|
!* 'popa' restores the 'for' loop variable save area from the
|
;*
|
!* stack
|
;* 'PUSHA' STACKS THE 'FOR' LOOP VARIABLE SAVE AREA INTO THE
|
!*
|
;* STACK
|
!* 'pusha' stacks the 'for' loop variable save area into the
|
;*
|
!* stack
|
MVUP RST 4 ;*** MVUP ***
|
!*
|
RZ ;DE = HL, RETURN
|
mvup: rst 4 !*** mvup ***
|
LDAX D ;GET ONE BYTE
|
rz !de = hl, return
|
STAX B ;MOVE IT
|
ldax d !get one byte
|
INX D ;INCREASE BOTH POINTERS
|
stax b !move it
|
INX B
|
inx d !increase both pointers
|
JMP MVUP ;UNTIL DONE
|
inx b
|
;*
|
jmp mvup !until done
|
MVDOWN MOV A,B ;*** MVDOWN ***
|
!*
|
SUB D ;TEST IFF DE = BC
|
mvdown:mov a,b !*** mvdown ***
|
JNZ MD1 ;NO, GO MOVE
|
sub d !test iff de = bc
|
MOV A,C ;MAYBE, OTHER BYTE?
|
jnz md1 !no, go move
|
SUB E
|
mov a,c !maybe, other byte?
|
RZ ;YES, RETURN
|
sub e
|
MD1 DCX D ;ELSE MOVE A BYTE
|
rz !yes, return
|
DCX H ;BUT FIRST DECREASE
|
md1: dcx d !else move a byte
|
LDAX D ;BOTH POINTERS AND
|
dcx h !but first decrease
|
MOV M,A ;THEN DO IT
|
ldax d !both pointers and
|
JMP MVDOWN ;LOOP BACK
|
mov m,a !then do it
|
;*
|
jmp mvdown !loop back
|
POPA POP B ;BC = RETURN ADDR.
|
!*
|
POP H ;RESTORE LOPVAR, BUT
|
popa: pop b !bc = return addr.
|
SHLD LOPVAR ;=0 MEANS NO MORE
|
pop h !restore lopvar, but
|
MOV A,H
|
shld lopvar !=0 means no more
|
ORA L
|
mov a,h
|
JZ PP1 ;YEP, GO RETURN
|
ora l
|
POP H ;NOP, RESTORE OTHERS
|
jz pp1 !yep, go return
|
SHLD LOPINC
|
pop h !nop, restore others
|
POP H
|
shld lopinc
|
SHLD LOPLMT
|
pop h
|
POP H
|
shld loplmt
|
SHLD LOPLN
|
pop h
|
POP H
|
shld lopln
|
SHLD LOPPT
|
pop h
|
PP1 PUSH B ;BC = RETURN ADDR.
|
shld loppt
|
RET
|
pp1: push b !bc = return addr.
|
;*
|
ret
|
PUSHA LXI H,STKLMT ;*** PUSHA ***
|
!*
|
CALL CHGSGN
|
pusha: lxi h,stklmt !*** pusha ***
|
POP B ;BC=RETURN ADDRESS
|
call chgsgn
|
DAD SP ;IS STACK NEAR THE TOP?
|
pop b !bc=return address
|
JNC QSORRY ;YES, SORRY FOR THAT.
|
dad sp !is stack near the top?
|
LHLD LOPVAR ;ELSE SAVE LOOP VAR.S
|
jnc qsorry !yes, sorry for that.
|
MOV A,H ;BUT IFF LOPVAR IS 0
|
lhld lopvar !else save loop var.s
|
ORA L ;THAT WILL BE ALL
|
mov a,h !but iff lopvar is 0
|
JZ PU1
|
ora l !that will be all
|
LHLD LOPPT ;ELSE, MORE TO SAVE
|
jz pu1
|
PUSH H
|
lhld loppt !else, more to save
|
LHLD LOPLN
|
push h
|
PUSH H
|
lhld lopln
|
LHLD LOPLMT
|
push h
|
PUSH H
|
lhld loplmt
|
LHLD LOPINC
|
push h
|
PUSH H
|
lhld lopinc
|
LHLD LOPVAR
|
push h
|
PU1 PUSH H
|
lhld lopvar
|
PUSH B ;BC = RETURN ADDR.
|
pu1: push h
|
RET
|
push b !bc = return addr.
|
;*
|
ret
|
;**************************************************************
|
!*
|
;*
|
!**************************************************************
|
;* *** OUTC *** & CHKIO ****!
|
!*
|
;* THESE ARE THE ONLY I/O ROUTINES IN TBI.
|
!* *** outc *** & chkio ****!
|
;* 'OUTC' IS CONTROLLED BY A SOFTWARE SWITCH 'OCSW'. IFF OCSW=0
|
!* these are the only i/o routines in tbi.
|
;* 'OUTC' WILL JUST RETURN TO THE CALLER. IFF OCSW IS NOT 0,
|
!* 'outc' is controlled by a software switch 'ocsw'. iff ocsw=0
|
;* IT WILL OUTPUT THE BYTE IN A. IFF THAT IS A CR, A LF IS ALSO
|
!* 'outc' will just return to the caller. iff ocsw is not 0,
|
;* SEND OUT. ONLY THE FLAGS MAY BE CHANGED AT RETURN, ALL REG.
|
!* it will output the byte in a. iff that is a cr, a lf is also
|
;* ARE RESTORED.
|
!* send out. only the flags may be changed at return, all reg.
|
;*
|
!* are restored.
|
;* 'CHKIO' CHECKS THE INPUT. IFF NO INPUT, IT WILL RETURN TO
|
!*
|
;* THE CALLER WITH THE Z FLAG SET. IFF THERE IS INPUT, Z FLAG
|
!* 'chkio' checks the input. iff no input, it will return to
|
;* IS CLEARED AND THE INPUT BYTE IS IN A. HOWERER, IFF THE
|
!* the caller with the z flag set. iff there is input, z flag
|
;* INPUT IS A CONTROL-O, THE 'OCSW' SWITCH IS COMPLIMENTED, AND
|
!* is cleared and the input byte is in a. howerer, iff the
|
;* Z FLAG IS RETURNED. IFF A CONTROL-C IS READ, 'CHKIO' WILL
|
!* input is a control-o, the 'ocsw' switch is complimented, and
|
;* RESTART TBI AND DO NOT RETURN TO THE CALLER.
|
!* z flag is returned. iff a control-c is read, 'chkio' will
|
;*
|
!* restart tbi and do not return to the caller.
|
;* OUTC PUSH AF THIS IS AT LOC. 10
|
!*
|
;* LD A,OCSW CHECK SOFTWARE SWITCH
|
!* outc push af this is at loc. 10
|
;* IOR A
|
!* ld a,ocsw check software switch
|
OC2 JNZ OC3 ;IT IS ON
|
!* ior a
|
POP PSW ;IT IS OFF
|
oc2: jnz oc3 !it is on
|
RET ;RESTORE AF AND RETURN
|
pop psw !it is off
|
OC3 POP A ;GET OLD A BACK
|
ret !restore af and return
|
PUSH B ;SAVE B ON STACK
|
oc3: pop a !get old a back
|
PUSH D ;AND D
|
push b !save b on stack
|
PUSH H ;AND H TOO
|
push d !and d
|
STA OUTCAR ;SAVE CHARACTER
|
push h !and h too
|
MOV E,A ;PUT CHAR. IN E FOR CPM
|
sta outcar !save character
|
MVI C,2 ;GET CONOUT COMMAND
|
mov e,a !put char. in e for cpm
|
CALL CPM ;CALL CPM AND DO IT
|
mvi c,2 !get conout command
|
LDA OUTCAR ;GET CHAR. BACK
|
call cpm !call cpm and do it
|
CPI 0DH ;WAS IT A 'CR'?
|
lda outcar !get char. back
|
JNZ DONE ;NO, DONE
|
cpi 0dh !was it a 'cr'?
|
MVI E,0AH ;GET LINEFEED
|
jnz done !no, done
|
MVI C,2 ;AND CONOUT AGAIN
|
mvi e,0ah !get linefeed
|
CALL CPM ;CALL CPM
|
mvi c,2 !and conout again
|
DONE LDA OUTCAR ;GET CHARACTER BACK
|
call cpm !call cpm
|
IDONE POP H ;GET H BACK
|
done: lda outcar !get character back
|
POP D ;AND D
|
idone: pop h !get h back
|
POP B ;AND B TOO
|
pop d !and d
|
RET ;DONE AT LAST
|
pop b !and b too
|
CHKIO PUSH B ;SAVE B ON STACK
|
ret !done at last
|
PUSH D ;AND D
|
chkio: push b !save b on stack
|
PUSH H ;THEN H
|
push d !and d
|
MVI C,11 ;GET CONSTAT WORD
|
push h !then h
|
CALL CPM ;CALL THE BDOS
|
mvi c,11 !get constat word
|
ORA A ;SET FLAGS
|
call cpm !call the bdos
|
JNZ CI1 ;IF READY GET CHARACTER
|
ora a !set flags
|
JMP IDONE ;RESTORE AND RETURN
|
jnz ci1 !if ready get character
|
CI1 MVI C,1 ;GET CONIN WORD
|
jmp idone !restore and return
|
CALL CPM ;CALL THE BDOS
|
ci1: mvi c,1 !get conin word
|
CPI 0FH ;IS IT CONTROL-O?
|
call cpm !call the bdos
|
JNZ CI2 ;NO, MORE CHECKING
|
cpi 0fh !is it control-o?
|
LDA OCSW ;CONTROL-O FLIP OCSW
|
jnz ci2 !no, more checking
|
CMA ;ON TO OFF, OFF TO ON
|
lda ocsw !control-o flip ocsw
|
STA OCSW ;AND PUT IT BACK
|
cma !on to off, off to on
|
JMP CHKIO ;AND GET ANOTHER CHARACTER
|
sta ocsw !and put it back
|
CI2 CPI 3 ;IS IT CONTROL-C?
|
jmp chkio !and get another character
|
JNZ IDONE ;RETURN AND RESTORE IF NOT
|
ci2: cpi 3 !is it control-c?
|
JMP RSTART ;YES, RESTART TBI
|
jnz idone !return and restore if not
|
LSTROM EQU $ ;ALL ABOVE CAN BE ROM
|
jmp rstart !yes, restart tbi
|
OUTIO OUT 0FFH
|
lstrom:equ $ !all above can be rom
|
RET
|
outio: out 0ffh
|
WAITIO IN 0FFH
|
ret
|
XRA H
|
waitio:in 0ffh
|
ANA L
|
xra h
|
JZ WAITIO
|
ana l
|
RST 6
|
jz waitio
|
INPIO IN 0FFH
|
rst 6
|
MOV L,A
|
inpio: in 0ffh
|
RET
|
mov l,a
|
OUTCAR DB 0 ;OUTPUT CHAR. STORAGE
|
ret
|
OCSW DB 0FFH ;SWITCH FOR OUTPUT
|
outcar:defb 0 !output char. storage
|
CURRNT DW 0 ;POINTS TO CURRENT LINE
|
ocsw: defb 0ffh !switch for output
|
STKGOS DW 0 ;SAVES SP IN 'GOSUB'
|
currnt:defw 0 !points to current line
|
VARNXT DW 0 ;TEMPORARY STORAGE
|
stkgos:defw 0 !saves sp in 'gosub'
|
STKINP DW 0 ;SAVES SP IN 'INPUT'
|
varnxt:defw 0 !temporary storage
|
LOPVAR DW 0 ;'FOR' LOOP SAVE AREA
|
stkinp:defw 0 !saves sp in 'input'
|
LOPINC DW 0 ;INCREMENT
|
lopvar:defw 0 !'for' loop save area
|
LOPLMT DW 0 ;LIMIT
|
lopinc:defw 0 !increment
|
LOPLN DW 0 ;LINE NUMBER
|
loplmt:defw 0 !limit
|
LOPPT DW 0 ;TEXT POINTER
|
lopln: defw 0 !line number
|
RANPNT DW START ;RANDOM NUMBER POINTER
|
loppt: defw 0 !text pointer
|
TXTUNF DW TXTBGN ;->UNFILLED TEXT AREA
|
ranpnt:defw start !random number pointer
|
TXTBGN DS 1 ;TEXT SAVE AREA BEGINS
|
txtunf:defw txtbgn !->unfilled text area
|
MSG1 DB 7FH,7FH,7FH,'SHERRY BROTHERS TINY BASIC VER. 3.1',0DH
|
txtbgn:defvs 1 !text save area begins
|
INIT MVI A,0FFH
|
msg1: defb 7fh,7fh,7fh,'Tiny basic ver. 3.1',0dh
|
STA OCSW ;TURN ON OUTPUT SWITCH
|
init: mvi a,0ffh
|
MVI A,0CH ;GET FORM FEED
|
sta ocsw !turn on output switch
|
RST 2 ;SEND TO CRT
|
mvi a,0ch !get form feed
|
PATLOP SUB A ;CLEAR ACCUMULATOR
|
rst 2 !send to crt
|
LXI D,MSG1 ;GET INIT MESSAGE
|
patlop:sub a !clear accumulator
|
CALL PRTSTG ;SEND IT
|
lxi d,msg1 !get init message
|
LSTRAM LDA 7 ;GET FBASE FOR TOP
|
call prtstg !send it
|
STA RSTART+2
|
lstram:lda 7 !get fbase for top
|
DCR A ;DECREMENT FOR OTHER POINTERS
|
sta rstart+2
|
STA SS1A+2 ;AND FIX THEM TOO
|
dcr a !decrement for other pointers
|
STA TV1A+2
|
sta ss1a+2 !and fix them too
|
STA ST3A+2
|
sta tv1a+2
|
STA ST4A+2
|
sta st3a+2
|
STA IP3A+2
|
sta st4a+2
|
STA SIZEA+2
|
sta ip3a+2
|
STA GETLN+3
|
sta sizea+2
|
STA PUSHA+2
|
sta getln+3
|
LXI H,ST1 ;GET NEW START JUMP
|
sta pusha+2
|
SHLD START+1 ;AND FIX IT
|
lxi h,st1 !get new start jump
|
JMP ST1
|
shld start+1 !and fix it
|
; RESTART TABLE
|
jmp st1
|
ORG 0A50H
|
jmp qwhat !print "what?" iff wrong
|
RSTBL:
|
txtend:equ $ !text save area ends
|
XTHL ;*** TSTC OR RST 1 ***
|
varbgn:defvs 2*27 !variable @(0)
|
RST 5 ;IGNORE BLANKS AND
|
defvs 1 !extra byte for buffer
|
CMP M ;TEST CHARACTER
|
buffer:defvs 80 !input buffer
|
JMP TC1 ;REST OF THIS IS AT TC1
|
bufend:equ $ !buffer ends
|
;*
|
defvs 40 !extra bytes for stack
|
CRLF: EQU 0EH ;EXECUTE TIME LOCATION OF THIS INSTRUCTION.
|
stklmt:equ $ !top limit for stack
|
MVI A,0DH ;*** CRLF ***
|
org 2000h
|
;*
|
stack: equ $ !stack starts here
|
PUSH PSW ;*** OUTC OR RST 2 ***
|
|
LDA OCSW ;PRINT CHARACTER ONLY
|
|
ORA A ;IFF OCSW SWITCH IS ON
|
|
JMP OC2 ;REST OF THIS IS AT OC2
|
|
;*
|
|
CALL EXPR2 ;*** EXPR OR RST 3 ***
|
|
PUSH H ;EVALUATE AN EXPRESION
|
|
JMP EXPR1 ;REST OF IT IS AT EXPR1
|
|
DB 'W'
|
|
;*
|
|
MOV A,H ;*** COMP OR RST 4 ***
|
|
CMP D ;COMPARE HL WITH DE
|
|
RNZ ;RETURN CORRECT C AND
|
|
MOV A,L ;Z FLAGS
|
|
CMP E ;BUT OLD A IS LOST
|
|
RET
|
|
DB 'AN'
|
|
;*
|
|
SS1: EQU 28H ;EXECUTE TIME LOCATION OF THIS INSTRUCTION.
|
|
LDAX D ;*** IGNBLK/RST 5 ***
|
|
CPI 40Q ;IGNORE BLANKS
|
|
RNZ ;IN TEXT (WHERE DE->)
|
|
INX D ;AND RETURN THE FIRST
|
|
JMP SS1 ;NON-BLANK CHAR. IN A
|
|
;*
|
|
POP PSW ;*** FINISH/RST 6 ***
|
|
CALL FIN ;CHECK END OF COMMAND
|
|
JMP QWHAT ;PRINT "WHAT?" IFF WRONG
|
|
DB 'G'
|
|
;*
|
|
RST 5 ;*** TSTV OR RST 7 ***
|
|
SUI 100Q ;TEST VARIABLES
|
|
RC ;C:NOT A VARIABLE
|
|
JMP TSTV1 ;JUMP AROUND RESERVED AREA
|
|
; ROUTINE TO COPY RESTART TABLE INTO LOW MEMORY
|
|
RST1: EQU 8 ;LOCATION FIRST REATART ROUTINE
|
|
|
|
EOT: EQU 40H ;LAST LOC TO BE FILLED
|
|
|
|
ORG 0AA0H
|
|
NINIT: LXI H,RST1 ;POINT TO BEGINNING OF MODEL TABLE
|
|
LXI D,RSTBL
|
|
NXT: LDAX D
|
|
MOV M,A
|
|
INX H
|
|
INX D
|
|
MVI A,EOT
|
|
CMP L
|
|
JNZ NXT
|
|
LXI H,INIT
|
|
SHLD START+1
|
|
JMP START
|
|
ORG 0F00H
|
|
TXTEND EQU $ ;TEXT SAVE AREA ENDS
|
|
VARBGN DS 2*27 ;VARIABLE @(0)
|
|
DS 1 ;EXTRA BYTE FOR BUFFER
|
|
BUFFER DS 80 ;INPUT BUFFER
|
|
BUFEND EQU $ ;BUFFER ENDS
|
|
DS 40 ;EXTRA BYTES FOR STACK
|
|
STKLMT EQU $ ;TOP LIMIT FOR STACK
|
|
ORG 2000H
|
|
STACK EQU $ ;STACK STARTS HERE
|
|
END
|
|
stack: equ $ !stack starts here
|
|