home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol164 / rec80.mac < prev    next >
Encoding:
Text File  |  1984-04-29  |  38.2 KB  |  1,218 lines

  1.  
  2. ;    *******************************************************
  3. ;    *  REC module containing the REC nucleus and some of  *
  4. ;    *  the really indispensable operators and predicates  *
  5. ;    *  such as those defining two byte binary numbers and *
  6. ;    *  ASCII constant strings.  The model of a pushdown   *
  7. ;    *  list is assumed in the expectation that additional *
  8. ;    *  operators and predicates will also follow reversed *
  9. ;    *  Polish notation. There are additionally many small *
  10. ;    *  service routines which may be used externally.     *
  11. ;    *                              *
  12. ;    *  The source language for these programs is the one  *
  13. ;    *  used by the Microsoft M80 macro assembler.          *
  14. ;    *                              *
  15. ;    *  REC.MAC contains the following compiling entries:  *
  16. ;    *                              *
  17. ;    *    reclp    left parenthesis              *
  18. ;    *    recco    colon                      *
  19. ;    *    recsc    semicolon                  *
  20. ;    *    recrp    right parenthesis              *
  21. ;    *    recop    operator                  *
  22. ;    *    recpr    predicate                  *
  23. ;    *    recsq    single quotes                  *
  24. ;    *    recdq    double quotes                  *
  25. ;    *    reccm    comments                  *
  26. ;    *    reco1    operator with one ASCII parameter     *
  27. ;    *    recp1    predicate with one ASCII parameter    *
  28. ;    *    recms    unary minus sign              *
  29. ;    *    recdd    decimal digit                  *
  30. ;    *                              *
  31. ;    *  REC.MAC contains the following operators and       *
  32. ;    *  predicates:                          *
  33. ;    *                              *
  34. ;    *    '    single quote                  *
  35. ;    *    "    double quote                  *
  36. ;    *    nu    two byte decimal number              *
  37. ;    *    O    decimal ASCII string to number          *
  38. ;    *    #    number to decimal ASCII string          *
  39. ;    *    L    erase argument (lift)              *
  40. ;    *    @    execute subroutine              *
  41. ;    *    {    initiate program segment          *
  42. ;    *    }    discontinue program segment          *
  43. ;    *    ?    report detected error              *
  44. ;    *                              *
  45. ;    *  The following are initialization programs which    *
  46. ;    *  can be called at the outset of a compilation.      *
  47. ;    *                              *
  48. ;    *    inre    initialize REC temporary registers    *
  49. ;    *                              *
  50. ;    * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  51. ;    *                              *
  52. ;    *  Version circulated at the Summer School, 1980.     *
  53. ;    *                              *
  54. ;    *        REC80  -  Copyright (C) 1980          *
  55. ;    *       Universidad Autonoma de Puebla          *
  56. ;    *        All Rights Reserved              *
  57. ;    *                              *
  58. ;    *       [Harold V. McIntosh, 28 August 1980]          *
  59. ;    *                              *
  60. ;    28 April 1982 - RER only records first error          *
  61. ;    12 May 1983 - @@ consults PDL for subroutine name     *
  62. ;    *******************************************************
  63.  
  64. ;    External references to REC RAM memory, situated in FXT.
  65.  
  66.     ext    read,tyin,tyou    ;I-O subroutines
  67.     ext    xpd,ypd,zpd    ;program reference points
  68.     ext    px,py,pz    ;pointers to pushdown list
  69.     ext    c1        ;pointers to compiling area
  70.     ext    fxt,vrt        ;pointers to directories
  71.     ext    er        ;error deposit
  72.  
  73. ;    =======================================================
  74. ;    The nucleus of REC is a compiler for control symbols,
  75. ;    operators and predicates, some auxiliary subroutines,
  76. ;    and an initilazation routine.
  77. ;
  78. ;    The compiler proper uses only the folowing external
  79. ;    references:
  80. ;
  81. ;        RAM storage        xpd, ypd, zpd
  82. ;        I-O routine        read
  83. ;        skip instruction    skp
  84. ;
  85. ;    The RAM storage must be initialized, which may be
  86. ;    accomplished by calling inre.
  87. ;
  88. ;    The location in which the object code is placed is
  89. ;    passed along through the register pair DE, which is
  90. ;    continually updated to reflect the next available byte.
  91. ;    None of the other registers are either conserved nor
  92. ;    significant after the completion of compilation.
  93. ;
  94. ;    The usage of the registers is the following
  95. ;
  96. ;        pair BC contains the execution pointer
  97. ;        pair DE contains the object program counter
  98. ;        pair HL contains the compiling address
  99. ;
  100. ;    =======================================================
  101.  
  102. ;    Equivalences defining INTEL 8080 instructions and some
  103. ;    constants.
  104.  
  105. ca    equ    (call)    ;call
  106. ju    equ    (jmp)    ;jump
  107. rn    equ    (ret)    ;return
  108. po    equ    (pop h)    ;pop h
  109. pu    equ    (push h);push h
  110. lh    equ    (lhld)    ;lhld
  111. sh    equ    (shld)    ;shld
  112. ix    equ    (inx h)    ;inx h
  113. lx    equ    (lxi h)    ;lxi h,
  114. xt    equ    (xthl)    ;xthl
  115. ze    equ    0000H    ;zero
  116. ff    equ    00FFH    ;one byte complement of zero
  117.  
  118. ;    Compile a left parenthesis.
  119.  
  120. reclp::    lhld    zpd    ;save the linkage to semicolon exits
  121.     xthl        ;which must be put under return address
  122.     push    h    ;
  123.     lhld    ypd    ;save the higher linkage to false jumps
  124.     xthl        ;which is also tucked away
  125.     push    h    ;
  126.     lhld    xpd    ;save the repeat references
  127.     xthl        ;as are all three data
  128.     push    h    ;
  129.     lxi    h,ze    ;initialze the new chains
  130.     shld    zpd    ;null TRUE exit list
  131.     shld    ypd    ;null FALSE jump list
  132.     dad    d    ;
  133.     shld    xpd    ;new parenthesis level begins here
  134.     ret
  135.  
  136. ;    Compile a colon.
  137.  
  138. recco::    lhld    xpd    ;pick up reference to left parenthesis
  139.     call    recju    ;and insert a jump to its location
  140.     jmp    recfy    ;fill in any FALSE predicate jumps
  141.  
  142. ;    Compile a semicolon.
  143.  
  144. recsc::    lhld    zpd    ;pick up link to TRUE exit chain
  145.     call    recju    ;insert this one on it too
  146.     shld    zpd    ;store it as the new head of the chain
  147.     jmp    recfy    ;fill in any FALSE predicate jumpe
  148.  
  149. ;    Compile an operator.
  150.  
  151. recop::    mvi    a,ca    ;get the 8080 code for a CALL
  152.     stax    d    ;include it in the compiled code
  153.     inx    d    ;advance DE to receive next byte
  154.     ldax    b    ;BC points to subroutine address
  155.     inx    b    ;we got low byte, get ready for high
  156.     stax    d    ;low byte into CALL instruction
  157.     inx    d    ;advance pointers at first opportunity
  158.     ldax    b    ;fetch high byte
  159.     stax    d    ;incorporate it in compiled code
  160.     inx    d    ;keep DE positioned
  161.     ret
  162.  
  163. ;    Compile a predicate.
  164.  
  165. recpr::    call    recop    ;call its subroutine, same as operator
  166. recyj:    lhld    ypd    ;linkage to FALSE exits
  167.     call    recju    ;incorporate a jump if result FALSE
  168.     shld    ypd    ;update for new head of chain
  169.     ret
  170.  
  171. ;    Compile a right parenthesis.
  172.  
  173. recrp::    pop    h    ;recover xpd, which is hidden
  174.     xthl        ;under return to <call recrp>
  175.     shld    xpd    ;replace it
  176.     mov    a,h    ;xpd = 0 is signal for top level
  177.     ora    l    ;test HL for zero
  178.     jz    recfp    ;if so, continue with recfp
  179.     pop    h    ;recover wpd
  180.     xthl        ;hidden under same return address
  181.     call    recju    ;link expr to ypd on its own level
  182.     push    h    ;but save pointer until we finish up
  183.     call    recfy    ;false predicates in last segment
  184.     pop    h    ;back to higher level
  185.     shld    ypd    ;replace ypd for higher level
  186.     lhld    zpd    ;now we have destination for semicolons
  187.     call    recfc    ;so insert all the correct addresses
  188.     pop    h    ;recover old zpd
  189.     xthl        ;which is also under return address
  190.     shld    zpd    ;replace old zpd
  191.     ret
  192.  
  193. ;    Final right parentheses get a different treatment.
  194.  
  195. recfp:    pop    h    ;fetch return to <call recrp>
  196.     xchg        ;exchange it with compile pointer
  197.     mvi    m,rn    ;store a <ret> for false exit
  198.     inx    h    ;ready for next byte
  199.     push    h    ;save compile pointer
  200.     lxi    d,skp    ;address of skip - TRUE exit from REC
  201.     call    recfy    ;use it for last segment
  202.     lhld    zpd    ;destination of semicolons now known
  203.     call    recfc    ;so fill out that chain
  204.     pop    d    ;compile pointer that was saved
  205.     pop    h    ;old ypd
  206.     shld    ypd    ;restore it
  207.     pop    h    ;old zpd
  208.     shld    zpd    ;restore it
  209.     ret        ;return one level higher than expected
  210.  
  211. ;    Insert a new element in a chain of jmp's which will
  212. ;    eventually have destination addresses.  In the interim
  213. ;    each is given the address of its predecessor. On entry
  214. ;    DE holds the address where the instruction will be
  215. ;    stored and HL holds the address of its predecessor.
  216. ;    On exit, DE is incremented by 3 to point to the next
  217. ;    free byte, and HL has the starting value of DE.
  218.  
  219. recju:    xchg        ;HL and DE exchanged is better
  220.     mvi    m,ju    ;store the jump instruction
  221.     inx    h    ;advance pointer
  222.     push    h    ;preserve location of new link
  223.     mov    m,e    ;store low order byte of old link
  224.     inx    h    ;advance pointer
  225.     mov    m,d    ;store high order byte of old link
  226.     inx    h    ;advance pointer
  227.     pop    d    ;recover new link
  228.     xchg        ;restore original roles to DE, HL
  229.     ret
  230.  
  231. ;    When the destination of a linked chain of jumps is
  232. ;    finally known, the destination can be substituted into
  233. ;    each one of the links.  On entry, HL contains the
  234. ;    address of the first link unless it is zero signifying
  235. ;    a null chain.
  236.  
  237. recfc:    mov    a,l    ;look at low byte of link address
  238.     ora    h    ;superimpose the high byte
  239.     rz        ;if the address was zero, chain ends
  240.     mov    c,m    ;save low byte of next link
  241.     mov    m,e    ;store low byte of destination
  242.     inx    h    ;advance to high byte
  243.     mov    b,m    ;save high byte of next link
  244.     mov    m,d    ;store high byte of destination
  245.     mov    l,c    ;update low byte of link
  246.     mov    h,b    ;update high byte of link
  247.     jmp    recfc    ;continue
  248.  
  249. ;    Call recfc with the intention of filling the y chain.
  250.  
  251. recfy:    lhld    ypd
  252.     call    recfc
  253.     shld    ypd
  254.     ret
  255.  
  256. ;    Subroutine which will initialize the temporary
  257. ;    registers used by the REC compiler.
  258.  
  259. inre::    lxi    h,ze
  260.     shld    xpd
  261.     shld    ypd
  262.     shld    zpd
  263.     ret
  264.  
  265.  
  266. ;    =======================================================
  267. ;    The following are specialized compiling subroutines
  268. ;    which apply to special structures and depend on the
  269. ;    model of a pushdown list with a linked chain structure
  270. ;    and special registers px and py delimiting the top
  271. ;    segment on the chain.
  272. ;    =======================================================
  273.  
  274. ;    -------------------------------------------------------
  275. ;    Compilation of quoted expressions.  Single and double
  276. ;    quotes may alternate with one another to an arbitrary
  277. ;    depth.  Both kinds of quotes are executed in the same
  278. ;    way, by loading the quoted expression from the program
  279. ;    onto the pushdown list.
  280. ;    -------------------------------------------------------
  281.  
  282. ;    Compile single quotes.
  283.  
  284. recsq::    call    recop    ;record call to qu
  285.     inx    d    ;set aside two bytes
  286.     inx    d    ;to hold length of ASCII chain
  287.     push    d    ;keep beginning for future reference
  288.     lxi    h,enqu    ;cleanup subroutine
  289.     push    h    ;delay its execution until ret
  290. sq:    call    read    ;read the next character
  291.     cpi    ''''    ;test for single quote
  292.     rz        ;if so go after entire chain
  293.     cpi    '"'    ;test for double quotes
  294.     cz    dq1    ;if so, read it all
  295. sq1:    stax    d    ;otherwise keep on storing
  296.     inx    d    ;and advancing pointer
  297.     jmp    sq    ;go after next character
  298.  
  299. ;    Compile double quotes.
  300.  
  301. recdq::    call    recop    ;record call to qu
  302.     inx    d    ;set aside two bytes
  303.     inx    d    ;to hold length of chain
  304.     push    d    ;put chain origin away for reference
  305.     lxi    h,enqu    ;cleanup subroutine
  306.     push    h    ;delay its execution until ret
  307. dq:    call    read    ;read the next character
  308.     cpi    '"'    ;test for double quotes
  309.     rz        ;if so chain finished
  310.     cpi    ''''    ;check for single quotes
  311.     cz    sq1    ;if so go after whole chain
  312. dq1:    stax    d    ;otherwise keep on storing
  313.     inx    d    ;and advancing pointer
  314.     jmp    dq    ;go after next character
  315.  
  316. ;    Cleanup for both quote compilers.
  317.  
  318. enqu:    xchg        ;put compile pointer in HL
  319.     pop    d    ;put origin of chain into DE
  320.     call    siz    ;length returns in BC
  321.     xchg        ;address of chain front back in HL
  322.     dcx    h    ;back one byte
  323.     mov    m,b    ;store high order byte of length
  324.     dcx    h    ;back another byte
  325.     mov    m,c    ;store low order byte of length
  326.     ret
  327.  
  328. ;    (') (")   Execute single or double quote.
  329.  
  330. qu::    pop    h    ;get call location off the 8080 stack
  331.     mov    c,m    ;low order byte of count
  332.     inx    h    ;
  333.     mov    b,m    ;high order byte of count
  334.     inx    h    ;
  335.     push    h    ;save source origin
  336.     dad    b    ;calculate source end = return adress
  337.     xthl        ;exchange it for source origin
  338.     push    h    ;but we're not ready to use it yet
  339.     call    narg    ;check space, put dest. pointer in HL
  340.     pop    d    ;put source pointer in DE
  341.     call    miuc    ;move from program to pushdown list
  342.     shld    py    ;record end of argument
  343.     ret
  344.  
  345. ;    -------------------------------------------------------
  346. ;    Comments are enclosed in square brackets, which must be
  347. ;    balanced.  Code may be disabled by enclosing it in
  348. ;    square brackets, but care must be taken that the
  349. ;    expression so isolated does not contain individual
  350. ;    brackets, such as arguments of arrobas or quoted
  351. ;    brackets, which might disrupt the balance. Since
  352. ;    comments are ignored by the compiler they are not
  353. ;    executed.
  354. ;    -------------------------------------------------------
  355.  
  356. ;    Compile comments by ignoring them.
  357.  
  358. reccm::    call    read    ;get next character
  359.     cpi    ']'    ;test for closing ]
  360.     rz        ;if so we're done
  361.     cpi    '['    ;test for beginning of new level
  362.     cz    reccm    ;if so go after it recursively
  363.     jmp    reccm    ;otherwise keep on reading
  364.  
  365. ;    -------------------------------------------------------
  366. ;    Sometimes, notably in compiling arroba as a call to a
  367. ;    subroutine named by a single letter, a parameter will
  368. ;    follow a subroutine call as its calling sequence.
  369. ;    -------------------------------------------------------
  370.  
  371. ;    Operator with one ASCII parameter.
  372.  
  373. reco1::    call    recop    ;always compile the subroutine call
  374.     call    read    ;read the parameter
  375.     stax    d    ;store as a 1-byte calling sequence
  376.     inx    d    ;always ready for next byte
  377.     ret
  378.  
  379. ;    Predicate with one ASCII parameter.
  380.  
  381. recp1::    call    reco1    ;compile as the analogous operator
  382.     jmp    recyj    ;then take account of false exit
  383.  
  384. ;    -------------------------------------------------------
  385. ;    Decimal numbers are of such frequent occurrence in the
  386. ;    form of counters, arguments, or just data that it is
  387. ;    convenient to compile them on sight without requiring
  388. ;    any special delimiters.  Likewise, negative numbers are
  389. ;    easier to designate using a minus sign than using their
  390. ;    modular form, but this should not prevent the use of a
  391. ;    minus sign as an operator.
  392. ;    -------------------------------------------------------
  393.  
  394. ;    Compile a minus sign. This involves determining whether
  395. ;    it is followed immediately by a decimal digit, in which
  396. ;    case it is compiled as part of a negative number.
  397.  
  398. recms::    call    read    ;read in one byte
  399.     call    ms1    ;decide whether it is a digit
  400.     push    psw    ;it was not, save it
  401.     call    recop    ;compile call to binary minus
  402.     pop    psw    ;recover the extra character
  403.     jmp    skp    ;skip because we have next character
  404.  
  405. ms1:    call    rnd    ;return if not digit
  406.     inx    sp    ;erase call to ms1
  407.     inx    sp    ;
  408.     call    recds    ;read and convert digit string
  409.     lxi    b,gnu    ;fake that it was nu, not ms
  410.     push    psw    ;save terminating character
  411.     call    nhl    ;negate HL
  412.     jmp    dd1    ;continue as though positive number
  413.  
  414. gnu:    dw    nu
  415.  
  416. ;    Compile a decimal digit, which requires reading any
  417. ;    further digits which follow, and saving the terminator.
  418.  
  419. recdd::    rrc        ;undo multiplication by 4
  420.     rrc        ;
  421.     push    b    ;save execution address
  422.     call    recds    ;read and transform rest of digits
  423.     pop    b    ;recover execution address
  424.     push    psw    ;recover terminating character
  425. dd1:    call    recop    ;compile subroutine call
  426.     xchg        ;DE and HL must be interchanged
  427.     mov    m,e    ;put low order byte in calling sequence
  428.     inx    h    ;
  429.     mov    m,d    ;put high order byte there too
  430.     inx    h    ;ready for next byte
  431.     xchg        ;put DE and HL back as they were
  432.     pop    psw    ;recover terminating character
  433.     jmp    skp    ;skip over character read call
  434.  
  435. ;    Negate HL.  BC and DE are conserved.
  436.  
  437. nhl:    mov    a,l    ;fetch low byte into accumulator
  438.     cma        ;complement it
  439.     mov    l,a    ;replace it in HL
  440.     mov    a,h    ;fetch high byte into accumulator
  441.     cma        ;complement it
  442.     mov    h,a    ;replace it in HL
  443.     inx    h    ;negatice is complement plus 1
  444.     ret
  445.  
  446. ;    Multiply HL by 10 and add A.  DE is conserved.
  447.  
  448. txp:    mov    b,h    ;transfer HL to BC
  449.     mov    c,l    ;
  450.     dad    h    ;multiply HL by 2
  451.     dad    h    ;another 2 makes 4
  452.     dad    b    ;the original HL makes 5
  453.     dad    h    ;another 2 makes 10
  454.     add    l    ;add in the accumulator
  455.     mov    l,a    ;returning sum to low byte
  456.     rnc        ;nothing more if no carry
  457.     inr    h    ;otherwise increment high byte
  458.     ret
  459.  
  460. ;    The heart of number compilation.
  461.  
  462. recds:    ani    0FH    ;mask ASCII down to binary value
  463.     mov    l,a    ;put it into register pair HL
  464.     mvi    h,ze    ;fill out H with a zero
  465. rd1:    call    read    ;read the next character
  466.     call    rnd    ;quit if it is not another digit
  467.     call    txp    ;multiply HL by ten and add A
  468.     jmp    rd1    ;continuing while digits keep coming
  469.  
  470. ;    Execute a number, which means load it on pdl.
  471.  
  472. nu::    lxi    b,02H    ;two bytes will be required
  473.     call    narg    ;close last argument, open new
  474.     pop    d    ;get beginning of calling sequence
  475.     ldax    d    ;fetch the low order byte
  476.     mov    m,a    ;and copy it over
  477.     inx    d    ;on to the high order byte
  478.     inx    h    ;and the place to store it
  479.     ldax    d    ;pick it up
  480.     mov    m,a    ;and set it down
  481.     inx    d    ;move on to program continuation
  482.     inx    h;    ;always leave PDL ready for next byte
  483.     push    d    ;put back the return address
  484.     shld    py    ;mark end of the argument
  485.     ret
  486.  
  487. ;    (O) Transform an ASCII character string on the PDL into
  488. ;    a two-byte number.  Predicate - false if the argument
  489. ;    is not a digit string or null, leaving the argument
  490. ;    unchanged.
  491.  
  492. uco::    lxi    b,2    ;two bytes are required
  493.     call    oarg    ;check that they are available
  494.     lhld    py    ;fetch the end of the argument string
  495.     mvi    m,ze    ;put a zero there to mark its end
  496.     lhld    px    ;load pointer to argument string
  497.     xchg        ;put it in register DE
  498.     lxi    h,ze    ;zero in HL to start the conversion
  499. o1:    ldax    d    ;fetch one character
  500.     inx    d    ;get ready for next
  501.     ora    a    ;test for zero
  502.     jz    o2    ;go to accumulation phase
  503.     call    rnd    ;FALSE, chain unaltered if non-digit
  504.     call    txp    ;otherwise continue to work up value
  505.     jmp    o1    ;and keep on reading bytes
  506. o2:    xchg        ;safeguard converted number in DE
  507.     lhld    px    ;get pointer to argument
  508.     mov    m,e    ;store low byte
  509.     inx    h    ;increment pointer
  510.     mov    m,d    ;store high byte
  511.     inx    h    ;increment pointer again
  512.     shld    py    ;store to close argument
  513.     jmp    skp    ;TRUE exit from predicate
  514.  
  515. ;    (#)  Change two-byte binary number into a decimal-based
  516. ;    ASCII string without sign. The special cases of a zero-
  517. ;    byte or a one-byte argument are also considered.
  518.  
  519. ns::    lxi    b,05H        ;five bytes may be required
  520.     call    oarg        ;reuse the old argument
  521.     call    psiz        ;get length of argument
  522.     mov    a,c        ;suppose length less than 256
  523.     xchg            ;pointer to low byte into HL
  524.     lxi    d,ze        ;put zero in DE for default
  525.     ora    a        ;test for zero bytes
  526.     jz    ns1        ;load nothing
  527.     mov    e,m        ;load low byte
  528.     dcr    a        ;test for one byte
  529.     jz    ns1        ;only byte and it's loaded
  530.     inx    h        ;advance to high byte
  531.     mov    d,m        ;load high byte
  532.     dcx    h        ;back to low byte
  533. ns1:    push    h        ;save pointer for ASCII string
  534.     mvi    a,'0'        ;prepare to write a zero
  535.     lxi    h,-10000    ;will there be 5 digits?
  536.     dad    d        ;
  537.     jc    ns2        ;
  538.     lxi    h,-1000        ;will there be 4 digits?
  539.     dad    d        ;
  540.     jc    ns3        ;
  541.     lxi    h,-100        ;will there be 3 digits?
  542.     dad    d        ;
  543.     jc    ns4        ;
  544.     lxi    h,-10        ;will there be 2 digits?
  545.     dad    d        ;
  546.     jc    ns5        ;
  547.     jmp    ns6        ;write one no matter what
  548. ns2:    lxi    b,-10000    ;ten thousands digit
  549.     call    nsa        ;
  550. ns3:    lxi    b,-1000        ;thousands digit
  551.     call    nsa        ;
  552. ns4:    lxi    b,-100        ;hundreds digit
  553.     call    nsa        ;
  554. ns5:    lxi    b,-10        ;tens digit
  555.     call    nsa        ;
  556. ns6:    add    e        ;units digit
  557.     pop    h        ;recover pointer to PDL
  558.     mov    m,a        ;store the digit
  559.     inx    h        ;position pointer for next byte
  560.     shld    py        ;done, store it as terminator
  561.     ret
  562.  
  563. nsa:    mov    l,c    ;put power of ten in HL
  564.     mov    h,b    ;
  565.     dad    d    ;subtract it once
  566.     jnc    nsb    ;can't subtract
  567.     inr    a    ;increase the count
  568.     xchg        ;put diminished number in DE
  569.     jmp    nsa    ;repeat the cycle
  570. nsb:    pop    h    ;get <call nsa> return address
  571.     xthl        ;we really wanted pointer to PDL
  572.     mov    m,a    ;store new digit
  573.     inx    h    ;advance pointer
  574.     xthl        ;put it back on 8080 stack
  575.     mvi    a,'0'    ;load a fresh ASCII zero
  576.     pchl        ;return to the <call nsa>
  577.  
  578. ;    =======================================================
  579. ;    Some simple procedures to compile REC expressions into
  580. ;    subroutines, deposit a reference to them in a symbol
  581. ;    table, and eventually to recover the space and erase
  582. ;    the symbol table reference. Compiling and execution are
  583. ;    two separate activities, for the latter the predicates
  584. ;    @ or x have to be used.  The pair emcu, emcv are used
  585. ;    by REC's main program, and can be used as parts of
  586. ;    subroutines in other REC modules to go through their
  587. ;    execution sequence. Compilation is the province of the
  588. ;    two entry points emce and emcx.
  589. ;    =======================================================
  590.  
  591. ;    Table look up.  On entry, A holds the serial number
  592. ;    of a table reference, HL the origin of the table.
  593. ;    On exit, HL holds HL+4*A, DE is preserved, the
  594. ;    other registers to be ignored.  The entry point tlv
  595. ;    produces the same results with the exception that HL
  596. ;    becomes HL+2*A.
  597.  
  598. tlu::    add    a    ;multiply A by 2
  599. tlv::    add    a    ;multiply A by 2
  600.     mov    c,a    ;insert A as low byte of BC
  601.     mvi    b,ze    ;make the high byte zero
  602.     jnc    tlw    ;finished if A was small
  603.     inr    b    ;carry if A was larger
  604. tlw:    dad    b    ;add to base address
  605.     ret
  606.  
  607. ;    Table search. The table whose address is stored at fxt
  608. ;    is consulted for its pair of addresses at position 4*A.
  609. ;    Thus on entry, A holds the table index.  This table
  610. ;    alternates the address of a compiling subroutine with
  611. ;    the execution address of the same entry.  On exit, BC
  612. ;    holds the execution address, DE is preserved, and a
  613. ;    jump is made to the compiling address.
  614.  
  615. rects:    lhld    fxt    ;load base address of table
  616.     call    tlu    ;read the table
  617.     mov    c,m    ;put the first entry in BC
  618.     inx    h    ;low byte first, then high byte
  619.     mov    b,m    ;
  620.     inx    h    ;keep advancing the pointer
  621.     push    b    ;jump address activated by a ret
  622.     mov    b,h    ;table pointer is going
  623.     mov    c,l    ;to be stored in BC
  624.     ret        ;then off to the compilation
  625.  
  626. ;    Advance to following ( or { bypassing [comments]
  627.  
  628. left::    call    read
  629.     cpi    '('
  630.     rz
  631.     cpi    '{'
  632.     rz
  633.     cpi    '['
  634.     cz    reccm
  635.     jmp    left
  636.  
  637. ;    A main program to compile characters one by one as
  638. ;    they are read in from the console.  Note that the
  639. ;    compiling programs invoked by rects can generate skips
  640. ;    when they have already read the following character.
  641. ;    This occurs most notably when compiling digits. Also
  642. ;    note that svc normalizes characters when it accepts
  643. ;    them.
  644.  
  645. recre::    call    read    ;read a character from whereever
  646. recrr::    call    svc    ;check for space, control character
  647.     jmp    recre    ;not valid, go back for another
  648.     call    rects    ;look up in table and compile it
  649.     jmp    recre    ;read another character and repeat
  650.     jmp    recrr    ;repeat but next character already read
  651.  
  652. ;    A subroutine which will pass over comments, and wait
  653. ;    for an opening left parenthesis before compiling a REC
  654. ;    expression.  A series of definitions may be enclosed in
  655. ;    braces, along with a subroutine to be executed.
  656.  
  657. emce::    call    ucl    ;entry here erases an argument from PDL
  658. emcx::    call    left    ;only ( or { can enclose a REC expression
  659.     lhld    c1    ;next location available in compile area
  660.     xchg        ;take it as compiling origin
  661.     lhld    c1    ;but also save it to restore later
  662.     xthl        ;save it under ret on PDL
  663.     push    h    ;
  664.     call    recrr    ;compiling prgrm one char already read
  665.     xchg        ;location for code which follows this one
  666.     shld    c1    ;for which c1 is the pointer
  667.     ret
  668.  
  669. ;    <call emcu> executes the subroutine whose address is on the
  670. ;    top of the 8080's stack, but then removes its definition from
  671. ;    REC's compiling area - as well as any subsequent definitions.
  672. ;    The cleanup is done by emcv (true) or emcw (false).
  673.  
  674. emcu::    pop    d    ;<call emcu>'s return address
  675.     pop    h    ;subroutine address
  676.     push    d    ;we don't need the return yet
  677.     push    h    ;emcv will need the origin, so we
  678.     lxi    d,emcv    ;load them both onto the 8080 stack
  679.     push    d    ;to be used after the subroutine call
  680.     pchl        ;execute the subroutine
  681. emcv::    jmp    emcw    ;it is a predicate, this is FALSE return
  682.     pop    h    ;and this its TRUE return
  683.     shld    c1    ;original origin erases definition
  684.     jmp    skp    ;pass on TRUE return to original call
  685. emcw:    pop    h    ;same as above but FALSE return
  686.     shld    c1    ;
  687.     ret        ;
  688.  
  689. ;    ({)  Any REC expression (xxx) may have a series of subroutine
  690. ;    definitions associated with it, but then the entire sequence
  691. ;    should be enclosed in braces: {(...) a (...) b ... (xxx)}.
  692. ;    Each of the secondary subroutines is compiled, as well as the
  693. ;    primary subroutine, but their initial addresses are not yet
  694. ;    recorded in the definition table VRT.  Rather, some special
  695. ;    code is generated, in which a call to the primary subroutine
  696. ;    will be surrounded by a series of pushes and then pops, which
  697. ;    are chosen so that the symbols representing subroutines have
  698. ;    the assignments within the braces only while the principal
  699. ;    subroutine is being executed. Consequently, any subroutine
  700. ;    definition made at a given brace level is valid throughout
  701. ;    that whole level, superceding any previous definitions of
  702. ;    the same subroutines, and of course susceptible to being
  703. ;    superceded within any of its own subbraces.
  704.  
  705. lbr::    mvi    a,ca    ;start with: call special
  706.     stax    d    ;         jmp  false
  707.     inx    d    ;         jmp  true
  708.     mov    c,e    ;place to put <call> address - keep in BC
  709.     mov    b,d    ;
  710.     inx    d    ;make room
  711.     inx    d    ;
  712.     call    recyj    ;link into 'predicate false' chain
  713.     call    recju    ;a <jmp> to the 'true' continuation
  714.     push    h    ;keep this address until very end
  715.     lhld    xpd    ;force the appearance of a main program
  716.     push    h    ;
  717.     lxi    h,ze    ;initialize definition counter
  718.     shld    xpd    ;this is top level for ensuing subroutines
  719. lb1:    push    d    ;record entry point to subroutine
  720.     inx    h    ;increment count of subroutines
  721.     push    h    ;keep it next to top on stack
  722.     push    b    ;call address at entry - keep it on top
  723.     call    left    ;ignore non-REC-expressions
  724.     call    recrr    ;compile one subroutine
  725. lb2:    call    read    ;get possible name of subroutine
  726.     cpi    '}'    ;no name so it's principal
  727.     jz    lb3    ;we compile the principal for execution
  728.     call    svc    ;convert name into serial number
  729.     jmp    lb2    ;punctuation instead of name
  730.     adi    ' '    ;32 variables in VRT - leave space for them
  731.     lhld    vrt    ;
  732.     call    tlv    ;convert serial to offset
  733.     pop    b    ;get this out of the way
  734.     xthl        ;store table address, put subr count in HL
  735.     jmp    lb1    ;on to next definition
  736.  
  737. ;    The preface and the code for all the subroutines has now been
  738. ;    compiled. The top of the 8080 stack has the location of the
  739. ;    call instruction to the special code which we are now going
  740. ;    to compile. DE as always is the next address where code will
  741. ;    be deposited.  Still more information is on the 8080 stack -
  742. ;    the number of subroutines, their names and starting addresses,
  743. ;    the saved value of XPD, address of the 'true' jump.
  744.  
  745. lb3:    pop    h    ;origin of brace compilation
  746.     mov    m,e    ;store next compilation address there
  747.     inx    h    ;
  748.     mov    m,d    ;
  749.     pop    b    ;number of subroutines
  750.     push    b    ;we'll need it again later
  751.     mov    l,c    ;put it in HL
  752.     mov    h,b    ;
  753.     dcx    h    ;calculate SP+4(HL-1),
  754.     dad    h    ;which is space used by names, addrs
  755.     dad    h    ;
  756.     dad    sp    ;
  757.     xchg        ;
  758. lb4:    dcx    b    ;loop: count off the secondary subroutines
  759.     mov    a,c    ;
  760.     ora    b    ;
  761.     jz    lb5    ;finished: compile special code
  762.     mvi    m,lh    ;for each defined symbol we insert the
  763.     inx    h    ;code which will set it up in the jump
  764.     ldax    d    ;table, namely
  765.     mov    m,a    ;    lhld table entry
  766.     inx    h    ;    xthl
  767.     inx    d    ;    push h
  768.     ldax    d    ;    lxi  h,jump address
  769.     mov    m,a    ;    shld table entry
  770.     inx    h    ;
  771.     inx    d    ;
  772.     mvi    m,xt    ;
  773.     inx    h    ;
  774.     mvi    m,pu    ;
  775.     inx    h    ;
  776.     mvi    m,lx    ;
  777.     inx    h    ;
  778.     ldax    d    ;
  779.     mov    m,a    ;
  780.     inx    h    ;
  781.     inx    d    ;
  782.     ldax    d    ;
  783.     mov    m,a    ;
  784.     inx    h    ;
  785.     dcx    d    ;
  786.     dcx    d    ;
  787.     dcx    d    ;
  788.     mvi    m,sh    ;
  789.     inx    h    ;
  790.     ldax    d    ;
  791.     mov    m,a    ;
  792.     inx    h    ;
  793.     inx    d    ;
  794.     ldax    d    ;
  795.     mov    m,a    ;
  796.     inx    h    ;
  797.     dcx    d    ;
  798.     dcx    d    ;
  799.     dcx    d    ;
  800.     dcx    d    ;
  801.     dcx    d    ;
  802.     jmp    lb4    ;
  803.  
  804. ;    We have compiled all the subroutines, including the
  805. ;    principal one. Now we compile a call to it, followed
  806. ;    by an adjustment to the pushdown stack which will
  807. ;    remember whether it was TRUE or FALSE as a predicate.
  808.  
  809. lb5:    pop    b    ;number of subroutines
  810.     pop    d    ;origin of principal subroutine
  811.     push    b    ;we don't want this right now
  812.     mvi    m,ca    ;after the definitions are set up we
  813.     inx    h    ;will call the executable subexpression
  814.     mov    m,e    ;and then adjust it for a delayed skip
  815.     inx    h    ;so as not to have to put the code which
  816.     mov    m,d    ;follows twice in two flow branches
  817.     inx    h    ;    call    principal
  818.     mvi    m,ju    ;    jmp    $+6
  819.     inx    h    ;    xthl
  820.     push    h    ;    inx    h
  821.     inx    h    ;    inx    h
  822.     inx    h    ;    inx    h
  823.     mvi    m,xt    ;    xthl
  824.     inx    h    ;
  825.     mvi    m,ix    ;
  826.     inx    h    ;
  827.     mvi    m,ix    ;
  828.     inx    h    ;
  829.     mvi    m,ix    ;
  830.     inx    h    ;
  831.     mvi    m,xt    ;
  832.     inx    h    ;
  833.     xchg        ;
  834.     pop    h    ;
  835.     mov    m,e    ;
  836.     inx    h    ;
  837.     mov    m,d    ;
  838.     xchg        ;
  839.     pop    b    ;
  840.  
  841. ;    Restore original meaning of all subroutine names.
  842.  
  843. lb6:    dcx    b    ;loop to compile pops
  844.     mov    a,c    ;count out number of definitions
  845.     ora    b    ;
  846.     jz    lb7    ;go compile termination
  847.     mvi    m,po    ;after an expression in braces finishes
  848.     inx    h    ;execution, all its definitions are
  849.     mvi    m,xt    ;erased and the earlier ones replaced
  850.     inx    h    ;    pop h
  851.     mvi    m,sh    ;    xthl
  852.     inx    h    ;    shld table entry
  853.     pop    d    ;
  854.     mov    m,e    ;
  855.     inx    h    ;
  856.     mov    m,d    ;
  857.     inx    h    ;
  858.     pop    d    ;
  859.     jmp    lb6    ;
  860.  
  861. ;    Terminal code must be compiled, and XPD restored to its
  862. ;    original level.  There are two cases - when we are dealing
  863. ;    with a main program, and when a brace lies within a larger
  864. ;    REC expression.
  865.  
  866. lb7:    mvi    m,rn    ;'false' exit is always <ret>
  867.     inx    h    ;PC ready for next byte
  868.     xchg        ;PC back in DE
  869.     pop    h    ;saved XPD
  870.     shld    xpd    ;restored
  871.     mov    a,l    ;test it for zero
  872.     ora    h    ;
  873.     pop    h    ;address of 'true' jump
  874.     jz    lb8    ;zero means main program
  875.     mov    m,e    ;jump up to here to continue
  876.     inx    h    ;
  877.     mov    m,d    ;
  878.     ret
  879.  
  880. ;    Terminal code for a main program or defined subroutine.
  881. ;    Here there is a stack pointer adjustment which suppresses
  882. ;    the return from <call rects> following recrr. The reason is
  883. ;    that lbr has already detected }, the closing right brace,
  884. ;    which therefore will not appear in the input stream to be
  885. ;    interpreted through FXT, as recrr would expect.
  886.  
  887. lb8:    lxi    b,skp    ;'true' exit realized by <jmp skp>
  888.     mov    m,c    ;
  889.     inx    h    ;
  890.     mov    m,b    ;
  891.     dcx    h    ;'false' exit realized by <jmp here>,
  892.     dcx    h    ;
  893.     dcx    h    ;
  894.     mov    m,d    ;
  895.     dcx    h    ;
  896.     mov    m,e    ;
  897.     xchg        ;
  898.     mvi    m,rn    ;a terminal <ret>,
  899.     inx    h    ;and an updated PC.
  900.     xchg        ;
  901.     inx    sp    ;we won't go back to recrr
  902.     inx    sp    ;
  903.     ret
  904.  
  905. ;    (@) Subroutine which will transform an ASCII character
  906. ;    into a table reference, and then jump to the address
  907. ;    so encountered.  This is essentially REC's subroutine
  908. ;    call mechanism, necessarily a predicate since it calls
  909. ;    a REC expression, which is itself a predicate.
  910.  
  911. ar::    pop    h    ;entry if name is a parameter
  912.     mov    a,m    ;read the calling sequence
  913.     inx    h    ;advance pointer for return
  914.     push    h    ;put it back on 8080 stack
  915.     cpi    '@'    ;@@ means consult PDL for subroutine
  916.     jnz    xar    ;otherwise proceed
  917. nar::    lhld    px    ;entry if subroutine index is argument
  918.     mov    a,m    ;get low byte of argument
  919.     push    psw    ;put it in temporary storage
  920.     call    ucl    ;lift the pushdown list, erasing it
  921.     pop    psw    ;recover index
  922. xar::    lhld    vrt    ;entry when index is in register A
  923.     call    tlv    ;locate entry in directory (vrt)
  924.     mov    e,m    ;low byte of entry into E
  925.     inx    h    ;on to high byte
  926.     mov    d,m    ;place it in D
  927.     xchg        ;first exchange entry into HL
  928.     pchl        ;then use it as jump address
  929.  
  930. ;    =======================================================
  931. ;    Some general service routines.
  932. ;    =======================================================
  933.  
  934. ;    Skip on valid character, meaning, not control symbol.
  935. ;    If valid, 20H (space) is subtracted, making A = 1, etc.
  936.  
  937. svc::    cpi    '!'    ;reject space, excl is lower limit
  938.     rc        ;control or space - no skip
  939.     cpi    7FH    ;seven bits is upper limit
  940.     rnc        ;no skip if upper limit passed
  941.     sui    ' '    ;normalize to begin with (excl) = 1
  942.     jmp    skp    ;generate skip for printable ASCII
  943.  
  944. ;    Return if not decimal. A unchanged if not decimal, else
  945. ;    reduced to binary.
  946.  
  947. rnd::    cpi    ':'    ;colon follows 9 in ASCII alphabet
  948.     jnc    rtn    ;not decimal at or beyond this limit
  949.     cpi    '0'    ;ASCII zero is lower limit
  950.     jc    rtn    ;not decimal below this limit
  951.     sui    '0'    ;normalize to get binary values
  952.     ret
  953.  
  954. ;    Return if equal. Return out of calling routine if HL
  955. ;    is equal to DE, otherwise normal return to sequential
  956. ;    execution in calling program.
  957.  
  958. req::    mov    a,e    ;compare low bytes
  959.     cmp    l    ;
  960.     rnz        ;not zero means equality impossible
  961.     mov    a,d    ;compare high bytes
  962.     cmp    h    ;
  963.     rnz        ;not zero means not equal
  964. rtn::    inx    sp    ;entry for general "returns"
  965.     inx    sp    ;eliminate return address
  966.     ret        ;return is to next higher level
  967.  
  968. ;    Second level return on error.
  969.  
  970. rr2::    pop    h    ;entry to clear two items from PDL
  971.     xthl        ;
  972. rr1::    pop    h    ;entry to clear one item from PDL
  973.     xthl        ;
  974. rer::    push    h    ;
  975.     lxi    h,er    ;
  976.     mov    a,m    ;
  977.     inx    h    ;
  978.     ora    m    ;
  979.     pop    h    ;
  980.     xthl        ;get return address into HL
  981.     jnz    rrr    ;
  982.     shld    er    ;so that it can be recorded
  983. rrr:    pop    h    ;but preserve the original HL
  984.     ret
  985.  
  986. ;    (?)  Test whether an error has been reported: predicate
  987. ;    which is true if er is nonzero, in which case it will
  988. ;    reset er.  It will also, if TRUE, place the calling
  989. ;    address of the last reported error on the pushdown
  990. ;    list.  If false, only a FALSE return is generated. Note
  991. ;    the ironic circumstance that, if PDL is exhausted, qm
  992. ;    can generate an error trying to report an error - but
  993. ;    the TRUE result will still be valid. (?!TL;;) will give
  994. ;    minimal evidence of an error. Generally an error handling
  995. ;    subroutine will have the form (?(<correction>;<more drastic
  996. ;    correction>;...<fatal error message>_;);). If the error
  997. ;    cannot be ignored or handled within a simple subroutine,
  998. ;    then the treatment of errors should have been incorporated
  999. ;    into the whole structure of the program from the beginning.
  1000.  
  1001. qm::    lhld    er    ;fetch the error cell
  1002.     xchg        ;set it aside in DE
  1003.     lxi    h,ze    ;load zero into HL
  1004.     shld    er    ;use it to reset er
  1005.     mov    a,e    ;prepare to test whether er was
  1006.     ora    d    ;zero by superposing D and E
  1007.     rz        ;FALSE return if no error
  1008.     push    d    ;keep DE on the 8080 stack
  1009.     lxi    b,02H    ;we want two bytes for error address
  1010.     call    narg    ;check space, prepare for new argument
  1011.     pop    d    ;we are ready to store error address
  1012.     mov    m,e    ;store low byte
  1013.     inx    h    ;advance for high byte
  1014.     mov    m,d    ;store it also
  1015.     inx    h    ;pointer must always advance
  1016.     shld    py    ;end of the argument
  1017.     jmp    skp    ;TRUE return - there was an error
  1018.  
  1019. ;    Generate a skip (skp), which is often combined with the
  1020. ;    erasure of an argument on the pushdown list (cucl).
  1021.  
  1022. cucl::    call    ucl    ;erase the top argument
  1023. skp::    xthl        ;get the return address, but save HL
  1024.     inx    h    ;assume the skip will be over a
  1025.     inx    h    ;three-byte instruction, such as a jump
  1026.     inx    h    ;
  1027.     xthl        ;restore HL, which must be preserved
  1028.     ret        ;return to the altered address
  1029.  
  1030. ;    Calculate the length of a proposed insertion on PDL.
  1031. ;    On return, BC holds HL - DE, which means py - px.
  1032. ;    However, the alternative entry <siz> can be used when
  1033. ;    HL and DE have been previously loaded.
  1034.  
  1035. psiz::    lhld    px    ;get the beginning of the segment
  1036.     xchg        ;pass it to DE
  1037.     lhld    py    ;get the end of the segment
  1038. siz::    mov    a,l    ;-- alternate entry for other sizes --
  1039.     sub    e    ;subtract the beginning
  1040.     mov    c,a    ;from the end
  1041.     mov    a,h    ;to get the length
  1042.     sbb    d    ;of the interval
  1043.     mov    b,a    ;which is placed in BC
  1044.     ret
  1045.  
  1046. ;    Skip on not greater. On entry, HL holds an address,
  1047. ;    DE a limit.  A skip is generated if the address is
  1048. ;    less than or equal to the limit.  BC is not altered.
  1049. ;    An alternate entry sing (skip if increment not greater)
  1050. ;    expects to find an increment in BC, which is added
  1051. ;    to HL and remains with it.
  1052.  
  1053. sing::    dad    b    ;add the increment
  1054. sng::    mov    a,e    ;put limit low byte in the accumulator
  1055.     sub    l    ;compare with low byte of address
  1056.     mov    a,d    ;put limit high byte in accumulator
  1057.     sbb    h    ;compare with high byte of address
  1058.     rc        ;return if it is greater
  1059.     jmp    skp    ;generate skip if less or equal
  1060.  
  1061. ;    Skip on equal. On entry, DE and HL contain two-byte
  1062. ;    numbers.  Comparison is made, which at most alters A;
  1063. ;    if they are equal a skip is generated. The alternate
  1064. ;    entry <sieq> (skip if increment equal) permits testing
  1065. ;    whether an increment to HL located in BC will reach
  1066. ;    equality with DE, but then HL remains incremented.
  1067.  
  1068. sieq::    dad    b    ;add the increment
  1069. seq::    mov    a,e    ;two byte comparison
  1070.     cmp    l    ;of the registers DE
  1071.     rnz        ;and HL generating
  1072.     mov    a,d    ;a return when they
  1073.     cmp    h    ;are not equal and
  1074.     rnz        ;a three-byte skip when
  1075.     jmp    skp    ;they are equal
  1076.  
  1077. ;    Move by increment until count.  On entry, BC contains
  1078. ;    the number of bytes to be moved, DE the address of
  1079. ;    the source, and HL the destination.  On exit, BC is
  1080. ;    zero, DE lies beyond the source, and HL shows
  1081. ;    the next byte following the end of the destination.
  1082.  
  1083. miuc::    mov    a,c    ;determine whether zero bytes
  1084.     ora    b    ;remain to be moved
  1085.     rz        ;if so return
  1086.     ldax    d    ;fetch source byte
  1087.     mov    m,a    ;deposit in destination
  1088.     dcx    b    ;decrement counter
  1089.     inx    d    ;increment source pointer
  1090.     inx    h    ;increment destination pointer
  1091.     jmp    miuc    ;repeat the cycle
  1092.  
  1093. ;    Move by decrement until count. On entry, BC holds the
  1094. ;    number of bytes to be moved, DE the byte beyond the
  1095. ;    source, and HL the byte beyond the destination.  On
  1096. ;    exit, BC is zero, DE lies at the beginning of the
  1097. ;    source, and HL lies at the front of the destination.
  1098.  
  1099. mduc::    mov    a,c    ;determine whether zero bytes
  1100.     ora    b    ;remain to be moved
  1101.     rz        ;if so, return
  1102.     dcx    b    ;decrement count
  1103.     dcx    d    ;retract source pointer
  1104.     dcx    h    ;retract destination pointer
  1105.     ldax    d    ;fetch source byte
  1106.     mov    m,a    ;store at destination
  1107.     jmp    mduc    ;repeat the cycle
  1108.  
  1109. ;    Test PDL space beginning at top argument. On entry BC
  1110. ;    contains the total space required.  On exit, BC stays
  1111. ;    unchanged, DE holds pz, while HL holds px+BC.
  1112. ;    If the space is not available, return is made from the
  1113. ;    calling program after noting the error.  Otherwise
  1114. ;    normal return to the calling program occurs. The likely
  1115. ;    use of oarg is to record a result without having to go
  1116. ;    through ucl, narg.
  1117.  
  1118. oarg::    lhld    pz    ;load limit of PDL
  1119.     dcx    h    ;keep one byte margin
  1120.     xchg        ;place it in DE
  1121.     lhld    px    ;load beginning of current argument
  1122.     call    sing    ;check available space
  1123.     jmp    rer    ;no, note error, quit calling program
  1124.     ret        ;yes, continue normally
  1125.  
  1126. ;    Check space for, and then set up, a new argument. On
  1127. ;    entry, BC should contain the amount of additional
  1128. ;    space required.  The program will automatically add
  1129. ;    two more bytes for the pointer which would close the
  1130. ;    argument and then, if the required space is available,
  1131. ;    close it, define the new px, and leave its value in
  1132. ;    HL.  DE will contain the old value of px to be used
  1133. ;    in case the superseded argument is still interesting.
  1134. ;    When space is not available, the error return rer is
  1135. ;    taken.
  1136. ;
  1137. ;    The entry RARG can be taken when it is known that
  1138. ;    sufficient space is available but the pointers still
  1139. ;    have to be set up.
  1140.  
  1141. narg::    lhld    pz    ;load limit of PDL
  1142.     dcx    h    ;keep one byte margin
  1143.     xchg        ;place it in DE
  1144.     lhld    py    ;load end of current argument
  1145.     inx    h    ;include a margin of 2
  1146.     inx    h    ;for the link closing current arg
  1147.     call    sing    ;check available space
  1148.     jmp    rer    ;no, note error, quit calling program
  1149. rarg::    lhld    px    ;entry if no space check needed
  1150.     xchg        ;put beginning of arg in DE
  1151.     lhld    py    ;end of argument into HL
  1152.     mov    m,e    ;low byte of closing link
  1153.     inx    h    ;on to high byte
  1154.     mov    m,d    ;argument ends with pointer to front
  1155.     inx    h    ;beginning of new space
  1156.     shld    px    ;which is recorded by px
  1157.     ret        ;and remains in HL
  1158.  
  1159. ;    (L)  Remove argument from pushdown list. There are no
  1160. ;    requirements for entry to ucl.  On exit, BC remains
  1161. ;    unchanged, DE holds the end of the former argument
  1162. ;    and HL holds the beginning of the former argument -
  1163. ;    the one that was exposed when the current argument was
  1164. ;    erased. Erasing non-existent arguments creates an error
  1165. ;    condition which is noted and ignored.
  1166.  
  1167. ucl::    lhld    px    ;pointer to current argument
  1168.     dcx    h    ;just behind the present
  1169.     mov    d,m    ;argument is the address
  1170.     dcx    h    ;of the previous argument
  1171.     mov    e,m    ;load it into DE
  1172.     mov    a,e    ;zero signals non-existent argument
  1173.     ora    d    ;so we always test out of caution
  1174.     cz    rer    ;record error if pointer was zero
  1175.     shld    py    ;HL now holds end of previous arg.
  1176.     xchg        ;exchange pointers
  1177.     shld    px    ;pointer to beginning of prev. arg.
  1178.     ret
  1179.  
  1180. ;    Null program for undefined operators.
  1181.  
  1182. noop::    ret
  1183.  
  1184. ;    =======================================================
  1185. ;
  1186. ;    Some of the service routines, which might be external
  1187. ;    references in other modules, are:
  1188. ;
  1189. ;        psiz    size of argument on PDL
  1190. ;        siz    size of an interval
  1191. ;        oarg    space when reusing an argument
  1192. ;        narg    close old argument, space for new
  1193. ;        rarg    same as narg when space is assured
  1194. ;        sng    skip when not greater
  1195. ;        sing    skip when increment not greater
  1196. ;        seq    skip when equal
  1197. ;        sieq    skip when increment equal
  1198. ;        skp    generic skip
  1199. ;        req    return on equal
  1200. ;        rer    return on error
  1201. ;        rr2    rer after popping two addresses
  1202. ;        rtn    generic return
  1203. ;        miuc    move by increment until count
  1204. ;        mduc    move by decrement until count
  1205. ;        ucl    lift argument from PDL (L)
  1206. ;        cucl    lift argument, then skip
  1207. ;
  1208. ;    Three entry points can be used according to the variant
  1209. ;    of the compiling operator C desired.  One of them could
  1210. ;    also be used by a main program.
  1211. ;
  1212. ;        emce    lift pushdown, open block, compile
  1213. ;        emcx    compile a sequence of subroutines
  1214. ;
  1215. ;    =======================================================
  1216.  
  1217.     end
  1218.