copyright 1989-1992 Frank C. Sergeant - see the file PYGMY.TXT. This block file contains source code for PYGMY.COM version 1.4. Here is how to generate a new version of Pygmy: Edit in your changes Type 1 LOAD to create a new kernel named A1.COM (or whatever name you used on block 1). Exit to DOS with BYE Run the new kernel (e.g. C:\>A1 ). Extend the kernel by typing 5 LOAD Simple? You bet! Edit the load blocks to include just the mix of options and extensions you prefer. _Starting Forth_ compatibility tips begin on block 178. All should be thoroughly tested by you before use. ( file PYGMY.SCR for metacompiling PYGMY.COM) 2 LOAD ( set options for kernel) 3 LOAD ( metacompiler) 4 LOAD ( kernel) PRUNE { dRELOC $100 + HERE SAVEM A1.COM } ( scr 5 is load block for editor, assembler, & extensions) ( type 1 LOAD to re-metacompile the kernel, then bring up the kernel and type 5 LOAD to extend it with editor, assembler, etc.) ( set options for PYGMY.COM kernel) 16 CONSTANT TMAX-FILES ( power of 2) -1 CONSTANT TFILES ( allow textfiles) 134 CONSTANT TMAX/LINE ( max line length for textfiles) 4 1- CONSTANT TNB ( number of disk buffers, power of 2) 1 ( 0) CONSTANT STACKSEG ( 0 for same, 1 for higher) $10000 CONSTANT TOP ( ie 65536 or very top of segment) ( note, if STACKSEG is 0, stack offsets must not be higher than TOP 1024 TNB 1+ * - 256 - 256 -) $FFFE ( $ECFE) CONSTANT RSTACK ( stacks grow down from ) $7FFE ( $EBFE) CONSTANT DSTACK ( these offset values ) ( use values in parentheses to put stacks in same segment ) $8000 CONSTANT dRELOC ( address of target image) ( metacompiler load block) 6 LOAD ( conditional compilation) ( 7 LOAD ( variants of LOAD and THRU for more information) 8 18 THRU ( kernel load block) 19 67 THRU TFILES .IF 68 70 THRU .ELSE 71 LOAD .THEN 72 77 THRU TFILES .IF 79 80 THRU .ELSE 78 LOAD .THEN 81 96 THRU ( extensions load block) ( load block for the editor, assembler, & extensions ) $C000 SET-EDGE ( allow for headerless words) 97 98 THRU ( NFA FORGET ) 99 111 THRU ( load the editor) SAVE A2.COM 112 132 THRU ( load the assembler) SAVE A3.COM 133 135 THRU ( other extensions) ' EPSON-CONDENSED ( ' NOP) ( ' LJ-CONDENSED) IS CONDENSED " PYGMY.DOW" 1 UNIT ( on bonus disk) " YOURFILE.SCR" 2 UNIT SAVE A4.COM ( conditional compilation) : ?LOAD ( scr flg -) IF DUP LOAD THEN DROP ; ( conditional) : MATCH? ( a a - f) ( end of input stream counts as a match) OVER C@ IF DUP C@ 1+ COMP 0= ELSE DROP THEN ; : .IF ( f -) 0= IF BEGIN 32 WORD DUP " .ELSE" MATCH? SWAP " .THEN" MATCH? OR UNTIL THEN ; : .ELSE ( -) BEGIN 32 WORD " .THEN" MATCH? UNTIL ; : .THEN ; ( optional versions to give more info while metacompiling) : LOAD ( n -) DUP CR ." loading scr # " . LOAD HERE 6 U.R 5 SPACES .S ; : THRU ( n n -) OVER - 1+ FOR ( n) DUP PUSH LOAD POP 1+ ?SCROLL NEXT DROP ; EXIT : XREF ( -) BASE @ HEX >PRN CR CONTEXT @ HASH BEGIN @ DUP WHILE DUP 2 + DUP dA @ - 5 U.R ( ie print nfa) 2 SPACES COUNT $1F AND TYPE ?SCROLL CR REPEAT DROP CR >SCR BASE ! ; ( cross reference list of nfa and name to printer ) ( initialize target space) VARIABLE RAM VARIABLE H' dRELOC , ( relocation amount ) ( 1st cell is tgt's DP & 2nd cell is tgt's offset) dRELOC $2000 0 FILL dRELOC H' ! ( ie we will start target image at dRELOC) ( meta variables pointing to target runtime code ) VARIABLE TVAR ( variable) VARIABLE TLIT ( literal) VARIABLE TCOL ( docol) VARIABLE TBRA ( branch) VARIABLE T0BR ( zero branch) VARIABLE TEXIT ( EXIT) VARIABLE TFOR ( for) VARIABLE TNEXT ( next) VARIABLE TARR ( array) VARIABLE TABORT ( abort") VARIABLE TDOT ( dot") VARIABLE TNULL ( switch between host & target spaces ) : { dA @ HERE H' 2@ H ! dA ! H' 2! ; : } { ; : RECOVER -2 ALLOT ; ( RECOVERs final EXIT when it can never be reached) ( headers) H/LESS OFF : THEAD ( -) ( this is the basic HEAD without VFA etc) HERE 0 , ( lf) 32 WORD CONTEXT @ 2DUP -FIND NIP NOT IF OVER TYPE$ ." not unique " THEN HASH 2DUP @ ( lfa nfa voc nfa prev.lfa) SWAP 2 - ( lfa nfa voc prev.lfa cur.lfa) ! SWAP ( lfa voc nfa) C@ ( lfa voc len) 1+ ALLOT ! ; : | ( -) H/LESS ON { ; ( make following word headless) ( headers) ( View fields are always created) : HEAD ( -) H/LESS @ IF THEAD $D6 C, ( magic) H' @ , H/LESS OFF { ELSE BLK @ , THEAD THEN ; ( meta compiling words ) HEX : forget ( -) CONTEXT @ HASH @ 2 + DUP C@ 20 XOR SWAP C! ; : CREATE ( -) ( - a) HEAD TVAR @ LJMP, ; : VARIABLE ( -) ( RAM @ CONSTANT 2 RAM +! for ROMing) CREATE 0 , ; : ARRAY ( a -) ( n -) ( n is a word, not byte, index) HEAD TARR @ LJMP, , ; : CODE HEAD ASM-RESET ; : DEFER ( ) ( ...) HEAD 0 #, AX MOV, AX JMP, ; : IS ( pfa -) dA @ - ' 1+ ! ; ( SCAN TRIM CLIP PRUNE to relink dictionary after metacompiling) : SCAN ( lfa - lfa) @ BEGIN DUP 1 dRELOC WITHIN WHILE @ REPEAT ; : TRIM ( lfa new-lfa - new-lfa) DUP PUSH dA @ - SWAP ! POP DUP 2 + DUP C@ $DF AND SWAP C! ( unsmudge) ; : CLIP ( voc-head -) DUP BEGIN DUP SCAN DUP WHILE TRIM REPEAT DROP TNULL @ dA @ - SWAP ! ( @ ,) DROP ; : PRUNE ( -) { 8 HASH CLIP 6 HASH CLIP TNULL @ OFF ( zero out its link field) { ; ( rename some host words ) : FORTH' FORTH ; : COMPILER' COMPILER ; : :' : ; ( LITERAL ] ) COMPILER : LITERAL ( n -) TLIT @ ,A , ; FORTH : ] BEGIN 4 -' ( restrict execution to host's COMPILER) IF 6 -FIND ( restrict finding to target's FORTH ) IF NUMBER \ LITERAL ELSE ,A THEN ELSE EXECUTE THEN AGAIN ; ( meta structures UNTIL AGAIN IF THEN etc ) COMPILER : BEGIN ( - a) HERE ; : UNTIL ( a -) T0BR @ ,A ,A ; : AGAIN ( a -) TBRA @ ,A ,A ; : THEN ( a -) HERE dA @ - SWAP ! ; : IF ( - a) T0BR @ ,A HERE 0 , ; : WHILE ( a - a a ) \ IF SWAP ; : REPEAT ( a a -) \ AGAIN \ THEN ; : ELSE ( a - a) TBRA @ ,A HERE 0 , SWAP \ THEN ; : FOR ( h -) TFOR @ ,A \ BEGIN 0 , ; ( FOR performs body of loop u times, not u+1 times ) : NEXT ( h -) DUP \ THEN 2 + TNEXT @ ,A ,A ; : \ 8 -' ABORT" ?" ,A ; ( F83's [COMPILE] ) FORTH ( more meta compiling words ) COMPILER : ABORT" TABORT @ ,A $22 STRING ; : ." TDOT @ ,A $22 STRING ; : ['] TLIT @ ,A ; FORTH : FORTH 6 CONTEXT ! ; : COMPILER 8 CONTEXT ! ; : : HEAD TCOL @ LJMP, ( lay down 3byte jump to docol) forget ] ; COMPILER' :' ; forget POP DROP TEXIT @ ,A ; ( must be the last colon) ( def in the metacompiler) FORTH' HEX ( start target code boot ) 6 HASH OFF 8 HASH OFF { ( to target) $100 ALLOT ( first 256 bytes reserved for DOS) FORTH ( sets context to 6 ) | CODE boot RSTACK #, BP MOV, ( initialize return stack) DSTACK #, SP MOV, ( initalize parameter stk) 0 #, AX MOV, CS PUSH, BX POP, STACKSEG $1000 * #, BX ADD, BX SS MOV, AX JMP, ( jump to RESET) END-CODE HERE 2 + TNULL ! ( null word $ will get renamed) CODE $ -2 ALLOT 0 C, SWITCH, SI POP, SWITCH, NXT, END-CODE HERE dA @ - RAM ! $32 TNB 1+ 2* + ALLOT ( room for system variables) ( lit array ) | CODE lit ( -n) HERE TLIT ! BX PUSH, ( push TOS to SOS) AX LODS, ( ax <-- [IP], IP++ ) ( get in-line value, not addr) AX BX MOV, ( to TOS) NXT, END-CODE | CODE array ( n -a) HERE TARR ! ( nth word index into array ) 3 #, AX ADD, ( jump over 3 byte JMP) AX BX XCHG, 0 [BX] BX MOV, 1 #, AX SHL, ( multiply by 2 to addr nth word) AX BX ADD, ( now TOS holds addr of nth word of array) NXT, END-CODE ( var 0branch branch ) | CODE var HERE TVAR ! BX PUSH, ( push TOS to SOS) 3 #, AX ADD, ( jump over 3 byte JMP) AX BX MOV, ( put that addr in TOS) NXT, END-CODE | CODE 0branch HERE T0BR ! AX LODS, BX BX TEST, 0=, IF, AX SI MOV, THEN, BX POP, NXT, END-CODE | CODE branch HERE TBRA ! 0 [SI] SI MOV, NXT, END-CODE ( VIEW,LINK,NAME,JMP<var>,VALUE ( 2 2 ? 3 2 (# of bytes in each field) ( docol dodoes ) | CODE docol HERE TCOL ! SWITCH, SI PUSH, SWITCH, 3 #, AX ADD, ( jump over 3 byte JMP to this code ) AX SI MOV, ( put addr of new word list in IP ) NXT, END-CODE | CODE dodoes SWITCH, SI PUSH, SWITCH, SI POP, BX PUSH, 3 #, AX ADD, AX BX MOV, ( addr of parm field) NXT, END-CODE ( runtime FOR - keeps only count on Rstk ) | CODE for HERE TFOR ! SWITCH, BX PUSH, ( save loop count on R stk) SWITCH, BX POP, ( refill TOS ) 0 [SI] SI MOV, ( branch to next to skip loop 1st time) NXT, END-CODE ( runtime NEXT - keeps only count on Rstk ) | CODE next HERE TNEXT ! 1 #, 0 [BP] W-PTR SUB, CS, NOT, IF, ( loop isn't finished ) ( AX LODS, AX SI MOV, ( 18 clocks & 3 bytes) 0 [SI] SI MOV, ( 17 clocks & 2 bytes) NXT, THEN, BP INC, BP INC, ( remove count) SI INC, SI INC, ( skip over back addr) NXT, END-CODE ( EXIT) CODE EXIT HERE TEXIT ! SWITCH, SI POP, ( recover previous IP ) SWITCH, NXT, END-CODE ( System variables RAM allocation - all RAM for now ) RAM @ DUP CONSTANT PREV ( last referenced buffer) 2 + DUP CONSTANT OLDEST ( Oldest loaded buffer ) 2 + DUP ARRAY BUFFERS ( Block in each buffer ) TNB DUP CONSTANT NB ( Number of buffers) 2* + 2 + DUP CONSTANT TIB 2 + DUP CONSTANT H/LESS 2 + DUP CONSTANT HEADERS 2 + DUP CONSTANT SPAN 2 + DUP CONSTANT >IN 2 + DUP CONSTANT BLK 2 + DUP CONSTANT dA 2 + DUP CONSTANT SCR 2 + DUP CONSTANT ATTR 2 + DUP CONSTANT >FIN 2 + DUP CONSTANT FBLK 2 + DUP CONSTANT #TIB 2 + DUP CONSTANT #FIB 2 + DUP CONSTANT FIB 2 + DUP CONSTANT FIBH 2 + DUP CONSTANT EBUF 2 + DUP CONSTANT BASE 2 + DUP CONSTANT H 10 + ( allow room for 4 vocabs ) DUP CONSTANT CONTEXT ( ram+) DROP ( instead of a central docon, CONSTANTS are defined in-line) 0 CONSTANT 0 1 CONSTANT 1 -1 CONSTANT -1 2 CONSTANT 2 ( primitives ) CODE 1+ ( n - n+1) BX INC, NXT, END-CODE CODE 1- ( n - n-1) BX DEC, NXT, END-CODE CODE SP! ( -) DSTACK #, SP MOV, NXT, END-CODE CODE RP! ( -) RSTACK #, BP MOV, NXT, END-CODE ( CS@ locate kernel's code segment ) CODE CS@ ( - seg) BX PUSH, CS PUSH, BX POP, NXT, END-CODE ( P! PC! P@ PC@ access I/O ports ) CODE P! ( n port -) BX DX MOV, AX POP, AX OUT, BX POP, NXT, END-CODE CODE PC! ( c port -) BX DX MOV, AX POP, AL OUT, BX POP, NXT, END-CODE CODE P@ ( port - n) BX DX MOV, AX IN, AX BX MOV, NXT, END-CODE CODE PC@ ( port - c) BX DX MOV, AL IN, AX BX MOV, BH BH SUB, NXT, END-CODE : NOP ( -) ; ( COMP compare two strings ) CODE COMP ( a1 a2 len - -1 | 0 | +1 ; a1<a2=-1;a1=a2=0) SI DX MOV, BX CX MOV, DI POP, SI POP, ( don't test for len 0) DS AX MOV, AX ES MOV, ( don't assume ES is set up) ( Robert Berkey suggests setting zero flag so zero length ok) AX AX SUB, ( set zero flag ) REPZ, AL CMPS, 0=, NOT, IF, U<, IF, -1 #, CX MOV, ELSE, 1 #, CX MOV, THEN, THEN, CX BX MOV, DX SI MOV, NXT, END-CODE ( shifts 2* 2/ ) CODE 2* 1 #, BX SHL, NXT, END-CODE CODE 2/ 1 #, BX SHR, NXT, END-CODE ( unsigned) ( 2/ does not preserve sign bit, it shifts in zeroes ) ( stack operators) CODE DROP ( n -) BX POP, NXT, END-CODE CODE NIP ( a b - b) AX POP, NXT, END-CODE CODE ROT ( n1 n2 n3 - n2 n3 n1 ) AX POP, DX POP, AX PUSH, BX PUSH, DX BX MOV, NXT, END-CODE CODE SWAP ( n1 n2 - n2 n1 ) AX POP, BX PUSH, AX BX MOV, NXT, END-CODE CODE OVER ( n1 n2 - n1 n2 n1) AX POP, AX PUSH, BX PUSH, AX BX MOV, NXT, END-CODE CODE DUP ( n - n n) BX PUSH, NXT, END-CODE CODE ?DUP ( n - n n) BX BX TEST, 0=, NOT, IF, BX PUSH, THEN, NXT, END-CODE CODE 2DUP ( d - d d) AX POP, AX PUSH, BX PUSH, AX PUSH, NXT, END-CODE CODE 2DROP ( d -) BX POP, BX POP, NXT, END-CODE ( math ) CODE + ( n n - n) AX POP, AX BX ADD, NXT, END-CODE CODE +UNDER ( a b c - a+c b) DX POP, AX POP, AX BX ADD, BX PUSH, DX BX MOV, NXT, END-CODE CODE - ( n n - n) BX AX MOV, BX POP, AX BX SUB, NXT, END-CODE CODE NEGATE ( n - -n) ( take two's complement of n) BX NEG, NXT, END-CODE CODE D2* ( l h - l h ) ( multiply double number by 2 ) AX POP, 1 #, AX SHL, AX PUSH, 1 #, BX RCL, NXT, END-CODE ( single operand flag words ) CODE 0= ( n - f) 1 #, BX SUB, BX BX SBB, NXT, END-CODE : NOT 0= ; CODE 0< 1 #, BX SHL, BX BX SBB, NXT, END-CODE ( technique from Andrew McKewan ) ( bit operators) CODE OR ( n n - n) AX POP, AX BX OR, NXT, END-CODE CODE XOR ( n n - n) AX POP, AX BX XOR, NXT, END-CODE CODE AND ( n n - n) AX POP, AX BX AND, NXT, END-CODE ( two operand flag words ) CODE < ( n n - f) AX POP, BX AX SUB, 0 #, BX MOV, <, IF, BX DEC, THEN, NXT, END-CODE ( 62 or 52 cycles - avg 57 cycles & 12 bytes ) CODE > ( n n - f) AX POP, AX BX SUB, 0 #, BX MOV, <, IF, BX DEC, THEN, NXT, END-CODE CODE = ( n n - f) AX POP, BX AX SUB, 1 #, AX SUB, BX BX SBB, NXT, END-CODE CODE U< ( u u - f) AX POP, BX AX SUB, BX BX SBB, NXT, END-CODE ( math ) CODE U/MOD ( u u - r q ) AX POP, DX DX SUB, BX DIV, ( unsigned div) DX PUSH, ( rem) AX BX MOV, ( quot) NXT, END-CODE : U/ ( u u - q) U/MOD NIP ; CODE UM/MOD ( l h u - r q ) DX POP, AX POP, BX DIV, ( unsigned div) DX PUSH, ( rem) AX BX MOV, ( quot) NXT, END-CODE CODE */ ( n1 n2 n3 - n1*n2/n3) AX POP, CX POP, CX IMUL, ( signed) BX IDIV, ( signed) AX BX MOV, NXT, END-CODE CODE * ( n n - n) AX POP, BX IMUL, AX BX MOV, NXT, END-CODE ( math ) CODE / ( n n - q) AX POP, CWD, BX IDIV, AX BX MOV, NXT, END-CODE CODE M* ( n n - d) AX POP, BX IMUL, AX PUSH, DX BX MOV, NXT, END-CODE CODE M/ ( l h n - q ) DX POP, AX POP, BX IDIV, AX BX MOV, NXT, END-CODE : UMOD ( u u - r ) U/MOD DROP ; ( fetch & store ) CODE ! ( n a -) AX POP, AX 0 [BX] MOV, BX POP, NXT, END-CODE CODE N! ( n a - n) AX POP, AX 0 [BX] MOV, AX BX MOV, NXT, END-CODE CODE @ ( a - n) 0 [BX] BX MOV, NXT, END-CODE CODE +! ( n a -) AX POP, AX 0 [BX] ADD, BX POP, NXT, END-CODE CODE C! ( b a -) AX POP, AL 0 [BX] MOV, BX POP, NXT, END-CODE CODE C@ ( a - b) 0 [BX] BL MOV, BH BH SUB, NXT, END-CODE CODE 2@ ( a - d) 2 [BX] PUSH, 0 [BX] BX MOV, NXT, END-CODE CODE 2! ( d a -) AX POP, AX 0 [BX] MOV, AX POP, AX 2 [BX] MOV, BX POP, NXT, END-CODE : ON -1 SWAP ! ; : OFF 0 SWAP ! ; ( CMOVE CMOVE> FILL ) CODE CMOVE ( fr to # - ) CLD, SI DX MOV, BX CX MOV, DI POP, SI POP, DS AX MOV, AX ES MOV, REP, ( BYTE) AL MOVS, BX POP, DX SI MOV, NXT, END-CODE CODE CMOVE> ( fr to # - ) STD, SI DX MOV, BX CX MOV, DI POP, SI POP, DS AX MOV, AX ES MOV, BX DEC, ( BX DEC,) BX SI ADD, BX DI ADD, REP, ( BYTE) AL MOVS, BX POP, DX SI MOV, CLD, NXT, END-CODE CODE FILL ( addr # value -) CLD, CX POP, ( #) DI POP, DS AX MOV, AX ES MOV, BX AX MOV, REP, AL STOS, BX POP, NXT, END-CODE ( return stack operators ) CODE PUSH ( n -) ( same as >R) SWITCH, BX PUSH, SWITCH, BX POP, NXT, END-CODE CODE POP ( - n) ( same as R>) BX PUSH, SWITCH, BX POP, SWITCH, NXT, END-CODE CODE I ( - n) ( same as R@) BX PUSH, 0 [BP] BX MOV, NXT, END-CODE CODE R@ ( - n) BX PUSH, 0 [BP] BX MOV, NXT, END-CODE ( BETWEEN WITHIN ) CODE BETWEEN ( n l h - f) ( true if n l - hi lo - U<= ) AX POP, AX BX SUB, ( h-l is in BX) DX POP, AX DX SUB, ( n-l is in DX) ( BX DX SUB,) DX BX SUB, CMC, BX BX SBB, NXT, END-CODE : WITHIN ( n l h - f) ( true if h-l is U< than n-l ) 1- BETWEEN ; ( n 0 0 works as n 0 65536 - see Robert Berkey) ( ABS MIN MAX EXECUTE ) CODE ABS ( n - u) ( 6 bytes + next ) BX BX TEST, 0<, IF, BX NEG, THEN, NXT, END-CODE CODE MIN ( n n - n) AX POP, AX BX CMP, >, IF, AX BX MOV, THEN, NXT, END-CODE CODE MAX ( n n - n) AX POP, AX BX CMP, <, IF, AX BX MOV, THEN, NXT, END-CODE CODE EXECUTE ( a -) BX AX MOV, BX POP, AX JMP, END-CODE EXIT ( below would be better if we did not keep TOS in a reg.) CODE ABS ( n - u) ( 7 bytes + next) BX AX MOV, CWD, DX BX XOR, DX BX SUB, NXT, END-CODE ( DEFER'd I/O words ) DEFER DEFAULT-EMIT DEFER EMIT DEFER KEY DEFER KEY? DEFER CR DEFER AT DEFER CUR@ DEFER CLS ( BIOS Int $10 video functions ) CODE (AT ( row col -) BL DL MOV, BX POP, BL DH MOV, BX BX SUB, $0200 #, AX MOV, $10 #, INT, BX POP, NXT, END-CODE CODE (CUR@ ( - row col) BX PUSH, BX BX SUB, $0300 #, AX MOV, $10 #, INT, BX BX SUB, DL BL MOV, DL DL SUB, DH DL XCHG, DX PUSH, NXT, END-CODE CODE (EMIT ( c -) BX AX MOV, $0E #, AH MOV, BH BL MOV, $10 #, INT, BX POP, NXT, END-CODE ( BIOS Int $10 video functions ) CODE AT@ ( - aacc) ( read attr & char at current cursor pos) BX PUSH, BX BX SUB, $0800 #, AX MOV, $10 #, INT, AX BX MOV, NXT, END-CODE CODE .ATTR ( # -) ( Write # blanks using ATTR Does not change cursor position) BX CX MOV, ' ATTR 2 + @ ) BX MOV, ( attr in BL) $0920 #, AX MOV, $10 #, INT, BX POP, NXT, END-CODE : (CLS ( -) 0 0 AT 2000 .ATTR ; ' (EMIT DUP IS EMIT IS DEFAULT-EMIT ' (AT IS AT ' (CUR@ IS CUR@ ' (CLS IS CLS HEX ( terminal I/O & DOS & DOS2 ) CODE (KEY ( - c) BX PUSH, 7 #, AH MOV, 21 #, INT, AH AH SUB, AX BX MOV, NXT, END-CODE CODE (KEY? ( - f) BX PUSH, 0B #, AH MOV, 21 #, INT, AL AH MOV, AX BX MOV, NXT, END-CODE CODE (BYE ( -) ( set cursor at bottom of screen & return) $1800 #, DX MOV, BX BX SUB, $0200 #, AX MOV, $10 #, INT, $4C00 #, AX MOV, 21 #, INT, ( exit to DOS) END-CODE CODE DOS ( DX CX BX AX - AX carry) BX AX MOV, BX POP, CX POP, DX POP, 21 #, INT, AX PUSH, BX BX SBB, NXT, END-CODE ( for DOS int 21 services) CODE DOS2 ( DX CX BX AX - DX AX carry) BX AX MOV, BX POP, CX POP, DX POP, 21 #, INT, DX PUSH, AX PUSH, BX BX SBB, NXT, END-CODE ( also for int 21 ) ( ?SCROLL (CR (KEY DECIMAL HEX ) DEFER QUIT : ?SCROLL ( -) KEY? IF KEY $1B = IF SP! QUIT THEN BEGIN KEY? UNTIL KEY $1B = IF SP! QUIT THEN THEN ; : (CR ( -) $0D EMIT $0A EMIT ; : (ONEKEY ( - c) (KEY DUP 0= IF DROP (KEY $80 OR THEN ; ( for the extended keys, set the most significant bit ) ' (ONEKEY IS KEY ' (KEY? IS KEY? ' (CR IS CR : DECIMAL 10 BASE ! ; : HEX 16 BASE ! ; ( C@+ COUNT TYPE TYPE$ -TRAILING SPACE SPACES) CODE C@+ ( a - a+1 c) 0 [BX] AL MOV, BX INC, BX PUSH, BX BX SUB, AL BL MOV, NXT, END-CODE : COUNT ( a - a+1 #) C@+ ; : TYPE ( a # -) FOR C@+ EMIT NEXT DROP ; : TYPE$ ( a -) COUNT TYPE ; : -TRAILING ( a # - a #') FOR DUP I + C@ 32 = WHILE NEXT 0 EXIT THEN POP 1+ ; : -TRAILING<> ( a # c - a #') ROT ROT FOR ( c a) 2DUP I + C@ ( c a c c') - WHILE ( c a) NEXT NIP 0 EXIT THEN ( c a) NIP POP 1+ ; : SPACE 32 EMIT ; : SPACES ( n) 0 MAX FOR SPACE NEXT ; : EXPECT ( a # -) SWAP ( #rem a) OVER PUSH ( #rem a) BEGIN OVER WHILE ( # a) KEY DUP $0D - WHILE ( # a key) DUP 8 = IF DROP ( # a) OVER R@ < IF ( # a) 1- 32 OVER C! ( # a) 1 +UNDER 8 EMIT SPACE 8 EMIT THEN ELSE ( # a key) DUP EMIT OVER C! ( # a) -1 +UNDER 1+ THEN REPEAT DROP SPACE THEN ( # a) DROP POP SWAP - SPAN ! ; EXIT ( you can use QUERY to get input ready for WORD to work on ) ( Numbers ) : HOLD ( ..# x n - ..# x) SWAP PUSH SWAP 1+ POP ; : DIGIT ( n - c) DUP 9 > 7 AND + 48 + ; : <# ( n - ..# n) 0 SWAP ; : #> ( ..# n -) DROP FOR EMIT NEXT ; : SIGN ( ..# n n - ..# n) 0< IF 45 HOLD THEN ; : # ( ..# n - ..# N) BASE @ U/MOD SWAP DIGIT HOLD ; : #S ( ..# n - ..# 0) BEGIN # DUP 0= UNTIL ; : (.) ( n - ..# n) DUP PUSH ABS <# #S POP SIGN ; : .R ( n n -) PUSH (.) OVER POP SWAP - SPACES #> ; : . ( n -) 0 .R SPACE ; : U.R ( u n -) PUSH <# #S OVER POP SWAP - SPACES #> ; : U. ( u -) 0 U.R SPACE ; ( this version takes 293 bytes) ( DUMP DU ) : DUMP ( a - a) BASE @ PUSH HEX CR DUP 5 U.R SPACE 2 FOR 8 FOR C@+ 3 U.R NEXT SPACE NEXT SPACE 16 - 2 FOR 8 FOR C@+ DUP 32 127 WITHIN NOT IF DROP 46 THEN EMIT NEXT SPACE NEXT POP BASE ! ; ( Note, DUMP now saves and restores BASE and automatically displays in hexadecimal.) : DU ( a n -) FOR DUMP ?SCROLL NEXT DROP ; ( Note, DU no longer leaves the next address on the stack) ( HERE abort" dot" ) : HERE ( - a) H @ ; : PAD ( - a) HERE 256 + ; DEFER ABORT | : abort" ( f -) IF ABORT THEN POP COUNT + PUSH ; ' abort" TABORT ! | : dot" ( POP TYPE$ PUSH can't do this w/ current def of TYPE$) POP DUP TYPE$ COUNT + PUSH ; ' dot" TDOT ! | : (") ( - a) POP DUP COUNT + 1+ ( skip over z) PUSH ; ( buffer manager ) | : ADDRESS ( n - a) -1024 * [ TOP 1024 - ] LITERAL + ; ( highest buffer always starts 1024 bytes below TOP) | : ABSENT ( n - n) NB 1+ FOR DUP R@ BUFFERS @ XOR 2* WHILE NEXT EXIT THEN POP PREV N! POP DROP NIP ADDRESS ; | : UPDATED ( - a n) OLDEST @ BEGIN 1+ NB AND ( cheap MOD) DUP PREV @ XOR UNTIL OLDEST N! PREV N! ( buf#) DUP ADDRESS SWAP BUFFERS DUP @ ( a buf# old-blk#) $7FFF ROT ! ( a old-blk#) DUP 0< NOT IF ( a old-blk#) POP DROP DROP THEN ; : UPDATE PREV @ BUFFERS DUP @ $8000 OR SWAP ! ; | : ESTABLISH ( n a - a) SWAP OLDEST @ PREV N! BUFFERS ! ; ( allow multiple block files open at same time ) TMAX-FILES ( 16) 1- CONSTANT MAX-FILES ( must be power of 2) CREATE FILES HERE ( a) TMAX-FILES 1+ 6 * DUP ALLOT ( a #) 0 FILL ( each entry is 6 bytes) ( handle #blocks address-of-name) ( when empty or closed, handle is -1) : >UNIT# ( block# - unit#) 1000 U/ ; : HANDLE ( unit# - a) 6 * FILES + ; : #BLOCKS ( unit# - a) HANDLE 2 + ; : FNAME ( unit# - a) HANDLE 4 + ; : RANGE ( unit# - starting# ending#) DUP 1000 * ( unit# starting#) SWAP #BLOCKS @ OVER + 1- ; ( Disk read/write ) : LBLK ( global-blk# - local-blk# handle) 1000 U/MOD ( rel# unit#) 2DUP #BLOCKS @ ( rel# unit# rel# #blks) U< NOT ( rel# unit# flg) ABORT" bad block# " ( rel# unit#) HANDLE @ ; ( list files & units and their statuses ) : .FILE ( n -) FNAME @ ?DUP IF TYPE$ THEN ; : .FILES ( -) CR ." UNIT 1ST LAST HANDLE FILE" 0 MAX-FILES 1+ FOR ( unit#) CR DUP FNAME @ PUSH DUP 4 .R DUP RANGE ( unit# start# end#) SWAP POP IF 8 .R ELSE DROP 8 SPACES THEN OVER HANDLE @ DUP 0< NOT IF SWAP 8 .R ELSE SWAP DROP 8 SPACES THEN ( unit# handle#) 8 .R DUP 4 SPACES .FILE ( unit#) 1+ NEXT DROP ( ) SPACE ; ( file positioning words) : >EOF ( handle -) ( move current position to end of file) ( HANDLE @ ( handle) 0 0 ROT $4202 DOS ( ax flg) ABORT" >EOF error" DROP ; : POSITION@ ( handle - ud) ( return current file position) ( HANDLE @ ( handle) 0 0 ROT $4201 DOS2 ( h l flg) ABORT" pos error" SWAP ; : >POSITION ( ud handle -) ( move to absolute position) ( HANDLE @) $4200 DOS ( ax flg) ABORT" pos error" DROP ; : >BOF ( handle -) 0 0 ROT >POSITION ; ( "to begin. of file") : +POSITION ( n handle -) PUSH DUP 0< ( sign extend to double) POP ( HANDLE @) $4201 DOS ( ax flg) ABORT" pos error" DROP ; ( go forward or backward relative to current position) ( file handling ) : FCLOSE ( handle -) ( was named HANDLE-CLOSE in ver 1.3) 0 0 ROT ?DUP IF $3E00 DOS THEN 2DROP ; : ?CLOSE ( unit# -) HANDLE DUP PUSH @ FCLOSE ( ) POP ON ; ( try to close it but ignore errors ) : FOPEN ( name - handle flag) 1+ 0 0 $3D02 DOS ( true=error) ; : FMAKE ( name - handle flag) 1+ 0 0 $3C00 DOS ( true=error) ; : ?OPEN ( unit# -) ( no errors reported) DUP ?CLOSE DUP FNAME ( unit# a) @ FOPEN ( unit# handle flag) IF 2DROP ( ) ELSE ( unit# handle) OVER HANDLE N! ( unit# handle) DUP >EOF POSITION@ ( unit# ud) 1024 UM/MOD ( unit# r q) SWAP IF 1+ THEN ( unit# #blks) SWAP #BLOCKS ! ( ) THEN ; ( OPEN? EXISTS? MAKE ?MAKE ) : OPEN? ( unit# - flag) ( true if file is open) DUP HANDLE @ ( 0=) 0< SWAP ( flag unit#) FNAME @ 0= ( flag flag) OR NOT ( flag) ; : EXISTS? ( unit# - flag) DUP ?OPEN DUP OPEN? ( unit# flag) IF ( unit#) HANDLE @ POSITION@ ( ud) OR NOT NOT ( flag) ELSE ( unit#) DROP 0 THEN ; ( this leaves file open, by the way) : MAKE ( unit# -) DUP ?CLOSE DUP FNAME @ ( 1+ 0 0 $3C00 DOS) FMAKE ( unit# handle flag) ABORT" MAKE error" ( unit# h) OVER HANDLE ! ( unit#) ?OPEN ; : ?MAKE ( unit# -) DUP EXISTS? NOT IF MAKE ELSE DROP THEN ; ( file write) : FILE-WRITE ( buf cnt handle# -) ( was named HANDLE-WRITE) OVER PUSH $4000 DOS SWAP POP - OR ABORT" write error" ; : FILE-SIZE ( handle - ud) DUP >EOF POSITION@ ; : SET-FILE-SIZE ( ud handle -) ( ** be careful ** ) DUP PUSH >POSITION 0 0 ( R@) POP FILE-WRITE ( POP ?OPEN ) ; ( above does not reset unit table info) : MORE ( #blks-to-add handle -) ( ** be careful ** ) PAD 1024 32 FILL SWAP OVER >EOF ( handle #blks) FOR ( handle) PAD OVER ( handle a handle) 1024 SWAP ( handle a 1024 handle) FILE-WRITE ( handle) NEXT ( handle) DROP ( ) ; ( file read) VARIABLE #BYTES-READ : EOF? ( - f) #BYTES-READ @ 0= ; : FILE-READ ( buf cnt handle -) $3F00 DOS ABORT" read error" #BYTES-READ ! ; ( Disk read/write ) : CLOSE-FILES ( -) MAX-FILES 1+ FOR I ?CLOSE NEXT ; : RESET-FILES ( -) FILES [ TMAX-FILES ( MAX-FILES 1+) 6 * ] LITERAL 0 FILL CLOSE-FILES ( to set handles to -1 ) ; : OPEN-FILES ( -) CLOSE-FILES 0 ( unit#) MAX-FILES 1+ FOR ( unit#) DUP ?OPEN 1+ NEXT DROP ; ( above opens in ascending order) ( open what's available; don't report errors ) ( block words ) | : buffer ( blk - blk a) UPDATED ( new-blk# a old-dirty-blk#) OVER SWAP $7FFF AND LBLK ( new-blk# a a rel-dirty# handle) PUSH 1024 M* R@ >POSITION ( new# a a) 1024 ( new# a a #) POP ( new# a a # handle) FILE-WRITE ( new# a) ; : BUFFER ( n - a) buffer ESTABLISH ; | : block ( n a - n a) OVER LBLK PUSH 1024 M* R@ >POSITION ( n a) DUP 1024 POP ( n a a # handle) FILE-READ ( n a) ; : BLOCK ( n - a) ABSENT buffer block ESTABLISH ; ( block words ) : FLUSH NB 1+ FOR $7FFF BUFFER DROP NEXT ; : BYE ( -) FLUSH (BYE ; : EMPTY-BUFFERS PREV [ ' NB 2 + @ 3 + 2* ] LITERAL 0 FILL FLUSH ; : COPY ( n1 n2 -) BUFFER UPDATE SWAP BLOCK SWAP 1024 CMOVE FLUSH ; : COPIES ( fr to # -) ( work from high end toward low end) FOR 2DUP R@ + R@ +UNDER COPY NEXT 2DROP ; ( string handling needed by WORD ) CODE -LEADING<> ( a # char - a #) ( eat e/g before 1st matching character) 1 #, AL OR, DS AX MOV, AX ES MOV, BX AX MOV, CX POP, DI POP, REPNZ, AL SCAS, 0=, IF, DI DEC, CX INC, THEN, DI PUSH, CX BX MOV, NXT, END-CODE CODE -LEADING= ( a # char - a #) ( eat leading delimiters) AX AX SUB, ( force zero flg true) DS AX MOV, AX ES MOV, BX AX MOV, CX POP, DI POP, REPZ, AL SCAS, 0=, NOT, IF, DI DEC, CX INC, THEN, DI PUSH, CX BX MOV, NXT, END-CODE ( : -LEADING ( a # - a #) ( eat leading spaces 32 -LEADING= ;) : /STRING ( a # n - a #) OVER MIN DUP PUSH +UNDER POP - ; ( used for textfile loading version ) TMAX/LINE CONSTANT MAX/LINE : READ-LINE ( - a #) ( always read into the FIB ) FIBH @ DUP 0= ABORT" fibh @ is zero" PUSH ( ie save handle) >FIN @ 0 R@ >POSITION ( easy to change this to doubles) FIB @ DUP MAX/LINE POP FILE-READ #BYTES-READ @ ( a #) MAX/LINE OVER > NOT PUSH ( a #) 2DUP 13 -LEADING<> ( ie find 1st cr) ( a # a' #') 0= POP AND ( ie both no cr and not last line) ABORT" line too long" ( a # a') 2 + ( include cr & lf) ROT - ( # len) MIN ( ie don't take more than were read in) DUP >FIN +! FIB @ SWAP ( a #) DUP #FIB ! ; : -CTRL ( a # -) FOR DUP C@ 32 MAX OVER C! 1+ NEXT DROP ; ( used for text-file loading version ) : ?REFILL ( handle - a #) PUSH #FIB @ >IN @ > NOT ( flg) ( ie no unprocessed characters) FIBH @ R@ - ( flg flg) ( ie has handle changed) ( flg flg) OR ( flg) FIB @ SWAP ( a flg) IF ( a) >IN OFF ( we must refill the buffer) BEGIN ( a) DROP ( ) R@ FIBH ! READ-LINE ( a #) ?DUP WHILE ( a #) ( buffer now ends in a cr) 2DUP -CTRL ( a #) -TRAILING ( and then with no blanks) ( a #) DUP #FIB ! ( a #) UNTIL ( a) THEN ( a) THEN ( a) POP DROP #FIB @ ( a #) ; ( used for textfile loading version ) : SOURCE ( - a #) BLK @ ?DUP IF ( blk) BLOCK 1024 ( a #) ELSE FBLK @ ?DUP IF ( handle) ?REFILL ( a #) ELSE TIB @ #TIB @ ( a #) THEN THEN ; ( used for block only loading version ) : SOURCE ( - a #) BLK @ ?DUP IF ( blk) BLOCK 1024 ( a #) ELSE TIB @ #TIB @ ( a #) THEN ; ( HASH WORD) : WORD ( c - a) PUSH SOURCE ( buf rem#) OVER SWAP ( buf buf #) >IN @ /STRING 0 MAX ( ie remaining string) R@ -LEADING= ( buf 1stChr rem#) OVER SWAP ( buf 1stChr 1stChr rem#) POP -LEADING<> ( buf 1stChr LastChr+1 rem#) DROP DUP PUSH ( buf 1stChr LastChr+1) OVER - ( buf 1stChr #) DUP HERE C! HERE 1+ SWAP CMOVE ( buf) POP SWAP - 1+ >IN ! ( ) HERE ( a) ; : HASH ( n - vocab-a) CONTEXT SWAP - ; HEX ( -FIND ) CODE -FIND ( h n - h true | pfa false) SI DX MOV, ( save IP) ' CONTEXT 2 + @ #, DI MOV, BX DI SUB, ( hash) DS AX MOV, AX ES MOV, BX POP, ( 'here') 0 [BX] AL MOV, AH AH SUB, ( cnt) AX INC, DI PUSH, BEGIN, DI POP, 0 [DI] DI MOV, ( get next link addr) DI DI TEST, 0=, IF, BX PUSH, BX BX SUB, BX DEC, DX SI MOV, NXT, THEN, DI PUSH, 2 #, DI ADD, ( move to name field) BX SI MOV, ( here) AX CX MOV, ( reload count) REPZ, AL CMPS, 0=, UNTIL, ( fall thru occurs when count is all used up and ) ( the last compare was still equal, now check if indirect ) AX POP, $D6 #, 0 [DI] CMP, ( is 1st byte of pfa magic #?) 0=, IF, ( get indirect addr) DI INC, 0 [DI] DI MOV, THEN, DI PUSH, ( the pfa) BX BX SUB, ( the flag) DX SI MOV, NXT, END-CODE ( Number input ) ( see Ting's version of -DIGIT that leaves a flag) : -DIGIT ( n - n) $30 - DUP 9 > IF 7 - DUP $A < OR THEN DUP BASE @ U< NOT ABORT" ?" ; | : 10*+ ( u a n - u a) ( multiplies number by BASE & adds digit) -DIGIT ROT BASE @ * + SWAP ; ( Number input ) DEFER NUMBER : (SNUMBER ( a # - n) BASE @ PUSH OVER C@ $2D = DUP PUSH IF 1- 1 +UNDER THEN OVER C@ $24 ( $) = IF HEX 1- 1 +UNDER THEN OVER C@ $27 ( ') = IF DROP 1+ C@ ( character value) ELSE 0 ( a # 0 ) ROT ROT ( 0 a #) FOR ( u a ) DUP C@ ( u a n) 10*+ ( u a) 1+ NEXT DROP THEN POP IF NEGATE THEN POP BASE ! ; ( above allows $FF and 'a type literals ) : SNUMBER ( a - n) COUNT ( a #) (SNUMBER ; ' SNUMBER IS NUMBER ( Control ) : -' ( u - here t | pfa f) 32 WORD SWAP -FIND ; : ' ( - pfa) CONTEXT @ -' ABORT" ?" ; : INTERPRET ( blk# offset -) >IN 2! BEGIN 2 -' ( search FORTH) IF NUMBER ELSE EXECUTE THEN AGAIN ; RECOVER : QUERY ( -) TIB @ 255 EXPECT SPAN @ #TIB ! 0 0 >IN 2! ; : (QUIT RP! BEGIN CR QUERY 0 0 ( blk offset) INTERPRET ." ok" AGAIN ; RECOVER ' (QUIT IS QUIT ( default ABORT allows textfiles ) FORTH : (ABORT ( -) ['] DEFAULT-EMIT 1+ @ ['] EMIT 1+ ! FBLK OFF FIBH @ FCLOSE FIBH OFF HERE TYPE$ SPACE POP POP TYPE$ SP! BLK @ ?DUP DROP QUIT ; RECOVER ' (ABORT IS ABORT ( LOAD THRU blocks only ) : LOAD ( u -) >IN 2@ PUSH PUSH ( >FIN 2@ PUSH PUSH 0 0 >FIN 2! ) 0 INTERPRET 10 BASE ! ( POP POP >FIN 2! ) POP POP >IN 2! ; : THRU ( u u -) OVER - 1+ FOR ( n) DUP PUSH LOAD POP 1+ NEXT DROP ; ( LOAD THRU allows text files ) : LOAD ( n -) >IN 2@ PUSH PUSH >FIN 2@ PUSH PUSH 0 0 >FIN 2! 0 INTERPRET 10 BASE ! POP POP >FIN 2! POP POP >IN 2! ; : THRU ( n n -) OVER - 1+ FOR ( n) DUP PUSH LOAD POP 1+ NEXT DROP ; ( source code loading from text files ) : FLOAD ( name -) ( ie " UTILITY.TXT" FLOAD ) >IN 2@ PUSH PUSH >FIN 2@ PUSH PUSH 0 0 >FIN 2! ( name) FOPEN ( handle flag) ABORT" file?" ( handle) FBLK ! ( ) 0 0 INTERPRET ( ) 10 BASE ! FBLK @ FCLOSE FIBH OFF POP POP >FIN 2! POP POP >IN 2! ; : INCLUDE ( -) ( eg INCLUDE options.txt ) 32 WORD 0 OVER COUNT + C! ( a) FLOAD ; ( CLEAR LIST ) : (LIST ( n -) BLOCK ( n a) 16 FOR CR DUP 64 TYPE 64 + NEXT DROP CR ; : LIST ( n -) SCR N! DUP CR ." scr " U. SPACE DUP >UNIT# .FILE (LIST ; : CLEAR ( n -) BLOCK 1024 32 FILL UPDATE ; ( compiling ) : ALLOT ( n -) H +! ; : , ( n -) H @ ! 2 ALLOT ; : C, ( c -) H @ C! 1 ALLOT ; : ,A ( a -) dA @ - , ; : COMPILE POP DUP @ , 2 + PUSH ; COMPILER DEFER LITERAL : SLITERAL ( n - ) COMPILE lit , ; ' SLITERAL IS LITERAL : [ POP DROP ; FORTH : ] BEGIN 4 -' IF 2 -FIND IF NUMBER \ LITERAL ELSE ,A THEN ELSE EXECUTE THEN AGAIN ; RECOVER ( compiling ) : PREVIOUS ( - a n) CONTEXT @ HASH @ 2 + DUP C@ ; : SMUDGE PREVIOUS $20 XOR SWAP C! ; ( flip bit 5 of len byte) : COMPILER 4 CONTEXT ! ; : FORTH 2 CONTEXT ! ; | : does PREVIOUS + 1+ ( to pfa) $E9 OVER C! 1+ DUP POP SWAP 2 + - SWAP ! ( jump to parent's call to dodoes) ; COMPILER : ['] COMPILE lit ; : DOES> COMPILE does $E8 C, ( call) ['] dodoes HERE 2 + - , ; : RECURSIVE PREVIOUS $0DF AND SWAP C! ; : ; \ RECURSIVE POP DROP COMPILE EXIT ; FORTH ( allows headerless words even when not metacompiling ) VARIABLE EDGE VARIABLE H' 0 , ( always zero when not metacompiling ) ( EDGE, ie the edge of the world the headers fall off of, and H' must be set prior to using { or } or HEADERS OFF, e.g. $C000 SET-EDGE. The relocation factor should always be zero. Headers remain visible until PRUNE'd.) : SET-EDGE ( a -) DUP EDGE ! H' ! ; ( e.g. $C000 SET-EDGE ) : { dA @ HERE H' 2@ H ! dA ! H' 2! ; : } { ; : | ( -) H/LESS ON ; ( This block allows relocating the dictionary when not metacompiling. The headers are visible until PRUNE unlinks them.) ( e.g. { NEW-ASSEMBLER LOAD } <use assembler> PRUNE ) | : SCAN ( lfa - lfa) @ BEGIN DUP EDGE @ -1 WITHIN WHILE @ REPEAT ; | : TRIM ( lfa new-lfa - new-lfa) DUP ROT ! ; | : CLIP ( voc-head -) BEGIN DUP SCAN DUP WHILE TRIM REPEAT 2DROP ; : PRUNE ( -) EDGE @ H' @ - IF 4 HASH CLIP 2 HASH CLIP EDGE @ H' ! THEN ; ( (HEAD ) : (HEAD ( -) BLK @ , ( vf) HERE 0 , ( lf) 32 WORD CONTEXT @ 2DUP -FIND NIP NOT IF OVER TYPE$ ." not unique " THEN HASH 2DUP @ ( lfa nfa voc nfa prev.lfa) SWAP 2 - ( lfa nfa voc prev.lfa cur.lfa) ! SWAP ( lfa voc nfa) C@ ( lfa voc len) 1+ ALLOT ! ; ( HEAD allows making individual words headerless with | and allows making whole sections headerless with HEADERS OFF ....... HEADERS ON ) : HEAD ( -) HEADERS @ H/LESS @ NOT AND IF (HEAD ELSE { (HEAD $D6 C, ( magic) H' @ , } THEN H/LESS OFF ; ( Defining words ) FORTH : CREATE HEAD $E9 C, ( JMP instr) lit var HERE 2 + - , ; : : HEAD $E9 C, lit docol HERE 2 + - , SMUDGE ] ; : CONSTANT ( n) HEAD $53 C, $BB C, , $AD C, $E0FF , ; ( 7 byte 46 cyc "in-line" vs 5 byte 86 cyc "central" docon ) : VARIABLE ( -) CREATE 0 , ; : CRASH ( -) -1 ABORT" no vector " ; RECOVER : DEFER ( -) HEAD $B8 C, COMPILE CRASH $E0FF , ; : IS ( a-) ' 1+ ! ; ( WORDS .S debugger .ID STRING ) : WORDS CR CONTEXT @ HASH BEGIN @ DUP WHILE DUP 2 + COUNT 31 AND TYPE 2 SPACES ?SCROLL REPEAT DROP ; CODE DEPTH ( - words) BX PUSH, SP BX MOV, HEX DSTACK 2 - #, BX SUB, BX NEG, 1 #, BX SAR, NXT, END-CODE DECIMAL : .S ( -) DEPTH DUP 0< ABORT" underflow " ?DUP IF DUP FOR POP ROT PUSH PUSH NEXT FOR POP POP DUP U. SWAP PUSH NEXT ." <top " ELSE ." stack empty " THEN ; : ? @ . ; : STRING ( delim -) WORD C@ 1+ ALLOT ; ( file names UNIT ) : FILE-NAME: ( ) ( -a) CREATE 32 STRING 0 C, ; : UNIT ( name unit# -) ( e.g. " SUPPL.SCR" 2 UNIT ) DUP ?CLOSE ( name unit#) FNAME ! ; : OPEN ( name unit# -) DUP PUSH UNIT ( ) R@ FNAME @ 0= ABORT" no name " R@ ?OPEN POP HANDLE @ 0< ABORT" OPEN err " ; EXIT ( examples) NAMEZ: PYGMY.SCR FILE-NAME: F3 ASM.SCR ( name unit# ) PYGMY.SCR 0 OPEN F3 1 OPEN " SUPPL.SCR" 2 OPEN ( SAVEM & SAVE for .COM files or memory images) : SAVEM ( fr to -) ( follow with file name) OVER - 1+ ( buf cnt) $20 WORD DUP C@ OVER + 1+ 0 SWAP C! ( buf cnt name) FMAKE ( buf cnt handle flag) ABORT" file?" DUP PUSH FILE-WRITE ( ) POP FCLOSE ; : SAVE ( -) ( follow w/ file name) PRUNE $100 HERE 1- SAVEM ; ( Structures ) COMPILER : \ 4 -' ABORT" ?" ,A ; : BEGIN ( - a) H @ ; : UNTIL ( a -) COMPILE 0branch ,A ; : AGAIN ( a -) COMPILE branch ,A ; : THEN ( a -) H @ dA @ - SWAP ! ; : IF ( - a) COMPILE 0branch H @ 0 , ; : WHILE ( a - a a ) \ IF SWAP ; : REPEAT ( a a -) \ AGAIN \ THEN ; : ELSE ( a - a) COMPILE branch H @ 0 , SWAP \ THEN ; : FOR ( - h) COMPILE for \ BEGIN 0 , ; : NEXT ( h -) DUP \ THEN 2 + COMPILE next ,A ; ( Strings ) HEX COMPILER : ABORT" COMPILE abort" 22 STRING ; : ." COMPILE dot" 22 STRING ; : ( 29 WORD DROP ; : IS ( pfa ) ' 1+ \ LITERAL COMPILE ! ; : " ( -) COMPILE (") 22 STRING 0 C, ( asciiz for files) ; FORTH : ( \ ( ; : ." 22 WORD TYPE$ ; forget : " ( - a) HERE '" STRING 0 C, ; ( embed the string in the dictionary ) ( (BOOT normal opening screen ) DEFER BOOT : (BOOT ( $1F ATTR ! CLS ( to force color to white on blue) CR ." PYGMY Forth v1.4 copyright 1989-1992 by Frank Sergeant" CR OPEN-FILES .FILES CR ." hi" QUIT ; RECOVER ' (BOOT IS BOOT ( RESET ) : RESET NB ADDRESS 256 - DUP TIB ! ( 256) 258 - FIB ! HEADERS ON H/LESS OFF >IN OFF dA OFF 10 BASE ! 0 0 AT AT@ 256 U/ ATTR ! CLS EMPTY-BUFFERS FORTH BOOT ; RECOVER ( final block of kernel ) ' RESET dA @ - ' boot 7 + ! ( patch) " PYGMY.SCR" dA @ - ' FILES 7 + ! ( ie " PYGMY.SCR" 0 UNIT) 6 HASH @ dA @ - ' CONTEXT 2 + @ dA @ - 2 - ! 8 HASH @ dA @ - ' CONTEXT 2 + @ dA @ - 4 - ! HERE dA @ - ' H 2 + @ dA @ - ! ( ie initialize target's dict. ptr) } ( to host ) ( Start of Extensions) ( NFA "works" with headerless words) : NFA ( pfa - nfa | 0) 2 FOR I 1+ 2* HASH BEGIN @ ( pfa lfa) ?DUP WHILE 2DUP 2 + C@+ $1F AND + DUP C@ $D6 = IF 1+ @ THEN ( pfa lfa pfa candidate-pfa) = UNTIL 2 + NIP POP DROP EXIT THEN NEXT DROP 0 ; ( FORGET ) : FORGET ( -) ' NFA 2 - ( lfa) DUP PUSH ( ie save the new HERE ) @ ( prev-lfa) ( ie will be the new top word in current vocab) 2 4 CONTEXT @ OVER = IF SWAP THEN ( current-lfa current-vocab other-vocab) DUP HASH @ ( ie top lfa of other vocab) BEGIN R@ OVER U< WHILE @ REPEAT ( ie walk back until lfa is before the new HERE ) SWAP HASH ! ( current-lfa current-vocab) HASH ! ( ) POP ( new-HERE) 2 - ( ie adjust for view field) H ! ; ( usage FORGET TST ) ( Editor ) | VARIABLE INS ( insert or overwrite flag) | VARIABLE XIN | VARIABLE #CUTS : CLAMP ( n lo hi - n') PUSH MAX POP MIN ; | : .H ( -) CUR@ 0 0 AT ." scr # " SCR @ DUP . >UNIT# .FILE ." find(3,1) rep(4,2) del(5) join(6) cut(7,8) " INS @ IF ." i c=" ELSE ." c=" THEN #CUTS ? AT ; | : L1 ( -) SCR @ BLOCK EBUF ! .H ; | : L2 ( -) CUR@ 1 0 AT EBUF @ 64 FOR 45 EMIT NEXT CR 16 FOR 64 FOR C@+ EMIT NEXT ." |" CR NEXT DROP ( ) 64 FOR 45 EMIT NEXT AT ; : L ( -) L1 L2 ; ( Editor ) | : A>B ( a - a) ( rel-addr to buffer addr) EBUF @ + ; | : CK-CUR ( -) XIN @ 0 MAX $3FF MIN XIN ! ; | : SET-CUR ( -) CK-CUR XIN @ 64 U/MOD 2 + SWAP AT ; | : S! ( c -) DUP XIN @ A>B C! EMIT 1 XIN +! UPDATE ; | : L>A ( line# - a) 64 * ; | : A>L ( a - line#) 64 / ; | : (B>B) ( fr to # - fr' to' #) ROT EBUF @ + ROT EBUF @ + ROT 0 MAX UPDATE ; | : B>B ( fr to # -) (B>B) CMOVE> ; | : B<B ( fr to # -) (B>B) CMOVE ; ( Editor ) | : X ( - pos) ( x= 0..63) XIN @ 63 AND ; | : #REM ( - #) 64 X - ; | : .EOL ( -) SET-CUR XIN @ ( a) A>B ( a') CUR@ ROT #REM FOR C@+ EMIT NEXT DROP AT ; | : >BEG ( a - a) $FFC0 AND ; | : >END ( a - a) 63 OR ; ( Editor ) | : BLANK ( a # -) SWAP A>B SWAP 32 FILL ; | : INSERT ( c -) SET-CUR XIN @ DUP 1+ ( c from to ) #REM 1- ( ie cnt) B>B ( c) S! X IF .EOL THEN ; | : DELETE ( -) SET-CUR XIN @ ( a) DUP DUP 1+ SWAP #REM 1- B<B ( a) >END 1 BLANK ( ) .EOL ; | : SPREAD ( l# -) L>A DUP 64 + 16 L>A OVER - B>B ; | : SPLIT ( -) XIN @ A>L 15 < IF XIN @ DUP DUP A>L 1+ DUP SPREAD ( a a l#) L>A DUP 64 BLANK ( a a a) #REM B>B ( a a) #REM BLANK ( ) XIN @ >BEG 64 + XIN ! SET-CUR L THEN ; ( Editor HOLES ) | : HOLES ( -) 19 0 2DUP AT ( y x) 80 SPACES AT ( ) ." how many holes? " ( 0 0 >IN 2!) QUERY ( TIB @ 4 EXPECT) 0 WORD NUMBER 0 50 CLAMP ?DUP ( 0 | u u) IF ( u) #CUTS OFF ( u) SCR @ >UNIT# ( u unit#) DUP RANGE PUSH ( save end# for later) DROP ( u unit#) 2DUP ( u unit# u unit#) HANDLE @ MORE ( u unit#) ?OPEN ( u) ( POP SWAP PUSH PUSH ) ( u) ( Rstk: end#) ( u) SCR @ ( #ins aft#) 2DUP ( #ins aft# #ins aft#) POP OVER - ( ie #above-insert-pt) PUSH ( #ins aft# #ins aft#) ( Rstk: unit# #above) 1+ ( ie 1st-scr-to-move) DUP ROT + POP COPIES ( #ins aft#) SWAP FOR ( aft#) 1+ DUP CLEAR NEXT DROP ( ) FLUSH L THEN ; ( Editor ) | : DEL-LN ( -) XIN @ >BEG DUP 64 + SWAP ( fr to) 15 L>A DUP PUSH OVER - ( fr to #) B<B POP 64 BLANK L ; | : JOIN ( -) XIN @ A>L 15 < IF XIN @ ( a) DUP 64 + >BEG DUP PUSH SWAP #REM B>B ( ) R@ DUP #REM + SWAP X B<B ( left justify) ( ) POP X + #REM BLANK L THEN ; | : CUT ( -) XIN @ >BEG A>B ( fr) #CUTS @ 64 * HERE + 256 + ( to) 64 CMOVE 1 #CUTS +! 64 XIN +! L ; | : UNCUT ( -) #CUTS @ ?DUP IF HERE 256 + DUP ( fr) XIN @ >BEG A>B ( to) 64 CMOVE ( # to) DUP 64 + ( fr) SWAP ROT 1- #CUTS N! 64 * ( #) CMOVE 64 XIN +! UPDATE L THEN ; ( Editor ) | VARIABLE SLEN ( holds len of following string) 1 SLEN ! | VARIABLE S$ 64 ALLOT 32 S$ ! ( default is a space) | : -SRCH ( - flg) XIN @ A>B ( a) 1024 XIN @ - FOR ( do it up to 1024 times) DUP S$ SLEN @ COMP WHILE 1+ NEXT -1 ( not found) ELSE POP DROP SLEN @ + 0 ( found) THEN SWAP EBUF @ - XIN ! ; | : SRCH ( -) -SRCH DROP ; | : SET$ ( -) 19 1 2DUP AT 80 SPACES ( y x) AT ." enter search string " S$ 64 EXPECT SPAN @ SLEN ! ( SPAN OFF) ." ok " SRCH ; | : SRCHX ( -) SCR @ >UNIT# RANGE PUSH DROP ( Rstk: end#) BEGIN ( ) ?SCROLL -SRCH ( flg) SCR @ R@ < ( flg flg) AND ( flg) WHILE ( ) 1 SCR +! XIN OFF L1 REPEAT POP DROP L2 ; ( Editor ) | VARIABLE RLEN ( holds len of following string) RLEN OFF | VARIABLE R$ 64 ALLOT ( default is null) | : REPL ( -) SET-CUR RLEN @ IF SLEN @ ( #) DUP NEGATE XIN +! FOR DELETE NEXT ( ) R$ RLEN @ FOR C@+ INSERT NEXT DROP THEN ; | : SETR$ ( -) 20 0 2DUP AT 80 SPACES ( y x) AT ( ) ." enter replace string " R$ 64 EXPECT SPAN @ RLEN ! ( SPAN OFF) ." ok " REPL ; ( Editor ) | : ?BUMP ( block-increment -) SCR @ DUP PUSH + ( scr') POP 2DUP >UNIT# RANGE ( scr' scr scr' 1st last) BETWEEN IF SWAP THEN SCR ! DROP L XIN OFF ; | : PgUp ( -) -1 ?BUMP ; | : PgDn ( -) 1 ?BUMP ; | : -INS INS @ NOT INS ! .H ; | : Rt 1 XIN +! ; | : Lt -1 XIN +! ; | : Up -64 XIN +! ; | : Dn 64 XIN +! ; | : Home ( -) ( move to beginning of line or to top of screen) X ?DUP IF NEGATE ELSE -1024 THEN XIN +! ; | : End ( -) ( move just past last chr on line) XIN @ >END A>B BEGIN DUP C@ 32 = WHILE 1- REPEAT EBUF @ - 1+ XIN @ >BEG MAX XIN ! ; ( Editor ) | CREATE MATES 0 , 0 , ( room for two block numbers) | : MARK ( -) SCR @ MATES DUP @ OVER 2 + ! ! ; | : ALTERNATE ( -) SCR @ PUSH MATES 2@ = IF 1000 R@ >UNIT# 1 AND ( odd?) IF NEGATE THEN ( rel) ELSE MATES 2@ - ABS NEGATE R@ DUP MATES 2@ PUSH U< SWAP POP U< OR IF ABS THEN ( rel) THEN ( rel) POP SWAP OVER + ( old new) DUP DUP >UNIT# RANGE BETWEEN IF SWAP THEN DROP SCR ! L XIN OFF ; ( Use Ctrl-A to alternate between shadow blocks, use Alt-A to mark the current block as one of the base blocks. ) ( Editor SPCL uses (ONEKEY codes ) : ', ( -) ' , ; | CREATE SPCL' 205 C, ', Rt 203 C, ', Lt 200 C, ', Up 208 C, ', Dn 199 C, ', Home 207 C, ', End 201 C, ', PgUp 209 C, ', PgDn 210 ( Ins) C, ', -INS 211 ( Del) C, ', DELETE 187 ( F1) C, ', SRCH 188 ( F2) C, ', REPL 189 ( F3) C, ', SET$ 190 ( F4) C, ', SETR$ 191 ( F5) C, ', DEL-LN 192 ( F6) C, ', JOIN 193 ( F7) C, ', CUT 194 ( F8) C, ', UNCUT 195 ( F9) C, ', HOLES 196 ( F10) C, ', SRCHX 1 ( Ctrl-A) C, ', ALTERNATE 158 ( Alt-A) C, ', MARK | : SPCL ( n -) SPCL' 22 FOR 2DUP C@ - WHILE 3 + NEXT 2DROP ELSE SWAP POP 2DROP ( a) 1+ @ EXECUTE THEN ; ( ED ) : BEEP 7 EMIT ; : ED ( -) DECIMAL XIN OFF CLS L BEGIN SET-CUR KEY DUP 27 - WHILE ( not ESC) DUP 08 = IF DROP XIN @ IF -1 XIN +! DELETE THEN ELSE DUP 13 = IF DROP SPLIT ELSE DUP 32 127 WITHIN IF ( regular-key) INS @ IF INSERT ELSE S! THEN ELSE SPCL THEN THEN THEN REPEAT DROP 19 0 AT ; : EDIT ( n -) INS OFF DUP BLOCK DROP SCR ! ED ; ( SETTLE let heavy blocks settle to the bottom of the range) : HEAVY? ( blk# - f) BLOCK 1024 -TRAILING NIP ; : SETTLE ( 1st last -) OVER - OVER SWAP ( 1st 1st #) 0 MAX FOR ( from to) 1 +UNDER OVER HEAVY? OVER HEAVY? NOT AND IF ( from to) 2DUP COPY OVER CLEAR 1+ ELSE DUP HEAVY? IF 1+ THEN THEN NEXT 2DROP ; : CHOP ( unit -) ( truncate ending blank blocks) FLUSH DUP DUP RANGE SWAP PUSH ( unit unit hi-blk#) ( Rstk: start#) BEGIN DUP HEAVY? NOT WHILE 1- REPEAT 1+ POP - ( unit unit #blks-to-keep) 1024 M* ROT HANDLE @ SET-FILE-SIZE ( unit) ?OPEN ; ( assembler control words ) VARIABLE DISP VARIABLE FLAGS ( xxxxxxccOMIAGSDW ) : ASM-RESET ( -) 2 FLAGS ! ( D on is default) DISP OFF ; : IF, ( opcode - a) C, HERE 0 C, ( save room for offset ) ; : WHILE, ( a1 opcode - a2 a1) IF, SWAP ; : NOT, ( opcode - opcode') 1 XOR ; : THEN, ( a -) HERE OVER 1+ - SWAP C! ; : ELSE, ( a - a') $EB ( ie intra-seg dir short jmp) C, HERE OVER - SWAP C! HERE 0 C, ; : BEGIN, ( - a) HERE ; : UNTIL, ( a opc -) C, HERE 1+ - C, ; : CODE HEAD ASM-RESET ; : END-CODE ; ( it doesn't need do anything in Pygmy) HEX ( relative jumps ) | : opc ( opcode -) ( - opcode) CREATE C, DOES> C@ ; 73 opc CS, 75 opc 0=, 79 opc 0<, 73 opc U<, E3 opc CXNZ, 7D opc <, 7E opc >, 76 opc U>, ( 71 opc OV, ) ( the rest can be made by following above with NOT, ) : LOOP, ( a -) E2 UNTIL, ; : LOOPZ, ( a -) E1 UNTIL, ; : LOOPNZ, ( a -) E0 UNTIL, ; HEX ( bit-flags and reg seg & r/m defining words ) ( VARIABLE DISP VARIABLE FLAGS ( xxxxxxccOMIAGSDW ) ( M=r/m; cc=reg count; I=immediate; A=accumulator; G=seg;) ( S=imm.size; D=direction; W=word or byte; O=disp only ) | : F-SET ( mask -) FLAGS @ OR FLAGS ! ; | : F-CLR ( mask -) -1 XOR FLAGS @ AND FLAGS ! ; | : F-GET ( mask -) FLAGS @ AND ; | : F-FLIP ( mask - ) FLAGS @ XOR FLAGS ! ; | : <reg> ( a - n) DUP 1+ C@ DUP 1 AND 1 XOR 2* 2* OR F-SET C@ ; | : reg ( 000a000w00rrr000 -) ( - 0000000000rrr000) CREATE , DOES> <reg> 100 FLAGS +! ( count regs) 2 F-FLIP ; | : seg ( n -) ( -n) CREATE , DOES> <reg> 2 F-SET ; | : r/m ( n -) ( disp - n) CREATE , DOES> <reg> 2 F-CLR ( D) SWAP DISP ! ; ( default D is on, r/m clears it, reg flips it, seg sets it) ( D=0 when r/m field is destination ) HEX ( R/M & REG are 16bit constants, but reg keeps count ) 4000 r/m [BX+SI] 4001 r/m [BX+DI] 4002 r/m [BP+SI] 4003 r/m [BP+DI] 4004 r/m [SI] 4005 r/m [DI] 4006 r/m [BP] 4007 r/m [BX] C006 r/m ) ( chg this?) ( bits 3-5=reg, bit 8=W, bit 9=D flg, bit 12=ACC flg ) 1100 reg AX 0108 reg CX 0110 reg DX 0118 reg BX 0120 reg SP 0128 reg BP 0130 reg SI 0138 reg DI 1000 reg AL 0008 reg CL 0010 reg DL 0018 reg BL 0020 reg AH 0028 reg CH 0030 reg DH 0038 reg BH 0900 seg ES 0908 seg CS 0910 seg SS 0918 seg DS | CREATE F$ 4457 , 4753 , 4941 , 4F4D , : 2^ ( n - 2^n) 1 SWAP FOR 2* NEXT ( 2/) ; | : .F ( -) FLAGS @ 8 FOR R@ 2^ F-GET IF F$ R@ + C@ ELSE 20 THEN EMIT NEXT 100 / 3 U.R ." regs " ; HEX ( REG>R/M #, orW 11mod 01mod 10mod ,DISP BYTE ) | : R>M ( reg -r/m) 2/ 2/ 2/ ; | : 1REG? 100 F-GET ; | : SHORT? ( n - f) -80 80 WITHIN ; : #, ( n1 - n1) 20 OVER SHORT? 04 AND OR F-SET ; | : orW ( --opc--- - --opc--w) 1 F-GET OR ; | : orDW ( --opc--- - --opc-dw) 3 F-GET OR ; | : modDISP, ( 2nd - ) 40 F-GET ( ie M) IF 80 F-GET ( ie Only) IF C, DISP @ , ELSE 8 F-GET ( ie G) DISP @ OR OVER 7 AND 6 = OR ( ie[BP]) IF DISP @ SWAP OVER SHORT? IF 40 OR C, C, ELSE 80 OR C, , THEN ELSE ( zero & not seg) C, THEN THEN ELSE C0 OR C, THEN ; | : IMM? ( -f) 20 F-GET ; : ACC? ( -f) 10 F-GET ; | : ,IMM ( n -) 5 F-GET 4 = IF ( S,-W) C, ELSE , THEN ; : W-PTR ( -) 1 F-SET ; ( the default is byte ) | : 2REGS? ( -f) 308 F-GET DUP 200 = SWAP 108 = OR ; ( one byte opcodes with no variables ) HEX | : M1 ( n -) ( -) CREATE , DOES> @ C, ASM-RESET ; 98 M1 CBW, F8 M1 CLC, FC M1 CLD, FA M1 CLI, F5 M1 CMC, 99 M1 CWD, CF M1 IRET, 90 M1 NOP, C3 M1 RET, CB M1 LRET, F9 M1 STC, FD M1 STD, FB M1 STI, D7 M1 XLAT, F3 M1 REP, F3 M1 REPZ, F2 M1 REPNZ, 9C M1 PUSHF, 9D M1 POPF, ( 2 operand instructions such as ADD, ) HEX | : M2 ( n -) ( various - ) CREATE , DOES> @ PUSH IMM? IF ACC? IF DROP POP orW 4 OR C, ELSE 1REG? IF R>M THEN 80 orW C, POP 38 AND OR modDISP, THEN ,IMM ELSE 2REGS? IF SWAP R>M THEN POP orDW C, OR modDISP, THEN ASM-RESET ; HEX ( use M2 to define ADD, like instructions ) 10 M2 ADC, 00 M2 ADD, 20 M2 AND, 38 M2 CMP, 08 M2 OR, 18 M2 SBB, 28 M2 SUB, 30 M2 XOR, HEX ( MOV, ) : MOV, IMM? IF 1REG? IF R>M B0 OR 1 F-GET 2* 2* 2* OR C, ELSE C6 orW C, modDISP, THEN ,IMM ELSE 90 F-GET 90 = IF 2DROP A0 2 F-FLIP orDW C, DISP @ , ELSE 2REGS? IF 2 F-GET ( ie D) IF SWAP THEN R>M THEN 8 F-GET ( ie G) IF 1 F-CLR 8C ELSE 88 THEN orDW C, OR modDISP, THEN THEN ASM-RESET ; ( one byte instr w/ W - the string instructions ) HEX | : M3 ( n -) ( reg -) CREATE , DOES> @ orW C, DROP ASM-RESET ; A6 M3 CMPS, AC M3 LODS, A4 M3 MOVS, AE M3 SCAS, AA M3 STOS, ( mul, div, etc. xxxxxxxW mdNNNr/m ) HEX | : M4 ( n -) ( -) CREATE , DOES> @ F6 orW C, SWAP 1REG? IF R>M THEN OR modDISP, ASM-RESET ; 30 M4 DIV, 38 M4 IDIV, 28 M4 IMUL, 20 M4 MUL, 18 M4 NEG, 10 M4 COM, ( NOT, is the the Intel name for my COM, but it would conflict w/ my flag inverter which I call NOT, ** be careful ** ) ( M5 for LDS, LEA, & LES, ) HEX | : M5 ( n -) ( -) CREATE , DOES> @ , OR modDISP, ASM-RESET ; C5 M5 LDS, 8D M5 LEA, C4 M5 LES, ( M6 for the rotate & shift instructions ) HEX | : M6 ( n -) ( n# r/m | r/m - ) CREATE , DOES> @ IMM? 10 U/ 2 XOR 1 F-GET ( ie W) OR D0 OR C, 1REG? IF SWAP R>M THEN OR modDISP, IMM? IF DROP THEN ASM-RESET ; 10 M6 RCL, 0 M6 ROL, 20 M6 SHL, 18 M6 RCR, 08 M6 ROR, 38 M6 SAR, 28 M6 SHR, ( examples to shift right 1 bit ) ( 1 #, SI SHR, 1 #, W-PTR 17 [BX] SHR, 1 #, AL SHR, ) ( examples to shift right the # of bits in CL ) ( SI SHR, AL SHR, 1300 rt-par SHR, 3752 W-PTR rt-par SHR, ) ( INC, & DEC, instructions ) HEX | : M7 ( n -) ( r1 | r/m -) CREATE , DOES> @ SWAP 1REG? IF ( opc r1) R>M THEN 1REG? 100 = 1 F-GET AND ( ie it's a 2-byte register) IF ( opc rX) OR 40 OR C, ELSE ( opc mem | opc rH | opc rL ) FE orW C, OR modDISP, THEN ASM-RESET ; 08 M7 DEC, 00 M7 INC, ( PUSH, & POP, instructions ) HEX | : M8 ( n -) ( reg | seg | r/m -) CREATE , DOES> @ 8 F-GET IF ( seg opc ) 2/ 2/ 2/ 2/ 1 AND 1 XOR 6 OR OR C, ELSE 1REG? IF ( reg opc ) 2/ 8 AND 8 XOR 50 OR SWAP R>M OR C, ELSE ( r/m opc) DUP 100 U/ FF AND C, OR modDISP, THEN THEN ASM-RESET ; FF30 M8 PUSH, 8F00 M8 POP, ( IN, OUT, instr ) HEX | : M9 ( n -) ( n# r1 | r1 -) CREATE , DOES> @ orW NIP IMM? IF ( n# opc) C, ( n#) ELSE ( opc) 8 OR THEN C, ASM-RESET ; E4 M9 IN, E6 M9 OUT, ( use port #, AL IN, or port #, AX IN, for 8 bit ports ) ( or AL IN, or AX IN, for port in the DX register ) ( do not use AL DX IN, - the DX is implied ) ( XCHG ) HEX : XCHG, ( reg mem | mem reg | reg1 reg2 -) 211 F-GET 211 = ( 2 regs & one is AX) IF ?DUP IF NIP THEN ( r1 ) R>M 90 OR C, ELSE 2REGS? IF R>M THEN OR 86 orW C, modDISP, THEN ASM-RESET ; ( TEST, instruction - almost like ADD, etc. ) HEX : TEST, ( various - ) IMM? IF ACC? IF DROP A8 orW ( 4 OR) C, ELSE 1REG? IF R>M THEN F6 orW C, ( OR) modDISP, THEN ,IMM ELSE 2REGS? IF SWAP R>M THEN 84 orW C, OR modDISP, THEN ASM-RESET ; ( INT, & segment override instructions ) HEX : INT, ( #n -) CD C, C, ASM-RESET ; ( eg 21 #, INT, ) : ES: ( -) 26 C, ; : CS: ( -) 2E C, ; : SS: ( -) 36 C, ; : DS: ( -) 3E C, ; ( CALL, instr ) HEX : CALL, ( various -) IMM? ( intra-seg direct ) IF ( n#) HERE 3 + - ( make it relative) E8 C, , ( eg 2389 #, CALL, calls addr $2389) ELSE ( mem | reg -) 1REG? IF R>M THEN FF C, 10 OR modDISP, ( eg 0 [BX] CALL, or DX CALL, ) THEN ASM-RESET ; ( this is intra-seg indirect ) ( I am not implementing the inter-seg direct or indirect versions ) ( JMP, instr & NXT, ) HEX : JMP, ( various -) 140 F-GET ( ie R or M intra-seg indirect ) IF ( mem | reg -) 1REG? IF R>M THEN FF C, 20 OR modDISP, ( eg 0 [BX] JMP, DX JMP, ) ( or 3759 rt-paren JMP, ) ELSE ( a) HERE 3 + - ( relative) DUP SHORT? IF 1+ EB C, C, ELSE E9 C, , THEN ( disp is added to IP, so this is a relative jump ) THEN ASM-RESET ; : LJMP, ( a -) $E9 C, HERE 2 + - , ; ( lay down 3byte jump) ( I am not implementing the inter-seg direct or indir. versions) : NXT, ( -) AX LODS, AX JMP, ; : SWITCH, SP BP XCHG, ; FORTH ( loading of options is controlled by parenthesis in column 1) ( 136 LOAD ( NEWFILE create new block file w/ 8 blocks) 137 LOAD ( MS clock speed independent timing) ( 138 LOAD ( VIEW for listing only) 139 LOAD ( VIEW for editing) 140 LOAD ( .ID SEE) ( 141 LOAD ( NAMEZ:) 142 LOAD ( OF THENS from Wil Baden ) 143 144 THRU ( L@ L! LC@ LC!) ( 145 LOAD ( various EMITs >STD >DOS allow redirection) ( 146 LOAD ( show IBM graphics characters ) ( 147 LOAD ( FLIP) ( 148 LOAD ( allow hundreds of files) ( loading of options is controlled by parenthesis in column 1) ( 149 LOAD ( the name is the string ) ( 150 LOAD ( 2/MOD ) ( 151 LOAD ( INDEX ) ( 152 LOAD ( LCMOVE & LCMOVE>) 153 159 THRU ( print blocks SHOW SHOW2 SHADOW ) ( 160 LOAD ( BELL ) ( 161 LOAD ( BLK>TXT append range of blocks to textfile) ( 162 LOAD ( TXT>BLK create new block file from textfile) ( 163 LOAD ( one possible CASE: ) ( 164 LOAD ( SCROLL-UP SCROLL-DOWN) ( 165 166 THRU ( COLORS RED ON-CYAN etc.) ( 167 LOAD ( INT3, for breakpoints) ( code, notes, & tips for Starting Forth begin on scr 178) ( loading of options is controlled by parenthesis in column 1) ( 168 LOAD ( #INPUT input a number ) ( 169 LOAD ( GETARG$ DOS Command Line Reader) ( 170 174 THRU ( SHELL for executing DOS commands) ( 175 LOAD ( DATE & TIME from DOS) ( 176 LOAD ( textfile left paren for multi-line cmnts) ( 177 LOAD ( load blocks relative to current unit# ) ( NEWFILE create new block file with 8 blocks) : NEWFILE ( name -) DUP FOPEN ( name handle flag) IF DROP ( name) FMAKE ABORT" file?" ( handle) 8 OVER MORE ( handle) ELSE DROP ." file already exists " THEN ( handle) FCLOSE ; EXIT examples " MYFILE.SCR" NEWFILE " TEST.SCR" NEWFILE then open as you would any file, e.g. " TEST.SCR" 4 OPEN ( machine speed independent MS for proper timing) CODE T0@ ( - u) ( read timer zero) BX PUSH, ( make room on the stack) AL AL SUB, $43 #, AL OUT, ( latch timer0) $40 #, AL IN, AL BL MOV, $40 #, AL IN, AL BH MOV, NXT, END-CODE ( timer 0 goes through 2 65,536 counts 18.2 times per second, so 65536 18.2 * 2* 1000 / should give time for 1 ms, or a count of 2385.5, but we'll reduce the count some to allow for the loop in MS) : 1ms ( -) T0@ BEGIN ( first) DUP T0@ - 2330 > UNTIL DROP ; : MS ( # -) FOR 1ms NEXT ; ( VFA VIEW list the block) : VFA ( pfa -) NFA 4 - ; : VIEW ( -) ' VFA @ ?DUP IF LIST THEN ; ( VFA VIEW V pop up the editor) : VFA ( pfa -) NFA 4 - ; : VIEW ( -) ( e.g. VIEW DUP) ' VFA @ ?DUP IF EDIT ( ELSE ." defined at keyboard" CR ) THEN ; : V VIEW ; ( shorthand ) ( streamlined version of SEE - used only for DEFER'd words) : .ID ( pfa -) NFA DUP 0= ABORT" ?" TYPE$ ; : SEE ( -) CR ' DUP C@ $B8 = IF DUP 1+ @ .ID CR THEN DROP ; ( NAMEZ: defines an asciiz string whose name is the string) : NAMEZ: ( -) ( - a) HEAD HERE NFA ( ** must not be headerless) $C000 , ( al al add, trick puts zero immediately after name ) $53 C, ( bx push,) $BB C, , ( a #, bx mov,) $AD C, $E0FF , ( nxt,) ; ( OF THENS ) COMPILER ( from Wil Baden) : OF COMPILE OVER COMPILE = \ IF COMPILE DROP ; : THENS ( n -) FOR \ THEN NEXT ; FORTH ( L@ & L! ) CODE L@ ( seg offset -- n) ( offset already in BX) ES POP, ( seg) ES: 0 [BX] BX MOV, ( retrieve n) NXT, END-CODE CODE L! ( n seg offset -- ) ( offset already in BX) ES POP, ( seg) AX POP, ( n) ES: AX 0 [BX] MOV, BX POP, ( refill TOS) NXT, END-CODE ( LC@ & LC! ) CODE LC@ ( seg offset -- c) ( offset already in BX) ES POP, ( seg) ES: 0 [BX] ( BX) BL MOV, ( retrieve c) BH BH SUB, NXT, END-CODE CODE LC! ( c seg offset -- ) ( offset already in BX) ES POP, ( seg) AX POP, ( c) ES: AL 0 [BX] MOV, BX POP, ( refill TOS) NXT, END-CODE ( DOS-EMIT for non-pc compatible MS-DOS computers ) VARIABLE TEMP : STD-OUT ( c -) ( uses handle 1) TEMP C! TEMP ( to DX) 1 ( to CX) 1 ( to BX) $4000 ( to AX) DOS 2DROP ; : DOS-OUT ( c -) ( uses Display Character function ) ( c to DX) 0 0 ( ie zeroes to CX & BX) $0200 ( func 2 to AX) DOS 2DROP ; : >DOS ( -) ['] DOS-OUT IS EMIT ; : >STD ( -) ['] STD-OUT IS EMIT ; ( show IBM graphics characters ) : TST-GPH ( -) CLS 128 ( chr) 128 FOR DUP . SPACE DUP EMIT SPACE 1+ NEXT DROP ; : TST-NON CLS 0 ( chr) 128 FOR DUP . SPACE DUP EMIT SPACE 1+ NEXT DROP ; ( FLIP ) : FLIP ( hhll - llhh) DUP $100 * SWAP $100 U/ OR ; ( relocate the handle alias table to allow more than 15 files) HEX CREATE HANDLE-ALIAS-TABLE MAX-FILES 5 + 20 MAX ALLOT 32 CONSTANT HAT-LENGTH 34 CONSTANT HAT-OFFSET VARIABLE HAT-LENGTH-SAVE VARIABLE HAT-OFFSET-SAVE : HAT-ON ( -) ['] HANDLE-ALIAS-TABLE MAX-FILES 5 + 20 MAX FF FILL HAT-OFFSET @ ['] HANDLE-ALIAS-TABLE MAX-FILES 5 + 20 MAX CMOVE HAT-OFFSET @ 5 + 0F FF FILL HAT-LENGTH @ HAT-LENGTH-SAVE ! HAT-OFFSET @ HAT-OFFSET-SAVE ! ['] HANDLE-ALIAS-TABLE HAT-OFFSET ! MAX-FILES 5 + 20 MAX HAT-LENGTH ! ; : HAT-OFF ( -) RESET-FILES HAT-OFFSET-SAVE @ HAT-OFFSET ! HAT-LENGTH-SAVE @ HAT-LENGTH ! ; ( words whose name is its string ) : NAME: ( -) ( -a) HERE 2 + CONSTANT ; ( this version does not put a zero at end of name) : NAMEZ: ( -) ( -a) HERE 2 + CODE ( AL AL ADD,) $C000 , ( trick to put a zero immediately after name ) BX PUSH, ( a) #, BX MOV, NXT, ; : .NAME: ( -) ( -) HERE 2 + CREATE , DOES> @ TYPE$ ; ( types its own name) EXIT usage NAME: AEROPLANE NAME: CABBAGE CABBAGE TYPE$ ( will type out "CABBAGE" ) ( 2/MOD ) CODE 2/MOD ( u - r q ) ( unsigned ) AX AX SUB, 1 #, BX SHR, 1 #, AX RCL, AX PUSH, NXT, END-CODE ( similar to regular INDEX but w/ only one argument) : INDEX ( n -) BEGIN DUP ?SCROLL CR DUP 4 .R SPACE DUP BLOCK 64 TYPE 1+ AGAIN ; ( It is designed to blow up at end of the file. Because paging up and down through a file is so fast, I don't usually use INDEX.) ( move anywhere in full PC address space ) CODE LCMOVE ( seg fr seg to # - :moving words & then ?odd byte) CLD, SI DX MOV, BX CX MOV, DI POP, ES POP, SI POP, DS POP, 1 #, CX SHR, REP, W-PTR AX MOVS, CX CX ADC, REP, ( BYTE) AL MOVS, CS AX MOV, AX DS MOV, BX POP, DX SI MOV, NXT, END-CODE CODE LCMOVE> ( seg fr seg to # - :moving words & then ?odd byte) STD, SI DX MOV, BX CX MOV, DI POP, ES POP, SI POP, DS POP, BX DEC, BX DEC, BX SI ADD, BX DI ADD, 1 #, CX SHR, REP, W-PTR AX MOVS, CX CX ADC, SI INC, DI INC, REP, ( BYTE) AL MOVS, CS AX MOV, AX DS MOV, BX POP, DX SI MOV, CLD, NXT, END-CODE ( (PEMIT to printer ) : (PEMIT ( c -) ( print chr to LPT1: ) 0 0 $0500 DOS 2DROP ; : >PRN ( -) ['] (PEMIT IS EMIT ; : >SCR ( -) ['] DEFAULT-EMIT 1+ @ IS EMIT ; ( SHOW ) VARIABLE SCR-LIMIT : SCR<LIMIT? ( n - f) SCR-LIMIT @ < ; : .SCR# ( n -) ." scr # " 5 .R ; : .LINE ( a - a') 64 FOR C@+ EMIT NEXT ; : 2LINES ( a1 a2 - a1' a2') SWAP .LINE 4 SPACES SWAP .LINE CR ; ( SHOW 3 blocks per page ) DEFER .HD ( print a heading ) : (.HD ( scr# -) ." file " >UNIT# .FILE ; ' (.HD IS .HD VARIABLE LM 7 LM ! ( left margin) : .LM ( -) LM @ SPACES ; : .UNDER ( -) 64 FOR ." _" NEXT ; : SHOW ( 1st last - ) >PRN OVER - 1+ ( 1st #) 0 SWAP ( 1st rel #) FOR ( 1st rel) DUP 3 UMOD 0= IF CR .LM OVER .HD THEN CR CR .LM ." scr # " OVER U. CR OVER BLOCK 16 FOR .LM ." |" .LINE ." |" CR NEXT DROP .LM ." |" .UNDER ." |" 1+ 1 +UNDER DUP 3 UMOD 0= IF $0C EMIT THEN NEXT 3 UMOD IF $0C EMIT THEN DROP >SCR ; ( make printer print in small type ) DEFER CONDENSED : ESC ( -) 27 EMIT ; : OKI-CONDENSED ( -) ( set OKI printer to small print) $1D EMIT ; : EPSON-CONDENSED ( -) ( this might set Epson printer to small print) ( if not, look it up in your printer manual ) 27 EMIT 33 EMIT 4 EMIT ; : LJ-CONDENSED ( -) ESC ." E" ESC ." &l0L" ESC ." &l5E" ( reset, left=0, top=5) ( ESC ." &l0o8D" ESC ." (10U" ESC ." (s0p17h8v0s-b0T" ) ESC ." &l0o6D" ESC ." (10U" ESC ." (s0p17h8v0s-b0T" ( 6 lpi) ( force internal-9 font) ; ( ' NOP) ' LJ-CONDENSED IS CONDENSED ( SHOW2 6 blocks per page ) : 2SCRS ( n1 n2 -) OVER SCR<LIMIT? IF DUP SCR<LIMIT? IF OVER .SCR# 57 SPACES DUP .SCR# CR SWAP BLOCK SWAP BLOCK 16 FOR 2LINES NEXT ELSE SWAP DUP .SCR# CR BLOCK 16 FOR .LINE CR NEXT THEN THEN 2DROP CR CR ; 3 CONSTANT SCRS/COLUMN : SHOW2 ( 1st last -) >PRN CONDENSED DUP 1+ SCR-LIMIT ! OVER - SCRS/COLUMN 2* U/MOD SWAP 1 MIN + FOR DUP .HD CR CR SCRS/COLUMN FOR DUP DUP SCRS/COLUMN + 2SCRS 1+ NEXT $0C EMIT SCRS/COLUMN + NEXT DROP >SCR ; ( SHADOW 6 blocks per page) : 2SCRS ( n1 n2 -) ( for use by SHADOW) OVER .SCR# 58 SPACES DUP .SCR# CR SWAP BLOCK SWAP BLOCK 16 FOR 2LINES NEXT 2DROP CR CR ; ( SHADOW 6 blocks per page) VARIABLE PAGE-CTRL : SHADOW ( 1st last 1st-shadow -) >PRN CONDENSED PAGE-CTRL OFF PUSH OVER - 1+ POP SWAP FOR ( 1st 1st-shadow) PAGE-CTRL @ SCRS/COLUMN UMOD 0= IF OVER .HD CR CR THEN 2DUP 2SCRS 1+ SWAP 1+ SWAP 1 PAGE-CTRL +! PAGE-CTRL @ SCRS/COLUMN UMOD 0= IF $0C EMIT THEN NEXT 2DROP PAGE-CTRL @ SCRS/COLUMN UMOD IF $0C EMIT THEN >SCR ; EXIT : IBM-PRO ( -) ( make NEC emulate IBM PRO-PRINTER) >PRN $1C EMIT ." Dc" >SCR ; : TST ( -) 3600 3602 3900 SHADOW ; ( BELL ) ( this works pc's speaker no matter where EMIT is vectored ) ( it may need longer delays for fast processors ) CODE BELL ( -) $61 #, DX MOV, AL IN, 3 #, AL OR, AL OUT, $1000 #, CX MOV, BEGIN, LOOP, $FC #, AL AND, AL OUT, NXT, END-CODE ( BLK>TXT append a range of blocks to a textfile ) : WRITE-EOL ( h -) PAD 2 ROT FILE-WRITE ; : BLK>TXT ( first last output-name -) 13 PAD C! 10 PAD 1+ C! ( setup crlf at PAD) FOPEN ABORT" output file?" ( first last handle) DUP >EOF ROT ROT OVER - 1+ ( handle first #) PUSH SWAP POP FOR ( blk# handle) DUP WRITE-EOL OVER BLOCK ( blk# h a) SWAP 16 FOR ( blk# a h) PUSH DUP 64 -TRAILING ( blk# a a #) R@ FILE-WRITE R@ WRITE-EOL 64 + POP NEXT SWAP DROP ( blk# handle) 1 +UNDER NEXT FCLOSE DROP ; ( if textfile does not exist you can create it with " textfile.ext" FMAKE DROP FCLOSE ) ( TXT>BLK convert a textfile to a block file ) VARIABLE #LINES : PUT-LINE ( a # h -) PUSH PAD 64 32 FILL PAD SWAP CMOVE ( ) PAD 64 POP FILE-WRITE 1 #LINES +! ; : GET-LINE ( - a #) READ-LINE ( a #) 2DUP -CTRL ( a #) ; : SETUP-FILES ( input-name output-name - handle) #LINES OFF SWAP FOPEN ABORT" input file?" ( out-name in-handle) FIBH ! 0 0 >FIN 2! ( out-name) FMAKE ABORT" output file?" ; : TXT>BLK ( input-name output-name -) SETUP-FILES ( out-handle) PUSH ( ) BEGIN GET-LINE ( a #) BEGIN ( a #) DUP 64 > WHILE ( a #) OVER 64 R@ PUT-LINE 64 - 64 +UNDER REPEAT ( a #) R@ PUT-LINE EOF? UNTIL ( ) PAD 64 32 FILL POP 16 #LINES @ 16 UMOD - ( handle #) FOR DUP PAD 64 ROT FILE-WRITE NEXT ( handle) FCLOSE FIBH @ FCLOSE FIBH OFF ; ( one possible CASE: ) : CASE: ( -) ( n -) CREATE ] DOES> ( n a) 2 + ( move past lit) BEGIN 2DUP @ DUP 0= PUSH ( n a n n') = POP OR NOT ( n a flg) WHILE ( no match) ( n a) 6 + REPEAT NIP 2 + @ EXECUTE ; EXIT N for default must be 00 and the default pair must be last. numbers can be in any order except 00 must be last, e.g. : RED ." RED" ; : BLUE ." BLUE" ; : ORANGE ." ORANGE" ; : PINK ." PINK" ; : BLACK ." BLACK" ; CASE: COLOR 7 RED 12 BLUE 472 ORANGE 15 PINK 00 BLACK ; an actual zero or a no match causes the default to be picked 7 COLOR REDok 472 COLOR ORANGEok 3000 COLOR BLACKok list must end with a semi-colon & numbers can't be constants ( additional BIOS Int $10 video words ) CODE SCROLL-UP ( r c r c #lines attr -) ( scroll w/in window) 6 #, AH MOV, HERE CX POP, CL AL MOV, DX POP, CX POP, CL DH MOV, CX POP, SWITCH, 0 [BP] CH MOV, SWITCH, BL BH MOV, $10 #, INT, BX POP, BX POP, NXT, END-CODE CODE SCROLL-DOWN ( r c r c #lines attr -) 7 #, AH MOV, JMP, END-CODE ( display and cycle through foreground & background colors) : COLORS ( -) BASE @ HEX BEGIN CLS CR CR ." This is attr $" ATTR @ DUP 2 U.R CR CR ." F1 changes foreground, F2 changes background, Esc exits" CR KEY DUP 27 - WHILE 188 ( F2) OF ( attr) $10 + $FF AND ( attr) ELSE 187 ( F1) OF ( attr) DUP 1+ $0F AND SWAP $F0 AND OR ( attr) ELSE DROP BEEP [ 2 ] THENS ATTR ! REPEAT 2DROP BASE ! ; ( Example of setting forground and background attributes.) ( Use previous block to find the attributes you like.) : FG: CREATE C, DOES> C@ ATTR @ $F0 AND OR ATTR ! ; : BG: CREATE C, DOES> C@ 16 * ATTR @ $F AND OR ATTR ! ; 0 FG: BLACK 1 FG: BLUE 2 FG: GREEN 3 FG: CYAN 4 FG: RED 5 FG: PURPLE 6 FG: BROWN 7 FG: GRAY 0 BG: ON-BLACK 1 BG: ON-BLUE 2 BG: ON-GREEN 3 BG: ON-CYAN 4 BG: ON-RED 5 BG: ON-PURPLE 6 BG: ON-BROWN 7 BG: ON-GRAY ( Don't take my color names seriously; I don't have any idea what cyan is, nor the difference between gray & light blue.) ( Addition to the assembler to allow 1-byte software interrupt) ( Suggested by Ian Watters ) : INT3, ( -) $CC C, ; ( input a number) : #INPUT ( - n) QUERY 0 WORD NUMBER ; ( DOS Command Line Reader by L. Greg Lisle) VARIABLE ARG$ 130 ALLOT : GETARG$ ( -- a ) ( Reads the command line args into ARG$ ) ( Returns ARG$ with length byte and ) ( null terminator. ) $80 DUP C@ 2 + 128 MIN ARG$ SWAP CMOVE ARG$ ; EXIT ( SHELL notes ) Greg Lisle and Brad Rodriguez offered versions of SHELL. Rather than choose between them I am just including my own version. Use it to execute DOS commands. Here are some examples to suggest ways to use it. Each usage wastes a little dictionary space, so I have no objection if you rewrite it to be prefix and use PAD. " DIR *.SCR" SHELL ( directory listing of .SCR files) " COPY XYZ.TXT ABC.TXT" SHELL ( copy a file) " DIR *.TXT >TEXTDIR" SHELL ( capture a directory listing) " TEXTDIR" " TEXTDIR.SCR" TXT>BLK ( so you can view it with) " TEXTDIR.SCR" 3 OPEN 3000 EDIT ( the editor) : DIR ( -) " DIR /P" SHELL ; : Q ( filename -) " Q" SWAP shell ; ( invoke textfile editor)( SHELL FCS ) CODE free ( #paragraphs - AX carry) DS AX MOV, AX ES MOV, $4A00 #, AX MOV, ( BX holds #paragraphs to retain) $21 INT, AX PUSH, BX BX SBB, NXT, END-CODE : FREE ( #paragraphs -) free IF ." err# " U. ABORT" FREE error" THEN DROP ; VARIABLE 'SP VARIABLE 'SS CREATE PBLK 14 ALLOT EXIT FREE is actually "FREE-ALL-EXCEPT" as you tell it how many 16-byte paragraphs to keep. ( SHELL FCS ) CODE EXEC ( pgm$ - AX f) BX DX MOV, ( set up file name) PBLK #, BX MOV, SI PUSH, BP PUSH, SS 'SS ) MOV, SP 'SP ) MOV, ( save stack pointer) DS AX MOV, AX ES MOV, $4B00 #, AX MOV, $21 INT, CLI, ( ints off) 'SS ) SS MOV, 'SP ) SP MOV, STI, BP POP, SI POP, BX BX SBB, ( f) AX PUSH, NXT, END-CODE ( SHELL FCS ) : shell ( pgm$ tail$ -) $3000 FREE ( you could lower this) PBLK 14 0 FILL 13 OVER DUP C@ 1+ + C! PBLK 2 + ! ( pgm$) CS@ PBLK 4 + ! ( pgm$) 1+ EXEC ( ax f) IF SPACE U. ABORT" Shell error" THEN DROP ; EXIT ordinarily you would use SHELL on the next block, but here are some usage examples of little shell: " DIR" " *.*" shell " COMMAND.COM" " DIR *.*" shell) ( SHELL FCS ) : SHELL ( tail$ -) " C:\COMMAND.COM" ( a) SWAP " /C " PAD 4 CMOVE ( a) COUNT DUP PUSH ( a+1 #) PAD 4 + SWAP CMOVE POP 3 + DUP PAD C! PAD + 1+ 13 SWAP C! ( pgm$ tail$) PAD shell ; ( This prepends " /C " to the command tail and moves it to PAD, and replaces ending zero byte with CR) ( DATE and TIME by L. Greg Lisle ) CODE DOS3 ( DX CX BX AX - DX CX AX carry) BX AX MOV, BX POP, CX POP, DX POP, 21 #, INT, DX PUSH, CX PUSH, AX PUSH, BX BX SBB, NXT, END-CODE ( also for int 21 ) ( : DOS2 ( dcba- DX AX c) ( DOS3 ROT DROP ; ) : DATE ( -y m d dow) 0 0 0 2A00 DOS3 DROP ROT DUP 100 / SWAP 0FF AND ROT 0FF AND ; : TIME ( -h m s ds) 0 0 0 2C00 DOS3 2DROP DUP 100 / SWAP 0FF AND ROT DUP 100 / SWAP 0FF AND ; ( left paren for multi-line comments in textfiles) COMPILER : ( BEGIN 32 WORD DUP C@ ?DUP WHILE + C@ ') = UNTIL ELSE DROP THEN ; FORTH : ( \ ( ; ( load blocks relative to current unit# ) : UBLK ( blk# - blk#) 1000 UMOD BLK @ >UNIT# 1000 * + ; : ULOAD ( blk# -) UBLK LOAD ; : UTHRU ( 1st last -) SWAP UBLK SWAP BLK THRU ; The following blocks contain some notes and code for using Pygmy with the 1st edition of the book _Starting Forth_ by Leo Brodie. ( DO LOOP R@ I compatible with _Starting Forth_) ( these words were written by Robert Berkey ) FORTH CODE 2R@ ( - x1 x2) BX PUSH, 2 [BP] PUSH, 0 [BP] BX MOV, NXT, END-CODE : (DO) ( limit index - for-index ;R ip - index-offset ip ) OVER 1- POP SWAP PUSH PUSH - ; COMPILER : DO ( runtime: limit index -) COMPILE (DO) \ FOR ; : LOOP ( runtime: - ;R x1 x2 - x1 x2 | x1 x2 - ) \ NEXT COMPILE POP COMPILE DROP ; : R@ ( rntime: - x ) ( r: x - x) COMPILE I ; : I ( -- index) ( ** do not use w/ FOR/NEXT, use R@ instead*) COMPILE 2R@ COMPILE - ; FORTH p. 12 & 13 STARS & CHAPTER 6 DO LOOP +LOOP instead of : STARS 0 DO STAR LOOP ; use : STARS FOR STAR NEXT ; the arguments for DO are limit & starting-index and the loop counts up from starting-index to just before limit e.g. : TST1 7 0 DO I . LOOP ; would print 0 1 2 3 4 5 6 ok FOR ... NEXT only takes one argument, the starting index. It counts down that many times, e.g. : TST1 7 FOR I . NEXT ; would print 6 5 4 3 2 1 0 ok p. 25 stack underflow and overflow Pygmy does not check for stack overflow. It checks for underflow whenever you do .S Anytime an error occurs - such as typing in a word that it doesn't know - it will reset the stack and the return stack to their correct initial values. The word .S will display the contents of the data stack. It shows the entire contents. This is handy for debugging. If before and after loading one or more blocks .S shows different stack pictures you have an error in the blocks, possibly an IF without a matching THEN. p. 50 & p. 83 non-destructive stack print The definition given in the book : .S CR 'S S0 @ 2- DO I @ . -2 +LOOP ; will not work in Pygmy as the following words are not even present in Pygmy 'S S0 2- DO +LOOP. Of course, 2- could be replaced by 2 - However, Pygmy has a built in .S that will work just fine. p. 52 & 53 2SWAP 2DUP 2OVER 2DROP double numbers Pygmy has 2DUP & 2DROP but does not have 2SWAP & 2OVER. Chapter 3 the editor To begin editing a specific block, type n EDIT The Esc key will get you out of the editor. To get back into the same block, just type ED without giving it a block number. There is a short reminder menu on the top line. F3 asks for the string to search for. F1 searches again using the same string. F4 asks for the string to replace it with. F2 does the replace again. F5 will delete the line the cursor is on. F6 will join the following line to the current one. -- continued -- Chapter 3 the editor -- continued -- F7 is the "cut" command and F8 is the "paste" command. Each time you press F7 it copies the current line to the "cut" buffer and moves the cursor down to the next line. Notice that the top status line shows you the count of the number of lines in the "cut" buffer. F8 removes the oldest line from the "cut" buffer and overlays the current line and moves the cursor down to the next line. Try it out on a dummy block to get a feel for it. Use the arrow keys to move around the block and just over- type to make your changes, or press the INS key to change to the insert mode. The backspace key deletes one char to left and Del key deletes the current char. Inserts & deletes only apply to the current line. -- continued -- Chapter 3 the editor The PgUp and PgDn keys allow for very fast movement between blocks. Press CR to split a line at the cursor and to scroll all the lower lines down. The bottom line will be lost. If you make changes you don't want to keep, you can get out ofthe editor with Esc and type EMPTY-BUFFERS to discard any changes that have not yet been written to disk. p. 101 ABORT" Pygmy has an IF built into ABORT" So, you can say DUP 0= ABORT" error " just as in the examples. p. 123 F83's >R is equivalent to Pygmy's PUSH p. 123 F83's R> is equivalent to Pygmy's POP p. 302 F83'S [COMPILE] is equiv to Pygmy's \ that's a backslash - it does not indicate the whole line is commented out as in F83. It forces compilation rather than execution of the following "immediate" word when you are making a colon definition. It only works on words that are in COMPILER. p. 177 <# and number conversion <# does not expect a double number, just a regular 16 bit number. However, in Pygmy you do not need to say TYPE after the ending #> as the the TYPE is done as part of #>. I was undecided as to whether I liked this or not, but it has grown on me. p. 258 TYPE in Pygmy is not like the TYPE in cmFORTH. In Pygmy, TYPE is the same as in _Starting Forth_ and F83, etc. Pygmy also has the word TYPE$ ( a -) which expects the address of a counted string. CHAPTER 9 internal structure In Pygmy, every definition consists of a 2-byte view field, a 2-byte link field, a 1 to 32-byte name field, and a variable length parameter field. The name field consists of a 1-byte count followed by zero to 31 characters. In a colon definition, the parameter field begins with a 3 byte jump to machine language code that nests down a level. Those 3 bytes are followed by the addresses of the words that make up the definition (2 bytes per address). In a CODE definition - machine language - the parameter field begins with the actual machine code. -- continued -- internal structure -- continued -- A word may be headerless, thus beginning with its parameter field. The following is information that you will not need unless you write CODE words: The top stack item is kept in register BX. The word must end with an "in-line" next. This is accomplished by the assembler macro NXT, Register SI is used for IP so if you want to use SI you need to save & restore it. PUSH, & POP, are used for both stacks, see source code examples of switching the value in registers BP & SP by using the assembler macro SWITCH, Miscellaneous _Starting Forth_'s is Pygmy's TIB TIB @