OpenCores
URL https://opencores.org/ocsvn/cpu8080/cpu8080/trunk

Subversion Repositories cpu8080

[/] [cpu8080/] [tags/] [update/] [project/] [tinybasic.asm] - Diff between revs 9 and 11

Go to most recent revision | Show entire file | Details | Blame | View Log

Rev 9 Rev 11
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
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.