;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;; COPYRIGHT 1977, TINY-C ASSOCIATES ;;;;;;;;;;;;;;;;;; ;;;;;; ALL RIGHTS RESERVED ;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG ($+100H)/100H*100H ;go to round address LCFIX EQU 20H ;maps literals to lower case ;error codes STATERR EQU 1 CURSERR EQU 2 SYMERR EQU 3 RPARERR EQU 5 RANGERR EQU 6 CLASERR EQU 7 SYNXERR EQU 9 LVALERR EQU 14 PUSHERR EQU 16 TMFUERR EQU 17 TMVRERR EQU 18 TMVLERR EQU 19 LINKERR EQU 20 ARGSERR EQU 21 LBRCERR EQU 22 MCERR EQU 24 SYMERRA EQU 26 KILL EQU 99 ;recognition length of symbols VLEN EQU 8 ;where tc exits to. TCEXIT EQU 0000H ;end-of-line character ASCRET EQU 0DH ; ;entry points JMP COLD JMP WARM JMP HOT ;tailoring vector ECHO DB 0 ;zero suppresses char echo INCH JMP 0103h OUTCH JMP 0106h CHRDY JMP 0109h FOPEN JMP 010Ch FREAD JMP 010Fh FWRITE JMP 0112h FCLOSE JMP 0115h USERMC JMP 0118h PRBEGIN NOP NOP RET STBEGIN NOP NOP RET PRDONE NOP NOP RET ;MC tools XMCESET JMP MCESET XTOPTOI JMP TOPTOI XPUSHK JMP PUSHK MCARGS DB 0 ;escape character ESCAPE DB 1BH ;space allocation BSTACK DW 1A00H ESTACK DW -1A80H BFUN DW 1A80H EFUN DW -1B00H BVAR DW 1B00H EVAR DW -2200H BPR DW 2200H EPR DW -4CF0H MSTACK DW 1A00H ;standard cells ERR DW 0 ERRAT DW 0 LEAVE DB 0 BRAKE DB 0 TOP DW 0 NXTVAR DW 0 CURFUN DW 0 CURGLBL DW 0 FNAME DW 0 LNAME DW 0 STCURS DW 0 CURSOR DW 0 PRUSED DW 0 PROGEND DW 0 ;stored negative APPLVL DB 0 ; ;literals BALPHS EQU $ ;beginning of alphabetics XIF DB LCFIX + 'i' DB LCFIX + 'f' DB 0 XELS DB LCFIX + 'e' DB LCFIX + 'l' DB LCFIX + 's' DB LCFIX + 'e' DB 0 XINT DB LCFIX + 'i' DB LCFIX + 'n' DB LCFIX + 't' DB 0 XCHAR DB LCFIX + 'c' DB LCFIX + 'h' DB LCFIX + 'a' DB LCFIX + 'r' DB 0 XWHI DB LCFIX + 'w' DB LCFIX + 'h' DB LCFIX + 'i' DB LCFIX + 'l' DB LCFIX + 'e' DB 0 XRET DB LCFIX + 'r' DB LCFIX + 'e' DB LCFIX + 't' DB LCFIX + 'u' DB LCFIX + 'r' DB LCFIX + 'n' DB 0 XBRK DB LCFIX + 'b' DB LCFIX + 'r' DB LCFIX + 'e' DB LCFIX + 'a' DB LCFIX + 'k' DB 0 XENDL DB LCFIX + 'e' DB LCFIX + 'n' DB LCFIX + 'd' DB LCFIX + 'l' DB LCFIX + 'i' DB LCFIX + 'b' DB LCFIX + 'r' DB LCFIX + 'a' DB LCFIX + 'r' DB LCFIX + 'y' DB 0 XR DB LCFIX + 'r' ;loader 'read' command XG DB LCFIX + 'g' ;'go' command DB 0FFH ;end of alphabetics LB DB '[' DB 0 RB DB ']' DB 0 LPAR DB '(' DB 0 RPAR DB ')' DB 0 COMMA DB ',' DB 0 NEWLINE DB ASCRET DB 0 CMNT DB '/' XSTAR DB '*' DB 0 SEMI DB ';' DB 0 XPCNT DB '%' DB 0 XSLASH DB '/' DB 0 XPLUS DB '+' DB 0 XMINUS DB '-' DB 0 LT DB '<' DB 0 GT DB '>' DB 0 NOTEQ DB '!' DB '=' DB 0 EQEQ DB '=' XEQ DB '=' DB 0 GE DB '>' DB '=' DB 0 LE DB '<' DB '=' DB 0 XNL DB ASCRET DB 0 ;EQ performs an assignment of top into top-1. Top-1 ; must be an lvalue. EQ CALL TOPTOI ;value into DE PUSH D ;stuff to be assigned CALL POPST ;where to assign ORA A JZ EQ2 ;if class>0 set size=2 MVI C,2 EQ2 MOV A,B ;must be lvalue CPI 'L' JNZ EQERR XCHG ;where -> HL POP D ;stuff -> DE MOV M,E ;assign lo byte DCR C ;size-- JZ PUSHK ;call/ret, put result on stack INX H MOV M,D ;hi byte JMP PUSHK ;call/ret, put result on stack EQERR CALL ESET DB LVALERR POP D JMP PUSHK ;skip the assign part ; ;-(BC) -> BC DNEG MOV A,C CMA MOV C,A MOV A,B CMA MOV B,A INX B RET ; ;difference between two top values -> DE, setting Z, CY TOPDIF CALL POPTWO ;hence fall into DSUB. ; ; (DE) - (BC) -> DE DSUB MOV A,E SUB C MOV E,A MOV A,D SBB B MOV D,A ORA E ;Z now set, CY clear MOV A,D RLC ;sign is now in CY RET ; ; (BC) + (DE) -> DE DADD MOV A,C ADD E MOV E,A MOV A,B ADC D MOV D,A ORA E ;Z now set. CY cleared. MOV A,D RLC ;Sign is now in CY, Z not hurt. RET ; ; (BC) * (DE) -> DE DMPY LXI H,0 DM2 MOV A,C ;test lo bit of BC RRC JNC DM3 DAD D ;add multiplier DM3 CALL BCRS ;shift BC right JNZ DM4 ;return if BC is 0 XCHG ;answer -> DE RET DM4 CALL DELS ;shift multiplier left, return JNZ DM2 ; if zero. XCHG RET ; ; shift BC right, setting Z if 0. BCRS XRA A ;zero CY flag MOV A,B RAR MOV B,A MOV A,C RAR ;picks up carry left by hi byte MOV C,A ORA B RET ; shift DE left. Sets z iff (DE)==0. DELS XRA A ;zero CY flag ; rotate DE left, CY -> lo bit RDEL MOV A,E ;lo byte first RAL MOV E,A MOV A,D RAL ;picks up carry left by lo byte MOV D,A ORA E RET ; ; (DE) % (BC) -> DE, quotient in HL. DREM MOV A,D ;sign of result -> stack XRA B PUSH PSW MOV A,D ;make factors positive ORA A CM DENEG MOV A,B ORA A CM DNEG MVI A,16 ;shift count -> stack PUSH PSW XCHG ;numerator -> HL LXI D,0 ;partial remainder -> DE DR2 CALL HLLS ;divide loop. Long left shift CALL RDEL ; DEHL. JZ DR3 CALL DCMP ;test BC <= DE JM DR3 MOV A,L ;set lo bit of L, and subtract ORI 1 ; divisor from partial MOV L,A ; remainder CALL DSUB DR3 POP PSW ;decrement shift count DCR A JZ DR4 PUSH PSW JMP DR2 DR4 POP PSW ;put sign on quotient and rem RP CALL DENEG XCHG CALL DENEG XCHG RET ; ; (DE) / (BC) -> DE DDIV CALL DREM XCHG RET ; ; -(DE) -> DE DENEG MOV A,D CMA MOV D,A MOV A,E CMA MOV E,A INX D RET ; ;double compare (DE) - (BC) changing neither, but ; setting s, cy ; Note that z is not set reliably. DCMP MOV A,E SUB C MOV A,D SBB B RET ; ;HL left shift HLLS DAD H RET ; ;@@@@@@@@ stack tools @@@@@@@@@@ ; ;TOPTOI pops top of stack into DE, converting lvalue ; to actual if necessary. TOPTOI CALL POPST ;class in A, lvalue in B, STA TPCLASS ; size in C, stuff in DE MOV A,B CPI 'A' JZ TT2 XCHG ;fetch data MOV E,M INX H MOV D,M TT2 DCR C ;if size 1 and class 0 return RNZ ; lo byte, with sign propgated LDA TPCLASS ; thru hi byte. ORA A RNZ MOV A,E RLC ;propogate sign into D. SBB A MOV D,A RET TPCLASS DB 0 ; ;pops two from stack, top -> bc, next -> de. POPTWO CALL TOPTOI PUSH D CALL TOPTOI POP B RET ; ;pops the stack into A, B, C, DE. New top in HL. POPST LHLD TOP MOV A,M ;class INX H MOV B,M ;lvalue INX H MOV C,M ;size INX H MOV E,M ;stuff, lo-byte INX H MOV D,M ;stuff, hi-byte PUSH B LXI B,-9 DAD B ;decrement top by 5. POP B SHLD TOP RET ; ;pushes constant 1. PONE LXI D,1 JMP PUSHK ;pushes constant 0. PZERO LXI D,0 ;pushes constant in DE PUSHK XRA A ;class 0 MVI B,'A' ;actual MVI C,2 ;2 byte size ;pushes class (A), lvalue (B), size (C), stuff (DE) ; onto stack. PUSHST LHLD TOP ;add 5 to top. PUSH D LXI D,5 DAD D SHLD TOP XCHG LHLD ESTACK DAD D XCHG ;top -> HL POP D ;restore stuff JC PERR MOV M,A INX H MOV M,B INX H MOV M,C INX H MOV M,E INX H MOV M,D RET PERR CALL ESET DB PUSHERR RET ; ; @@@@@@@@ ESET sets ERR unless one is already set @@@@ ESET LDA ERR XTHL ORA A JZ ES2 INX H XTHL RET ES2 MOV A,M INX H XTHL STA ERR LHLD CURSOR SHLD ERRAT RET ; ;store 0's from (DE) thru (HL) inclusive ZERO MVI B,0 ;store (B) from (DE) thru (HL) inclusive BZAP MOV A,L SUB E MOV A,H SBB D RC MOV M,B DCX H JMP BZAP ; ;print string starting at (HL), terminated by null byte PS MOV A,M ORA A RZ CALL OUTCH INX H JMP PS ; ;@@@@@@@@@ SCAN TOOLS @@@@@@@@@@@@ ; ;LIT is used to match literals. It advances the cursor ; over blanks, then attempts a match with the literal. ; DE points to the literal, which is terminated by a ; null byte. On match, the cursor is advanced ; beyond the matched text, and NZ is set. On no match ; the cursor is not advanced (except over the initial ; blanks), and Z is set. LIT is called often, so some ; attention to speed is given, mainly by using inline ; code for blanks and string matching. LIT LHLD CURSOR MVI A,' ' ;trim blanks LIT2 CMP M JNZ LIT3 INX H JMP LIT2 LIT3 SHLD CURSOR ;capture cursor, in case no mch LIT4 LDAX D ;char from literal ORA A JZ MATCH ;null signals end of literal CMP M ;char from program INX D INX H JZ LIT4 XRA A ;no match, return Zero ORA A RET MATCH SHLD CURSOR ;capture new cursor CMA ;return Not Zero ORA A RET ; ;advances cursor over blanks. Puts cursor in HL. BLANKS LHLD CURSOR MVI A,' ' LOOP CMP M JNZ BLOUT INX H JMP LOOP BLOUT SHLD CURSOR RET ; ;skips over balanced l-r delimiters, (assuming the ;first l delimiter is already matched.) Tests that ;cursor stays within program limits, and sets ERR and ;doesn't advance cursor on violation. SKIP MVI D,1 ;counter SK2 MOV A,M CMP B JZ SKL ;match left delimiter CMP C JNZ SKNEXT DCR D ;match right delimiter JNZ SKNEXT INX H ;all done, bump over last SHLD CURSOR ; matched. STC CMC ;CY off on success RET SKL INR D SKNEXT INX H ;bump HL, test for overflow XCHG ;cursor -> DE PUSH H ;make H safe LHLD PROGEND ;stored negative, so add DAD D POP H XCHG ;now all reg's restored JNC SK2 CALL ESET DB CURSERR STC ;CY set on error RET ; ;tests if (A) is alphanumeric. Plus on yes. ALNUM CPI '0' RM CPI '9'+1 JM YESA ;tests if (A) is alpha. Plus on yes. ALPHA CPI 'A' RM ;not alpha CPI 'Z'+1 JM YESA CPI LCFIX + 'a' RM CPI LCFIX + 'z'+1 JM YESA CMA ;not alpha, this sets Minus. ORA A RET YESA XRA A ;set Plus. RET ; ;matches a variable or function name. Sets FNAME, ; LNAME to first and last chars of the name. Returns ; Not Zero on match, Zero on no match. SYMNAME CALL BLANKS SHLD FNAME MOV A,M CALL ALPHA JM SY3 SY2 INX H ;is a symbol, find its end. MOV A,M CALL ALNUM JP SY2 SHLD CURSOR ;just beyond symbol DCX H SHLD LNAME ;symbol end RET SY3 XRA A ;no symbol, return Z RET ; ;matches 3 kinds of constants, setting FNAME, LNAME as ; in SYMNAME. Sets A to 0 on no match, 1,2,or 3 on mch CONST CALL BLANKS MOV A,M ;first char CPI '+' ;test for number JZ CN2 CPI '-' JZ CN2 CPI '0' JM CN3 CPI '9'+1 JP CN3 CN2 SHLD FNAME ;number, cursor to fname CN4 INX H ;find end MOV A,M CPI '0' JM CN5 CPI '9'+1 JM CN4 ;is a digit, keep going CN5 SHLD CURSOR ;not a digit DCX H SHLD LNAME MVI A,1 ;type 1 constant (integer) RET CN3 CPI '"' ;test for quoted string JNZ CN6 INX H ;quote found SHLD FNAME ;first char of string (quote CN7 MOV A,M ; excluded ORA A ;ended by either null or " JZ CN8 SBI '"' JZ CN8 INX H XCHG ;cursor check LHLD PROGEND DAD D XCHG JNC CN7 JMP CNERR ;cursor overflow CN8 MOV M,A ;end quote found, replace with DCX H ; a null. SHLD LNAME ;last char of string MVI A,2 ;constant of type 2 (char str) ORA A INX H INX H SHLD CURSOR RET CN6 CPI 27H ;test for prime JNZ CN9 INX H SHLD FNAME CN12 MOV A,M ;scan for matching prime CPI 27H JZ CN11 INX H XCHG ;cursor check LHLD PROGEND DAD D XCHG JNC CN12 JMP CNERR CN11 MVI A,3 ;found matching prime ORA A INX H SHLD CURSOR RET CN9 XRA A ;no match RET CNERR CALL ESET DB CURSERR RET ; ;skips over remarks and/or end-of-lines in any order. REM LXI D,NEWLINE CALL LIT JZ RE2 RE3 MOV A,M ;skip linefeeds CPI 0AH JNZ REM INX H SHLD CURSOR JMP REM RE2 LXI D,CMNT CALL LIT RZ MVI B,1 ;comment found, skip its text MVI C,ASCRET CALL SKIP RC ;error check JMP RE3 ; ;HL points to start of digit string. Converts to intger ; leaving result in DE. Uses all digits, even if DE ; overflows. First nondigit stops scan. ATON XCHG ;pointer into DE LXI H,0 ;answer developed here AN2 LDAX D ;next ascii SUI 48 JC AN3 ;test for digit CPI 10 JNC AN3 MOV B,H ;digit, set HL=10*HL+A MOV C,L DAD H DAD H DAD B DAD H MOV C,A MVI B,0 DAD B INX D ;bump pointer JMP AN2 AN3 XCHG ;answer -> DE RET ; ;HL points to beginning of ascii integer, possibly ; signed. Converts to integer and leaves value in DE. AISGN DB 0 ;nonzero for - ATOI XRA A STA AISGN AI6 MOV A,M ;skip blanks CPI ' ' JNZ AI2 INX H JMP AI6 AI2 CPI '-' ;test sign JNZ AI3 STA AISGN ;is - INX H AI3 CPI '+' JNZ AI4 INX H AI4 MOV A,M ;skip more blanks CPI ' ' JNZ AI5 INX H JMP AI4 AI5 CALL ATON ;does the digits LDA AISGN ;magnitude in DE ORA A RZ JMP DENEG ;computes negative and returns ; ;@@@@@@@@@ SYMBOL TOOLS @@@@@@@@@@@ ; ;allocate reference in FUNB for variables of a function NEWFUN LHLD CURFUN LXI D,6 ;bump CURFUN by 6 DAD D SHLD CURFUN XCHG ;test too many active functions LHLD EFUN DAD D XCHG JNC NF2 CALL ESET DB TMFUERR RET NF2 LDA NXTVAR ;init first and last var MOV M,A ;fv lo byte SUI 6+VLEN MOV C,A ;lv lo byte -> C for now LDA NXTVAR+1 INX H MOV M,A ;fv hi byte SBI 0 ;picks up possible carry INX H MOV M,C ;lv lo byte INX H MOV M,A ;lv hi byte LDA PRUSED ;now set up backup pointer INX H MOV M,A ;bu lo byte LDA PRUSED+1 INX H MOV M,A ;bu hi bytv RET ;all done ; ;deallocate variables of last function. FUNDONE LHLD CURFUN MOV A,M STA NXTVAR ;lo byte INX H MOV A,M STA NXTVAR+1 INX H INX H INX H MOV A,M STA PRUSED INX H MOV A,M STA PRUSED+1 LXI D,-11 DAD D ;subtract 5 for above INX's, SHLD CURFUN ; plus 5 more to pop FUNB. RET ; ;allocate a variable. Class in A, size in B, len in DE, ; passed value in HL. CLASS DB 0 ;temps used by newvar OBSIZE DB 0 PASSED DW 0 LEN DW 0 FVAL DW 0 KF DW 0 ; NEWVAR STA CLASS MOV A,B STA OBSIZE SHLD PASSED XCHG SHLD LEN LHLD NXTVAR CALL CANON ;put canonical form of name ; into (NXTVAR). Leaves HL ; pointing to last byte of NAME of VARB. INX H ;-> CLASS in VARB. LDA CLASS MOV M,A INX H ;-> OBJSIZE in VARB. LDA OBSIZE MOV M,A INX H ;-> LEN in VARB (2 bytes). LDA LEN MOV M,A INX H LDA LEN+1 MOV M,A INX H SHLD FVAL ;address where fval will be put LDA CLASS ORA A ;if class is 0, or not a passed JZ NR2 ; arg, then get value space. LHLD PASSED MOV A,L ORA H JNZ NR3 NR2 LHLD PRUSED ;get value space INX H ; starting at PRUSED + 1 SHLD KF ;Put in KF for later use. XCHG LHLD FVAL MOV M,E INX H MOV M,D ;fval part of varb set to LHLD LEN ; prused+1. Now bump prused XCHG ; by obsize*len. LHLD PRUSED LDA OBSIZE DAD D DCR A JZ NR7 DAD D NR7 SHLD PRUSED XCHG ;test if allocation exceeds LHLD EPR ; limits of prog space. DAD D XCHG JNC NR4 CALL ESET ;RAM exceeded DB TMVLERR RET NR4 LHLD KF ;zero the allocated space XCHG LHLD PRUSED CALL ZERO JMP NR5 ;end of space allocation NR3 LHLD FVAL ;Value is passed and is a LDA PASSED ; class > 0. Put value in fval MOV M,A ; part of VARB. Dont allocate INX H ; space. LDA PASSED+1 MOV M,A JMP NR6 NR5 LDA CLASS ;if passed & class is 0 move ORA A ; the passed value into the JNZ NR6 ; allocated space. LHLD PASSED MOV A,H ORA L JZ NR6 XCHG ;passed -> DE LHLD KF MOV M,E ;lo byte of passed value INX H MOV M,D ;hi byte, or junk if only one ; byte passed. Who cares. NR6 LHLD CURFUN ;in FUNB set lvar part to this INX H ; variable. INX H LDA NXTVAR MOV M,A INX H LDA NXTVAR+1 MOV M,A LHLD NXTVAR ;increment NXTVAR LXI D,6+VLEN ; by 6 + vlen DAD D SHLD NXTVAR XCHG ;test if too many variables LHLD EVAR DAD D XCHG LHLD FVAL RNC ;normal return, FVAL in HL. CALL ESET ;VARB exceeded. DB TMVRERR RET ; ;ADDRVAL looks up a symbol pointed to by FNAME,LNAME. ; Returns address in HL, class in A, size in B, and ; length in DE. Sets err if symbol cannot be found. ; Searches 3 areas: ; area 0 locals ; 1 globals ; 2 library symbols NAME DS VLEN ;holds canonical form of name PVAR DW 0 AREA DB 0 SFUN DW 0 LAST DW 0 ; ADDRVAL LHLD CURFUN SHLD SFUN ;search locals first LXI H,NAME CALL CANON XRA A STA AREA ;area 0 AD8 LHLD SFUN ;variable search area MOV E,M INX H MOV D,M ;fvar of search area -> DE INX H MOV C,M INX H MOV B,M ;lvar -> BC XCHG SHLD PVAR ;currently searched variable MOV H,B MOV L,C SHLD LAST ;last to search in this area LHLD PVAR ;begin search loop AD2 LDA LAST ;test for end of loop SUB L LDA LAST+1 SBB H JC AD3 MVI C,VLEN ;number of chars to match LXI D,NAME ;match string address AD4 LDAX D ;(HL already as table entry) CMP M JNZ AD5 ;no match DCR C INX D INX H JNZ AD4 ;next char MOV A,M ;MATCH. HL points to class. INX H MOV B,M ;obsize INX H MOV E,M INX H MOV D,M ;length INX H ORA A ;if class > 0 & class < 'E' JZ AD9 ; then return address of fval CPI 'E' ; part of VARB, which is alrdy RNZ ; in HL. AD9 PUSH D ;otherwise return contents of MOV E,M ; fval part of VARB. INX H MOV D,M XCHG POP D RET AD5 LHLD PVAR ;go to next variable LXI D,VLEN+6 DAD D SHLD PVAR JMP AD2 AD3 LDA AREA ;go to next area ORA A JNZ AD6 LHLD CURGLBL ;second search area, globals AD7 SHLD SFUN INR A STA AREA JMP AD8 AD6 CPI 2 JP ADERR LHLD BFUN ;third area is library, which JMP AD7 ; is at beginning of FUNB. ADERR CALL ESET DB SYMERRA RET ; ;canonicalizes symbol from FNAME to LNAME inclusive, ; putting form with VLEN chars in (HL). OUTNAME DW 0 CANON SHLD OUTNAME MVI A,VLEN ;zero output field MVI B,0 MOV C,B ;zero C for later CA2 MOV M,B DCR A JZ CA3 INX H JMP CA2 CA3 PUSH H ;save pointer to last byte LHLD FNAME ;compute symbols actual length LDA LNAME SUB L INR A CPI VLEN JM CA6 MVI A,VLEN ;A now has number of chars to MOV C,A ; be moved, and C is nonzero CA6 XCHG ; iff act len > VLEN. MOV B,A LHLD OUTNAME ;FNAME -> DE, OUTNAME -> HL CA4 LDAX D ;copy loop MOV M,A DCR B JZ CA5 INX D INX H JMP CA4 CA5 POP H ;pointer to last byte XRA A ORA C ;test if short name RZ XCHG ;long name, put last char in LHLD LNAME ; the canon form. MOV A,M ;last char of name XCHG MOV M,A ;into last pos of outname RET ;ASGN is the expression evaluator,so called because ; the highest form of an expression is an assignment. ; An asgn is a reln or an lvalue = asgn. Note that ; reln can match an lvalue. ;Returns non-zero if valid expression, 0 if invalid. ASGN CALL RELN ;stacked as lvalue if that's ; what it is. LXI D,XEQ ; test for = CALL LIT JZ A2 CALL ASGN LDA ERR ;check for error ORA A CZ EQ ;perform assignment A2 LDA ERR ;return 0 (i.e. no match) if ORA A ; there was an error JZ A3 XRA A RET A3 DCR A ;no error so return non-zero A RET ; ;a RELN is an expr or a comparison of exprs RELN CALL EXPR LXI D,LE ; <= CALL LIT JZ R2 CALL EXPR ;right side CALL TOPDIF ;sets Z,C flags. C set as JZ PONE ; though it were S. Must be JC PONE ; zero or negative for true. JMP PZERO ;These jumps all call/rets. R2 LXI D,GE ; >= CALL LIT JZ R3 CALL EXPR CALL TOPDIF JZ PONE JNC PONE JMP PZERO R3 LXI D,EQEQ ; == CALL LIT JZ R4 CALL EXPR CALL TOPDIF JZ PONE JMP PZERO R4 LXI D,NOTEQ CALL LIT JZ R5 CALL EXPR CALL TOPDIF JNZ PONE JMP PZERO R5 LXI D,GT ; > CALL LIT JZ R6 CALL EXPR CALL TOPDIF JZ PZERO JC PZERO JMP PONE R6 LXI D,LT ; < CALL LIT RZ ; no relational operator CALL EXPR CALL TOPDIF JC PONE JMP PZERO ; ;an EXPR is a term or sum (diff) of terms. EXPR LXI D,XMINUS ; unary - CALL LIT JZ EX2 CALL TERM CALL TOPTOI ;push negative of top back onto MOV A,E CMA MOV E,A MOV A,D CMA MOV D,A INX D CALL PUSHK JMP EX3 EX2 LXI D,XPLUS ;optional unary + CALL LIT CALL TERM ;first term is now stacked. Check for error so far. EX3 LDA ERR ORA A RNZ LXI D,XPLUS ; + CALL LIT JZ EX4 CALL TERM CALL POPTWO ;top two values on stack are ; actualized and put into ; (BC) and (DE). CALL DADD ; (BC)+(DE)->(DE) CALL PUSHK ; sum onto stack. JMP EX3 ;back for more terms EX4 LXI D,XMINUS ; - CALL LIT RZ ;no more terms CALL TERM CALL POPTWO CALL DSUB CALL PUSHK JMP EX3 ;back for more terms. ; ;a term is a factor or a product of factors. TERM CALL FACTOR TE2 LDA ERR ;check for error so far ORA A RNZ LXI D,XSTAR ; * CALL LIT JZ TE3 CALL FACTOR CALL POPTWO CALL DMPY CALL PUSHK JMP TE2 ;back for more factors. TE3 CALL REM ;make sure no /* LXI D,XSLASH ; / CALL LIT JZ TE4 CALL FACTOR CALL POPTWO CALL DDIV CALL PUSHK JMP TE2 TE4 LXI D,XPCNT ; % CALL LIT RZ ;no more factors. CALL FACTOR CALL POPTWO CALL DREM CALL PUSHK JMP TE2 ; ;a FACTOR is a ( asgn ), or a constant, or a variable ; reference, or a function reference. FACTOR LXI D,LPAR ; ( CALL LIT JZ FA2 CALL ASGN LXI D,RPAR ; ) CALL LIT RNZ CALL ESET ;right paren error DB RPARERR RET FA2 CALL CONST ;recognizes 3 types of constant JZ FA5 ; setting A accordingly. CPI 1 JNZ FA3 LHLD FNAME ;type 1: integer. FNAME points CALL ATOI ; to beginning. ATOI converts JMP PUSHK ; it, leaving value in (DE). FA3 CPI 2 JNZ FA4 MVI A,1 ;type 2: char string. Push MVI B,'A' ; class=1, lval='A', size=1, MVI C,1 ; and stuff=address of LHLD FNAME ; beginning of string. XCHG JMP PUSHST FA4 XRA A ;type 3: char constant. Push MVI B,'A' ; class=0, lval='A', size=1, MVI C,1 ; and stuff=actual character. LHLD FNAME MOV E,M JMP PUSHST FA5 CALL SYMNAME ;not a constant, try symbol. JZ FA6 LHLD FNAME ;symbol. Test for special INX H ; symbol MC. First is symbol LDA LNAME ; length exactly 2. CMP L JNZ FA7 LDA LNAME+1 CMP H JNZ FA7 MOV A,M ;length is 2, and (HL)=FNAME. CPI 'C' JNZ FA7 DCX H MOV A,M CPI 'M' JNZ FA7 LXI H,0 JMP ENTER ;causes machine call. FA7 CALL ADDRVAL ;not MC, look up symbol. SHLD FWHERE STA CLASS MOV A,B ;save results of lookup. STA OBSIZE XCHG SHLD LEN MOV A,D ;where is now in DE ORA E JZ FA8 LDA CLASS CPI 'E' ;class E => function entry JZ FA9 LXI D,LPAR ;variable. Test for subscript. CALL LIT JZ FA10 LDA CLASS ;subscripted, class must be > 0 DCR A STA CLASS ;class of element is one less JP FA11 ; than class of array. CALL ESET DB CLASERR RET FA11 LHLD FWHERE ;replace where by two bytes MOV E,M ; referenced by where. INX H MOV D,M PUSH D ;save where, len, class, LHLD LEN ; obsize. PUSH H LHLD CLASS ;(also gets obsize) PUSH H CALL ASGN ;evaluate subscript POP H SHLD CLASS ;restore everything POP H SHLD LEN POP H SHLD FWHERE RZ ;assign error LXI D,RPAR ;skip ) CALL LIT CALL TOPTOI ;subscript value -> DE XCHG SHLD SUBSCR XCHG LHLD LEN MOV A,L DCR A ORA H ;for LEN = 1 skip subscript JZ FA12 ; check. LDA CLASS ORA A JNZ FA12 ;skip for pointers, too. ORA D JM SUBERR ;cant be negative MOV B,H ;len -> BC MOV C,L CALL DSUB JC FA12 ;subscr-len must be negative SUBERR CALL ESET DB RANGERR FA12 LHLD SUBSCR XCHG ;where =+ subscr * obsize LHLD FWHERE LDA OBSIZE FA13 DCR A JM FA14 DAD D JMP FA13 FA14 SHLD FWHERE FA10 LDA OBSIZE ;push class, 'L', obsize, MOV C,A ; stuff=where. LDA CLASS MVI B,'L' LHLD FWHERE XCHG JMP PUSHST ;call/ret FA9 LHLD FWHERE JMP ENTER ;call/ret FA8 CALL ESET ;symbol error DB SYMERR RET FA6 CALL ESET ;cannot recognize factor DB SYNXERR RET ; ;locals used by ASGN, etc. FWHERE DW 0 SUBSCR DW 0 ;SKIPST skips over a (possibly compound) statement, ; including whole nested sets of if-then-elses. ; Assumes balanced [], even within comments. SKIPST CALL REM LXI D,LB ;test for [ CALL LIT JZ SS2 MVI B,'[' MVI C,']' CALL SKIP JMP REM ;and done SS2 LXI D,XIF ;test for if or while CALL LIT JNZ SS6 LXI D,XWHI CALL LIT JZ SS3 SS6 LXI D,LPAR CALL LIT MVI B,'(' MVI C,')' CALL SKIP ;skip over (condition) part CALL SKIPST ;skip then part LXI D,XELS ;test for ELSE CALL LIT CNZ SKIPST ;skip else part JMP REM ;and done SS3 LHLD CURSOR ;simple statement, move cursor SS4 MOV A,M ; past next ; or return. CPI ASCRET JZ SS8 CPI ';' JZ SS5 INX H XCHG ;test cursor overflow LHLD PROGEND DAD D XCHG JNC SS4 JMP REM ;and done SS5 INX H SS8 SHLD CURSOR JMP REM ;and done ; ;VALLOC parses one variable behind INT or CHAR and ; makes allocation and symbol entry. TYPE DB 0 ;'C' or 'I' VPASSED DW 0 ;0 for global or local, two ; byte value if param to fnction ; It turns out a 0 valued parameter gets the same ; treatment as a local. VCLASS DB 0 ;defined in globals section. ALEN DW 0 ;elements in an array. ; VALLOC STA TYPE SHLD VPASSED CALL SYMNAME ;sets FNAME, LNAME around symbl JZ V2 ;error if no symbol. XRA A STA VCLASS ;assume class 0 (not an array) LXI D,LPAR CALL LIT JZ V3 LHLD FNAME ;array, evaluate subscript PUSH H ; expression. Must push FNAME, LHLD LNAME ; LNAME, and class, because PUSH H ; subscripts may invoke LDA VCLASS ; functions which themselves INR A ; allocate variables. PUSH PSW CALL ASGN POP PSW ;restore pushed stuff. STA VCLASS POP H SHLD LNAME POP H SHLD FNAME LDA ERR ;test for error in ASGN ORA A RNZ LXI D,RPAR CALL LIT ;skip ) CALL TOPTOI ;value of subscript + 1 into INX D ; LEN XCHG SHLD ALEN JMP V5 V3 LXI H,1 ;non-subscripted variable SHLD ALEN ; has ALEN 1. V5 LDA TYPE ;object size is 1 of 'C', 2 for MVI B,1 ; 'I' CPI 'C' JZ V7 INR B ;obsize in B V7 LDA VCLASS ;class in A LHLD ALEN ;len in DE. XCHG LHLD VPASSED ;passed in HL JMP NEWVAR ;call/ret, NEWVAR allocates the ; variable V2 CALL ESET DB SYMERR RET ; ;@@@@@@@@@@ tiny - c interpreter @@@@@@@@@@@@ ; ;ST interprets a possibly compound statement ; ST CALL QUIT ;test if program should quit. LDA ERR ORA A RNZ CALL REM ;pass over remarks and/or ; end of line CALL STBEGIN ;bugout for blips, statistics, ; ; etc, user provided. ST2 LHLD CURSOR ;capture cursor SHLD STCURS CALL DECL ;test for declaration JNZ REM LXI D,LB ;test for left bracket CALL LIT JZ TIF CALL REM CMPND LDA ERR ;compound statement. Execute MOV B,A ; each of its inner stmnts. LDA LEAVE ; Exit on error, leave, break, ORA B ; or ] literal. MOV B,A LDA BRAKE ORA B RNZ LXI D,RB ; ] CALL LIT JNZ REM ;and done CALL ST ;recursive call to ST JMP CMPND ;then do next statement. TIF LXI D,XIF ;test for IF CALL LIT JZ TWHI LXI D,LPAR ;skip ( CALL LIT CALL ASGN ;evaluate condition RZ ;return on error LXI D,RPAR ;skip ) CALL LIT CALL TOPTOI ;condition value MOV A,D ORA E JZ IF2 CALL ST ;true, execute conditional LXI D,XELS ;skip else clause if there CALL LIT CNZ SKIPST RET IF2 CALL SKIPST ;false, skip conditional LXI D,XELS ;execute else clause if there CALL LIT CNZ ST RET TWHI LXI D,XWHI ;test for WHILE CALL LIT JZ TSEM LXI D,LPAR ;skip ( CALL LIT CALL ASGN ;condition RZ ;return on error LXI D,RPAR ;skip ) CALL LIT CALL TOPTOI ;condition value MOV A,D ORA E JZ WH2 LHLD STCURS ;true, save STCURS and CURSOR PUSH H LHLD CURSOR PUSH H CALL ST ;execute object of while POP H ;saved cursor into OBJT SHLD OBJT POP H ; and stcurs into AGIN SHLD AGIN LDA BRAKE ;if a BREAK statement caused ORA A ; this return, then set CURSOR JZ WH3 ; to object of the while and LHLD OBJT ; skip over it, and restore SHLD CURSOR ; break. The WHILE is alllll CALL SKIPST ; done. XRA A STA BRAKE RET WH3 LHLD AGIN ;Otherwise, set cursor back to SHLD CURSOR ; beginning of while statement RET ; and return, causing WHILE to ; to be done again. WH2 CALL SKIPST ;If condition is false, skip RET ; the object, and done. TSEM LXI D,SEMI ;test for null statement CALL LIT JNZ REM ;and done TRET LXI D,XRET ;test for RETURN statement CALL LIT JZ TBRK LXI D,SEMI ;if ; or remark push a 0. CALL LIT JNZ TR2 LXI D,XNL CALL LIT JNZ TR2 CALL ASGN ;otherwise push return value JMP TR4 TR2 CALL PZERO TR4 MVI A,1 ;set leave flag STA LEAVE RET TBRK LXI D,XBRK ;test for BREAK CALL LIT JZ TASG MVI A,1 ;set break flag STA BRAKE RET TASG CALL ASGN ;if none of above, must be an JZ STER ; expression, or an error. CALL TOPTOI ;if an expression, discard its ; value. LXI D,SEMI ;skip optional ; CALL LIT JMP REM ;and done STER CALL ESET DB STATERR ;statement error RET OBJT DW 0 ;points to object of while AGIN DW 0 ;points to beginning of while ; ;DECL tests for and interprets declarations DECL LXI D,XCHAR CALL LIT ;test for CHAR JZ TINT CH2 MVI A,'C' LXI H,0 CALL VALLOC LXI D,COMMA CALL LIT JNZ CH2 ;get all vars CH3 LXI D,SEMI ;skip optional ; CALL LIT MVI A,07FH ;set flag to Not Zero ORA A RET TINT LXI D,XINT CALL LIT RZ ;flag is zero IN2 MVI A,'I' LXI H,0 CALL VALLOC LXI D,COMMA CALL LIT JNZ IN2 JMP CH3 ; ;catches interrupts (ESC key) at appl level. QUIT LDA APPLVL ORA A RZ CALL CHRDY RZ MOV B,A ;char keyed in -> B LDA ESCAPE CMP B RNZ CALL INCH ;discard the ESC CALL ESET ;signal the escape DB KILL RET ; ;evaluates arguments of a function. Sets cursor to ; beginning of function's text. Parses its argument ; declarations, giving them values of the parameters. ; executes the function. Determines cause of exit, and ; pushes default 0 return value if needed. Restores ; cursor. NARGS DB 0 ;number of args WHERE DW 0 ;0 for MC, otherwise address of ; function. ARG DW 0 ;pointer into stack to first ; arg. ENTER SHLD WHERE XRA A STA NARGS LHLD TOP LXI D,5 DAD D SHLD ARG LXI D,LPAR ;skip optional ( CALL LIT LXI D,RPAR ;test for no args, several ways CALL LIT JNZ ARGSDNE LHLD CURSOR MOV A,M CPI ']' JZ ARGSDNE CPI ';' JZ ARGSDNE CPI ASCRET JZ ARGSDNE CPI '/' JZ ARGSDNE EN2 LDA ERR ;eval args, first test for err ORA A RNZ LHLD ARG ;save locals PUSH H LHLD WHERE PUSH H LHLD NARGS PUSH H CALL ASGN ;evaluate POP H ;restore locals MOV A,L POP H SHLD WHERE POP H SHLD ARG INR A ;increment NARGS STA NARGS LXI D,COMMA CALL LIT ;comma means more args JNZ EN2 LXI D,RPAR ;optional ) CALL LIT ARGSDNE LDA ERR ORA A RNZ LHLD WHERE ;test for MC MOV A,H ORA L JNZ EN3 LDA NARGS CALL MC RET EN3 LHLD CURSOR ;save current cursor PUSH H LHLD STCURS PUSH H LHLD WHERE ;set cursor to start of fctn SHLD CURSOR CALL NEWFUN ;new layer of value space EN4 CALL REM ;parse arg decls and pass value LXI D,XINT ;works just like DECL, except CALL LIT ; uses SETARG instead of JZ EN5 ; VALLOC. EN6 LHLD ARG MVI B,'I' CALL SETARG LHLD ARG ;bump ARG pointer to next LXI D,5 ; stack layer DAD D SHLD ARG LXI D,COMMA CALL LIT JNZ EN6 LXI D,SEMI CALL LIT JMP EN4 EN5 LXI D,XCHAR CALL LIT JZ EN7 EN8 LHLD ARG MVI B,'C' CALL SETARG LHLD ARG LXI D,5 DAD D SHLD ARG LXI D,COMMA CALL LIT JNZ EN8 LXI D,SEMI CALL LIT JMP EN4 EN7 LHLD TOP ;test correct number of args LXI D,5 DAD D LDA ARG ;should be TOP+5 CMP L JZ EN9 POP D ;set up old cursor for POP H ; the error call SHLD CURSOR PUSH H PUSH D CALL ESET DB ARGSERR EN9 LXI H,NARGS ;pop all args off stack DCR M JM EN11 CALL POPST JMP EN9 EN11 LDA ERR ;if no errors, execute function ORA A CZ ST LDA LEAVE ;push 0 if default leave ORA A CZ PZERO XRA A ;zero LEAVE STA LEAVE POP H ;restore cvrsor SHLD STCURS POP H SHLD CURSOR CALL FUNDONE ;pop layer of value space RET ; ;HL points into stack to an arg. B (used by VALLOC) is ; type. SETARG gets actual value of arg, calls VALLOC ; to allocate local space, which also puts arg value ; into allocated space. SETARG PUSH B MOV B,M ;class INX H MOV A,M ;lvalue INX H MOV C,M ;size INX H MOV E,M ;stuff INX H MOV D,M CPI 'A' ;test for actual JZ SE2 XCHG ;address of datum -> HL MOV E,M INX H MOV D,M SE2 MOV A,C ;if size==1 & class==0 DCR A ORA B JNZ SE3 MOV A,E ; then propogate sign RLC SBB A MOV D,A SE3 POP B ;type -> A MOV A,B XCHG ;passed value -> HL JMP VALLOC ;call/ret, valloc does the rest ; ;scans program and allocates all externals in next fctn ; layer. An "endlibrary" line causes a new fctn layer ; to be opened. LINK CALL NEWFUN LI2 LDA ERR ;check no error ORA A RNZ LHLD CURSOR INX H INX H XCHG LHLD PROGEND DAD D XCHG RC CALL REM ;more text to process, skip LXI D,LB ; remarks. CALL LIT ;test for compound statement. JZ LIDCL MVI B,'[' ;skip compound st. MVI C,']' CALL SKIP JMP LI2 LIDCL CALL DECL ;test for declaration, and JNZ LI2 ; allocate it LXI D,XENDL ;test for endlibrary statement. CALL LIT JZ LISYM CALL NEWFUN JMP LI2 LISYM CALL SYMNAME ;test for symbol JZ LIERR MVI A,'E' ;allocate a variable with MVI B,2 ; class E, size 2, len 1, MVI E,1 ; passed value = cursor. (This MVI D,0 ; is a function entry.) LHLD CURSOR CALL NEWVAR LHLD CURSOR ;advance cursor to beginning of MVI A,'[' ; program body. LI3 CMP M JZ LI4 INX H XCHG LHLD PROGEND DAD D XCHG JNC LI3 CALL ESET DB LBRCERR RET LI4 SHLD CURSOR ;skip body CALL SKIPST JMP LI2 LIERR CALL ESET DB LINKERR RET ; ;move -(bc) bytes from (hl) to (de) MOVE MOV A,M STAX D INX D INX H INR C JNZ MOVE INR B JNZ MOVE RET ;it all starts here!!!!! ;cold start erases system level tc programs, and enters ; the loader. Used to load a tailered or different ; system program. ;warm start does not erase sys level progs, but enters ; the loader so more can be loaded. ;hot start assumes all the loading is done, and immed ; starts up the loaded sys level tc prog. ;Unfortunately, there is no hot start that preserves ; application programs. COLD LHLD MSTACK ;initialize 8080 stack, if need MOV A,H ORA L JZ CL2 SPHL CL2 LXI B,-10 ;copy initial statement LHLD BPR ; PR XCHG LXI H,INST ; into PR CALL MOVE LHLD BPR LXI D,9 DAD D CALL HLNEG SHLD PROGEND CALL LOGO WARM CALL LOADER HOT CALL LOGO LHLD PROGEND CALL HLNEG SHLD PRUSED LHLD BPR SHLD CURSOR LHLD BFUN LXI D,6 DAD D SHLD CURGLBL LXI D,-12 DAD D SHLD CURFUN LHLD BVAR SHLD NXTVAR LHLD BSTACK LXI D,-5 DAD D SHLD TOP XRA A MOV H,A MOV L,A STA ERR SHLD ERRAT STA LEAVE STA BRAKE CALL LINK CALL NEWFUN LHLD BPR SHLD CURSOR CALL PRBEGIN CALL ST ;this executes the system progm CALL PRDONE LXI H,DONEMSG CALL PS LDA ERR ORA A JZ NOERR LHLD ERR XCHG CALL PN MVI A,' ' ; and a space, CALL OUTCH LHLD ERRAT XCHG CALL PN NOERR MVI A,0DH CALL OUTCH JMP WARM DONEMSG DB 0DH DB 0DH DB 'D' DB 'O' DB 'N' DB 'E' DB ' ' DB 0 INST DB '[' DB 'm' DB 'a' DB 'i' DB 'n' DB '(' DB ')' DB ';' DB ']' DB 0 ; LOADER LXI H,BUFF MVI A,'>' CALL OUTCH CALL OUTCH CALL OUTCH D2 CALL INCH MOV B,A LDA ECHO ORA A MOV A,B CNZ OUTCH MOV M,A CPI 7FH ;delete char JZ D3 CPI 0DH ;return JZ DOIT INX H JMP D2 D3 LXI D,-BUFF-1 PUSH H DAD D POP H JNC D2 DCX H JMP D2 DOIT MVI M,0 ;null at command's end LDA BUFF+1 ;ignore period in buff. MOV B,A LDA XR ;the letter r CMP B JZ LOAD MVI A,LCFIX+'x' ; .x is the exit command CMP B JZ TCEXIT LDA XG ;the letter g CMP B RZ ;leaves editor MVI A,'?' ;unrecognized command CALL OUTCH CALL OUTCH CALL OUTCH MVI A,0DH CALL OUTCH JMP LOADER LOAD LXI H,BUFF+3 ;file name LXI D,1 ;read option LXI B,1 ;unit MVI A,1 ;open to read CALL FOPEN JNZ LOADER LHLD PROGEND ;where to load (stored neg) L2 CALL HLNEG LXI B,1 ;unit CALL FREAD ;read one block JNZ L5 ;err or end of file DAD D ;# bytes read in DE MVI M,0 ;just beyond last byte read CALL HLNEG SHLD PROGEND ;points to null byte at end JMP L2 L5 LXI B,1 ;close unit 1 CALL FCLOSE JMP LOADER BUFF DS 40 ; ;Negate HL HLNEG MOV A,H CMA MOV H,A MOV A,L CMA MOV L,A INX H RET ; ;print (DE) as signed integer PN LXI H,BUFF CALL ITOA MVI M,0 ;put null at end LXI H,BUFF JMP PS ;and done ; ;convert (DE) to ascii signed integer ITOA MOV A,D ;test for minus ORA A JP NTOA CALL DENEG ;make positive MVI M,'-' ;output minus INX H ;now fall into NTOA ;convert (DE) to ascii unsigned integer NTOA MOV A,D ORA E ;must be at least one digit, so JNZ NT2 ; test for 0. MVI M,'0' INX H RET NT2 XRA A ;put mark on stack PUSH PSW NT3 LXI B,10 PUSH H CALL DDIV MOV A,L ;remainder -> A POP H ADI '0' PUSH PSW ;ascii digit -> stack MOV A,D ;done if quotient is zero ORA E JNZ NT3 NT4 POP PSW ;top of stack is digit or mark. RZ ;done if mark. MOV M,A ;otherwise digit -> buffer. INX H JMP NT4 ; ;prints the copyright message on the terminal. LOGO LXI H,CPMSG JMP PS CPMSG DB 0CH DB '*' DB '*' DB '*' DB ' ' DB ' ' DB 'T' DB 'I' DB 'N' DB 'Y' DB '-' DB 'C' DB ' ' DB ' ' DB ' ' DB 'V' DB 'E' DB 'R' DB 'S' DB 'I' DB 'O' DB 'N' DB ' ' DB '1' DB '.' DB '0' DB ' ' DB ' ' DB '*' DB '*' DB '*' DB 0DH DB 0AH DB 'C' DB 'O' DB 'P' DB 'Y' DB 'R' DB 'I' DB 'G' DB 'H' DB 'T' DB ' ' DB '1' DB '9' DB '7' DB '7' DB ',' DB ' ' DB 'T' DB ' ' DB 'A' DB ' ' DB 'G' DB 'I' DB 'B' DB 'S' DB 'O' DB 'N' DB 0DH DB 0AH DB 0 ;move the block (DE)...(HL) inclusive (BC) bytes. If ; (BC) is positive, the block is moved up in RAM, ; highest byte first, lowest byte last. If (BC) is ; negative, the block is moved down in RAM, lowest ; byte first. Thus large blocks can be safely moved ; up or down short distances. MOVEBL MOV A,B ORA A JM MOVEDN ORA C RZ MOVEUP SHLD FROMPTR ;hi end of block is fromptr DAD B ;to pointer -> DE XCHG LDA FROMPTR ; - length -> BC CMA ADD L ; - length = MOV C,A ; current HL - fromptr +1 LDA FROMPTR+1 CMA ADC H MOV B,A LHLD FROMPTR MU2 MOV A,M STAX D DCX H DCX D INR C JNZ MU2 INR B JNZ MU2 RET MOVEDN XCHG ;lo end of block is from ptr SHLD FROMPTR DAD B ;to pointer -> HL LDA FROMPTR ; - length -> BC SUB E MOV C,A LDA FROMPTR+1 SBB D MOV B,A DCX B XCHG ;to ptr -> DE LHLD FROMPTR ;from ptr -> HL JMP MOVE FROMPTR DW 0 ; ;scan for the Nth occurance of a character in a block, ; or the end of the block, whichever comes first. The ; block is (DE)..(HL) inclusive. N is (BC) and can be ; 0 to 65k. (A) is the character. On completion, (DE) ; points to the Nth occurance, or to the last byte of ; the block. (BC) is N minus the number of (A) found, ; e.g. 0 if N (A)'s were found. HL is undisturbed. SCANN PUSH PSW ;ch -> stack XCHG ;reverse first and last SC2 MOV A,C ORA B ;test if done JZ SC9 MOV A,E SUB L MOV A,D SBB H JC SC9 POP PSW PUSH PSW CMP M JNZ SC3 DCX B SC3 INX H JMP SC2 SC9 DCX H XCHG POP PSW RET ; ;count the occurances of a character in a block. (A) is ; the character. The block is (DE)..(HL) inclusive. ; The count is returned in (BC). (A) and (DE) are ; unchanged. (HL) is clobbered. COUNTCH LXI B,0 PUSH PSW ;ch -> stack CC2 MOV A,L ;test for end SUB E MOV A,H SBB D JC CC9 POP PSW PUSH PSW CMP M DCX H JNZ CC2 INX B ;count this one JMP CC2 CC9 POP PSW RET ;Machine Call routine to interface to 8080 coded ; routines. Standard routines used by the system ; are coded here, numbers 1 to 11. 12 to 999 are ; reserved. 1000 and up are available to users. MC STA MCARGS ;for checking, CALL TOPTOI ; for MC's that need it. LXI H,-1000 ;test for user MC DAD D JC USERMC MOV A,E ;fctn num -> A CPI 1 JZ MC1 CPI 2 JZ MC2 CPI 3 JZ MC3 CPI 4 JZ MC4 CPI 5 JZ MC5 CPI 6 JZ MC6 CPI 7 JZ MC7 CPI 8 JZ MC8 CPI 9 JZ MC9 CPI 10 JZ MC10 CPI 11 JZ MC11 CPI 12 JZ MC12 CPI 13 JZ MC13 CPI 14 JZ MC14 MCESET CALL ESET DB MCERR RET ; ;put a character to screen MC1 CALL TOPTOI ;char -> A CALL PUSHK ;push it back MOV A,E JMP OUTCH ; ;get a char from keyboard MC2 CALL INCH ;char -> DE MOV B,A ;test for ESC in appl level LDA APPLVL ORA A JZ USEIT LDA ESCAPE CMP B JNZ USEIT CALL ESET DB KILL USEIT LDA ECHO ;test if echo required ORA A MOV A,B CNZ OUTCH MOV E,A XRA A MOV D,A JMP PUSHK ;put char onto stack ; ;file open (r/w, name, fsize, unit) MC3 CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI ;r/w -> A MOV A,E ORA D POP H ;name pointer -> HL POP D ;file size -> DE POP B ;unit -> BC CALL FOPEN LXI D,0 MOV E,A ;push result code JMP PUSHK ; ; read block( where, unit) MC4 CALL TOPTOI PUSH D CALL TOPTOI XCHG ;where -> HL POP B ;unit -> BC CALL FREAD JZ MC4P ;if result code is 0 DE has LXI D,-1 ; byte count to be pushed. MOV E,A ; Otherwise A is an err or eof MC4P JMP PUSHK ; code to be returned negative ; ;write block ( first byte, last byte, unit). Block may ; be any size from 1 to 256. MC5 CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI XCHG ;first -> HL POP D ;last -> DE POP B ;unit -> BC CALL FWRITE LXI D,0 ;push result code MOV E,A JMP PUSHK ; ;close file ( unit ) MC6 CALL TOPTOI MOV C,E ;unit -> BC MOV B,D CALL FCLOSE JMP PZERO ;return a 0 ; ;move a block up or down. Args are first,last,K. If K ; negative, block is moved down |k| bytes, if positive ; then up K bytes. MC7 CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI ;first -> DE POP H ;last POP B ;K CALL MOVEBL JMP PZERO ;return a 0 ; ;count # instances of character CH in a block. Args are ; first,last,CH. MC8 CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI ;first -> DE POP H ;last POP B ;ch -> A MOV A,C CALL COUNTCH MOV E,C ;count -> DE MOV D,B JMP PUSHK ; ;scan for nth occurance of CH in a block. Args are ; first,last,CH,cnt address. Return pointer to nth ; occurance,if it exists, otherwise to last. Also ; cnt is reduced by one for every CH found. MC9 CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI PUSH D CALL TOPTOI ;first -> DE POP H ;last POP B ;ch -> A MOV A,C XTHL MOV C,M ;cnt -> BC INX H MOV B,M DCX H XTHL ;addr of cnt still on stack PUSH D ;first on stack, too CALL SCANN POP H ;make ptr (DE) relative to MOV A,E ; first SUB L MOV E,A MOV A,D SBB H MOV D,A POP H ;BC -> cnt MOV M,C INX H MOV M,B JMP PUSHK ;return pointer to last byte ; ; examined. ; ;trap to moniter 4.0 for debugging. MC10 DB 0FFH ;RST 7 RET ; ;enters an application program, setting up a new ; globals variable level, redefining progend, links ; the program, executes if no error occured, upon ; completion captures a few facts (err, and either ; cursor or errat) and restores old globals level, ; progend, zeros err, pushes a zero as the value of ; this function, and resumes the calling program. MC11 LHLD CURSOR PUSH H LHLD PROGEND PUSH H LHLD PRUSED PUSH H LHLD CURGLBL PUSH H CALL TOPTOI ;appl pr address XCHG PUSH H SHLD CURSOR CALL TOPTOI ;end of appl addr XCHG SHLD PRUSED CALL HLNEG SHLD PROGEND CALL LINK LHLD CURFUN SHLD CURGLBL CALL TOPTOI ;start statement address XCHG SHLD CURSOR CALL NEWFUN CALL TOPTOI ;facts address PUSH D LXI H,APPLVL ;increment appl level INR M PUSH H LDA ERR ;if no err so far, do it!! ORA A JNZ DONE CALL PRBEGIN CALL ST CALL PRDONE DONE POP H ;its done, decrement appl level DCR M CALL FUNDONE ;discard appl locals CALL FUNDONE ; and globals LHLD CURSOR ;set up facts LDA ERR ORA A JZ MCEN2 LHLD ERRAT MCEN2 XCHG ;returned currsor -> DE POP H ;facts -> HL POP B ;appl pr address -> BC MOV A,E ;make returned cursor relative SUB C ; to appl address MOV E,A MOV A,D SBB B MOV D,A LDA ERR MOV M,A ;err -> facts XRA A INX H MOV M,A ;err hi byte -> facts INX H MOV M,E ;cursor -> facts INX H MOV M,D POP H ;curglobal SHLD CURGLBL POP H SHLD PRUSED POP H ;progend SHLD PROGEND POP H ;cursor SHLD CURSOR XRA A ;zero the error STA ERR JMP PZERO ;value of MC11 ; ;test if keyboard char ready, return copy if so,else 0. MC12 CALL CHRDY MVI D,0 MOV E,A JMP PUSHK ; ;print RAM, from and to addresses are given ; nulls are mapped to quotes MC13 CALL TOPTOI PUSH D CALL TOPTOI XCHG ;from -> HL POP D ;to -> DE LOOP13 MOV A,E ;test if done SUB L MOV A,D SBB H JC PZERO ;done MOV A,M ORA A JNZ EC13 MVI A,'"' EC13 CALL OUTCH INX H JMP LOOP13 ; ;print a signed integer MC14 CALL TOPTOI PUSH D CALL PN POP D JMP PUSHK