; Copyright 1990 Bill Boyd; All rights reserved ; ; I am uploading this file to CompuServe so that others may learn from ; it and modify it for their own use. I am not granting permission for ; sale of this program or derivative programs for profit. ; ; This program is adapted from the CompuServe source uploaded by Michael ; Weiblen, CIS 72506,2072. He, in turn, had adapted the FIG-Forth model ; to the Model 100. ; ; The current status of this file: can load from file! ; Contains words OPENF, CLOSEF, GETC, GETLINE, and LOADF for interpreting ; from mass storage. Also examine ERROR to see if it does the ; "right" things. Can we remove abort to BASIC on file not found or I/O ; error? Think more about how KEY works (cannot handle softkeys, etc., ; and doesn't receive ^S or ^C for use as cursor keys). ; ; Revision details: ; 4 Oct 87 Loading from file works! ; 6 Oct 87 Move change of IN from GETLINE to NULL and LOADF ; 11 Oct 87 Add some floating-point; NUMBER doesn't handle E notation ; 21 Oct 87 NUMBER handles E notation; have FLIT for fvars in definitions ; 8 Nov 87 Add F$CODE, PICK; change 1+ and 2+ to code ; 23 Mar 88 Add DEPTH; modify OPENF, GETC, and CLOSEF to use buffer 0 ; 1 Apr 88 Add BPLOT (copies up to 240 bytes of graphics to LCD) ; 30 Apr 88 Add (NEXT); makes address of NEXT available ; 20 May 88 Add MSGARRAY; change MESSAGE to take message from there ; ; Words affected by modifications: ; EMIT removed LF filter for printer echo (think about more!) ; KEY moved code into word, rather than jumping to it ; B/BUF removed altogether ; B/SCR removed altogether ; BLK use as a flag to indicate loading from mass storage ; ^@ (null character) if loading, try to read another line ; (LINE) removed (applied to disk system) ; .LINE removed (applied to disk system) ; SEC removed ; TRACK removed ; USE removed ; PREV removed ; SEC/BLK removed ; NUMBUF removed ; DISK-ERROR removed ; +BUF removed ; UPDATE removed ; EMPTYBUFFERS removed ; BUFFER removed ; BLOCK removed ; T&SCALC removed ; SEC-READ removed ; SEC-WRITE removed ; R/W removed ; LOAD removed ; --> removed ; MENU used call documented in Tech Ref Manual ; LIST removed ; INDEX removed ; TRIAD removed ; " new; puts addr and byte count of string on stack ; (") new; run-time procedure for the new word " ; EOF new variable; tells whether open file has reached end ; OPENF new; opens file (using buffer #0) whose name has addr ; and count on stack; use as " " OPENF ; GETC new; gets character from opened file, returns on stack ; CLOSEF new; closes file opened by OPENF ; GETLINE new; gets line from file into (single) buffer starting at ; FIRST; also sets IN to 0 ; LOADF new; redirects interpretation to file specified; usage: ; " " LOADF ; FLIT new; used in definition when NUMBER detects floating number ; (FNUMBER) new; used in interpret to handle floating-point ; NUMBER modified to interpret floating-point ; INTERPRET modified for floating point ; DEPTH new; gives number of items on stack ; BPLOT new; sends bytes to LCD (for graphics) ; (NEXT) new; returns address of NEXT in interpreter ; MESSAGE modified to obtain message from memory, not disc ; MSGARRAY new; contains error messages ; ; Other changes: ; Initialize RP in startup code ; Changed origin to 50176 ; Allowed space for user dictionary, pad, and stack to MAXRAM ; Have only one buffer, which holds line from mass storage (255 chars) ; ORG 50176 Accum1 equ 0FC18H ;address ROM routines use for current X Accum2 equ 0FC69H ;address ROM routines use for current Y BrkChk equ 729FH ;returns with carry set if Shift-Break CHGET equ 12CBH ;ROM routine to await keyboard char CloseFile equ 4D38H ;ROM routine to close file CursorOff equ 44BAH ;ROM routine to turn off cursor DisableTimeInt equ 765CH ;ROM routine to disable timer interrupt EnableTimeInt equ 743CH ;ROM routine to enable timer interrupt false equ 0 ;logic false FileBufPtr equ 0FC8CH ;pointer to current file buffer Ftemp equ 0FC58h ;used for temporary math operations L0000 equ 0 ;restart addr of 8085 LCD equ 4B44H ;routine to send char in A to LCD LCDDriverTable0 equ 7643H ;address of ROM table of LCD selects LCDDriverTable1 equ 764DH ;LCD selects for rows 4-7 of screen MENUaddr equ 5797H ;routine to display file menu ParseName equ 4C21H ;routine to parse file name PRINTR equ 6D3FH ;routine to send char in A to printer ROMDIV equ 2DC7H ;ROM routine to divide two floating point ROMMINUS equ 2B69H ;ROM routine to subtract two floatings ROMMULT equ 2CFFH ;ROM routine to subtract two floating point ROMPLUS equ 2B78H ;ROM routine to add two floating point SelectLCD equ 753BH ;ROM routine to select an LCD driver (via table) true equ 1 ;logic true VarLength equ 0FB65H ;loc used by ROM routines WriteLCDBytes equ 74F6H ;ROM routine to send E bytes to LCD START NOP JMP ColdStart NOP JMP WarmStart dw 0101H ;CPU parameter dw 0E04H ;revision parameter LA00C dw TASKnfa BSaddr dw 8 ;backspace character dw RInit ;initial user area ; ; Initialization constants; do not change order, delete, or insert any!!! LA012 dw TOSInit ;initial top of stack (S0) dw RInit ;initial top of return stack (R0) dw TOSInit ;terminal input buffer (TIB) dw 31 ;name field width (WIDTH) dw 0 ;initial WARNING value (WARNING) dw FENCEInit ;initial FENCE (FENCE) dw FENCEInit ;cold-start value for DP (DP) dw VOCLINKInit ;(VOC-LINK) dw 5 ;believe it or not, 5B320H is 8080 when printed dw 0B320H ; in base 36! USERSpace DW RInit RP DW RInit ;---------------------------------- ; The main code of the interpreter follows PUSHDEHL ;put DE, then HL, on stack, then NEXT PUSH D PUSHHL ;put HL on stack, then NEXT PUSH H NEXT LDAX B ;get number pointed to by IP INX B MOV L,A LDAX B INX B ;move IP to next word MOV H,A ;HL has value that IP pointed to (W) (this is cfa) GetCodeAddr MOV E,M ;get value pointed to by W INX H MOV D,M XCHG ;put value pointed to by W in HL (DE = W + 1) PCHL ;start execution there ;---------------------------------- LITnfa DB 80H+LITlfa-LITnfa-1 DB 'LI' DB 'T'+80H LITlfa ; ( -- n ) Put next number from def onto stack DW 0 ;last word LIT DW LITpfa ;code LITpfa LDAX B ;get number pointed to by IP INX B MOV L,A LDAX B INX B ;bump IP by 2 MOV H,A JMP PUSHHL ;push number retrieved ;---------------------------------- EXECUTEnfa DB 80H+EXECUTElfa-EXECUTEnfa-1 DB 'EXECUT' DB 'E'+80H EXECUTElfa DW LITnfa EXECUTE ; ( cfa -- ) Execute definition at cfa DW EXECUTEpfa ;code EXECUTEpfa POP H ;get number on stack JMP GetCodeAddr ;---------------------------------- BRANCHnfa DB 80H+BRANCH-BRANCHnfa-3 DB 'BRANC' DB 'H'+80H DW EXECUTEnfa BRANCH ; ( -- ) Branch number of bytes given by next word DW BRANCHpfa ;code BRANCHpfa MOV H,B ;get IP into HL MOV L,C MOV E,M ;get offset there into DE INX H MOV D,M DCX H DAD D ;add offset to IP MOV C,L ;make that new IP MOV B,H JMP NEXT ;---------------------------------- ZBRANCHnfa DB 87H DB '0BRANC' DB 'H'+80H DW BRANCHnfa ZBRANCH ; ( f -- ) Branch if f is 0, offset in next word DW ZBRANCHpfa ;code ZBRANCHpfa POP H ;get top of stack MOV A,L ORA H ;if it is 0, branch JZ BRANCHpfa BUMP ;else bump IP by 2 INX B INX B JMP NEXT ;---------------------------------- XLOOPnfa DB 86H DB '(LOOP' DB ')'+80H DW ZBRANCHnfa XLOOP ; ( -- ) Run-time code for LOOP. DW XLOOPpfa ;code XLOOPpfa LXI D,1 ;increment by 1 XLOOPAddDE LHLD RP ;HL points to R stack MOV A,M ;add increment to index ADD E MOV M,A MOV E,A INX H MOV A,M ADC D MOV M,A INX H INR D DCR D MOV D,A ;index also in DE JM DownTo ;if increment >= 0 then MOV A,E ;..subtract final from index SUB M MOV A,D INX H SBB M JMP TestFinal DownTo ;else (increment<0) MOV A,M ;..subtract index from final SUB E INX H MOV A,M SBB D TestFinal JM BRANCHpfa ;if result<0 then do loop again INX H ;else SHLD RP ;..drop two values from stack INX B ;..skip over offset value INX B JMP NEXT ;---------------------------------- XPLOOPnfa DB 87H DB '(+LOOP' DB ')'+80H DW XLOOPnfa XPLOOP ; ( n -- ) Run-time code for +LOOP DW XPLOOPpfa ;code XPLOOPpfa POP D ;get increment into DE JMP XLOOPAddDE ;---------------------------------- XDOnfa DB 84H DB '(DO' DB ')'+80H DW XPLOOPnfa XDO ; ( limit i0 -- ) Run-time procedure for DO DW XDOpfa XDOpfa LHLD RP ;reserve room for two words on R stack DCX H DCX H DCX H DCX H SHLD RP POP D ;transfer top of stack to top of R stack MOV M,E INX H MOV M,D POP D ;transfer top of stack to one below on R stack INX H MOV M,E INX H MOV M,D JMP NEXT ;---------------------------------- Infa DB 81H DB 'I'+80H DW XDOnfa I ; ( -- n ) Copy top of R stack to data stack DW Ipfa Ipfa LHLD RP ;copy value on top of R stack MOV E,M INX H MOV D,M PUSH D JMP NEXT ;---------------------------------- DIGITnfa DB 85H DB 'DIGI' DB 'T'+80H DW Infa DIGIT ; ( c n1 -- n2 tf | ff ) Converts base-n char to value DW DIGITpfa ;code DIGITpfa POP H ;get base POP D ;get character MOV A,E SUI '0' JM DIGITBad ;bad if char too small CPI 10 JM DIGITHaveVal ;if result >= 10 then SUI 07 ;..subtract offset from '9' to 'A' CPI 10 ;..bad if char too small JM DIGITBad DIGITHaveVal CMP L JP DIGITBad ;bad if value larger than base MOV E,A ;result in DE LXI H,true ;true flag in HL JMP PUSHDEHL DIGITBad MOV L,H ;false flag in HL (bad input) JMP PUSHHL ;---------------------------------- XFINDnfa DB 86H DB '(FIND' DB ')'+80H DW DIGITnfa XFIND ; ( addr1 addr2 -- pfa b tf | ff ) Search dictionary, ; for counted word at addr1, starting at addr2. DW XFINDpfa XFINDpfa POP D ;DE has first nfa to check XFINDCheck POP H ;HL has addr of name to match PUSH H LDAX D ;compare character counts XRA M ANI 3FH ; (only last 6 bits count) JNZ XFINDFindLastCh XFINDNextChar ;repeat (until mismatch or last char) INX H ;..move to next char INX D LDAX D ;..compare chars XRA M ADD A JNZ XFINDBadChar JNC XFINDNextChar ;until high bit indicates last char LXI H,5 ;advance to PFA DAD D XTHL ;put onto stack, removing addr of name to match XFINDBackUp ;back up until DE has NFA DCX D LDAX D ORA A JP XFINDBackUp MOV E,A ;DE has character count MVI D,00H LXI H,true ;HL has TRUE JMP PUSHDEHL XFINDBadChar JC XFINDGetLFA ;if not on last char of name then XFINDFindLastCh ;repeat (until char has bit 7 set) INX D ;..get next char LDAX D ORA A JP XFINDFindLastCh ;until char has bit 7 set XFINDGetLFA ;on last char of name here INX D ;DE = LFA XCHG MOV E,M ;get address there into DE INX H MOV D,M MOV A,D ;if address not zero then ORA E JNZ XFINDCheck ;..check next word in dictionary POP H ;else (last word) remove top of stack LXI H,0 ;..return false flag JMP PUSHHL ;---------------------------------- ENCLOSEnfa DB 87H DB 'ENCLOS' DB 'E'+80H DW XFINDnfa ENCLOSE ; ( addr c -- addr n1 n2 n3 ) Determine byte offsets ; to first non-c char, first following c char, and next DW ENCLOSEpfa ENCLOSEpfa POP D ;DE has enclosing char POP H ;HL ptr to string begin PUSH H ;return start addr to stack MOV A,E LXI D,-1 ;char count := -1 DCX H ;point one before begin ENCLOSE2 ;repeat (until non-"blank" or end) INX H ;..ptr to next char INX D ;..count := count + 1 CMP M ;until reach non-"blank" JZ ENCLOSE2 PUSH D ;put count onto stack PUSH PSW ;save enclosing char MOV A,M ANA A ;if (premature) end of line JNZ ENCLOSE3 POP PSW ;..retrieve enclosing char INX D PUSH D ;..n2 := n1 + 1 DCX D PUSH D ;..n3 := n1 JMP NEXT ;..end of word ENCLOSE3 ;else (not end of line) repeat POP PSW ;..retrieve enclosing char in A INX H ;..move to next char INX D ;..count it CMP M ;exit if enclosing char JZ ENCLOSE4 PUSH PSW ;..save enclosing char MOV A,M ANA A ;..if end of line JNZ ENCLOSE3 POP PSW PUSH D ;....n2 := loc of end of line PUSH D ;....n3 := n2 JMP NEXT ;....done with word ENCLOSE4 ;here if trailing enclosing char found PUSH D ;n2 := pointer to trailing char INX D PUSH D ;n3 := n2 + 1 (may be a new word) JMP NEXT ;---------------------------------- EMITnfa DB 84H DB 'EMI' DB 'T'+80H DW ENCLOSEnfa EMIT ; ( c -- ) Send char c to display DW DOCOL DW EMITSUB DW ONE ;(This doesn't really look correct; I think system DW OUT ; memory should be read and result copied) DW PLSTORE DW SEMIS ;---------------------------------- EMITSUB ;this (unnamed) codeword does work of EMIT DW EMITSUBpfa EMITSUBpfa POP H ;get char to emit into HL PUSH B ;save IP on stack MOV A,L CALL EMITA ;display char in A POP B ;retrieve IP from stack JMP NEXT PrintFlag dw 0 EMITA call LCD ;send char in A to LCD mov e,a lda PrintFlag ora a rz ;if PrintFlag<>0 then mov a,e jmp printr ;..send to line printer also ;---------------------------------- KEYnfa DB 83H DB 'KE' DB 'Y'+80H DW EMITnfa KEY ; ( -- c ) Waits, then returns char of key pressed DW KEYpfa KEYpfa CALL CHGET ;repeat get char from keyboard CPI 03H JZ KEYpfa ;until (char<>^C) and CPI 'P'-40H ;(control-P toggles echoing to printer) JNZ LB468 ; (char<>^P) LXI H,PrintFlag MOV A,M XRI 01H MOV M,A JMP KEYpfa LB468 MOV L,A ;return key on stack MVI H,00H JMP PUSHHL ;---------------------------------- QTERMINALnfa DB 89H DB '?TERMINA' DB 'L'+80H DW KEYnfa QTERMINAL ; ( -- f ) If break key held, f is TRUE, else FALSE DW QTERMINALpfa QTERMINALpfa CALL BrkChk ;return with C flag if Shift-Break LXI H,false ;result is false JNC LB44E INR L ;unless Shift-Break LB44E JMP PUSHHL ;---------------------------------- CRnfa DB 82H DB 'C' DB 'R'+80H DW QTERMINALnfa CR ; ( -- ) Transmit CR/LF to display dw DOCOL dw LIT dw 0DH dw EMIT ;send CR to display dw LIT dw 0AH dw EMIT dw SEMIS ;send LF to display ;---------------------------------- CMOVEnfa DB 85H DB 'CMOV' DB 'E'+80H DW CRnfa CMOVE ; ( from to count -- ) Copy (unsigned) count bytes. DW CMOVEpfa CMOVEpfa MOV L,C ;save IP in HL MOV H,B POP B ;count in BC POP D ;destination in DE XTHL ;IP to top of stack JMP CMOVE3 CMOVE2 MOV A,M ;..transfer a byte INX H ;.. and update pointers STAX D INX D DCX B CMOVE3 MOV A,B ;while count<>0 ORA C JNZ CMOVE2 POP B ;get IP back JMP NEXT ;---------------------------------- USTARnfa DB 82H DB 'U' DB 0AAH ;'*'+80H DW CMOVEnfa USTAR ; ( u1 u2 -- ud ) Unsigned multiply DW USTARpfa USTARpfa POP D ;get operands into DE POP H ; and HL PUSH B ;save IP on stack MOV B,H ;save high byte in B MOV A,L ;mult low byte by DE CALL ATimesDE PUSH H ;save low word of result MOV H,A ;mult high byte by DE, MOV A,B MOV B,H ; saving high byte of result in B CALL ATimesDE POP D ;get low word of 1st result MOV C,D ;put middle byte in C DAD B ;add BC to A and HL ACI 00H MOV D,L ;low word in DE, MOV L,H ; high word in HL MOV H,A POP B ;retrieve IP JMP PUSHDEHL ATimesDE ;mult A * DE LXI H,0 ;HL := low word of result MVI C,08H ;count := 8 ATimesDE2 ;repeat (until C = 0) DAD H ;..shift A and HL left RAL JNC ATimesDE3 ;..if high bit of A then DAD D ;....A and HL := A and HL + DE ACI 00H ATimesDE3 DCR C ;..count := count - 1 JNZ ATimesDE2 ;..until count=0 RET ;---------------------------------- USLASHnfa DB 82H DB 'U' DB '/'+80H DW USTARnfa USLASH ; ( ud u1 -- uremainder uquotient ) Unsigned divide DW USLASHpfa USLASHpfa LXI H,4 DAD SP ;HL := SP + 4 MOV E,M ;DE := [SP+4] (low word of dividend) MOV M,C ;[SP+4] := IP INX H MOV D,M MOV M,B POP B ;BC := TOS (divisor) POP H ;HL := next on stack (high word) MOV A,L ;set flags on HL-BC SUB C MOV A,H SBB B JC USLASH2 ;if no carry then LXI H,0FFFFH ;..leave FFFF,FFFF on stack (overflow) LXI D,0FFFFH JMP USLASH7 USLASH2 MVI A,10H ;count := 16 USLASH3 ;shift dividend left one bit DAD H RAL ;..store carry in A, bit 0 XCHG DAD H JNC USLASH4 INX D ANA A ;..clear carry USLASH4 XCHG ;..HL high word, DE low word RAR ;..restore A, carry from shift back PUSH PSW ;..save count JNC USLASH5 ;..if carry from shift, then MOV A,L ;....HL := HL - BC SUB C MOV L,A MOV A,H SBB B MOV H,A JMP USLASH6 USLASH5 ;..else (no carry from shift) MOV A,L ;....HL := HL - BC SUB C MOV L,A MOV A,H SBB B MOV H,A JNC USLASH6 ;....if carry resulted then DAD B ;......undo subtraction DCX D ;......subtract 1 from result (undone next) USLASH6 INX D ;..add 1 to result POP PSW DCR A ;..count := count - 1 JNZ USLASH3 ;until count=0 USLASH7 POP B ;pop IP back into BC xchg jmp PUSHDEHL ;push remainder, then result ;---------------------------------- ANDnfa DB 83H DB 'AN' DB 'D'+80H DW USLASHnfa AND ;( n1 n2 -- n3 ) Bitwise AND DW ANDpfa ANDpfa POP D POP H MOV A,E ANA L MOV L,A MOV A,D ANA H MOV H,A JMP PUSHHL ;---------------------------------- ORnfa DB 82H DB 'O' DB 'R'+80H DW ANDnfa OR ; ( n1 n2 -- n3 ) Bitwise OR DW ORpfa ORpfa POP D POP H MOV A,E ORA L MOV L,A MOV A,D ORA H MOV H,A JMP PUSHHL ;---------------------------------- XORnfa DB 83H DB 'XO' DB 'R'+80H DW ORnfa XOR ; ( n1 n2 -- n3 ) Bitwise exclusive-OR DW XORpfa XORpfa POP D POP H MOV A,E XRA L MOV L,A MOV A,D XRA H MOV H,A JMP PUSHHL ;---------------------------------- SHLnfa db 83H db 'SH' db 'L'+80h dw XORnfa SHL ; ( n1 n2 -- n3 ) n1 is shifted left n2 positions ; n2 ranges from 0 to 15 dw SHLpfa SHLpfa pop d ;get byte count into DE mov a,e ;convert count to number from 0 to 15 ani 0fh mov e,a pop h ;get number to shift inr e SHL2 ;while count>0 dcr e ;..count := count - 1 jz SHL3 dad h ;..shift number left jmp SHL2 SHL3 jmp PUSHHL ;---------------------------------- SHRnfa db 83h db 'SH' db 'R'+80h dw SHLnfa SHR ; ( n1 n2 -- n3 ) Shifts n1 right by (n2 mod 16) bits dw SHRpfa SHRpfa pop d ;get n2 into DE mov a,e ani 0fh ;get count = (n2 mod 16) into E mov e,a pop h ;get n1 into HL inr e SHR2 dcr e jz SHR3 xra a ;clear carry mov a,h ;shift 16-bit number right 1 bit rar mov h,a mov a,l rar mov l,a jmp SHR2 SHR3 jmp PUSHHL ;---------------------------------- SPATnfa DB 83H DB 'SP' DB '@'+80H DW SHRnfa SPAT ;( -- n ) Return value of stack pointer (before) DW SPATpfa SPATpfa LXI H,0 DAD SP JMP PUSHHL ;---------------------------------- SPSTOREnfa DB 83H DB 'SP' DB '!'+80H DW SPATnfa SPSTORE ; ( -- ) Initialize stack pointer from S0 DW SPSTOREpfa SPSTOREpfa LHLD USERSpace LXI D,6 DAD D MOV E,M INX H MOV D,M XCHG SPHL JMP NEXT ;---------------------------------- RPATnfa DB 83H DB 'RP' DB '@'+80H DW SPSTOREnfa ; ( -- n ) Current value of RP DW RPATpfa RPATpfa LHLD RP JMP PUSHHL ;---------------------------------- RPSTOREnfa DB 83H DB 'RP' DB '!'+80H DW RPATnfa RPSTORE ; ( -- ) Initialize RP from R0 DW RPSTOREpfa RPSTOREpfa LHLD USERSpace LXI D,8 DAD D MOV E,M INX H MOV D,M XCHG SHLD RP JMP NEXT ;---------------------------------- SEMISnfa DB 82H DB ';' DB 'S'+80H DW RPSTOREnfa SEMIS ; ( -- ) End of word definition DW SEMISpfa SEMISpfa LHLD RP MOV C,M ;load IP from RP stack INX H MOV B,M INX H SHLD RP ;decrement RP by two JMP NEXT ;---------------------------------- LEAVEnfa DB 85H DB 'LEAV' DB 'E'+80H DW SEMISnfa LEAVE DW LEAVEpfa LEAVEpfa ; ( -- ) Set loop limit to I to terminate loop LHLD RP MOV E,M INX H MOV D,M INX H MOV M,E INX H MOV M,D JMP NEXT ;---------------------------------- TORnfa DB 82H DB '>' DB 'R'+80H DW LEAVEnfa TOR ; ( n -- ) Move word to R stack DW TORpfa TORpfa POP D LHLD RP ;reserve space on R stack DCX H DCX H SHLD RP MOV M,E ;put number there INX H MOV M,D JMP NEXT ;---------------------------------- FROMRnfa DB 82H DB 'R' DB '>'+80H DW TORnfa FROMR ; ( -- n ) Retrieve number from R stack DW FROMRpfa FROMRpfa LHLD RP MOV E,M INX H MOV D,M INX H SHLD RP PUSH D JMP NEXT ;---------------------------------- Rnfa DB 81H DB 'R'+80H DW FROMRnfa R ; ( -- n ) Copy word from R stack DW Ipfa ;same as I ;---------------------------------- ZEQUnfa DB 82H DB '0' DB '='+80H DW Rnfa ZEQU ; ( n -- f ) Returns f TRUE if n=0 DW ZEQUpfa ZEQUpfa POP H MOV A,L ORA H LXI H,false JNZ ZEQU2 INX H ZEQU2 JMP PUSHHL ;---------------------------------- ZLESSnfa DB 82H DB '0' DB '<'+80H DW ZEQUnfa ZLESS ; ( n -- f ) Returns f true if n has sign bit set DW ZLESSpfa ZLESSpfa POP H DAD H LXI H,false JNC ZLESS2 INX H ZLESS2 JMP PUSHHL ;---------------------------------- PLUSnfa DB 81H DB '+'+80H DW ZLESSnfa PLUS ; ( n1 n2 -- n3 ) Add two numbers DW PLUSpfa PLUSpfa POP D POP H DAD D JMP PUSHHL ;---------------------------------- DPLUSnfa DB 82H DB 'D' DB '+'+80H DW PLUSnfa DPLUS ; ( d1 d2 -- d3 ) Add two double numbers DW DPLUSpfa DPLUSpfa LXI H,6 DAD SP MOV E,M ;DE := low word of d1 MOV M,C ;IP saved on stack there INX H MOV D,M MOV M,B POP B ;BC := high word of d2 POP H ;HL := low word of d2 DAD D ;add the two low words XCHG ;put result in DE POP H ;HL := high word of d1 jnc DPLUS2 ;if carry set then inx b ;..increment number in BC DPLUS2 dad b ;add two high words POP B ;pop IP into BC again JMP PUSHDEHL ;push result; interpret next ;---------------------------------- CHSnfa DB 85H DB 'MINU' DB 'S'+80H DW DPLUSnfa CHS ; ( n1 -- -n1 ) Forth calls it "MINUS" (changes sign) DW CHSpfa CHSpfa POP H ;get n1 from stack MOV A,L ;complement by complementing low byte, ... CMA MOV L,A MOV A,H ;...then high byte CMA MOV H,A INX H ;add 1 to get negative JMP PUSHHL ;---------------------------------- DCHSnfa DB 86H DB 'DMINU' DB 'S'+80H DW CHSnfa DCHS ; ( d -- -d ) Forth calls it "DMINUS" DW DCHSpfa DCHSpfa POP H ;get high word into HL POP D ;get low word into DE SUB A SUB E ;subtract lowest byte from 0 MOV E,A MVI A,00H ;subtract successive bytes from 0 with borrow SBB D MOV D,A MVI A,00H SBB L MOV L,A MVI A,00H SBB H MOV H,A JMP PUSHDEHL ;---------------------------------- OVERnfa DB 84H DB 'OVE' DB 'R'+80H DW DCHSnfa OVER ; ( n1 n2 -- n1 n2 n1 ) Copy number below TOS DW OVERpfa OVERpfa POP D POP H PUSH H JMP PUSHDEHL ;---------------------------------- DROPnfa DB 84H DB 'DRO' DB 'P'+80H DW OVERnfa DROP ; ( n -- ) Remove word from stack DW DROPpfa DROPpfa POP H JMP NEXT ;---------------------------------- SWAPnfa DB 84H DB 'SWA' DB 'P'+80H DW DROPnfa SWAP ; ( n1 n2 -- n2 n1 ) DW SWAPpfa SWAPpfa POP H XTHL JMP PUSHHL ;---------------------------------- DUPnfa DB 83H DB 'DU' DB 'P'+80H DW SWAPnfa DUP ; ( n -- n n ) DW DUPpfa DUPpfa POP H PUSH H JMP PUSHHL ;---------------------------------- TWODUPnfa DB 84H DB '2DU' DB 'P'+80H DW DUPnfa TWODUP ; ( d -- d d ) Duplicate 32-bit number DW TWODUPpfa TWODUPpfa POP H POP D PUSH D PUSH H JMP PUSHDEHL ;---------------------------------- PLSTOREnfa DB 82H DB '+' DB '!'+80H DW TWODUPnfa PLSTORE ; ( n addr -- ) Add n to number at addr DW PLSTOREpfa PLSTOREpfa POP H ;HL has address POP D ;DE has number to add MOV A,M ;add low byte ADD E MOV M,A INX H MOV A,M ;add high byte with carry ADC D MOV M,A JMP NEXT ;---------------------------------- TOGGLEnfa DB 86H DB 'TOGGL' DB 'E'+80H DW PLSTOREnfa TOGGLE ; ( addr n -- ) Byte at addr XOR'ed with n (byte) DW TOGGLEpfa TOGGLEpfa POP D POP H MOV A,M XRA E MOV M,A JMP NEXT ;---------------------------------- ATnfa DB 81H DB '@'+80H DW TOGGLEnfa AT ; ( addr -- n ) Retrieve word at addr DW ATpfa ATpfa POP H MOV E,M INX H MOV D,M PUSH D JMP NEXT ;---------------------------------- CATnfa DB 82H DB 'C' DB '@'+80H DW ATnfa CAT ; ( addr -- b ) Returns byte at addr DW CATpfa CATpfa POP H MOV L,M MVI H,00H JMP PUSHHL ;---------------------------------- TWOATnfa DB 82H DB '2' DB '@'+80H DW CATnfa TWOAT ; ( addr -- d ) Get double at addr DW TWOATpfa TWOATpfa POP H ;get address into HL inx h inx h MOV E,M ;notice low word from higher address! INX H MOV D,M PUSH D dcx h dcx h dcx h MOV E,M INX H MOV D,M PUSH D JMP NEXT ;---------------------------------- STOREnfa DB 81H DB '!'+80H DW TWOATnfa STORE ;( n addr -- ) Store n at addr DW STOREpfa STOREpfa POP H POP D MOV M,E INX H MOV M,D JMP NEXT ;---------------------------------- CSTOREnfa DB 82H DB 'C' DB '!'+80H DW STOREnfa CSTORE ; ( b addr -- ) Store byte at addr DW CSTOREpfa CSTOREpfa POP H POP D MOV M,E JMP NEXT ;---------------------------------- TWOSTOREnfa DB 82H DB '2' DB '!'+80H DW CSTOREnfa ; ( d addr -- ) Store double at addr DW TWOSTORE TWOSTORE POP H ;HL gets address POP D ;store low word at address MOV M,E INX H MOV M,D INX H POP D ;store high word at addr+2 MOV M,E INX H MOV M,D JMP NEXT ;---------------------------------- COLONnfa DB 0C1H ;notice: IMMEDIATE DB ':'+80H DW TWOSTOREnfa ; ( -- ) Begin word definition DW DOCOL DW QEXEC ;Issue error if already compiling DW STORECSP ;Save SP for later error check DW CURRENT ;Make CONTEXT vocabulary same as CURRENT DW AT DW CONTEXT DW STORE DW CREATE ;Save word in definition DW RBRACKET ;Change to compilation state DW DOSEMICODE DOCOL ;run-time code executed for colon-definition LHLD RP ;put IP on R stack DCX H MOV M,B DCX H MOV M,C SHLD RP INX D ;set IP to point to next word in definition MOV C,E MOV B,D JMP NEXT ;start interpreting there ;---------------------------------- SEMInfa DB 0C1H ;note: IMMEDIATE DB 0BBH ;';'+80H DW COLONnfa ; ( -- ) End word definition DW DOCOL DW QCSP ;Make sure stack depth hasn't changed since ':' DW COMPILE ;Place cfa for ;S at end of definition DW SEMIS DW SMUDGE ;Change state of smudge bit to activate DW LBRACKET ;Change state to not compiling DW SEMIS ;---------------------------------- NOOPnfa DB 84H DB 'NOO' DB 'P'+80H DW SEMInfa NOOP ; ( -- ) Why do we need a NOOP? DW DOCOL DW SEMIS ;---------------------------------- CONSTANTnfa DB 88H DB 'CONSTAN' DB 'T'+80H DW NOOPnfa CONSTANT ; ( n -- ) Place a constant definition in dictionary DW DOCOL DW CREATE ;Place name in dictionary DW SMUDGE ;Make it visible DW COMMA ;Store value in dictionary DW DOSEMICODE DOCONST ;run-time code for ',' INX D ;point to next word in definition XCHG MOV E,M ;get word there INX H MOV D,M PUSH D ;push it onto stack JMP NEXT ;---------------------------------- VARIABLEnfa DB 88H DB 'VARIABL' DB 'E'+80H DW CONSTANTnfa ; ( n -- ) Set up a variable in dictionary DW DOCOL ;Storage for constant and variable are identical DW CONSTANT DW DOSEMICODE DOVARIABLE ;Run-time code for variable returns addr, not value INX D PUSH D JMP NEXT ;---------------------------------- USERnfa DB 84H DB 'USE' DB 'R'+80H DW VARIABLEnfa ;( n -- ) Storage for user and constant are identical DW DOCOL DW CONSTANT DW DOSEMICODE DOUSER ;run-time code for USER INX D ;DE points to next word in definition XCHG MOV E,M ;get BYTE there (this is offset into USER space) MVI D,00H LHLD USERSpace DAD D ;return address within user space JMP PUSHHL ;---------------------------------- ZEROnfa DB 81H DB '0'+80H DW USERnfa ZERO ; ( -- n ) Returns n, which is 0 DW DOCONST DW 0 ;---------------------------------- ONEnfa DB 81H DB '1'+80H DW ZEROnfa ONE ; ( -- n ) Returns n, which is 1 DW DOCONST DW 1 ;---------------------------------- TWOnfa DB 81H DB '2'+80H DW ONEnfa TWO ; ( -- n ) Returns n, which is 2 DW DOCONST DW 2 ;---------------------------------- THREEnfa DB 81H DB '3'+80H DW TWOnfa THREE ; ( -- n ) Returns n, which is 3 DW DOCONST DW 3 ;---------------------------------- DONEXTnfa DB 86H DB '(NEXT',')'+80H DW THREEnfa DONEXT ; ( -- adr ) Returns address of NEXT, for assembler DW DOCONST DW NEXT ;---------------------------------- BLnfa DB 82H DB 'B' DB 'L'+80H DW DONEXTnfa BL ; ( -- c ) Returns c, which represents space char DW DOCONST DW ' ' ;---------------------------------- CPERLnfa DB 83H DB 'C/' DB 'L'+80H DW BLnfa CPERL ; ( -- n ) Returns n, the number of chars per line DW DOCONST DW 64 ;!!!Shouldn't this be 40 for Model 100?!!! ;---------------------------------- FIRSTnfa DB 85H DB 'FIRS' DB 'T'+80H DW CPERLnfa FIRST ; ( -- n ) Returns the addr of first byte of file buf dw DOCONST dw FIRSTstart ;---------------------------------- LIMITnfa DB 85H DB 'LIMI' DB 'T'+80H DW FIRSTnfa LIMIT ; ( -- n ) Returns first addr not reserved by Forth DW DOCONST DW LIMITaddr ;---------------------------------- PLORIGINnfa DB 87H DB '+ORIGI' DB 'N'+80H DW LIMITnfa PLORIGIN ; ( n1 -- addr ) Adds n1 to origin parameter address DW DOCOL DW LIT DW START dw plus dw semis ;---------------------------------- S0nfa DB 82H DB 'S' DB '0'+80H DW PLORIGINnfa S0 ; ( -- addr ) User var with init stack pointer value DW DOUSER DB 06H ;---------------------------------- R0nfa DB 82H DB 'R' DB '0'+80H DW S0nfa ; ( -- addr ) User var with init return pointer value DW DOUSER DB 08H ;---------------------------------- TIBnfa DB 83H DB 'TI' DB 'B'+80H DW R0nfa TIB ; ( -- addr ) User var for terminal input buffer addr DW DOUSER DB 0AH ;---------------------------------- WIDTHnfa DB 85H DB 'WIDT' DB 'H'+80H DW TIBnfa WIDTH ; ( -- addr ) User var for maximum word width DW DOUSER DB 0CH ;---------------------------------- WARNINGnfa ;!!!Think about for RAM4TH!!! DB 87H DB 'WARNIN' DB 'G'+80H DW WIDTHnfa WARNING ; ( -- addr ) User var for action on error (0=no disk) DW DOUSER DB 0EH ;---------------------------------- FENCEnfa DB 85H DB 'FENC' DB 'E'+80H DW WARNINGnfa FENCE ; ( -- addr ) User var with lowest addr FORGETting OK DW DOUSER DB 10H ;---------------------------------- DPnfa DB 82H DB 'D' DB 'P'+80H DW FENCEnfa DP ; ( -- addr ) User var, ptr to next free loc in dict DW DOUSER DB 12H ;---------------------------------- VOCLINKnfa DB 88H DB 'VOC-LIN' DB 'K'+80H DW DPnfa VOCLINK ; ( -- addr ) User var, addr of a field in the ; definition of the most recently created vocabulary. DW DOUSER DB 14H ;---------------------------------- BLKnfa ;!!!Probably changes for RAM4TH!!! DB 83H DB 'BL' DB 'K'+80H DW VOCLINKnfa BLK ; ( -- addr ) User var for block number interpreted DW DOUSER DB 16H ;---------------------------------- INnfa DB 82H DB 'I' DB 'N'+80H DW BLKnfa IN ; ( -- addr ) User var, byte offset within text stream DW DOUSER DB 18H ;---------------------------------- OUTnfa ;!!!Needs work for usefulness (copy system loc)!!! DB 83H DB 'OU' DB 'T'+80H DW INnfa OUT ; ( -- addr ) User var for cursor position DW DOUSER DB 1AH ;---------------------------------- SCRnfa ;!!!Probably changes for RAM4TH!!! DB 83H DB 'SC' DB 'R'+80H DW OUTnfa SCR ; ( -- addr ) User var, screen referenced by LIST DW DOUSER DB 1CH ;---------------------------------- OFFSETnfa ;!!!Probably changes for RAM4TH!!! DB 86H DB 'OFFSE' DB 'T'+80H DW SCRnfa OFFSET ; ( -- addr ) User var for block offset to disk drives DW DOUSER DB 1EH ;---------------------------------- CONTEXTnfa DB 87H DB 'CONTEX' DB 'T'+80H DW OFFSETnfa CONTEXT ; ( -- addr ) User var, ptr to search vocabulary DW DOUSER DB 20H ;---------------------------------- CURRENTnfa DB 87H DB 'CURREN' DB 'T'+80H DW CONTEXTnfa CURRENT ; ( -- addr ) User var, ptr to install vocabulary DW DOUSER DB 22H ;---------------------------------- STATEnfa DB 85H DB 'STAT' DB 'E'+80H DW CURRENTnfa STATE ; ( -- addr ) User var for state, 0=executing DW DOUSER DB 24H ;---------------------------------- BASEnfa DB 84H DB 'BAS' DB 'E'+80H DW STATEnfa BASE ; ( -- addr ) User var for current number base DW DOUSER DB 26H ;---------------------------------- DPLnfa DB 83H DB 'DP' DB 'L'+80H DW BASEnfa DPL ; ( -- addr ) User var, # of digits right of decimal DW DOUSER DB 28H ;---------------------------------- FLDnfa DB 83H DB 'FL' DB 'D'+80H DW DPLnfa ; ( -- addr ) User var for control of field width DW DOUSER DB 2AH ;---------------------------------- CSPnfa DB 83H DB 'CS' DB 'P'+80H DW FLDnfa CSP ; ( -- addr ) User var for compilation stack pointer DW DOUSER DB 2CH ;---------------------------------- RNUMBERnfa DB 82H DB 'R' DB '#'+80H DW CSPnfa ; ( -- addr ) User var for editing cursor (unused) DW DOUSER DB 2EH ;---------------------------------- HLDnfa DB 83H DB 'HL' DB 'D'+80H DW RNUMBERnfa HLD ; ( -- addr ) User var, used in numeric conversion DW DOUSER DB 30H ;'0' ;---------------------------------- ONEPLnfa DB 82H DB '1' DB '+'+80H DW HLDnfa ONEPL ; ( n1 -- n1+1 ) Add 1 to number on stack DW ONEPLpfa ONEPLpfa pop h inx h jmp PUSHHL ;---------------------------------- TWOPLnfa DB 82H DB '2' DB '+'+80H DW ONEPLnfa TWOPL ; ( n1 -- n1+2 ) Add 2 to number on stack DW TWOPLpfa TWOPLpfa pop h inx h inx h jmp PUSHHL ;---------------------------------- HEREnfa DB 84H DB 'HER' DB 'E'+80H DW TWOPLnfa HERE ; ( -- addr ) Return next available addr in dict DW DOCOL DW DP DW AT DW SEMIS ;---------------------------------- ALLOTnfa DB 85H DB 'ALLO' DB 'T'+80H DW HEREnfa ALLOT ; ( n -- ) Allocate (reserve) n bytes in dictionary DW DOCOL DW DP DW PLSTORE DW SEMIS ;---------------------------------- COMMAnfa DB 81H DB ','+80H DW ALLOTnfa COMMA ; ( n -- ) Store n in dictionary, allocate 2 bytes DW DOCOL DW HERE DW STORE DW TWO DW ALLOT DW SEMIS ;---------------------------------- CCOMMAnfa DB 82H DB 'C' DB ','+80H DW COMMAnfa ; ( b -- ) Store byte b in dictionary, allocate byte DW DOCOL DW HERE DW CSTORE DW ONE DW ALLOT DW SEMIS ;---------------------------------- HLMinusDE ;subroutine to subtract DE from HL MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A RET ;---------------------------------- MINUSnfa DB 81H DB '-'+80H DW CCOMMAnfa MINUS ; ( n1 n2 -- n1-n2 ) Subtract two numbers on stack DW MINUSpfa MINUSpfa POP D POP H CALL HLMinusDE JMP PUSHHL ;---------------------------------- EQUnfa DB 81H DB '='+80H DW MINUSnfa EQU ; ( n1 n2 -- f ) Flag f TRUE if n1=n2, else f FALSE DW DOCOL DW MINUS DW ZEQU DW SEMIS ;---------------------------------- LESSnfa DB 81H DB '<'+80H DW EQUnfa LESS ; ( n1 n2 -- f ) Flag f TRUE if n1= 0, then DCR H JM LESS3 LXI H,false ;..return a false flag JMP PUSHHL LESS3 ;else (number in HL is < 0) LXI H,true ;..return a true flag JMP PUSHHL ;---------------------------------- ULESSnfa DB 82H DB 'U' DB '<'+80H DW LESSnfa ULESS ; ( u1 u2 -- f ) Flag f TRUE if u1'+80H DW ULESSnfa GREATER ; ( n1 n2 -- f ) Flag f TRUE if u1>u2, else FALSE DW DOCOL DW SWAP DW LESS DW SEMIS ;---------------------------------- ROTnfa DB 83H DB 'RO' DB 'T'+80H DW GREATERnfa ROT ; ( n1 n2 n3 -- n2 n3 n1 ) Rearrange stack as shown DW ROTpfa ROTpfa POP D POP H XTHL JMP PUSHDEHL ;---------------------------------- SPACEnfa DB 85H DB 'SPAC' DB 'E'+80H DW ROTnfa SPACE ; ( -- ) Send blank character to display DW DOCOL DW BL DW EMIT DW SEMIS ;---------------------------------- NZDUPnfa DB 84H DB '-DU' DB 'P'+80H DW SPACEnfa NZDUP ; ( n1 -- n1 | n1 n1 ) Duplicate if n1<>0 DW DOCOL DW DUP DW ZBRANCH DW NZDUP2-$ DW DUP NZDUP2 DW SEMIS ;---------------------------------- TRAVERSEnfa DB 88H DB 'TRAVERS' DB 'E'+80H DW NZDUPnfa TRAVERSE ; ( addr1 +1 -- addr2 ) Scan + for byte w/bit7 ; ( addr1 -1 -- addr2 ) Scan - for byte w/bit7 DW DOCOL DW SWAP TRAVERSE2 DW OVER DW PLUS ;Compute addr of next byte ( +/-1 nextaddr ) DW LIT DW 7FH DW OVER DW CAT ; ( +/-1 nextaddr 07FH b ) DW LESS DW ZBRANCH ;repeat until b>07FH DW TRAVERSE2-$ DW SWAP ; ( nextaddr +/-1 ) DW DROP DW SEMIS ;---------------------------------- LATESTnfa DB 86H DB 'LATES' DB 'T'+80H DW TRAVERSEnfa LATEST ; ( -- nfa ) Leave nfa of topmost word in CURRENT voc DW DOCOL DW CURRENT DW AT ;get pointer to vocabulary DW AT DW SEMIS ;---------------------------------- LFAnfa DB 83H DB 'LF' DB 'A'+80H DW LATESTnfa LFA ; ( pfa -- lfa ) Convert pfa to lfa DW DOCOL DW LIT DW 4 DW MINUS DW SEMIS ;---------------------------------- CFAnfa DB 83H DB 'CF' DB 'A'+80H DW LFAnfa CFA ; ( pfa -- cfa ) Convert pfa to cfa DW DOCOL DW TWO DW MINUS DW SEMIS ;---------------------------------- NFAnfa DB 83H DB 'NF' DB 'A'+80H DW CFAnfa NFA ; ( pfa -- nfa ) Convert parameter addr to name addr DW DOCOL DW LIT DW 5 DW MINUS DW LIT DW -1 DW TRAVERSE DW SEMIS ;---------------------------------- PFAnfa DB 83H DB 'PF' DB 'A'+80H DW NFAnfa PFA ; ( nfa -- pfa ) Convert name addr to parameter addr DW DOCOL DW ONE DW TRAVERSE DW LIT DW 5 DW PLUS DW SEMIS ;---------------------------------- STORECSPnfa DB 84H DB '!CS' DB 'P'+80H DW PFAnfa STORECSP ; ( -- ) Store current stack pointer in CSP DW DOCOL DW SPAT DW CSP DW STORE DW SEMIS ;---------------------------------- QERRORnfa DB 86H DB '?ERRO' DB 'R'+80H DW STORECSPnfa QERROR ; ( f n -- ) If f TRUE, issue error message #n DW DOCOL DW SWAP DW ZBRANCH DW QERROR2-$ DW ERROR DW BRANCH DW QERROR3-$ QERROR2 DW DROP QERROR3 DW SEMIS ;---------------------------------- QCOMPnfa DB 85H DB '?COM' DB 'P'+80H DW QERRORnfa QCOMP ; ( -- ) If not compiling, issue error #17 DW DOCOL DW STATE DW AT DW ZEQU DW LIT DW 17 DW QERROR DW SEMIS ;---------------------------------- QEXECnfa DB 85H DB '?EXE' DB 'C'+80H DW QCOMPnfa QEXEC ; ( -- ) If compiling, issue error message #18 DW DOCOL DW STATE DW AT DW LIT DW 18 DW QERROR DW SEMIS ;---------------------------------- QPAIRnfa DB 86H DB '?PAIR' DB 'S'+80H DW QEXECnfa QPAIR ; ( n1 n2 -- ) If n1<>n2, issue error message #19 DW DOCOL DW MINUS DW LIT DW 19 DW QERROR DW SEMIS ;---------------------------------- QCSPnfa DB 84H DB '?CS' DB 'P'+80H DW QPAIRnfa QCSP ; ( -- ) If stack pointer <> one in CSP, error #20 DW DOCOL DW SPAT DW CSP DW AT DW MINUS DW LIT DW 20 DW QERROR DW SEMIS ;---------------------------------- QLOADINGnfa DB 88H DB '?LOADIN' DB 'G'+80H DW QCSPnfa QLOADING ; ( -- ) If not loading from disk, error #22 DW DOCOL DW BLK DW AT DW ZEQU DW LIT DW 22 DW QERROR DW SEMIS ;---------------------------------- COMPILEnfa ; (Used in immediate word in defining part) DB 87H DB 'COMPIL' DB 'E'+80H DW QLOADINGnfa COMPILE ; ( -- ) Cfa of next definition word put into dict DW DOCOL DW QCOMP ;check that compiling DW FROMR ;get address of next word in definition DW DUP DW TWOPL DW TOR ;skip next word in definition DW AT DW COMMA ;store next word in definition in new definition DW SEMIS ;---------------------------------- LBRACKETnfa ;note...IMMEDIATE DB 0C1H DB '['+80H DW COMPILEnfa LBRACKET ; ( -- ) Changes STATE to execution state DW DOCOL DW ZERO DW STATE DW STORE DW SEMIS ;---------------------------------- RBRACKETnfa DB 81H DB ']'+80H DW LBRACKETnfa RBRACKET ; ( -- ) Changes STATE to compiling state DW DOCOL DW LIT DW 0C0H ;0C0H used so immediate words are executed DW STATE DW STORE DW SEMIS ;---------------------------------- SMUDGEnfa DB 86H DB 'SMUDG' DB 'E'+80H DW RBRACKETnfa SMUDGE ; ( -- ) Toggle SMUDGE bit of latest definition DW DOCOL DW LATEST DW LIT DW 020H DW TOGGLE DW SEMIS ;---------------------------------- HEXnfa DB 83H DB 'HE' DB 'X'+80H DW SMUDGEnfa ; ( -- ) Change base to hexadecimal DW DOCOL DW LIT DW 16 DW BASE DW STORE DW SEMIS ;---------------------------------- DECIMALnfa DB 87H DB 'DECIMA' DB 'L'+80H DW HEXnfa DECIMAL ; ( -- ) Change base to decimal DW DOCOL DW LIT DW 10 DW BASE DW STORE DW SEMIS ;---------------------------------- DOSEMICODEnfa DB 87H DB '(;CODE' DB ')'+80H DW DECIMALnfa DOSEMICODE ; ( -- ) Runtime procedure for ;CODE DW DOCOL DW FROMR ;get address of calling word (abort its interpreting) DW LATEST DW PFA DW CFA DW STORE ;store addr in its code addr DW SEMIS ;---------------------------------- SEMICODEnfa DB 0C5H ;IMMEDIATE DB ';COD' DB 'E'+80H DW DOSEMICODEnfa ; ( -- ) Compile (;CODE), and start assembling DW DOCOL DW QCSP ;check that stack is clean DW COMPILE ;(;CODE) goes into definition DW DOSEMICODE DW LBRACKET ;change STATE to execution state DW NOOP DW SEMIS ;---------------------------------- LESSBUILDSnfa DB 87H DB ''+80H DW LESSBUILDSnfa DOESGREAT ; ( -- ) Define execution action of word DW DOCOL DW FROMR ;get pointer to cfa of next word in definition DW LATEST DW PFA DW STORE ;store that pointer at pfa of latest definition DW DOSEMICODE DODOESGREAT ;run-time action for DOES> LHLD RP ;put current IP on R stack DCX H MOV M,B DCX H MOV M,C SHLD RP INX D ;move DE to pfa XCHG MOV C,M ;get IP from that address INX H MOV B,M INX H ;put next addr onto stack JMP PUSHHL ;---------------------------------- COUNTnfa DB 85H DB 'COUN' DB 'T'+80H DW DOESGREATnfa COUNT ; ( addr1 -- addr1+1 b ) DW DOCOL DW DUP DW ONEPL DW SWAP DW CAT DW SEMIS ;---------------------------------- TYPEnfa DB 84H DB 'TYP' DB 'E'+80H DW COUNTnfa TYPE ; ( addr u -- ) Emit u chars, starting at addr DW DOCOL DW NZDUP ;if count<>0 then DW ZBRANCH DW TYPE3-$ DW OVER ;..compute termination address DW PLUS DW SWAP ;..for I := addr to (termination addr - 1) DW XDO TYPE2 DW I DW CAT ;....get char at addr I DW LIT DW 07FH DW AND DW EMIT ;....emit it DW XLOOP ;..end for DW TYPE2-$ DW BRANCH DW TYPE4-$ TYPE3 ;else (count was 0) DW DROP ;..drop count TYPE4 DW SEMIS ;---------------------------------- MINUSTRAILINGnfa DB 89H DB '-TRAILIN' DB 'G'+80H DW TYPEnfa MINUSTRAILING ; ( addr n1 -- addr n2 ) Deletes trailing spaces DW DOCOL DW DUP DW ZERO DW XDO ;for I:=0 to n1-1 do ( addr n ) MINUSTRAILING2 DW TWODUP DW PLUS DW ONE DW MINUS DW CAT ;..get char at addr+i-1 ( addr n b ) DW BL DW MINUS ;..if char<>blank, then DW ZBRANCH DW MINUSTRAILING3-$ DW LEAVE ;....leave loop DW BRANCH DW MINUSTRAILING4-$ MINUSTRAILING3 ;..else (char=blank) DW ONE DW MINUS ;..decrement n ( addr n ) MINUSTRAILING4 DW XLOOP ;next I DW MINUSTRAILING2-$ DW SEMIS ;---------------------------------- DODOTQUOTEnfa DB 84H DB '(."' DB ')'+80H DW MINUSTRAILINGnfa DODOTQUOTE ; ( -- ) Run-time procedure for ." DW DOCOL DW R ;get pointer to next loc in definition DW COUNT ; ( addr u ) DW DUP DW ONEPL ; ( addr u u+1 ) DW FROMR DW PLUS DW TOR ;adjust number of top of R stack ( addr u ) DW TYPE ;display string from definition DW SEMIS ;---------------------------------- DOTQUOTEnfa DB 0C2H ;IMMEDIATE DB '.' DB '"'+80H DW DODOTQUOTEnfa ; ( -- ) Type string following, terminated by " DW DOCOL DW LIT DW 34 ;Quote character DW STATE DW AT ;if compiling, then ( c ) DW ZBRANCH DW DOTQUOTE2-$ DW COMPILE ;..put (.") into definition DW DODOTQUOTE DW WORD ;..copy into dictionary until " DW HERE DW CAT DW ONEPL DW ALLOT ;..allot number of bytes needed DW BRANCH DW DOTQUOTE3-$ DOTQUOTE2 ;else (executing) DW WORD ;..copy into dictionary until " DW HERE DW COUNT DW TYPE ;..copy it to display (dictionary space NOT allotted) DOTQUOTE3 DW SEMIS ;---------------------------------- EXPECTnfa DB 86H DB 'EXPEC' DB 'T'+80H DW DOTQUOTEnfa EXPECT ; ( addr count -- ) Copy keys to addr until eoln DW DOCOL DW OVER DW PLUS ; ( addr addr+count ) DW OVER ; ( addr addr+count addr ) DW XDO ;for I:=addr to addr+count-1 do ( addr ) EXPECT2 DW KEY DW DUP DW LIT DW BSaddr-START DW PLORIGIN DW AT DW EQU ;.. ( addr key f ) DW ZBRANCH ;..if key=backspace then DW EXPECT5-$ DW DROP ;.... ( addr ) DW DUP DW I DW EQU ;....True if addr=R value ( addr f ) DW DUP ;.... ( addr f f ) DW FROMR ;.... ( addr f f i ) DW TWO DW MINUS DW PLUS ;.... ( addr f f+i-2 ) DW TOR ;.... ( addr f ) DW ZBRANCH ;....if addr=R value then DW EXPECT3-$ DW LIT ;...... ( addr 7 ) DW 7 DW BRANCH DW EXPECT4-$ EXPECT3 ;....else (addr<>R value) DW LIT DW 8 ;...... ( addr 8 ) EXPECT4 DW BRANCH DW 40 EXPECT5 ;..else (not backspace) ( addr key ) DW DUP DW LIT DW 13 DW EQU ;..flag true if CR ( addr key f ) DW ZBRANCH ;..if CR then DW EXPECT6-$ DW LEAVE ;....leave loop DW DROP DW BL DW ZERO ;....( addr bl 0 ) DW BRANCH DW EXPECT7-$ EXPECT6 ;..else (not BS or CR) DW DUP ;.... ( addr key key ) EXPECT7 ;..endif DW I DW CSTORE ;..store at I address ( addr key ) DW ZERO DW I ;.. ( addr key 0 i ) DW ONEPL DW STORE ;..put 0 at next addr ( addr key ) DW EMIT ;..display character ( addr ) DW XLOOP ;next I DW EXPECT2-$ DW DROP ;drop address DW SEMIS ;---------------------------------- QUERYnfa DB 85H DB 'QUER' DB 'Y'+80H DW EXPECTnfa QUERY ; ( -- ) Take in up to 80 chars from keyboard to TIB DW DOCOL DW TIB DW AT DW LIT DW 80 DW EXPECT DW ZERO DW IN DW STORE DW SEMIS ;---------------------------------- NULLnfa ;Changed for RAM4TH DB 0C1H ;IMMEDIATE ( 1 char long ) DB 80H ;char is null char ( chr(0) ) DW QUERYnfa ; ( -- ) Word which terminates interpret at eoln DW DOCOL dw BLK dw AT dw ZBRANCH ;if loading from mass storage then dw NULL3-$ dw EOF dw AT dw ZBRANCH ;..if at eof then dw NULL2-$ dw CLOSEF ;....close file dw FROMR ;....drop return addr (; will lead to its caller) dw DROP dw BRANCH dw NULL4-$ NULL2 ;..else (not at eof) dw GETLINE ;....get a new line from mass storage dw ZERO ;....reset pointer IN to beginning of buffer dw IN dw STORE dw BRANCH dw NULL4-$ NULL3 ;else (interpreting from keyboard) DW FROMR ;drop return addr (; will lead to its caller) DW DROP NULL4 ;endif DW SEMIS ;---------------------------------- FILLnfa DB 84H DB 'FIL' DB 'L'+80H DW NULLnfa FILL ; ( addr count b -- ) Fill count bytes at addr with b DW FILLpfa FILLpfa MOV L,C ;move IP into HL MOV H,B POP D ;get byte into DE POP B ;get count into BC XTHL ;!!!why not leave addr in HL, so can MOV M,E?!!! XCHG ;get addr into DE, byte into L, IP on stack FILL2 ;while count<>0 do MOV A,B ORA C JZ FILL3 MOV A,L STAX D ;..store byte at address INX D ;..increment address DCX B ;..decrement count JMP FILL2 FILL3 ;endwhile POP B ;retrieve IP into BC JMP NEXT ;---------------------------------- ERASEnfa DB 85H DB 'ERAS' DB 'E'+80H DW FILLnfa ERASE ; ( addr count -- ) Fill count bytes at addr with 0 DW DOCOL DW ZERO DW FILL DW SEMIS ;---------------------------------- BLANKSnfa DB 86H DB 'BLANK' DB 'S'+80H DW ERASEnfa BLANKS ; ( addr count -- ) Fill count bytes @ addr with space DW DOCOL DW BL DW FILL DW SEMIS ;---------------------------------- HOLDnfa DB 84H DB 'HOL' DB 'D'+80H DW BLANKSnfa HOLD ; ( c -- ) Used after <# to place c in numeric string DW DOCOL DW LIT DW -1 DW HLD DW PLSTORE ;decrement HLD address DW HLD DW AT DW CSTORE ;store character c there DW SEMIS ;---------------------------------- PADnfa DB 83H DB 'PA' DB 'D'+80H DW HOLDnfa PAD ; ( -- addr ) Address of text output buffer DW DOCOL DW HERE DW LIT DW 68 DW PLUS DW SEMIS ;---------------------------------- WORDnfa DB 84H DB 'WOR' DB 'D'+80H DW PADnfa WORD ; ( c -- ) Temporarily accumulate into dict until c DW DOCOL dw BLK dw AT dw ZBRANCH dw WORD2-$ dw FIRST dw BRANCH dw WORD3-$ WORD2 DW TIB ;leave addr of TIB ( c addr ) DW AT WORD3 DW IN DW AT DW PLUS ;add value of IN to addr ( c addr ) DW SWAP DW ENCLOSE ;find limits of word ( addr n1 n2 n3 ) DW HERE DW LIT DW 34 DW BLANKS ;fill dictionary with 34 spaces (1 for count, 31 name) DW IN DW PLSTORE ;update IN for next word ( addr n1 n2 ) DW OVER DW MINUS ;compute length of word ( addr n1 n2-n1 ) DW TOR ;make a temporary copy on R stack DW R DW HERE DW CSTORE ;put count into dictionary ( addr n1 ) DW PLUS ;compute addr of first char after left trim ( addr ) DW HERE DW ONEPL ;compute dest addr ( from dest ) DW FROMR ;retrieve number of bytes to copy DW CMOVE ;copy them DW SEMIS ;---------------------------------- FLITnfa db 84H db 'FLI' db 'T'+80H dw WORDnfa FLIT ; ( -- ) Lift float stack, copy num in def into X dw DOCOL dw FENTER ;lift floating point stack dw FROMR dw DUP ;copy number from def into X dw X dw FMOVE dw LIT ;adjust number on top of floating point stack dw 8 dw PLUS dw TOR dw SEMIS ;---------------------------------- FLITERALnfa db 0C8H ;IMMEDIATE db 'FLITERA' db 'L'+80H dw FLITnfa FLITERAL ; ( -- ) If compiling, put floating point into def ; If exec, put floating point into X dw DOCOL dw STATE dw AT dw ZBRANCH ;if compiling (STATE<>0) then dw FLITERAL2-$ dw COMPILE ;..put FLIT into definition dw FLIT dw HERE dw LIT ;..make room for 8 bytes (floating number) in def dw 8 dw ALLOT dw XU ;..copy number from XU to definition dw SWAP dw FMOVE dw BRANCH dw FLITERAL3-$ FLITERAL2 ;else (executing) dw FENTER ;..lift floating point stack dw XU dw X dw FMOVE ;..copy number from XU to X FLITERAL3 dw SEMIS ;---------------------------------- DONUMBERnfa DB 88H DB '(NUMBER' DB ')'+80H DW WORDnfa DONUMBER ; ( d1 addr1 -- d2 addr2 ) Convert text at addr1+1 to ; number d2; addr2 is addr of first unconvertible DW DOCOL DONUMBER2 DW ONEPL DW DUP DW TOR ;put copy of address on R stack DW CAT DW BASE DW AT DW DIGIT ;convert to digit ( d1 n f ) DW ZBRANCH ;if no error then DW DONUMBER4-$ DW SWAP ;..get high word of d1 on top of stack ( d1l n d1h ) DW BASE DW AT DW USTAR ;..multiply it by the current base DW DROP ;..drop the high word of the result ( d1l n d2l ) DW ROT ;..get low word of d1 to top of stack ( n d2l d1l ) DW BASE DW AT DW USTAR ;..multiply it by current base ( n d2l d3 ) DW DPLUS ;..combine to get result ( d ) DW DPL DW AT DW ONEPL DW ZBRANCH ;..if [DPL]<>-1 then DW DONUMBER3-$ DW ONE DW DPL DW PLSTORE ;....increment value of DPL (# of digits after dot) DONUMBER3 DW FROMR ;..get addr back from R stack ( d addr ) DW BRANCH DW DONUMBER2-$ DONUMBER4 ;endif DW FROMR DW SEMIS ;---------------------------------- LEAD0nfa db 85H db 'LEAD' db '0'+80H dw DONUMBERnfa LEAD0 ; ( addr -- addr count ) Skip leading '0's, while ; counting them dw DOCOL dw ZERO ;initialize count to 0 LEAD02 ;begin dw OVER dw CAT ;..get byte there ( addr count b ) dw LIT dw '0' dw EQU dw ZBRANCH ;while byte=0 ( addr count ) dw LEAD03-$ dw ONEPL ;..increment count dw SWAP dw ONEPL ;..increment addr dw SWAP dw BRANCH ;repeat dw LEAD02-$ LEAD03 dw SEMIS ;---------------------------------- FCHARSnfa db 86H db 'FCHAR' db 'S'+80H dw LEAD0nfa FCHARS ; ( addr1 count -- addr2 count ) ; puts characters into floating point, if count<14 dw DOCOL FCHARS2 ;begin dw OVER dw CAT dw LIT dw 10 ;.. ( addr count char 10 ) dw DIGIT dw ZBRANCH ;while valid digit dw FCHARS7-$ dw OVER ;.. ( addr count digit count ) dw LIT dw 14 dw LESS dw ZBRANCH ;..if count<14 then dw FCHARS5-$ dw OVER ;.... ( addr count digit count ) dw ONE dw AND dw ZEQU dw ZBRANCH ;....if count even then dw FCHARS3-$ dw LIT dw 4 dw SHL ;......shift digit left 4 bits FCHARS3 ;....endif ( addr count digit ) dw OVER ;....compute storage addr dw ONE dw SHR dw XU dw PLUS dw ONEPL dw SWAP dw TOGGLE ;....and update number at location dw BRANCH ;.... ( addr count ) dw FCHARS6-$ FCHARS5 ;..else (count>=14) dw DROP ;....drop digit ( addr count ) FCHARS6 ;..endif (count<14) dw ONEPL ;..increment count dw SWAP dw ONEPL ;..increment reading addr dw SWAP dw BRANCH ;repeat ( addr count ) dw FCHARS2-$ FCHARS7 dw SEMIS ;---------------------------------- NUMBERnfa DB 86H DB 'NUMBE' DB 'R'+80H DW FCHARSnfa NUMBER ; ( addr - d ) Convert counted string to signed double ; ( addr -- ) If number has decimal, put into XU fvar DW DOCOL dw ZERO dw FFLAG dw STORE ;clear FFLAG (assume is not floating) dw DUP ; ( addr addr ) dw COUNT ; ( addr addr+1 count ) dw ZERO dw XDO NUMBER1a dw DUP ;.. ( addr addr2 addr2 ) dw CAT dw LIT dw '.' dw EQU dw ZBRANCH ;..if char at addr2 is '.', then ( addr addr2 ) dw NUMBER1b-$ dw ONE ;....set FFLAG and dw FFLAG dw STORE dw LEAVE ;....leave loop NUMBER1b dw ONEPL ;..increment addr2 ( addr addr2 ) dw XLOOP ;until find decimal or check all characters dw NUMBER1a-$ dw DROP ;drop addr2 ( addr ) dw FFLAG ;if found floating point then dw AT dw ZBRANCH dw NUMBER1c-$ dw XU dw LIT dw 8 dw ERASE ;..clear XU ( addr ) dw ONEPL ;..move to first number (prev was count) dw DUP dw CAT ;.. ( addr b ) dw LIT dw '-' dw EQU dw ZBRANCH ;..if first char is '-' then dw NUMBER1d-$ dw LIT ;....set sign bit in XU dw 80H dw XU dw CSTORE dw ONEPL ;....advance to next char dw BRANCH dw NUMBER1e-$ NUMBER1d ;..else (first char<>'-') dw DUP dw CAT dw LIT dw '+' dw EQU dw ZBRANCH ;..if first char='+' then dw NUMBER1e-$ dw ONEPL ;....advance address ( addr ) NUMBER1e ;..have taken care of leading sign dw LEAD0 ;..skip leading '0's dw DROP ;..drop count (don't care how many) ( addr ) dw ZERO dw FCHARS ;..put digits in, until non-digit ( addr count ) dw OVER dw CAT dw LIT dw '.' dw MINUS dw ZERO dw QERROR ;..msg #0 if not decimal point ( addr count ) dw SWAP ;.. ( count addr ) dw ONEPL ;..move to next character dw OVER ;.. ( count addr count ) dw ZEQU dw ZBRANCH ;..if count is zero then dw NUMBER1m-$ dw LEAD0 ;....skip leading zeros dw CHS ;....exponent is neg of # of zeros skipped dw BRANCH dw NUMBER1n-$ NUMBER1m ;..else (non-zeros already found) dw OVER ;....exponent is # of digits processed NUMBER1n ;..endif ( count addr exponent ) dw LIT dw 40H ;..put bias into exponent dw PLUS dw XU dw CAT dw PLUS ;..include negative, if any dw XU dw CSTORE ;..exponent now set ( count addr ) dw SWAP ;.. ( addr count ) dw FCHARS ;..process chars right of decimal ( addr count ) dw DROP ;..drop count dw DUP dw CAT ;.. ( addr byte ) dw LIT dw 0DFH dw AND ;..zero bit 5 (distinguishes lower from upper case) dw LIT dw 'E' dw EQU dw ZBRANCH ;..if 'e' or 'E' follows, then dw NUMBER1f-$ dw LIT dw -1 dw DPL dw STORE ;....set DPL to -1 (for use by (NUMBER) ) dw ONEPL ;....increment address ( addr ) dw DUP dw CAT dw LIT dw '-' dw EQU ;.... ( addr f ) dw SWAP dw OVER ;.... ( f addr f ) dw ZEQU dw ZBRANCH ;....if character<>'-' then dw NUMBER1g-$ dw DUP dw CAT dw LIT dw '+' dw MINUS dw ZBRANCH ;......if character<>'+' then dw NUMBER1g-$ dw ONE dw MINUS ;........back up one address (for (NUMBER) ( f addr ) NUMBER1g ;....endif (neither '-' nor '+' in exponent) dw BASE ;....store current base dw AT dw TOR dw DECIMAL ;....set decimal base for (NUMBER) dw ZERO dw ZERO ;.... ( f addr 0,0 ) dw ROT ;.... ( f 0,0 addr ) dw DONUMBER dw SWAP dw DROP ;....drop high word of exponent ( f n addr ) dw SWAP ;.... ( f addr n ) dw FROMR dw BASE dw STORE ;....restore previous base dw ROT ;.... ( addr n f ) dw ZBRANCH ;....if there was minus sign then dw NUMBER1h-$ dw CHS ;......change sign of exponent NUMBER1h ;.... ( addr n ) dw XU dw CAT dw LIT dw 7FH ;....get exponent previously saved (strip sign bit) dw AND dw PLUS ;.... ( addr n ) dw DUP dw LIT dw 7FH dw GREATER dw OVER dw ONE dw LESS dw OR dw ZERO dw QERROR ;....error if exponent out of range dw XU dw CAT dw LIT dw 80H dw AND dw PLUS dw XU dw CSTORE ;.... ( addr ) NUMBER1f ;..reached end of number dw CAT dw BL dw MINUS dw ZERO dw QERROR ;..if this char<>' ', msg #0 dw XU ;..if first four digits are 0000 then dw ONEPL dw AT dw ZEQU dw ZBRANCH dw NUMBER1v-$ dw ZERO ;....replace exponent with 00 dw XU dw CSTORE NUMBER1v dw BRANCH ;..done with floating point number ( ) dw NUMBER4-$ NUMBER1c ;else (not floating point, do as double integer) DW ZERO DW ZERO DW ROT ; ( 0,0 addr ) DW DUP DW ONEPL DW CAT ; ( 0,0 addr b ) DW LIT DW '-' DW EQU ;Check whether minus sign precedes ( 0,0 addr f ) DW DUP DW TOR DW PLUS ;if was a minus sign, increment addr DW LIT DW -1 ; ( 0,0 addr -1 ) NUMBER2 DW DPL DW STORE ;set value of DPL to number on stack ( 0,0 addr ) DW DONUMBER ;do number conversion ( d addr2 ) DW DUP DW CAT ; ( d addr2 b ) DW BL DW MINUS ;check whether next char is blank ( d addr2 f ) DW ZBRANCH ;if next char not blank, then DW NUMBER3-$ DW DUP DW CAT DW LIT DW ',' DW MINUS ;..set flag if next char not comma ( d addr2 f ) DW ZERO DW QERROR ;..abort if not with message #0 DW ZERO ;.. ( d addr2 0 ) DW BRANCH DW NUMBER2-$ NUMBER3 ;endif (next char not blank) DW DROP DW FROMR ;retrieve flag from R stack ( d f ) DW ZBRANCH ;if minus sign preceded DW NUMBER4-$ DW DCHS ;..change sign of result NUMBER4 DW SEMIS ; ( d ) ;---------------------------------- MINUSFINDnfa DB 85H DB '-FIN' DB 'D'+80H DW NUMBERnfa MINUSFIND ; ( -- pfa b tf | ff ) Search for next text word DW DOCOL DW BL DW WORD ;copy word into dictionary (not smudged) DW HERE ;return addr of count byte DW CONTEXT DW AT DW AT ;get address of last in context DW XFIND ; ( pfa b tf | ff ) DW DUP DW ZEQU ;if not found, set flag DW ZBRANCH ;if not found, then DW 10 DW DROP ; ( ) DW HERE ;..search CURRENT vocabulary DW LATEST DW XFIND DW SEMIS ;---------------------------------- DOABORTnfa DB 87H DB '(ABORT' DB ')'+80H DW MINUSFINDnfa DOABORT ; ( anything -- ) Same as ABORT DW DOCOL DW ABORT DW SEMIS ;---------------------------------- ERRORnfa ;!!!Deviates from fig-FORTH model for diskless!!! ;!!!May change in RAM4TH!!! DB 85H DB 'ERRO' DB 'R'+80H DW DOABORTnfa ERROR ; ( line -- in blk | ) Respond to error with message DW DOCOL DW WARNING DW AT DW ZLESS DW ZBRANCH ;if WARNING<0, then execute (ABORT) DW ERROR2-$ DW DOABORT ERROR2 ;else (WARNING>=0) ( line ) DW HERE DW COUNT DW TYPE ;..type word being interpreted by WORD DW DODOTQUOTE DB 02H DB '? ' ;..followed by '? ' DW MESSAGE DW SPSTORE ;..clear stack DW BLK DW AT ;..check whether loading from disk DW NZDUP DW ZBRANCH ;..if loading from disk DW ERROR3-$ DW IN ;....return interpretation pointer DW AT DW SWAP ;.... ( in blk ) ERROR3 ;..endif DW QUIT ;---------------------------------- IDDOTnfa ;!!!Seems unnecessarily complicated!!! DB 83H DB 'ID' DB '.'+80H DW ERRORnfa IDDOT ; ( nfa -- ) Print definition's name from nfa DW DOCOL DW PAD DW LIT DW 32 DW LIT DW '_' DW FILL ;fill 32 bytes, starting at PAD, with underscore! DW DUP DW PFA DW LFA ; ( nfa lfa ) DW OVER ; ( nfa lfa nfa ) DW MINUS ; ( nfa count ) DW PAD ; ( nfa count pad ) DW SWAP ; ( nfa pad count ) DW CMOVE ;copy name field (including count) to PAD DW PAD DW COUNT DW LIT DW 1FH DW AND DW TYPE ;send characters to display (TYPE strips bit 7) DW SPACE DW SEMIS ;---------------------------------- CREATEnfa DB 86H DB 'CREAT' DB 'E'+80H DW IDDOTnfa CREATE ; ( -- ) Defining word; puts into dictionary DW DOCOL DW MINUSFIND ; ( pfa b tf | ff ) DW ZBRANCH ;if found DW CREATE2-$ DW DROP DW NFA DW IDDOT ;..print word's name DW LIT DW 4 DW MESSAGE ;..print message #4 DW SPACE CREATE2 ;end (if word already exists) DW HERE ; ( here ) DW DUP DW CAT ; ( here count ) DW WIDTH DW AT DW MIN ;Allowed count is <= width ( here n ) DW ONEPL DW ALLOT ;reserve dictionary space ( here ) DW DUP DW LIT DW 0A0H DW TOGGLE ;smudge and set high bit ( here ) DW HERE ;new HERE (with bytes allotted) ( here here2 ) DW ONE DW MINUS DW LIT DW 80H DW TOGGLE ;set bit7 of last char ( here ) DW LATEST ; ( here latest ) DW COMMA ;put value into lfa DW CURRENT DW AT DW STORE ;put nfa into current vocabulary DW HERE ;return current value DW TWOPL ;set cfa to point to pfa DW COMMA ;here now points to pfa also DW SEMIS ;---------------------------------- BRAKCOMPILEnfa DB 0C9H DB '[COMPILE' DB ']'+80H DW CREATEnfa ; ( ) Force compilation of next word in stream DW DOCOL DW MINUSFIND ; ( pfa b tf | ff ) DW ZEQU DW ZERO DW QERROR ;if not found, error message #0 DW DROP ; ( pfa ) DW CFA DW COMMA DW SEMIS ;---------------------------------- LITERALnfa DB 0C7H ;IMMEDIATE DB 'LITERA' DB 'L'+80H DW BRAKCOMPILEnfa LITERAL ; ( n -- ) Compiles n into definition as literal DW DOCOL DW STATE DW AT DW ZBRANCH ;if compiling, then DW LITERAL2-$ DW COMPILE ;..put LIT into definition DW LIT DW COMMA ;..followed by the number on top of stack LITERAL2 DW SEMIS ;---------------------------------- DLITERALnfa DB 0C8H ;IMMEDIATE DB 'DLITERA' DB 'L'+80H DW LITERALnfa DLITERAL ; ( d -- ) If compiling, put double literal in def DW DOCOL DW STATE DW AT DW ZBRANCH ;if compiling, then DW DLITERAL2-$ DW SWAP ;..swap halves of double number on stack DW LITERAL ;..perform LITERAL twice DW LITERAL DLITERAL2 DW SEMIS ;---------------------------------- QSTACKnfa DB 86H DB '?STAC' DB 'K'+80H DW DLITERALnfa QSTACK ; ( -- ) Error if stack underflow or no room DW DOCOL DW SPAT DW S0 DW AT DW SWAP DW ULESS ;flag true if S0 less than current stack pointer DW ONE DW QERROR DW SPAT DW HERE DW LIT DW 384 ;allow room for bytes at PAD and stack growth DW PLUS DW ULESS ;set flag if SP@ less than HERE+384 DW LIT DW 7 DW QERROR ;if flag set, issue error #7 DW SEMIS ;---------------------------------- INTERPRETnfa DB 89H DB 'INTERPRE' DB 'T'+80H DW QSTACKnfa INTERPRET ; ( -- ) Interpreter for input stream DW DOCOL INTERPRET2 DW MINUSFIND ;seek in CONTEXT and CURRENT vocabularies DW ZBRANCH ;if found in vocabulary then DW INTERPRET5-$ DW STATE DW AT ;.. ( pfa b state ) DW LESS DW ZBRANCH ;..if the number found is less than STATE then DW INTERPRET3-$ DW CFA ;....convert pfa to cfa ( cfa ) DW COMMA ;....store it in dictionary DW BRANCH DW INTERPRET4-$ INTERPRET3 ;..else (b>=STATE) DW CFA ;....convert pfa to cfa ( cfa ) DW EXECUTE ;....execute it INTERPRET4 DW QSTACK ;..check for stack underflow and stack full DW BRANCH DW INTERPRET8-$ INTERPRET5 ;else (not found in vocabulary) ( ) DW HERE ;..word is stored at HERE DW NUMBER ;..attempt to convert it to double number dw FFLAG ;..if floating point number then dw AT dw ZBRANCH dw INTERPRET9-$ dw FLITERAL ;....handle floating point literal dw BRANCH dw INTERPRET7-$ INTERPRET9 DW DPL ;..else (not floating point) DW AT DW ONEPL DW ZBRANCH ;..if DPL<>-1 then (double number found) DW INTERPRET6-$ DW DLITERAL ;....if compiling, compile double number DW BRANCH DW INTERPRET7-$ INTERPRET6 ;..else (DPL=-1) DW DROP DW LITERAL ;....if compiling, compile single number INTERPRET7 DW QSTACK ;check for stack underflow and stack full INTERPRET8 DW BRANCH DW INTERPRET2-$ ;---------------------------------- IMMEDIATEnfa DB 89H DB 'IMMEDIAT' DB 'E'+80H DW INTERPRETnfa ; ( -- ) Toggle immediate bit of latest word DW DOCOL DW LATEST DW LIT DW 40H DW TOGGLE DW SEMIS ;---------------------------------- VOCABULARYnfa DB 8AH DB 'VOCABULAR' DB 'Y'+80H DW IMMEDIATEnfa ; ( -- ) Builds a vocabulary DW DOCOL DW LESSBUILDS ;start definition of action on compilation DW LIT ;put the cfa of BUMP into the definition DW BUMP DW COMMA DW CURRENT ;put the cfa of the CURRENT vocabulary in the def DW AT DW CFA DW COMMA DW HERE DW VOCLINK DW AT DW COMMA DW VOCLINK DW STORE DW DOESGREAT LAEC3 ;define run-time action of a vocabulary DW TWOPL ;store the pfa+2+2 at CONTEXT DW CONTEXT DW STORE DW SEMIS ;---------------------------------- FORTHnfa DB 0C5H DB 'FORT' DB 'H'+80H DW VOCABULARYnfa FORTH ; ( -- ) Make FORTH the CONTEXT vocabulary DW DODOESGREAT DW LAEC3 DW BUMP ;??? LAED9 DW TASKnfa VOCLINKInit dw 0 ;---------------------------------- DEFINITIONSnfa DB 8BH DB 'DEFINITION' DB 'S'+80H DW FORTHnfa DEFINITIONS ; ( -- ) Set the CURRENT vocabulary to the CONTEXT DW DOCOL DW CONTEXT DW AT DW CURRENT DW STORE DW SEMIS ;---------------------------------- LPARENnfa ;IMMEDIATE DB 0C1H DB '('+80H DW DEFINITIONSnfa ; ( -- ) Skip over a comment until closing paren DW DOCOL DW LIT DW 41 DW WORD DW SEMIS ;---------------------------------- QUITnfa DB 84H DB 'QUI' DB 'T'+80H DW LPARENnfa QUIT ; ( -- ) Clear return stack, stop compile DW DOCOL DW ZERO DW BLK DW STORE ;return interpretation to keyboard DW LBRACKET ;stop compilation QUIT2 DW RPSTORE ;clear return stack DW CR ;new line DW QUERY ;get a line from keyboard DW INTERPRET ;interpret it DW STATE DW AT DW ZEQU DW ZBRANCH ;if state=0 then DW QUIT3-$ DW DODOTQUOTE DB 05H ;..display "Ok {" DB 'Ok { ' dw DEPTH dw DOT dw DODOTQUOTE db 02H db '} ' QUIT3 DW BRANCH DW QUIT2-$ ;---------------------------------- ABORTnfa DB 85H DB 'ABOR' DB 'T'+80H DW QUITnfa ABORT ; ( anything -- ) Return to 0 state, clear stacks DW DOCOL DW SPSTORE ;clear stack DW DECIMAL ;set base to decimal DW QSTACK ;make sure dictionary not full DW CR DW DOTCPU ;identify CPU type DW DODOTQUOTE DB 0EH ;print revision number DB 'fig-FORTH 1' DB '.1D' DW FORTH ;set CONTEXT and CURRENT vocabularies to FORTH DW DEFINITIONS DW QUIT ;---------------------------------- WarmStart LXI B,LAF62 JMP NEXT LAF62 dw WARM ;---------------------------------- WARMnfa DB 84H DB 'WAR' DB 'M'+80H DW ABORTnfa WARM ;returns to command interpreter DW DOCOL DW ABORT ;---------------------------------- ColdStart mvi a,8 sta VarLength ;floating point math is double precision LXI B,LAF7B ;initialize IP to point to COLD lxi h,RInit shld RP ;initialize RP LHLD LA012 ;initialize SP SPHL JMP NEXT LAF7B DW COLD ;---------------------------------- COLDnfa DB 84H DB 'COL' DB 'D'+80H DW WARMnfa COLD ; ( -- ) Routine for cold start ;!!!Needs to change for RAM4TH!!! DW DOCOL DW ZERO ;no echo to printer at first DW LIT DW PrintFlag DW STORE DW LIT ;initialize first 8 user variables DW LA012 DW LIT DW USERSpace DW AT DW LIT DW 6 DW PLUS DW LIT DW 16 DW CMOVE DW LIT DW LA00C DW AT DW LIT DW LAED9 ;initialize FORTH vocabulary pointer DW STORE DW ABORT ;---------------------------------- S2Dnfa DB 84H DB 'S->' DB 'D'+80H DW COLDnfa S2D ; ( n -- d ) Convert n to double, preserving sign DW S2Dpfa S2Dpfa POP D ;get n into DE LXI H,0 ;HL := 0 MOV A,D ;if bit15 of DE set, then ANI 80H JZ S2D2 DCX H ;..HL: = -1 S2D2 JMP PUSHDEHL ;put double number results on stack ;---------------------------------- PLMINUSnfa DB 82H DB '+' DB '-'+80H DW S2Dnfa PLMINUS ; ( n1 n2 -- n3 ) If n2<0, n3:=-n1, else n3:=n1 DW DOCOL DW ZLESS DW ZBRANCH ;if n2<0, then DW PLMINUS2-$ DW CHS ;..change sign of n1 PLMINUS2 DW SEMIS ;---------------------------------- DPLMINUSnfa DB 83H DB 'D+' DB '-'+80H DW PLMINUSnfa DPLMINUS ; ( d1 n -- d2 ) If n<0, d2 := -d1, else d2 := d1 DW DOCOL DW ZLESS DW ZBRANCH DW DPLMINUS2-$ DW DCHS DPLMINUS2 DW SEMIS ;---------------------------------- ABSnfa DB 83H DB 'AB' DB 'S'+80H DW DPLMINUSnfa ABS ; ( n1 -- n2 ) n2 is absolute value of n1 DW DOCOL DW DUP DW PLMINUS DW SEMIS ;---------------------------------- DABSnfa DB 84H DB 'DAB' DB 'S'+80H DW ABSnfa DABS ; ( d1 -- d2 ) d2 is absolute value of d1 DW DOCOL DW DUP DW DPLMINUS DW SEMIS ;---------------------------------- MINnfa DB 83H DB 'MI' DB 'N'+80H DW DABSnfa MIN ; ( n1 n2 -- n3 ) n3 is smaller of n1 and n2 DW DOCOL DW TWODUP ; ( n1 n2 n1 n2 ) DW GREATER ; ( n1 n2 f ) DW ZBRANCH ;if n1>n2 then DW MIN2-$ DW SWAP ;..put larger number on top ( n2 n1 ) MIN2 DW DROP ;drop larger number DW SEMIS ;---------------------------------- MAXnfa DB 83H DB 'MA' DB 'X'+80H DW MINnfa MAX ; ( n1 n2 -- n3 ) n3 is larger of n1 and n2 DW DOCOL DW TWODUP ; ( n1 n2 n1 n2 ) DW LESS ; ( n1 n2 f ) DW ZBRANCH ;if n12 DW HERE ; ( addr here ) DW OVER ; ( addr here addr ) DW MINUS ; ( addr here-addr ) DW SWAP ; ( here-addr addr ) DW STORE ;store offset at addr (of IF) DW SEMIS ;---------------------------------- THENnfa DB 0C4H ;IMMEDIATE DB 'THE' DB 'N'+80H DW ENDIFnfa ; ( addr 2 -- ) Same as ENDIF DW DOCOL DW ENDIF DW SEMIS ;---------------------------------- DOnfa DB 0C2H ;IMMEDIATE DB 'D' DB 'O'+80H DW THENnfa ; ( -- addr 3 ) Compile required stuff for DO loop DW DOCOL DW COMPILE ;Run-time procedure is (DO) DW XDO DW HERE DW THREE DW SEMIS ; ( here 3 ) ;---------------------------------- LOOPnfa DB 0C4H ;IMMEDIATE DB 'LOO' DB 'P'+80H DW DOnfa ; ( addr 3 -- ) Compile loop back to DO statement DW DOCOL DW THREE DW QPAIR ;abort with error if top of stack <> 3 DW COMPILE DW XLOOP ;put (LOOP) into definition DW BACK ;put backwards offset into definition DW SEMIS ;---------------------------------- PLLOOPnfa DB 0C5H ;IMMEDIATE DB '+LOO' DB 'P'+80H DW LOOPnfa ; ( addr 3 -- ) Compile code for +LOOP DW DOCOL DW THREE DW QPAIR ;abort with error message if top<>3 DW COMPILE DW XPLOOP ;put (+LOOP) into definition DW BACK ;put reverse offset into definition DW SEMIS ;---------------------------------- UNTILnfa DB 0C5H ;IMMEDIATE DB 'UNTI' DB 'L'+80H DW PLLOOPnfa UNTIL ; ( addr 1 -- ) Compile code for UNTIL DW DOCOL DW ONE DW QPAIR ;abort if top<>1 DW COMPILE DW ZBRANCH ;put 0BRANCH into definition DW BACK ;put reverse offset into definition DW SEMIS ;---------------------------------- ENDnfa DB 0C3H ;IMMEDIATE DB 'EN' DB 'D'+80H DW UNTILnfa ; ( addr 1 -- ) Same as UNTIL DW DOCOL DW UNTIL DW SEMIS ;---------------------------------- AGAINnfa DB 0C5H ;IMMEDIATE DB 'AGAI' DB 'N'+80H DW ENDnfa AGAIN ; ( addr 1 -- ) Branch forever! DW DOCOL DW ONE DW QPAIR ;error if top<>1 DW COMPILE DW BRANCH ;put BRANCH into definition DW BACK ;put reverse offset into definition DW SEMIS ;---------------------------------- REPEATnfa DB 0C6H ;IMMEDIATE DB 'REPEA' DB 'T'+80H DW AGAINnfa ; ( addr1 1 addr2 4 -- ) DW DOCOL DW TOR DW TOR ; ( addr1 1 ) DW AGAIN ;put branch back to BEGIN location DW FROMR DW FROMR ; ( addr2 4 ) DW TWO DW MINUS ; ( addr2 2 ) DW ENDIF ;put branch from WHILE to here DW SEMIS ;---------------------------------- IFnfa DB 0C2H ;IMMEDIATE DB 'I' DB 'F'+80H DW REPEATnfa IF ; ( -- addr 2 ) Begin IF statement DW DOCOL DW COMPILE ;put ZBRANCH in definition DW ZBRANCH DW HERE ;put HERE on stack for offset computation DW ZERO ;put 0 into definition for now DW COMMA DW TWO DW SEMIS ; ( here 2 ) ;---------------------------------- ELSEnfa DB 0C4H ;IMMEDIATE DB 'ELS' DB 'E'+80H DW IFnfa ; ( addr1 2 -- addr2 2 ) DW DOCOL DW TWO ;abort with error if top <> 2 DW QPAIR DW COMPILE ;put BRANCH into dictionary DW BRANCH DW HERE ;put current addr onto stack ( addr1 addr2 ) DW ZERO ;reserve place for branch offset DW COMMA DW SWAP ; ( addr2 addr1 ) DW TWO ; ( addr2 addr1 2 ) DW ENDIF ;put offset to here in previous 0BRANCH ( addr2 ) DW TWO DW SEMIS ; ( addr2 2 ) ;---------------------------------- WHILEnfa DB 0C5H ;IMMEDIATE DB 'WHIL' DB 'E'+80H DW ELSEnfa ; ( -- addr 4 ) DW DOCOL DW IF ;same as if except DW TWOPL ;put 4 on top of stack DW SEMIS ;---------------------------------- SPACESnfa DB 86H DB 'SPACE' DB 'S'+80H DW WHILEnfa SPACES ; ( n -- ) Emit n spaces to display DW DOCOL DW ZERO ; ( n 0 ) DW MAX ;Make sure number not negative DW NZDUP DW ZBRANCH ;if n>0 then DW SPACES3-$ DW ZERO ; ( n 0 ) DW XDO ;..for I:=0 to n-1 do SPACES2 DW SPACE ;....send space to display DW XLOOP ;..next I DW SPACES2-$ SPACES3 DW SEMIS ;endif ;---------------------------------- LESSSHARPnfa DB 82H DB '<' DB '#'+80H DW SPACESnfa LESSSHARP ; ( -- ) Set up for pictured number conversion DW DOCOL DW PAD ;get address of PAD DW HLD ;store it in user variable HLD DW STORE DW SEMIS ;---------------------------------- SHARPGREATnfa DB 82H DB '#' DB '>'+80H DW LESSSHARPnfa SHARPGREAT ; ( d -- addr count ) Terminate pictured conversion DW DOCOL DW DROP ;drop d DW DROP DW HLD ;get addr of first char DW AT DW PAD DW OVER ; ( hld@ pad hld@ ) DW MINUS ; ( addr count ) DW SEMIS ;---------------------------------- SIGNnfa DB 84H DB 'SIG' DB 'N'+80H DW SHARPGREATnfa SIGN ; ( n d -- d ) Puts ASCII '-' before number if n<0 DW DOCOL DW ROT ; ( d n ) DW ZLESS ; ( d f ) DW ZBRANCH ;if n<0 then DW SIGN2-$ DW LIT DW '-' ;..put '-' on stack DW HOLD ;..put it in front of pictured conversion SIGN2 DW SEMIS ;---------------------------------- SHARPnfa DB 81H DB '#'+80H DW SIGNnfa SHARP ; ( d1 -- d2 ) Convert digit from number DW DOCOL DW BASE DW AT DW MSLASHMOD ; ( urem uquotd ) DW ROT ; ( uquotd urem ) DW LIT DW 9 DW OVER ; ( uquotd urem 9 urem ) DW LESS DW ZBRANCH ;if 9c/l then DW VLIST3-$ DW CR ;....send CR DW ZERO ;....OUT := 0 DW OUT DW STORE VLIST3 ;..endif DW DUP ;.. ( nfa nfa ) DW IDDOT ;..print word's name DW SPACE ;..print two spaces ( nfa ) DW SPACE DW PFA DW LFA ;.. ( lfa ) DW AT ;..get nfa of preceding word ( nfa ) DW DUP DW ZEQU ;.. ( nfa f1 ) DW QTERMINAL ;.. ( nfa f1 f2 ) DW OR ;.. ( nfa f3 ) DW ZBRANCH ;until last word or ShiftBreak keys DW VLIST2-$ DW DROP ;drop nfa DW SEMIS ;---------------------------------- MENUnfa DB 84H DB 'MEN' DB 'U'+80H DW VLISTnfa MENU dw MENUaddr ;---------------------------------- DOTCPUnfa DB 84H DB '.CP' DB 'U'+80H DW MENUnfa DOTCPU ; ( -- ) Print CPU type DW DOCOL DW BASE DW AT ;get current base ( base@ ) DW LIT DW 36 DW BASE DW STORE ;BASE := 36 DW LIT DW 34 DW PLORIGIN DW TWOAT ;get double number at origin+34 ( base@ d ) DW DDOT ;print it (in base 36!) ( base@ ) DW BASE ;BASE := original base DW STORE DW SEMIS ;---------------------------------- CALLnfa DB 84H DB 'CAL' DB 'L'+80H DW DOTCPUnfa ; ( psw1 bc1 de1 hl1 addr -- psw2 bc2 de2 hl2 ) ; Call routine at addr DW CALLpfa CALLpfa MOV L,C MOV H,B SHLD LB870 ;save current IP in memory POP H ;get addr from top of stack SHLD LB862 ;put address into instruction POP H ;load registers POP D POP B POP PSW CALL 0000 LB862 EQU $-2 PUSH PSW ;put registers onto stack PUSH B PUSH D PUSH H LHLD LB870 ;get IP back from memory MOV C,L MOV B,H JMP NEXT LB870 dw 0 ;---------------------------------- DoQuoteNfa db 83h db '("' db ')'+80h dw CALLnfa DOQUOTE ; ( -- addr n ) Move string from definition to PAD dw DOCOL dw PAD ;get current address of PAD dw LIT dw 255 dw OVER dw CSTORE ;store 255 at address of PAD dw ONEPL ;move one address higher dw R ;get address of next word in definition dw TWODUP ; ( pad+1 r pad+1 r ) dw CAT ;get byte there ( pad+1 r pad+1 count ) dw ONEPL dw CMOVE ;copy string to pad+1 from definition ( pad+1 ) dw COUNT ; ( addr n ) dw FROMR ;adjust top of R stack to point past these bytes dw OVER dw ONEPL dw PLUS dw TOR dw SEMIS ; ( addr n ) ;---------------------------------- QuoteNfa DB 0c1h ;IMMEDIATE db '"'+80h dw DoQuoteNfa QUOTE ; ( -- addr n ) copy string to PAD (if executing) ; ( -- ) copy string to definition (if compiling) dw DOCOL dw LIT dw '"' ;Put quote character on stack (for WORD) dw STATE dw AT dw ZBRANCH dw Quote3-$ Quote2 ;if compiling (as opposed to immediate execution) dw COMPILE ;..put (") into definition dw DOQUOTE dw WORD ;..put string itself into definition (not yet reserved) dw HERE dw CAT ;..get number of bytes in string ( n ) dw ONEPL dw ALLOT ;..allot bytes for count and for each byte in string dw BRANCH dw Quote4-$ Quote3 ;else (immediate execution) dw WORD ;..put string into dictionary (temporary) dw PAD dw LIT dw 255 dw OVER ; ( pad 255 pad ) dw CSTORE ;store 255 (max bytes) at pad dw ONEPL dw HERE dw TWODUP ; ( pad+1 here pad+1 here ) dw CAT ;get num bytes in string ( pad+1 here pad+1 count ) dw ONEPL dw CMOVE ;copy count and string to pad+1 dw COUNT ; ( addr n ) Quote4 dw SEMIS ;---------------------------------- EofNfa db 83h db 'EO' db 'F'+80h dw QuoteNfa EOF ; ( -- addr ) Variable holding EOF flag dw DOVARIABLE EofPfa dw 0 ;---------------------------------- OpenfNfa db 85h db 'OPEN' db 'F'+80H dw EofNfa OPENF ; ( addr n -- ) dw OpenfPfa OpenfPfa lxi h,false ;set EOF to 0 (false) shld EofPfa pop d ;get number of bytes into DE pop h ;get start addr of filename into HL push b ;save interpret pointer call OpenfSub1 ;parse filename, copy to 0FC93H mov a,d ;if device type unspecified then ora a jnz Openf2 mvi d,0f8h ;..default device type = RAM: Openf2 mvi a,0 ;file buffer number = 0 mov h,d ;put device type into H mvi l,1 ;operation 1 = open file call OpenfSub2 ;open file pop b ;restore interpret pointer jmp NEXT ;---------------------------------- OpenfSub1 push h jmp ParseName ;---------------------------------- OpenfSub2 push h lxi h,0c39h xthl push h jmp 4d1bh ;---------------------------------- GetCNfa db 84h db 'GET' db 'C'+80H dw OpenfNfa GETC ; ( -- c ) Get character c from Forth file dw GetCPfa GetCPfa lhld FileBufPtr push h ;save current pointer to file buffer push b ;save interpret pointer mvi a,0 ;read from file number 0 call 4cbfh call 4e7ah pop b ;retrieve interpret pointer pop h ;restore original pointer to file buffer shld FileBufPtr mvi h,0 ;get character returned into HL mov l,a jmp PUSHHL ;---------------------------------- ClosefNfa db 86h db 'CLOSE' db 'F'+80h dw GetCNfa CLOSEF ; ( -- ) Close file used by Forth dw ClosefPfa ClosefPfa push b ;save interpret pointer mvi a,0 ;file number = 0 call CloseFile pop b ;restore interpret pointer jmp NEXT ;---------------------------------- GetLineNfa db 87h db 'GETLIN' db 'E'+80h dw ClosefNfa GETLINE ; ( -- ) Read line from file until eoln or eof dw DOCOL dw FIRST ;destination address dw GETC ;get character from file dw DUP dw LIT dw 0ah dw EQU dw ZBRANCH ;if character is a linefeed then dw GETLINE2-$ dw DROP ;..forget it and dw GETC ;..get another character GETLINE2 ;begin dw DUP dw LIT dw 1ah dw EQU ;..set flag if char is eof dw DUP dw EOF dw STORE ;..store flag at EOF variable dw OVER dw LIT dw 0dh dw EQU dw OR dw ZEQU dw ZBRANCH ;..if char not eof or CR then dw GETLINE3-$ dw OVER dw CSTORE ;....store at current dest addr dw ONEPL ;....increment dest addr dw GETC ;....get char from file dw BRANCH ;until eof or CR encountered dw GETLINE2-$ GETLINE3 dw DROP ;drop EOF char returned dw ZERO ;store two bytes of 0's at next dest addr dw SWAP dw STORE dw SEMIS ;---------------------------------- LoadfNfa ;!!!May want to error if BLK<>0!!! db 85H db 'LOAD' db 'F'+80H dw GetLineNfa LOADF ; ( addr n -- ) Load from mass storage dw DOCOL dw ONE dw BLK dw STORE ;set flag to indicate loading from mass storage dw IN dw AT dw TOR ;save current value of IN on R stack dw OPENF ;open file requested dw GETLINE ;get first line (if any) from file requested dw ZERO dw IN dw STORE ;reset interpret pointer to beginning of line dw INTERPRET ;interpret from file dw ZERO ;return to interpreting from keyboard dw BLK dw STORE dw FROMR ;restore old value of IN dw IN dw STORE dw SEMIS ;---------------------------------- FVARIABLEnfa db 89H db 'FVARIABL' db 'E'+80H dw LoadfNfa ; ( -- ) Creates storage for 8 bytes of floating var dw DOCOL dw CREATE ;place name in dictionary dw SMUDGE ;make it visible dw LIT ;allot 8 bytes for its data dw 8 dw ALLOT dw DOSEMICODE DOFVARIABLE inx d ;point to next word in definition push d ;put its address on stack jmp NEXT ;---------------------------------- Xnfa db 81H db 'X'+80H dw FVARIABLEnfa X ; ( -- addr ) Returns Xpfa (storage for fvar X) dw DOFVARIABLE Xpfa dw 0,0,0,0 ;save room for it ;---------------------------------- Ynfa db 81H db 'Y'+80H dw Xnfa Y ; ( -- addr ) Returns Ypfa (storage for fvar Y) dw DOFVARIABLE Ypfa dw 0,0,0,0 ;---------------------------------- Znfa db 81H db 'Z'+80H dw Ynfa Z ; ( -- addr ) Returns Zpfa (storage for fvar Z) dw DOFVARIABLE Zpfa dw 0,0,0,0 ;---------------------------------- Tnfa db 81H db 'T'+80H dw Znfa T ; ( -- addr ) Returns Tpfa (storage for fvar T) dw DOFVARIABLE Tpfa dw 0,0,0,0 ;---------------------------------- XUnfa db 82H db 'X' db 'U'+80H dw Tnfa XU ; ( -- addr ) Returns XUpfa (storage for fvar XU) ;(used for temporary, system storage ONLY) dw DOFVARIABLE XUpfa dw 0,0,0,0 ;---------------------------------- FFLAGnfa db 85h db 'FFLA' db 'G'+80H dw XUnfa FFLAG ; ( -- addr ) Holds variable telling whether number ;encountered a decimal point or not dw DOVARIABLE FFLAGpfa dw 0 ;---------------------------------- FMOVEnfa db 85H db 'FMOV' db 'E'+80H dw FFLAGnfa FMOVE ; ( from to -- ) Copies floating num from from to to dw DOCOL dw LIT dw 8 dw CMOVE dw SEMIS ;---------------------------------- FENTERnfa db 86H db 'FENTE' db 'R'+80H dw FMOVEnfa FENTER ; ( -- ) Raises floating-point stack dw DOCOL dw Z dw T dw FMOVE ;copy value from Z to T (T is lost) dw Y dw Z dw FMOVE ;copy value from Y to Z (Z is now in T) dw X dw Y dw FMOVE ;copy value from X to Y (Y is now in Z) dw SEMIS ;---------------------------------- CALLXnfa db 85h db 'CALL' db 'X'+80H dw FENTERnfa CALLX ; ( addr -- ) Call with no parameters dw CALLXpfa CALLXpfa pop h ;get address to call into HL push b ;put IP onto stack lxi d,CALLXret push d ;put return address on stack (as CALL would) pchl ;jump to addr CALLXret pop b jmp NEXT ;---------------------------------- PICKnfa db 84h db 'PIC' db 'K'+80h dw CALLXnfa PICK ; ( n1 -- n2 ) Copy n1th number from stack ; 1 PICK = DUP; 2 PICK = OVER dw PICKpfa PICKpfa pop h ;get n1 into HL dcx h ;adjust for pulling n1 from stack dad h ;each stack item is two bytes long dad sp ;compute addr of n1th item on stack mov a,m ;get number there inx h mov h,m mov l,a jmp PUSHHL ;put it onto stack ;---------------------------------- FSTRCODEnfa db 86h db 'F$COD' db 'E'+80h dw PICKnfa FSTRCODE ; ( fmode numdig pad -- adr n ) Does main work of ; formatting floating point number into string dw FSTRCODEpfa FSTRCODEpfa push b ;save instruction pointer onto stack lxi h,Accum1+7 ;last byte of Floating Point accumulator lxi d,Ftemp+15 ;destination of expand mvi c,7 ;seven bytes have packed BCD digits FSTRCODE2 ;repeat (expand packed digits) mov b,m ;..get digits mvi a,0Fh ;..separate out low digit ana b stax d ;..store low digit dcx d mvi a,0F0h ana b ;..separate out high digit rrc rrc rrc rrc stax d ;..store high digit dcx d dcx h dcr c ;..byte count := byte count - 1 jnz FSTRCODE2 ;until byte count = 0 mov a,m ;get exponent and sign ani 7Fh ;separate out exponent stax d dcx d mvi a,80h ana m ;separate out sign stax d inx d ldax d ora a ;if number is zero then jnz FSTRCODE2a mvi a,41h ;..set exponent to 1 (simplifies handling of 0.0) stax d ;Number is expanded; at Ftemp FSTRCODE2a ;Stack contents: ( fmode numdig pad IP ) pop h ;HL=IP pop b ;BC=pad pop d ;DE=numdig xthl ;HL=fmode; TopOfStack=IP mov h,b mov a,c mov c,l mvi b,0 ;BC=fmode mov l,a ;HL=pad push h ;save pointer to PAD on top of stack push d ;save numdig on top of stack ( IP pad numdig ) mvi a,3 ;convert fmode to a number from 0 to 3 ana c mov c,a jnz FSTRCODE3 ;if FIX notation lda Ftemp+1 cpi 4Fh ; and 15 or more digits to left of decimal then jc FSTRCODE3 mvi c,1 ;..display in scientific notation FSTRCODE3 ;number is expanded; mode is correct (unless rounding) mov a,c ora a ;if FIX notation then jnz FSTRCODE6 lda Ftemp+1 ;..compute number of digits to left of decimal add e ;..add number of digits to right of decimal sui 40h ;..subtract bias jnc FSTRCODE4 ;..if number<0 then mvi a,14 ;....replace it with 14 FSTRCODE4 mov e,a ;..E has number of digits for fixed notation (min 0) jmp FSTRCODE9 FSTRCODE6 ;else (not FIX notation) inr e ;..total number of digits = # specified + 1 FSTRCODE9 ;E has total number of digits ;HL points to PAD, C has fmode mov a,e cpi 14 ;if character count<14 then (round) jnc FSTRCODE9z push d ;..save character count (total in E) mvi d,0 lxi h,Ftemp+2 dad d ;..HL points to last character for rounding push h ;..save pointer to last char for rounding mov a,m ;..if >= 5 then (round up) cpi 5 jc FSTRCODE9t FSTRCODE9a ;....repeat (until no carry or out of digits) dcx h ;......move back one digit inr m ;......increment that digit dcr e ;......decrement char count jm FSTRCODE9c ;......if not out of chars then mov a,m ;........get digit we just did sbi 10 ;........if that digit is 10 jnz FSTRCODE9t mov m,a ;..........replace digit with 0 jmp FSTRCODE9a ;....until out of digits or no carry out FSTRCODE9c ;....if out of digits (just did exponent) then inx h mvi m,1 ;......replace first digit with 1 pop d ;......discard pointer to first digit to zero pop d ;......retrieve number of chars not zeroed (in E) mvi e,1 ;......number of chars not zeroed is 1 push d inx h ;......start zeroing one address higher push h dcr c ;......if FIX notation inr c jnz FSTRCODE9t lda Ftemp+1 ;...... and 15 or more digits left of decimal then cpi 4Fh jc FSTRCODE9t mvi c,1 ;........change to SCI notation FSTRCODE9t ;..digits on left are rounded pop h ;..recall pointer to first digit to zero pop d ;..retrieve number of chars not zeroed (in E) mvi a,14 ;..compute number of chars to zero sub e mov e,a sub a FSTRCODE9v ;..repeat mov m,a ;....zero digit inx h ;....advance pointer dcr e ;....decrement digit count jnz FSTRCODE9v ;..until digit count is zero FSTRCODE9z ;endif (rounding is needed) pop d ;retrieve numdig dcr c inr c ;if fmode is 0 (FIX notation) then jnz FSTRCODE10g lda FTEMP+1 ;..num digits on left = exponent + 1 sui 40h ;..if exponent < -1 then jnc FSTRCODE10a sub a ;....num digits on left = 0 FSTRCODE10a mov d,a ;..D has number of digits on left of decimal add e ;..E has number of digits to right of decimal cpi 15 ;..if total is greater than 14 then jc FSTRCODE10z mvi a,14 ;....number on right is 14 - (number on left) sub d mov e,a jmp FSTRCODE10z FSTRCODE10g ;else fmode <> 0 (SCI or ENG notation) mvi d,1 ;..default: 1 digit before decimal mov a,c dcr a ;..if ENG mode (not SCI) jz FSTRCODE10z mvi b,60h ;....get highest possible multiple of 3 into B lda Ftemp+1 ;....get exponent into A inr a FSTRCODE10h ;....repeat (do mod 3) sub b ;......subtract current divisor jnc FSTRCODE10k ;......if borrow then add b ;........add it back FSTRCODE10k mov d,a ;......save latest result in D mov a,b rrc ;......divide divisor by 2 mov b,a mov a,d jnc FSTRCODE10h ;....until a carry is generated inr d ;....D has number of digits left of decimal mov a,e ;....number of digits on right is total - left inr a sub d ;....if result is negative jnc FSTRCODE10m sub a ;......then number on right is 0 FSTRCODE10m mov e,a ;....E has number of digits on right of decimal FSTRCODE10z ;endif (test of fmode) pop h ;retrieve pointer to pad mvi m,0FFh ;allow 255 chars in string inx h push h ;save pointer to byte count inx h ;points to output position for first char push d ;save number of digits left, right lxi d,Ftemp ldax d inx d inx d ;D points to source ora a ;if sign bit set then jp FSTRCODE10 mvi m,'-' ;..put '-' into output string inx h FSTRCODE10 mov a,c pop b ;B has number on left, C has number on right ora a ;if FIX notation then jnz FSTRCODE19 dcr b ;..if B=0 then jp FSTRCODE11 mvi m,'0' ;....put leading 0 inx h jmp FSTRCODE12 FSTRCODE11 ;..else B>0; repeat (copy B chars) ldax d ;....copy byte to output adi '0' ;....changing number to char mov m,a inx d inx h dcr b ;....decrement count jp FSTRCODE11 ;..until B goes negative FSTRCODE12 mvi m,'.' ;..put decimal point into output inx h lda Ftemp+1 sui 40h ;..compute -(number of leading zeros) mov b,a ;..put count into B jnc FSTRCODE16 ;..if leading zeros then add c ;....if more than number right of decimal then jc FSTRCODE13 sub a ;......replace with -(total count) sub c mov b,a FSTRCODE13 dcr b jmp FSTRCODE15 FSTRCODE14 ;....while number of leading zeros>0 do mvi m,'0' ;......put '0' into output inx h dcr c FSTRCODE15 inr b jm FSTRCODE14 FSTRCODE16 ;..endif (leading zeros) inr c jmp FSTRCODE18 FSTRCODE17 ;..while digits left to transfer do ldax d ;....transfer them, changing to ASCII adi '0' mov m,a inx d inx h FSTRCODE18 dcr c jnz FSTRCODE17 jmp FSTRCODE26 FSTRCODE19 ;else (ENG or SCI notation) push b ;..B has number on left, C has number on right FSTRCODE20 ;..repeat (once per digit) ldax d adi '0' ;....convert digit to char mov m,a ;....and put it in output inx d inx h dcr b ;..until digits on left are done jnz FSTRCODE20 mvi m,'.' ;..put decimal point into output inx h jmp FSTRCODE22 FSTRCODE21 ;..while right count>0 do ldax d adi '0' ;....convert digit to char mov m,a ;....and put into output inx d inx h FSTRCODE22 dcr c ;....decrement right count jp FSTRCODE21 mvi m,'E' ;..put 'E' into output inx h lda Ftemp+1 ;..get exponent sui 40h ;..subtract bias pop b ;..get left count back (in B) sub b ;..subtract it also jp FSTRCODE23 ;..if result negative then mvi m,'-' ;....put '-' into output cma inr a ;....and get absolute value of exponent jmp FSTRCODE24 FSTRCODE23 ;..else exponent positive mvi m,'+' ;....put '+' into output FSTRCODE24 inx h mvi b,'0'-1 mvi c,10 FSTRCODE25 ;..repeat inr b ;....output char ++ sub c ;....exponent -= 10 jnc FSTRCODE25 ;..until exponent < 0 mov m,b ;..put output char into string inx h add c ;..undo last subtraction adi '0' ;..convert to ASCII mov m,a ;..write to output inx h FSTRCODE26 ;HL points to (last loc. used + 1) pop d ;DE has pointer to count byte dcx h call HLMinusDE mov a,l stax d ;store byte count inx d ;desired address = address of first char pop b ;get IP back jmp PUSHDEHL ;---------------------------------- DEPTHnfa DB 85H DB 'DEPT' DB 'H'+80H dw FSTRCODEnfa ; ( -- n ) Returns number of (2-byte) items on stack DEPTH dw DOCOL dw SPAT ;get current stack pointer dw S0,AT ;get address of stack top dw SWAP ;want (top - current) dw MINUS dw ONE,SHR ;divide by 2 dw SEMIS ;---------------------------------- BPLOTnfa DB 85H ;length of 'BPLOT' db 'BPLO','T'+80H dw DEPTHnfa BPLOT ; ( addr count row col -- ) Sends count (0 to 240-col) ; bytes, starting at addr, to LCD row (0-7) and ; column (0-239) dw BPLOTpfa ;this one is machine code BPLOTpfa call CursorOff lhld RP ;first, put IP onto R stack dcx h dcx h shld RP ;reserve space on R stack mov m,c inx h mov m,b ;now, can use all registers safely mvi c,-1 ;set driver count to -1 pop d ;get ColNumber into E mov a,e BPLOT2 ;repeat (until ColNumber goes negative) inr c ;..increment driver count mov e,a ;..save non-negative ColNumber sbi 50 ;..ColNumber := ColNumber - 50 jnc BPLOT2 ;until ColNumber<0 mov l,e ;ColNumber mod 50 temporarily in L pop d ;E has RowNumber mov d,l ;D has ColNumber mod 50 mov a,c ;index into table = offset + (2 * ( col div 50 )) add c mov c,a mvi b,0 lxi h,LCDDriverTable0 mov a,e ;get row number rar ;shift into upper two bits and carry rar rar jnc BPLOT3 ;if lower half of display (RowNumber>=4) then lxi h,LCDDriverTable1 ;..use table for upper half BPLOT3 dad b ;HL has pointer to bytes in table shld TablePointer pop b ;get number of bytes to send (becomes ColsToGo) in BC pop h ;retrieve memory pointer into HL mov b,a ;two high bits determine row mov a,c ;if ColsToGo = 0 then ora a jz BPLOT4 ;..abort operation (no dots to write) BPLOT5 ;repeat (do group of bytes until ColsToGo<=0) call DisableTimeInt call SelectDriver mov a,b ;..get row in LCD half (in bits 6 and 7) ani 0C0h ;..make sure only two high bits active ora d ;..include starting column mov b,a ;..B has select code for LCD mvi a,50 ;..BytesToSend := 50 - FirstCol sub d cmp c ;..if BytesToSend >= ColsToGo then jc BPLOT6 mov a,c ;....BytesToSend := ColsToGo BPLOT6 mov e,a ;..BytesToSend (this time) in E call WriteLCDBytes call EnableTimeInt ;..timer interrupts OK now mvi d,0 ;..StartCol := 0 for any subsequent drivers mov a,c ;..ColsToGo := ColsToGo - BytesToSend sub e mov c,a jz BPLOT4 ;until ColsToGo <= 0 jnc BPLOT5 BPLOT4 lhld RP ;retrieve IP from R stack mov c,m inx h mov b,m inx h shld RP ;release bytes on R stack jmp NEXT SelectDriver ;subroutine to select the proper LCD driver. push h ;save memory pointer lhld TablePointer ;get pointer into select-code table call SelectLCD ;use ROM routine to select driver shld TablePointer ;save new pointer into select-code table pop h ;retrieve memory pointer ret TablePointer dw 0 ;actually, variable storage ;---------------------------------- TASKnfa DB 84H DB 'TAS' DB 'K'+80H dw BPLOTnfa ; ( -- ) Do-nothing word; last before user words dw DOCOL dw SEMIS FENCEInit dw 0 MAXRAM equ 0F5F0h TOSInit equ MAXRAM-482 ;allow from FENCEInit to here for dictionary ;160 bytes (80 entries) allotted for R stack RInit equ MAXRAM-322 ;user area has 64 bytes (32 variables) allotted FIRSTstart equ MAXRAM-258 ;allow 257 bytes for buffer LIMITaddr equ MAXRAM-1 ZROOM equ TOSInit-FENCEInit-384 ;compute for interest's sake END START