home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol167 / rec86.a86 < prev    next >
Encoding:
Text File  |  1984-04-29  |  28.4 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. ;    14 January 1984 - 4 explicit jmps's for sorcim.cnv    *
  75. ;    *******************************************************
  76.  
  77. ;    =======================================================
  78. ;    The nucleus of REC is a compiler for control symbols,
  79. ;    operators and predicates, some auxiliary subroutines,
  80. ;    and an initilazation routine.
  81. ;
  82. ;    The compiler proper uses only the folowing external
  83. ;    references:
  84. ;
  85. ;        RAM storage        xpd, ypd, zpd
  86. ;        I-O routine        read
  87. ;        skip instruction    skp
  88. ;
  89. ;    The RAM storage must be initialized, which may be
  90. ;    accomplished by calling inre.
  91. ;
  92. ;    The location in which the object code is placed is
  93. ;    passed along through the register pair (dx), which is
  94. ;    continually updated to reflect the next available byte.
  95. ;    None of the other registers are either conserved nor
  96. ;    significant after the completion of compilation.
  97. ;
  98. ;    The usage of the registers is the following
  99. ;
  100. ;        pair (cx) contains the execution pointer
  101. ;        pair (dx) contains the object program counter
  102. ;        pair (bx) contains the compiling address
  103. ;
  104. ;    =======================================================
  105.  
  106. ;    Equivalences defining INTEL 8086 instructions and some
  107. ;    constants.
  108.  
  109. CA    equ    .0E8H        ;call w/16 bit displacement
  110. JU    equ    .0E9H        ;jump w/16 bit displacement
  111. RN    equ    .0C3H        ;return w/o change of segment
  112. POBX    equ    .05BH        ;pop bx
  113. PUBX    equ    .053H        ;push bx
  114. JUBX    equ    .0E3FFH        ;jmp bx
  115. PUME    equ    .036FFH        ;push direct address
  116. POME    equ    .0068FH        ;pop into memory
  117. INBX    equ    .043H        ;inc bx
  118. LDME    equ    .006C7H        ;ld mem,imm
  119. ZE    equ    .0000H        ;zero
  120. FF    equ    .00FFH        ;one byte complement of zero
  121.  
  122.  
  123. ;    =============
  124.     org    0100H
  125. ;    =============
  126.  
  127.  
  128.     jmp    main    ;<===============================<<<
  129.  
  130.  
  131. ;    Compile a left parenthesis.
  132.  
  133. RECLP:    pop    bp
  134.     push    ZPD        ;save the linkage to semicolon exits
  135.     push    YPD        ;save the higher linkage to false jumps
  136.     push    XPD        ;save the repeat references
  137.     mov    ax,(offset ZE)        ;initialze the new chains
  138.     mov    ZPD,ax        ;null TRUE exit list
  139.     mov    YPD,ax        ;null FALSE jump list
  140.     mov    XPD,dx        ;new parenthesis level begins here
  141.     jmp    bp
  142.  
  143. ;    Compile a colon.
  144.  
  145. RECCO:    mov    bx,XPD        ;pick up reference to left parenthesis
  146.     sub    bx,dx
  147.     lea    bx,-3[bx]
  148.     call    RECJU        ;and insert a jump to its location
  149.     jmp    RECFY        ;fill in any FALSE predicate jumps
  150.  
  151. ;    Compile a semicolon.
  152.  
  153. RECSC:    mov    bx,ZPD        ;pick up link to TRUE exit chain
  154.     call    RECJU        ;insert this one on it too
  155.     mov    ZPD,bx        ;store it as the new head of the chain
  156.     jmp    RECFY        ;fill in any FALSE predicate jumpe
  157.  
  158. ;    Compile an operator.
  159.  
  160. RECOP:    xchg    bx,dx
  161.     mov    byte ptr [bx],(offset CA)    ;store the 8086 code for a call
  162.     lea    bx,3[bx]    ;advance (dx) to receive next byte
  163.     sub    cx,bx
  164.     mov    -2[bx],cx
  165.     xchg    bx,dx
  166.     ret
  167.  
  168. ;    Compile a predicate.
  169.  
  170. RECPR:    call    RECOP        ;call its subroutine, same as operator
  171. RECYJ:    mov    bx,YPD        ;linkage to FALSE exits
  172.     call    RECJU        ;incorporate a jump if result FALSE
  173.     mov    YPD,bx        ;update for new head of chain
  174.     ret
  175.  
  176. ;    Compile a right parenthesis.
  177.  
  178. RECRP:    pop    bp        ;recover xpd, which is hidden
  179.     pop    XPD        ;replace it
  180.     cmp    XPD,(offset ZE)
  181.     jz    RECFP        ;if so, continue with recfp
  182.     pop    bx        ;recover wpd
  183.     call    RECJU        ;link expr to ypd on its own level
  184.     push    bx        ;but save pointer until we finish up
  185.     call    RECFY        ;false predicates in last segment
  186.     pop    YPD        ;replace ypd for higher level
  187.     mov    bx,ZPD        ;now we have destination for semicolons
  188.     call    recfc        ;so insert all the correct addresses
  189.     pop    ZPD        ;replace old zpd
  190.     jmp    bp
  191.  
  192. ;    Final right parentheses get a different treatment.
  193.  
  194. RECFP:    mov    bx,dx        ;compile pointer in bx
  195.     mov    byte ptr [bx],(offset RN)    ;store a <ret> for false exit
  196.     inc    bx        ;ready for next byte
  197.     push    bx        ;save compile pointer
  198.     mov    dx,(offset SKP)        ;address of skip - TRUE exit from REC
  199.     call    RECFY        ;use it for last segment
  200.     mov    bx,ZPD        ;destination of semicolons now known
  201.     call    recfc        ;so fill out that chain
  202.     pop    dx        ;compile pointer that was saved
  203.     pop    YPD        ;restore old ypd
  204.     pop    ZPD        ;restore old zpd
  205.     ret            ;return one level higher than expected
  206.  
  207. ;    Insert a new element in a chain of jmp's which will
  208. ;    eventually have destination addresses.  In the interim
  209. ;    each is given the address of its predecessor. On entry
  210. ;    (dx) holds the address where the instruction will be
  211. ;    stored and (bx) holds the address of its predecessor.
  212. ;    On exit, (dx) is incremented by 3 to point to the next
  213. ;    free byte, and (bx) has the starting value of (dx).
  214.  
  215. RECJU:    xchg    bx,dx        ;(bx) and (dx) exchanged is better
  216.     mov    byte ptr [bx],(offset JU)    ;store the jump instruction
  217.     inc    bx
  218.     mov    [bx],dx        ;store old link
  219.     lea    dx,2[bx]
  220.     ret
  221.  
  222. ;    When the destination of a linked chain of jumps is
  223. ;    finally known, the destination can be substituted into
  224. ;    each one of the links.  On entry, (bx) contains the
  225. ;    address of the first link unless it is zero signifying
  226. ;    a null chain.
  227.  
  228. recfc:    or    bx,bx        ;test for end of chain
  229.     jz    recfx        ;if address is zero, chain ends
  230.     mov    ax,dx
  231.     dec    ax
  232.     dec    ax
  233. recfi:    mov    cx,[bx]        ;save next link
  234.     mov    [bx],ax        ;store destination
  235.     sub    word ptr [bx],bx
  236.     mov    bx,cx        ;update link
  237.     or    bx,bx
  238.     jnz    recfi        ;continue
  239. recfx:    ret
  240.  
  241. ;    Call recfc with the intention of filling the y chain.
  242.  
  243. RECFY:    mov    bx,YPD
  244.     call    recfc
  245.     mov    YPD,bx
  246.     ret
  247.  
  248. ;    Subroutine which will initialize the temporary
  249. ;    registers used by the REC compiler.
  250.  
  251. INRE:    mov    bx,(offset ZE)
  252.     mov    XPD,bx
  253.     mov    YPD,bx
  254.     mov    ZPD,bx
  255.     ret
  256.  
  257.  
  258. ;    =======================================================
  259. ;    The following are specialized compiling subroutines
  260. ;    which apply to special structures and depend on the
  261. ;    model of a pushdown list with a linked chain structure
  262. ;    and special registers px and py delimiting the top
  263. ;    segment on the chain.
  264. ;    =======================================================
  265.  
  266. ;    -------------------------------------------------------
  267. ;    Compilation of quoted expressions.  Single and double
  268. ;    quotes may alternate with one another to an arbitrary
  269. ;    depth.  Both kinds of quotes are executed in the same
  270. ;    way, by loading the quoted expression from the program
  271. ;    onto the pushdown list.
  272. ;    -------------------------------------------------------
  273.  
  274. ;    Compile single quotes.
  275.  
  276. RECSQ:    call    RECOP        ;record call to qu
  277.     inc    dx        ;set aside two bytes
  278.     inc    dx        ;to hold length of ASCII chain
  279.     push    dx        ;keep beginning for future reference
  280.     push    QUEN        ;delay cleanup until ret
  281. SQ:    mov    bp,read        ;read the next character
  282.     call    bp
  283.     cmp    al,''''    ;test for single quote
  284.     jz    SQ2        ;if so go after entire chain
  285.     cmp    al,'"'        ;test for double quotes
  286.     jnz    SQ1
  287.     call    DQ1        ;if so, read it all
  288. SQ1:    xchg    bx,dx
  289.     mov    [bx],al
  290.     xchg    bx,dx        ;otherwise keep on storing
  291.     inc    dx        ;and advancing pointer
  292.     jmp    SQ        ;go after next character
  293. SQ2:    ret
  294.  
  295. ;    Compile double quotes.
  296.  
  297. RECDQ:    call    RECOP        ;record call to qu
  298.     inc    dx        ;set aside two bytes
  299.     inc    dx        ;to hold length of chain
  300.     push    dx        ;put chain origin away for reference
  301.     push    QUEN        ;delay cleanup until ret
  302. DQ:    mov    bp,read        ;read the next character
  303.     call    bp
  304.     cmp    al,'"'        ;test for double quotes
  305.     jz    DQ2        ;if so, chain finished
  306.     cmp    al,''''    ;check for single quotes
  307.     jnz    DQ1
  308.     call    SQ1        ;if so go after whole chain
  309. DQ1:    xchg    bx,dx
  310.     mov    [bx],al
  311.     xchg    bx,dx        ;otherwise keep on storing
  312.     inc    dx        ;and advancing pointer
  313.     jmp    DQ        ;go after next character
  314. DQ2:    ret
  315.  
  316. ;    Cleanup for both quote compilers.
  317.  
  318. QUEN    dw    ENQU        ;for the direct push
  319. ENQU:    pop    bx        ;beginning of chain in (bx)
  320.     mov    cx,dx
  321.     sub    cx,bx
  322.     mov    -2[bx],cx    ;store length
  323.     ret
  324.  
  325. ;    (') (")   Execute single or double quote.
  326.  
  327. QU:    pop    bx        ;get call location off the 8080 stack
  328.     mov    cx,[bx]        ;count
  329.     inc    bx        ;
  330.     inc    bx        ;
  331.     mov    si,bx        ;save source origin
  332.     add    bx,cx        ;calculate source end = return adress
  333.     push    bx
  334.     call    NARG        ;check space, put dest. pointer in (bx)
  335.     cld
  336.     mov    di,bx
  337.     mov    ax,ds
  338.     mov    es,ax
  339.     mov    ax,cs
  340.     mov    ds,ax
  341.     repnz    movs    byte [di],[si]
  342.     mov    ax,es
  343.     mov    ds,ax
  344.     mov    PY,di        ;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:    mov    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.     mov    bp,read        ;read the parameter
  380.     call    bp
  381.     mov    bx,dx
  382.     mov    [bx],al        ;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:    mov    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.     mov    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,1        ;undo multiplication by 4
  428.     ror    al,1        ;
  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.     mov    [bx],dx        ;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.     mov    BH,(offset ZE)        ;fill out H with a zero
  457. RD1:    mov    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:    mov    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.     mov    ax,[bx]
  470.     xchg    bx,dx
  471.     mov    [bx],ax        ;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.     mov    PY,bx        ;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:    mov    cx,2        ;two bytes are required
  486.     call    OARG        ;check that they are available
  487.     mov    bx,PY        ;fetch the end of the argument string
  488.     mov    byte ptr [bx],(offset ZE)    ;put a zero there to mark its end
  489.     mov    dx,PX        ;load pointer to argument string
  490.     mov    bx,(offset ZE)        ;zero in (bx) to start the conversion
  491. O1:    xchg    bx,dx
  492.     mov    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.     mov    bx,PX        ;get pointer to argument
  502.     mov    [bx],dx        ;store low byte
  503.     inc    bx        ;increment pointer
  504.     inc    bx        ;increment pointer again
  505.     mov    PY,bx        ;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:    mov    cx,05H        ;five bytes may be required
  513.     call    OARG        ;reuse the old argument
  514.     mov    cx,PY
  515.     mov    bx,PX
  516.     sub    cx,bx
  517.     mov    dx,(offset ZE)        ;put zero in (dx) for default
  518.     jcxz    NS1        ;load nothing
  519.     mov    dl,[bx]        ;load low byte
  520.     dec    cx        ;test for one byte
  521.     jcxz    NS1        ;only byte and it's loaded
  522.     mov    dh,1[bx]    ;load high byte
  523. NS1:    push    bx        ;save pointer for ASCII string
  524.     mov    al,'0'        ;prepare to write a zero
  525.     mov    bx,-10000    ;will there be 5 digits?
  526.     add    bx,dx        ;
  527.     jb    NS2        ;
  528.     mov    bx,-1000    ;will there be 4 digits?
  529.     add    bx,dx        ;
  530.     jb    NS3        ;
  531.     mov    bx,-100    ;will there be 3 digits?
  532.     add    bx,dx        ;
  533.     jb    NS4        ;
  534.     mov    bx,-10        ;will there be 2 digits?
  535.     add    bx,dx        ;
  536.     jb    NS5        ;
  537.     jmp    NS6        ;write one no matter what
  538. NS2:    mov    cx,-10000    ;ten thousands digit
  539.     call    NSA        ;
  540. NS3:    mov    cx,-1000    ;thousands digit
  541.     call    NSA        ;
  542. NS4:    mov    cx,-100    ;hundreds digit
  543.     call    NSA        ;
  544. NS5:    mov    cx,-10        ;tens digit
  545.     call    NSA        ;
  546. NS6:    add    al,dl        ;units digit
  547.     pop    bx        ;recover pointer to PDL
  548.     mov    [bx],al        ;store the digit
  549.     inc    bx        ;position pointer for next byte
  550.     mov    PY,bx        ;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.     jnb    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.     mov    [bx],al        ;store new digit
  562.     inc    bx        ;advance pointer
  563.     mov    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:    mov    ah,(offset ZE)
  583.     add    ax,ax
  584.     add    ax,ax
  585.     mov    bx,FXT        ;load base address of table
  586.     add    bx,ax
  587.     push    word ptr [bx]        ;put the first entry in (cx)
  588.     mov    cx,2[bx]    ;table pointer is going
  589.     ret            ;then off to the compilation
  590.  
  591. ;    Pick out left delimiters: (, {, or [.
  592.  
  593. left:    mov    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:    mov    bp,read        ;read a character from whereever
  614.     call    bp
  615. recrr:    call    svc86        ;check for space, control character
  616.     jmps    recre        ;not valid, go back for another
  617.     call    rects        ;look up in table and compile it
  618.     jmps    recre        ;read another character and repeat
  619.     jmps    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.     mov    dx,C1
  628.     mov    bx,C1
  629.     mov    bp,sp
  630.     xchg    bx,[bp]
  631.     push    bx
  632.     call    recrr        ;compiling prgrm one char already read
  633.     mov    C1,dx
  634.     ret
  635.  
  636. EMCU:    pop    dx
  637.     pop    bx
  638.     push    dx
  639.     push    bx
  640.     mov    dx,(offset 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.     mov    byte ptr [bx],(offset CA)    ;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.     mov    XPD,(offset ZE)        ;this is top level for ensuing subroutines
  663.     mov    bx,(offset 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:    mov    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.     jmps    LB2        ;punctuation instead of name
  676.     add    al,' '
  677.     mov    ah,(offset ZE)
  678.     mov    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.     mov    [bx],di        ;store displacement at initial jump
  691.     sub    word ptr [bx],bx
  692.     dec    word ptr [bx]
  693.     dec    word ptr [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.     mov    al,(offset POBX)
  702.     stos    byte [di]
  703.     jmp    LB5
  704. LB4:    mov    ax,(offset PUME)    ;for each defined symbol we insert the
  705.     stos    word [di]
  706.     mov    ax,[bp]
  707.     stos    word [di]
  708.     mov    ax,(offset LDME)    ;
  709.     stos    word [di]
  710.     mov    ax,[bp]
  711.     stos    word [di]
  712.     mov    ax,2[bp]
  713.     stos    word [di]
  714.     sub    bp,4        ;we read the stack backwards
  715. LB5:    loop    LB4
  716.     mov    al,(offset PUBX)
  717.     stos    byte [di]
  718.     mov    al,(offset CA)
  719.     stos    byte [di]
  720.     pop    cx
  721.     pop    ax
  722.     sub    ax,di
  723.     dec    ax
  724.     dec    ax
  725.     stos    word [di]
  726.     push    cx
  727.     mov    al,(offset JU)        ;    jmp    $+6
  728.     stos    byte [di]
  729.     push    di        ;    inx    h
  730.     inc    di        ;    inx    h
  731.     inc    di        ;    inx    h
  732.     mov    al,(offset POBX)
  733.     stos    byte [di]
  734.     mov    al,(offset INBX)
  735.     stos    byte [di]
  736.     stos    byte [di]
  737.     stos    byte [di]
  738.     mov    al,(offset PUBX)
  739.     stos    byte [di]
  740.     pop    bx
  741.     mov    [bx],di
  742.     sub    word ptr [bx],bx
  743.     dec    word ptr [bx]
  744.     dec    word ptr [bx]
  745.     mov    al,(offset POBX)
  746.     stos    byte [di]
  747.     pop    cx
  748.     jmp    LB7
  749. LB6:    mov    ax,(offset POME)    ;after an expression in braces is
  750.     stos    word [di]
  751.     pop    ax
  752.     stos    word [di]
  753.     inc    sp
  754.     inc    sp
  755. LB7:    loop    LB6
  756.     mov    ax,(offset JUBX)    ;the whole thing is finished off by a return
  757.     stos    word [di]
  758.     mov    dx,di
  759.     pop    XPD
  760.     pop    bx
  761.     cmp    XPD,(offset ZE)
  762.     jz    LB8
  763.     mov    [bx],dx
  764.     sub    word ptr [bx],bx
  765.     dec    word ptr [bx]
  766.     dec    word ptr [bx]
  767.     ret
  768. LB8:    mov    cx,[bx]
  769.     mov    [bx],(offset SKP)
  770.     sub    word ptr [bx],bx
  771.     dec    word ptr [bx]
  772.     dec    word ptr [bx]
  773.     sub    bx,cx
  774.     mov    [bx],dx
  775.     sub    word ptr [bx],bx
  776.     dec    word ptr [bx]
  777.     dec    word ptr [bx]
  778.     xchg    bx,dx
  779.     mov    [bx],(offset JUBX)
  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.     mov    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:    mov    bx,PX        ;entry if subroutine index is argument
  800.     mov    al,[bx]
  801.     call    UCL
  802. XAR:    mov    ah,(offset ZE)
  803.     add    ax,ax
  804.     mov    di,ax
  805.     mov    bx,VRT        ;entry when index is in register A
  806.     jmp    word ptr [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.     jb    sv
  817.     cmp    al,7FH        ;seven bits is upper limit
  818.     jnb    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.     jnb    RTN
  831.     cmp    al,'0'        ;ASCII zero is lower limit
  832.     jb    RTN
  833.     sub    al,'0'        ;normalize to get binary values
  834.     mov    ah,(offset 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,(offset ZE)        ;only record the first error
  850.     jnz    RRR
  851.     mov    ER,ax
  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,(offset ZE)        ;test the error cell
  864.     jz    QQ        ;FALSE return if no error
  865.     mov    cx,2        ;we want two bytes for error address
  866.     call    NARG        ;check space, prepare for new argument
  867.     mov    ax,ER        ;fetch error address
  868.     mov    [bx],ax        ;transfer it to REC PDL
  869.     inc    bx        ;
  870.     inc    bx        ;pointer must always advance
  871.     mov    PY,bx        ;end of the argument
  872.     mov    ER,(offset ZE)        ;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    word ptr [bx]        ;assume the skip will be over a
  882.     inc    word ptr [bx]        ;three-byte instruction, such as a jump
  883.     inc    word ptr [bx]        ;
  884.     xchg    bx,sp
  885.     ret            ;return to the altered address
  886.  
  887. skp86:    xchg    bx,sp
  888.     inc    word ptr [bx]
  889.     inc    word ptr [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:    mov    dx,PZ        ;load limit of PDL
  903.     dec    dx        ;keep one byte margin
  904.     mov    bx,PX        ;load beginning of current argument
  905.     add    bx,cx
  906.     sub    dx,bx
  907.     jb    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.     mov    bx,PY        ;load end of current argument
  928.     lea    ax,3[bx+di]
  929.     cmp    ax,PZ
  930.     jnb    NRER        ;check available space
  931. RARG:    mov    dx,PX        ;entry if no space check needed
  932.     mov    bx,PY
  933.     mov    [bx],dx        ;low byte of closing link
  934.     inc    bx        ;on to high byte
  935.     inc    bx        ;beginning of new space
  936.     mov    PX,bx        ;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:    mov    bx,PX        ;pointer to current argument
  949.     dec    bx        ;just behind the present
  950.     dec    bx
  951.     mov    dx,[bx]        ;argument is the address
  952.     or    dx,dx        ;so we always test out of caution
  953.     jz    ULE
  954.     mov    PY,bx        ;(bx) now holds end of previous arg.
  955.     mov    PX,dx        ;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.     END
  989.  
  990.  
  991.