;************************************************************** ;* ;* TINY BASIC FOR INTEL 8080 ;* VERSION 1.0 ;* BY LI-CHEN WANG ;* 10 JUNE, 1976 ;* @COPYLEFT ;* ALL WRONGS RESERVED ;* ;************************************************************** ;* ;* ;*** ZERO PAGE SUBROUTINES *** ;* ;* THE 8080 INSTRUCTION SET LETS YOU HAVE 8 ROUTINES IN LOW ;* 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 ;* THE THREE BYTE INSTRUCTION CALL LLHH. TINY BASIC WILL ;* USE RST 0 AS START AND RST 1 THROUGH RST 7 FOR ;* THE SEVEN MOST FREQUENTLY USED SUBROUTINES. ;* TWO OTHER SUBROUTINES (CRLF AND TSTNUM) ARE ALSO IN THIS ;* 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 ;* LOCATION 100H AND THIS SECTION WILL END AT LOCATION 3FH ;* WITH A JUMP TO 108H. ;* ; ORG 8H ; XTHL ;*** TSTC OR RST 1 *** ; RST 5 ;IGNORE BLANKS AND ; CMP M ;TEST CHARACTER ; JMP TC1 ;REST OF THIS IS AT TC1 ;* ;CRLF MVI A,0DH ;*** CRLF *** ;* ; 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 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 ORG 100H ;OF CPM. START JMP NINIT ;GO TO INITIALIZATION ROUTINE. JIF TSTV1 JNZ TV1 ;NOT "@" ARRAY INX D ;IT IS THE "@" ARRAY CALL PARN ;@ SHOULD BE FOLLOWED DAD H ;BY (EXPR) AS ITS INDEX JC QHOW ;IS INDEX TOO BIG? PUSH D ;WILL IT OVERWRITE XCHG ;TEXT? CALL SIZE ;FIND SIZE OF FREE RST 4 ;AND CHECK THAT JC ASORRY ;IFF SO, SAY "SORRY" SS1A LXI H,VARBGN ;IFF NOT, GET ADDRESS CALL SUBDE ;OF @(EXPR) AND PUT IT POP D ;IN HL RET ;C FLAG IS CLEARED TV1 CPI 33Q ;NOT @, IS IT A TO Z? CMC ;IFF NOT RETURN C FLAG RC INX D ;IFF A THROUGH Z TV1A LXI H,VARBGN ;COMPUTE ADDRESS OF RLC ;THAT VARIABLE ADD L ;AND RETURN IT IN HL MOV L,A ;WITH C FLAG CLEARED MVI A,0 ADC H MOV H,A RET ;* ;* TSTC XCH HL,(SP) ;*** TSTC OR RST 1 *** ;* IGNBLK THIS IS AT LOC. 8 ;* CMP M AND THEN JMP HERE TC1 INX H ;COMPARE THE BYTE THAT JZ TC2 ;FOLLOWS THE RST INST. PUSH B ;WITH THE TEXT (DE->) MOV C,M ;IFF NOT =, ADD THE 2ND MVI B,0 ;BYTE THAT FOLLOWS THE DAD B ;RST TO THE OLD PC POP B ;I.E., DO A RELATIVE DCX D ;JUMP IFF NOT = TC2 INX D ;IFF =, SKIP THOSE BYTES INX H ;AND CONTINUE XTHL RET ;* TSTNUM LXI H,0 ;*** TSTNUM *** MOV B,H ;TEST IFF THE TEXT IS RST 5 ;A NUMBER TN1 CPI 60Q ;IFF NOT, RETURN 0 IN RC ;B AND HL CPI 72Q ;IFF NUMBERS, CONVERT RNC ;TO BINARY IN HL AND MVI A,360Q ;SET A TO # OF DIGITS ANA H ;IFF H>255, THERE IS NO JNZ QHOW ;ROOM FOR NEXT DIGIT INR B ;B COUNTS # OF DIGITS PUSH B MOV B,H ;HL=10;*HL+(NEW DIGIT) MOV C,L DAD H ;WHERE 10;* IS DONE BY DAD H ;SHIFT AND ADD DAD B DAD H LDAX D ;AND (DIGIT) IS FROM INX D ;STRIPPING THE ASCII ANI 17Q ;CODE ADD L MOV L,A MVI A,0 ADC H MOV H,A POP B LDAX D ;DO THIS DIGIT AFTER JP TN1 ;DIGIT. S SAYS OVERFLOW QHOW PUSH D ;*** ERROR: "HOW?" *** AHOW LXI D,HOW JMP ERROR HOW DB 'HOW?',0DH OK DB 'OK',0DH WHAT DB 'WHAT?',0DH SORRY DB 'SORRY',0DH ;* ;************************************************************** ;* ;* *** MAIN *** ;* ;* 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 ;* ">" AND READS A LINE. IFF THE LINE STARTS WITH A NON-ZERO ;* NUMBER, THIS NUMBER IS THE LINE NUMBER. THE LINE NUMBER ;* (IN 16 BIT BINARY) AND THE REST OF THE LINE (INCLUDING CR) ;* IS STORED IN THE MEMORY. IFF A LINE WITH THE SAME LINE ;* NUMBER IS ALREDY THERE, IT IS REPLACED BY THE NEW ONE. IF ;* 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 ;* 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 ;* 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 ;* 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 LXI D,OK ;DE->STRING SUB A ;A=0 CALL PRTSTG ;PRINT STRING UNTIL 0DH LXI H,ST2+1 ;LITERAL 0 SHLD CURRNT ;CURRNT->LINE # = 0 ST2 LXI H,0 SHLD LOPVAR SHLD STKGOS ST3 MVI A,76Q ;PROMPT '>' AND CALL GETLN ;READ A LINE PUSH D ;DE->END OF LINE ST3A LXI D,BUFFER ;DE->BEGINNING OF LINE CALL TSTNUM ;TESt IFF IT IS A NUMBER RST 5 MOV A,H ;HL=VALUE OF THE # OR ORA L ;0 IFF NO # WAS FOUND POP B ;BC->END OF LINE JZ DIRECT DCX D ;BACKUP DE AND SAVE MOV A,H ;VALUE OF LINE # THERE STAX D DCX D MOV A,L STAX D PUSH B ;BC,DE->BEGIN, END PUSH D MOV A,C SUB E PUSH PSW ;A=# OF BYTES IN LINE CALL FNDLN ;FIND THIS LINE IN SAVE PUSH D ;AREA, DE->SAVE AREA JNZ ST4 ;NZ:NOT FOUND, INSERT PUSH D ;Z:FOUND, DELETE IT CALL FNDNXT ;FIND NEXT LINE ;* DE->NEXT LINE POP B ;BC->LINE TO BE DELETED LHLD TXTUNF ;HL->UNFILLED SAVE AREA CALL MVUP ;MOVE UP TO DELETE MOV H,B ;TXTUNF->UNFILLED AREA MOV L,C SHLD TXTUNF ;UPDATE ST4 POP B ;GET READY TO INSERT LHLD TXTUNF ;BUT FIRT CHECK IF POP PSW ;THE LENGTH OF NEW LINE PUSH H ;IS 3 (LINE # AND CR) CPI 3 ;THEN DO NOT INSERT JZ RSTART ;MUST CLEAR THE STACK ADD L ;COMPUTE NEW TXTUNF MOV L,A MVI A,0 ADC H MOV H,A ;HL->NEW UNFILLED AREA ST4A LXI D,TXTEND ;CHECK TO SEE IF THERE RST 4 ;IS ENOUGH SPACE JNC QSORRY ;SORRY, NO ROOM FOR IT SHLD TXTUNF ;OK, UPDATE TXTUNF POP D ;DE->OLD UNFILLED AREA CALL MVDOWN POP D ;DE->BEGIN, HL->END POP H CALL MVUP ;MOVE NEW LINE TO SAVE JMP ST3 ;AREA ;* ;************************************************************** ;* ;* *** 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 ;* 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, ;* 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.', ;* '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 ;* 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 ;* MATCH THIS NULL ITEM AS DEFAULT. ;* TAB1 EQU $ ;DIRECT COMMANDS DB 'LIST' DB LIST SHR 8 + 128,LIST AND 0FFH DB 'RUN' DB RUN SHR 8 + 128,RUN AND 255 DB 'NEW' DB NEW SHR 8 + 128,NEW AND 255 DB 'LOAD' DB DLOAD SHR 8 + 128,DLOAD AND 255 DB 'SAVE' DB DSAVE SHR 8 + 128,DSAVE AND 255 DB 'BYE',80H,0H ;GO BACK TO CPM TAB2 EQU $ ;DIRECT/TATEMENT DB 'NEXT' DB NEXT SHR 8 + 128,NEXT AND 255 DB 'LET' DB LET SHR 8 + 128,LET AND 255 DB 'OUT' DB OUTCMD SHR 8 + 128,OUTCMD AND 255 DB 'POKE' DB POKE SHR 8 + 128,POKE AND 255 DB 'WAIT' DB WAITCM SHR 8 + 128,WAITCM AND 255 DB 'IF' DB IFF SHR 8 + 128,IFF AND 255 DB 'GOTO' DB GOTO SHR 8 + 128,GOTO AND 255 DB 'GOSUB' DB GOSUB SHR 8 + 128,GOSUB AND 255 DB 'RETURN' DB RETURN SHR 8 + 128,RETURN AND 255 DB 'REM' DB REM SHR 8 + 128,REM AND 255 DB 'FOR' DB FOR SHR 8 + 128,FOR AND 255 DB 'INPUT' DB INPUT SHR 8 + 128,INPUT AND 255 DB 'PRINT' DB PRINT SHR 8 + 128,PRINT AND 255 DB 'STOP' DB STOP SHR 8 + 128,STOP AND 255 DB DEFLT SHR 8 + 128,DEFLT AND 255 DB 'YOU CAN ADD MORE' ;COMMANDS BUT ;REMEMBER TO MOVE DEFAULT DOWN. TAB4 EQU $ ;FUNCTIONS DB 'RND' DB RND SHR 8 + 128,RND AND 255 DB 'INP' DB INP SHR 8 + 128,INP AND 255 DB 'PEEK' DB PEEK SHR 8 + 128,PEEK AND 255 DB 'USR' DB USR SHR 8 + 128,USR AND 255 DB 'ABS' DB ABS SHR 8 + 128,ABS AND 255 DB 'SIZE' DB SIZE SHR 8 + 128,SIZE AND 255 DB XP40 SHR 8 + 128,XP40 AND 255 DB 'YOU CAN ADD MORE' ;FUNCTIONS BUT REMEMBER ;TO MOVE XP40 DOWN TAB5 EQU $ ;"TO" IN "FOR" DB 'TO' DB FR1 SHR 8 + 128,FR1 AND 255 DB QWHAT SHR 8 + 128,QWHAT AND 255 TAB6 EQU $ ;"STEP" IN "FOR" DB 'STEP' DB FR2 SHR 8 + 128,FR2 AND 255 DB FR3 SHR 8 + 128,FR3 AND 255 TAB8 EQU $ ;RELATION OPERATORS DB '>=' DB XP11 SHR 8 + 128,XP11 AND 255 DB '#' DB XP12 SHR 8 + 128,XP12 AND 255 DB '>' DB XP13 SHR 8 + 128,XP13 AND 255 DB '=' DB XP15 SHR 8 + 128,XP15 AND 255 DB '<=' DB XP14 SHR 8 + 128,XP14 AND 255 DB '<' DB XP16 SHR 8 + 128,XP16 AND 255 DB XP17 SHR 8 + 128,XP17 AND 255 ;* DIRECT LXI H,TAB1-1 ;*** DIRECT *** ;* EXEC EQU $ ;*** EXEC *** EX0 RST 5 ;IGNORE LEADING BLANKS PUSH D ;SAVE POINTER EX1 LDAX D ;IFF FOUND '.' IN STRING INX D ;BEFORE ANY MISMATCH CPI 56Q ;WE DECLARE A MATCH JZ EX3 INX H ;HL->TABLE CMP M ;IFF MATCH, TEST NEXT JZ EX1 MVI A,177Q ;ELSE, SEE IFF BIT 7 DCX D ;OF TABLEIS SET, WHICH CMP M ;IS THE JUMP ADDR. (HI) JC EX5 ;C:YES, MATCHED EX2 INX H ;NC:NO, FIND JUMP ADDR. CMP M JNC EX2 INX H ;BUMP TO NEXT TAB. ITEM POP D ;RESTORE STRING POINTER JMP EX0 ;TEST AGAINST NEXT ITEM EX3 MVI A,177Q ;PARTIAL MATCH, FIND EX4 INX H ;JUMP ADDR., WHICH IS CMP M ;FLAGGED BY BIT 7 JNC EX4 EX5 MOV A,M ;LOAD HL WITH THE JUMP INX H ;ADDRESS FROM THE TABLE MOV L,M ANI 177Q ;MASK OFF BIT 7 MOV H,A 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 ;* COMMAND TABLE LOOKUP CODE OF 'DIRECT' AND 'EXEC' IN LAST ;* 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 ;* GO BACK TO 'RSTART'. ;* FOR 'GOTO' AND 'GOSUB': GO EXECUTE THE TARGET LINE. ;* FOR 'RETURN' AND 'NEXT': GO BACK TO SAVED RETURN LINE. ;* 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(CR)' SETS 'TXTUNF' TO POINT TO 'TXTBGN' ;* ;* '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 ;* 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. ;* '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. ;* 'DLOAD' LOADS A NAMED PROGRAM FROM DISK. ;* '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 SHLD TXTUNF ;* STOP CALL ENDCHK ;*** STOP(CR) *** JMP RSTART ;* RUN CALL ENDCHK ;*** RUN(CR) *** LXI D,TXTBGN ;FIRST SAVED LINE ;* RUNNXL LXI H,0 ;*** RUNNXL *** CALL FNDLNP ;FIND WHATEVER LINE # JC RSTART ;C:PASSED TXTUNF, QUIT ;* RUNTSL XCHG ;*** RUNTSL *** SHLD CURRNT ;SET 'CURRNT'->LINE # XCHG INX D ;BUMP PASS LINE # INX D ;* RUNSML CALL CHKIO ;*** RUNSML *** LXI H,TAB2-1 ;FIND COMMAND IN TAB2 JMP EXEC ;AND EXECUTE IT ;* GOTO RST 3 ;*** GOTO EXPR *** PUSH D ;SAVE FOR ERROR ROUTINE CALL ENDCHK ;MUST FIND A 0DH CALL FNDLN ;FIND THE TARGET LINE JNZ AHOW ;NO SUCH LINE # POP PSW ;CLEAR THE "PUSH DE" JMP RUNTSL ;GO DO IT CPM EQU 5 ;DISK PARAMETERS FCB EQU 5CH SETDMA EQU 26 OPEN EQU 15 READD EQU 20 WRITED EQU 21 CLOSE EQU 16 MAKE EQU 22 DELETE EQU 19 ;* DLOAD RST 5 ;IGNORE BLANKS PUSH H ;SAVE H CALL FCBSET ;SET UP FILE CONTROL BLOCK PUSH D ;SAVE THE REST PUSH B LXI D,FCB ;GET FCB ADDRESS MVI C,OPEN ;PREPARE TO OPEN FILE CALL CPM ;OPEN IT CPI 0FFH ;IS IT THERE? JZ QHOW ;NO, SEND ERROR XRA A ;CLEAR A STA FCB+32 ;START AT RECORD 0 LXI D,TXTUNF ;GET BEGINNING LOAD PUSH D ;SAVE DMA ADDRESS MVI C,SETDMA ; CALL CPM ;SET DMA ADDRESS MVI C,READD ; LXI D,FCB CALL CPM ;READ SECTOR CPI 1 ;DONE? JC RDMORE ;NO, READ MORE JNZ QHOW ;BAD READ MVI C,CLOSE LXI D,FCB CALL CPM ;CLOSE FILE POP D ;THROW AWAY DMA ADD. POP B ;GET OLD REGISTERS BACK POP D POP H RST 6 ;FINISH RDMORE POP D ;GET DMA ADDRESS LXI H,80H ;GET 128 DAD D ;ADD 128 TO DMA ADD. XCHG ;PUT IT BACK IN D JMP LOAD ;AND READ SOME MORE ;* DSAVE RST 5 ;IGNORE BLANKS PUSH H ;SAVE H CALL FCBSET ;SETUP FCB PUSH D PUSH B ;SAVE OTHERS LXI D,FCB MVI C,DELETE CALL CPM ;ERASE FILE IF IT EXISTS LXI D,FCB MVI C,MAKE CALL CPM ;MAKE A NEW ONE CPI 0FFH ;IS THERE SPACE? JZ QHOW ;NO, ERROR XRA A ;CLEAR A STA FCB+32 ;START AT RECORD 0 LXI D,TXTUNF ;GET BEGINNING SAVE PUSH D ;SAVE DMA ADDRESS MVI C,SETDMA ; CALL CPM ;SET DMA ADDRESS MVI C,WRITED LXI D,FCB CALL CPM ;WRITE SECTOR ORA A ;SET FLAGS JNZ QHOW ;IF NOT ZERO, ERROR POP D ;GET DMA ADD. BACK LDA TXTUNF+1 ;AND MSB OF LAST ADD. CMP D ;IS D SMALLER? JC SAVDON ;YES, DONE JNZ WRITMOR ;DONT TEST E IF NOT EQUAL LDA TXTUNF ;IS E SMALLER? CMP E JC SAVDON ;YES, DONE WRITMOR LXI H,80H DAD D ;ADD 128 TO DMA ADD. XCHG ;GET IT BACK IN D JMP SAVE ;WRITE SOME MORE SAVDON MVI C,CLOSE LXI D,FCB CALL CPM ;CLOSE FILE POP B ;GET REGISTERS BACK POP D POP H RST 6 ;FINISH ;* FCBSET LXI H,FCB ;GET FILE CONTROL BLOCK ADDRESS MVI M,0 ;CLEAR ENTRY TYPE FNCLR INX H ;NEXT LOCATION MVI M,' ' ;CLEAR TO SPACE MVI A,FCB+8 AND 255 CMP L ;DONE? JNZ FNCLR ;NO, DO IT AGAIN INX H ;NEXT MVI M,'T' ;SET FILE TYPE TO 'TBI' INX H MVI M,'B' INX H MVI M,'I' EXRC INX H ;CLEAR REST OF FCB MVI M,0 MVI A,FCB+15 AND 255 CMP L ;DONE? JNZ EXRC ;NO, CONTINUE LXI H,FCB+1 ;GET FILENAME START FN LDAX D ;GET CHARACTER CPI 0DH ;IS IT A 'CR' RZ ;YES, DONE CPI '!' ;LEGAL CHARACTER? JC QWHAT ;NO, SEND ERROR CPI '[' ;AGAIN JNC QWHAT ;DITTO MOV M,A ;SAVE IT IN FCB INX H ;NEXT INX D MVI A,FCB+9 AND 255 CMP L ;LAST? JNZ FN ;NO, CONTINUE RET ;TRUNCATE AT 8 CHARACTERS ;* ;************************************************************* ;* ;* *** LIST *** & PRINT *** ;* ;* LIST HAS TWO FORMS: ;* 'LIST(CR)' LISTS ALL SAVED LINES ;* '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- ;* 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 ;* BE PRINTED. IT STAYS EFFECTIVE FOR THE REST OF THE PRINT ;* 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 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 ;* ENDED WITH A COMMA, NO (CRL) IS GENERATED. ;* LIST CALL TSTNUM ;TEST IFF THERE IS A # CALL ENDCHK ;IFF NO # WE GET A 0 CALL FNDLN ;FIND THIS OR NEXT LINE LS1 JC RSTART ;C:PASSED TXTUNF CALL PRTLN ;PRINT THE LINE CALL CHKIO ;STOP IFF HIT CONTROL-C CALL FNDLNP ;FIND NEXT LINE JMP LS1 ;AND LOOP BACK ;* PRINT MVI C,6 ;C = # OF SPACES RST 1 ;IFF NULL LIST & ";" DB 73Q DB 6Q CALL CRLF ;GIVE CR-LF AND JMP RUNSML ;CONTINUE SAME LINE PR2 RST 1 ;IFF NULL LIST (CR) DB 0DH DB 6Q CALL CRLF ;ALSO GIVE CR-LF AND JMP RUNNXL ;GO TO NEXT LINE PR0 RST 1 ;ELSE IS IT FORMAT? DB '#' DB 5Q RST 3 ;YES, EVALUATE EXPR. MOV C,L ;AND SAVE IT IN C JMP PR3 ;LOOK FOR MORE TO PRINT PR1 CALL QTSTG ;OR IS IT A STRING? JMP PR8 ;IFF NOT, MUST BE EXPR. PR3 RST 1 ;IFF ",", GO FIND NEXT DB ',' DB 6Q CALL FIN ;IN THE LIST. JMP PR0 ;LIST CONTINUES PR6 CALL CRLF ;LIST ENDS RST 6 PR8 RST 3 ;EVALUATE THE EXPR PUSH B CALL PRTNUM ;PRINT THE VALUE POP B JMP PR3 ;MORE TO PRINT? ;* ;************************************************************** ;* ;* *** GOSUB *** & RETURN *** ;* ;* 'GOSUB EXPR;' OR 'GOSUB EXPR (CR)' IS LIKE THE 'GOTO' ;* COMMAND, EXCEPT THAT THE CURRENT TEXT POINTER, STACK POINTER ;* ETC. ARE SAVE SO THAT EXECUTION CAN BE CONTINUED AFTER THE ;* SUBROUTINE 'RETURN'. IN ORDER THAT 'GOSUB' CAN BE NESTED ;* (AND EVEN RECURSIVE), THE SAVE AREA MUST BE STACKED. ;* THE STACK POINTER IS SAVED IN 'STKGOS'. THE OLD 'STKGOS' IS ;* SAVED IN THE STACK. IFF WE ARE IN THE MAIN ROUTINE, 'STKGOS' ;* 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 ;* '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 PUSH D ;AND TEXT POINTER CALL FNDLN ;FIND THE TARGET LINE JNZ AHOW ;NOT THERE. SAY "HOW?" LHLD CURRNT ;FOUND IT, SAVE OLD PUSH H ;'CURRNT' OLD 'STKGOS' LHLD STKGOS PUSH H LXI H,0 ;AND LOAD NEW ONES SHLD LOPVAR DAD SP SHLD STKGOS JMP RUNTSL ;THEN RUN THAT LINE RETURN CALL ENDCHK ;THERE MUST BE A 0DH LHLD STKGOS ;OLD STACK POINTER MOV A,H ;0 MEANS NOT EXIST ORA L JZ QWHAT ;SO, WE SAY: "WHAT?" SPHL ;ELSE, RESTORE IT POP H SHLD STKGOS ;AND THE OLD 'STKGOS' POP H SHLD CURRNT ;AND THE OLD 'CURRNT' POP D ;OLD TEXT POINTER CALL POPA ;OLD "FOR" PARAMETERS RST 6 ;AND WE ARE BACK HOME ;* ;************************************************************** ;* ;* *** FOR *** & NEXT *** ;* ;* 'FOR' HAS TWO FORMS: ;* 'FOR VAR=EXP1 TO EXP2 STEP EXP1' AND 'FOR VAR=EXP1 TO EXP2' ;* THE SECOND FORM MEANS THE SAME THING AS THE FIRST FORM WITH ;* EXP1=1. (I.E., WITH A STEP OF +1.) ;* TBI WILL FIND THE VARIABLE VAR. AND SET ITS VALUE TO THE ;* CURRENT VALUE OF EXP1. IT ALSO EVALUATES EXPR2 AND EXP1 ;* AND SAVE ALL THESE TOGETHER WITH THE TEXT POINTERr ETC. IN ;* THE 'FOR' SAVE AREA, WHICH CONSISTS OF 'LOPVAR', 'LOPINC', ;* 'LOPLMT', 'LOPLN', AND 'LOPPT'. IFF THERE IS ALREADY SOME- ;* THING IN THE SAVE AREA (THIS IS INDICATED BY A NON-ZERO ;* 'LOPVAR'), THEN THE OLD SAVE AREA IS SAVED IN THE STACK ;* BEFORE THE NEW ONE OVERWRITES IT. ;* TBI WILL THEN DIG IN THE STACK AND FIND OUT IFF THIS SAME ;* VARIABLE WAS USED IN ANOTHER CURRENTLY ACTIVE 'FOR' LOOP. ;* 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 ;* WITH THE 'LOPVAR'. IFF THEY ARE NOT THE SAME, TBI DIGS IN ;* THE STACK TO FIND THE RIGHTt ONE AND PURGES ALL THOSE THAT ;* DID NOT MATCH. EITHER WAY, TBI THEN ADDS THE 'STEP' TO ;* THAT VARIABLE AND CHECK THE RESULT WITH THE LIMIT. IFF IT ;* IS WITHIN THE LIMIT, CONTROL LOOPS BACK TO THE COMMAND ;* 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. DCX H ;HL IS ITS ADDRESS SHLD LOPVAR ;SAVE THAT LXI H,TAB5-1 ;USE 'EXEC' TO LOOK JMP EXEC ;FOR THE WORD 'TO' FR1 RST 3 ;EVALUATE THE LIMIT SHLD LOPLMT ;SAVE THAT LXI H,TAB6-1 ;USE 'EXEC' TO LOOK JMP EXEC ;FOR THE WORD 'STEP' FR2 RST 3 ;FOUND IT, GET STEP JMP FR4 FR3 LXI H,1Q ;NOT FOUND, SET TO 1 FR4 SHLD LOPINC ;SAVE THAT TOO FR5 LHLD CURRNT ;SAVE CURRENT LINE # SHLD LOPLN XCHG ;AND TEXT POINTER SHLD LOPPT LXI B,12Q ;DIG INTO STACK TO LHLD LOPVAR ;FIND 'LOPVAR' XCHG MOV H,B MOV L,B ;HL=0 NOW DAD SP ;HERE IS THE STACK DB 76Q FR7 DAD B ;EACH LEVEL IS 10 DEEP MOV A,M ;GET THAT OLD 'LOPVAR' INX H ORA M JZ FR8 ;0 SAYS NO MORE IN IT MOV A,M DCX H CMP D ;SAME AS THIS ONE? JNZ FR7 MOV A,M ;THE OTHER HALF? CMP E JNZ FR7 XCHG ;YES, FOUND ONE LXI H,0Q DAD SP ;TRY TO MOVE SP MOV B,H MOV C,L LXI H,12Q DAD D CALL MVDOWN ;AND PURGE 10 WORDS SPHL ;IN THE STACK FR8 LHLD LOPPT ;JOB DONE, RESTORE DE XCHG RST 6 ;AND CONTINUE ;* NEXT RST 7 ;GET ADDRESS OF VAR. JC QWHAT ;NO VARIABLE, "WHAT?" SHLD VARNXT ;YES, SAVE IT NX0 PUSH D ;SAVE TEXT POINTER XCHG LHLD LOPVAR ;GET VAR. IN 'FOR' MOV A,H ORA L ;0 SAYS NEVER HAD ONE JZ AWHAT ;SO WE ASK: "WHAT?" RST 4 ;ELSE WE CHECK THEM JZ NX3 ;OK, THEY AGREE POP D ;NO, LET'S SEE CALL POPA ;PURGE CURRENT LOOP LHLD VARNXT ;AND POP ONE LEVEL JMP NX0 ;GO CHECK AGAIN NX3 MOV E,M ;COME HERE WHEN AGREED INX H MOV D,M ;DE=VALUE OF VAR. LHLD LOPINC PUSH H DAD D ;ADD ONE STEP XCHG LHLD LOPVAR ;PUT IT BACK MOV M,E INX H MOV M,D LHLD LOPLMT ;HL->LIMIT POP PSW ;OLD HL ORA A JP NX1 ;STEP > 0 XCHG NX1 CALL CKHLDE ;COMPARE WITH LIMIT POP D ;RESTORE TEXT POINTER JC NX2 ;OUTSIDE LIMIT LHLD LOPLN ;WITHIN LIMIT, GO SHLD CURRNT ;BACK TO THE SAVED LHLD LOPPT ;'CURRNT' AND TEXT XCHG ;POINTER RST 6 NX2 CALL POPA ;PURGE THIS LOOP RST 6 ;* ;************************************************************** ;* ;* *** 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. ;* ;* 'IF' IS FOLLOWED BY AN EXPR. AS A CONDITION AND ONE OR MORE ;* COMMANDS (INCLUDING OUTHER 'IF'S) SEPERATED BY SEMI-COLONS. ;* NOTE THAT THE WORD 'THEN' IS NOT USED. TBI EVALUATES THE ;* EXPR. IFF IT IS NON-ZERO, EXECUTION CONTINUES. IFF THE ;* 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 ;* DOUBLE QUOTES, OR IS A BACK-ARROW, IT HAS THE SAME EFFECT AS ;* IN 'PRINT'. IFF AN ITEM IS A VARIABLE, THIS VARIABLE NAME IS ;* PRINTED OUT FOLLOWED BY A COLON. THEN TBI WAITS FOR AN ;* EXPR. TO BE TYPED IN. THE VARIABLE ISs THEN SET TO THE ;* VALUE OF THIS EXPR. IFF THE VARIABLE IS PROCEDED BY A STRING ;* (AGAIN IN SINGLE OR DOUBLE QUOTES), THE STRING WILL BE ;* 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. ;* 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. ;* TBI EVALUATES THE EXPR. AND SET THE VARIBLE TO THAT VALUE. ;* TB WILL ALSO HANDLE 'LET' COMMAND WITHOUT THE WORD 'LET'. ;* THIS IS DONE BY 'DEFLT'. ;* REM LXI H,0Q ;*** REM *** DB 76Q ;* IFF RST 3 ;*** IFF *** MOV A,H ;IS THE EXPR.=0? ORA L JNZ RUNSML ;NO, CONTINUE CALL FNDSKP ;YES, SKIP REST OF LINE JNC RUNTSL JMP RSTART ;* INPERR LHLD STKINP ;*** INPERR *** SPHL ;RESTORE OLD SP POP H ;AND OLD 'CURRNT' SHLD CURRNT POP D ;AND OLD TEXT POINTER POP D ;REDO INPUT ;* INPUT EQU $ ;*** INPUT *** IP1 PUSH D ;SAVE IN CASE OF ERROR CALL QTSTG ;IS NEXT ITEM A STRING? JMP IP2 ;NO RST 7 ;YES. BUT FOLLOWED BY A JC IP4 ;VARIABLE? NO. JMP IP3 ;YES. INPUT VARIABLE IP2 PUSH D ;SAVE FOR 'PRTSTG' RST 7 ;MUST BE VARIABLE NOW JC QWHAT ;"WHAT?" IT IS NOT? LDAX D ;GET READY FOR 'RTSTG' MOV C,A SUB A STAX D POP D CALL PRTSTG ;PRINT STRING AS PROMPT MOV A,C ;RESTORE TEXT DCX D STAX D IP3 PUSH D ;SAVE IN CASE OF ERROR XCHG LHLD CURRNT ;ALSO SAVE 'CURRNT' PUSH H LXI H,IP1 ;A NEGATIVE NUMBER SHLD CURRNT ;AS A FLAG LXI H,0Q ;SAVE SP TOO DAD SP SHLD STKINP PUSH D ;OLD HL MVI A,72Q ;PRINT THIS TOO CALL GETLN ;AND GET A LINE IP3A LXI D,BUFFER ;POINTS TO BUFFER RST 3 ;EVALUATE INPUT NOP ;CAN BE 'CALL ENDCHK' NOP NOP POP D ;OK, GET OLD HL XCHG MOV M,E ;SAVE VALUE IN VAR. INX H MOV M,D POP H ;GET OLD 'CURRNT' SHLD CURRNT POP D ;AND OLD TEXT POINTER IP4 POP PSW ;PURGE JUNK IN STACK RST 1 ;IS NEXT CH. ','? DB ',' DB 3Q JMP IP1 ;YES, MORE ITEMS. IP5 RST 6 ;* DEFLT LDAX D ;*** DEFLT *** CPI 0DH ;EMPTY LINE IS OK JZ LT1 ;ELSE IT IS 'LET' ;* LET CALL SETVAL ;*** LET *** RST 1 ;SET VALUE TO VAR. DB ',' DB 3Q JMP LET ;ITEM BY ITEM LT1 RST 6 ;UNTIL FINISH ;* ;************************************************************** ;* ;* *** EXPR *** ;* ;* '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. ;* ::=(+ 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 ;* CAN BE AN IN PARANTHESE. ;* ;* EXPR CALL EXPR2 THIS IS AT LOC. 18 ;* PUSH HL SAVE VALUE EXPR1 LXI H,TAB8-1 ;LOOKUP REL.OP. JMP EXEC ;GO DO IT XP11 CALL XP18 ;REL.OP.">=" RC ;NO, RETURN HL=0 MOV L,A ;YES, RETURN HL=1 RET XP12 CALL XP18 ;REL.OP."#" RZ ;FALSE, RETURN HL=0 MOV L,A ;TRUE, RETURN HL=1 RET XP13 CALL XP18 ;REL.OP.">" RZ ;FALSE RC ;ALSO FALSE, HL=0 MOV L,A ;TRUE, HL=1 RET XP14 CALL XP18 ;REL.OP."<=" MOV L,A ;SET HL=1 RZ ;REL. TRUE, RETURN RC MOV L,H ;ELSE SET HL=0 RET XP15 CALL XP18 ;REL.OP."=" RNZ ;FALSE, RETRUN HL=0 MOV L,A ;ELSE SET HL=1 RET XP16 CALL XP18 ;REL.OP."<" RNC ;FALSE, RETURN HL=0 MOV L,A ;ELSE SET HL=1 RET XP17 POP H ;NOT REL.OP. RET ;RETURN HL= XP18 MOV A,C ;SUBROUTINE FOR ALL POP H ;REL.OP.'S POP B PUSH H ;REVERSE TOP OF STACK PUSH B MOV C,A CALL EXPR2 ;GET 2ND XCHG ;VALUE IN DE NOW XTHL ;1ST IN HL CALL CKHLDE ;COMPARE 1ST WITH 2ND POP D ;RESTORE TEXT POINTER LXI H,0Q ;SET HL=0, A=1 MVI A,1 RET ;* EXPR2 RST 1 ;NEGATIVE SIGN? DB '-' DB 6Q LXI H,0Q ;YES, FAKE '0-' JMP XP26 ;TREAT LIKE SUBTRACT XP21 RST 1 ;POSITIVE SIGN? IGNORE DB '+' DB 0Q XP22 CALL EXPR3 ;1ST XP23 RST 1 ;ADD? DB '+' DB 25Q PUSH H ;YES, SAVE VALUE CALL EXPR3 ;GET 2ND XP24 XCHG ;2ND IN DE XTHL ;1ST IN HL MOV A,H ;COMPARE SIGN XRA D MOV A,D DAD D POP D ;RESTORE TEXT POINTER JM XP23 ;1ST 2ND SIGN DIFFER XRA H ;1ST 2ND SIGN EQUAL JP XP23 ;SO ISp RESULT JMP QHOW ;ELSE WE HAVE OVERFLOW XP25 RST 1 ;SUBTRACT? DB '-' DB 203Q XP26 PUSH H ;YES, SAVE 1ST CALL EXPR3 ;GET 2ND CALL CHGSGN ;NEGATE JMP XP24 ;AND ADD THEM ;* EXPR3 CALL EXPR4 ;GET 1ST XP31 RST 1 ;MULTIPLY? DB '*' DB 54Q PUSH H ;YES, SAVE 1ST CALL EXPR4 ;AND GET 2ND MVI B,0Q ;CLEAR B FOR SIGN CALL CHKSGN ;CHECK SIGN XCHG ;2ND IN DE NOW XTHL ;1ST IN HL CALL CHKSGN ;CHECK SIGN OF 1ST MOV A,H ;IS HL > 255 ? ORA A JZ XP32 ;NO MOV A,D ;YES, HOW ABOUT DE ORA D XCHG ;PUT SMALLER IN HL JNZ AHOW ;ALSO >, WILL OVERFLOW XP32 MOV A,L ;THIS IS DUMB LXI H,0Q ;CLEAR RESULT ORA A ;ADD AND COUNT JZ XP35 XP33 DAD D JC AHOW ;OVERFLOW DCR A JNZ XP33 JMP XP35 ;FINISHED XP34 RST 1 ;DIVIDE? DB '/' DB 104Q PUSH H ;YES, SAVE 1ST CALL EXPR4 ;AND GET 2ND ONE MVI B,0Q ;CLEAR B FOR SIGN CALL CHKSGN ;CHECK SIGN OF 2ND XCHG ;PUT 2ND IN DE XTHL ;GET 1ST IN HL CALL CHKSGN ;CHECK SIGN OF 1ST MOV A,D ;DIVIDE BY 0? ORA E JZ AHOW ;SAY "HOW?" PUSH B ;ELSE SAVE SIGN CALL DIVIDE ;USE SUBROUTINE MOV H,B ;RESULT IN HL NOW MOV L,C POP B ;GET SIGN BACK XP35 POP D ;AND TEXT POINTER MOV A,H ;HL MUST BE + ORA A JM QHOW ;ELSE IT IS OVERFLOW MOV A,B ORA A 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 XP40 RST 7 ;NO, NOT A FUNCTION JC XP41 ;NOR A VARIABLE MOV A,M ;VARIABLE INX H MOV H,M ;VALUE IN HL MOV L,A RET XP41 CALL TSTNUM ;OR IS IT A NUMBER MOV A,B ;# OF DIGIT ORA A RNZ ;OK PARN RST 1 ;NO DIGIT, MUST BE DB '(' DB 5Q RST 3 ;"(EXPR)" RST 1 DB ')' DB 1Q XP42 RET XP43 JMP QWHAT ;ELSE SAY: "WHAT?" ;* RND CALL PARN ;*** RND(EXPR) *** MOV A,H ;EXPR MUST BE + ORA A JM QHOW ORA L ;AND NON-ZERO JZ QHOW PUSH D ;SAVE BOTH PUSH H LHLD RANPNT ;GET MEMORY AS RANDOM LXI D,LSTROM ;NUMBER RST 4 JC RA1 ;WRAP AROUND IFF LAST LXI H,START RA1 MOV E,M INX H MOV D,M SHLD RANPNT POP H XCHG PUSH B CALL DIVIDE ;RND(N)=MOD(M,N)+1 POP B POP D INX H RET ;* ABS CALL PARN ;*** ABS(EXPR) *** CALL CHKSGN ;CHECK SIGN MOV A,H ;NOTE THAT -32768 ORA H ;CANNOT CHANGE SIGN JM QHOW ;SO SAY: "HOW?" RET SIZE LHLD TXTUNF ;*** SIZE *** PUSH D ;GET THE NUMBER OF FREE XCHG ;BYTES BETWEEN 'TXTUNF' SIZEA LXI H,VARBGN ;AND 'VARBGN' CALL SUBDE POP D RET ;* ;********************************************************* ;* ;* *** OUT *** INP *** WAIT *** POKE *** PEEK *** & USR ;* ;* 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 ;* THIS COMMAND MODIFIES ;* THIS COMMAND MODIFIES ;* THIS COMMAND MODIFY'S A SMALL SECTION OF CODE LOCATED ;* JUST ABOVE ADDRESS 2K ;* ;* INP (I) ;* ;* THIS FUNCTION RETURNS DATA READ FROM INPUT PORT 'I' AS ;* IT'S VALUE. ;* IT ALSO MODIFIES CODE JUST ABOVE 2K. ;* ;* 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, ;* AND'S WITH 'J' AND RETURNS WHEN THE RESULT IS NONZERO. ;* ITS MODIFIED CODE IS ALSO ABOVE 2K. ;* ;* POKE I,J(,K,L) ;* ;* THIS COMMAND WORKS LIKE OUT EXCEPT THAT IT PUTS DATA 'J' ;* INTO MEMORY LOCATION 'I'. ;* ;* PEEK (I) ;* ;* THIS FUNCTION WORKS LIKE INP EXCEPT IT GETS IT'S VALUE ;* FROM MEMORY LOCATION 'I'. ;* ;* USR (I(,J)) ;* ;* USR CALLS A MACHINE LANGUAGE SUBROUTINE AT LOCATION 'I' ;* 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 STA OUTIO + 1 RST 1 DB ',' DB 2FH RST 3 MOV A,L CALL OUTIO RST 1 DB ',' DB 03H JMP OUTCMD RST 6 WAITCM RST 3 MOV A,L STA WAITIO + 1 RST 1 DB ',' DB 1BH RST 3 PUSH H RST 1 DB ',' DB 7H RST 3 MOV A,L POP H MOV H,A JMP $ + 2 MVI H,0 JMP WAITIO INP CALL PARN MOV A,L STA INPIO + 1 MVI H,0 JMP INPIO JMP QWHAT POKE RST 3 PUSH H RST 1 DB ',' DB 12H RST 3 MOV A,L POP H MOV M,A RST 1 DB ',',03H JMP POKE RST 6 PEEK CALL PARN MOV L,M MVI H,0 RET JMP QWHAT USR PUSH B RST 1 DB '(',28D ;QWHAT RST 3 ;EXPR RST 1 DB ')',7 ;PASPARM PUSH D LXI D,USRET PUSH D PUSH H RET ;CALL USR ROUTINE PASPRM RST 1 DB ',',14D PUSH H RST 3 RST 1 DB ')',9 POP B PUSH D LXI D,USRET PUSH D PUSH B RET ;CALL USR ROUTINE USRET POP D POP B RET JMP QWHAT ;* ;************************************************************** ;* ;* *** DIVIDE *** SUBDE *** CHKSGN *** CHGSGN *** & CKHLDE *** ;* ;* 'DIVIDE' DIVIDES HL BY DE, RESULT IN BC, REMAINDER IN HL ;* ;* 'SUBDE' SUBTRACTS DE FROM HL ;* ;* 'CHKSGN' CHECKS SIGN OF HL. IFF +, NO CHANGE. IFF -, CHANGE ;* SIGN AND FLIP SIGN OF B. ;* ;* '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 ;* CASE, HL DE ARE THEN COMPARED TO SET THE FLAGS. ;* DIVIDE PUSH H ;*** DIVIDE *** MOV L,H ;DIVIDE H BY DE MVI H,0 CALL DV1 MOV B,C ;SAVE RESULT IN B MOV A,L ;(REMAINDER+L)/DE POP H MOV H,A DV1 MVI C,377Q ;RESULT IN C DV2 INR C ;DUMB ROUTINE CALL SUBDE ;DIVIDE BY SUBTRACT JNC DV2 ;AND COUNT DAD D RET ;* SUBDE MOV A,L ;*** SUBDE *** SUB E ;SUBTRACT DE FROM MOV L,A ;HL MOV A,H SBB D MOV H,A RET ;* CHKSGN MOV A,H ;*** CHKSGN *** ORA A ;CHECK SIGN OF HL RP ;IFF -, CHANGE SIGN ;* CHGSGN MOV A,H ;*** CHGSGN *** CMA ;CHANGE SIGN OF HL MOV H,A MOV A,L CMA MOV L,A INX H MOV A,B ;AND ALSO FLIP B XRI 200Q MOV B,A RET ;* CKHLDE MOV A,H XRA D ;SAME SIGN? JP CK1 ;YES, COMPARE XCHG ;NO, XCH AND COMP CK1 RST 4 RET ;* ;************************************************************** ;* ;* *** 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 ;* 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 ;* 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.) ;* ;* "ERROR" PRINTS THE STRING POINTED BY DE (AND ENDS WITH CR). ;* IT THEN PRINTS THE LINE POINTED BY 'CURRNT' WITH A "?" ;* INSERTED AT WHERE THE OLD TEXT POINTER (SHOULD BE ON TOP ;* O THE STACK) POINTS TO. EXECUTION OF TB IS STOPPED ;* AND TBI IS RESTARTED. HOWEVER, IFF 'CURRNT' -> ZERO ;* (INDICATING A DIRECT COMMAND), THE DIRECT COMMAND IS NOT ;* PRINTED. AND IFF 'CURRNT' -> NEGATIVE # (INDICATING 'INPUT' ;* 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?" ;* 'AWHAT' JUST GET MESSAGE "WHAT?" AND JUMP TO 'ERROR'. ;* '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 PUSH H ;SAVE ADDRESS OF VAR. RST 1 ;PASS "=" SIGN DB '=' DB 10Q RST 3 ;EVALUATE EXPR. MOV B,H ;VALUE IN BC NOW MOV C,L POP H ;GET ADDRESS MOV M,C ;SAVE VALUE INX H MOV M,B RET SV1 JMP QWHAT ;NO "=" SIGN ;* FIN RST 1 ;*** FIN *** DB 73Q DB 4Q POP PSW ;";", PURGE RET ADDR. JMP RUNSML ;CONTINUE SAME LINE FI1 RST 1 ;NOT ";", IS IT CR? DB 0DH DB 4Q POP PSW ;YES, PURGE RET ADDR. JMP RUNNXL ;RUN NEXT LINE FI2 RET ;ELSE RETURN TO CALLER ;* ENDCHK RST 5 ;*** ENDCHK *** CPI 0DH ;END WITH CR? RZ ;OK, ELSE SAY: "WHAT?" ;* QWHAT PUSH D ;*** QWHAT *** AWHAT LXI D,WHAT ;*** AWHAT *** ERROR SUB A ;*** ERROR *** CALL PRTSTG ;PRINT 'WHAT?', 'HOW?' POP D ;OR 'SORRY' LDAX D ;SAVE THE CHARACTER PUSH PSW ;AT WHERE OLD DE -> SUB A ;AND PUT A 0 THERE STAX D LHLD CURRNT ;GET CURRENT LINE # PUSH H MOV A,M ;CHECK THE VALUE INX H ORA M POP D JZ RSTART ;IFF ZERO, JUST RERSTART MOV A,M ;IFF NEGATIVE, ORA A JM INPERR ;REDO INPUT CALL PRTLN ;ELSE PRINT THE LINE DCX D ;UPTO WHERE THE 0 IS POP PSW ;RESTORE THE CHARACTER STAX D MVI A,77Q ;PRINTt A "?" RST 2 SUB A ;AND THE REST OF THE CALL PRTSTG ;LINE JMP RSTART QSORRY PUSH D ;*** QSORRY *** ASORRY LXI D,SORRY ;*** ASORRY *** JMP ERROR ;* ;************************************************************** ;* ;* *** 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 ;* THE BUFFER AND ECHOS. IT IGNORES LF'S AND NULLS, BUT STILL ;* ECHOS THEM BACK. RUB-OUT IS USED TO CAUSE IT TO DELETE ;* THE LAST CHARATER (IFF THERE IS ONE), AND ALT-MOD IS USED TO ;* 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 ;* LINE IS FOUND, DE WILL POINT TO THE BEGINNING OF THAT LINE ;* (I.E., THE LOW BYTE OF THE LINE #), AND FLAGS ARE NC & Z. ;* IFF THAT LINE IS NOT THERE AND A LINE WITH A HIGHER LINE # ;* IS FOUND, DE POINTS TO THERE AND FLAGS ARE NC & NZ. IFF ;* WE REACHED THE END OF TEXT SAVE ARE AND CANNOT FIND THE ;* LINE, FLAGS ARE C & NZ. ;* 'FNDLN' WILL INITIALIZE DE TO THE BEGINNING OF THE TEXT SAVE ;* AREA TO START THE SEARCH. SOME OTHER ENTRIES OF THIS ;* ROUTINE WILL NOT INITIALIZE DE AND DO THE SEARCH. ;* 'FNDLNP' WILL START WITH DE AND SEARCH FOR THE LINE #. ;* '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 GL1 CALL CHKIO ;CHECK KEYBOARD JZ GL1 ;NO INPUT, WAIT CPI 177Q ;DELETE LST CHARACTER? JZ GL3 ;YES CPI 12Q ;IGNORE LF JZ GL1 ORA A ;IGNORE NULL JZ GL1 CPI 134Q ;DELETE THE WHOLE LINE? JZ GL4 ;YES STAX D ;ELSE, SAVE INPUT INX D ;AND BUMP POINTER CPI 15Q ;WAS IT CR? JNZ GL2 ;NO MVI A,12Q ;YES, GET LINE FEED RST 2 ;CALL OUTC AND LINE FEED RET ;WE'VE GOT A LINE GL2 MOV A,E ;MORE FREE ROOM? CPI BUFEND AND 0FFH JNZ GL1 ;YES, GET NEXT INPUT GL3 MOV A,E ;DELETE LAST CHARACTER CPI BUFFER AND 0FFH ;BUT DO WE HAVE ANY? JZ GL4 ;NO, REDO WHOLE LINE DCX D ;YES, BACKUP POINTER MVI A,'_' ;AND ECHO A BACK-SPACE RST 2 JMP GL1 ;GO GET NEXT INPUT GL4 CALL CRLF ;REDO ENTIRE LINE MVI A,136Q ;CR, LF AND UP-ARROW JMP GETLN ;* FNDLN MOV A,H ;*** FNDLN *** ORA A ;CHECK SIGN OF HL JM QHOW ;IT CANNT BE - LXI D,TXTBGN ;INIT. TEXT POINTER ;* FNDLNP EQU $ ;*** FNDLNP *** FL1 PUSH H ;SAVE LINE # LHLD TXTUNF ;CHECK IFF WE PASSED END DCX H RST 4 POP H ;GET LINE # BACK RC ;C,NZ PASSED END LDAX D ;WE DID NOT, GET BYTE 1 SUB L ;IS THIS THE LINE? MOV B,A ;COMPARE LOW ORDER INX D LDAX D ;GET BYTE 2 SBB H ;COMPARE HIGH ORDER JC FL2 ;NO, NOT THERE YET DCX D ;ELSE WE EITHER FOUND ORA B ;IT, OR IT IS NOT THERE RET ;NC,Z:FOUND; NC,NZ:NO ;* FNDNXT EQU $ ;*** FNDNXT *** INX D ;FIND NEXT LINE FL2 INX D ;JUST PASSED BYTE 1 & 2 ;* FNDSKP LDAX D ;*** FNDSKP *** CPI 0DH ;TRY TO FIND 0DH JNZ FL2 ;KEEP LOOKING INX D ;FOUND CR, SKIP OVER JMP FL1 ;CHECK IFF END OF TEXT ;* ;************************************************************* ;* ;* *** 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 ;* 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, ;* OUTPUT A 0DHWITHOUT A LF. IFF SINGLE OR DOUBLE QUOTE, PRINT ;* THE STRING IN THE QUOTE AND DEMANDS A MATCHING UNQUOTE. ;* 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. ;* HOWEVER, IFF THE NUMBER OF DIGITS IS LARGER THAN THE # IN ;* 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. ;* PRTSTG MOV B,A ;*** PRTSTG *** PS1 LDAX D ;GET A CHARACTERr INX D ;BUMP POINTER CMP B ;SAME AS OLD A? RZ ;YES, RETURN RST 2 ;ELSE PRINT IT CPI 0DH ;WAS IT A CR? JNZ PS1 ;NO, NEXT RET ;YES, RETURN ;* QTSTG RST 1 ;*** QTSTG *** DB '"' DB 17Q MVI A,42Q ;IT IS A " QT1 CALL PRTSTG ;PRINT UNTIL ANOTHER CPI 0DH ;WAS LAST ONE A CR? POP H ;RETURN ADDRESS JZ RUNNXL ;WAS CR, RUN NEXT LINE QT2 INX H ;SKIP 3 BYTES ON RETURN INX H INX H PCHL ;RETURN QT3 RST 1 ;IS IT A ' ? DB 47Q DB 5Q MVI A,47Q ;YES, DO SAME JMP QT1 ;AS IN " QT4 RST 1 ;IS IT BACK-ARROW? DB 137Q DB 10Q MVI A,215Q ;YES, 0DHWITHOUT LF!! RST 2 ;DO IT TWICE TO GIVE RST 2 ;TTY ENOUGH TIME POP H ;RETURN ADDRESS JMP QT2 QT5 RET ;NONE OF ABOVE ;* PRTNUM PUSH D ;*** PRTNUM *** LXI D,12Q ;DECIMAL PUSH D ;SAVE AS A FLAG MOV B,D ;B=SIGN DCR C ;C=SPACES CALL CHKSGN ;CHECK SIGN JP PN1 ;NO SIGN MVI B,55Q ;B=SIGN DCR C ;'-' TAKES SPACE PN1 PUSH B ;SAVE SIGN & SPACE PN2 CALL DIVIDE ;DEVIDE HL BY 10 MOV A,B ;RESULT 0? ORA C JZ PN3 ;YES, WE GOT ALL XTHL ;NO, SAVE REMAINDER DCR L ;AND COUNT SPACE PUSH H ;HL IS OLD BC MOV H,B ;MOVE RESULT TO BC MOV L,C JMP PN2 ;AND DIVIDE BY 10 PN3 POP B ;WE GOT ALL DIGITS IN PN4 DCR C ;THE STACK MOV A,C ;LOOK AT SPACE COUNT ORA A JM PN5 ;NO LEADING BLANKS MVI A,40Q ;LEADING BLANKS RST 2 JMP PN4 ;MORE? PN5 MOV A,B ;PRINT SIGN RST 2 ;MAYBE - OR NULL MOV E,L ;LAST REMAINDER IN E PN6 MOV A,E ;CHECK DIGIT IN E CPI 12Q ;10 IS FLAG FOR NO MORE POP D RZ ;IFF SO, RETURN ADI 60Q ;ELSE CONVERT TO ASCII RST 2 ;AND PRINT THE DIGIT JMP PN6 ;GO BACK FOR MORE ;* PRTLN LDAX D ;*** PRTLN *** MOV L,A ;LOW ORDER LINE # INX D LDAX D ;HIGH ORDER MOV H,A INX D MVI C,4Q ;PRINT 4 DIGIT LINE # CALL PRTNUM MVI A,40Q ;FOLLOWED BY A BLANK RST 2 SUB A ;AND THEN THE TEXT CALL PRTSTG RET ;* ;************************************************************** ;* ;* *** MVUP *** MVDOWN *** POPA *** & PUSHA *** ;* ;* '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 ;* ;* 'POPA' RESTORES THE 'FOR' LOOP VARIABLE SAVE AREA FROM THE ;* STACK ;* ;* 'PUSHA' STACKS THE 'FOR' LOOP VARIABLE SAVE AREA INTO THE ;* STACK ;* MVUP RST 4 ;*** MVUP *** RZ ;DE = HL, RETURN LDAX D ;GET ONE BYTE STAX B ;MOVE IT INX D ;INCREASE BOTH POINTERS INX B JMP MVUP ;UNTIL DONE ;* MVDOWN MOV A,B ;*** MVDOWN *** SUB D ;TEST IFF DE = BC JNZ MD1 ;NO, GO MOVE MOV A,C ;MAYBE, OTHER BYTE? SUB E RZ ;YES, RETURN MD1 DCX D ;ELSE MOVE A BYTE DCX H ;BUT FIRST DECREASE LDAX D ;BOTH POINTERS AND MOV M,A ;THEN DO IT JMP MVDOWN ;LOOP BACK ;* POPA POP B ;BC = RETURN ADDR. POP H ;RESTORE LOPVAR, BUT SHLD LOPVAR ;=0 MEANS NO MORE MOV A,H ORA L JZ PP1 ;YEP, GO RETURN POP H ;NOP, RESTORE OTHERS SHLD LOPINC POP H SHLD LOPLMT POP H SHLD LOPLN POP H SHLD LOPPT PP1 PUSH B ;BC = RETURN ADDR. RET ;* PUSHA LXI H,STKLMT ;*** PUSHA *** CALL CHGSGN POP B ;BC=RETURN ADDRESS DAD SP ;IS STACK NEAR THE TOP? JNC QSORRY ;YES, SORRY FOR THAT. LHLD LOPVAR ;ELSE SAVE LOOP VAR.S MOV A,H ;BUT IFF LOPVAR IS 0 ORA L ;THAT WILL BE ALL JZ PU1 LHLD LOPPT ;ELSE, MORE TO SAVE PUSH H LHLD LOPLN PUSH H LHLD LOPLMT PUSH H LHLD LOPINC PUSH H LHLD LOPVAR PU1 PUSH H PUSH B ;BC = RETURN ADDR. RET ;* ;************************************************************** ;* ;* *** OUTC *** & CHKIO *** *! ;* THESE ARE THE ONLY I/O ROUTINES IN TBI. ;* 'OUTC' IS CONTROLLED BY A SOFTWARE SWITCH 'OCSW'. IFF OCSW=0 ;* 'OUTC' WILL JUST RETURN TO THE CALLER. IFF OCSW IS NOT 0, ;* IT WILL OUTPUT THE BYTE IN A. IFF THAT IS A CR, A LF IS ALSO ;* 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 ;* IS CLEARED AND THE INPUT BYTE IS IN A. HOWERER, IFF THE ;* INPUT IS A CONTROL-O, THE 'OCSW' SWITCH IS COMPLIMENTED, AND ;* 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 ;* IOR A OC2 JNZ OC3 ;IT IS ON POP PSW ;IT IS OFF RET ;RESTORE AF AND RETURN OC3 POP A ;GET OLD A BACK PUSH B ;SAVE B ON STACK PUSH D ;AND D PUSH H ;AND H TOO STA OUTCAR ;SAVE CHARACTER MOV E,A ;PUT CHAR. IN E FOR CPM MVI C,2 ;GET CONOUT COMMAND CALL CPM ;CALL CPM AND DO IT LDA OUTCAR ;GET CHAR. BACK CPI 0DH ;WAS IT A 'CR'? JNZ DONE ;NO, DONE MVI E,0AH ;GET LINEFEED MVI C,2 ;AND CONOUT AGAIN CALL CPM ;CALL CPM DONE LDA OUTCAR ;GET CHARACTER BACK IDONE POP H ;GET H BACK POP D ;AND D POP B ;AND B TOO RET ;DONE AT LAST CHKIO PUSH B ;SAVE B ON STACK PUSH D ;AND D PUSH H ;THEN H MVI C,11 ;GET CONSTAT WORD CALL CPM ;CALL THE BDOS ORA A ;SET FLAGS JNZ CI1 ;IF READY GET CHARACTER JMP IDONE ;RESTORE AND RETURN CI1 MVI C,1 ;GET CONIN WORD CALL CPM ;CALL THE BDOS CPI 0FH ;IS IT CONTROL-O? JNZ CI2 ;NO, MORE CHECKING LDA OCSW ;CONTROL-O FLIP OCSW CMA ;ON TO OFF, OFF TO ON STA OCSW ;AND PUT IT BACK JMP CHKIO ;AND GET ANOTHER CHARACTER CI2 CPI 3 ;IS IT CONTROL-C? JNZ IDONE ;RETURN AND RESTORE IF NOT JMP RSTART ;YES, RESTART TBI LSTROM EQU $ ;ALL ABOVE CAN BE ROM OUTIO OUT 0FFH RET WAITIO IN 0FFH XRA H ANA L JZ WAITIO RST 6 INPIO IN 0FFH MOV L,A RET OUTCAR DB 0 ;OUTPUT CHAR. STORAGE OCSW DB 0FFH ;SWITCH FOR OUTPUT CURRNT DW 0 ;POINTS TO CURRENT LINE STKGOS DW 0 ;SAVES SP IN 'GOSUB' VARNXT DW 0 ;TEMPORARY STORAGE STKINP DW 0 ;SAVES SP IN 'INPUT' LOPVAR DW 0 ;'FOR' LOOP SAVE AREA LOPINC DW 0 ;INCREMENT LOPLMT DW 0 ;LIMIT LOPLN DW 0 ;LINE NUMBER LOPPT DW 0 ;TEXT POINTER RANPNT DW START ;RANDOM NUMBER POINTER TXTUNF DW TXTBGN ;->UNFILLED TEXT AREA TXTBGN DS 1 ;TEXT SAVE AREA BEGINS MSG1 DB 7FH,7FH,7FH,'SHERRY BROTHERS TINY BASIC VER. 3.1',0DH INIT MVI A,0FFH STA OCSW ;TURN ON OUTPUT SWITCH MVI A,0CH ;GET FORM FEED RST 2 ;SEND TO CRT PATLOP SUB A ;CLEAR ACCUMULATOR LXI D,MSG1 ;GET INIT MESSAGE CALL PRTSTG ;SEND IT LSTRAM LDA 7 ;GET FBASE FOR TOP STA RSTART+2 DCR A ;DECREMENT FOR OTHER POINTERS STA SS1A+2 ;AND FIX THEM TOO STA TV1A+2 STA ST3A+2 STA ST4A+2 STA IP3A+2 STA SIZEA+2 STA GETLN+3 STA PUSHA+2 LXI H,ST1 ;GET NEW START JUMP SHLD START+1 ;AND FIX IT JMP ST1 ; RESTART TABLE ORG 0A50H RSTBL: XTHL ;*** TSTC OR RST 1 *** RST 5 ;IGNORE BLANKS AND CMP M ;TEST CHARACTER JMP TC1 ;REST OF THIS IS AT TC1 ;* CRLF: EQU 0EH ;EXECUTE TIME LOCATION OF THIS INSTRUCTION. MVI A,0DH ;*** CRLF *** ;* 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