home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8707 / 52 < prev    next >
Encoding:
Internet Message Format  |  1990-07-13  |  49.9 KB

  1. From: vandys@lindy.stanford.edu (Andy Valencia)
  2. Newsgroups: comp.sources.misc
  3. Subject: Forth interpreter in 68000 assembler
  4. Message-ID: <2931@ncoast.UUCP>
  5. Date: 18 Jul 87 00:29:03 GMT
  6. Sender: allbery@ncoast.UUCP
  7. Lines: 2148
  8. Approved: allbery@ncoast.UUCP
  9. X-Archive: comp.sources.misc/8707/52
  10.  
  11. [68000 assembler, yes.  Motorola format, not UN*X format; you'll have to change
  12. all "bar.l"-type opcodes to "barl" (i.e. "move.l" to "movl") to use it on a Sun
  13. or Plexus box.  ++bsa]
  14.  
  15.     Since I'm on the warpath, here's a hand-coded 68000 forth interpreter
  16. which conforms to the Forth-83 standard.  As I recollect, it actually deviates
  17. from it in two areas: first, words are 32 bits. Second, I didn't do Forth-
  18. standard I/O; I just use a pushdown stack of file descriptors, and read and
  19. write streams.
  20.     I don't know what the policy is concerning machine-dependent code
  21. (especially a monstrosity like this :-)), but it sure isn't doing anyone any
  22. good sitting around here, and I'm sure you'll know what to do with it.
  23.  
  24.                 Thanks,
  25.                 Andy Valencia
  26.                 vandys@lindy.stanford.edu
  27.  
  28. #!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
  29. #    This is a shell archive.
  30. #    Run the following text with /bin/sh to extract.
  31.  
  32. cat - << \Funky!Stuff! > Makefile
  33. forth: forth.o
  34.     ld -N -o forth -e init forth.o -l881 -lm
  35.     @size forth
  36.     @echo "Forth done"
  37. forth.o: forth.s
  38.     as -o forth.o forth.s
  39. Funky!Stuff!
  40. cat - << \Funky!Stuff! > forth.s
  41. ;
  42. ; forth.s--a 68K forth interpreter
  43. ;
  44. ; Register allocation:
  45. ;    A7--68K stack pointer
  46. ;    A6--IP
  47. ;    A5--SP
  48. ;    A4--RSP
  49. ;    A3..A0--General
  50. ;    D7--Here
  51. ;    D6--Input line pointer
  52. ;    D5..D0--General
  53. ;
  54.  
  55. ;
  56. ; Flag bits in status field
  57. ;
  58. Priority    equ    1
  59. Smudged        equ    2
  60.  
  61. ;
  62. ; control structure checking flags
  63. ;
  64. FlgDef        equ    1    ; : .. ;
  65. FlgBeg        equ    2    ; begin .. again, while, repeat
  66. FlgWhi        equ    5    ;    the "while" part flag
  67. FlgIf        equ    3    ; if .. endif
  68. FlgDo        equ    4    ; do .. loop, +loop
  69.  
  70. ;
  71. ; Other constants/offsets
  72. ;
  73. stacksize equ    100
  74. umem    equ    96        ; K of dict. space for user
  75. rstack    ds.l    stacksize    ; 100 Words for return stack
  76. stack    ds.l    stacksize    ;  and 100 for user's stack
  77. mstack    ds.l    stacksize    ;  and 100 for the 68K processor stack
  78. Inbufsize  equ    1024+4*3    ; Input buffer record
  79. InUnit    equ    1024+1        ;  Unix file descriptor number
  80. InbufIdx   equ    1024+4        ;  Holds index into it for nesting of units
  81. InbufPrev  equ    1024+8        ;  Pointer to previous input unit (nesting)
  82. MaxIn    equ    4        ; Max # open input units
  83. MaxOut    equ    MaxIn        ;  and output units
  84. inbufs
  85.     ds.b    1024        ; Input buffer
  86.     dc.b    0,0        ;  <NULL>, <STDIN>
  87.     ds.b    2        ;  two bytes wasted
  88.     ds.l    1        ;  holds index
  89.     dc.l    0        ;  ptr to prev--is NULL for first
  90.  
  91.     ds.b    Inbufsize*(MaxIn-1) ; The rest of the input units
  92. End_inbufs
  93.  
  94. outfds    dc.l    1        ; <STDOUT>
  95.     ds.l    MaxOut-1    ;  The rest of the output units
  96.  
  97. ounit    dc.l    outfds        ; Current output unit
  98. iunit    dc.l    inbufs        ;  and current input unit
  99.  
  100. ;
  101. ; init--start up forth. Set up our dictionary & use ABORT
  102. ;
  103.     globl    init
  104. init    move.l    #udict,d7        ; Set up HERE
  105.  
  106. ;
  107. ; abort--clear I/O, reset stacks, clear state, enter INTERP
  108. ;
  109. abort    
  110.     move.l    #rstack+stacksize,a4    ; Initialize return stack
  111.     move.l    #stack+stacksize,a5    ;  and user stack
  112.     move.l    #mstack+stacksize,a7    ;  and processor stack
  113.     clr.l    state1            ; Set state back to interpretive
  114.  
  115.     move.l    #interp,a6        ; Set IP to top of INTERP
  116.  
  117.     move.l    #inbufs,a0        ; Set up & clear input buffer
  118.     clr.b    (a0)
  119.     clr.b    1024(a0)
  120.     move.l    a0,d6
  121.     move.l    d6,iunit
  122.     clr.b    InUnit(a0)
  123.     move.l    #outfds,a0        ; Set up & clear output buffer
  124.     move.l    #1,(a0)
  125.     move.l    a0,ounit
  126.  
  127.     move.l    #3,-(a7)        ; Close all open files
  128.     clr.l    -(a7)            ;   Dummy place holder
  129.     move.l    #20,d3            ;  How many units to close
  130. abor1    moveq    #6,d0            ;  UNIX "close" system call
  131.     trap    #0
  132.     addq.l    #1,4(a7)        ;  Move to next file descriptor
  133.     dbra    d3,abor1
  134.     add.l    #8,a7            ; Remove arguments from stack
  135.                     ; Fall into...
  136.                     ;         V
  137. ;
  138. ; Next--the "fetch/execute" code of FORTH
  139. ;
  140. next    move.l    (a6)+,a0        ; Get CFA's addr, advance IP
  141. next2    move.l    (a0)+,a1        ; Get contents of CFA
  142.     jmp    (a1)            ; Jump to that address
  143.  
  144. ;
  145. ; interp--a high level definition
  146. ; : interp
  147. ;    getword lookup if
  148. ;        state @ 0= or if execute else [compile] (lit) , endif
  149. ;    else
  150. ;        number if
  151. ;            state @ if , endif
  152. ;        else notfound abort endif
  153. ;    endif
  154. ; ;
  155. ;
  156. interp    dc.l    ckstack,getword,lookup,zbranch,inter1
  157.     dc.l    state,fetch,zeq,l_or,zbranch,inter2
  158.     dc.l    execute,branch,interp
  159. inter2    dc.l    comma,branch,interp
  160. inter1    dc.l    number,zbranch,inter3
  161.     dc.l    state,fetch,zbranch,interp
  162.     dc.l    plit,plit,comma,comma,branch,interp
  163. inter3    dc.l    notfound
  164.  
  165. ;
  166. ; or--bitwise "or"
  167. ;
  168. l_or2   dc.l    0
  169. l_or    dc.l    l_or1,l_or1,0
  170.         dc.b    'or      '
  171. l_or1   move.l  (a5)+,d0
  172.         or.l    d0,(a5)
  173.         jmp     next
  174.  
  175. ;
  176. ; and--logical bit-wise AND
  177. ;
  178. l_and2  dc.l    l_or2,l_and1,l_and1,0
  179.         dc.b    'and     '
  180. l_and1  move.l  (a5)+,d0
  181.         and.l   d0,(a5)
  182.         jmp     next
  183.  
  184. ;
  185. ; 0<--push whether top is less than 0
  186. ;
  187. zlt2    dc.l    l_and2,zlt1,zlt1,0
  188.         dc.b    '0<      '
  189. zlt1    tst.l   (a5)
  190.         blt     puttrue
  191.         bra     putfalse
  192.  
  193. ;
  194. ; 0>--push whether top is greater than 0
  195. ;
  196. zgt2    dc.l    zlt2,zgt1,zgt1,0
  197.         dc.b    '0>      '
  198. zgt1    tst.l   (a5)
  199.         bgt     puttrue
  200.         bra     putfalse
  201.  
  202. ;
  203. ; u<--unsigned version of "less than"
  204. ;
  205. ult2    dc.l    zgt2,ult1,ult1,0
  206.         dc.b    'u<      '
  207. ult1    move.l  (a5)+,d0
  208.         cmp.l   (a5),d0
  209.     beq    putfalse
  210.         bcc     puttrue
  211.         bra     putfalse
  212.  
  213. ;
  214. ; 0=--a logical "not"
  215. ;
  216. zeq2    dc.l    ult2
  217. zeq     dc.l    zeq1,zeq1,0
  218.         dc.b    '0=      '
  219. zeq1    tst.l   (a5)
  220.         bne     putfalse
  221. puttrue
  222.         move.l  #-1,(a5)
  223.         jmp     next
  224. putfalse
  225.         clr.l   (a5)
  226.         jmp     next
  227.  
  228. ;
  229. ; <--less than. Push whether second is less than top
  230. ;
  231. lt2    dc.l    zeq2,lt1,lt1,0
  232.     dc.b    '<       '
  233. lt1    move.l    (a5)+,d0
  234.     cmp.l    (a5),d0
  235.     bgt    puttrue
  236.     bra    putfalse
  237.  
  238. ;
  239. ; >--greater than. Push whether second is greater than top
  240. ;
  241. gt2    dc.l    lt2,gt1,gt1,0
  242.     dc.b    '>       '
  243. gt1    move.l    (a5)+,d0
  244.     cmp.l    (a5),d0
  245.     blt    puttrue
  246.     bra    putfalse
  247.  
  248. ;
  249. ; =--push whether top and second are equal
  250. ;
  251. equal2    dc.l    gt2,equal1,equal1,0
  252.     dc.b    '=       '
  253. equal1    move.l    (a5)+,d0
  254.     cmp.l    (a5),d0
  255.     beq    puttrue
  256.     bra    putfalse
  257.  
  258. ;
  259. ; ccomma--store a byte into the next location
  260. ;
  261. ccomma2    dc.l    equal2,ccomma1,ccomma1,0
  262.     dc.b    'c,      '
  263. ccomma1    move.l    d7,a0
  264.     move.l    (a5)+,d0    ; Get word off stack
  265.     move.b    d0,(a0)        ; Store its low byte
  266.     addq.l    #1,d7        ; Advance HERE
  267.     jmp    next
  268.  
  269. ;
  270. ; comma--store a word into the next free location, advancing the
  271. ;    current location pointer
  272. ;
  273. comma2    dc.l    ccomma2
  274. comma    dc.l    comma1,comma1,0
  275.     dc.b    ',       '
  276. comma1    addq.l    #3,d7        ; Word-align data
  277.     and.l    #0xFFFFFFFC,d7
  278.     move.l    d7,a0
  279.     move.l    (a5)+,(a0)+
  280.     move.l    a0,d7
  281.     jmp    next
  282.  
  283. ;
  284. ; !--store second at address pointed to by top
  285. ;
  286. store2    dc.l    comma2
  287. store    dc.l    store1,store1,0
  288.     dc.b    '!       '
  289. store1    move.l    (a5)+,a0
  290.     move.l    (a5)+,(a0)
  291.     jmp    next
  292.  
  293. ;
  294. ; @--replace top of stack with what it pointed to
  295. ;
  296. fetch2    dc.l    store2
  297. fetch    dc.l    fetch1,fetch1,0
  298.     dc.b    '@       '
  299. fetch1    move.l    (a5),a0
  300.     move.l    (a0),(a5)
  301.     jmp    next
  302.  
  303. ;
  304. ; branch--replace IP with next sequential word in execution
  305. ;
  306. branch2    dc.l    fetch2
  307. branch    dc.l    branch1,branch1,0
  308.     dc.b    'branch  '
  309. branch1    move.l    (a6),a6
  310.     jmp    next
  311.  
  312. ;
  313. ; zbranch--"branch" if top of stack is zero
  314. ;
  315. zbran2    dc.l    branch2
  316. zbranch    dc.l    zbran1,zbran1,0
  317.     dc.b    'zbranch '
  318. zbran1    move.l    (a6)+,d0    ; Get the conditional destination
  319.     tst.l    (a5)+        ; Should we take it?
  320.     beq    zbran3
  321.     jmp    next
  322. zbran3    move.l    d0,a6        ; Take the branch
  323.     jmp    next
  324.  
  325. ;
  326. ; run-time code to push the PFA to stack
  327. ;
  328. getpfa    move.l    (a0),-(a5)
  329.     jmp    next
  330.  
  331. ;
  332. ; state--variable which holds the state: 0 == interp, <>0 == compiling
  333. ;
  334. state2    dc.l    zbran2
  335. state    dc.l    getpfa,state1,0
  336.     dc.b    'state   '
  337. state1    dc.l    0
  338.  
  339. ;
  340. ; getword--get the next word from the input stream, put it in "pad".
  341. ;
  342. getw2    dc.l    state2
  343. getword    dc.l    getwo1,getwo1,0
  344.     dc.b    'getword '
  345. getwo1    jsr    getw1
  346.     jmp    next
  347.  
  348. getw1    move.l    d6,a0        ; A0 will be our line pointer
  349.     jsr    skipwhite    ; Skip leading white space
  350.     move.l    #pad1,a1    ; Build into "pad" via A1
  351.     clr.b    8(a1)        ;  Put in Null-termination
  352.     move.l    #8,d1        ; Count # chars stored
  353. getw3    move.b    (a0)+,(a1)+    ; Get next char
  354.     bne.s    getw10        ; Need to read in a new buffer?
  355.  
  356.     subq.l    #1,a1        ;  Back up destination ptr
  357. getw20    movem.l    a1/d1,-(a7)    ;  Save registers
  358.     jsr    getline        ;  Get new line
  359.     movem.l    (a7)+,a1/d1    ;  Restore registers
  360.     move.l    d6,a0        ;  Update input line pointer
  361.     bra.s    getw4
  362.  
  363. getw10    subq.l    #1,d1        ; Decrement character count
  364.     beq    getw5        ;  If run out, truncate rest of word
  365. getw4    jsr    iswhite        ; See if at end of word
  366.     bne    getw3
  367.     tst.b    (a0)        ; At end of buffer?
  368.     beq.s    getw20
  369.  
  370.     tst.l    d1        ; Blank-fill word
  371.     beq    getw6
  372. getw7    move.b    #32,(a1)+
  373.     subq.l    #1,d1
  374.     bne    getw7
  375. getw6    move.l    a0,d6        ; Save input pointer
  376.     rts
  377.  
  378. getw5    tst.b    (a0)        ; Get new buffer at end of current
  379.     bne.s    getw11
  380.     jsr    getline
  381.     move.l    d6,a0
  382.     bra.s    getw5
  383. getw11    jsr    iswhite        ; Quit when get white space
  384.     beq.s    getw6
  385.     addq.l    #1,a0        ;  Skip over characters
  386.     bra.s    getw5
  387.  
  388. ;
  389. ; skipwhite--skip over white space.  For a number of bizarre reasons,
  390. ;    this is also the best place to read in a new buffer if we run
  391. ;    off the end of the current one. It is expected that all input lines
  392. ;    will end in NEWLINE--if they don't, you're taking a chance.
  393. ;
  394. skipwhite
  395.     jsr    iswhite        ; Check next char:
  396.     bne    skipw2        ;  No white space, return
  397.     tst.b    (a0)+        ; At end of input buffer?
  398.     bne    skipwhite    ;  No--continue
  399.     jsr    getline        ;  Yes--get a fresh buffer
  400.     move.l    d6,a0        ;   update our line buffer pointer
  401.     bra    skipwhite
  402. skipw2    rts
  403.  
  404. ;
  405. ; iswhite--return via the Z flag whether the char pointed to by A0
  406. ;    is a white space character. Uses D3 to hold the char.
  407. ;
  408. iswhite    move.b    (a0),d3        ; Get the char
  409.     cmp.b    #32,d3        ; Check space
  410.     beq    iswh2
  411.     cmp.b    #9,d3        ;  ..Tab
  412.     beq    iswh2
  413.     cmp.b    #10,d3        ;  ..Newline
  414.     beq    iswh2
  415.     tst.b    d3        ;  ..NULL
  416. iswh2    rts
  417.  
  418. ;
  419. ; getline--get another buffer-full from the current input unit. If no
  420. ;    more input is available on it, pop back a level. If there are
  421. ;    no more levels (i.e., the user typed ^D), exit. If the input is
  422. ;    TTY, prompt.
  423. ;
  424. ok_msg    dc.b    'Ok',10,'> ',0
  425.     even
  426. getline    move.l    iunit,a0    ; Get ptr to head of current input record
  427.     cmp.l    #inbufs,a0    ; See if it's the TTY
  428.     bne    getl9
  429.     move.l    #ok_msg,a0    ; Print "Ok"
  430.     jsr    prstr
  431.     move.l    iunit,a0    ; restore A0
  432.  
  433. getl9    move.l    a0,d6        ;  Set up our input line pointer
  434.  
  435. getl4    move.b    InUnit(a0),d0    ; Get file descriptor
  436.     ext.w    d0
  437.         ext.l   d0              ; Turn file descriptor into longword
  438.         move.l  #1024,-(a7)     ;  Third arg: # bytes
  439.         move.l  a0,-(a7)        ;  Second: store buffer
  440.         move.l  d0,-(a7)        ;  First arg is file descriptor
  441.         clr.l   -(a7)           ;  Dummy space holder
  442.         moveq   #3,d0        ;  UNIX READ syscall
  443.         trap    #0
  444.         bcc     getl2           ; On carry set, abort on an I/O error
  445.         jmp     io_err
  446. getl2   add.l   #16,a7          ; Pop off arguments
  447.         tst.l   d0              ; Zero bytes read means EOF--pop up a unit!
  448.         beq.s    getl3
  449.         add.l   d0,a0           ; Tack on the trailing NULL
  450.         clr.b   (a0)
  451.         rts                     ;  and return
  452.  
  453. getl3                           ; Hit EOF--pop back a unit, or exit
  454.         move.l  InbufPrev(a0),d0 ; Get previous record
  455.         beq     leave           ;  STDIN at EOF--exit
  456.     move.l    d0,a0
  457.         move.l  a0,iunit        ;  Update current unit
  458.         move.l  InbufIdx(a0),d6 ;  Get the old line index
  459.         rts
  460.  
  461. ;
  462. ; leave--do an "exit" syscall
  463. ;
  464. leave   move.l  #1,d0           ; Request 1 means "exit"
  465.         clr.l   -(a7)           ;  We will give a return code of 0
  466.         clr.l   -(a7)
  467.         trap    #0
  468.     trap    #1        ; Shouldn't reach here!
  469.  
  470. ;
  471. ; pad--an area of storage to use
  472. ;
  473. pad2    dc.l    getw2
  474. pad    dc.l    getpfa,pad1,0
  475.     dc.b    'pad     '
  476. pad1    ds.b    84
  477.  
  478. ;
  479. ; lookup--search for the word represented by the first 8 bytes of PAD
  480. ;    in the dictionary. If it's not found, push FALSE. Otherwise,
  481. ;    push the CFA, the priority, and TRUE.
  482. ;
  483. look2    dc.l    pad2
  484. lookup    dc.l    look1,look1,0
  485.     dc.b    'lookup  '
  486. look1    jsr    look99
  487.     jmp    next
  488.  
  489. look99    move.l    latest+4,a0        ; Get pointer to latest definition
  490.     move.l    pad1,d3            ; Get search string
  491.     move.l    pad1+4,d4
  492. look5    cmp.l    16(a0),d3        ; Compare first 4 bytes
  493.     bne    look3
  494.     cmp.l    20(a0),d4        ; Compare second 4 bytes
  495.     bne    look3
  496.     move.l    12(a0),d5        ; See if smudged
  497.     and.l    #Smudged,d5
  498.     bne    look3
  499.     add.l    #4,a0            ; turn A0 into CFA addr and push
  500.     move.l    a0,-(a5)
  501.     move.l    8(a0),d0        ; Get status field
  502.     and.l    #Priority,d0        ; Push flag for priority
  503.     move.l    d0,-(a5)
  504.     move.l    #-1,-(a5)        ; Push true flag--word found
  505.     rts
  506.  
  507. look3    move.l    (a0),d0            ; Move to next entry
  508.     tst.l    d0            ; Check null ptr (end of chain)
  509.     beq    look4
  510.     move.l    d0,a0            ; Move back to A0
  511.     bra    look5
  512. look4    clr.l    -(a5)            ; Not found--push false
  513.     rts
  514.  
  515. ;
  516. ; execute--pop a CFA off the stack & invoke that word
  517. ;
  518. exec2    dc.l    look2
  519. execute    dc.l    exec1,exec1,0
  520.     dc.b    'execute '
  521. exec1    move.l    (a5)+,a0
  522.     jmp    next2
  523.  
  524. ;
  525. ; number--if the string in PAD is not a legal number, push FALSE.
  526. ;    If it is, push the value and TRUE.
  527. ;
  528. num2    dc.l    exec2
  529. number    dc.l    num1,num1,0
  530.     dc.b    'number  '
  531. num1    move.l    #pad1,a0        ; This is where our number is
  532.     jsr    num99
  533.     jmp    next
  534.  
  535. num99    clr.l    d0            ; D0 accumulates the result
  536.     move.l    base,d5            ; D5 is the current base
  537.     cmp.b    #45,(a0)        ; Flag negation if leading '-' there
  538.     seq    d3
  539.     bne    num3
  540.     add.l    #1,a0
  541.  
  542. num3    move.b    (a0)+,d1        ; Get next char
  543.     tst.b    d1            ; At end of string?
  544.     beq    num4
  545.     cmp.b    #32,d1            ; At the trailing blanks?
  546.     beq    num4
  547.     jsr    isdig            ; Legal numeric digit?
  548.     bne    num6            ;  No, this isn't a number
  549.     muls    d5,d0            ;  Yes, shift and add
  550.     add.l    d1,d0            ;   ("isdigit" converts it)
  551.     bra    num3
  552.  
  553. num4    tst.b    d3            ; See if it should be negated
  554.     beq    num5
  555.     neg.l    d0
  556. num5    move.l    d0,-(a5)        ; Push number
  557.     move.l    #-1,-(a5)        ;  and true flag
  558.     rts
  559.  
  560. num6    clr.l    -(a5)            ; Not number, push false
  561.     rts
  562.  
  563. ;
  564. ; isdig--check whether the character in D1 is a legal digit. If it is,
  565. ;    return its value in D2, and Z set. Otherwise, return with
  566. ;    Z cleared. We assume that BASE has already been put in D5,
  567. ;
  568. isdig    sub.l    #48,d1        ; Shift '0' down to 0
  569.     blt    isdi1        ;  Was lower than '0'--can't be a digit
  570.     cmp.b    #10,d1        ; Was it 0..9?
  571.     blt    isdi2
  572.     sub.b    #7,d1        ; Map 'A'..'F' down to 10..15
  573.     blt    isdi1
  574.     cmp.b    #16,d1        ; Was it in range 10..15?
  575.     blt    isdi2
  576.     sub.b    #32,d1        ; Finally, map 'a'..'f' down to 10..15
  577.     blt    isdi1
  578.     cmp.b    #16,d1        ; Was it in range 10..15?
  579.     bge    isdi1
  580.  
  581. isdi2    ext.w    d1        ; Turn the number into a longword
  582.     ext.l    d1
  583.     cmp.l    d5,d1        ; See if it's within the base
  584.     bge    isdi1
  585.     ori    #4,ccr        ; Set Z--we have a legal number
  586.     rts
  587.  
  588. isdi1    andi    #0xFB,ccr    ; Clear Z--not a digit!
  589.     rts
  590.  
  591. ;
  592. ; (lit)--run-time word to push a literal onto the stack
  593. ;
  594. plit2    dc.l    num2
  595. plit    dc.l    plit1,plit1,0
  596.     dc.b    '(lit)   '
  597. plit1    move.l    (a6)+,-(a5)
  598.     jmp    next
  599.  
  600. base2    dc.l    plit2,getpfa        ; Current base for numbers
  601.     dc.l    base,0
  602.     dc.b    'base    '
  603. base    dc.l    10
  604.  
  605. ;
  606. ; prstr--print a string to the current output unit. No management of the
  607. ;    TTY is implied here--it just writes to the current output unit.
  608. ;    The string to print is pointed to by A0.
  609. ;
  610. prstr    clr.l    d0        ; String length counter
  611.     move.l    a0,a1        ; Local copy of the pointer
  612. prst1    tst.b    (a1)+        ; At end of string?
  613.     beq    prst2
  614.     add.l    #1,d0        ; No, increment count
  615.     bra    prst1        ;  and loop
  616. prst2    move.l    ounit,a1    ; Build syscall parameters
  617.     move.l    d0,-(a7)    ;  Number of bytes
  618.     move.l    a0,-(a7)    ;  Buffer
  619.     move.l    (a1),-(a7)    ;  File descriptor
  620.     clr.l    -(a7)        ;  Dummy place holder
  621.     move.l    #4,d0        ; A write syscall
  622.     trap    #0        ;  Do the call
  623.     add.l    #16,a7        ; Remove the arguments
  624.     bcc    prst3
  625.     jmp    io_err        ; Complain if the I/O failed
  626. prst3    rts
  627.  
  628. ;
  629. ; io_err--complain about an I/O error
  630. ;
  631. io_err    move.l    #io_err_msg,a0    ; The error message
  632.     jsr    prstr
  633.     jmp    abort
  634. io_err_msg
  635.     dc.b    10,'I/O error!',10,0
  636.     even
  637.  
  638. ;
  639. ; notfound--routine to call when the compiler gets a word it
  640. ;    doesn't know.
  641. ;
  642. notf2    dc.l    base2
  643. notfound
  644.     dc.l    notf1,notf1,0
  645.     dc.b    'notfound'
  646. notf1    move.l    #pad1,a0    ; Print the word
  647.     jsr    prstr
  648.     move.l    #notf_msg,a0    ; Print ": not found"
  649.     jsr    prstr
  650.     jmp    abort
  651. notf_msg
  652.     dc.b    ': not found',10,0
  653.     even
  654.  
  655. ;
  656. ; The match primitives--+, -, *, /
  657. ;
  658. plus2    dc.l    notf2,plus1,plus1,0
  659.     dc.b    '+       '
  660. plus1    move.l    (a5)+,d0
  661.     add.l    d0,(a5)
  662.     jmp    next
  663. sub2    dc.l    plus2,sub1,sub1,0
  664.     dc.b    '-       '
  665. sub1    move.l    (a5)+,d0
  666.     sub.l    d0,(a5)
  667.     jmp    next
  668.  
  669.     globl    _lrem
  670. mod2    dc.l    sub2,mod1,mod1,0
  671.     dc.b    'mod     '
  672. mod1    move.l    (a5)+,-(sp)
  673.     move.l    (a5),-(sp)
  674.     jbsr    _lrem
  675.     addq.l    #8,sp
  676.     move.l    d0,(a5)
  677.     jmp    next
  678.  
  679.     globl    _ldiv
  680. div2    dc.l    mod2,div1,div1,0
  681.         dc.b    '/       '
  682. div1    move.l  (a5)+,-(sp)        ; Divisor
  683.         move.l  (a5),-(sp)         ; Dividend
  684.     jbsr    _ldiv
  685.     addq    #8,sp
  686.     move.l    d0,(a5)
  687.         jmp     next
  688.  
  689. tdm2    dc.l    div2,tdm1,tdm1,0
  690.         dc.b    '*/mod   '
  691. tdm1    move.l  (a5)+,d0        ; Hold divisor
  692.         move.l  (a5)+,d1        ; Get two multipliers
  693.         move.l  (a5),d2
  694.         muls    d1,d2
  695.         divs    d0,d2           ; Divide into the product
  696.         move.l  d2,d3           ; push remainder
  697.         swap    d3
  698.         ext.l   d3
  699.         move.l  d3,(a5)
  700.         ext.l   d2              ; now push quotient
  701.         move.l  d2,-(a5)
  702.         jmp     next
  703. td2     dc.l    tdm2,td1,td1,0
  704.         dc.b    '*/      '
  705. td1     move.l  (a5)+,d0        ; Divisor
  706.         move.l  (a5)+,d1        ; Two multipliers
  707.         move.l  (a5),d2
  708.         muls    d1,d2
  709.         divs    d0,d2           ; divide into product
  710.         ext.l   d2              ; Extend quotient to longword and push
  711.         move.l  d2,(a5)
  712.         jmp     next
  713.  
  714. divmod2 dc.l    td2,divmod1,divmod1,0
  715.         dc.b    '/mod    '
  716. divmod1 move.l  (a5)+,d0        ; Divisor
  717.         move.l  (a5),d1         ; Dividend
  718.         divs    d0,d1
  719.         move.l  d1,d0
  720.         swap    d0              ; Put remainder in low word
  721.         ext.l   d0              ;  fill remainder to longword quantity
  722.         move.l  d0,(a5)
  723.         ext.l   d1              ; Now fill quotient to longword
  724.         move.l  d1,-(a5)
  725.         jmp     next
  726.  
  727. mul2    dc.l    divmod2,mul1,mul1,0
  728.         dc.b    '*       '
  729. mul1    move.l  (a5)+,d0
  730.     move.w    d0,d1
  731.     move.w    (a5)+,d0
  732.     tst.l    d0
  733.     beq.s    timesl1
  734.     move.w    d1,a0
  735.     mulu    d0,d1
  736.     swap    d0
  737.     mulu    (a5),d0
  738.     add.w    d1,d0
  739.     swap    d0
  740.     clr.w    d0
  741.     move.w    a0,d1
  742.     mulu    (a5)+,d1
  743.     add.l    d1,d0
  744.     bra.s    timesl2
  745. timesl1    move.w    (a5)+,d0
  746.     mulu    d1,d0
  747. timesl2    move.l  d0,-(a5)
  748.         jmp     next
  749.  
  750. ;
  751. ; u.--due to the stupidity of the 68K divide instructions, this has
  752. ;    to be just an alias for ".".
  753. ;
  754. udot2    dc.l    mul2,dot1,dot1,0
  755.     dc.b    'u.      '
  756.  
  757. ;
  758. ; .--pop and print the top of stack in the current base
  759. ;
  760. dot2    dc.l    udot2,dot1,dot1,0
  761.         dc.b    '.       '
  762. dot1    move.l    (a5)+,d0    ; The number to print
  763.     move.l    base,d2        ;  In this base
  764.     move.l    #pad1+20,a0    ; Where to build the number
  765.     clr.b    (a0)        ;  A terminating NULL
  766.     move.b    #32,-(a0)    ;  Add a trailing blank
  767.     tst.l    d0        ; Handle negative numbers
  768.     slt    d1        ; Flag a negative
  769.     move.l    d1,-(sp)
  770.     bge    dot3
  771.     neg.l    d0        ; Negate a negative
  772.  
  773. dot3    move.l    d2,-(sp)
  774.     move.l    d0,-(sp)
  775.     jbsr    _lrem        ; divide, getting the next digit
  776.     addq.l    #8,sp
  777.     add.b    #48,d0        ; Move 0..9 to '0'..'9'
  778.     cmp.b    #58,d0        ; Hex digit?
  779.     blt    dot4
  780.     addq.b    #7,d0
  781. dot4    move.b    d0,-(a0)    ; Store the digit
  782.     move.l    d1,d0        ; Get quotient
  783.     tst.l    d0        ; All of the number printed?
  784.     bne    dot3
  785.  
  786.     move.l    (sp)+,d2
  787.     tst.b    d2        ; Tack on a leading '-' if it's needed
  788.     beq    dot7
  789.     move.b    #45,-(a0)
  790. dot7    jsr    prstr
  791. dot9    jmp    next
  792.  
  793. ;
  794. ; ckstack--check the user's stack for underflow
  795. ;
  796. cks_msg    dc.b    '? Stack empty',10,0
  797.     even
  798. cks2    dc.l    dot2
  799. ckstack    dc.l    cks1,cks1,0
  800.     dc.b    '?stack  '
  801. cks1    cmp.l    #stack+stacksize,a5
  802.     ble    dot9
  803.     move.l    #cks_msg,a0    ; Underflowed--complain
  804.     jsr    prstr
  805.     jmp    abort
  806.  
  807. ;
  808. ; words--list contents of dictionary
  809. ;
  810. wrdpad    dc.b    '    '
  811. word2    dc.l    cks2,word1,word1,0
  812.     dc.b    'words   '
  813.  
  814. word1    move.l    late1,a2    ; For following the dictionary chain
  815.  
  816. word3    move.l    #pad1,a1    ; Set up for next line
  817.     moveq    #6,d0        ; Number of entries per line
  818. word4    cmp.l    #0,a2        ; See if at end of chain
  819.     beq    word5
  820.     move.l    16(a2),(a1)+    ; Copy string
  821.     move.l    20(a2),(a1)+
  822.     move.l    wrdpad,(a1)+    ; Pad with 4 spaces
  823.     move.l    (a2),a2        ; Advance to next entry
  824.     subq.l    #1,d0
  825.     bne    word4
  826. word5    move.b    #10,(a1)+    ; Trailing newline
  827.     clr.b    (a1)        ;  and NULL
  828.     move.l    #pad1,a0    ; Write it
  829.     jsr    prstr
  830.     cmp.l    #0,a2        ; All done?
  831.     bne    word3
  832.     jmp    next
  833.  
  834. ;
  835. ; make_head--build a FORTH header, return its address in
  836. ;    register A0.
  837. ;
  838. make_head
  839.     move.l    d7,a0        ; For returning it
  840.     move.l    d7,a1        ; For storing sequentially
  841.     move.l    late1,(a1)+    ; Build this def into the chain
  842.     move.l    d7,late1
  843.     clr.l    (a1)+        ; Empty CFA
  844.     lea    24(a0),a2    ; Point PFA to the def body
  845.     move.l    a2,(a1)+
  846.     clr.l    (a1)+
  847.     movem.l    a0/a1,-(a5)    ; Stash our work reg
  848.     jsr    getw1        ; Build the name in-line
  849.     movem.l    (a5)+,a0/a1    ; Stash our work reg
  850.     move.l    pad1,(a1)+
  851.     move.l    pad1+4,(a1)+
  852.     move.l    a1,d7        ; Reset D7
  853.     rts
  854.  
  855. ;
  856. ; variable--allocate a variable in the dictionary
  857. ;
  858. var2    dc.l    word2,var1,var1,0
  859.     dc.b    'variable'
  860. var1    addq.l    #3,d7        ; Word-align HERE
  861.     and.l    #0xFFFFFFFC,d7
  862.     jsr    make_head    ; Build a header
  863.     move.l    #getpfa,4(a0)    ; Our run-time code will push the PFA
  864.     addq.l    #4,d7        ; Our body starts with one word
  865.     jmp    next
  866.  
  867. ;
  868. ; constant--allocate a constant in the dictionary
  869. ;
  870. const2    dc.l    var2,const1,const1,0
  871.     dc.b    'constant'
  872. const1    addq.l    #3,d7        ; Word-align HERE
  873.     and.l    #0xFFFFFFFC,d7
  874.     jsr    make_head    ; Build header
  875.     move.l    #getpfa,4(a0)    ; run-time code pushes PFA
  876.     move.l    (a5)+,8(a0)    ; Our PFA is the number on-stack
  877.     jmp    next
  878.  
  879. ;
  880. ; colon--go into compilation mode
  881. ;
  882. colon2    dc.l    const2,colon1,colon1,0
  883.     dc.b    ':       '
  884. colon1    addq.l    #3,d7        ; Word-align definitions
  885.     and.l    #0xFFFFFFFC,d7
  886.     move.l    #1,state1    ; Go into compilation state
  887.     jsr    make_head    ; Build our header
  888.     move.l    #hilev,4(a0)    ; our CFA invokes a high-level def
  889.     move.l    #Smudged,12(a0)    ;  and we start Smudged
  890.     move.l    #FlgDef,-(a5)    ; Push our flag for a definition
  891.     jmp    next
  892.  
  893. ;
  894. ; semicolon--come out of compilation mode
  895. ;
  896. semi_msg
  897.     dc.b    'control structure not matched',10,0
  898.     even
  899. semi2    dc.l    colon2,semi1,semi1,Priority
  900.     dc.b    59,'       '
  901. semi1    clr.l    state1        ; Back to interpretive state
  902.     move.l    late1,a0    ; Turn off the smudge bit
  903.     clr.l    12(a0)
  904.     move.l    d7,a0        ; Compile in a trailing ';s'
  905.     move.l    #popup,(a0)+
  906.     move.l    a0,d7
  907.     cmp.l    #FlgDef,(a5)+    ; See if control structures matched
  908.     bne    semi3
  909.     jmp    next
  910. semi3    move.l    #semi_msg,a0    ; Complain
  911.     jsr    prstr
  912.     jmp    abort
  913.  
  914. ;
  915. ; hilev--the machine code which sets off a high-level definition
  916. ;
  917. hilev    move.l    a6,-(a4)    ; Save old IP
  918.     move.l    (a0),a6        ; Get new IP
  919.     jmp    next
  920.  
  921. ;
  922. ; popup--aka ';s'. Pop the IP from the return stack. For exiting
  923. ;    a high-level word.
  924. ;
  925. pop2    dc.l    semi2
  926. popup    dc.l    pop1,pop1,0
  927.     dc.b    59,'s      '
  928. pop1    move.l    (a4)+,a6
  929.     jmp    next
  930.  
  931. ;
  932. ; do--build the opening part of a do..loop
  933. ;
  934. do2    dc.l    pop2,do1,do1,Priority
  935.     dc.b    'do      '
  936. do1    move.l    d7,a0
  937.     move.l    #pushr,(a0)+    ; Generate code to get the loop parameters
  938.     move.l    #pushr,(a0)+
  939.     move.l    a0,-(a4)    ; Save this place for backbranching
  940.     move.l    #pdo,(a0)+    ; compile (do)
  941.     clr.l    (a0)+        ; Leave room for our forward branch
  942.     move.l    #FlgDo,-(a5)    ; Flag our control structure
  943.     move.l    a0,d7
  944. do3    jmp    next
  945.  
  946. ;
  947. ; (do)--run-time word to set off a do..loop
  948. ;
  949. pdo2    dc.l    do2
  950. pdo    dc.l    pdo1,pdo1,0
  951.     dc.b    '(do)    '
  952. pdo1    move.l    4(a4),d0    ; Check for exit condition
  953.     cmp.l    (a4),d0        ; Check for exit condition
  954.     blt    pdo3
  955.     addq.l    #8,a4        ; Clear the loop parameters
  956.     move.l    (a6),a6        ; Jump out of loop
  957.     jmp    next
  958.  
  959. pdo3    addq.l    #4,a6        ; Loop's not done--advance IP
  960.     jmp    next        ;  and continue
  961.  
  962. ;
  963. ; loop--compile in the closing part of a loop
  964. ;
  965. loop2    dc.l    pdo2,loop1,loop1,Priority
  966.     dc.b    'loop    '
  967. loop1    cmp.l    #FlgDo,(a5)    ; See if they botched
  968.     bne    loop3
  969.     addq.l    #4,a5        ; Free the flag
  970.     move.l    d7,a0
  971.     move.l    #ploop,(a0)+    ; Compile (loop)
  972.     move.l    (a4)+,a1    ; Get address of "loop"
  973.     move.l    a1,(a0)+    ; This is our backbranch address
  974.     move.l    a0,4(a1)    ; Give them the forward branch address
  975.     move.l    a0,d7        ; Restore HERE
  976.     jmp    next
  977. loop3    move.l    #loop_msg,a0
  978.     jsr    prstr
  979.     jmp    abort
  980. loop_msg
  981.     dc.b    10,'do not matched by loop',10,0
  982.     even
  983.  
  984. ;
  985. ; +loop--compile in the closing part of a loop
  986. ;
  987. aloop2    dc.l    loop2,aloop1,aloop1,Priority
  988.     dc.b    '+loop   '
  989. aloop1    cmp.l    #FlgDo,(a5)    ; See if they botched
  990.     bne    aloop3
  991.     addq.l    #4,a5        ; Free the flag
  992.     move.l    d7,a0
  993.     move.l    #paloop,(a0)+    ; Compile (loop)
  994.     move.l    (a4)+,a1    ; Get address of "loop"
  995.     move.l    a1,(a0)+    ; This is our backbranch address
  996.     move.l    a0,4(a1)    ; Give them the forward branch address
  997.     move.l    a0,d7        ; Restore HERE
  998.     jmp    next
  999. aloop3    move.l    #loop_msg,a0
  1000.     jsr    prstr
  1001.     jmp    abort
  1002. aloop_msg
  1003.     dc.b    10,'do not matched by +loop',10,0
  1004.     even
  1005.  
  1006. ;
  1007. ; (+loop)--run-time loop execution
  1008. ;
  1009. paloop2    dc.l    aloop2
  1010. paloop    dc.l    paloop1,paloop1,0
  1011.     dc.b    '(+loop) '
  1012. paloop1    move.l    (a5)+,d0    ; Add on number from user's stack
  1013.     add.l    d0,4(a4)
  1014.     move.l    (a6),a6        ; branch back
  1015.     jmp    next
  1016.  
  1017. ;
  1018. ; (loop)--run-time loop execution
  1019. ;
  1020. ploop2    dc.l    paloop2
  1021. ploop    dc.l    ploop1,ploop1,0
  1022.     dc.b    '(loop)  '
  1023. ploop1    addq.l    #1,4(a4)    ; Increment the run-time index
  1024.     move.l    (a6),a6        ; branch back
  1025.     jmp    next
  1026.  
  1027. ;
  1028. ; >r--pop top of operand stack & push on return stack
  1029. ;
  1030. pushr2    dc.l    ploop2
  1031. pushr    dc.l    pushr1,pushr1,0
  1032.     dc.b    '>r      '
  1033. pushr1    move.l    (a5)+,-(a4)
  1034.     jmp    next
  1035.  
  1036. ;
  1037. ; r>--pop top of return stack & push on operand stack
  1038. ;
  1039. popr2   dc.l    pushr2
  1040. popr    dc.l    popr1,popr1,0
  1041.         dc.b    'r>      '
  1042. popr1   move.l  (a4)+,-(a5)
  1043.         jmp     next
  1044.  
  1045. ;
  1046. ; r@--copy top of return stack to user stack
  1047. ;
  1048. rget2   dc.l    popr2,rget1,rget1,0
  1049.         dc.b    'r@      '
  1050. rget1   move.l  (a4),-(a5)
  1051.         jmp     next
  1052.  
  1053. ;
  1054. ; depth--tell how many elements are on user stack
  1055. ;
  1056. depth2  dc.l    rget2,depth1,depth1,0
  1057.         dc.b    'depth   '
  1058. depth1  move.l  #stack+stacksize,d0
  1059.         sub.l   a5,d0
  1060.         asr.l   #2,d0
  1061.         move.l  d0,-(a5)
  1062.         jmp     next
  1063.  
  1064. ;
  1065. ; i--push index of innermost do..loop context
  1066. ;
  1067. push_i2 dc.l    depth2,push_i1,push_i1,0
  1068.         dc.b    'i       '
  1069. push_i1 move.l  4(a4),-(a5)
  1070.         jmp     next
  1071. ;
  1072. ; j--like i, but second most-innermost
  1073. ;
  1074. push_j2 dc.l    push_i2,push_j1,push_j1,0
  1075.         dc.b    'j       '
  1076. push_j1 move.l  12(a4),-(a5)
  1077.         jmp     next
  1078.  
  1079. ;
  1080. ; leave--jump out of the innermost loop structure. Note that control
  1081. ;    structure matching isn't done here, since we will probably be
  1082. ;    inside of multiple if..endif contexts--meaningful error checking
  1083. ;    would be very difficult to provide.
  1084. ;
  1085. leave2    dc.l    push_j2,leave1,leave1,Priority
  1086.     dc.b    'leave   '
  1087. leave1    move.l    (a4),a1        ; This is the address of the (do) part
  1088.     move.l    d7,a0        ; We will be compiling some stuff in:
  1089.     move.l    #pleave,(a0)+    ;  (leave)
  1090.     addq.l    #4,a1        ;  addr of the exit location--(do)+1
  1091.     move.l    a1,(a0)+
  1092.     move.l    a0,d7
  1093.     jmp    next
  1094.  
  1095. ;
  1096. ; (leave)--fetch via the word which follows us, and make that the IP
  1097. ;
  1098. pleave2    dc.l    leave2
  1099. pleave    dc.l    pleave1,pleave1,0
  1100.     dc.b    '(leave) '
  1101. pleave1    move.l    (a6),a0        ; Addr of exit address
  1102.     move.l    (a0),a6        ; Set IP to it
  1103.     addq.l    #8,a4        ; Clear the do..loop's parameters of rstack
  1104.     jmp    next
  1105.  
  1106. ;
  1107. ; if--starting part of a conditional
  1108. ;
  1109. if2    dc.l    pleave2,if1,if1,Priority
  1110.     dc.b    'if      '
  1111. if1    move.l    d7,a0
  1112.     move.l    #zbranch,(a0)+    ; If false, branch around
  1113.     move.l    a0,-(a5)    ;  save this place for back-branch
  1114.     clr.l    (a0)+        ;  leave room for it
  1115.     move.l    a0,d7
  1116.     move.l    #FlgIf,-(a5)    ; Flag the control structure
  1117.     jmp    next
  1118.  
  1119. ;
  1120. ; else--optional middle part of a conditional
  1121. ;
  1122. else2    dc.l    if2,else1,else1,Priority
  1123.     dc.b    'else    '
  1124. else1    cmp.l    #FlgIf,(a5)    ; Check control structure
  1125.     bne    else3
  1126.     move.l    d7,a0
  1127.     move.l    4(a5),a1    ; Save location to backpatch
  1128.     move.l    #branch,(a0)+    ; Patch in a branch out of the conditional
  1129.     move.l    a0,4(a5)    ;  the new back-patch location
  1130.     clr.l    (a0)+
  1131.     move.l    a0,(a1)        ; Now patch in address of false part of cond.
  1132.     move.l    a0,d7
  1133.     jmp    next
  1134.  
  1135. else3    move.l    #else_msg,a0    ; Complain about bad control structure
  1136.     jsr    prstr
  1137.     jmp    abort
  1138. else_msg
  1139.     dc.b    10,'else does not match an if',10,0
  1140.     even
  1141.  
  1142. ;
  1143. ; endif--ending part of a conditional
  1144. ;
  1145. endif2    dc.l    else2,endif1,endif1,Priority
  1146.     dc.b    'endif   '
  1147. endif1    cmp.l    #FlgIf,(a5)    ; Check control strucure
  1148.     bne    endif3
  1149.     addq.l    #4,a5        ; Pop off flag
  1150.     move.l    (a5)+,a0    ; Get address to back-patch
  1151.     move.l    d7,(a0)        ;  backpatch it
  1152.     jmp    next
  1153.  
  1154. endif3    move.l    #endif_msg,a0    ; complain
  1155.     jsr    prstr
  1156.     jmp    abort
  1157. endif_msg
  1158.     dc.b    10,'endif does not match if/else',10,0
  1159.     even
  1160.  
  1161. ;
  1162. ; stack manipulation words--dup, swap, rot, -rot, drop, over
  1163. ;
  1164. over2   dc.l    endif2,over1,over1,0
  1165.         dc.b    'over    '
  1166. over1   move.l  4(a5),-(a5)
  1167.         jmp     next
  1168. pick2   dc.l    over2,pick1,pick1,0
  1169.         dc.b    'pick    '
  1170. pick1   move.l  (a5)+,d0
  1171.         asl.l   #2,d0           ; Scale D0 for a word offset
  1172.         move.l  0(a5,d0.l),-(a5)
  1173.         jmp     next
  1174. roll2   dc.l    pick2,roll1,roll1,0
  1175.         dc.b    'roll    '
  1176. roll1   move.l  (a5)+,d0
  1177.         asl.l   #2,d0
  1178.         move.l  0(a5,d0.l),d1           ; Save word rolling into
  1179. roll3   tst.l   d0                      ; While not to top of stack...
  1180.         beq     roll4
  1181.         move.l  -4(a5,d0.l),0(a5,d0.l)  ; Copy down a word
  1182.         subq.l  #4,d0                   ; Advance a word
  1183.         bra     roll3
  1184. roll4   move.l  d1,(a5)                 ; Replace top with word
  1185.         jmp     next
  1186. dup2    dc.l    roll2,dup1,dup1,0
  1187.         dc.b    'dup     '
  1188. dup1    move.l  (a5),-(a5)
  1189.         jmp     next
  1190. qdup2   dc.l    dup2,qdup1,qdup1,0
  1191.         dc.b    '?dup    '
  1192. qdup1   move.l  (a5),d0
  1193.         beq     qdup3
  1194.         move.l  d0,-(a5)
  1195. qdup3   jmp    next
  1196. swap2   dc.l    qdup2,swap1,swap1,0
  1197.         dc.b    'swap    '
  1198. swap1   move.l  (a5)+,d0
  1199.         move.l  (a5),d1
  1200.         move.l  d0,(a5)
  1201.         move.l  d1,-(a5)
  1202.         jmp     next
  1203. rot2    dc.l    swap2,rot1,rot1,0
  1204.         dc.b    'rot     '
  1205. rot1    move.l  (a5)+,d0
  1206.         move.l  (a5)+,d1
  1207.         move.l  (a5),d2
  1208.         move.l  d1,(a5)
  1209.         move.l  d0,-(a5)
  1210.         move.l  d2,-(a5)
  1211.         jmp     next
  1212. drot2   dc.l    rot2,drot1,drot1,0
  1213.         dc.b    '-rot    '
  1214. drot1    move.l    (a5)+,d0
  1215.     move.l    (a5)+,d1
  1216.   move.l  (a5),d2
  1217.         move.l  d0,(a5)
  1218.         move.l  d2,-(a5)
  1219.         move.l  d1,-(a5)
  1220.         jmp     next
  1221. drop2   dc.l    drot2,drop1,drop1,0
  1222.         dc.b    'drop    '
  1223. drop1   addq.l  #4,a5
  1224.         jmp     next
  1225.  
  1226. ;
  1227. ; begin--start a structured loop
  1228. ;
  1229. beg2    dc.l    drop2,beg1,beg1,Priority
  1230.         dc.b    'begin   '
  1231. beg1    move.l  d7,-(a5)
  1232.         move.l  #FlgBeg,-(a5)
  1233.         jmp     next
  1234.  
  1235. ;
  1236. ; again--unconditional branch back; an infinite loop
  1237. ;
  1238. again2  dc.l    beg2,again1,again1,Priority
  1239.         dc.b    'again   '
  1240. again1  cmp.l   #FlgBeg,(a5)
  1241.         bne     again3          
  1242.         addq.l  #4,a5
  1243.         move.l  d7,a0
  1244.         move.l  #branch,(a0)+
  1245.         move.l  (a5)+,(a0)+
  1246.         move.l  a0,d7
  1247.         jmp     next
  1248. again3  move.l  #again_msg,a0
  1249.         jsr     prstr
  1250.         jmp     abort
  1251. again_msg
  1252.         dc.b    10,'again does not match a begin',10,0
  1253.         even
  1254.  
  1255. ;
  1256. ; until--branch back until condition becomes true
  1257. ;
  1258. until2  dc.l    again2,until1,until1,Priority
  1259.         dc.b    'until   '
  1260. until1  cmp.l   #FlgBeg,(a5)
  1261.         bne     until3
  1262.         addq.l  #4,a5
  1263.         move.l  d7,a0
  1264.         move.l  #zbranch,(a0)+
  1265.         move.l  (a5)+,(a0)+
  1266.         move.l  a0,d7
  1267.         jmp     next
  1268. until3  move.l  #until_msg,a0
  1269.         jsr     prstr
  1270.         jmp     abort
  1271. until_msg
  1272.         dc.b    10,'until does not match a begin',10,0
  1273.         even
  1274.  
  1275. ;
  1276. ; while..repeat: loop with exit check up front
  1277. ;
  1278. while2    dc.l    until2,while1,while1,Priority
  1279.     dc.b    'while   '
  1280. while1    cmp.l    #FlgBeg,(a5)        ; Check control structure
  1281.     bne    while3
  1282.     move.l    d7,a0
  1283.     move.l    #zbranch,(a0)+        ; Branch out on false
  1284.     move.l    a0,(a5)            ;  save where to backpatch
  1285.     clr.l    (a0)+
  1286.     move.l    a0,d7
  1287.     move.l    #FlgWhi,-(a5)        ; And place our own flag
  1288.     jmp    next
  1289. while3    move.l    #while_msg,a0        ; Complain
  1290.     jsr    prstr
  1291.     jmp    abort
  1292. while_msg
  1293.     dc.b    10,'while does not match a begin',10,0
  1294.     even
  1295.  
  1296. ;
  1297. ; repeat--the closing part of a begin..while..repeat structure
  1298. ;
  1299. rep2    dc.l    while2,rep1,rep1,Priority
  1300.     dc.b    'repeat  '
  1301. rep1    cmp.l    #FlgWhi,(a5)        ; Check control structure
  1302.     bne    rep3
  1303.         addq.l  #4,a5
  1304.         move.l  (a5)+,a1                ; Save where to backpatch
  1305.         move.l  d7,a0
  1306.         move.l  #branch,(a0)+           ; Generate a backbranch
  1307.         move.l  (a5)+,(a0)+             ;  to top of loop
  1308.         move.l  a0,d7
  1309.         move.l  d7,(a1)                 ; Backpatch exit location, HERE
  1310.         jmp     next
  1311. rep3    move.l  #rep_msg,a0             ; Complain
  1312.         jsr     prstr
  1313.         jmp     abort
  1314. rep_msg dc.b    10,'repeat does not match a while',10,0
  1315.         even
  1316.  
  1317. ;
  1318. ; xor--exclusive OR
  1319. ;
  1320. xor2    dc.l    rep2,xor1,xor1,0
  1321.         dc.b    'xor     '
  1322. xor1    move.l  (a5)+,d0
  1323.         eor     d0,(a5)
  1324.         jmp     next
  1325.  
  1326. ;
  1327. ; not--one's complement
  1328. ;
  1329. not2    dc.l    xor2,not1,not1,0
  1330.         dc.b    'not     '
  1331. not1    eor     #0xFFFFFFFF,(a5)
  1332.         jmp     next
  1333.  
  1334. ;
  1335. ; 1+, 1-, 2+, 2-, 2*, 2/--common, quick math operations
  1336. ;
  1337. onep2   dc.l    not2,onep1,onep1,0
  1338.         dc.b    '1+      '
  1339. onep1   addq.l  #1,(a5)
  1340.         jmp     next
  1341. onem2   dc.l    onep2,onem1,onem1,0
  1342.         dc.b    '1-      '
  1343. onem1   subq.l  #1,(a5)
  1344.         jmp     next
  1345. twop2   dc.l    onem2,twop1,twop1,0
  1346.         dc.b    '2+      '
  1347. twop1   addq.l  #2,(a5)
  1348.         jmp     next
  1349. twom2   dc.l    twop2,twom1,twom1,0
  1350.         dc.b    '2-      '
  1351. twom1   subq.l  #2,(a5)
  1352.         jmp     next
  1353. twot2   dc.l    twom2,twot1,twot1,0
  1354.         dc.b    '2*      '
  1355. twot1   move.l  (a5),d0
  1356.         asl.l   #1,d0
  1357.         move.l  d0,(a5)
  1358.         jmp     next
  1359. twod2   dc.l    twot2,twod1,twod1,0 
  1360.         dc.b    '2/      '
  1361. twod1   move.l  (a5),d0
  1362.         asr.l   #1,d0
  1363.         move.l  d0,(a5)
  1364.         jmp     next
  1365.  
  1366. ;
  1367. ; c@, c!--character fetch/store
  1368. ;
  1369. cfetch2 dc.l    twod2,cfetch1,cfetch1,0
  1370.         dc.b    'c@      '
  1371. cfetch1 move.l  (a5),a0
  1372.         move.b  (a0),d0
  1373.         ext.w   d0
  1374.         ext.l   d0
  1375.         move.l  d0,(a5)
  1376.         jmp     next
  1377. cstore2 dc.l    cfetch2,cstore1,cstore1,0
  1378.         dc.b    'c!      '
  1379. cstore1 move.l  (a5)+,a0
  1380.         move.l  (a5)+,d0
  1381.         move.b  d0,(a0)
  1382.         jmp     next
  1383. pstore2 dc.l    cstore2,pstore1,pstore1,0
  1384.         dc.b    '+!      '
  1385. pstore1 move.l  (a5)+,a0
  1386.         move.l  (a5)+,d0
  1387.         add.l   d0,(a0)
  1388.         jmp     next
  1389.  
  1390. ;
  1391. ; min and max--push greater or less of two numbers
  1392. ;
  1393. min2    dc.l    pstore2,min1,min1,0
  1394.         dc.b    'min     '
  1395. min1    move.l  (a5)+,d0
  1396.         cmp.l   (a5),d0
  1397.         bge     min3
  1398. min4    move.l  d0,(a5)
  1399. min3    jmp     next
  1400. max2    dc.l    min2,max1,max1,0
  1401.         dc.b    'max     '
  1402. max1    move.l  (a5)+,d0
  1403.         cmp.l   (a5),d0
  1404.         ble     min3
  1405.         bra     min4
  1406.  
  1407. ;
  1408. ; abs, negate--replace number with its absolute value or negation
  1409. ;
  1410. abs2    dc.l    max2,abs1,abs1,0
  1411.         dc.b    'abs     '
  1412. abs1    move.l  (a5),d0
  1413.         bge     min3
  1414.         neg.l   (a5)
  1415.         jmp     next
  1416. neg2    dc.l    abs2,neg1,neg1,0
  1417.         dc.b    'negate  '
  1418. neg1    neg.l   (a5)
  1419.         jmp     next
  1420.  
  1421. ;
  1422. ; cmove--move a range of bytes
  1423. ;
  1424. cmov2    dc.l    neg2,cmov1,cmov1,0
  1425.     dc.b    'cmove   '
  1426. cmov1    move.l    (a5)+,d0    ; Count
  1427.     move.l    (a5)+,a0    ; Destination
  1428.     move.l    (a5)+,a1    ; Source
  1429.     tst.l    d0        ; Catch case of zero-length
  1430.     beq    cmov4
  1431. cmov3    move.b    (a1)+,(a0)+    ; Move bytes
  1432.     dbra    d0,cmov3
  1433. cmov4    jmp    next
  1434.  
  1435. ;
  1436. ; cmove>--like cmove, but set up to guard against the "ripple" effect
  1437. ;
  1438. cmovu2    dc.l    cmov2,cmovu1,cmovu1,0
  1439.     dc.b    'cmove>  '
  1440. cmovu1    move.l    (a5)+,d0    ; Count
  1441.     move.l    (a5)+,a0    ; Destination
  1442.     move.l    (a5)+,a1    ; Source
  1443.     tst.l    d0        ; Zero-length?
  1444.     beq    cmov4
  1445.     add.l    d0,a0        ; Point to end of destination
  1446.     add.l    d0,a1        ;  same for source
  1447. cmovu3    move.b    -(a1),-(a0)    ; Move bytes
  1448.     dbra    d0,cmovu3
  1449.     jmp    next
  1450.  
  1451. ;
  1452. ; fill--fill a range of bytes with a constant
  1453. ;
  1454. fill2    dc.l    cmovu2,fill1,fill1,0
  1455.     dc.b    'fill    '
  1456. fill1    move.l    (a5)+,d0    ; Get byte constant to use
  1457.     move.l    (a5)+,d1    ; # Bytes to fill
  1458.     move.l    (a5)+,a0    ; Where to start
  1459.     tst.l    d0        ; Avoid zero-length
  1460.     beq    cmov4
  1461. fill3    move.b    d0,(a0)+    ; Fill bytes
  1462.     subq.l    #1,d1
  1463.     bne    fill3
  1464.     jmp    next
  1465.  
  1466. ;
  1467. ; count--get byte at addr, advance addr
  1468. ;
  1469. count2    dc.l    fill2,count1,count1,0
  1470.     dc.b    'count   '
  1471. count1    move.l    (a5),a0        ; Get addr
  1472.     move.b    (a0)+,d0    ; Get byte at addr, advance
  1473.     move.l    a0,(a5)        ; Store back addr
  1474.     ext.w    d0        ;  and extended byte
  1475.     ext.l    d0
  1476.     move.l    d0,-(a5)
  1477.     jmp    next
  1478.  
  1479. ;
  1480. ; -trailing--trim trailing spaces
  1481. ;
  1482. dtrail2    dc.l    count2,dtrail1,dtrail1,0
  1483.     dc.b    '-trailin'
  1484. dtrail1    move.l    (a5)+,d0    ; Current count
  1485.     beq    dtrail4        ;  handle zero-length
  1486.     move.l    (a5),a0        ; Address of string
  1487.     add.l    d0,a0        ; Get address of current end of string
  1488. dtrail3    cmp.b    #32,-(a0)    ; Check next char
  1489.     beq    dtrail4
  1490.     subq.l    #1,d0
  1491.     bne    dtrail3
  1492. dtrail4    move.l    d0,-(a5)    ; Push back count
  1493.     jmp    next
  1494.  
  1495. ;
  1496. ; decimal, hex, octal--set BASE
  1497. ;
  1498. deci2    dc.l    dtrail2,deci1,deci1,0
  1499.     dc.b    'decimal '
  1500. deci1    move.l    #10,base
  1501.     jmp    next
  1502. hexa2    dc.l    deci2,hexa1,hexa1,0
  1503.     dc.b    'hex     '
  1504. hexa1    move.l    #16,base
  1505.     jmp    next
  1506. octa2    dc.l    hexa2,octa1,octa1,0
  1507.     dc.b    'octal   '
  1508. octa1    move.l    #8,base
  1509.     jmp    next
  1510.  
  1511. ;
  1512. ; The number printing words--<# # #> #s hold sign
  1513. ;
  1514. lsh_pos    ds.l    1            ; Position in output buffer
  1515.  
  1516. lsh2    dc.l    octa2,lsh1,lsh1,0
  1517.     dc.b    '<#      '        ; Prepare for conversion
  1518. lsh1    move.l    #pad1+70,lsh_pos
  1519.     jmp    next
  1520.  
  1521. sh2    dc.l    lsh2,sh1,sh1,0
  1522.     dc.b    '#       '        ; Convert next digit
  1523. sh1    jsr    sh99
  1524.     jmp    next
  1525.  
  1526. sh99    move.l    base,-(sp)        ;  get BASE--format is wrong in mem.
  1527.     move.l    (a5),-(sp)
  1528.     jbsr    _lrem
  1529.     move.l    d1,(a5)            ;  put quotient back to stack
  1530.     add.l    #48,d0            ;  Remainder: map 0 to '0'
  1531.     cmp.l    #58,d0            ;  Check for HEX digits
  1532.     blt    sh3
  1533.     addq.l    #7,d0            ;  Map 10 to 'A'
  1534. sh3    move.l    lsh_pos,a0        ;  Store character into PAD, advance
  1535.     move.b    d0,-(a0)
  1536.     move.l    a0,lsh_pos
  1537.     rts
  1538.  
  1539. shg2    dc.l    sh2,shg1,shg1,0
  1540.     dc.b    '#>      '        ; End conversion
  1541. shg1    move.l    lsh_pos,d0
  1542.     move.l    d0,(a5)            ; Push address
  1543.     move.l    #pad1+70,d1        ; Calculate count
  1544.     sub.l    d0,d1
  1545.     move.l    d1,-(a5)        ; Push count
  1546.     jmp    next
  1547.  
  1548. shs2    dc.l    shg2,shs1,shs1,0
  1549.     dc.b    '#s      '        ; Convert all remaining digits
  1550. shs1    jsr    sh99            ; Do a digit
  1551.     tst.l    (a5)            ; See if done
  1552.     bne    shs1
  1553.     jmp    next
  1554.  
  1555. hold2    dc.l    shs2,hold1,hold1,0
  1556.     dc.b    'hold    '        ; Put a char into the string
  1557. hold1    move.l    lsh_pos,a0
  1558.     move.l    (a5)+,d0
  1559.     move.b    d0,-(a0)
  1560.     move.l    a0,lsh_pos
  1561. hold3    jmp    next
  1562.  
  1563. sign2    dc.l    hold2,sign1,sign1,0
  1564.     dc.b    'sign    '        ; Add a '-' if sign negative
  1565. sign1    tst.l    (a5)+
  1566.     bge    hold3
  1567.     move.l    #45,-(a5)
  1568.     bra    hold1
  1569.  
  1570. ;
  1571. ; ."--generate code to print a string at run-time
  1572. ;
  1573. dotq2    dc.l    sign2,dotq1,dotq1,Priority
  1574.     dc.b    '."      '
  1575. dotq1    move.l    d7,a0
  1576.     move.l    #pdotq,(a0)+    ; Compile (.")
  1577.     move.l    d6,a1        ; Get line pointer
  1578.     addq.l    #1,a1        ;  advance past current word delimiter
  1579. dotq3
  1580.     move.b    (a1)+,d0    ; Get next char
  1581.     beq    dotq5        ;  read a new buffer if we run out
  1582.     cmp.b    #34,d0        ; End when we find the closing "
  1583.     beq    dotq4
  1584.     move.b    d0,(a0)+    ; Add the character
  1585.     bra    dotq3
  1586.  
  1587. dotq5    move.l    a0,-(sp)
  1588.     jsr    getline        ; Get new buffer
  1589.     move.l    (sp)+,a0
  1590.     move.l    d6,a1
  1591.     bra    dotq3
  1592.  
  1593. dotq4    clr.b    (a0)+        ; Terminating NULL
  1594.     move.l    a1,d6        ; Update line pointer
  1595.     move.l    a0,d7
  1596.     addq.l    #3,d7        ; Longword-align DP
  1597.     and.l    #0xFFFFFFFC,d7
  1598.     jmp    next
  1599.  
  1600. ;
  1601. ; (.")--run-time word to print a string
  1602. ;
  1603. pdotq2    dc.l    dotq2
  1604. pdotq    dc.l    pdotq1,pdotq1,0
  1605.     dc.b    '(.")    '
  1606. pdotq1    move.l    a6,a0
  1607.     jsr    prstr
  1608. pdotq3    tst.b    (a6)+        ; Skip past text
  1609.     bne    pdotq3
  1610.     move.l    a6,d0
  1611.     addq.l    #3,d0        ; Align IP
  1612.     and.l    #0xFFFFFFFC,d0
  1613.     move.l    d0,a6
  1614.     jmp    next
  1615.  
  1616. ;
  1617. ; .(--print a message to the terminal from the input stream
  1618. ;
  1619. dotp2    dc.l    pdotq2,dotp1,dotp1,Priority
  1620.     dc.b    '.(      '
  1621. dotp1    move.l    d6,a1        ; Get line pointer
  1622.     addq.l    #1,a1        ;  advance past current word delimiter
  1623.     move.l    #pad1,a0    ; Build message into PAD
  1624.  
  1625. dotp3    move.b    (a1)+,d0    ; Get next char
  1626.     beq    dotp5        ;  read a new buffer if we run out
  1627.     cmp.b    #41,d0        ; End when we find the closing "
  1628.     beq    dotp4
  1629.     move.b    d0,(a0)+    ; Add the character
  1630.     bra    dotp3
  1631.  
  1632. dotp5    jsr    getline        ; Get new buffer
  1633.     move.l    d6,a1
  1634.     bra    dotp3
  1635.  
  1636. dotp4    clr.b    (a0)+        ; Terminating NULL
  1637.     move.l    a1,d6        ; Update line pointer
  1638.     move.l    #pad1,a0    ; Print the message
  1639.     jsr    prstr
  1640.     jmp    next
  1641.  
  1642. ;
  1643. ; cr--print newline
  1644. ;
  1645. cr_msg    dc.b    10,0
  1646. cr2    dc.l    dotp2,cr1,cr1,0
  1647.     dc.b    'cr      '
  1648. cr1    move.l    #cr_msg,a0
  1649.     jsr    prstr
  1650.     jmp    next
  1651.  
  1652. ;
  1653. ; emit--print out a character
  1654. ;
  1655. emit_buf
  1656.     ds.b    1
  1657.     dc.b    0,0,0        ; Terminating NULL, 2 wasted
  1658. emit2    dc.l    cr2,emit1,emit1,0
  1659.     dc.b    'emit    '
  1660. emit1    move.l    (a5)+,d0
  1661.     move.b    d0,emit_buf
  1662.     move.l    #emit_buf,a0
  1663.     jsr    prstr
  1664.     jmp    next
  1665.  
  1666. ;
  1667. ; type--print out a string given a count & a pointer
  1668. ;
  1669. type2    dc.l    emit2,type1,type1,0
  1670.     dc.b    'type    '
  1671. type1    move.l    (a5)+,d0    ; Count
  1672.     move.l    (a5)+,a0    ; Addr
  1673.     move.l    #pad1,a1    ; Where to buffer to
  1674. type3    tst.l    d0        ; Out of chars?
  1675.     beq    type4
  1676.     move.b    (a0)+,(a1)+    ; Store a char
  1677.     subq.l    #1,d0        ; Decrement count
  1678.     bra    type3
  1679. type4    clr.b    (a1)        ; Terminating NULL
  1680.     move.l    #pad1,a0
  1681.     jsr    prstr
  1682.     jmp    next
  1683.  
  1684. ;
  1685. ; space--emit a space
  1686. ;
  1687. space2    dc.l    type2,space1,space1,0
  1688.     dc.b    'space   '
  1689. space1    move.l    #32,-(a5)
  1690.     bra    emit1
  1691.  
  1692. ;
  1693. ; spaces--emit N spaces
  1694. ;
  1695. spac_buf            ; A printable space
  1696.     dc.b    32,0,0,0
  1697. spaces2    dc.l    space2,spaces1,spaces1,0
  1698.     dc.b    'spaces  '
  1699. spaces1    tst.l    (a5)        ; Enough spaces?
  1700.     beq    spaces3
  1701.     move.l    #spac_buf,a0
  1702.     jsr    prstr
  1703.     sub.l    #1,(a5)        ; Decrement count
  1704.     bra    spaces1
  1705. spaces3    addq.l    #4,a5        ; Pop count
  1706.     jmp    next
  1707.  
  1708. ;
  1709. ; key--get a key from STDIN. Normally, this will block until a whole
  1710. ;    line is entered. However, if the TTY is put into RAW mode,
  1711. ;    this will respond on a key-by-key basis.
  1712. ;
  1713. keybuf    ds.l    1        ; Holds the keystroke
  1714. key2    dc.l    spaces2,key1,key1,0
  1715.     dc.b    'key     '
  1716. key1    move.l    #1,-(a7)    ; Build READ syscall parameters--1 byte
  1717.     move.l    #keybuf,-(a7)    ;  buffer address
  1718.     clr.l    -(a7)        ;  0--STDIN
  1719.     clr.l    -(a7)        ;  dummy
  1720.         moveq   #3,d0        ;  UNIX READ syscall
  1721.     trap    #0
  1722.     add.l    #16,a7        ; Remove the parameters from stack
  1723.     move.b    keybuf,d0    ; Push byte
  1724.     ext.w    d0
  1725.     ext.l    d0
  1726.     move.l    d0,-(a5)
  1727.     jmp    next
  1728.  
  1729. ;
  1730. ; expect--read a number of chars from the terminal
  1731. ;
  1732. expect2    dc.l    key2,expect1,expect1,0
  1733.     dc.b    'expect  '
  1734. expect1    move.l    (a5)+,-(a7)    ; UNIX syscall: N bytes
  1735.     move.l    (a5)+,-(a7)    ;  to buffer
  1736.     clr.l    -(a7)        ;  STDIN
  1737.     clr.l    -(a7)        ;  dummy
  1738.         moveq   #3,d0        ;  UNIX READ syscall
  1739.     trap    #0
  1740.     move.l    d0,span        ; Store # bytes read
  1741.     add.l    #16,a7        ; Remove the parameters from stack
  1742.     jmp    next
  1743. span2    dc.l    expect2,getpfa,span,0
  1744.     dc.b    'span    '
  1745. span    ds.l    1
  1746.  
  1747. ;
  1748. ; abort--jump to abort
  1749. ;
  1750. abort2    dc.l    span2
  1751. do_abort dc.l    abort,abort,0
  1752.     dc.b    'abort   '
  1753.  
  1754. ;
  1755. ; abort"--if top is true, print a message and abort
  1756. ;
  1757. qabort2    dc.l    abort2,qabort1,qabort1,Priority
  1758.     dc.b    'abort"  '
  1759. qabort1    move.l    d7,a0
  1760.     move.l    #zbranch,(a0)+    ; Skip the whole shebang on false
  1761.     move.l    a0,a2        ; Mark where to backpatch
  1762.     clr.l    (a0)+        ; Leave room for the branch address
  1763.  
  1764.     move.l    #pdotq,(a0)+    ; Compile (.")
  1765.     move.l    d6,a1        ; Get line pointer
  1766.     addq.l    #1,a1        ;  advance past current word delimiter
  1767. qabort3
  1768.     move.b    (a1)+,d0    ; Get next char
  1769.     beq    qabort5        ;  read a new buffer if we run out
  1770.     cmp.b    #34,d0        ; End when we find the closing "
  1771.     beq    qabort4
  1772.     move.b    d0,(a0)+    ; Add the character
  1773.     bra    qabort3
  1774.  
  1775. qabort5    jsr    getline        ; Get new buffer
  1776.     move.l    d6,a1
  1777.     bra    qabort3
  1778.  
  1779. qabort4    clr.b    (a0)+        ; Terminating NULL
  1780.     move.l    a1,d6        ; Update line pointer
  1781.     move.l    a0,d7
  1782.     addq.l    #3,d7        ; Longword-align DP
  1783.     and.l    #0xFFFFFFFC,d7
  1784.     move.l    d7,a0
  1785.     move.l    #do_abort,(a0)+    ; Put in ABORT
  1786.     move.l    a0,d7
  1787.     move.l    d7,(a2)        ; Backpatch false case
  1788.     jmp    next
  1789.  
  1790. ;
  1791. ; quit--leave parameter stack alone, but return to INTERP
  1792. ;
  1793. quit2    dc.l    qabort2,quit1,quit1,0
  1794.     dc.b    'quit    '
  1795. quit1    move.l    #rstack+stacksize,a4    ; Clear return stack
  1796.     move.l    #interp,a6
  1797.     jmp    next
  1798.  
  1799. ;
  1800. ; here--push address of next free location
  1801. ;
  1802. here2    dc.l    quit2,here1,here1,0
  1803.     dc.b    'here    '
  1804. here1    move.l    d7,-(a5);
  1805.     jmp    next
  1806.  
  1807. ;
  1808. ; tib--address of text input buffer
  1809. ;
  1810. tib2    dc.l    here2,tib1,tib1,0
  1811.     dc.b    'tib     '
  1812. tib1    move.l    iunit,-(a5)
  1813.     jmp    next
  1814.  
  1815. ;
  1816. ; >body--turn pointer to CFA field into pointer to parameter field
  1817. ;
  1818. gbod2    dc.l    tib2,gbod1,gbod1,0
  1819.     dc.b    '>body   '
  1820. gbod1    move.l    (a5),a0
  1821.     move.l    4(a0),(a5)
  1822.     jmp    next
  1823.  
  1824. ;
  1825. ; (--start a forth comment )
  1826. ;
  1827. paren2    dc.l    gbod2,paren1,paren1,Priority
  1828.     dc.b    '(       '    ; )
  1829. paren1    move.l    d6,a0
  1830. paren4    move.b    (a0)+,d0    ; Get next char
  1831.     cmp.b    #41,d0        ; End on closing paren
  1832.     beq    paren3
  1833.     tst.b    d0        ; Get new buffer on end of current
  1834.     bne    paren4
  1835.     jsr    getline
  1836.     bra    paren1
  1837. paren3    move.l    a0,d6        ; Restore line pointer
  1838.     jmp    next
  1839.  
  1840. ;
  1841. ; allot--allocate N bytes off end of dictionary
  1842. ;
  1843. allot2    dc.l    paren2,allot1,allot1,0
  1844.     dc.b    'allot   '
  1845. allot1    move.l    (a5)+,d0
  1846.     add.l    d0,d7
  1847.     jmp    next
  1848.  
  1849. ;
  1850. ; does>--terminate execution of word which calls this, but also set it up
  1851. ;    so that the LATEST word has its PFA directed to after this word.
  1852. ;    : definer create ...1... does> ...2... ;
  1853. ;    Will be used as: definer <word>
  1854. ;    <word> will be added to the dictionary, and ...1... may do any
  1855. ;    actions it wishes. When <word> is later executed, it will run
  1856. ;    the code ...2...
  1857. ;
  1858. does2    dc.l    allot2,does1,does1,Priority
  1859.     dc.b    'does>   '
  1860. does1    move.l    d7,a0
  1861.     move.l    #pdoes,(a0)+    ; Compile in (does)
  1862.     move.l    a0,d7
  1863.     jmp    next
  1864. pdoes2    dc.l    does2
  1865. pdoes    dc.l    pdoes1,pdoes1,0
  1866.     dc.b    '(does)  '
  1867. pdoes1    move.l    late1,a0    ; Get LFA of latest definition
  1868.     move.l    #hilev,4(a0)    ; Make this execute as a high-level def
  1869.     move.l    a6,8(a0)    ; Fill in PFA with rest of this word's body
  1870.     move.l    (a4)+,a6    ; Return from this word
  1871.     jmp    next
  1872.  
  1873. ;
  1874. ; immediate--set the Priority bit of the latest definition
  1875. ;
  1876. immed2    dc.l    pdoes2,immed1,immed1,0
  1877.     dc.b    'immediat'
  1878. immed1    move.l    late1,a0
  1879.     or.l    #Priority,12(a0)    ; Set Priority in SFA word
  1880.     jmp    next
  1881.  
  1882. ;
  1883. ; [compile], compile--immediate & non-immediate versions of compile
  1884. ;
  1885. bcomp2    dc.l    immed2,bcomp1,bcomp1,Priority
  1886.     dc.b    '[compile'
  1887. bcomp1    jsr    getw1        ; Fetch next word from stream
  1888.     jsr    look99        ; See if it can be found
  1889.     tst.l    (a5)+        ; Error if it couldn't
  1890.     beq    bcomp3
  1891.     addq.l    #4,a5        ; Drop the priority field
  1892.     move.l    d7,a0        ; Compile in CFA
  1893.     move.l    (a5)+,(a0)+
  1894.     move.l    a0,d7
  1895.     jmp    next
  1896. bcomp3    jmp    notf1        ; Not found: complain
  1897.  
  1898. comp2    dc.l    bcomp2,bcomp1,bcomp1,0
  1899.     dc.b    'compile '
  1900.  
  1901. ;
  1902. ; literal--compile a literal
  1903. ;
  1904. lit2    dc.l    comp2,lit1,lit1,Priority
  1905.     dc.b    'literal '
  1906. lit1    move.l    d7,a0
  1907.     move.l    #plit,(a0)+
  1908.     move.l    (a5)+,(a0)+
  1909.     move.l    a0,d7
  1910.     jmp    next
  1911.  
  1912. ;
  1913. ; [, ]--turn compilation off & on, respectively
  1914. ;
  1915. compon2    dc.l    lit2,compon1,compon1,0
  1916.     dc.b    ']       '
  1917. compon1    move.l    #-1,state1
  1918.     jmp    next
  1919. compof2    dc.l    compon2,compof1,compof1,Priority
  1920.     dc.b    '[       '
  1921. compof1    clr.l    state1
  1922.     jmp    next
  1923.  
  1924. ;
  1925. ; word--get a word from the input stream, put in string
  1926. ;
  1927. word_buf ds.b    84
  1928. gword2    dc.l    compof2,gword1,gword1,0
  1929.     dc.b    'word    '
  1930. gword1    move.l    (a5)+,d0    ; Delimiter char
  1931.     move.l    #word_buf+1,a0    ; Where to put the chars
  1932.     move.l    d6,a1        ; Input line buffer
  1933.     clr.l    d2        ; Count # chars received
  1934. gword3    move.b    (a1)+,d1    ; Get next char
  1935.     beq    gword4        ;  get new bufferfull if current empty
  1936.     cmp.b    d0,d1        ; Found delimiter?
  1937.     beq    gword5
  1938.     move.b    d1,(a0)+    ; Store char
  1939.     addq.l    #1,d2        ; Increment count
  1940.     bra    gword3
  1941. gword4
  1942.     movem.l    d0/a0,-(a7)    ; Save d0 and a0
  1943.     jsr    getline        ; Get next line
  1944.     movem.l    (a7)+,d0/a0
  1945.     move.l    d6,a1
  1946.     bra    gword3
  1947. gword5
  1948.     clr.b    (a0)        ; Add NULL termination
  1949.     move.b    d2,word_buf    ; Store count in first byte
  1950.     move.l    a1,d6        ; Update line pointer
  1951.     move.l    #word_buf,-(a5)    ; Return pointer to it
  1952.     jmp    next
  1953.  
  1954. ;
  1955. ; >in--give a byte offset into current buffer
  1956. ;
  1957. to_in2    dc.l    gword2,to_in1,to_in1,0
  1958.     dc.b    '>in     '
  1959. to_in1    move.l    d6,d0
  1960.     sub.l    iunit,d0
  1961.     move.l    d0,-(a5)
  1962.     jmp    next
  1963.  
  1964. ;
  1965. ; #tib--length of current input buffer
  1966. ;
  1967. ntib2    dc.l    to_in2,ntib1,ntib1,0
  1968.     dc.b    '#tib    '
  1969. ntib1    move.l    iunit,a0    ; Ptr into buf
  1970.     clr.l    d1        ; Counter of # chars
  1971. ntib3    tst.b    (a0)+        ; Check next byte
  1972.     beq    ntib4
  1973.     addq.l    #1,d1
  1974.     bra    ntib3
  1975. ntib4    move.l    d1,-(a5)    ; Push count
  1976.     jmp    next
  1977.  
  1978. ;
  1979. ; create--create a dictionary entry
  1980. ;
  1981. creat2    dc.l    ntib2,creat1,creat1,0
  1982.     dc.b    'create  '
  1983. creat1    jsr    make_head    ; Build the header
  1984.     move.l    #getpfa,4(a0)    ; Set it up to be variable/constant
  1985.     jmp    next
  1986.  
  1987. ;
  1988. ; '--push address of CFA
  1989. ;
  1990. tick2    dc.l    creat2,tick1,tick1,0
  1991.     dc.b    39,'       '
  1992. tick1    jsr    getw1        ; Get word
  1993.     jsr    look99        ; Look up word
  1994.     tst.l    (a5)+        ; Abort on error
  1995.     beq    tick3
  1996.     addq.l    #4,a5        ; Drop priority flag
  1997.     jmp    next
  1998. tick3
  1999.     jmp    notf1
  2000.  
  2001. ;
  2002. ; [']--for compiling in a compilation address as a literal
  2003. ;
  2004. btick2    dc.l    tick2,btick1,btick1,Priority
  2005.     dc.b    '[',39,']     '
  2006. btick1    jsr    getw1        ; Get word
  2007.     jsr    look99        ; Look up word
  2008.     tst.l    (a5)+        ; Abort on error
  2009.     beq    tick3
  2010.     addq.l    #4,a5        ; Drop priority flag
  2011.     move.l    d7,a0        ; Compile in (lit)
  2012.     move.l    #plit,(a0)+
  2013.     move.l    (a5)+,(a0)+    ;  <compilation addr>
  2014.     move.l    a0,d7
  2015.     jmp    next
  2016.  
  2017. ;
  2018. ; find--find a string in the dictionary
  2019. ;
  2020. find2    dc.l    btick2,find1,find1,0
  2021.     dc.b    'find    '
  2022. find1    move.l    latest+4,a0        ; Get pointer to latest definition
  2023.     move.l    (a5),a1            ; Get search string
  2024.     move.l    (a1),d3
  2025.     move.l    4(a1),d4
  2026.     jsr    look5            ; Go find the string
  2027.     tst.l    (a5)            ; See if it was found
  2028.     beq    find3            ;  wasn't, can just return
  2029.     addq.l    #4,a5            ; Was, pop boolean flag
  2030.     tst.l    (a5)+            ; Change priority flag
  2031.     bne    find4
  2032.  
  2033.     move.l    (a5),4(a5)        ; Move comp addr over string addr
  2034.     move.l    #-1,(a5)        ;  not priority, flag -1
  2035.     bra    find3
  2036.  
  2037. find4    move.l    (a5),4(a5)        ; Move comp addr over string addr
  2038.     move.l    #1,(a5)            ;  was priority, flag 1
  2039.  
  2040. find3    jmp    next
  2041.  
  2042. ;
  2043. ; forget--find a word in the dictionary, and remove it
  2044. ;
  2045. forg2    dc.l    find2,forg1,forg1,0
  2046.     dc.b    'forget  '
  2047. forg1    jsr    getw1        ; Get the name to forget
  2048.     jsr    look99        ; Find it in the dictionary
  2049.     tst.l    (a5)+        ; Found it?
  2050.     beq    forg3        ;  nope...
  2051.     addq.l    #4,a5        ; Drop priority flag
  2052.     move.l    (a5)+,a0    ; Put CFA into A0
  2053.     subq.l    #4,a0        ; Put A0 back to LFA
  2054.     move.l    (a0),late1    ; Point LATEST to previous word
  2055.     move.l    a0,d7        ; Free memory back to here
  2056.     jmp    next
  2057.  
  2058. forg3    jmp    notf1        ; Forget WHO?
  2059.  
  2060. ;
  2061. ; input <file>--redirect input from a file
  2062. ;
  2063. input2    dc.l    forg2,input1,input1,0
  2064.     dc.b    'input   '
  2065. input1    move.l    iunit,a0    ; Room for more nesting?
  2066.     add.l    #Inbufsize,a0
  2067.     cmp.l    #End_inbufs,a0
  2068.     beq    input4
  2069.     move.l    a0,-(a7)    ; Save address of new buffer
  2070.  
  2071.     move.l    d6,a0        ; Read in until end of word
  2072.     jsr    skipwhite
  2073.     lea    pad1,a1        ; Where to build into
  2074. input10    jsr    iswhite        ; While not at end of word
  2075.     bne.s    input11
  2076.     tst.b    (a0)        ; At end of input buffer?
  2077.     bne.s    input12
  2078.     move.l    a1,-(a7)    ; Get new buffer-full
  2079.     jsr    getline
  2080.     move.l    (a7)+,a1
  2081.     move.l    d6,a0
  2082.     bra.s    input10
  2083.  
  2084. input11    move.b    (a0)+,(a1)+    ; Store next char
  2085.     bra.s    input10
  2086.  
  2087. input12    clr.b    (a1)        ; Trailing NULL
  2088.     move.l    a0,d6        ;  update input pointer
  2089.     clr.l    -(a7)        ; Mode 0=read
  2090.     pea    pad1        ; Pointer to file name
  2091.     clr.l    -(a7)        ; dummy space
  2092.     moveq    #5,d0        ; Open request
  2093.     trap    #0
  2094.     bcs    input3
  2095.     add.l    #12,a7        ; Get rid of parameters
  2096.     move.l    (a7)+,a0    ; Get new buffer addr again
  2097.     move.l    iunit,a1    ; Get previous
  2098.     move.l    a1,InbufPrev(a0) ;  Save
  2099.     move.l    d6,InbufIdx(a1)    ;   Save index into old buffer
  2100.     move.l    a0,InbufIdx(a0)    ; Clear the buffer
  2101.     move.b    d0,InUnit(a0)    ; Save UNIX FD to use
  2102.     clr.b    (a0)
  2103.     move.l    a0,d6
  2104.     move.l    a0,iunit    ; Update current input unit
  2105.     jmp    next
  2106.  
  2107. input3    lea    input_msg,a0
  2108. input5    jsr    prstr
  2109.     jmp    abort
  2110. input4    lea    input_msg2,a0
  2111.     bra.s    input5
  2112. input_msg asciz    'Could not open file for input'
  2113. input_msg2 asciz 'Too many files nested'
  2114.     even
  2115.  
  2116. ;
  2117. ; exit--return from the current high-level word
  2118. ;
  2119. exit2    dc.l    input2,pop1,pop1,0
  2120.     dc.b    'exit    '
  2121.  
  2122. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2123. ; Insert new definitions above here ;
  2124. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2125. ;
  2126. ; latest--pointer to most current LFA defined
  2127. ;
  2128. late2   dc.l    exit2
  2129. latest  dc.l    getpfa,late1,0
  2130.         dc.b    'latest  '
  2131. late1   dc.l    late2
  2132.  
  2133. ;
  2134. ; The user dictionary space
  2135. ;
  2136.     comm    udict,umem*1024    ; User dictionary space
  2137.  
  2138. ;
  2139. ; The End!
  2140. ;
  2141. Funky!Stuff!
  2142. cat - << \Funky!Stuff! > primes.fth
  2143. : isprime ( n -- b | Return whether 'n' is prime )
  2144. (  dup 2 mod 0= if drop 0 exit endif )
  2145.   -1 swap dup 2/ 1+ 3 do
  2146.     dup i mod 0= if swap drop 0 swap leave endif
  2147.   2 +loop
  2148.   drop
  2149. ;
  2150.  
  2151. : primes
  2152.   2001 5 do
  2153.     i isprime if i . cr endif
  2154.   2 +loop
  2155. ;
  2156. Funky!Stuff!
  2157.