; ; 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 (<>) 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 ((<>) 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