home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol164 / rec86.asm < prev    next >
Encoding:
Assembly Source File  |  1984-04-29  |  27.5 KB  |  991 lines

  1.  
  2.  
  3. ;    *******************************************************
  4. ;    *  REC module containing the REC nucleus and some of  *
  5. ;    *  the really indispensable operators and predicates  *
  6. ;    *  such as those defining two byte binary numbers and *
  7. ;    *  ASCII constant strings.  The model of a pushdown   *
  8. ;    *  list is assumed in the expectation that additional *
  9. ;    *  operators and predicates will also follow reversed *
  10. ;    *  Polish notation. There are additionally many small *
  11. ;    *  service routines which may be used externally.     *
  12. ;    *                              *
  13. ;    *  The source language for these programs is the one  *
  14. ;    *  introduced by SORCIM for ACT86.COM, which is not   *
  15. ;    *  quite the same that Intel's ASM86 uses.          *
  16. ;    *                              *
  17. ;    *  REC86 was obtained from the previously existing    *
  18. ;    *  REC80 by applying SORCIM's TRANS86 translator and  *
  19. ;    *  then adjusting the resulting code manually. It is  *
  20. ;    *  intended that REC86 will be functionally identical *
  21. ;    *  to REC80. All error corrections, additions, or     *
  22. ;    *  alterations are made simultaneously to the two     *
  23. ;    *  programs, when they are not purely cosmetic.          *
  24. ;    *  Braces, creating a different style of subroutine   *
  25. ;    *  definition, were incorporated in REC at the time   *
  26. ;    *  the translation to the Intel 8086 was made.          *
  27. ;    *                              *
  28. ;    *  REC86 contains the following compiling entries:    *
  29. ;    *                              *
  30. ;    *    reclp    left parenthesis              *
  31. ;    *    recco    colon                      *
  32. ;    *    recsc    semicolon                  *
  33. ;    *    recrp    right parenthesis              *
  34. ;    *    recop    operator                  *
  35. ;    *    recpr    predicate                  *
  36. ;    *    recsq    single quotes                  *
  37. ;    *    recdq    double quotes                  *
  38. ;    *    reccm    comments                  *
  39. ;    *    reco1    operator with one ASCII parameter     *
  40. ;    *    recp1    predicate with one ASCII parameter    *
  41. ;    *    recms    unary minus sign              *
  42. ;    *    recdd    decimal digit                  *
  43. ;    *                              *
  44. ;    *  REC86 contains the following operators and         *
  45. ;    *  predicates:                          *
  46. ;    *                              *
  47. ;    *    '    single quote                  *
  48. ;    *    "    double quote                  *
  49. ;    *    nu    two byte decimal number              *
  50. ;    *    O    decimal ASCII string to number          *
  51. ;    *    #    number to decimal ASCII string          *
  52. ;    *    L    erase argument (lift)              *
  53. ;    *    @    execute subroutine              *
  54. ;    *    {    initiate program segment          *
  55. ;    *    }    discontinue program segment          *
  56. ;    *    ?    report detected error              *
  57. ;    *                              *
  58. ;    *  The following are initialization programs which    *
  59. ;    *  can be called at the outset of a compilation.      *
  60. ;    *                              *
  61. ;    *    inre    initialize REC temporary registers    *
  62. ;    *                              *
  63. ;    * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  64. ;    *                              *
  65. ;    *        REC86  -  Copyright (C) 1982          *
  66. ;    *       Universidad Autonoma de Puebla          *
  67. ;    *        All Rights Reserved              *
  68. ;    *                              *
  69. ;    *        [Harold V. McIntosh, 25 April 1982]          *
  70. ;    *                              *
  71. ;    14 April 1983 - AR recognizes @@              *
  72. ;    14 April 1983 - cosmetic changes: use of <lea>          *
  73. ;    14 April 1983 - suppress TLU and TLV              *
  74. ;    *******************************************************
  75.  
  76. ;    =======================================================
  77. ;    The nucleus of REC is a compiler for control symbols,
  78. ;    operators and predicates, some auxiliary subroutines,
  79. ;    and an initilazation routine.
  80. ;
  81. ;    The compiler proper uses only the folowing external
  82. ;    references:
  83. ;
  84. ;        RAM storage        xpd, ypd, zpd
  85. ;        I-O routine        read
  86. ;        skip instruction    skp
  87. ;
  88. ;    The RAM storage must be initialized, which may be
  89. ;    accomplished by calling inre.
  90. ;
  91. ;    The location in which the object code is placed is
  92. ;    passed along through the register pair (dx), which is
  93. ;    continually updated to reflect the next available byte.
  94. ;    None of the other registers are either conserved nor
  95. ;    significant after the completion of compilation.
  96. ;
  97. ;    The usage of the registers is the following
  98. ;
  99. ;        pair (cx) contains the execution pointer
  100. ;        pair (dx) contains the object program counter
  101. ;        pair (bx) contains the compiling address
  102. ;
  103. ;    =======================================================
  104.  
  105. ;    Equivalences defining INTEL 8086 instructions and some
  106. ;    constants.
  107.  
  108. CA    equ    0E8H        ;call w/16 bit displacement
  109. JU    equ    0E9H        ;jump w/16 bit displacement
  110. RN    equ    0C3H        ;return w/o change of segment
  111. POBX    equ    05BH        ;pop bx
  112. PUBX    equ    053H        ;push bx
  113. JUBX    equ    0E3FFH        ;jmp bx
  114. PUME    equ    036FFH        ;push direct address
  115. POME    equ    0068FH        ;pop into memory
  116. INBX    equ    043H        ;inc bx
  117. LDME    equ    006C7H        ;ld mem,imm
  118. ZE    equ    0000H        ;zero
  119. FF    equ    00FFH        ;one byte complement of zero
  120.  
  121.  
  122. ;    =============
  123.     org    0100H
  124. ;    =============
  125.  
  126.  
  127.     jmp    main    ;<===============================<<<
  128.  
  129.  
  130. ;    Compile a left parenthesis.
  131.  
  132. RECLP:    pop    bp
  133.     push    ZPD        ;save the linkage to semicolon exits
  134.     push    YPD        ;save the higher linkage to false jumps
  135.     push    XPD        ;save the repeat references
  136.     ld    ax,#ZE        ;initialze the new chains
  137.     sto    ax,ZPD        ;null TRUE exit list
  138.     sto    ax,YPD        ;null FALSE jump list
  139.     sto    dx,XPD        ;new parenthesis level begins here
  140.     jmp    bp
  141.  
  142. ;    Compile a colon.
  143.  
  144. RECCO:    ld    bx,XPD        ;pick up reference to left parenthesis
  145.     sub    bx,dx
  146.     lea    bx,[bx-3]
  147.     call    RECJU        ;and insert a jump to its location
  148.     jmp    RECFY        ;fill in any FALSE predicate jumps
  149.  
  150. ;    Compile a semicolon.
  151.  
  152. RECSC:    ld    bx,ZPD        ;pick up link to TRUE exit chain
  153.     call    RECJU        ;insert this one on it too
  154.     sto    bx,ZPD        ;store it as the new head of the chain
  155.     jmp    RECFY        ;fill in any FALSE predicate jumpe
  156.  
  157. ;    Compile an operator.
  158.  
  159. RECOP:    xchg    bx,dx
  160.     stob    #CA,[bx]    ;store the 8086 code for a call
  161.     lea    bx,[bx+3]    ;advance (dx) to receive next byte
  162.     sub    cx,bx
  163.     sto    cx,[bx-2]
  164.     xchg    bx,dx
  165.     ret
  166.  
  167. ;    Compile a predicate.
  168.  
  169. RECPR:    call    RECOP        ;call its subroutine, same as operator
  170. RECYJ:    ld    bx,YPD        ;linkage to FALSE exits
  171.     call    RECJU        ;incorporate a jump if result FALSE
  172.     sto    bx,YPD        ;update for new head of chain
  173.     ret
  174.  
  175. ;    Compile a right parenthesis.
  176.  
  177. RECRP:    pop    bp        ;recover xpd, which is hidden
  178.     pop    XPD        ;replace it
  179.     cmp    XPD,#ZE
  180.     jz    RECFP        ;if so, continue with recfp
  181.     pop    bx        ;recover wpd
  182.     call    RECJU        ;link expr to ypd on its own level
  183.     push    bx        ;but save pointer until we finish up
  184.     call    RECFY        ;false predicates in last segment
  185.     pop    YPD        ;replace ypd for higher level
  186.     ld    bx,ZPD        ;now we have destination for semicolons
  187.     call    recfc        ;so insert all the correct addresses
  188.     pop    ZPD        ;replace old zpd
  189.     jmp    bp
  190.  
  191. ;    Final right parentheses get a different treatment.
  192.  
  193. RECFP:    mov    bx,dx        ;compile pointer in bx
  194.     stob    #RN,[bx]    ;store a <ret> for false exit
  195.     inc    bx        ;ready for next byte
  196.     push    bx        ;save compile pointer
  197.     ld    dx,#SKP        ;address of skip - TRUE exit from REC
  198.     call    RECFY        ;use it for last segment
  199.     ld    bx,ZPD        ;destination of semicolons now known
  200.     call    recfc        ;so fill out that chain
  201.     pop    dx        ;compile pointer that was saved
  202.     pop    YPD        ;restore old ypd
  203.     pop    ZPD        ;restore old zpd
  204.     ret            ;return one level higher than expected
  205.  
  206. ;    Insert a new element in a chain of jmp's which will
  207. ;    eventually have destination addresses.  In the interim
  208. ;    each is given the address of its predecessor. On entry
  209. ;    (dx) holds the address where the instruction will be
  210. ;    stored and (bx) holds the address of its predecessor.
  211. ;    On exit, (dx) is incremented by 3 to point to the next
  212. ;    free byte, and (bx) has the starting value of (dx).
  213.  
  214. RECJU:    xchg    bx,dx        ;(bx) and (dx) exchanged is better
  215.     stob    #JU,[bx]    ;store the jump instruction
  216.     inc    bx
  217.     sto    dx,[bx]        ;store old link
  218.     lea    dx,[bx+2]
  219.     ret
  220.  
  221. ;    When the destination of a linked chain of jumps is
  222. ;    finally known, the destination can be substituted into
  223. ;    each one of the links.  On entry, (bx) contains the
  224. ;    address of the first link unless it is zero signifying
  225. ;    a null chain.
  226.  
  227. recfc:    or    bx,bx        ;test for end of chain
  228.     jz    recfx        ;if address is zero, chain ends
  229.     mov    ax,dx
  230.     dec    ax
  231.     dec    ax
  232. recfi:    ld    cx,[bx]        ;save next link
  233.     sto    ax,[bx]        ;store destination
  234.     sub    [bx],bx
  235.     mov    bx,cx        ;update link
  236.     or    bx,bx
  237.     jnz    recfi        ;continue
  238. recfx:    ret
  239.  
  240. ;    Call recfc with the intention of filling the y chain.
  241.  
  242. RECFY:    ld    bx,YPD
  243.     call    recfc
  244.     sto    bx,YPD
  245.     ret
  246.  
  247. ;    Subroutine which will initialize the temporary
  248. ;    registers used by the REC compiler.
  249.  
  250. INRE:    ld    bx,#ZE
  251.     sto    bx,XPD
  252.     sto    bx,YPD
  253.     sto    bx,ZPD
  254.     ret
  255.  
  256.  
  257. ;    =======================================================
  258. ;    The following are specialized compiling subroutines
  259. ;    which apply to special structures and depend on the
  260. ;    model of a pushdown list with a linked chain structure
  261. ;    and special registers px and py delimiting the top
  262. ;    segment on the chain.
  263. ;    =======================================================
  264.  
  265. ;    -------------------------------------------------------
  266. ;    Compilation of quoted expressions.  Single and double
  267. ;    quotes may alternate with one another to an arbitrary
  268. ;    depth.  Both kinds of quotes are executed in the same
  269. ;    way, by loading the quoted expression from the program
  270. ;    onto the pushdown list.
  271. ;    -------------------------------------------------------
  272.  
  273. ;    Compile single quotes.
  274.  
  275. RECSQ:    call    RECOP        ;record call to qu
  276.     inc    dx        ;set aside two bytes
  277.     inc    dx        ;to hold length of ASCII chain
  278.     push    dx        ;keep beginning for future reference
  279.     push    QUEN        ;delay cleanup until ret
  280. SQ:    ld    bp,read        ;read the next character
  281.     call    bp
  282.     cmp    al,#''''    ;test for single quote
  283.     jz    SQ2        ;if so go after entire chain
  284.     cmp    al,#'"'        ;test for double quotes
  285.     jnz    SQ1
  286.     call    DQ1        ;if so, read it all
  287. SQ1:    xchg    bx,dx
  288.     sto    al,[bx]
  289.     xchg    bx,dx        ;otherwise keep on storing
  290.     inc    dx        ;and advancing pointer
  291.     jmp    SQ        ;go after next character
  292. SQ2:    ret
  293.  
  294. ;    Compile double quotes.
  295.  
  296. RECDQ:    call    RECOP        ;record call to qu
  297.     inc    dx        ;set aside two bytes
  298.     inc    dx        ;to hold length of chain
  299.     push    dx        ;put chain origin away for reference
  300.     push    QUEN        ;delay cleanup until ret
  301. DQ:    ld    bp,read        ;read the next character
  302.     call    bp
  303.     cmp    al,#'"'        ;test for double quotes
  304.     jz    DQ2        ;if so, chain finished
  305.     cmp    al,#''''    ;check for single quotes
  306.     jnz    DQ1
  307.     call    SQ1        ;if so go after whole chain
  308. DQ1:    xchg    bx,dx
  309.     sto    al,[bx]
  310.     xchg    bx,dx        ;otherwise keep on storing
  311.     inc    dx        ;and advancing pointer
  312.     jmp    DQ        ;go after next character
  313. DQ2:    ret
  314.  
  315. ;    Cleanup for both quote compilers.
  316.  
  317. QUEN:    dw    ENQU        ;for the direct push
  318. ENQU:    pop    bx        ;beginning of chain in (bx)
  319.     mov    cx,dx
  320.     sub    cx,bx
  321.     sto    cx,[bx-2]    ;store length
  322.     ret
  323.  
  324. ;    (') (")   Execute single or double quote.
  325.  
  326. QU:    pop    bx        ;get call location off the 8080 stack
  327.     ld    cx,[bx]        ;count
  328.     inc    bx        ;
  329.     inc    bx        ;
  330.     mov    si,bx        ;save source origin
  331.     add    bx,cx        ;calculate source end = return adress
  332.     push    bx
  333.     call    NARG        ;check space, put dest. pointer in (bx)
  334.     cld
  335.     mov    di,bx
  336.     mov    ax,ds
  337.     mov    es,ax
  338.     mov    ax,cs
  339.     mov    ds,ax
  340.     rep
  341.     movsb
  342.     mov    ax,es
  343.     mov    ds,ax
  344.     sto    di,PY        ;record end of argument
  345.     ret
  346.  
  347. ;    -------------------------------------------------------
  348. ;    Comments are enclosed in square brackets, which must be
  349. ;    balanced.  Code may be disabled by enclosing it in
  350. ;    square brackets, but care must be taken that the
  351. ;    expression so isolated does not contain individual
  352. ;    brackets, such as arguments of arrobas or quoted
  353. ;    brackets, which might disrupt the balance. Since
  354. ;    comments are ignored by the compiler they are not
  355. ;    executed.
  356. ;    -------------------------------------------------------
  357.  
  358. ;    Compile comments by ignoring them.
  359.  
  360. RECCM:    ld    bp,read        ;get next character
  361.     call    bp
  362.     cmp    al,#']'        ;test for closing ]
  363.     jz    RECCX        ;if so we're done
  364.     cmp    al,#'['        ;test for beginning of new level
  365.     jnz    RECCM        ;otherwise keep on reading
  366.     call    RECCM        ;if so go after it recursively
  367.     jmp    RECCM
  368. RECCX:    ret
  369.  
  370. ;    -------------------------------------------------------
  371. ;    Sometimes, notably in compiling arroba as a call to a
  372. ;    subroutine named by a single letter, a parameter will
  373. ;    follow a subroutine call as its calling sequence.
  374. ;    -------------------------------------------------------
  375.  
  376. ;    Operator with one ASCII parameter.
  377.  
  378. RECO1:    call    RECOP        ;always compile the subroutine call
  379.     ld    bp,read        ;read the parameter
  380.     call    bp
  381.     mov    bx,dx
  382.     sto    al,[bx]        ;store as a 1-byte calling sequence
  383.     inc    dx        ;always ready for next byte
  384.     ret
  385.  
  386. ;    Predicate with one ASCII parameter.
  387.  
  388. RECP1:    call    RECO1        ;compile as the analogous operator
  389.     jmp    RECYJ        ;then take account of false exit
  390.  
  391. ;    -------------------------------------------------------
  392. ;    Decimal numbers are of such frequent occurrence in the
  393. ;    form of counters, arguments, or just data that it is
  394. ;    convenient to compile them on sight without requiring
  395. ;    any special delimiters.  Likewise, negative numbers are
  396. ;    easier to designate using a minus sign than using their
  397. ;    modular form, but this should not prevent the use of a
  398. ;    minus sign as an operator.
  399. ;    -------------------------------------------------------
  400.  
  401. ;    Compile a minus sign. This involves determining whether
  402. ;    it is followed immediately by a decimal digit, in which
  403. ;    case it is compiled as part of a negative number.
  404.  
  405. RECMS:    ld    bp,read        ;read in one byte
  406.     call    bp
  407.     call    MS1        ;decide whether it is a digit
  408.     push    ax        ;it was not, save it
  409.     call    RECOP        ;compile call to binary minus
  410.     pop    ax        ;recover the extra character
  411.     jmp    skp86        ;skip because we have next character
  412.  
  413. MS1:    call    RND        ;return if not digit
  414.     inc    sp        ;erase call to ms1
  415.     inc    sp        ;
  416.     call    RECDS        ;read and convert digit string
  417.     ld    cx,GNU        ;fake that it was nu, not ms
  418.     push    ax        ;save terminating character
  419.     neg    bx        ;negate (bx)
  420.     jmp    DD1        ;continue as though positive number
  421.  
  422. GNU:    DW    NU
  423.  
  424. ;    Compile a decimal digit, which requires reading any
  425. ;    further digits which follow, and saving the terminator.
  426.  
  427. RECDD:    ror    al        ;undo multiplication by 4
  428.     ror    al        ;
  429.     push    cx        ;save execution address
  430.     call    RECDS        ;read and transform rest of digits
  431.     pop    cx        ;recover execution address
  432.     push    ax        ;recover terminating character
  433. DD1:    call    RECOP        ;compile subroutine call
  434.     xchg    bx,dx        ;(dx) and (bx) must be interchanged
  435.     sto    dx,[bx]        ;put low order byte in calling sequence
  436.     inc    bx        ;
  437.     inc    bx        ;ready for next byte
  438.     xchg    bx,dx        ;put (dx) and (bx) back as they were
  439.     pop    ax        ;recover terminating character
  440.     jmp    skp86        ;skip over character read call
  441.  
  442. ;    Multiply (bx) by 10 and add A.  (dx) is conserved.
  443.  
  444. TXP:    mov    cx,bx        ;transfer (bx) to (cx)
  445.     add    bx,bx        ;multiply (bx) by 2
  446.     add    bx,bx        ;another 2 makes 4
  447.     add    bx,cx        ;the original (bx) makes 5
  448.     add    bx,bx        ;another 2 makes 10
  449.     add    bx,ax        ;add in the accumulator
  450.     ret
  451.  
  452. ;    The heart of number compilation.
  453.  
  454. RECDS:    and    al,#0FH        ;mask ASCII down to binary value
  455.     mov    BL,al        ;put it into register pair (bx)
  456.     ld    BH,#ZE        ;fill out H with a zero
  457. RD1:    ld    bp,read        ;read the next character
  458.     call    bp
  459.     call    RND        ;quit if it is not another digit
  460.     call    TXP        ;multiply (bx) by ten and add A
  461.     jmp    RD1        ;continuing while digits keep coming
  462.  
  463. ;    Execute a number, which means load it on pdl.
  464.  
  465. NU:    ld    cx,#2        ;two bytes will be required
  466.     call    NARG        ;close last argument, open new
  467.     pop    dx        ;get beginning of calling sequence
  468.     xchg    bx,dx
  469.     ld    ax,[bx]
  470.     xchg    bx,dx
  471.     sto    ax,[bx]        ;and copy it over
  472.     inc    dx        ;on to the high order byte
  473.     inc    bx        ;and the place to store it
  474.     inc    dx        ;move on to program continuation
  475.     inc    bx        ;always leave PDL ready for next byte
  476.     push    dx        ;put back the return address
  477.     sto    bx,PY        ;mark end of the argument
  478.     ret
  479.  
  480. ;    (O) Transform an ASCII character string on the PDL into
  481. ;    a two-byte number.  Predicate - false if the argument
  482. ;    is not a digit string or null, leaving the argument
  483. ;    unchanged.
  484.  
  485. UCO:    ld    cx,#2        ;two bytes are required
  486.     call    OARG        ;check that they are available
  487.     ld    bx,PY        ;fetch the end of the argument string
  488.     stob    #ZE,[bx]    ;put a zero there to mark its end
  489.     ld    dx,PX        ;load pointer to argument string
  490.     ld    bx,#ZE        ;zero in (bx) to start the conversion
  491. O1:    xchg    bx,dx
  492.     ld    al,[bx]
  493.     xchg    bx,dx        ;fetch one character
  494.     inc    dx        ;get ready for next
  495.     or    al,al        ;test for zero
  496.     jz    O2        ;go to accumulation phase
  497.     call    RND        ;FALSE, chain unaltered if non-digit
  498.     call    TXP        ;otherwise continue to work up value
  499.     jmp    O1        ;and keep on reading bytes
  500. O2:    xchg    bx,dx        ;safeguard converted number in (dx)
  501.     ld    bx,PX        ;get pointer to argument
  502.     sto    dx,[bx]        ;store low byte
  503.     inc    bx        ;increment pointer
  504.     inc    bx        ;increment pointer again
  505.     sto    bx,PY        ;store to close argument
  506.     jmp    SKP        ;TRUE exit from predicate
  507.  
  508. ;    (#)  Change two-byte binary number into a decimal-based
  509. ;    ASCII string without sign. The special cases of a zero-
  510. ;    byte or a one-byte argument are also considered.
  511.  
  512. NS:    ld    cx,#05H        ;five bytes may be required
  513.     call    OARG        ;reuse the old argument
  514.     ld    cx,PY
  515.     ld    bx,PX
  516.     sub    cx,bx
  517.     ld    dx,#ZE        ;put zero in (dx) for default
  518.     jcxz    NS1        ;load nothing
  519.     ld    dl,[bx]        ;load low byte
  520.     dec    cx        ;test for one byte
  521.     jcxz    NS1        ;only byte and it's loaded
  522.     ld    dh,[bx+1]    ;load high byte
  523. NS1:    push    bx        ;save pointer for ASCII string
  524.     ld    al,#'0'        ;prepare to write a zero
  525.     ld    bx,#-10000    ;will there be 5 digits?
  526.     add    bx,dx        ;
  527.     jc    NS2        ;
  528.     ld    bx,#-1000    ;will there be 4 digits?
  529.     add    bx,dx        ;
  530.     jc    NS3        ;
  531.     ld    bx,#-100    ;will there be 3 digits?
  532.     add    bx,dx        ;
  533.     jc    NS4        ;
  534.     ld    bx,#-10        ;will there be 2 digits?
  535.     add    bx,dx        ;
  536.     jc    NS5        ;
  537.     jmp    NS6        ;write one no matter what
  538. NS2:    ld    cx,#-10000    ;ten thousands digit
  539.     call    NSA        ;
  540. NS3:    ld    cx,#-1000    ;thousands digit
  541.     call    NSA        ;
  542. NS4:    ld    cx,#-100    ;hundreds digit
  543.     call    NSA        ;
  544. NS5:    ld    cx,#-10        ;tens digit
  545.     call    NSA        ;
  546. NS6:    add    al,dl        ;units digit
  547.     pop    bx        ;recover pointer to PDL
  548.     sto    al,[bx]        ;store the digit
  549.     inc    bx        ;position pointer for next byte
  550.     sto    bx,PY        ;done, store it as terminator
  551.     ret
  552.  
  553. NSA:    mov    bx,cx        ;put power of ten in (bx)
  554.     add    bx,dx        ;subtract it once
  555.     jnc    NSB        ;can't subtract
  556.     inc    al        ;increase the count
  557.     xchg    bx,dx        ;put diminished number in (dx)
  558.     jmp    NSA        ;repeat the cycle
  559. NSB:    pop    bp        ;get <call nsa> return address
  560.     pop    bx
  561.     sto    al,[bx]        ;store new digit
  562.     inc    bx        ;advance pointer
  563.     ld    al,#'0'        ;load a fresh ASCII zero
  564.     push    bx
  565.     jmp    bp        ;return to the <call nsa>
  566.  
  567. ;    =======================================================
  568. ;    Some simple procedures to compile REC expressions into
  569. ;    subroutines, deposit a reference to them in a symbol
  570. ;    table, and eventually to recover the space and erase
  571. ;    the symbol table reference.
  572. ;    =======================================================
  573.  
  574. ;    Table search. The table whose address is stored at fxt
  575. ;    is consulted for its pair of addresses at position 4*A.
  576. ;    Thus on entry, A holds the table index.  This table
  577. ;    alternates the address of a compiling subroutine with
  578. ;    the execution address of the same entry.  On exit, (cx)
  579. ;    holds the execution address, (dx) is preserved, and a
  580. ;    jump is made to the compiling address.
  581.  
  582. rects:    ld    ah,#ZE
  583.     add    ax,ax
  584.     add    ax,ax
  585.     ld    bx,FXT        ;load base address of table
  586.     add    bx,ax
  587.     push    [bx]        ;put the first entry in (cx)
  588.     ld    cx,[bx+2]    ;table pointer is going
  589.     ret            ;then off to the compilation
  590.  
  591. ;    Pick out left delimiters: (, {, or [.
  592.  
  593. left:    ld    bp,read
  594.     call    bp
  595.     cmp    al,#'('
  596.     jz    eft
  597.     cmp    al,#'{'
  598.     jz    eft
  599.     cmp    al,#'['
  600.     jnz    left
  601.     call    reccm
  602.     jmps    left
  603. eft:    ret
  604.  
  605. ;    A main program to compile characters one by one as
  606. ;    they are read in from the console.  Note that the
  607. ;    compiling programs invoked by rects can generate skips
  608. ;    when they have already read the following character.
  609. ;    This occurs most notably when compiling digits. Also
  610. ;    note that svc86 normalizes characters when it accepts
  611. ;    them.
  612.  
  613. recre:    ld    bp,read        ;read a character from whereever
  614.     call    bp
  615. recrr:    call    svc86        ;check for space, control character
  616.     jmp    recre        ;not valid, go back for another
  617.     call    rects        ;look up in table and compile it
  618.     jmp    recre        ;read another character and repeat
  619.     jmp    recrr        ;repeat but next character already read
  620.  
  621. ;    A subroutine which will pass over comments, and wait
  622. ;    for an opening left parenthesis or brace before compiling
  623. ;    a REC expression.
  624.  
  625. EMCE:    call    UCL        ;entry here erases an argument from PDL
  626. EMCX:    call    left         ;get a character from whereever
  627.     ld    dx,C1
  628.     ld    bx,C1
  629.     mov    bp,sp
  630.     xchg    bx,[bp]
  631.     push    bx
  632.     call    recrr        ;compiling prgrm one char already read
  633.     sto    dx,C1
  634.     ret
  635.  
  636. EMCU:    pop    dx
  637.     pop    bx
  638.     push    dx
  639.     push    bx
  640.     ld    dx,#EMCV
  641.     push    dx
  642.     jmp    bx
  643. EMCV:    jmp    EMCW
  644.     pop    C1
  645.     jmp    skp86
  646. EMCW:    pop    C1
  647.     ret
  648.  
  649. ;    ({) Introduce a series of definitions.
  650.  
  651. LBR:    xchg    bx,dx
  652.     stob    #CA,[bx]    ;insert a call to the executable subroutine
  653.     xchg    bx,dx
  654.     inc    dx
  655.     mov    cx,dx        ;place to put call address - keep in BC
  656.     inc    dx        ;make room
  657.     inc    dx
  658.     call    RECYJ        ;link in the FALSE exit
  659.     call    RECJU
  660.     push    bx        ;keep this address
  661.     push    XPD
  662.     sto    #ZE,XPD        ;this is top level for ensuing subroutines
  663.     ld    bx,#ZE
  664. LB1:    push    dx        ;record entry point to subroutine
  665.     inc    bx        ;increment count of subroutines
  666.     push    bx        ;keep it next to top on stack
  667.     push    cx        ;jump address at entry - keep it on top
  668.     call    left
  669.     call    recrr        ;compile at least one subroutine
  670. LB2:    ld    bp,read        ;get possible name of subroutine
  671.     call    bp
  672.     cmp    al,#'}'        ;no name - we execute this one
  673.     jz    LB3
  674.     call    svc86        ;convert name into serial number
  675.     jmp    LB2        ;punctuation instead of name
  676.     add    al,#' '
  677.     ld    ah,#ZE
  678.     ld    bx,VRT
  679.     add    bx,ax
  680.     add    bx,ax
  681.     pop    cx        ;get this out of the way
  682.     mov    bp,sp
  683.     xchg    bx,[bp]        ;store table address, put subr count in bx
  684.     jmp    LB1
  685. LB3:    cld
  686.     mov    ax,ds
  687.     mov    es,ax
  688.     pop    bx        ;origin of brace compilation
  689.     mov    di,dx
  690.     sto    di,[bx]        ;store displacement at initial jump
  691.     sub    [bx],bx
  692.     dec    [bx]
  693.     dec    [bx]
  694.     pop    cx        ;number of subroutines + 1
  695.     push    cx        ;we'll need it again later
  696.     mov    bp,cx        ;put it in bp too
  697.     dec    bp
  698.     add    bp,bp
  699.     add    bp,bp
  700.     add    bp,sp
  701.     ld    al,#POBX
  702.     stosb
  703.     jmp    LB5
  704. LB4:    ld    ax,#PUME    ;for each defined symbol we insert the
  705.     stos
  706.     ld    ax,[bp]
  707.     stos
  708.     ld    ax,#LDME    ;
  709.     stos
  710.     ld    ax,[bp]
  711.     stos
  712.     ld    ax,[bp+2]
  713.     stos
  714.     sub    bp,#4        ;we read the stack backwards
  715. LB5:    loop    LB4
  716.     ld    al,#PUBX
  717.     stosb
  718.     ld    al,#CA
  719.     stosb
  720.     pop    cx
  721.     pop    ax
  722.     sub    ax,di
  723.     dec    ax
  724.     dec    ax
  725.     stos
  726.     push    cx
  727.     ld    al,#JU        ;    jmp    $+6
  728.     stosb
  729.     push    di        ;    inx    h
  730.     inc    di        ;    inx    h
  731.     inc    di        ;    inx    h
  732.     ld    al,#POBX
  733.     stosb
  734.     ld    al,#INBX
  735.     stosb
  736.     stosb
  737.     stosb
  738.     ld    al,#PUBX
  739.     stosb
  740.     pop    bx
  741.     sto    di,[bx]
  742.     sub    [bx],bx
  743.     dec    [bx]
  744.     dec    [bx]
  745.     ld    al,#POBX
  746.     stosb
  747.     pop    cx
  748.     jmp    LB7
  749. LB6:    ld    ax,#POME    ;after an expression in braces is
  750.     stos
  751.     pop    ax
  752.     stos
  753.     inc    sp
  754.     inc    sp
  755. LB7:    loop    LB6
  756.     ld    ax,#JUBX    ;the whole thing is finished off by a return
  757.     stos
  758.     mov    dx,di
  759.     pop    XPD
  760.     pop    bx
  761.     cmp    XPD,#ZE
  762.     jz    LB8
  763.     sto    dx,[bx]
  764.     sub    [bx],bx
  765.     dec    [bx]
  766.     dec    [bx]
  767.     ret
  768. LB8:    ld    cx,[bx]
  769.     sto    #SKP,[bx]
  770.     sub    [bx],bx
  771.     dec    [bx]
  772.     dec    [bx]
  773.     sub    bx,cx
  774.     sto    dx,[bx]
  775.     sub    [bx],bx
  776.     dec    [bx]
  777.     dec    [bx]
  778.     xchg    bx,dx
  779.     sto    #JUBX,[bx]
  780.     inc    bx
  781.     inc    bx
  782.     xchg    bx,dx
  783.     inc    sp
  784.     inc    sp
  785.     ret
  786.  
  787. ;    (@) Subroutine which will transform an ASCII character
  788. ;    into a table reference, and then jump to the address
  789. ;    so encountered.  This is essentially REC's subroutine
  790. ;    call mechanism, necessarily a predicate since it calls
  791. ;    a REC expression, which is itself a predicate.
  792.  
  793. AR:    pop    bx        ;entry if name is a parameter
  794.     ld    al,[bx]        ;read the calling sequence
  795.     inc    bx        ;advance pointer for return
  796.     push    bx        ;put it back on 8080 stack
  797.     cmp    al,#'@'
  798.     jnz    XAR
  799. NAR:    ld    bx,PX        ;entry if subroutine index is argument
  800.     ld    al,[bx]
  801.     call    UCL
  802. XAR:    ld    ah,#ZE
  803.     add    ax,ax
  804.     mov    di,ax
  805.     ld    bx,VRT        ;entry when index is in register A
  806.     jmp    [bx+di]        ;then use it as jump address
  807.  
  808. ;    =======================================================
  809. ;    Some general service routines.
  810. ;    =======================================================
  811.  
  812. ;    Skip on valid character, meaning, not control symbol.
  813. ;    If valid, 20H (space) is subtracted, making A = 1, etc.
  814.  
  815. svc86:    cmp    al,#'!'        ;reject space, excl is lower limit
  816.     jc    sv
  817.     cmp    al,#7FH        ;seven bits is upper limit
  818.     jnc    sv
  819.     sub    al,#' '        ;normalize to begin with (excl) = 1
  820.     pop    bp
  821.     inc    bp
  822.     inc    bp
  823.     jmp    bp
  824. sv:    ret            ;don't skip for control or flag bit
  825.  
  826. ;    Return if not decimal. A unchanged if not decimal, else
  827. ;    reduced to binary.
  828.  
  829. RND:    cmp    al,#':'        ;colon follows 9 in ASCII alphabet
  830.     jnc    RTN
  831.     cmp    al,#'0'        ;ASCII zero is lower limit
  832.     jc    RTN
  833.     sub    al,#'0'        ;normalize to get binary values
  834.     ld    ah,#ze        ;zero for uncomplicated arithmetic
  835.     ret
  836. RTN:    inc    sp
  837.     inc    sp
  838.     ret
  839.  
  840. ;    Second level return on error.
  841.  
  842. RR2:    pop    bx        ;entry to clear two items from PDL
  843.     mov    bp,sp
  844.     xchg    bx,[bp]        ;
  845. RR1:    pop    bx        ;entry to clear one item from PDL
  846.     mov    bp,sp
  847.     xchg    bx,[bp]        ;
  848. RER:    pop    ax        ;site where ther error occurred
  849.     cmp    ER,#ZE        ;only record the first error
  850.     jnz    RRR
  851.     sto    ax,ER
  852. RRR:    ret
  853.  
  854. ;    (?)  Test whether an error has been reported: predicate
  855. ;    which is true if er is nonzero, in which case it will
  856. ;    reset er.  It will also, if TRUE, place the calling
  857. ;    address of the last reported error on the pushdown
  858. ;    list.  If false, only a FALSE return is generated. Note
  859. ;    the ironic circumstance that, if PDL is exhausted, qm
  860. ;    can generate an error trying to report an error - but
  861. ;    the TRUE result will still be valid.
  862.  
  863. QM:    cmp    ER,#ZE        ;test the error cell
  864.     jz    QQ        ;FALSE return if no error
  865.     ld    cx,#2        ;we want two bytes for error address
  866.     call    NARG        ;check space, prepare for new argument
  867.     ld    ax,ER        ;fetch error address
  868.     sto    ax,[bx]        ;transfer it to REC PDL
  869.     inc    bx        ;
  870.     inc    bx        ;pointer must always advance
  871.     sto    bx,PY        ;end of the argument
  872.     sto    #ZE,ER        ;reset ER
  873.     jmp    SKP        ;TRUE return - there was an error
  874. QQ:    ret
  875.  
  876. ;    Generate a skip (skp), which is often combined with the
  877. ;    erasure of an argument on the pushdown list (cucl).
  878.  
  879. CUCL:    call    UCL        ;erase the top argument
  880. SKP:    xchg    bx,sp
  881.     inc    [bx]        ;assume the skip will be over a
  882.     inc    [bx]        ;three-byte instruction, such as a jump
  883.     inc    [bx]        ;
  884.     xchg    bx,sp
  885.     ret            ;return to the altered address
  886.  
  887. skp86:    xchg    bx,sp
  888.     inc    [bx]
  889.     inc    [bx]
  890.     xchg    bx,sp
  891.     ret
  892.  
  893. ;    Test PDL space beginning at top argument. On entry (cx)
  894. ;    contains the total space required.  On exit, (cx) stays
  895. ;    unchanged, (dx) holds pz, while (bx) holds px+(cx).
  896. ;    If the space is not available, return is made from the
  897. ;    calling program after noting the error.  Otherwise
  898. ;    normal return to the calling program occurs. The likely
  899. ;    use of oarg is to record a result without having to go
  900. ;    through ucl, NARG.
  901.  
  902. OARG:    ld    dx,PZ        ;load limit of PDL
  903.     dec    dx        ;keep one byte margin
  904.     ld    bx,PX        ;load beginning of current argument
  905.     add    bx,cx
  906.     sub    dx,bx
  907.     jc    oar        ;no, note error, quit calling program
  908.     ret            ;yes, continue normally
  909. oar:    call    RER        ;this must be here to get a short jump
  910.  
  911. ;    Check space for, and then set up, a new argument. On
  912. ;    entry, (cx) should contain the amount of additional
  913. ;    space required.  The program will automatically add
  914. ;    two more bytes for the pointer which would close the
  915. ;    argument and then, if the required space is available,
  916. ;    close it, define the new px, and leave its value in
  917. ;    (bx).  (dx) will contain the old value of px to be used
  918. ;    in case the superseded argument is still interesting.
  919. ;    When space is not available, the error return rer is
  920. ;    taken.
  921. ;
  922. ;    The entry RARG can be taken when it is known that
  923. ;    sufficient space is available but the pointers still
  924. ;    have to be set up.
  925.  
  926. NARG:    mov    di,cx
  927.     ld    bx,PY        ;load end of current argument
  928.     lea    ax,[bx+di+3]
  929.     cmp    ax,PZ
  930.     jnc    NRER        ;check available space
  931. RARG:    ld    dx,PX        ;entry if no space check needed
  932.     ld    bx,PY
  933.     sto    dx,[bx]        ;low byte of closing link
  934.     inc    bx        ;on to high byte
  935.     inc    bx        ;beginning of new space
  936.     sto    bx,PX        ;which is recorded by px
  937.     ret            ;and remains in (bx)
  938. NRER:    call    RER
  939.  
  940. ;    (L)  Remove argument from pushdown list. There are no
  941. ;    requirements for entry to ucl.  On exit, (cx) remains
  942. ;    unchanged, (dx) holds the end of the former argument
  943. ;    and (bx) holds the beginning of the former argument -
  944. ;    the one that was exposed when the current argument was
  945. ;    erased. Erasing non-existent arguments creates an error
  946. ;    condition which is noted and ignored.
  947.  
  948. UCL:    ld    bx,PX        ;pointer to current argument
  949.     dec    bx        ;just behind the present
  950.     dec    bx
  951.     ld    dx,[bx]        ;argument is the address
  952.     or    dx,dx        ;so we always test out of caution
  953.     jz    ULE
  954.     sto    bx,PY        ;(bx) now holds end of previous arg.
  955.     sto    dx,PX        ;pointer to beginning of prev. arg.
  956.     xchg    bx,dx
  957.     ret
  958. ULE:    call    RER        ;record error if pointer was zero
  959.  
  960. ;    Null program for undefined operators.
  961.  
  962. NOOP:    ret
  963.  
  964. ;    =======================================================
  965. ;
  966. ;    Some of the service routines, which might be external
  967. ;    references in other modules, are:
  968. ;
  969. ;        oarg    space when reusing an argument
  970. ;        NARG    close old argument, space for new
  971. ;        rarg    same as NARG when space is assured
  972. ;        skp    generic skip
  973. ;        rer    return on error
  974. ;        rr2    rer after popping two addresses
  975. ;        rtn    generic return
  976. ;        ucl    lift argument from PDL (L)
  977. ;        cucl    lift argument, then skip
  978. ;
  979. ;    Three entry points can be used according to the variant
  980. ;    of the compiling operator C desired.  One of them could
  981. ;    also be used by a main program.
  982. ;
  983. ;        emce    lift pushdown, open block, compile
  984. ;        emcx    compile a sequence of subroutines
  985. ;
  986. ;    =======================================================
  987.  
  988.     LINK    PDL86.ASM
  989.  
  990.  
  991.