home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol167 / pdl86.a86 < prev    next >
Encoding:
Text File  |  1984-04-29  |  22.2 KB  |  848 lines

  1.  
  2. ;    =======================================================
  3. ;
  4. ;    REC module for the operators and predicates pertaining
  5. ;    to the pushdown list, other than the most important
  6. ;    ones already contained in the REC nucleus.  These
  7. ;    comprise:
  8. ;
  9. ;        arithmetic
  10. ;
  11. ;            +    sum modulo 2**16
  12. ;            -    difference modulo 2**16
  13. ;            *    product modulo 2**16
  14. ;            /    remainder, quotient
  15. ;            =    equality
  16. ;            ~    complement or negative
  17. ;            d    decrement, false on zero
  18. ;            ^    increment
  19. ;            N    comparison of top 2 args
  20. ;
  21. ;        modification of arguments
  22. ;
  23. ;            H    hex ASCII string to binary
  24. ;            [exclm]    binary to hex ASCII string
  25. ;            %    restrict argument to one byte
  26. ;            \    embed argument in two bytes
  27. ;            &    exchange top arguments
  28. ;            |    concatinate top arguments
  29. ;
  30. ;        block movements
  31. ;
  32. ;            G    fetch a block from memory
  33. ;            g    address fetch
  34. ;            r    replace address by contents
  35. ;            u    incrementing byte fetch
  36. ;            y    incrementing word fetch
  37. ;            P    put buffer block in memory
  38. ;            S    store block in memory
  39. ;            s    store into buffer
  40. ;            v    incrementing byte store
  41. ;            m    move arg to end of PDL space
  42. ;            n    recover arg from end of PDL
  43. ;
  44. ;        generate pointers
  45. ;
  46. ;            c    reserve block, generate pointer
  47. ;            p    put px, py-px on PDL
  48. ;            l    put pz on PDL
  49. ;            $    form addr of variable cell
  50. ;
  51. ;    -------------------------------------------------------
  52. ;    Version of REC released during the summer school, 1980
  53. ;    -------------------------------------------------------
  54. ;
  55. ;         PDL86  -  Copyright (C) 1982
  56. ;        Universidad Autonoma de Puebla
  57. ;             All Rights Reserved
  58. ;
  59. ;         [Harold V. McIntosh, 25 April 1982]
  60. ;
  61. ;    May 29, 1983 - & exchanges arbitrary arguments
  62. ;    May 29, 1983 - ~ discontinued; use m&n instead
  63. ;    May 29, 1983 - ~ Complement or Negate top element
  64. ;    May 29, 1983 - N for Numerical comparison on PDL
  65. ;    July 7, 1983 - $ with char arg gets subroutine addr
  66. ;    January 23, 1984 - at comp:, <cmp ax,> is <cmp cx,>
  67. ;    =======================================================
  68.  
  69. ;    =======================================================
  70. ;    A collection of subroutines for two-byte arithmetic,
  71. ;    including loading and storage of the 8080 registers
  72. ;    from the pushdown list.
  73. ;    =======================================================
  74.  
  75. ;    -------------------------------------------------------
  76. ;    Load and store subroutines for 2-byte arithmetic.
  77. ;    -------------------------------------------------------
  78.  
  79. ;    Push a one-byte value onto the PDL.  The value to be
  80. ;    pushed should be placed on the 8080's stack in the
  81. ;    low byte position (say by using <push psw>) before
  82. ;    calling PUON.
  83.  
  84. PUON:    mov    cx,1        ;one byte is required
  85.     call    NARG        ;close old variable, reserve space
  86.     pop    bp        ;source was pushed before calling
  87.     pop    ax
  88.     mov    [bx],al        ;store byte, which is low order
  89.     inc    bx        ;pointer to next byte
  90.     mov    PY,bx        ;close new argument
  91.     jmp    bp
  92.  
  93. ;    Push a two-byte value onto the PDL.  The value to be
  94. ;    pushed should be placed on the 8080's stack before
  95. ;    calling PUTW.
  96.  
  97. PUTW:    mov    cx,2        ;two bytes are required
  98.     call    NARG        ;close old variable, reserve space
  99.     mov    bp,sp
  100.     mov    ax,2[bp]
  101.     mov    [bx],ax        ;store low order byte
  102.     inc    bx        ;on to high order destination
  103.     inc    bx        ;always leave pointer in good condition
  104.     mov    PY,bx        ;close top argument
  105.     ret    2
  106.  
  107. ;    (&) Exchange top two arguments, assumed two-byte.
  108.  
  109. EXCH:    mov    ax,PY
  110.     mov    bx,PX        ;org1
  111.     sub    ax,bx        ;siz1
  112.     mov    dx,-2[bx]    ;org2
  113.     lea    cx,-2[bx]
  114.     sub    cx,dx        ;siz2
  115.     cmp    ax,cx
  116.     jnz    XTNE
  117.     jcxz    XTRE
  118. XTEQ:    mov    al,[bx]
  119.     xchg    bx,dx
  120.     mov    ah,[bx]
  121.     mov    [bx],al
  122.     xchg    bx,dx
  123.     mov    [bx],ah
  124.     inc    bx
  125.     inc    dx
  126.     loop    XTEQ
  127. XTRE:    ret
  128. XTNE:    push    cx
  129.     push    dx
  130.     push    bx
  131.     push    ax
  132.     push    dx
  133.     call    NARG
  134.     mov    ax,ds
  135.     mov    es,ax
  136.     cld
  137.     mov    di,bx
  138.     pop    si
  139.     repnz    movs    byte [di],[si]
  140.     pop    cx
  141.     pop    si
  142.     pop    di
  143.     push    di
  144.     repnz    movs    byte [di],[si]
  145.     pop    word ptr [di]
  146.     lea    di,2[di]
  147.     mov    si,PX
  148.     mov    PX,di
  149.     pop    cx
  150.     repnz    movs    byte [di],[si]
  151.     mov    PY,di
  152.     ret
  153.  
  154. ;    Load top three arguments into (cx),(dx),(bx).  In
  155. ;    reality so many permutations exist for places to put
  156. ;    the arguments as they are taken off the REC stack that
  157. ;    they are simply transferred to the 8080 stack, to be
  158. ;    popped into the desired registers on return from the
  159. ;    corresponding call.  It is assumed that all quantities
  160. ;    involved in these transactions are of two bytes.  A
  161. ;    sequence of entry points is provided so as to pop off
  162. ;    one, two, or three arguments.
  163.  
  164. THRG:    mov    bx,PX        ;get pointer to top argument
  165. THRL:    pop    bp        ;enter here if (bx) already loaded
  166.     push    word ptr [bx]
  167.     push    bp
  168.     call    UCL        ;pop top argument, load (bx) from px
  169. TWOL:    pop    bp        ;continue, or entry for two args
  170.     push    word ptr [bx]
  171.     push    bp
  172.     call    UCL        ;pop argument, put px in (bx)
  173. ONEL:    pop    bp        ;continue, or entry for one argument
  174.     push    word ptr [bx]
  175.     push    bp
  176.     jmp    UCL        ;pop the last argument, quit
  177.  
  178. ;    Load up pointers to top two arguments.
  179.  
  180. ARGG:    mov    di,PX        ;org1
  181.     mov    cx,-2[di]
  182.     jcxz    arge        ;no second argument
  183.     mov    ax,PY
  184.     sub    ax,di        ;siz1
  185.     mov    bx,cx        ;org2
  186.     lea    dx,-2[di]
  187.     mov    cx,dx
  188.     sub    cx,bx        ;siz2
  189.     cmp    cx,ax
  190.     jnz    arge        ;arguments not same length
  191.     ret
  192. ARGE:    call    rer
  193. ARGS:    call    ARGG
  194.     mov    PX,bx
  195.     mov    PY,dx
  196.     ret
  197.  
  198. ;    -------------------------------------------------------
  199. ;    Two-byte arithmetic according to the four operations.
  200. ;    -------------------------------------------------------
  201.  
  202. ;    (+)  Add top registers on pdl: <a,b,+> leaves (a+b).
  203. ;    The sum is calculated modulo 2**16, no evidence of any
  204. ;    overflow remains behind.
  205.  
  206. SUM:    call    args
  207.     cmp    ax,01
  208.     jz    SUM1
  209.     cmp    ax,02
  210.     jz    SUM2
  211.     call    rer
  212. SUM1:    mov    al,[di]
  213.     or    [bx],al
  214.     ret
  215. SUM2:    mov    ax,[di]
  216.     add    word ptr [bx],ax
  217.     ret
  218.  
  219. ;    (-)  Subtract top from next: <a,b,-> leaves (a-b).
  220. ;    Reverse subtraction can be accomplished by exchanging
  221. ;    arguments: write <a,b,&,-> to get (b-a).  Subtraction
  222. ;    is carried out modulo 2**16; thus -1 = FFFF hex.
  223.  
  224. DIF:    call    args
  225.     cmp    ax,01
  226.     jz    DIF1
  227.     cmp    ax,02
  228.     jz    DIF2
  229.     call    rer
  230. DIF1:    mov    al,[di]
  231.     xor    [bx],al
  232.     ret
  233. DIF2:    mov    ax,[di]
  234.     sub    word ptr [bx],ax
  235.     ret
  236.  
  237. ;    (*)  Multiply top: <a,b,*> leaves (a*b).  The product
  238. ;    is for integer arithmetic, modulo 2**16, and so is not
  239. ;    directly suitable for a 32-bit product.
  240.  
  241. MPY:    call    args
  242.     cmp    ax,01
  243.     jz    MPY1
  244.     cmp    ax,02
  245.     jz    MPY2
  246.     call    rer
  247. MPY1:    mov    al,[di]
  248.     and    [bx],al
  249.     ret
  250. MPY2:    mov    ax,[di]
  251.     mul    word ptr [bx]
  252.     mov    [bx],ax
  253.     ret
  254.  
  255. ;    (/)  Divide top: <a,b,/> leaves rem(a/b), int(a/b).
  256. ;    Reverse division is possible by exchanging arguments;
  257. ;    thus <b,a,&,/> leaves rem(b/a), int(b/a).  If just
  258. ;    the remainder is required, write <a,b,/,L>, while if
  259. ;    only the quotient is desired, write <a,b,/,&,L>, and
  260. ;    finally, if the order of the remainder and quotient is
  261. ;    not satisfactory, they can be exchanged.  The division
  262. ;    is unsigned integer division.  It can also be used to
  263. ;    split a two-byte word into two parts through division
  264. ;    by the corresponding power of two.
  265.  
  266. DVD:    call    ARGG
  267.     cmp    word ptr [di],0000
  268.     jz    DER
  269.     mov    ax,[bx]
  270.     mov    dx,0000
  271.     div    word ptr [di]
  272.     mov    [di],ax
  273.     mov    [bx],dx
  274.     ret
  275. DER:    call    RER
  276.  
  277. ;    (~)  Complement or Negate the top of the pushdown list.
  278.  
  279. comp:    mov    bx,PX
  280.     mov    cx,PY
  281.     sub    cx,bx
  282.     cmp    cx,01
  283.     jz    com1
  284.     cmp    cx,02
  285.     jz    com2
  286.     call    rer
  287. com1:    not    byte ptr [bx]
  288.     ret
  289. com2:    neg    word ptr [bx]
  290.     ret
  291.  
  292. ;    (^)  Increment the top of the pushdown list.
  293.  
  294. INCR:    mov    bx,PX        ;pointer to argument
  295.     inc    word ptr [bx]
  296.     ret
  297.  
  298. ;    (d)  Decrement top of PDL if it is not zero; otherwise
  299. ;    FALSE, erasing the counter.  Equivalent to ((0=;1-)).
  300.  
  301. DECR:    mov    bx,PX        ;fetch pointer to argument
  302.     sub    word ptr [bx],1        ;dec won't work because of c flag
  303.     jb    DCF
  304.     jmp    SKP        ;no carry means TRUE
  305. DCF:    jmp    UCL        ;when FALSE, erase counter
  306.  
  307. ;    (N) Numerical comparison of top two elements on PDL. <a,b,N>
  308. ;    is TRUE if a .LE. b; both arguments are erased irrespective
  309. ;    of the result.  Numerical comparison is for integers; for one-
  310. ;    byte arguments the comparison is logical.
  311.  
  312. UCN:    call    args
  313.     cmp    ax,01
  314.     jz    UN1
  315.     cmp    ax,02
  316.     jz    UN2
  317.     call    rer
  318. UN1:    mov    al,[di]
  319.     test    al,[bx]
  320.     jz    UNF
  321.     jmp    UNT
  322. UN2:    mov    ax,[di]
  323.     cmp    ax,[bx]
  324.     jz    UNF
  325. UNT:    jmp    CUCL
  326. UNF:    jmp    UCL
  327.  
  328. ;    -------------------------------------------------------
  329. ;    Conversion between binary and hexadecimal ASCII strings
  330. ;    -------------------------------------------------------
  331.  
  332. ;    Return if not hexadecimal. A unchanged if not hex, else
  333. ;    reduced to binary.
  334.  
  335. RNH:    cmp    al,'G'        ;no hex characters beyond F
  336.     jnb    RH2
  337.     cmp    al,'A'        ;hex letters equal A or beyond
  338.     jb    RH1
  339.     sub    al,'7'        ;compensate the gap between 9 and A
  340.     ret
  341. RH1:    jmp    RND
  342. RH2:    inc    sp
  343.     inc    sp
  344.     ret
  345.  
  346. ;    Cummulation to convert a hex ASCII string to binary.
  347.  
  348. HXP:    add    bx,bx        ;shift left 4 bits
  349.     add    bx,bx        ;
  350.     add    bx,bx        ;
  351.     add    bx,bx        ;
  352.     or    bl,al        ;or in the nibble in the accumulator
  353.     ret
  354.  
  355. ;    (H) Convert a hex ASCII string on the PDL into binary.
  356. ;    Whatever the length of the argument, conversion will be
  357. ;    made to a two-byte binary number.  Thus, if more than
  358. ;    four hex digits are present, the result will be reduced
  359. ;    modulo 2**16.  It should be noted that the conversion
  360. ;    starts with the first byte of the argument and procedes
  361. ;    onward.
  362.  
  363. HE:    mov    cx,2        ;two bytes required for result
  364.     call    OARG        ;check if they are available
  365.     mov    bx,PY        ;fetch terminal address of string
  366.     mov    byte ptr [bx],(offset ZE)    ;zero signals its end
  367.     mov    dx,PX        ;fetch beginning of string
  368.     mov    bx,(offset ZE)        ;place zero in (bx) to prime conversion
  369. H1:    xchg    bx,dx
  370.     mov    al,[bx]
  371.     xchg    bx,dx        ;fetch ASCII character
  372.     inc    dx        ;ready for the next one
  373.     or    al,al        ;check the terminator byte
  374.     jz    H2        ;when end reached, close off argument
  375.  
  376.     call    RNH        ;if not hex digit, forget it all
  377.     call    HXP        ;otherwise times 16 plus new digit
  378.     jmp    H1        ;repeat the cycle
  379. H2:    xchg    bx,dx        ;binary number into (dx)
  380.     mov    bx,PX        ;place to store the result
  381.     mov    [bx],dx        ;store low byte
  382.     inc    bx        ;on to high byte
  383.     inc    bx        ;pointer must always be one ahead
  384.     mov    PY,bx        ;store terminal address
  385.     jmp    SKP        ;TRUE return from predicate
  386.  
  387. ;    ([exclm])  Convert a two-byte binary number into an ASCII
  388. ;    string.  A one-byte number will also be converted, but
  389. ;    into two nibbles rather than four, to serve in some
  390. ;    applications where the leading zeroes are not wanted.
  391.  
  392. HX:    mov    cx,PY
  393.     sub    cx,PX
  394.     cmp    cx,1        ;see if it's one byte
  395.     jnz    HS        ;if not, continue elsewhere
  396.  
  397. HN:    mov    cx,2        ;two nibble result for 1 byte
  398.     call    OARG        ;see that there's that much space
  399.     mov    bx,PX
  400.     mov    dl,[bx]        ;load low bit
  401.     jmp    HSI        ;
  402. HS:    mov    cx,4        ;four nibble result for 2 bytes
  403.     call    OARG        ;be sure there's space for it
  404.     mov    bx,PX        ;pointer to first byte
  405.     mov    dx,[bx]        ;load low byte
  406.     mov    al,dh        ;separate high byte first
  407.     call    HSA        ;write out left nibble
  408.     mov    al,dh        ;high byte again
  409.     call    HSB        ;write out right nibble
  410. HSI:    mov    al,dl        ;separate low byte
  411.     call    HSA        ;write out left nibble
  412.     mov    al,dl        ;low byte second trip
  413.     call    HSB        ;write out right nibble
  414.     mov    PY,bx        ;store end of argument
  415.     ret
  416.  
  417. HSA:    ror    al,1        ;shift byte right four bits
  418.     ror    al,1        ;
  419.     ror    al,1        ;
  420.     ror    al,1        ;
  421. HSB:    and    al,0FH        ;mask in right nibble
  422.     add    al,90H        ;prepare for some carries from <daa>
  423.     daa            ;create gap if nibble beyond 10
  424.     adc    al,40H        ;code for @ if we have a letter
  425.     daa            ;decide 3 for digit, 4 for letter
  426.     mov    [bx],al        ;record the ASCII digit
  427.     inc    bx        ;pointer ready for next deposit
  428.     ret
  429.  
  430. ;    -------------------------------------------------------
  431. ;    Fetch and store bytes, addresses, and blocks to and fro
  432. ;    between the PDL and the memory.  The following chart
  433. ;    shows the relation between all the different operators
  434. ;    which are available.
  435. ;
  436. ;                byte    word    block
  437. ;                ----    ----    -----
  438. ;
  439. ;    replace            -    r    G
  440. ;    fetch, nonincement    g    -    -
  441. ;    fetch, increment    u    y    -
  442. ;
  443. ;    store            -    -    S
  444. ;    store, increment    -    -    v
  445. ;    store w.r.t. limit    -    -    s
  446. ;    store into buffer    -    -    P
  447. ;
  448. ;    variable head cell    -    $    -
  449. ;
  450. ;    The main operators for saving and fetching variables
  451. ;    are G and S.  The remainder were especially chosen
  452. ;    on the one hand to scrutinize the memory under REC
  453. ;    control, and on the other to give the widest possible
  454. ;    latitude in defining variables in applications of REC.
  455. ;
  456. ;    The following chart shows how to employ variables:
  457. ;
  458. ;        'data' n$ S        define 2-byte variable
  459. ;         n$ r        fetch 2-byte variable
  460. ;        'data' ml n$ S    save fixed variable
  461. ;         n$ ryG        fetch fixed variable
  462. ;        'data' n$rs        redefine existing fixed var
  463. ;         kc Lml n$ S    create k-byte buffered variable
  464. ;         kc n$ S        alternative k-byte buffered var
  465. ;        'data' n$r P    redefine buffered variable
  466. ;         n$ ryLyG        fetch buffered variable
  467. ;
  468. ;    Memory can be examined bytewise with the following
  469. ;    combinations:
  470. ;
  471. ;        org g        fetch a byte, keep origin
  472. ;        org u        autoincrementing byte fetch
  473. ;        org v        autoincrementing byte store
  474. ;        org (g  ... v:;)    read, modify, store, ready next
  475. ;        o1 o2 (u~...v&:;)    move from o1 to o2
  476. ;
  477. ;    -------------------------------------------------------
  478.  
  479. ;    (g) (u)  Fetch a byte from memory and leave on PDL. The
  480. ;    sequence <org, g> leaves <org, (org)[1 byte]> on PDL.
  481. ;    The sequence <org, u> leaves <org+1, (org)[1 byte]> on
  482. ;    PDL.
  483.  
  484. GB:    mov    bx,PX        ;/g/ pointer to top argument
  485.     push    word ptr [bx]        ;fetch low byte of origin
  486.     jmp    GBJ        ;if the origin is not to be incremented
  487. GBI:    mov    bx,PX        ;/u/ pointer to arg, which is org
  488.     push    word ptr [bx]        ;fetch low byte of origin
  489.     inc    word ptr [bx]
  490. GBJ:    mov    cx,1        ;require space for one byte
  491.     call    NARG        ;close old arg, check space, open new
  492.     pop    dx        ;here's the origin we saved
  493.     xchg    bx,dx
  494.     mov    al,[bx]
  495.     xchg    bx,dx        ;fetch the byte there
  496.     mov    [bx],al        ;store on the PDL
  497.     inc    bx        ;pointer always ready for next byte
  498.     mov    PY,bx        ;right deliniter of argument
  499.     ret
  500.  
  501. ;    (y)  Fetch two bytes from memory and leave on PDL.
  502. ;    The sequence <org, y> leaves <org+2, (org)[2 bytes]>
  503. ;    on PDL.
  504.  
  505. GW:    mov    bx,PX        ;/ / pointer to the argument
  506.     push    word ptr [bx]        ;low byte of origin
  507.     jmp    GWJ        ;common continuation of gw, gwi
  508. GWI:    mov    bx,PX        ;/y/ pointer to the argument
  509.     push    word ptr [bx]        ;place low byte in A
  510.     add    word ptr [bx],2        ;origin to be incremented by 2
  511. GWJ:    mov    cx,2        ;require space for two bytes
  512.     call    NARG        ;close old arg, check space, open new
  513.     pop    dx        ;now we're ready for that origin
  514.     xchg    bx,dx
  515.     mov    ax,[bx]
  516.     xchg    bx,dx        ;fetch the byte sitting there
  517.     mov    [bx],ax        ;and store it on PDL
  518.     inc    bx
  519.     inc    bx        ;keep the pointer moving along
  520.     mov    PY,bx        ;value's finished, store its end
  521.     ret
  522.  
  523. ;    (G)  Fetch a block from memory, leave on PDL.
  524. ;    <org,siz, G> leaves (org, ...) on PDL.
  525.  
  526. GA:    call    CXLD        ;load siz into (cx)
  527.     call    OARG        ;reuse the argument, but with siz bytes
  528.     mov    bx,PX        ;fetch the destination address
  529.     mov    si,[bx]        ;but the source address is stored there
  530.     cld
  531.     mov    di,bx
  532.     mov    ax,ds
  533.     mov    es,ax
  534.     repnz    movs    byte [di],[si]
  535.     mov    PY,di        ;(bx) holds the destination terminator
  536.     ret
  537.  
  538. ;    (S)  Store a block forward from the designated memory
  539. ;    location.  <'data' org S> stores 'data' starting at
  540. ;    org; leaves no residue on the PDL.
  541.  
  542. SA:    call    CXLD        ;fetch destination origin
  543.     mov    di,cx        ;save it for a while
  544.     mov    si,PX
  545.     mov    cx,PY
  546.     sub    cx,si
  547.     cld
  548.     mov    ax,ds
  549.     mov    es,ax
  550.     repnz    movs    byte [di],[si]
  551.     jmp    UCL        ;pop the second argument too
  552.  
  553. ;    (v)  Store a block, leaving incremented address.
  554. ;    <org,'data' v> leaves org+size['data'] on PDL, stores
  555. ;    'data' starting from org.
  556.  
  557. SAI:    mov    si,PX
  558.     mov    cx,PY
  559.     sub    cx,si        ;determine length of data
  560.     call    UCL        ;pop top argument, exposing second
  561.     mov    di,[bx]        ;(bx) has px, which is destn address
  562.     mov    ax,ds
  563.     mov    es,ax
  564.     mov    ax,si
  565.     add    ax,cx
  566.     cmp    di,ax
  567.     jb    LVB
  568.     cld
  569.     repnz    movs    byte [di],[si]
  570.     mov    [bx],di
  571.     ret
  572. LVB:    std
  573.     add    si,cx
  574.     add    di,cx
  575.     mov    [bx],di
  576.     dec    si
  577.     dec    di
  578.     repnz    movs    byte [di],[si]
  579.     ret
  580.  
  581. ;    (s)  Store into an area of limited size. The sequence
  582. ;    <'data' org s> will store 'data' beginning at org+2,
  583. ;    supposing that siz('data') is less than or equal to
  584. ;    (org, org+1).  In either event no residue is left, but
  585. ;    an error notation is generated if the data doesn't fit.
  586. ;    No data at all is stored if all will not fit.  If it
  587. ;    matters to know how much of the space was used, the
  588. ;    operator P should probably be used instead.
  589.  
  590. LCS:    call    CXLD        ;fetch destination origin
  591.     mov    bx,cx        ;save it while calling psiz
  592.     mov    si,PX
  593.     mov    cx,PY
  594.     sub    cx,si        ;determine length of data
  595.     mov    ax,[bx]        ;low byte of capacity
  596.     cmp    ax,cx
  597.     jnb    LST
  598.     call    UCL
  599.     call    RER        ;note error, return if it won't fit
  600. LST:    cld
  601.     inc    bx
  602.     inc    bx
  603.     mov    di,bx
  604.     mov    ax,ds
  605.     mov    es,ax
  606.     repnz    movs    byte [di],[si]
  607.     jmp    UCL        ;pop second argument
  608.  
  609. ;    (P)  Store into a buffer and note length.  Used to
  610. ;    store data of variable length into an area whose
  611. ;    maximum length is fixed.  The buffer has the form
  612. ;
  613. ;       /available/used/data/data/.../data/.../end/
  614. ;
  615. ;    The sequence <'data' org P> will store the data
  616. ;    in the buffer beginning at org. (org, org+1) holds
  617. ;    the maximum length of data that may be stored in the
  618. ;    buffer, (org+2, org+3) is siz('data'), and 'data' is
  619. ;    stored from org+4 onward if it will fit.  If it will
  620. ;    not, P is a noop and error is set.
  621.  
  622. UCP:    call    CXLD        ;pointer to destination
  623.     mov    bx,cx        ;save destination while calling psiz
  624.     mov    si,PX
  625.     mov    cx,PY
  626.     sub    cx,si        ;load (cx) with length of data
  627.     inc    cx        ;data has to appear two bytes larger
  628.     inc    cx        ;to include cell showing its size
  629.     mov    ax,[bx]        ;low byte of destination capacity
  630.     inc    bx        ;
  631.     inc    bx        ;
  632.     cmp    ax,cx
  633.     jnb    UP1
  634.     call    RER        ;capacity exceeded: mark error, return
  635. UP1:    dec    cx        ;we want to store the true size
  636.     dec    cx        ;subtract out the two byte margin
  637.     mov    [bx],cx        ;low byte into usage cell
  638.     inc    bx        ;just keep moving along
  639.     inc    bx        ;ready to start moving data
  640.     cld
  641.     mov    di,bx
  642.     mov    ax,ds
  643.     mov    es,ax
  644.     repnz    movs    byte [di],[si]
  645.     jmp    UCL        ;lift second argument, leave nothing
  646.  
  647. ;    (r)  Replace address on top of pdl by its contents.
  648.  
  649. IND:    mov    bx,PX        ;pointer to top argument
  650.     mov    dx,[bx]        ;load low byte
  651.     xchg    bx,dx        ;(bx) now has top argument
  652.     mov    ax,[bx]        ;low byte of indirect address
  653.     xchg    bx,dx        ;address of top argument again
  654.     mov    [bx],ax        ;store low indirect byte
  655.     ret
  656.  
  657. ;    ($)  Generate the address of the nth cell in the array
  658. ;    of variables, which is a block of two-byte addresses.
  659. ;    These cells may be used to store data directly - for
  660. ;    example counters or addresses - or indirectly through
  661. ;    pointers to the actual location of the data.  By giving
  662. ;    a one-byte character argument, <'x'$>, the location where
  663. ;    the address of subroutine x is stored may be obtained.
  664.  
  665. VBLE:    mov    bx,PX        ;pointer to argument
  666.     mov    cx,PY
  667.     sub    cx,bx
  668.     cmp    cx,2
  669.     jz    VBLF
  670.     mov    cx,2
  671.     call    OARG
  672.     mov    bx,PX
  673.     mov    al,[bx]
  674.     mov    ah,0
  675.     jmp    VBLG
  676. VBLF:    mov    ax,[bx]
  677. VBLG:    add    ax,ax
  678.     add    ax,VRT
  679.     mov    [bx],ax
  680.     add    bx,2
  681.     mov    PY,bx
  682.     ret
  683.  
  684. ;    (l)  Load pz onto PDL.
  685.  
  686. LCL:    push    PZ        ;putw requires arg on 8080 stack
  687.     call    PUTW        ;record two-byte argument
  688.     ret            ;can't use simply <jmp putw>
  689.  
  690. ;    (m)  Set aside top argument on PDL.  It is moved to the
  691. ;    other end of the array reserved for the PDL, which can
  692. ;    be used as a temporary storage stack without name.  The
  693. ;    mechanism by which pz is moved and the block size is
  694. ;    recorded makes this an attractive mechanism to create
  695. ;    storage space for REC variables.
  696.  
  697. LCM:    mov    si,PY
  698.     mov    cx,si
  699.     sub    cx,PX        ;get length of top argument
  700.     push    cx
  701.     call    UCL        ;pop top argument
  702.     mov    di,PZ        ;load destination origin
  703.     std
  704.     dec    si
  705.     dec    di
  706.     mov    ax,ds
  707.     mov    es,ax
  708.     repnz    movs    byte [di],[si]
  709.     lea    bx,-1[di]
  710.     mov    PZ,bx
  711.     pop    word ptr [bx]        ;recover length
  712.     ret
  713.  
  714. ;    (n)  Recover segment which was set aside.
  715.  
  716. LCN:    mov    cx,(offset ZE)        ;there won't be any net length change
  717.     call    NARG        ;close old argument, ready for new
  718.     mov    di,bx        ;place destination origin in (dx)
  719.     mov    bx,PZ        ;place source origin in (bx)
  720.     mov    cx,[bx]        ;place length in cx
  721.     lea    si,2[bx]
  722.     cld
  723.     mov    ax,ds
  724.     mov    es,ax
  725.     repnz    movs    byte [di],[si]
  726.     mov    PY,di        ;end of destination is end of argument
  727.     mov    PZ,si        ;update pz
  728.     ret
  729.  
  730. ;    (|)  Concatinate the top arguments on the PDL.
  731.  
  732. CONC:    mov    si,PX
  733.     mov    cx,PY
  734.     sub    cx,si        ;get length of top argument
  735.     call    UCL        ;pop top argument, set up pntrs to next
  736.     mov    di,dx        ;new py is destination
  737.     cld
  738.     mov    ax,ds
  739.     mov    es,ax
  740.     repnz    movs    byte [di],[si]
  741.     mov    PY,di        ;record new terminal address
  742.     ret
  743.  
  744. ;    (%)  Restrict multiple-byte argument to one byte.
  745.  
  746. PE:    mov    ax,PX
  747.     cmp    ax,PY
  748.     jz    PE1        ;leave a null argument in peace
  749.     inc    ax        ;add one to it
  750.     mov    PY,ax        ;store as limit to the argument
  751. PE1:    ret
  752.  
  753. ;    (\)  Embed a single byte in a pair.
  754.  
  755. IP:    mov    cx,2        ;we want to have two bytes
  756.     call    OARG        ;verify that that much space remains
  757.     mov    bx,PX        ;pointer to argument
  758.     inc    bx        ;pass over first byte
  759.     mov    byte ptr [bx],(offset ZE)    ;make high byte zero
  760.     inc    bx        ;pass on to next byte
  761.     mov    PY,bx        ;record end of argument
  762.     ret
  763.  
  764. ;    (p)  Put px and siz on the pushdown list.
  765.  
  766. GXS:    mov    dx,PX
  767.     mov    bx,PY
  768.     mov    cx,bx
  769.     sub    cx,dx        ;calculate length of top argument
  770.     push    cx        ;put length on 8080 stack
  771.     push    dx        ;put origin on 8080 stack
  772.     call    PUTW        ;put top of 8080 stack on REC PDL
  773.     call    PUTW        ;put the next item there too
  774.     ret            ;can't combine <call, ret> into <jmp>
  775.  
  776. ;    (c) Reserve a block on the pushdown list. <n,c> creates
  777. ;    a block of length n, and puts n-2 at the front of the
  778. ;    block as a size indicator.  Then, if n .ge. 2, it will
  779. ;    be there as a length indicator for a buffer.   <=====maybe change this?
  780.  
  781. BLOK:    mov    bx,PX        ;pointer to argument
  782.     mov    cx,[bx]        ;fetch the argument
  783.     mov    [bx],cx        ;store header
  784.     sub    word ptr [bx],2
  785.     call    OARG        ;is there enough space to reuse arg?
  786.     mov    PY,bx        ;increment in (bx), it goes into py
  787.     push    PX        ;px has origin of block just formed
  788.     call    PUTW        ;record block origin as new argument
  789.     ret            ;can't replace <call putw, ret> by jmp
  790.  
  791. ;    Load a single variable into (cx) from the pushdown
  792. ;    list.  No register is sure to be preserved.
  793.  
  794. CXLD:    mov    bx,PX        ;pointer to argument
  795.     mov    cx,[bx]        ;fetch low order byte
  796.     jmp    UCL        ;erase argument [(cx) is unchanged]
  797.  
  798. ;    Load register pair (dx) from the pushdown list.
  799. ;    (cx) will be preserved, (bx) not.
  800.  
  801. DXLD:    mov    bx,PX        ;pointer to argument
  802.     push    word ptr [bx]        ;fetch word
  803.     call    UCL        ;erase argument
  804.     pop    dx        ;restore (dx) since UCL modified it
  805.     ret
  806.  
  807. ;    (=)  Test the two top arguments on the pushdown list
  808. ;    for equality.  The arguments may be of any length, but
  809. ;    will be equal only when of the same length and composed
  810. ;    of the same sequence of bytes. The top argument will be
  811. ;    popped whatever the outcome, but when equality is true
  812. ;    both will be popped.
  813.  
  814. EQL:    mov    di,PX        ;under argument
  815.     mov    cx,PY
  816.     sub    cx,di        ;obtain length of top argument
  817.     call    UCL        ;lift top argument
  818.     mov    si,PX
  819.     mov    bx,PY
  820.     sub    bx,si
  821.     cmp    bx,cx        ;compare lengths
  822.     jnz    EQF
  823.     cld
  824.     mov    ax,ds
  825.     mov    es,ax
  826.     repz    cmps    byte [di],[si]
  827.     jnz    EQF
  828.     jmp    CUCL        ;both agree, erase second arg, TRUE
  829. EQF:    ret            ;disagree so FALSE
  830.  
  831. ;    -------------------------------------------------------
  832. ;
  833. ;    Some of the service routines which are likely to be
  834. ;    external references in other modules are:
  835. ;
  836. ;        puon    push one byte on PDL
  837. ;        putw    push address on PDL
  838. ;        thrl    load  three arguments onto 8080 stack
  839. ;        twol    load two arguments onto 8080 stack
  840. ;        onel    load one argument onto 8080 stack
  841. ;        bcld    load (cx) from PDL, pop PDL
  842. ;        deld    load (dx) from PDL, pop PDL
  843. ;
  844. ;    -------------------------------------------------------
  845.  
  846.     END
  847.  
  848.