home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / MISC / REC.ZIP / PDL86.ASM < prev    next >
Encoding:
Assembly Source File  |  1986-02-04  |  27.4 KB  |  1,027 lines

  1. ;    =======================================================
  2. ;
  3. ;    REC module for the operators and predicates pertaining
  4. ;    to the pushdown list, other than the most important
  5. ;    ones already contained in the REC nucleus.  These
  6. ;    comprise:
  7. ;
  8. ;        arithmetic
  9. ;
  10. ;            +    sum modulo 2**16
  11. ;            -    difference modulo 2**16
  12. ;            *    product modulo 2**16
  13. ;            /    remainder, quotient
  14. ;            =    equality
  15. ;            ~    complement or negative
  16. ;            d    decrement, false on zero
  17. ;            ^    increment
  18. ;            N    comparison of top 2 args
  19. ;
  20. ;        modification of arguments
  21. ;
  22. ;            H    hex ASCII string to binary
  23. ;            [exclm] binary to hex ASCII string
  24. ;            %    restrict argument to one byte
  25. ;            \    embed argument in two bytes
  26. ;            &    exchange top arguments
  27. ;            |    concatinate top arguments
  28. ;
  29. ;        block movements
  30. ;
  31. ;            G    fetch a block from memory
  32. ;            g    address fetch
  33. ;            r    replace address by contents
  34. ;            u    incrementing byte fetch
  35. ;            y    incrementing word fetch
  36. ;            P    put buffer block in memory
  37. ;            S    store block in memory
  38. ;            s    store into buffer
  39. ;            v    incrementing byte store
  40. ;            m    move arg to end of PDL space
  41. ;            n    recover arg from end of PDL
  42. ;            h    store/restore machine state
  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, 1984
  53. ;    -------------------------------------------------------
  54. ;    8086 version with separate segments for code, PDL, WS.
  55. ;    -------------------------------------------------------
  56. ;
  57. ;        PDL86  -  Copyright (C) 1982, 1984
  58. ;        Universidad Autonoma de Puebla
  59. ;            All Rights Reserved
  60. ;
  61. ;        [Harold V. McIntosh, 25 April 1982]
  62. ;        [Gerardo Cisneros, 8 February 1984]
  63. ;
  64. ;    May 29, 1983 - & exchanges arbitrary arguments
  65. ;    May 29, 1983 - ~ discontinued; use m&n instead
  66. ;    May 29, 1983 - ~ Complement or Negate top element
  67. ;    May 29, 1983 - N for Numerical comparison on PDL
  68. ;    July 7, 1983 - $ with char arg gets subroutine addr
  69. ;    January 23, 1984 - at comp:, <cmp ax,> is <cmp cx,>
  70. ;    February 8, 1984 - separate segments (GCS)
  71. ;    May 9, 1984 - h stores/restores machine state (GCS)
  72. ;    May 24,1984 - H and exclm handle strings of arb. length (GCS)
  73. ;    May 31 1984 - PDL becomes 1st module of package -GCS
  74. ;    18 June 1984 - n quits on emtpy PDL complement - GCS
  75. ;    3 July 1984 - entry points for pG, $S, $r - GCS
  76. ;    15 Aug 1984 - E.P.s for ^^ and &S - GCS
  77. ;    29 June 1984 - $ changed for word-sized entries - GCS
  78. ;    8 Aug 1985 - Qm included - GCS
  79. ;    =======================================================
  80.  
  81.  
  82. ;    =======================================================
  83. ;    A collection of subroutines for two-byte arithmetic,
  84. ;    including loading and storage of the 8080 registers
  85. ;    from the pushdown list.
  86. ;    =======================================================
  87.  
  88. ;    -------------------------------------------------------
  89. ;    Load and store subroutines for 2-byte arithmetic.
  90. ;    -------------------------------------------------------
  91.  
  92. ;    Push a one-byte value onto the PDL.  The value to be
  93. ;    pushed should be placed on the 8080's stack in the
  94. ;    low byte position (say by using <push psw>) before
  95. ;    calling PUON.
  96.  
  97. PUON:    mov    cx,1        ;one byte is required
  98.     call    NARG        ;close old variable, reserve space
  99.     pop    bp        ;source was pushed before calling
  100.     pop    ax
  101.     mov    [bx],al     ;store byte, which is low order
  102.     inc    bx        ;pointer to next byte
  103.     mov    PY,bx        ;close new argument
  104.     jmp    bp
  105.  
  106. ;    Push a two-byte value onto the PDL.  The value to be
  107. ;    pushed should be placed on the 8080's stack before
  108. ;    calling PUTW.
  109.  
  110. PUTW:    mov    cx,2        ;two bytes are required
  111.     call    NARG        ;close old variable, reserve space
  112.     mov    bp,sp
  113.     mov    ax,2[bp]
  114.     mov    [bx],ax     ;store low order byte
  115.     inc    bx        ;on to high order destination
  116.     inc    bx        ;always leave pointer in good condition
  117.     mov    PY,bx        ;close top argument
  118.     ret    2
  119.  
  120. ;    (&) Exchange top two arguments
  121.  
  122. EXCH:    mov    ax,PY
  123.     mov    bx,PX        ;org1
  124.     sub    ax,bx        ;siz1
  125.     mov    dx,-2[bx]    ;org2
  126.     test    dx,dx
  127.     jz    XTER        ;PDL empty
  128.     lea    cx,-2[bx]
  129.     sub    cx,dx        ;siz2
  130.     jnz    xta
  131.     mov    bp,-4[bx]    ;check if only 1 arg
  132.     test    bp,bp
  133.     jnz    xta
  134. XTER:    call    RER
  135.  
  136. xta:    cmp    ax,cx
  137.     jnz    XTNE
  138.     jcxz    XTRE
  139. XTEQ:    mov    al,[bx]
  140.     xchg    bx,dx
  141.     mov    ah,[bx]
  142.     mov    [bx],al
  143.     xchg    bx,dx
  144.     mov    [bx],ah
  145.     inc    bx
  146.     inc    dx
  147.     loop    XTEQ
  148. XTRE:    ret
  149. XTNE:    push    cx
  150.     push    dx
  151.     push    bx
  152.     push    ax
  153.     push    dx
  154.     call    NARG
  155.     mov    ax,ds
  156.     mov    es,ax
  157.     cld
  158.     mov    di,bx
  159.     pop    si
  160.     repnz    movsb
  161.     pop    cx
  162.     pop    si
  163.     pop    di
  164.     push    di
  165.     repnz    movsb
  166.     pop    word ptr [di]
  167.     lea    di,2[di]
  168.     mov    si,PX
  169.     mov    PX,di
  170.     pop    cx
  171.     repnz    movsb
  172.     mov    PY,di
  173.     ret
  174.  
  175. ;    Load top three arguments into (cx),(dx),(bx).  In
  176. ;    reality so many permutations exist for places to put
  177. ;    the arguments as they are taken off the REC stack that
  178. ;    they are simply transferred to the 8080 stack, to be
  179. ;    popped into the desired registers on return from the
  180. ;    corresponding call.  It is assumed that all quantities
  181. ;    involved in these transactions are of two bytes.  A
  182. ;    sequence of entry points is provided so as to pop off
  183. ;    one, two, or three arguments.
  184.  
  185. THRG:    mov    bx,PX        ;get pointer to top argument
  186. THRL:    pop    bp        ;enter here if (bx) already loaded
  187.     push    word ptr [bx]
  188.     push    bp
  189.     call    UCL        ;pop top argument, load (bx) from px
  190. TWOL:    pop    bp        ;continue, or entry for two args
  191.     push    word ptr [bx]
  192.     push    bp
  193.     call    UCL        ;pop argument, put px in (bx)
  194. ONEL:    pop    bp        ;continue, or entry for one argument
  195.     push    word ptr [bx]
  196.     push    bp
  197.     jmp    UCL        ;pop the last argument, quit
  198.  
  199. ;    Load up pointers to top two arguments.
  200.  
  201. ARGG:    mov    di,PX        ;org1
  202.     mov    cx,-2[di]
  203.     jcxz    arge        ;no second argument
  204.     mov    ax,PY
  205.     sub    ax,di        ;siz1
  206.     mov    bx,cx        ;org2
  207.     lea    dx,-2[di]
  208.     mov    cx,dx
  209.     sub    cx,bx        ;siz2
  210.     cmp    cx,ax
  211.     jnz    arge        ;arguments not same length
  212.     ret
  213. ARGE:    call    rer
  214. ARGS:    call    ARGG
  215.     mov    PX,bx
  216.     mov    PY,dx
  217.     ret
  218.  
  219. ;    -------------------------------------------------------
  220. ;    Two-byte arithmetic according to the four operations.
  221. ;    -------------------------------------------------------
  222.  
  223. ;    (+)  Add top registers on pdl: <a,b,+> leaves (a+b).
  224. ;    The sum is calculated modulo 2**16, no evidence of any
  225. ;    overflow remains behind.
  226.  
  227. SUM:    call    args
  228.     cmp    ax,01
  229.     jz    SUM1
  230.     cmp    ax,02
  231.     jz    SUM2
  232.     call    rer
  233. SUM1:    mov    al,[di]
  234.     or    [bx],al
  235.     ret
  236. SUM2:    mov    ax,[di]
  237.     add    word ptr [bx],ax
  238.     ret
  239.  
  240. ;    (-)  Subtract top from next: <a,b,-> leaves (a-b).
  241. ;    Reverse subtraction can be accomplished by exchanging
  242. ;    arguments: write <a,b,&,-> to get (b-a).  Subtraction
  243. ;    is carried out modulo 2**16; thus -1 = FFFF hex.
  244.  
  245. DIF:    call    args
  246.     cmp    ax,01
  247.     jz    DIF1
  248.     cmp    ax,02
  249.     jz    DIF2
  250.     call    rer
  251. DIF1:    mov    al,[di]
  252.     xor    [bx],al
  253.     ret
  254. DIF2:    mov    ax,[di]
  255.     sub    word ptr [bx],ax
  256.     ret
  257.  
  258. ;    (*)  Multiply top: <a,b,*> leaves (a*b).  The product
  259. ;    is for integer arithmetic, modulo 2**16, and so is not
  260. ;    directly suitable for a 32-bit product.
  261.  
  262. MPY:    call    args
  263.     cmp    ax,01
  264.     jz    MPY1
  265.     cmp    ax,02
  266.     jz    MPY2
  267.     call    rer
  268. MPY1:    mov    al,[di]
  269.     and    [bx],al
  270.     ret
  271. MPY2:    mov    ax,[di]
  272.     mul    word ptr [bx]
  273.     mov    [bx],ax
  274.     ret
  275.  
  276. ;    (/)  Divide top: <a,b,/> leaves rem(a/b), int(a/b).
  277. ;    Reverse division is possible by exchanging arguments;
  278. ;    thus <b,a,&,/> leaves rem(b/a), int(b/a).  If just
  279. ;    the remainder is required, write <a,b,/,L>, while if
  280. ;    only the quotient is desired, write <a,b,/,&,L>, and
  281. ;    finally, if the order of the remainder and quotient is
  282. ;    not satisfactory, they can be exchanged.  The division
  283. ;    is unsigned integer division.  It can also be used to
  284. ;    split a two-byte word into two parts through division
  285. ;    by the corresponding power of two.
  286.  
  287. DVD:    call    ARGG
  288.     cmp    word ptr [di],0000
  289.     jz    DER
  290.     mov    ax,[bx]
  291.     mov    dx,0000
  292.     div    word ptr [di]
  293.     mov    [di],ax
  294.     mov    [bx],dx
  295.     ret
  296. DER:    call    RER
  297.  
  298. ;    (~)  Complement or Negate the top of the pushdown list.
  299.  
  300. comp:    mov    bx,PX
  301.     mov    cx,PY
  302.     sub    cx,bx
  303.     cmp    cx,01
  304.     jz    com1
  305.     cmp    cx,02
  306.     jz    com2
  307.     call    rer
  308. com1:    not    byte ptr [bx]
  309.     ret
  310. com2:    neg    word ptr [bx]
  311.     ret
  312.  
  313. ;    (^)  Increment the top of the pushdown list.
  314.  
  315. INTW:    call    INCR        ;Entry point for ^^
  316.  
  317. INCR:    mov    bx,PX        ;pointer to argument
  318.     inc    word ptr [bx]
  319.     ret
  320.  
  321. ;    (d)  Decrement top of PDL if it is not zero; otherwise
  322. ;    FALSE, erasing the counter.  Equivalent to ((0=;1-)).
  323.  
  324. DECR:    mov    bx,PX        ;fetch pointer to argument
  325.     sub    word ptr [bx],1     ;dec won't work because of c flag
  326.     jb    DCF
  327.     jmp    SKP        ;no carry means TRUE
  328. DCF:    jmp    UCL        ;when FALSE, erase counter
  329.  
  330. ;    (N) Numerical comparison of top two elements on PDL. <a,b,N>
  331. ;    is TRUE if a .LE. b; both arguments are erased irrespective
  332. ;    of the result.  Numerical comparison is for integers; for one-
  333. ;    byte arguments the comparison is logical.
  334.  
  335. UCN:    call    args
  336.     cmp    ax,01
  337.     jz    UN1
  338.     cmp    ax,02
  339.     jz    UN2
  340.     call    rer
  341. UN1:    mov    al,[di]
  342.     test    al,[bx]
  343.     jz    UNF
  344.     jmp    UNT
  345. UN2:    mov    ax,[di]
  346.     cmp    ax,[bx]
  347.     jc    UNF
  348. UNT:    jmp    CUCL
  349. UNF:    jmp    UCL
  350.  
  351. ;    -------------------------------------------------------
  352. ;    Conversion between binary and hexadecimal ASCII strings
  353. ;    -------------------------------------------------------
  354.  
  355. ;    Return if not hexadecimal. A unchanged if not hex, else
  356. ;    reduced to binary.
  357.  
  358. RNH:    cmp    al,'G'        ;no hex characters beyond F
  359.     jnb    RH2
  360.     cmp    al,'A'        ;hex letters equal A or beyond
  361.     jb    RH1
  362.     sub    al,'7'        ;compensate the gap between 9 and A
  363.     ret
  364. RH1:    jmp    RND
  365. RH2:    inc    sp
  366.     inc    sp
  367.     ret
  368.  
  369. ;    (H) Convert a hex ASCII string on the PDL into binary.
  370. ;    If the length of the string is n, the result will have
  371. ;    int((n+1)/2) bytes, stored in Intel form: the least
  372. ;    significant byte in the lowest addressed location.
  373.  
  374. HE:    mov    cx,PY        ;compute length of string
  375.     mov    si,PX        ;while saving pointers
  376.     sub    cx,si
  377.     jz    H4        ;leave null strings alone
  378.     mov    bp,cx        ;save byte count
  379.     cld
  380. H0:    lodsb            ;ck without all characters
  381.     call    RNH        ;are hex digits, return false if not
  382.     loop    H0
  383.     mov    cx,bp
  384.     inc    cx        ;compute length of final number
  385.     shr    cx,1
  386.     mov    si,PX        ;reload PX to process the hex string
  387.     mov    bp,si        ;copy of PX to be used later
  388.     mov    bx,si        ;another copy as pointer for result
  389.     mov    di,cx        ;copy of byte count to be used later
  390.     mov    dl,0
  391.     jnc    H2        ;start in the middle if length odd
  392. H1:    lodsb
  393.     call    RNH        ;reduce to binary
  394.     shl    al,1        ;multiply by 16
  395.     shl    al,1
  396.     shl    al,1
  397.     shl    al,1
  398.     mov    dl,al        ;save it while getting next nibble
  399. H2:    lodsb
  400.     call    RNH
  401.     or    al,dl        ;put nibbles together
  402.     mov    [bx],al        ;and store on PDL
  403.     inc    bx        ;keep pointing to the next byte
  404.     loop    H1
  405.     mov    PY,bx        ;update PY
  406.     call    HXC        ;turn the string around
  407. H4:    jmp    SKP        ;return TRUE
  408.  
  409. ;    Turn around string of (DI) bytes starting at (BP) and
  410. ;    ending at (BX)-1
  411.  
  412. HXC:    dec    bx        ;highest byte
  413.     mov    cx,di        ;total byte count
  414.     shr    cx,1        ;half of number of bytes
  415.     jcxz    H5
  416. H3:    mov    al,[bx]
  417.     xchg    ds:[bp],al
  418.     mov    [bx],al
  419.     dec    bx
  420.     inc    bp
  421.     loop    H3
  422. H5:    ret
  423.  
  424. ;    ([exclm])  Convert an n-byte binary number into an ASCII
  425. ;    string of length 2n.  The high order byte is assumed to be
  426. ;    in the highest-addressed location.
  427.  
  428. HX:    mov    cx,PY        ;compute lenght of number
  429.     mov    si,cx
  430.     mov    bp,PX        ;while saving pointers in other
  431.     sub    cx,bp        ;registers
  432.     jnz    HX0
  433.     ret            ;leave null strings alone
  434. HX0:    mov    di,cx
  435.     shl    cx,1        ;twice as many bytes will be made
  436.     call    OARG        ;verify availability of space
  437.     mov    PY,bx        ;update PY right away
  438.     mov    cx,di        ;restore old count
  439.     std            ;conversion proceeds backwards
  440.     dec    si        ;last byte is one below original PY
  441. HX1:    lodsb            ;get the byte
  442.     mov    ah,al        ;make a copy
  443.     call    HSA        ;produce digit from high nibble
  444.     mov    al,ah        ;back to AL
  445.     call    HSB        ;produce digit from low nibble
  446.     loop    HX1
  447.     shl    di,1        ;prepare to turn string around
  448.     mov    bx,PY
  449.     jmp    short HXC
  450.  
  451. HSA:    ror    al,1        ;shift byte right four bits
  452.     ror    al,1        ;
  453.     ror    al,1        ;
  454.     ror    al,1        ;
  455. HSB:    and    al,0FH        ;mask in right nibble
  456.     add    al,90H        ;prepare for some carries from <daa>
  457.     daa            ;create gap if nibble beyond 10
  458.     adc    al,40H        ;code for @ if we have a letter
  459.     daa            ;decide 3 for digit, 4 for letter
  460.     dec    bx        ;get pointer ready for deposit
  461.     mov    [bx],al        ;record the ASCII digit
  462.     ret
  463.  
  464. ;    -------------------------------------------------------
  465. ;    Fetch and store bytes, addresses, and blocks to and fro
  466. ;    between the PDL and the memory.  The following chart
  467. ;    shows the relation between all the different operators
  468. ;    which are available.
  469. ;
  470. ;                byte    word    block
  471. ;                ----    ----    -----
  472. ;
  473. ;    replace         -    r    G
  474. ;    fetch, nonincrement    g    -    -
  475. ;    fetch, increment    u    y    -
  476. ;
  477. ;    store            -    -    S
  478. ;    store, increment    -    -    v
  479. ;    store w.r.t. limit    -    -    s
  480. ;    store into buffer    -    -    P
  481. ;
  482. ;    variable head cell    -    $    -
  483. ;
  484. ;    The main operators for saving and fetching variables
  485. ;    are G and S.  The remainder were especially chosen
  486. ;    on the one hand to scrutinize the memory under REC
  487. ;    control, and on the other to give the widest possible
  488. ;    latitude in defining variables in applications of REC.
  489. ;
  490. ;    The following chart shows how to employ variables:
  491. ;
  492. ;        'data' [var#] $ S    define 2-byte variable
  493. ;        [var#] $ r        fetch 2-byte variable
  494. ;        'data' ml [var#] $ S    save fixed variable
  495. ;        [var#] $ ryG        fetch fixed variable
  496. ;        'data' [var#] $rs    redefine existing fixed var
  497. ;        kc Lml [var#] $ S    create k-byte buffered variable
  498. ;        kc [var#] $ S        alternative k-byte buffered var
  499. ;        'data' [var#] $r P    redefine buffered variable
  500. ;        [var#] $ ryLyG        fetch buffered variable
  501. ;
  502. ;    Memory can be examined bytewise with the following
  503. ;    combinations:
  504. ;
  505. ;        org g        fetch a byte, keep origin
  506. ;        org u        autoincrementing byte fetch
  507. ;        org v        autoincrementing byte store
  508. ;        org (g  ... v:;)    read, modify, store, ready next
  509. ;        o1 o2 (u~...v&:;)    move from o1 to o2
  510. ;
  511. ;    -------------------------------------------------------
  512.  
  513. ;    (g) (u)  Fetch a byte from memory and leave on PDL. The
  514. ;    sequence <org, g> leaves <org, (org)[1 byte]> on PDL.
  515. ;    The sequence <org, u> leaves <org+1, (org)[1 byte]> on
  516. ;    PDL.
  517.  
  518. GB:    mov    bx,PX        ;/g/ pointer to top argument
  519.     push    word ptr [bx]    ;fetch low byte of origin
  520.     jmp    GBJ        ;if the origin is not to be incremented
  521. GBI:    mov    bx,PX        ;/u/ pointer to arg, which is org
  522.     push    word ptr [bx]    ;fetch low byte of origin
  523.     inc    word ptr [bx]
  524. GBJ:    call    ESLD        ;get segment address
  525.     mov    cx,1        ;require space for one byte
  526.     call    NARG        ;close old arg, check space, open new
  527.     pop    dx        ;here's the origin we saved
  528.     xchg    bx,dx
  529.     mov    al,es:[bx]
  530.     xchg    bx,dx        ;fetch the byte there
  531.     mov    [bx],al        ;store on the PDL
  532.     inc    bx        ;pointer always ready for next byte
  533.     mov    PY,bx        ;right deliniter of argument
  534.     ret
  535.  
  536. ;    (y)  Fetch two bytes from memory and leave on PDL.
  537. ;    The sequence <org, y> leaves <org+2, (org)[2 bytes]>
  538. ;    on PDL.
  539.  
  540. GW:    mov    bx,PX        ;/ / pointer to the argument
  541.     push    word ptr [bx]        ;low byte of origin
  542.     jmp    GWJ        ;common continuation of gw, gwi
  543. GWI:    mov    bx,PX        ;/y/ pointer to the argument
  544.     push    word ptr [bx]        ;place low byte in A
  545.     add    word ptr [bx],2     ;origin to be incremented by 2
  546. GWJ:    call    ESLD        ;get segment address
  547.     mov    cx,2        ;require space for two bytes
  548.     call    NARG        ;close old arg, check space, open new
  549.     pop    dx        ;now we're ready for that origin
  550.     xchg    bx,dx
  551.     mov    ax,es:[bx]
  552.     xchg    bx,dx        ;fetch the byte sitting there
  553.     mov    [bx],ax        ;and store it on PDL
  554.     inc    bx
  555.     inc    bx        ;keep the pointer moving along
  556.     mov    PY,bx        ;value's finished, store its end
  557.     ret
  558.  
  559. ;    (G)  Fetch a block from memory, leave on PDL.
  560. ;    <org,siz, G> leaves (org, ...) on PDL.
  561.  
  562. GA:    call    CXLD        ;load siz into (cx)
  563.     call    OARG        ;reuse the argument, but with siz bytes
  564.     call    ESLD        ;get segment base
  565.     mov    si,[bx]        ;pick up source address too
  566.     cld
  567.     mov    di,bx
  568.     mov    bp,ds
  569.     mov    ax,es
  570.     mov    ds,ax
  571.     mov    es,bp
  572.     repnz    movsb
  573.     mov    ds,bp        ;restore data segment
  574.     mov    PY,di        ;(bx) holds the destination terminator
  575.     ret
  576.  
  577. ;    (pG)  Make a copy of the last argument, a combination
  578. ;    which is compiled as a single operator
  579.  
  580. DUPP:    mov    cx,PY    ;compute size of current top
  581.     sub    cx,PX
  582.     call    NARG    ;check availability of space
  583.     mov    ax,ds
  584.     mov    es,ax    ;prepare for move
  585.     cld
  586.     mov    si,dx    ;old PX is in DX
  587.     mov    di,bx    ;new PX is in BX
  588.     repnz    movsb    ;count still in  CX
  589.     mov    PY,di    ;note new argument end
  590.     ret
  591.  
  592. ;    (S)  Store a block forward from the designated memory
  593. ;    location.  <'data' org S> stores 'data' starting at
  594. ;    org; leaves no residue on the PDL.
  595.  
  596. XSTO:    call    EXCH        ;entry point for &S
  597.     jmp    short SA
  598.  
  599. VSTO:    call    VBLE        ;entry pt for combination $S
  600.  
  601. SA:    call    CXLD        ;fetch destination origin
  602.     mov    di,cx        ;save it for a while
  603.     mov    si,PX
  604.     mov    cx,PY
  605.     sub    cx,si
  606.     cld
  607.     repnz    movsb
  608.     jmp    UCL        ;pop the second argument too
  609.  
  610. ;    (v)  Store a block, leaving incremented address.
  611. ;    <org,'data' v> leaves org+size['data'] on PDL, stores
  612. ;    'data' starting from org.
  613.  
  614. SAI:    mov    si,PX
  615.     mov    cx,PY
  616.     sub    cx,si        ;determine length of data
  617.     call    UCL        ;pop top argument, exposing second
  618.     call    ESLD        ;get segment address
  619.     mov    di,[bx]     ;(bx) has px, which is destn address
  620.     mov    ax,si
  621.     add    ax,cx
  622.     cmp    di,ax
  623.     jb    LVB
  624.     cld
  625.     repnz    movsb
  626.     mov    [bx],di
  627.     ret
  628. LVB:    std
  629.     add    si,cx
  630.     add    di,cx
  631.     mov    [bx],di
  632.     dec    si
  633.     dec    di
  634.     repnz    movsb
  635.     ret
  636.  
  637. ;    (s)  Store into an area of limited size. The sequence
  638. ;    <'data' org s> will store 'data' beginning at org+2,
  639. ;    supposing that siz('data') is less than or equal to
  640. ;    (org, org+1).  In either event no residue is left, but
  641. ;    an error notation is generated if the data doesn't fit.
  642. ;    No data at all is stored if all will not fit.  If it
  643. ;    matters to know how much of the space was used, the
  644. ;    operator P should probably be used instead.
  645.  
  646. LCS:    call    CXLD        ;fetch destination origin
  647.     mov    bx,cx        ;save it while calling psiz
  648.     mov    si,PX
  649.     mov    cx,PY
  650.     sub    cx,si        ;determine length of data
  651.     mov    ax,es:[bx]        ;low byte of capacity
  652.     cmp    ax,cx
  653.     jnb    LST
  654.     call    UCL
  655.     call    RER        ;note error, return if it won't fit
  656. LST:    cld
  657.     inc    bx
  658.     inc    bx
  659.     mov    di,bx
  660.     repnz    movsb
  661.     jmp    UCL        ;pop second argument
  662.  
  663. ;    (P)  Store into a buffer and note length.  Used to
  664. ;    store data of variable length into an area whose
  665. ;    maximum length is fixed.  The buffer has the form
  666. ;
  667. ;      /available/used/data/data/.../data/.../end/
  668. ;
  669. ;    The sequence <'data' org P> will store the data
  670. ;    in the buffer beginning at org. (org, org+1) holds
  671. ;    the maximum length of data that may be stored in the
  672. ;    buffer, (org+2, org+3) is siz('data'), and 'data' is
  673. ;    stored from org+4 onward if it will fit.  If it will
  674. ;    not, P is a noop and error is set.
  675.  
  676. UCP:    call    CXLD        ;pointer to destination
  677.     mov    bx,cx        ;save destination while calling psiz
  678.     mov    si,PX
  679.     mov    cx,PY
  680.     sub    cx,si        ;load (cx) with length of data
  681.     inc    cx        ;data has to appear two bytes larger
  682.     inc    cx        ;to include cell showing its size
  683.     mov    ax,es:[bx]        ;low byte of destination capacity
  684.     inc    bx        ;
  685.     inc    bx        ;
  686.     cmp    ax,cx
  687.     jnb    UP1
  688.     call    RER        ;capacity exceeded: mark error, return
  689. UP1:    dec    cx        ;we want to store the true size
  690.     dec    cx        ;subtract out the two byte margin
  691.     mov    es:[bx],cx        ;low byte into usage cell
  692.     inc    bx        ;just keep moving along
  693.     inc    bx        ;ready to start moving data
  694.     cld
  695.     mov    di,bx
  696.     mov    ax,ds
  697.     mov    es,ax
  698.     repnz    movsb
  699.     jmp    UCL        ;lift second argument, leave nothing
  700.  
  701. ;    (r)  Replace address on top of pdl by its contents.
  702.  
  703. VREP:    call    VBLE        ;entry pt for combination $r
  704. IND:    call    ESLD        ;get segment address
  705.     mov    dx,[bx]     ;load word
  706.     xchg    bx,dx        ;(bx) now has top argument
  707.     mov    ax,es:[bx]        ;indirect address
  708.     xchg    bx,dx        ;address of top argument again
  709.     mov    [bx],ax     ;store low indirect byte
  710.     add    bx,2        ;set PY in case old arg had 4 bytes
  711.     mov    PY,bx
  712.     ret
  713.  
  714. ;    ($)  Generate the address of the nth cell in the array
  715. ;    of variables, which is a block of four-byte addresses.
  716. ;    These cells may be used to store data directly - for
  717. ;    example counters or addresses - or indirectly through
  718. ;    pointers to the actual location of the data.  By giving
  719. ;    a one-byte character argument, <'x'$>, the location where
  720. ;    the address of subroutine x is stored may be obtained.
  721.  
  722. VBLE:    mov    bx,PX        ;pointer to argument
  723.     mov    cx,PY
  724.     sub    cx,bx
  725.     cmp    cx,2
  726.     jz    VBLF
  727.     mov    cx,2
  728.     call    OARG        ;reuse old arg with size 2
  729.     mov    bx,PX
  730.     mov    al,[bx]
  731.     mov    ah,0
  732.     jmp    VBLG
  733. VBLF:    mov    ax,[bx]
  734. VBLG:    add    ax,ax
  735. ;    add    ax,ax        ;word-size entries
  736.     add    ax,VRT
  737.     mov    [bx],ax
  738.     add    bx,2
  739.     mov    PY,bx
  740.     ret
  741.  
  742. ;    (l)  Load pz onto PDL.
  743.  
  744. LCL:    push    PZ        ;putw requires arg on 8080 stack
  745.     call    PUTW        ;record two-byte argument
  746.     ret            ;can't use simply <jmp putw>
  747.  
  748. ;    (m)  Set aside top argument on PDL.  It is moved to the
  749. ;    other end of the array reserved for the PDL, which can
  750. ;    be used as a temporary storage stack without name.  The
  751. ;    mechanism by which pz is moved and the block size is
  752. ;    recorded makes this an attractive mechanism to create
  753. ;    storage space for REC variables.
  754.  
  755. LCM:    mov    si,PY
  756.     mov    cx,si
  757.     sub    cx,PX        ;get length of top argument
  758.     mov    ax,ds        ;source in data seg
  759. lcm1:    push    cx
  760.     call    UCL        ;pop top argument
  761.     mov    di,PZ        ;load destination origin
  762.     std
  763.     dec    si
  764.     dec    di
  765.     mov    bp,ds        ;save ds
  766.     mov    ds,ax        ;set source
  767.     mov    es,bp        ;set destination
  768.     repnz    movsb
  769.     mov    ds,bp        ;reset ds
  770.     lea    bx,-1[di]
  771.     mov    PZ,bx
  772.     pop    word ptr [bx]        ;recover length
  773.     ret
  774.  
  775. ;    (Qm)  Copy WS to PDL complement.  A combination 
  776. ;    essential to Convert's variable binding mechanism.
  777.  
  778. QUEM:    mov    si,P2        ;source is top of segment
  779.     mov    cx,si        ;compute the string's length
  780.     sub    cx,P1
  781.     call    NARG        ;check PDL space
  782.     mov    ax,WSEG        ;source segment in ax
  783.     jmp    short lcm1    ;use code at m
  784.  
  785. ;    (n)  Recover segment which was set aside.
  786.  
  787. LCN:    mov    cx,0        ;there won't be any net length change
  788.     call    NARG        ;close old argument, ready for new
  789.     mov    di,bx        ;place destination origin in (dx)
  790.     mov    PY,di        ;leave null string in case of error
  791.     mov    bx,PZ        ;place source origin in (bx)
  792.     mov    cx,[bx]     ;place length in cx
  793.     cmp    cx,0FFFFH    ;check for top of PDL flag
  794.     jnz    lcn1
  795.     call    RER        ;quit if PDL complement empty
  796. lcn1:    lea    si,2[bx]
  797.     cld
  798.     mov    ax,ds
  799.     mov    es,ax
  800.     repnz    movsb
  801.     mov    PY,di        ;end of destination is end of argument
  802.     mov    PZ,si        ;update pz
  803.     ret
  804.  
  805. ;    (nL)  Lift from PDL complement
  806.  
  807. ENLF:    mov    bx,PZ        ;place source origin in (bx)
  808.     mov    cx,[bx]     ;place length in cx
  809.     cmp    cx,0FFFFH    ;check for top of PDL flag
  810.     jnz    enlf1
  811.     call    RER        ;quit if PDL complement empty
  812. enlf1:  add    bx,cx        ;compute start of next compl. arg.
  813.     inc    bx
  814.     inc    bx
  815.     mov    PZ,bx        ;store new upper limit of PDL
  816.     ret
  817.  
  818. ;    (|)  Concatinate the top arguments on the PDL.
  819.  
  820. CONC:    mov    si,PX
  821.     mov    cx,PY
  822.     sub    cx,si        ;get length of top argument
  823.     call    UCL        ;pop top argument, set up pntrs to next
  824.     mov    di,dx        ;new py is destination
  825.     cld
  826.     mov    ax,ds
  827.     mov    es,ax
  828.     repnz    movsb
  829.     mov    PY,di        ;record new terminal address
  830.     ret
  831.  
  832. ;    (%)  Restrict multiple-byte argument to one byte.
  833.  
  834. PE:    mov    ax,PX
  835.     cmp    ax,PY
  836.     jz    PE1        ;leave a null argument in peace
  837.     inc    ax        ;add one to it
  838.     mov    PY,ax        ;store as limit to the argument
  839. PE1:    ret
  840.  
  841. ;    (\)  Embed a single byte in a pair.
  842.  
  843. IP:    mov    cx,2        ;we want to have two bytes
  844.     call    OARG        ;verify that that much space remains
  845.     mov    bx,PX        ;pointer to argument
  846.     inc    bx        ;pass over first byte
  847.     mov    byte ptr [bx],0    ;make high byte zero
  848.     inc    bx        ;pass on to next byte
  849.     mov    PY,bx        ;record end of argument
  850.     ret
  851.  
  852. ;    (p)  Put px and siz on the pushdown list.
  853.  
  854. GXS:    mov    dx,PX
  855.     mov    bx,PY
  856.     mov    cx,bx
  857.     sub    cx,dx        ;calculate length of top argument
  858.     push    cx        ;put length on 8080 stack
  859.     push    dx        ;put origin on 8080 stack
  860.     call    PUTW        ;put top of 8080 stack on REC PDL
  861.     call    PUTW        ;put the next item there too
  862.     ret            ;can't combine <call, ret> into <jmp>
  863.  
  864. ;    (c) Reserve a block on the pushdown list. <n,c> creates
  865. ;    a block of length n, and puts n-2 at the front of the
  866. ;    block as a size indicator.  Then, if n .ge. 2, it will
  867. ;    be there as a length indicator for a buffer. <=====maybe change this?
  868.  
  869. BLOK:    mov    bx,PX        ;pointer to argument
  870.     mov    cx,[bx]     ;fetch the argument
  871.     mov    [bx],cx     ;store header
  872.     sub    word ptr [bx],2
  873.     call    OARG        ;is there enough space to reuse arg?
  874.     mov    PY,bx        ;increment in (bx), it goes into py
  875.     push    PX        ;px has origin of block just formed
  876.     call    PUTW        ;record block origin as new argument
  877.     ret            ;can't replace <call putw, ret> by jmp
  878.  
  879. ;    (h) Save the state of the machine and leave the SP value on
  880. ;    the PDL, if arg is null; otherwise restore the state of the
  881. ;    machine from values at the stack pointed to by the address
  882. ;    on the PDL.
  883.  
  884. MST:    pop    rtaddr      ;put return address aside
  885.     pushf            ;Flags to the stack
  886.     push    ax        ;Accumulator to stack
  887.     push    bx        ;BX to stack
  888.     push    cx        ;CX to stack
  889.     push    dx        ;DX to stack
  890.     push    bp        ;Base pointer to stack
  891.     push    si        ;Source index to stack
  892.     push    di        ;Destination index to stack
  893.     push    ds        ;Data segment to stack
  894.     push    es        ;Extra segment to stack
  895.     mov    ax,PY        ;compute argument length
  896.     sub    ax,PX        ;and lift top
  897.     call    UCL        ;before pushing pointers
  898. ;    push    PX        ;PDL pointers
  899. ;    push    PY
  900. ;    push    PZ
  901. ;    push    P0        ;WS pointers
  902. ;    push    P1
  903. ;    push    P2
  904. ;    push    P3
  905. ;    push    P4
  906. ;    push    WSEG
  907.     test    ax,ax
  908.     jnz    mrst        ;restore if arg nonnull
  909.     mov    cx,4        ;need 4 bytes for SS and SP
  910.     call    NARG        ;A ROYAL MESS WILL ENSUE IF NARG FAILS
  911.     mov    [bx],ss     ;record stack segment value
  912.     mov    2[bx],sp    ;record stack pointer value
  913.     add    bx,cx        ;compute new PY
  914.     mov    PY,bx        ;and update it
  915.     jmp    short mrst2
  916.  
  917. mrst:    xchg    bx,dx        ;get previous PY back in BX
  918.     cmp    al,2        ;see if top arg has size 2
  919.     jnz    mrst1
  920.     call    UCL        ;yes, clean up stack
  921.     xchg    dx,bx        ;get PY back into BX
  922.     mov    ss,2[bx]    ;restoring pointers should be below
  923.     mov    sp,4[bx]    ;the 2-byte argument
  924.     add    sp,20        ;get rid of stored stuff (10 regs)
  925. ;    add    sp,38        ;get rid of stored stuff (10 regs + 9 ptrs)
  926.     jmp    short mrst2
  927.  
  928. mrst1:    mov    ss,2[bx]    ;fetch SS from the PDL
  929.     mov    sp,4[bx]    ;SP too, now it points to previous store
  930. ;    pop    WSEG        ;so we can pop everything
  931. ;    pop    P4        ;we pushed in reverse
  932. ;    pop    P3
  933. ;    pop    P2
  934. ;    pop    P1
  935. ;    pop    P0
  936. ;    pop    PZ
  937. ;    pop    PY
  938. ;    pop    PX
  939.     pop    es
  940.     pop    ds
  941.     pop    di
  942.     pop    si
  943.     pop    bp
  944.     pop    dx
  945.     pop    cx
  946.     pop    bx
  947.     pop    ax
  948.     popf
  949. mrst2:    push    rtaddr        ;restore return address
  950.     ret            ;and exit
  951.  
  952. ;    Load a single variable into (cx) from the pushdown
  953. ;    list.  No register is sure to be preserved.
  954.  
  955. CXLD:    mov    bx,PX        ;pointer to argument
  956.     mov    cx,[bx]     ;fetch low order byte
  957.     call    ESLD        ;get segment address
  958.     jmp    UCL        ;erase argument [(cx) is unchanged]
  959.  
  960. ;    Load register pair (dx) from the pushdown list.
  961. ;    (cx) will be preserved, (bx) not.
  962.  
  963. DXLD:    mov    bx,PX        ;pointer to argument
  964.     push    word ptr [bx]        ;fetch word
  965.     call    UCL        ;erase argument
  966.     pop    dx        ;restore (dx) since UCL modified it
  967.     ret
  968.  
  969. ;    (=)  Test the two top arguments on the pushdown list
  970. ;    for equality.  The arguments may be of any length, but
  971. ;    will be equal only when of the same length and composed
  972. ;    of the same sequence of bytes. The top argument will be
  973. ;    popped whatever the outcome, but when equality is true
  974. ;    both will be popped.
  975.  
  976. EQL:    mov    di,PX        ;under argument
  977.     mov    cx,PY
  978.     sub    cx,di        ;obtain length of top argument
  979.     call    UCL        ;lift top argument
  980.     mov    si,PX
  981.     mov    bx,PY
  982.     sub    bx,si
  983.     cmp    bx,cx        ;compare lengths
  984.     jnz    EQF
  985.     cld
  986.     mov    ax,ds
  987.     mov    es,ax
  988.     repz    cmpsb
  989.     jnz    EQF
  990.     jmp    CUCL        ;both agree, erase second arg, TRUE
  991. EQF:    ret            ;disagree so FALSE
  992.  
  993. ;    Load (es) with (ds) if PDL argument length is 2 bytes
  994. ;    Load!(es) from upper two bytes if length is 4 bytes
  995.  
  996. ESLD:    mov    ax,PY
  997.     mov    bx,PX
  998.     sub    ax,bx
  999.     cmp    ax,2
  1000.     jnz    esl1
  1001.     mov    ax,ds        ;two-byte argument
  1002.     mov    es,ax
  1003.     ret
  1004. esl1:    cmp    ax,4
  1005.     jnz    esl2
  1006.     mov    ax,2[bx]    ;four-byte argument
  1007.     mov    es,ax
  1008.     ret
  1009. esl2:    call    RR1        ;error record with one lift
  1010.  
  1011. ;    -------------------------------------------------------
  1012. ;
  1013. ;    Some of the service routines which are likely to be
  1014. ;    external references in other modules are:
  1015. ;
  1016. ;        puon    push one byte on PDL
  1017. ;        putw    push address on PDL
  1018. ;        thrl    load  three arguments onto 8080 stack
  1019. ;        twol    load two arguments onto 8080 stack
  1020. ;        onel    load one argument onto 8080 stack
  1021. ;        bcld    load (cx) from PDL, pop PDL
  1022. ;        deld    load (dx) from PDL, pop PDL
  1023. ;
  1024. ;    -------------------------------------------------------
  1025.  
  1026. ;    END
  1027.