home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / mbug / mbug028.arc / SMFORTH1.ASM < prev    next >
Assembly Source File  |  1979-12-31  |  22KB  |  933 lines

  1. ;
  2. ;    Minimal Forth-like  interpreter as presented
  3. ;    in Kilobaud Magazine, February, 1981
  4. ;    issue, page 76    by Richard Fritzson
  5. ;
  6. ;    Note: this is part 1 of a promised 3-part
  7. ;    system, to include a compiler, and, hopefully,
  8. ;    an editor.
  9. ;
  10. ;
  11. ;                   02/04/81:
  12. ;        typed in by Ron Fowler, Westland, Mich
  13. ;        (modified slightly: the original version
  14. ;        had I/O routines to BIOS hard-coded, and
  15. ;        memory fixed at time of assembly.   Mod-
  16. ;        ified for any size CPM, and dynamic  fix
  17. ;        of memory size at run time)
  18. ;
  19. ;
  20. ;    if you're not using MAC to assemble this program,
  21. ;    delete the next statement:
  22.  
  23.     TITLE    'Threaded Code Interpreter for 8080'
  24. ;
  25. ; Richard Fritzson
  26. ; 29 January 1980      Version 1.0
  27. ;
  28. ; This version contains only the basic internal
  29. ; interpreter and a simple interactive console
  30. ; interpreter.
  31. ;
  32.     ORG    100H    ;START UP ADDRESS
  33. ;
  34. BASE:    LXI    SP,STACK ;INITIALIZE PARAMETER STACK
  35.     CALL    DICMOVE ;MOVE DICTIONARY TO HIGH MEMORY
  36.     LXI    H,TOP-1 ;SET PC TO TOP LEVEL LOOP
  37.     SHLD    PC
  38.     JMP    NEXT    ;AND START INTERPRETER
  39. ;
  40. ; TOP - Top Level System Loop
  41. ; DESCRIPTION: TOP in an infinite
  42. ; loop which picks up the contents of the
  43. ; EXEC variable and executes it.
  44. ;
  45. TOP:    DW    EXEC,PEEKW    ;GET TOP LEVEL PROGRAM
  46.     DW    EXECUTE     ;RUN IT
  47.     DW    JUMP,TOP-1    ;AND LOOP
  48. ;
  49. ; EXEC - address of top level routine
  50. ;
  51. EXEC:    DW    VARIABLE    ;THREADED CODE VARIABLE
  52.     DW    INTERACT    ;ADDRESS OF USER INTERPRETER
  53. ;
  54. ; Reserved Stack Space
  55. ;
  56.     DS    128        ;PARAMETER STACK
  57. STACK    EQU    $
  58.     PAGE
  59. ;
  60. ; The interpreter's architecture: a program counter and a stack
  61. ;
  62. PC    DW    0    ;A 16 BIT POINTER INTO THE MIDDLE OF
  63.             ;THE CURRENT INSTRUCTION (NOT THE
  64.             ;FIRST BYTE, BUT THE SECOND)
  65. ;
  66. RSTACK    DW    $+2    ;THE STACK POINTER POINTS TO THE NEXT
  67.             ;AVAILABLE STACK POSITION (NOT THE
  68.             ;TOPMOST OCCUPIED POSITION)
  69. ;
  70.     DS    80H    ;RESERVED STACK SPACE
  71. ;
  72. ; RPUSH - push DE on stack
  73. ; ENTRY: DE - number to be pushed on stack
  74. ; EXIT:  DE - is unchanged
  75. ; DESCRIPTION: this code is illustrative of how the
  76. ; stack works.    However it is not used in the system and
  77. ; can be left out.
  78. ;
  79. RPUSH:    LHLD    RSTACK    ;GET STACK POINTER
  80.     MOV    M,E    ;STORE LOW BYTE
  81.     INX    H    ;BUMP POINTER TO NEXT BYTE
  82.     MOV    M,D    ;STORE HIGH BYTE
  83.     INX    H    ;BUMP POINTER TO NEXT EMPTY SLOT
  84.     SHLD    RSTACK    ;RESTORE POINTER
  85.     RET
  86. ;
  87. ; RPOP - pop DE from stack
  88. ; ENTRY: No Register Values Expected
  89. ; EXIT:  DE - top element of RSTACK
  90. ; DESCRIPTION: this code is illustrative of how the
  91. ; stack works.    However it is not used in the system and
  92. ; can be left out.
  93. ;
  94. RPOP:    LHLD    RSTACK    ;GET STACK POINTER
  95.     DCX    H    ;DROP TO FIRST STACK POSITION
  96.     MOV    D,M    ;GET HIGH BYTE
  97.     DCX    H
  98.     MOV    E,M    ;GET LOW BYTE
  99.     SHLD    RSTACK    ;RESTORE STACK POINTER
  100.     RET
  101. ;
  102. ; NEXT - main internal interpreter loop
  103. ; ENTRY: PC - points into the instruction just completed
  104. ; EXIT:  PC - incremented by 2, points to next
  105. ;          instruction
  106. ;     DE - points to middle of first word of
  107. ;          next routine (i.e. (PC)+1)
  108. ; DESCRIPTION: increments the PC; picks up the code
  109. ; word of the next routine and jumps to it.
  110. ;
  111. NEXT:    LHLD    PC    ;INCREMENT PROGRAM COUNTER
  112.     INX    H    ;  WHILE LOADING DE WITH
  113.     MOV    E,M    ;  NEXT INSTRUCTION
  114.     INX    H
  115.     MOV    D,M
  116.     SHLD    PC
  117.     XCHG        ;PICK UP WORD ADDRESSED
  118.     MOV    E,M    ;BY NEXT INSTRUCTION (WHICH
  119.     INX    H    ;  IS CODE, TCALL OR SOME OTHER
  120.     MOV    D,M    ;  EXECUTABLE ADDRESS)
  121.     XCHG        ;  AND
  122.     PCHL        ;  JUMP TO IT
  123. ;
  124. ; TCALL - the threaded call routine
  125. ; ENTRY: DE - middle of first word of routine being called
  126. ; EXIT:  No Register Values Returned
  127. ; DESCRIPTION: pushes the current congtents of the PC
  128. ; onto the return stack; makes DE the new PC
  129. ;
  130. TCALL:    LHLD    PC    ;GET OLD PROGRAM COUNTER
  131.     XCHG        ;REPLACE WITH DE
  132.     SHLD    PC
  133.     LHLD    RSTACK    ;PUSH OLD PC ON RSTACK
  134.     MOV    M,E
  135.     INX    H
  136.     MOV    M,D
  137.     INX    H
  138.     SHLD    RSTACK
  139.     JMP    NEXT    ;BACK TO INTERPRETER
  140. ;
  141. ; TRET - the threaded code return
  142. ; DESCRIPTION: pops the top element of the
  143. ;           return stack and puts it into the PC.
  144. ;
  145. TRET:    DW    $+2    ;CODE
  146.     LHLD    RSTACK    ;GET STACK POINTER
  147.     DCX    H    ;HIGH BYTE OF TOP ELEMENT
  148.     MOV    D,M
  149.     DCX    H    ;LOW BYTE OF TOP ELEMENT
  150.     MOV    E,M
  151.     SHLD    RSTACK    ;RESTORE STACK POINTER
  152.     XCHG        ;STORE TOP OF STACK IN PC
  153.     SHLD    PC
  154.     JMP    NEXT    ;BACK TO INTERPRETER
  155. ;
  156. ; Simple arithmetic routines
  157. ;
  158. ; INC - increment the top of the stack
  159. ;
  160. INC:    DW    $+2    ;CODE
  161.     POP    H    ;GET TOP
  162.     INX    H    ;INCREMENT
  163.     PUSH    H    ;RESTORE
  164.     JMP    NEXT
  165. ;
  166. ; DEC - decrement the top of the stack
  167. ;
  168. DEC:    DW    $+2    ;CODE
  169.     POP    H    ;GET TOP
  170.     DCX    H    ;DECREMENT
  171.     PUSH    H    ;RESTORE
  172.     JMP    NEXT
  173. ;
  174. ; TADD - add the top two elements of the stack
  175. ;
  176. TADD:    DW    $+2    ;CODE
  177.     POP    H    ;FIRST ELEMENT
  178.     POP    D    ;SECOND ELEMENT
  179.     DAD    D    ;ADD 'EM
  180.     PUSH    H    ;PUSH RESULT
  181.     JMP    NEXT
  182. ;
  183. ; MINUS - negate top of stack
  184. ;
  185. MINUS:    DW    $+2    ;CODE
  186.     POP    H    ;GET TOP
  187.     CALL    MINUSH    ;NEGATE IT
  188.     PUSH    H    ;PUSH IT
  189.     JMP    NEXT
  190. ;
  191. MINUSH: DCX    H    ;GOOD OLE 2S COMPLEMENT
  192.     MOV    A,H
  193.     CMA
  194.     MOV    H,A
  195.     MOV    A,L
  196.     CMA
  197.     MOV    L,A
  198.     RET
  199. ;
  200. ; TSUB - subtract TOP from TOP-1
  201. ;
  202. TSUB:    DW    TCALL    ;THREADED CODE
  203.     DW    MINUS    ;NEGATE TOP
  204.     DW    TADD    ;AND ADD
  205.     DW    TRET
  206. ;
  207. ; PEEKB - retrieve a byte from memory
  208. ; ENTRY: TOP - address
  209. ; EXIT:  TOP - byte at address
  210. ;
  211. PEEKB:    DW    $+2    ;CODE
  212.     POP    H    ;GET ADDRESS
  213.     MOV    E,M    ;GET BYTE
  214.     MVI    D,0
  215.     PUSH    D    ;SAVE
  216.     JMP    NEXT
  217. ;
  218. ; PEEKW - retrieve a word from memory
  219. ; ENTRY: TOP - address
  220. ; EXIT:  TOP - word at address
  221. ;
  222. PEEKW:    DW    $+2    ;CODE
  223.     POP    H    ;GET ADDRESS
  224.     MOV    E,M    ;GET WORD
  225.     INX    H
  226.     MOV    D,M
  227.     PUSH    D    ;SAVE
  228.     JMP    NEXT
  229. ;
  230. ; POKEB - store byte in memory
  231. ; ENTRY: TOP - address
  232. ;     TOP-1 - byte to store
  233. ; EXIT:  No Values Returned
  234. ;
  235. POKEB:    DW    $+2    ;CODE
  236.     POP    H    ;GET ADDRESS
  237.     POP    D    ;GET BYTE
  238.     MOV    M,E    ;STORE
  239.     JMP    NEXT
  240. ;
  241. ; POKEW - store word in memory
  242. ; ENTRY: TOP - address
  243. ;     TOP-1 - word to store
  244. ; EXIT: No Values returned
  245. ;
  246. POKEW:    DW    $+2    ;CODE
  247.     POP    H    ;GET ADDRESS
  248.     POP    D    ;GET WORD
  249.     MOV    M,E    ;STORE WORD
  250.     INX    H
  251.     MOV    M,D
  252.     JMP    NEXT
  253. ;
  254. ; Some standart threaded code functions
  255. ; TPUSH - push the next word onto the stack
  256. ;
  257. TPUSH:    DW    $+2    ;CODE
  258.     LHLD    PC    ;GET PROGRAM COUNTER
  259.     INX    H    ;ADVANCE TO NEXT WORD
  260.     MOV    E,M    ;AND PICK UP CONTENTS
  261.     INX    H
  262.     MOV    D,M
  263.     SHLD    PC    ;STORE NEW PROGRAM COUNTER
  264.     PUSH    D    ;PUSH WORD ONTO PARAM STACK
  265.     JMP    NEXT    ;CONTINUE
  266. ;
  267. ; TPOP - drop the top of the parameter stack
  268. ;
  269. TPOP:    DW    $+2    ;CODE
  270.     POP    H    ;POP ONE ELEMENT
  271.     JMP    NEXT    ;  AND CONTINUE
  272. ;
  273. ; SWAP - exchange top two elements of the stack
  274. ;
  275. SWAP:    DW    $+2    ;CODE
  276.     POP    H    ;GET ONE ELEMENT
  277.     XTHL        ;XCHG
  278.     PUSH    H    ;PUT BACK
  279.     JMP    NEXT    ;AND CONTINUE
  280. ;
  281. ; DUP - duplicate the top of the stack
  282. ; DESCRIPTION: often used before functions which
  283. ; consume the top of the stack (e.g. conditional jumps)
  284. ;
  285. DUP:    DW    $+2    ;CODE
  286.     POP    H    ;GET TOP
  287.     PUSH    H    ;SAVE IT TWICE
  288.     PUSH    H
  289.     JMP    NEXT
  290. ;
  291. ; CLEAR - clear the stack
  292. ;
  293. CLEAR:    DW    $+2    ;CODE
  294.     LXI    SP,STACK ;RESET STACK POINTER;
  295.     JMP    NEXT
  296. ;
  297. ; Threaded Code Jumps
  298. ;
  299. ; All Jumps are to absolute locations
  300. ; All Conditional jumps consume the
  301. ; elements of the stack that they test
  302. ;
  303. ; JUMP - unconditional jump
  304. ;
  305. JUMP:    DW    $+2    ;CODE
  306. JUMP1:    LHLD    PC    ;GET PROGRAM COUNTER
  307.     INX    H    ;GET NEXT WORD
  308.     MOV    E,M
  309.     INX    H
  310.     MOV    D,M
  311.     XCHG        ;MAKE IT THE PC
  312.     SHLD    PC
  313.     JMP    NEXT
  314. ;
  315. ; IFZ - jump if top is zero
  316. ;
  317. IFZ:    DW    $+2    ;CODE
  318.     POP    H    ;GET TOP
  319.     MOV    A,H    ;TEST FOR ZERO
  320.     ORA    L
  321.     JZ    JUMP1    ;IF YES, JUMP
  322. SKIP:    LHLD    PC    ;ELSE SIMPLY SKIP NEXT WORD
  323.     INX    H
  324.     INX    H
  325.     SHLD    PC
  326.     JMP    NEXT
  327. ;
  328. ; IFNZ - jump if top not zero
  329. ;
  330. IFNZ:    DW    $+2    ;CODE
  331.     POP    H    ;GET TOP
  332.     MOV    A,H    ;TEST FOR ZERO
  333.     ORA    L
  334.     JNZ    JUMP1    ;IF NOT, JUMP
  335.     JMP    SKIP    ;ELSE DON'T
  336. ;
  337. ; IFEQ - jump if TOP = TOP-1
  338. ;
  339. IFEQ:    DW    $+2    ;CODE
  340.     POP    H    ;GET TOP
  341.     CALL    MINUSH    ;NEGATE IT
  342.     POP    D    ;GET TOP-1
  343.     DAD    D    ;ADD 'EM
  344.     MOV    A,H    ;TEST FOR ZERO
  345.     ORA    L
  346.     JZ    JUMP1    ;IF EQUAL, JUMP
  347.     JMP    SKIP    ;IF NOT, DON'T
  348. ;
  349. ; Implementation of Constants and Variables in a
  350. ; threaded code system
  351. ;
  352.  ;CONSTANT - code address for constants
  353. ; ENTRY: DE - points to middle of code word for
  354. ;          constant
  355. ; DESCRIPTION: picks up the contents of the word
  356. ; following the code word and pushes it onto the stack.
  357. ;
  358. CONSTANT:
  359.     XCHG        ;HL <- ADDRESS OF CODE WORD
  360.     INX    H    ;GET CONSTANT
  361.     MOV    E,M
  362.     INX    H
  363.     MOV    D,M
  364.     PUSH    D    ;PUSH IT ON THE PARAMETER STACK
  365.     JMP    NEXT    ;RETURN TO INTERPRETER
  366. ;
  367. ; Some common constants
  368. ;
  369. ZERO:    DW    CONSTANT    ;THREADED CODE CONSTANT
  370.     DW    0
  371. ;
  372. ONE:    DW    CONSTANT    ;THREADED CODE CONSTANT
  373.     DW    1
  374. ;
  375. NEGONE: DW    CONSTANT    ;THREADED CODE CONSTANT
  376.     DW    -1
  377. ;
  378. MEMORY: DW    CONSTANT    ;LAST AVAILABLE BYTE
  379.     DW    8*1024-1    ;8K SYSTEM
  380. ;
  381. ; VARIABLE - code address for variables
  382. ; ENTRY: DE - points to middle of code word for
  383. ;          variable
  384. ; DESCRIPTION: pushes address of word following code
  385. ;           word onto the stack
  386. ;
  387. VARIABLE:
  388.     INX    D    ;INCREMENT TO VARIABLE ADDRESS
  389.     PUSH    D    ;STORE ON PARAMETER STACK
  390.     JMP    NEXT    ;RETURN TO INTERPRETER
  391. ;
  392. ; Top Level External Interpreter Version 1.0
  393. ;
  394. ; This routine reads one line of reverse
  395. ; polish notation from the console and executes it.
  396. ;
  397. INTERACT:
  398.     DW    TCALL        ;THREADED CODE
  399. ;
  400.     DW    PROMPT        ;PROMPT THE USER AND
  401.     DW    READLINE    ;READ A CONSOLE LINE
  402. ;
  403. SLOOP:    DW    SCAN        ;SCAN FOR NEXT WORD
  404.     DW    IFZ,EXIT-1    ;IF END OF LINE, QUIT
  405.     DW    LOOKUP        ;ELSE LOOKUP WORD IN DICTIONARY
  406.     DW    IFZ,NUMBER-1    ;IF NOT FOUND, TRY NUMBER
  407.     DW    EXECUTE     ;ELSE EXECUTE IT
  408.     DW    JUMP,SLOOP-1    ;AND CONTINUE SCANNING
  409. ;
  410. NUMBER: DW    CONAXB        ;TRY CONVERTING TO NUMBER
  411.     DW    IFNZ,SLOOP-1    ;IF SUCCESSFUL, LEAVE ON STACK
  412.                 ;AND CONTINUE SCANNING
  413.     DW    TPUSH,ERRMSG    ;ELSE PUSH ERROR MESSAGE
  414.     DW    PRINTS        ;AND PRINT IT
  415.     DW    PRINTS        ;THEN PRINT STRING
  416.     DW    TRET        ;AND RETURN
  417. ;
  418. EXIT:    DW    DUP,CONBXA    ;COPY AND CONVERT TOP OF STACK
  419.     DW    PRINTS        ;PRINT IT
  420.     DW    TRET        ;RETURN
  421. ;
  422. ERRMSG: DB    13,'Not Defined: '
  423. ;
  424. ; LOOKUP - the dictionary lookup routine
  425. ; ENTRY: TOP  -  pointer to string to be looked up
  426. ; EXIT:  TOP  -  -1 if string found in dictionary
  427. ;          0 if string not found
  428. ;     TOP-1 - pointer to code of found subroutine
  429. ;            or
  430. ;         string pointer if not found
  431. ; DESCRIPTION: performs a linear search of the
  432. ; dictionary. Returns the code address if the string
  433. ; is found, or else the string pointer if not found
  434. ;
  435. LOOKUP: DW    TCALL        ;THREADED CODE
  436.     DW    NAMES,PEEKW    ;GET TOP OF DICTIONARY
  437. ;
  438. SEARCH: DW    DUP,PEEKB    ;GET CHAR COUNT OF NEXT ENTRY
  439.     DW    IFZ,FAIL-1    ;IF END OF DICTIONARY
  440. ;
  441.     DW    MATCH        ;ELSE ATTEMPT A MATCH
  442.     DW    IFNZ,SUCCEED-1    ;IF SUCCESSFUL MATCH
  443. ;
  444.     DW    FIRST,TADD    ;ELSE SKIP STRING
  445.     DW    TPUSH,2,TADD    ;AND POINTER
  446.     DW    JUMP,SEARCH-1    ;AND TRY NEXT ENTRY
  447. ;
  448. FAIL:    DW    TPOP        ;DROP DICTIONARY POINTER
  449.     DW    ZERO        ;LEAVE A ZERO ON THE STACK
  450.     DW    TRET        ;AND QUIT
  451. ;
  452. SUCCEED:
  453.     DW    SWAP,TPOP    ;DROP STRING POINTER
  454.     DW    FIRST,TADD,PEEKW ;GET CODE POINTER
  455.     DW    NEGONE        ;PUSH A MINUS ONE
  456.     DW    TRET        ;AND RETURN
  457. ;
  458. ; Names - address of dictionary names
  459. ;
  460. NAMES:    DW    VARIABLE    ;THREADED CODE VARIABLE
  461.     DW    NAMEBEG     ;BEGINNING OF NAMES
  462. ;
  463. ; MATCH - match strings
  464. ; ENTRY: TOP   - ptr to string
  465. ;     TOP-1 - ptr to another string
  466. ; EXIT:  TOP   - -1 if strings are the same
  467. ;          0 if strings do not match
  468. ;     TOP-1 - ptr to first string
  469. ;     TOP-2 - ptr to second string
  470. ; DESCRIPTION: written in assembly to speed things up
  471. ;
  472. MATCH:    DW    $+2        ;CODE
  473.     POP    H        ;FIRST STRING
  474.     POP    D        ;SECOND STRING
  475.     PUSH    D        ;LEAVE ON STACK
  476.     PUSH    H
  477.     LDAX    D        ;GET 2ND COUNT
  478.     CMP    M        ;COMPARE WITH FIRST
  479.     JNZ    MATCHF        ;IF NO MATCH
  480.                 ;ELSE TRY STRING MATCHING
  481.     MOV    B,A
  482. MATCH1: INX    H        ;NEXT BYTE
  483.     INX    D
  484.     LDAX    D
  485.     CMP    M
  486.     JNZ    MATCHF        ;IF NO MATCH
  487.     DCR    B        ;ELSE DEC COUNT
  488.     JNZ    MATCH1        ;IF MORE TO COMPARE
  489.     LXI    H,-1        ;ELSE PUSH SUCCESS
  490.     PUSH    H
  491.     JMP    NEXT
  492. ;
  493. MATCHF: LXI    H,0        ;FAILURE
  494.     PUSH    H
  495.     JMP    NEXT
  496. ;
  497. ; EXECUTE - execute routine at top of stack
  498. ; ENTRY: TOP  - address of routine to be executed
  499. ; EXIT:  DE   - middle of word addressed by top
  500. ; DESCRIPTION: The address is of a threaded code
  501. ; interpreter routine, so the contents of the
  502. ; first word is an executable address. EXECUTE
  503. ; gets that address and jumps to it, leaving DE
  504. ; in the same state that the main interpreter
  505. ; loop (NEXT) would have.
  506. ;
  507. EXECUTE:
  508.     DW    $+2        ;CODE
  509.     POP    H        ;GET ADDRESS
  510.     MOV    E,M        ;GET FIRST WORD
  511.     INX    H
  512.     MOV    D,M
  513.     XCHG            ;AND JUMP TO IT
  514.     PCHL
  515. ;
  516. ; READLINE - fill console buffer
  517. ; DESCRIPTION: reads characters from the console, echoing them
  518. ; to the screen and storing them in the console buffer,
  519. ; beginning in the third character of the buffer.
  520. ; Stops on encountering a carriage return and stores a
  521. ; final zero after the other characters.
  522. ; Takes appropriate action for a backspace character.
  523. ;
  524. READLINE:
  525.     DW    TCALL        ;THREADED CALL
  526.     DW    ZERO        ;MARK BUFFER AS UNSCANNED
  527.     DW    CONBUF,POKEB
  528. ;
  529.     DW    CONBUF,INC,INC    ;PUSH FIRST BYTE OF BUFFER
  530. ;
  531. RLOOP:    DW    DUP        ;DUPLICATE BUFFER POINTER
  532.     DW    CIN        ;GET CHARACTER
  533.     DW    DUP,COUT    ;ECHO TO SCREEN
  534. ;
  535.     DW    DUP,TPUSH,08H    ;COMPARE WITH BACKSPACE
  536.     DW    IFEQ,BKSP-1
  537. ;
  538.     DW    DUP,TPUSH,0DH    ;COMPARE WITH CARRIAGE RETURN
  539.     DW    IFEQ,EOL-1
  540. ;
  541.     DW    SWAP,POKEB    ;IF NEITHER, STORE IN BUFFER
  542.     DW    INC        ;INCREMENT BUFFER POINTER
  543.     DW    JUMP,RLOOP-1    ;AND KEEP READING
  544. ;
  545. BKSP:    DW    TPOP,TPOP    ;DROP BS AND BUFFER PTR COPY
  546.     DW    DEC        ;BACKUP POINTER
  547.     DW    TPUSH,20H,COUT    ;PRINT A SPACE
  548.     DW    TPUSH,08H,COUT    ;AND ANOTHER BACKSPACE
  549.     DW    JUMP,RLOOP-1
  550. ;
  551. EOL:    DW    TPOP,TPOP    ;DROP CR AND BUFFER PTR COPY
  552.     DW    ZERO,SWAP,POKEB ;STORE FINAL ZERO
  553.     DW    TPUSH,0AH,COUT    ;PRINT A LINE FEED
  554.     DW    TRET        ;AND RETURN
  555. ;
  556. ; Console Buffer
  557. ; DESCRIPTION: First byte contains the scan pointer which
  558. ; points to the next byte to be scanned. The remaining bytes
  559. ; contain characters read from the console.
  560. ;
  561. CONBUF: DW    VARIABLE    ;THREADED CODE VARIABLE
  562.     DS    101D        ;LONG ENOUGH FOR MOST SCREENS
  563. ;
  564. ; PROMPT - prompt the user
  565. ; DESCRIPTION: clears to a new line and prints a hyphen
  566. ;
  567. PROMPT: DW    TCALL        ;THREADED CODE
  568.     DW    TPUSH,PRMSG    ;PUSH PROMPT MESSAGE
  569.     DW    PRINTS        ;AND PRINT IT
  570.     DW    TRET
  571. ;
  572. PRMSG:    DB    3,0DH,0AH,'-'
  573. ;
  574. ; PRINTS - prints string
  575. ; ENTRY: TOP  - points to string
  576. ; DESCRIPTION: Uses first byte of string as a character count
  577. ;
  578. PRINTS: DW    TCALL        ;THREADED CODE
  579.     DW    FIRST        ;GET COUNT
  580. PRINTS1:
  581.     DW    DUP,IFZ,PRINTX-1 ;IF DONE RETURN
  582.     DW    SWAP,FIRST    ;ELSE GET NEXT CHARACTER
  583.     DW    COUT        ;PRINT IT
  584.     DW    SWAP,DEC    ;DECREMENT COUNT
  585.     DW    JUMP,PRINTS1-1    ;AND KEEP LOOPING
  586. ;
  587. PRINTX: DW    TPOP,TPOP    ;DROP COUNT AND POOINTER
  588.     DW    TRET        ;THEN RETURN
  589. ;
  590. ; FIRST - get next byte of string on stack
  591. ; ENTRY: TOP   - ptr to string
  592. ; EXIT:  TOP   - first character of string
  593. ;     TOP-1 - ptr to rest of string
  594. ; DESCRIPTION: useful for advancing through strings a byte
  595. ; at a time.
  596. ;
  597. FIRST:    DW    $+2        ;CODE
  598.     POP    H        ;GET POINTER
  599.     MOV    C,M        ;BC <- CHARACTER
  600.     MVI    B,0
  601.     INX    H        ;BUMP POINTER
  602.     PUSH    H        ;RESTORE POINTER
  603.     PUSH    B        ;ADD CHARACTER
  604.     JMP    NEXT        ;CONTINUE
  605. ;
  606. ; COUT - character output routine
  607. ; ENTRY: TOP - character to print
  608. ; DESCRIPTION: uses operating system to print character
  609. ; <<<=== NOTE: MODIFIED FOR VAR. SIZE CPM SYS (RGF) ===>>>
  610. ;
  611. COUT:    DW    $+2        ;CODE
  612.     POP    B        ;C <- CHARACTER
  613. VCOUT:    CALL    7E0CH        ;PRINT IT (<<MODIFIED AT INIT>>)
  614.     JMP    NEXT        ;RETURN
  615. ;
  616. ; CIN - character input routine
  617. ; EXIT: TOP  - character read from console
  618. ; DESCRIPTION: Uses operating system
  619. ; <<<=== NOTE: MODIFIED FOR VAR. SIZE CPM SYS (RGF) ===>>>
  620. ;
  621. CIN:    DW    $+2        ;CODE
  622. VCIN:    CALL    7E09H        ;READ CHARACTER ((<<MODIFIED AT INIT>>)
  623.     MOV    L,A        ;HL <- CHARACTER
  624.     MVI    H,0
  625.     PUSH    H        ;PUSH ON STACK
  626.     JMP    NEXT        ;RETURN
  627. ;
  628. ; SCAN - Scan for next word
  629. ; ENTRY: No Values Expected
  630. ; EXIT: TOP    -  -1 if word found, 0 if word not found
  631. ;    TOP-1  -  ptr to word if found (else nothing)
  632. ; DESCRIPTION: first byte of buffer contains a counter of
  633. ; characters already scanned. The next word is moved to the
  634. ; beginning of the line with a leading byte count.
  635. ;
  636. SCAN:    DW    $+2        ;CODE
  637.     LXI    H,CONBUF+2    ;BC <- CHARACTER COUNT
  638.     MOV    C,M
  639.     MVI    B,0
  640.     INR    M        ;TEST FOR END OF LINE ALREADY
  641.     JZ    SCANX        ;IF YES
  642.     INX    H        ;HL <- SCANNING START POINT
  643.     DAD    B
  644.     MOV    B,C        ;B <- CHARACTER COUNT
  645. SCAN1:    INX    H        ;INCREMENT POINTER
  646.     INR    B        ;INCREMENT COUNT
  647.     MOV    A,M        ;GET NEXT CHARACTER
  648.     ORA    A        ;TEST FOR END OF LINE
  649.     JZ    SCANX        ;IF YES,
  650.     CPI    20H        ;ELSE, CHECK FOR BLANK
  651.     JZ    SCAN1        ;IF YES, SKIP IT
  652.     LXI    D,CONBUF+3    ;ELSE BEGIN MOVING WORD
  653.     MVI    C,0        ;C <- SIZE OF STRING
  654. SCAN2:    INX    D
  655.     STAX    D
  656.     INR    C        ;INC WORD SIZE
  657.     INR    B        ;INC SCANNED CHAR COUNT
  658.     INX    H        ;GET NEXT BYTE
  659.     MOV    A,M
  660.     ORA    A        ;TEST FOR END OF LINE
  661.     JNZ    SCAN3        ;IF NOT,
  662.     MVI    B,-1        ;ELSE SET EOL FLAG
  663.     MVI    A,20H        ;AND CHANGE EOL TO DELIMETER
  664. SCAN3:    CPI    20H        ;CHECK FOR SPACE
  665.     JNZ    SCAN2        ;IF NOT YET
  666.     LXI    H,CONBUF+2    ;ELSE SAVE SCANNED CHAR COUNT
  667.     MOV    M,B
  668.     INX    H        ;AND WORD SIZE
  669.     MOV    M,C
  670.     PUSH    H        ;AND RETURN WORD POINTER
  671.     LXI    H,-1
  672.     PUSH    H
  673.     JMP    NEXT
  674. ;
  675. SCANX:    MVI    A,-1        ;HIT END OF LINE
  676.     STA    CONBUF+2    ;MARK BUFFER EMPTY
  677.     LXI    H,0        ;RETURN A ZERO
  678.     PUSH    H
  679.     JMP    NEXT
  680. ;
  681. ; CONBXA - convert binary to ascii
  682. ;
  683. ; ENTRY: TOP - 16 bit positive integer
  684. ; EXIT:  TOP - address of converted ASCII string
  685. ; DESCRIPTION: pushes the digits of the number
  686. ; on to the stack, least significant digits first.
  687. ; Then pops them up and stores them in a local
  688. ; buffer.
  689. ;
  690. CONBXA: DW    TCALL        ;THREADED CODE
  691.     DW    NEGONE,SWAP    ;MARK END OF STRING WITH -1
  692. CONB1:    DW    TPUSH,10,DIV    ;DIVIDE NUMBER BY 10
  693.     DW    SWAP        ;PUT QUOTIENT ON TOP
  694.     DW    DUP
  695.     DW    IFNZ,CONB1-1    ;CONTINUE UNTIL Q = 0
  696. ;
  697.     DW    TPOP        ;THEN DROP QUOTIENT
  698.     DW    ZERO        ;STORE BYTE IN FIRST
  699.     DW    NBUFR,POKEB    ;BYTE OF BUFFER
  700. ;
  701. CONB2:    DW    DUP,NEGONE    ;TEST FOR END OF STRING
  702.     DW    IFEQ,CONB3-1    ;IF YES
  703.     DW    NBUFR,PEEKB    ;ELSE, INCREMENT BYTE COUNT
  704.     DW    INC
  705.     DW    NBUFR,POKEB
  706.     DW    TPUSH,'0',TADD    ;CONVERT DIGIT TO ASCII
  707.                 ;AND STORE IN NEXT LOCATION
  708.     DW    NBUFR
  709.     DW    NBUFR,PEEKB,TADD
  710.     DW    POKEB
  711.     DW    JUMP,CONB2-1    ;REPEAT
  712. ;
  713. CONB3:    DW    TPOP        ;DROP END OF STRING MARKER
  714.     DW    NBUFR        ;PUSH RETURN BUFFER ADDRESS
  715.     DW    TRET        ;AND RETURN
  716. ;
  717. NBUFR:    DW    VARIABLE    ;THREADED VARIABLE
  718.     DS    10        ;PLENTY LONG ENOUGH
  719. ;
  720. ; CONAXB - convert ASCII decimal string to binary
  721. ; ENTRY: TOP   - pointer to string
  722. ; EXIT:  TOP   - -1 if converted to binary
  723. ;          0 if not
  724. ;     TOP-1 - value of number if converted
  725. ;         ptr to string if not
  726. ; DESCRIPTION: converts only positive, unsigned
  727. ; integers. WRitten in assembly because I had it around
  728. ; and didn't want to rewrite it in threaded code.
  729. ;
  730. CONAXB: DW    $+2        ;CODE
  731.     POP    D        ;GET STRING POINTER
  732.     PUSH    D        ;BUT LEAVE ON STACK
  733.     LDAX    D        ;GET BYTE COUNT
  734.     MOV    B,A
  735.     LXI    H,0        ;STARTING VALUE
  736. ;
  737. CONA1:    INX    D
  738.     LDAX    D        ;GET NEXT CHARACTER
  739.     CPI    '0'        ;TEST FOR DIGIT
  740.     JC    CONAX        ;IF NOT
  741.     CPI    '9'+1
  742.     JNC    CONAX        ;IF NOT
  743.     SUI    '0'        ;CONVERT TO BINARY
  744.     PUSH    D        ;SAVE POINTER
  745.     DAD    H        ;MULTIPLY CURRENT VALUE BY 10
  746.     PUSH    H
  747.     DAD    H
  748.     DAD    H
  749.     POP    D
  750.     DAD    D
  751.     MOV    E,A        ;ADD NEW BINARY DIGIT
  752.     MVI    D,0
  753.     DAD    D
  754.     POP    D        ;RESTORE POINTER
  755.     DCR    B        ;DEC COUNT
  756.     JNZ    CONA1        ;CONTINUE TILL DONE
  757.     POP    D        ;THEN DROP POINTER
  758.     PUSH    H        ;PUSH NUMBER
  759.     LXI    H,-1        ;AND -1
  760.     PUSH    H
  761.     JMP    NEXT
  762. ;
  763. CONAX:    LXI    H,0        ;FAILURE: PUSH A ZERO
  764.     PUSH    H
  765.     JMP    NEXT
  766. ;
  767. ; DIV - 16 bit divide
  768. ; ENTRY: TOP   - divisor
  769. ;     TOP-1 - dividend
  770. ; EXIT:  TOP   - remainder
  771. ;     TOP-1 - quotient
  772. ; DESCRIPTION: performs a 32 bit by 16 bit division for
  773. ; positive integers only.  The quotient must be resolved
  774. ; in 16 bits.
  775. ;
  776. DIV:    DW    $+2        ;CODE
  777.     POP    B        ;BC <- DIVISOR
  778.     POP    D        ;HLDE <- DIVIDEND
  779.     LXI    H,0
  780.     CALL    DIV1        ;DO DIVISION
  781.     PUSH    D        ;PUSH QUOTIENT
  782.     PUSH    H        ;PUSH REMAINDER
  783.     JMP    NEXT
  784. ;
  785. DIV1:    DCX    B        ;NEGATE BC
  786.     MOV    A,B
  787.     CMA
  788.     MOV    B,A
  789.     MOV    A,C
  790.     CMA
  791.     MOV    C,A
  792.     MVI    A,16D        ;ITERATION COUNT
  793. DIV2:    DAD    H        ;SHIFT HLDE
  794.     PUSH    PSW        ;SAVE OVERFLOW
  795.     XCHG
  796.     DAD    H
  797.     XCHG
  798.     JNC    DIV3
  799.     INR    L
  800. DIV3:    POP    PSW        ;GET OVERFLOW
  801.     JC    DIV5        ;IF OVERFLOW, FORCE SUBTRACTION
  802.     PUSH    H        ;ELSE, SAVE DIVIDEND
  803.     DAD    B        ;ATTEMPT SUBTRACTION
  804.     JC    DIV4        ;IF IT GOES
  805.     POP    H        ;ELSE RESTORE DIVIDEND
  806.     JMP    DIV6
  807. DIV4:    INR    E        ;INCREMENT QUOTIENT
  808.     INX    SP        ;DROP OLD DIVIDEND
  809.     INX    SP
  810.     JMP    DIV6
  811. DIV5:    DAD    B        ;FORCE SUBTRACTION
  812.     INR    E        ;INC QUOTIENT
  813. DIV6:    DCR    A        ;DECREMENT COUNT
  814.     JNZ    DIV2        ;REPEAT UNTIL DONE
  815.     RET
  816. ;
  817. ; The Names in the dictionary
  818. ; Notice that the actual printed names are chosen for typing
  819. ; convenience and do not necessarily match the internal names,
  820. ; which must conform to the assembler's rules. Also, not all
  821. ; functions have been included here.
  822. ;
  823. NAMEBEG EQU    $
  824. ;
  825.     DB    1,'+'
  826.     DW    TADD
  827. ;
  828.     DB    1,'-'
  829.     DW    TSUB
  830. ;
  831.     DB    4,'/MOD'
  832.     DW    DIV
  833. ;
  834.     DB    7,'EXECUTE'
  835.     DW    EXECUTE
  836. ;
  837.     DB    5,'CLEAR'
  838.     DW    CLEAR
  839. ;
  840.     DB    5,'MATCH'
  841.     DW    MATCH
  842. ;
  843.     DB    6,'LOOKUP'
  844.     DW    LOOKUP
  845. ;
  846.     DB    4,'EXEC'
  847.     DW    EXEC
  848. ;
  849.     DB    6,'MEMORY'
  850.     DW    MEMORY
  851. ;
  852.     DB    6,'CONBXA'
  853.     DW    CONBXA
  854. ;
  855.     DB    3,'INC'
  856.     DW    INC
  857. ;
  858.     DB    3,'DEC'
  859.     DW    DEC
  860. ;
  861.     DB    5,'MINUS'
  862.     DW    MINUS
  863. ;
  864.     DB    5,'PEEKW'
  865.     DW    PEEKW
  866. ;
  867.     DB    5,'PEEKB'
  868.     DW    PEEKB
  869. ;
  870.     DB    5,'POKEW'
  871.     DW    POKEW
  872. ;
  873.     DB    5,'POKEB'
  874.     DW    POKEB
  875. ;
  876.     DB    3,'POP'
  877.     DW    TPOP
  878. ;
  879.     DB    4,'SWAP'
  880.     DW    SWAP
  881. ;
  882.     DB    3,'DUP'
  883.     DW    DUP
  884. ;
  885.     DB    5,'FIRST'
  886.     DW    FIRST
  887. ;
  888.     DB    0            ;END OF DICTIONARY
  889. ;
  890. NAMEEND EQU    $-1
  891. ;
  892. DICSIZE EQU    NAMEEND-NAMEBEG+1    ;DICTIONARY SIZE IN BYTES
  893. ;
  894. ; Initialition Code
  895. ; Executed on start up of system but eventually overwritten by
  896. ; the expanding dictionary
  897. ;
  898. ; DICMOVE - moves the dictionary names
  899. ;        to the top of available memory
  900. ;
  901. ; <<<===  Modified For CPM initialization (RGF)  ===>>>
  902. ;
  903. DICMOVE:
  904.     LHLD    6
  905.     SHLD    MEMORY+2    ;INIT TOP OF MEMORY
  906.     XCHG            ;DE <- TOP OF MEMORY
  907.     LXI    H,NAMEEND    ;HL <- SOURCE (END OF NAMES)
  908.     LXI    B,DICSIZE    ;BC <- BYTE COUNT
  909.                 ;TRANSFER LOOP
  910. DIC1:    MOV    A,M        ;GET NEXT BYTE
  911.     STAX    D        ;MOVE IT
  912.     DCX    H        ;DEC SOURCE POINTER
  913.     DCX    D        ;DEC TARGET POINTER
  914.     DCX    B        ;DEC COUNT
  915.     MOV    A,B        ;TEST FOR ZERO
  916.     ORA    C
  917.     JNZ    DIC1        ;NOT YET
  918. ;
  919.     XCHG            ;SET DICTIONARY VARIABLE
  920.     INX    H
  921.     SHLD    NAMES+2
  922. ;
  923.     LDA    2        ;MODIFIY I/O ROUTINES
  924.     STA    VCOUT+2     ;  SO THEY WILL WORD
  925.     STA    VCIN+2        ; IN ANY SIZE CPM SYSTEM
  926. ;
  927.     RET
  928. ;
  929. ;
  930. ;
  931. ;
  932.     END    BASE
  933.