home *** CD-ROM | disk | FTP | other *** search
- ;
- ; Minimal Forth-like interpreter as presented
- ; in Kilobaud Magazine, February, 1981
- ; issue, page 76 by Richard Fritzson
- ;
- ; Note: this is part 1 of a promised 3-part
- ; system, to include a compiler, and, hopefully,
- ; an editor.
- ;
- ;
- ; 02/04/81:
- ; typed in by Ron Fowler, Westland, Mich
- ; (modified slightly: the original version
- ; had I/O routines to BIOS hard-coded, and
- ; memory fixed at time of assembly. Mod-
- ; ified for any size CPM, and dynamic fix
- ; of memory size at run time)
- ;
- ;
- ; if you're not using MAC to assemble this program,
- ; delete the next statement:
-
- TITLE 'Threaded Code Interpreter for 8080'
- ;
- ; Richard Fritzson
- ; 29 January 1980 Version 1.0
- ;
- ; This version contains only the basic internal
- ; interpreter and a simple interactive console
- ; interpreter.
- ;
- ORG 100H ;START UP ADDRESS
- ;
- BASE: LXI SP,STACK ;INITIALIZE PARAMETER STACK
- CALL DICMOVE ;MOVE DICTIONARY TO HIGH MEMORY
- LXI H,TOP-1 ;SET PC TO TOP LEVEL LOOP
- SHLD PC
- JMP NEXT ;AND START INTERPRETER
- ;
- ; TOP - Top Level System Loop
- ; DESCRIPTION: TOP in an infinite
- ; loop which picks up the contents of the
- ; EXEC variable and executes it.
- ;
- TOP: DW EXEC,PEEKW ;GET TOP LEVEL PROGRAM
- DW EXECUTE ;RUN IT
- DW JUMP,TOP-1 ;AND LOOP
- ;
- ; EXEC - address of top level routine
- ;
- EXEC: DW VARIABLE ;THREADED CODE VARIABLE
- DW INTERACT ;ADDRESS OF USER INTERPRETER
- ;
- ; Reserved Stack Space
- ;
- DS 128 ;PARAMETER STACK
- STACK EQU $
- PAGE
- ;
- ; The interpreter's architecture: a program counter and a stack
- ;
- PC DW 0 ;A 16 BIT POINTER INTO THE MIDDLE OF
- ;THE CURRENT INSTRUCTION (NOT THE
- ;FIRST BYTE, BUT THE SECOND)
- ;
- RSTACK DW $+2 ;THE STACK POINTER POINTS TO THE NEXT
- ;AVAILABLE STACK POSITION (NOT THE
- ;TOPMOST OCCUPIED POSITION)
- ;
- DS 80H ;RESERVED STACK SPACE
- ;
- ; RPUSH - push DE on stack
- ; ENTRY: DE - number to be pushed on stack
- ; EXIT: DE - is unchanged
- ; DESCRIPTION: this code is illustrative of how the
- ; stack works. However it is not used in the system and
- ; can be left out.
- ;
- RPUSH: LHLD RSTACK ;GET STACK POINTER
- MOV M,E ;STORE LOW BYTE
- INX H ;BUMP POINTER TO NEXT BYTE
- MOV M,D ;STORE HIGH BYTE
- INX H ;BUMP POINTER TO NEXT EMPTY SLOT
- SHLD RSTACK ;RESTORE POINTER
- RET
- ;
- ; RPOP - pop DE from stack
- ; ENTRY: No Register Values Expected
- ; EXIT: DE - top element of RSTACK
- ; DESCRIPTION: this code is illustrative of how the
- ; stack works. However it is not used in the system and
- ; can be left out.
- ;
- RPOP: LHLD RSTACK ;GET STACK POINTER
- DCX H ;DROP TO FIRST STACK POSITION
- MOV D,M ;GET HIGH BYTE
- DCX H
- MOV E,M ;GET LOW BYTE
- SHLD RSTACK ;RESTORE STACK POINTER
- RET
- ;
- ; NEXT - main internal interpreter loop
- ; ENTRY: PC - points into the instruction just completed
- ; EXIT: PC - incremented by 2, points to next
- ; instruction
- ; DE - points to middle of first word of
- ; next routine (i.e. (PC)+1)
- ; DESCRIPTION: increments the PC; picks up the code
- ; word of the next routine and jumps to it.
- ;
- NEXT: LHLD PC ;INCREMENT PROGRAM COUNTER
- INX H ; WHILE LOADING DE WITH
- MOV E,M ; NEXT INSTRUCTION
- INX H
- MOV D,M
- SHLD PC
- XCHG ;PICK UP WORD ADDRESSED
- MOV E,M ;BY NEXT INSTRUCTION (WHICH
- INX H ; IS CODE, TCALL OR SOME OTHER
- MOV D,M ; EXECUTABLE ADDRESS)
- XCHG ; AND
- PCHL ; JUMP TO IT
- ;
- ; TCALL - the threaded call routine
- ; ENTRY: DE - middle of first word of routine being called
- ; EXIT: No Register Values Returned
- ; DESCRIPTION: pushes the current congtents of the PC
- ; onto the return stack; makes DE the new PC
- ;
- TCALL: LHLD PC ;GET OLD PROGRAM COUNTER
- XCHG ;REPLACE WITH DE
- SHLD PC
- LHLD RSTACK ;PUSH OLD PC ON RSTACK
- MOV M,E
- INX H
- MOV M,D
- INX H
- SHLD RSTACK
- JMP NEXT ;BACK TO INTERPRETER
- ;
- ; TRET - the threaded code return
- ; DESCRIPTION: pops the top element of the
- ; return stack and puts it into the PC.
- ;
- TRET: DW $+2 ;CODE
- LHLD RSTACK ;GET STACK POINTER
- DCX H ;HIGH BYTE OF TOP ELEMENT
- MOV D,M
- DCX H ;LOW BYTE OF TOP ELEMENT
- MOV E,M
- SHLD RSTACK ;RESTORE STACK POINTER
- XCHG ;STORE TOP OF STACK IN PC
- SHLD PC
- JMP NEXT ;BACK TO INTERPRETER
- ;
- ; Simple arithmetic routines
- ;
- ; INC - increment the top of the stack
- ;
- INC: DW $+2 ;CODE
- POP H ;GET TOP
- INX H ;INCREMENT
- PUSH H ;RESTORE
- JMP NEXT
- ;
- ; DEC - decrement the top of the stack
- ;
- DEC: DW $+2 ;CODE
- POP H ;GET TOP
- DCX H ;DECREMENT
- PUSH H ;RESTORE
- JMP NEXT
- ;
- ; TADD - add the top two elements of the stack
- ;
- TADD: DW $+2 ;CODE
- POP H ;FIRST ELEMENT
- POP D ;SECOND ELEMENT
- DAD D ;ADD 'EM
- PUSH H ;PUSH RESULT
- JMP NEXT
- ;
- ; MINUS - negate top of stack
- ;
- MINUS: DW $+2 ;CODE
- POP H ;GET TOP
- CALL MINUSH ;NEGATE IT
- PUSH H ;PUSH IT
- JMP NEXT
- ;
- MINUSH: DCX H ;GOOD OLE 2S COMPLEMENT
- MOV A,H
- CMA
- MOV H,A
- MOV A,L
- CMA
- MOV L,A
- RET
- ;
- ; TSUB - subtract TOP from TOP-1
- ;
- TSUB: DW TCALL ;THREADED CODE
- DW MINUS ;NEGATE TOP
- DW TADD ;AND ADD
- DW TRET
- ;
- ; PEEKB - retrieve a byte from memory
- ; ENTRY: TOP - address
- ; EXIT: TOP - byte at address
- ;
- PEEKB: DW $+2 ;CODE
- POP H ;GET ADDRESS
- MOV E,M ;GET BYTE
- MVI D,0
- PUSH D ;SAVE
- JMP NEXT
- ;
- ; PEEKW - retrieve a word from memory
- ; ENTRY: TOP - address
- ; EXIT: TOP - word at address
- ;
- PEEKW: DW $+2 ;CODE
- POP H ;GET ADDRESS
- MOV E,M ;GET WORD
- INX H
- MOV D,M
- PUSH D ;SAVE
- JMP NEXT
- ;
- ; POKEB - store byte in memory
- ; ENTRY: TOP - address
- ; TOP-1 - byte to store
- ; EXIT: No Values Returned
- ;
- POKEB: DW $+2 ;CODE
- POP H ;GET ADDRESS
- POP D ;GET BYTE
- MOV M,E ;STORE
- JMP NEXT
- ;
- ; POKEW - store word in memory
- ; ENTRY: TOP - address
- ; TOP-1 - word to store
- ; EXIT: No Values returned
- ;
- POKEW: DW $+2 ;CODE
- POP H ;GET ADDRESS
- POP D ;GET WORD
- MOV M,E ;STORE WORD
- INX H
- MOV M,D
- JMP NEXT
- ;
- ; Some standart threaded code functions
- ; TPUSH - push the next word onto the stack
- ;
- TPUSH: DW $+2 ;CODE
- LHLD PC ;GET PROGRAM COUNTER
- INX H ;ADVANCE TO NEXT WORD
- MOV E,M ;AND PICK UP CONTENTS
- INX H
- MOV D,M
- SHLD PC ;STORE NEW PROGRAM COUNTER
- PUSH D ;PUSH WORD ONTO PARAM STACK
- JMP NEXT ;CONTINUE
- ;
- ; TPOP - drop the top of the parameter stack
- ;
- TPOP: DW $+2 ;CODE
- POP H ;POP ONE ELEMENT
- JMP NEXT ; AND CONTINUE
- ;
- ; SWAP - exchange top two elements of the stack
- ;
- SWAP: DW $+2 ;CODE
- POP H ;GET ONE ELEMENT
- XTHL ;XCHG
- PUSH H ;PUT BACK
- JMP NEXT ;AND CONTINUE
- ;
- ; DUP - duplicate the top of the stack
- ; DESCRIPTION: often used before functions which
- ; consume the top of the stack (e.g. conditional jumps)
- ;
- DUP: DW $+2 ;CODE
- POP H ;GET TOP
- PUSH H ;SAVE IT TWICE
- PUSH H
- JMP NEXT
- ;
- ; CLEAR - clear the stack
- ;
- CLEAR: DW $+2 ;CODE
- LXI SP,STACK ;RESET STACK POINTER;
- JMP NEXT
- ;
- ; Threaded Code Jumps
- ;
- ; All Jumps are to absolute locations
- ; All Conditional jumps consume the
- ; elements of the stack that they test
- ;
- ; JUMP - unconditional jump
- ;
- JUMP: DW $+2 ;CODE
- JUMP1: LHLD PC ;GET PROGRAM COUNTER
- INX H ;GET NEXT WORD
- MOV E,M
- INX H
- MOV D,M
- XCHG ;MAKE IT THE PC
- SHLD PC
- JMP NEXT
- ;
- ; IFZ - jump if top is zero
- ;
- IFZ: DW $+2 ;CODE
- POP H ;GET TOP
- MOV A,H ;TEST FOR ZERO
- ORA L
- JZ JUMP1 ;IF YES, JUMP
- SKIP: LHLD PC ;ELSE SIMPLY SKIP NEXT WORD
- INX H
- INX H
- SHLD PC
- JMP NEXT
- ;
- ; IFNZ - jump if top not zero
- ;
- IFNZ: DW $+2 ;CODE
- POP H ;GET TOP
- MOV A,H ;TEST FOR ZERO
- ORA L
- JNZ JUMP1 ;IF NOT, JUMP
- JMP SKIP ;ELSE DON'T
- ;
- ; IFEQ - jump if TOP = TOP-1
- ;
- IFEQ: DW $+2 ;CODE
- POP H ;GET TOP
- CALL MINUSH ;NEGATE IT
- POP D ;GET TOP-1
- DAD D ;ADD 'EM
- MOV A,H ;TEST FOR ZERO
- ORA L
- JZ JUMP1 ;IF EQUAL, JUMP
- JMP SKIP ;IF NOT, DON'T
- ;
- ; Implementation of Constants and Variables in a
- ; threaded code system
- ;
- ;CONSTANT - code address for constants
- ; ENTRY: DE - points to middle of code word for
- ; constant
- ; DESCRIPTION: picks up the contents of the word
- ; following the code word and pushes it onto the stack.
- ;
- CONSTANT:
- XCHG ;HL <- ADDRESS OF CODE WORD
- INX H ;GET CONSTANT
- MOV E,M
- INX H
- MOV D,M
- PUSH D ;PUSH IT ON THE PARAMETER STACK
- JMP NEXT ;RETURN TO INTERPRETER
- ;
- ; Some common constants
- ;
- ZERO: DW CONSTANT ;THREADED CODE CONSTANT
- DW 0
- ;
- ONE: DW CONSTANT ;THREADED CODE CONSTANT
- DW 1
- ;
- NEGONE: DW CONSTANT ;THREADED CODE CONSTANT
- DW -1
- ;
- MEMORY: DW CONSTANT ;LAST AVAILABLE BYTE
- DW 8*1024-1 ;8K SYSTEM
- ;
- ; VARIABLE - code address for variables
- ; ENTRY: DE - points to middle of code word for
- ; variable
- ; DESCRIPTION: pushes address of word following code
- ; word onto the stack
- ;
- VARIABLE:
- INX D ;INCREMENT TO VARIABLE ADDRESS
- PUSH D ;STORE ON PARAMETER STACK
- JMP NEXT ;RETURN TO INTERPRETER
- ;
- ; Top Level External Interpreter Version 1.0
- ;
- ; This routine reads one line of reverse
- ; polish notation from the console and executes it.
- ;
- INTERACT:
- DW TCALL ;THREADED CODE
- ;
- DW PROMPT ;PROMPT THE USER AND
- DW READLINE ;READ A CONSOLE LINE
- ;
- SLOOP: DW SCAN ;SCAN FOR NEXT WORD
- DW IFZ,EXIT-1 ;IF END OF LINE, QUIT
- DW LOOKUP ;ELSE LOOKUP WORD IN DICTIONARY
- DW IFZ,NUMBER-1 ;IF NOT FOUND, TRY NUMBER
- DW EXECUTE ;ELSE EXECUTE IT
- DW JUMP,SLOOP-1 ;AND CONTINUE SCANNING
- ;
- NUMBER: DW CONAXB ;TRY CONVERTING TO NUMBER
- DW IFNZ,SLOOP-1 ;IF SUCCESSFUL, LEAVE ON STACK
- ;AND CONTINUE SCANNING
- DW TPUSH,ERRMSG ;ELSE PUSH ERROR MESSAGE
- DW PRINTS ;AND PRINT IT
- DW PRINTS ;THEN PRINT STRING
- DW TRET ;AND RETURN
- ;
- EXIT: DW DUP,CONBXA ;COPY AND CONVERT TOP OF STACK
- DW PRINTS ;PRINT IT
- DW TRET ;RETURN
- ;
- ERRMSG: DB 13,'Not Defined: '
- ;
- ; LOOKUP - the dictionary lookup routine
- ; ENTRY: TOP - pointer to string to be looked up
- ; EXIT: TOP - -1 if string found in dictionary
- ; 0 if string not found
- ; TOP-1 - pointer to code of found subroutine
- ; or
- ; string pointer if not found
- ; DESCRIPTION: performs a linear search of the
- ; dictionary. Returns the code address if the string
- ; is found, or else the string pointer if not found
- ;
- LOOKUP: DW TCALL ;THREADED CODE
- DW NAMES,PEEKW ;GET TOP OF DICTIONARY
- ;
- SEARCH: DW DUP,PEEKB ;GET CHAR COUNT OF NEXT ENTRY
- DW IFZ,FAIL-1 ;IF END OF DICTIONARY
- ;
- DW MATCH ;ELSE ATTEMPT A MATCH
- DW IFNZ,SUCCEED-1 ;IF SUCCESSFUL MATCH
- ;
- DW FIRST,TADD ;ELSE SKIP STRING
- DW TPUSH,2,TADD ;AND POINTER
- DW JUMP,SEARCH-1 ;AND TRY NEXT ENTRY
- ;
- FAIL: DW TPOP ;DROP DICTIONARY POINTER
- DW ZERO ;LEAVE A ZERO ON THE STACK
- DW TRET ;AND QUIT
- ;
- SUCCEED:
- DW SWAP,TPOP ;DROP STRING POINTER
- DW FIRST,TADD,PEEKW ;GET CODE POINTER
- DW NEGONE ;PUSH A MINUS ONE
- DW TRET ;AND RETURN
- ;
- ; Names - address of dictionary names
- ;
- NAMES: DW VARIABLE ;THREADED CODE VARIABLE
- DW NAMEBEG ;BEGINNING OF NAMES
- ;
- ; MATCH - match strings
- ; ENTRY: TOP - ptr to string
- ; TOP-1 - ptr to another string
- ; EXIT: TOP - -1 if strings are the same
- ; 0 if strings do not match
- ; TOP-1 - ptr to first string
- ; TOP-2 - ptr to second string
- ; DESCRIPTION: written in assembly to speed things up
- ;
- MATCH: DW $+2 ;CODE
- POP H ;FIRST STRING
- POP D ;SECOND STRING
- PUSH D ;LEAVE ON STACK
- PUSH H
- LDAX D ;GET 2ND COUNT
- CMP M ;COMPARE WITH FIRST
- JNZ MATCHF ;IF NO MATCH
- ;ELSE TRY STRING MATCHING
- MOV B,A
- MATCH1: INX H ;NEXT BYTE
- INX D
- LDAX D
- CMP M
- JNZ MATCHF ;IF NO MATCH
- DCR B ;ELSE DEC COUNT
- JNZ MATCH1 ;IF MORE TO COMPARE
- LXI H,-1 ;ELSE PUSH SUCCESS
- PUSH H
- JMP NEXT
- ;
- MATCHF: LXI H,0 ;FAILURE
- PUSH H
- JMP NEXT
- ;
- ; EXECUTE - execute routine at top of stack
- ; ENTRY: TOP - address of routine to be executed
- ; EXIT: DE - middle of word addressed by top
- ; DESCRIPTION: The address is of a threaded code
- ; interpreter routine, so the contents of the
- ; first word is an executable address. EXECUTE
- ; gets that address and jumps to it, leaving DE
- ; in the same state that the main interpreter
- ; loop (NEXT) would have.
- ;
- EXECUTE:
- DW $+2 ;CODE
- POP H ;GET ADDRESS
- MOV E,M ;GET FIRST WORD
- INX H
- MOV D,M
- XCHG ;AND JUMP TO IT
- PCHL
- ;
- ; READLINE - fill console buffer
- ; DESCRIPTION: reads characters from the console, echoing them
- ; to the screen and storing them in the console buffer,
- ; beginning in the third character of the buffer.
- ; Stops on encountering a carriage return and stores a
- ; final zero after the other characters.
- ; Takes appropriate action for a backspace character.
- ;
- READLINE:
- DW TCALL ;THREADED CALL
- DW ZERO ;MARK BUFFER AS UNSCANNED
- DW CONBUF,POKEB
- ;
- DW CONBUF,INC,INC ;PUSH FIRST BYTE OF BUFFER
- ;
- RLOOP: DW DUP ;DUPLICATE BUFFER POINTER
- DW CIN ;GET CHARACTER
- DW DUP,COUT ;ECHO TO SCREEN
- ;
- DW DUP,TPUSH,08H ;COMPARE WITH BACKSPACE
- DW IFEQ,BKSP-1
- ;
- DW DUP,TPUSH,0DH ;COMPARE WITH CARRIAGE RETURN
- DW IFEQ,EOL-1
- ;
- DW SWAP,POKEB ;IF NEITHER, STORE IN BUFFER
- DW INC ;INCREMENT BUFFER POINTER
- DW JUMP,RLOOP-1 ;AND KEEP READING
- ;
- BKSP: DW TPOP,TPOP ;DROP BS AND BUFFER PTR COPY
- DW DEC ;BACKUP POINTER
- DW TPUSH,20H,COUT ;PRINT A SPACE
- DW TPUSH,08H,COUT ;AND ANOTHER BACKSPACE
- DW JUMP,RLOOP-1
- ;
- EOL: DW TPOP,TPOP ;DROP CR AND BUFFER PTR COPY
- DW ZERO,SWAP,POKEB ;STORE FINAL ZERO
- DW TPUSH,0AH,COUT ;PRINT A LINE FEED
- DW TRET ;AND RETURN
- ;
- ; Console Buffer
- ; DESCRIPTION: First byte contains the scan pointer which
- ; points to the next byte to be scanned. The remaining bytes
- ; contain characters read from the console.
- ;
- CONBUF: DW VARIABLE ;THREADED CODE VARIABLE
- DS 101D ;LONG ENOUGH FOR MOST SCREENS
- ;
- ; PROMPT - prompt the user
- ; DESCRIPTION: clears to a new line and prints a hyphen
- ;
- PROMPT: DW TCALL ;THREADED CODE
- DW TPUSH,PRMSG ;PUSH PROMPT MESSAGE
- DW PRINTS ;AND PRINT IT
- DW TRET
- ;
- PRMSG: DB 3,0DH,0AH,'-'
- ;
- ; PRINTS - prints string
- ; ENTRY: TOP - points to string
- ; DESCRIPTION: Uses first byte of string as a character count
- ;
- PRINTS: DW TCALL ;THREADED CODE
- DW FIRST ;GET COUNT
- PRINTS1:
- DW DUP,IFZ,PRINTX-1 ;IF DONE RETURN
- DW SWAP,FIRST ;ELSE GET NEXT CHARACTER
- DW COUT ;PRINT IT
- DW SWAP,DEC ;DECREMENT COUNT
- DW JUMP,PRINTS1-1 ;AND KEEP LOOPING
- ;
- PRINTX: DW TPOP,TPOP ;DROP COUNT AND POOINTER
- DW TRET ;THEN RETURN
- ;
- ; FIRST - get next byte of string on stack
- ; ENTRY: TOP - ptr to string
- ; EXIT: TOP - first character of string
- ; TOP-1 - ptr to rest of string
- ; DESCRIPTION: useful for advancing through strings a byte
- ; at a time.
- ;
- FIRST: DW $+2 ;CODE
- POP H ;GET POINTER
- MOV C,M ;BC <- CHARACTER
- MVI B,0
- INX H ;BUMP POINTER
- PUSH H ;RESTORE POINTER
- PUSH B ;ADD CHARACTER
- JMP NEXT ;CONTINUE
- ;
- ; COUT - character output routine
- ; ENTRY: TOP - character to print
- ; DESCRIPTION: uses operating system to print character
- ; <<<=== NOTE: MODIFIED FOR VAR. SIZE CPM SYS (RGF) ===>>>
- ;
- COUT: DW $+2 ;CODE
- POP B ;C <- CHARACTER
- VCOUT: CALL 7E0CH ;PRINT IT (<<MODIFIED AT INIT>>)
- JMP NEXT ;RETURN
- ;
- ; CIN - character input routine
- ; EXIT: TOP - character read from console
- ; DESCRIPTION: Uses operating system
- ; <<<=== NOTE: MODIFIED FOR VAR. SIZE CPM SYS (RGF) ===>>>
- ;
- CIN: DW $+2 ;CODE
- VCIN: CALL 7E09H ;READ CHARACTER ((<<MODIFIED AT INIT>>)
- MOV L,A ;HL <- CHARACTER
- MVI H,0
- PUSH H ;PUSH ON STACK
- JMP NEXT ;RETURN
- ;
- ; SCAN - Scan for next word
- ; ENTRY: No Values Expected
- ; EXIT: TOP - -1 if word found, 0 if word not found
- ; TOP-1 - ptr to word if found (else nothing)
- ; DESCRIPTION: first byte of buffer contains a counter of
- ; characters already scanned. The next word is moved to the
- ; beginning of the line with a leading byte count.
- ;
- SCAN: DW $+2 ;CODE
- LXI H,CONBUF+2 ;BC <- CHARACTER COUNT
- MOV C,M
- MVI B,0
- INR M ;TEST FOR END OF LINE ALREADY
- JZ SCANX ;IF YES
- INX H ;HL <- SCANNING START POINT
- DAD B
- MOV B,C ;B <- CHARACTER COUNT
- SCAN1: INX H ;INCREMENT POINTER
- INR B ;INCREMENT COUNT
- MOV A,M ;GET NEXT CHARACTER
- ORA A ;TEST FOR END OF LINE
- JZ SCANX ;IF YES,
- CPI 20H ;ELSE, CHECK FOR BLANK
- JZ SCAN1 ;IF YES, SKIP IT
- LXI D,CONBUF+3 ;ELSE BEGIN MOVING WORD
- MVI C,0 ;C <- SIZE OF STRING
- SCAN2: INX D
- STAX D
- INR C ;INC WORD SIZE
- INR B ;INC SCANNED CHAR COUNT
- INX H ;GET NEXT BYTE
- MOV A,M
- ORA A ;TEST FOR END OF LINE
- JNZ SCAN3 ;IF NOT,
- MVI B,-1 ;ELSE SET EOL FLAG
- MVI A,20H ;AND CHANGE EOL TO DELIMETER
- SCAN3: CPI 20H ;CHECK FOR SPACE
- JNZ SCAN2 ;IF NOT YET
- LXI H,CONBUF+2 ;ELSE SAVE SCANNED CHAR COUNT
- MOV M,B
- INX H ;AND WORD SIZE
- MOV M,C
- PUSH H ;AND RETURN WORD POINTER
- LXI H,-1
- PUSH H
- JMP NEXT
- ;
- SCANX: MVI A,-1 ;HIT END OF LINE
- STA CONBUF+2 ;MARK BUFFER EMPTY
- LXI H,0 ;RETURN A ZERO
- PUSH H
- JMP NEXT
- ;
- ; CONBXA - convert binary to ascii
- ;
- ; ENTRY: TOP - 16 bit positive integer
- ; EXIT: TOP - address of converted ASCII string
- ; DESCRIPTION: pushes the digits of the number
- ; on to the stack, least significant digits first.
- ; Then pops them up and stores them in a local
- ; buffer.
- ;
- CONBXA: DW TCALL ;THREADED CODE
- DW NEGONE,SWAP ;MARK END OF STRING WITH -1
- CONB1: DW TPUSH,10,DIV ;DIVIDE NUMBER BY 10
- DW SWAP ;PUT QUOTIENT ON TOP
- DW DUP
- DW IFNZ,CONB1-1 ;CONTINUE UNTIL Q = 0
- ;
- DW TPOP ;THEN DROP QUOTIENT
- DW ZERO ;STORE BYTE IN FIRST
- DW NBUFR,POKEB ;BYTE OF BUFFER
- ;
- CONB2: DW DUP,NEGONE ;TEST FOR END OF STRING
- DW IFEQ,CONB3-1 ;IF YES
- DW NBUFR,PEEKB ;ELSE, INCREMENT BYTE COUNT
- DW INC
- DW NBUFR,POKEB
- DW TPUSH,'0',TADD ;CONVERT DIGIT TO ASCII
- ;AND STORE IN NEXT LOCATION
- DW NBUFR
- DW NBUFR,PEEKB,TADD
- DW POKEB
- DW JUMP,CONB2-1 ;REPEAT
- ;
- CONB3: DW TPOP ;DROP END OF STRING MARKER
- DW NBUFR ;PUSH RETURN BUFFER ADDRESS
- DW TRET ;AND RETURN
- ;
- NBUFR: DW VARIABLE ;THREADED VARIABLE
- DS 10 ;PLENTY LONG ENOUGH
- ;
- ; CONAXB - convert ASCII decimal string to binary
- ; ENTRY: TOP - pointer to string
- ; EXIT: TOP - -1 if converted to binary
- ; 0 if not
- ; TOP-1 - value of number if converted
- ; ptr to string if not
- ; DESCRIPTION: converts only positive, unsigned
- ; integers. WRitten in assembly because I had it around
- ; and didn't want to rewrite it in threaded code.
- ;
- CONAXB: DW $+2 ;CODE
- POP D ;GET STRING POINTER
- PUSH D ;BUT LEAVE ON STACK
- LDAX D ;GET BYTE COUNT
- MOV B,A
- LXI H,0 ;STARTING VALUE
- ;
- CONA1: INX D
- LDAX D ;GET NEXT CHARACTER
- CPI '0' ;TEST FOR DIGIT
- JC CONAX ;IF NOT
- CPI '9'+1
- JNC CONAX ;IF NOT
- SUI '0' ;CONVERT TO BINARY
- PUSH D ;SAVE POINTER
- DAD H ;MULTIPLY CURRENT VALUE BY 10
- PUSH H
- DAD H
- DAD H
- POP D
- DAD D
- MOV E,A ;ADD NEW BINARY DIGIT
- MVI D,0
- DAD D
- POP D ;RESTORE POINTER
- DCR B ;DEC COUNT
- JNZ CONA1 ;CONTINUE TILL DONE
- POP D ;THEN DROP POINTER
- PUSH H ;PUSH NUMBER
- LXI H,-1 ;AND -1
- PUSH H
- JMP NEXT
- ;
- CONAX: LXI H,0 ;FAILURE: PUSH A ZERO
- PUSH H
- JMP NEXT
- ;
- ; DIV - 16 bit divide
- ; ENTRY: TOP - divisor
- ; TOP-1 - dividend
- ; EXIT: TOP - remainder
- ; TOP-1 - quotient
- ; DESCRIPTION: performs a 32 bit by 16 bit division for
- ; positive integers only. The quotient must be resolved
- ; in 16 bits.
- ;
- DIV: DW $+2 ;CODE
- POP B ;BC <- DIVISOR
- POP D ;HLDE <- DIVIDEND
- LXI H,0
- CALL DIV1 ;DO DIVISION
- PUSH D ;PUSH QUOTIENT
- PUSH H ;PUSH REMAINDER
- JMP NEXT
- ;
- DIV1: DCX B ;NEGATE BC
- MOV A,B
- CMA
- MOV B,A
- MOV A,C
- CMA
- MOV C,A
- MVI A,16D ;ITERATION COUNT
- DIV2: DAD H ;SHIFT HLDE
- PUSH PSW ;SAVE OVERFLOW
- XCHG
- DAD H
- XCHG
- JNC DIV3
- INR L
- DIV3: POP PSW ;GET OVERFLOW
- JC DIV5 ;IF OVERFLOW, FORCE SUBTRACTION
- PUSH H ;ELSE, SAVE DIVIDEND
- DAD B ;ATTEMPT SUBTRACTION
- JC DIV4 ;IF IT GOES
- POP H ;ELSE RESTORE DIVIDEND
- JMP DIV6
- DIV4: INR E ;INCREMENT QUOTIENT
- INX SP ;DROP OLD DIVIDEND
- INX SP
- JMP DIV6
- DIV5: DAD B ;FORCE SUBTRACTION
- INR E ;INC QUOTIENT
- DIV6: DCR A ;DECREMENT COUNT
- JNZ DIV2 ;REPEAT UNTIL DONE
- RET
- ;
- ; The Names in the dictionary
- ; Notice that the actual printed names are chosen for typing
- ; convenience and do not necessarily match the internal names,
- ; which must conform to the assembler's rules. Also, not all
- ; functions have been included here.
- ;
- NAMEBEG EQU $
- ;
- DB 1,'+'
- DW TADD
- ;
- DB 1,'-'
- DW TSUB
- ;
- DB 4,'/MOD'
- DW DIV
- ;
- DB 7,'EXECUTE'
- DW EXECUTE
- ;
- DB 5,'CLEAR'
- DW CLEAR
- ;
- DB 5,'MATCH'
- DW MATCH
- ;
- DB 6,'LOOKUP'
- DW LOOKUP
- ;
- DB 4,'EXEC'
- DW EXEC
- ;
- DB 6,'MEMORY'
- DW MEMORY
- ;
- DB 6,'CONBXA'
- DW CONBXA
- ;
- DB 3,'INC'
- DW INC
- ;
- DB 3,'DEC'
- DW DEC
- ;
- DB 5,'MINUS'
- DW MINUS
- ;
- DB 5,'PEEKW'
- DW PEEKW
- ;
- DB 5,'PEEKB'
- DW PEEKB
- ;
- DB 5,'POKEW'
- DW POKEW
- ;
- DB 5,'POKEB'
- DW POKEB
- ;
- DB 3,'POP'
- DW TPOP
- ;
- DB 4,'SWAP'
- DW SWAP
- ;
- DB 3,'DUP'
- DW DUP
- ;
- DB 5,'FIRST'
- DW FIRST
- ;
- DB 0 ;END OF DICTIONARY
- ;
- NAMEEND EQU $-1
- ;
- DICSIZE EQU NAMEEND-NAMEBEG+1 ;DICTIONARY SIZE IN BYTES
- ;
- ; Initialition Code
- ; Executed on start up of system but eventually overwritten by
- ; the expanding dictionary
- ;
- ; DICMOVE - moves the dictionary names
- ; to the top of available memory
- ;
- ; <<<=== Modified For CPM initialization (RGF) ===>>>
- ;
- DICMOVE:
- LHLD 6
- SHLD MEMORY+2 ;INIT TOP OF MEMORY
- XCHG ;DE <- TOP OF MEMORY
- LXI H,NAMEEND ;HL <- SOURCE (END OF NAMES)
- LXI B,DICSIZE ;BC <- BYTE COUNT
- ;TRANSFER LOOP
- DIC1: MOV A,M ;GET NEXT BYTE
- STAX D ;MOVE IT
- DCX H ;DEC SOURCE POINTER
- DCX D ;DEC TARGET POINTER
- DCX B ;DEC COUNT
- MOV A,B ;TEST FOR ZERO
- ORA C
- JNZ DIC1 ;NOT YET
- ;
- XCHG ;SET DICTIONARY VARIABLE
- INX H
- SHLD NAMES+2
- ;
- LDA 2 ;MODIFIY I/O ROUTINES
- STA VCOUT+2 ; SO THEY WILL WORD
- STA VCIN+2 ; IN ANY SIZE CPM SYSTEM
- ;
- RET
- ;
- ;
- ;
- ;
- END BASE
-