; ******************************************************* ; * REC module containing the REC nucleus and some of * ; * the really indispensable operators and predicates * ; * such as those defining two byte binary numbers and * ; * ASCII constant strings. The model of a pushdown * ; * list is assumed in the expectation that additional * ; * operators and predicates will also follow reversed * ; * Polish notation. There are additionally many small * ; * service routines which may be used externally. * ; * * ; * The source language for these programs is the one * ; * introduced by SORCIM for ACT86.COM, which is not * ; * quite the same that Intel's ASM86 uses. * ; * * ; * REC86 was obtained from the previously existing * ; * REC80 by applying SORCIM's TRANS86 translator and * ; * then adjusting the resulting code manually. It is * ; * intended that REC86 will be functionally identical * ; * to REC80. All error corrections, additions, or * ; * alterations are made simultaneously to the two * ; * programs, when they are not purely cosmetic. * ; * Braces, creating a different style of subroutine * ; * definition, were incorporated in REC at the time * ; * the translation to the Intel 8086 was made. * ; * * ; * REC86 contains the following compiling entries: * ; * * ; * reclp left parenthesis * ; * recco colon * ; * recsc semicolon * ; * recrp right parenthesis * ; * recop operator * ; * recpr predicate * ; * recsq single quotes * ; * recdq double quotes * ; * reccm comments * ; * reco1 operator with one ASCII parameter * ; * recp1 predicate with one ASCII parameter * ; * recms unary minus sign * ; * recdd decimal digit * ; * * ; * REC86 contains the following operators and * ; * predicates: * ; * * ; * ' single quote * ; * " double quote * ; * nu two byte decimal number * ; * O decimal ASCII string to number * ; * # number to decimal ASCII string * ; * L erase argument (lift) * ; * @ execute subroutine * ; * { initiate program segment * ; * } discontinue program segment * ; * ? report detected error * ; * * ; * The following are initialization programs which * ; * can be called at the outset of a compilation. * ; * * ; * inre initialize REC temporary registers * ; * * ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * REC86 - Copyright (C) 1982 * ; * Universidad Autonoma de Puebla * ; * All Rights Reserved * ; * * ; * [Harold V. McIntosh, 25 April 1982] * ; * * ; 14 April 1983 - AR recognizes @@ * ; 14 April 1983 - cosmetic changes: use of * ; 14 April 1983 - suppress TLU and TLV * ; ******************************************************* ; ======================================================= ; The nucleus of REC is a compiler for control symbols, ; operators and predicates, some auxiliary subroutines, ; and an initilazation routine. ; ; The compiler proper uses only the folowing external ; references: ; ; RAM storage xpd, ypd, zpd ; I-O routine read ; skip instruction skp ; ; The RAM storage must be initialized, which may be ; accomplished by calling inre. ; ; The location in which the object code is placed is ; passed along through the register pair (dx), which is ; continually updated to reflect the next available byte. ; None of the other registers are either conserved nor ; significant after the completion of compilation. ; ; The usage of the registers is the following ; ; pair (cx) contains the execution pointer ; pair (dx) contains the object program counter ; pair (bx) contains the compiling address ; ; ======================================================= ; Equivalences defining INTEL 8086 instructions and some ; constants. CA equ 0E8H ;call w/16 bit displacement JU equ 0E9H ;jump w/16 bit displacement RN equ 0C3H ;return w/o change of segment POBX equ 05BH ;pop bx PUBX equ 053H ;push bx JUBX equ 0E3FFH ;jmp bx PUME equ 036FFH ;push direct address POME equ 0068FH ;pop into memory INBX equ 043H ;inc bx LDME equ 006C7H ;ld mem,imm ZE equ 0000H ;zero FF equ 00FFH ;one byte complement of zero ; ============= org 0100H ; ============= jmp main ;<===============================<<< ; Compile a left parenthesis. RECLP: pop bp push ZPD ;save the linkage to semicolon exits push YPD ;save the higher linkage to false jumps push XPD ;save the repeat references ld ax,#ZE ;initialze the new chains sto ax,ZPD ;null TRUE exit list sto ax,YPD ;null FALSE jump list sto dx,XPD ;new parenthesis level begins here jmp bp ; Compile a colon. RECCO: ld bx,XPD ;pick up reference to left parenthesis sub bx,dx lea bx,[bx-3] call RECJU ;and insert a jump to its location jmp RECFY ;fill in any FALSE predicate jumps ; Compile a semicolon. RECSC: ld bx,ZPD ;pick up link to TRUE exit chain call RECJU ;insert this one on it too sto bx,ZPD ;store it as the new head of the chain jmp RECFY ;fill in any FALSE predicate jumpe ; Compile an operator. RECOP: xchg bx,dx stob #CA,[bx] ;store the 8086 code for a call lea bx,[bx+3] ;advance (dx) to receive next byte sub cx,bx sto cx,[bx-2] xchg bx,dx ret ; Compile a predicate. RECPR: call RECOP ;call its subroutine, same as operator RECYJ: ld bx,YPD ;linkage to FALSE exits call RECJU ;incorporate a jump if result FALSE sto bx,YPD ;update for new head of chain ret ; Compile a right parenthesis. RECRP: pop bp ;recover xpd, which is hidden pop XPD ;replace it cmp XPD,#ZE jz RECFP ;if so, continue with recfp pop bx ;recover wpd call RECJU ;link expr to ypd on its own level push bx ;but save pointer until we finish up call RECFY ;false predicates in last segment pop YPD ;replace ypd for higher level ld bx,ZPD ;now we have destination for semicolons call recfc ;so insert all the correct addresses pop ZPD ;replace old zpd jmp bp ; Final right parentheses get a different treatment. RECFP: mov bx,dx ;compile pointer in bx stob #RN,[bx] ;store a for false exit inc bx ;ready for next byte push bx ;save compile pointer ld dx,#SKP ;address of skip - TRUE exit from REC call RECFY ;use it for last segment ld bx,ZPD ;destination of semicolons now known call recfc ;so fill out that chain pop dx ;compile pointer that was saved pop YPD ;restore old ypd pop ZPD ;restore old zpd ret ;return one level higher than expected ; Insert a new element in a chain of jmp's which will ; eventually have destination addresses. In the interim ; each is given the address of its predecessor. On entry ; (dx) holds the address where the instruction will be ; stored and (bx) holds the address of its predecessor. ; On exit, (dx) is incremented by 3 to point to the next ; free byte, and (bx) has the starting value of (dx). RECJU: xchg bx,dx ;(bx) and (dx) exchanged is better stob #JU,[bx] ;store the jump instruction inc bx sto dx,[bx] ;store old link lea dx,[bx+2] ret ; When the destination of a linked chain of jumps is ; finally known, the destination can be substituted into ; each one of the links. On entry, (bx) contains the ; address of the first link unless it is zero signifying ; a null chain. recfc: or bx,bx ;test for end of chain jz recfx ;if address is zero, chain ends mov ax,dx dec ax dec ax recfi: ld cx,[bx] ;save next link sto ax,[bx] ;store destination sub [bx],bx mov bx,cx ;update link or bx,bx jnz recfi ;continue recfx: ret ; Call recfc with the intention of filling the y chain. RECFY: ld bx,YPD call recfc sto bx,YPD ret ; Subroutine which will initialize the temporary ; registers used by the REC compiler. INRE: ld bx,#ZE sto bx,XPD sto bx,YPD sto bx,ZPD ret ; ======================================================= ; The following are specialized compiling subroutines ; which apply to special structures and depend on the ; model of a pushdown list with a linked chain structure ; and special registers px and py delimiting the top ; segment on the chain. ; ======================================================= ; ------------------------------------------------------- ; Compilation of quoted expressions. Single and double ; quotes may alternate with one another to an arbitrary ; depth. Both kinds of quotes are executed in the same ; way, by loading the quoted expression from the program ; onto the pushdown list. ; ------------------------------------------------------- ; Compile single quotes. RECSQ: call RECOP ;record call to qu inc dx ;set aside two bytes inc dx ;to hold length of ASCII chain push dx ;keep beginning for future reference push QUEN ;delay cleanup until ret SQ: ld bp,read ;read the next character call bp cmp al,#'''' ;test for single quote jz SQ2 ;if so go after entire chain cmp al,#'"' ;test for double quotes jnz SQ1 call DQ1 ;if so, read it all SQ1: xchg bx,dx sto al,[bx] xchg bx,dx ;otherwise keep on storing inc dx ;and advancing pointer jmp SQ ;go after next character SQ2: ret ; Compile double quotes. RECDQ: call RECOP ;record call to qu inc dx ;set aside two bytes inc dx ;to hold length of chain push dx ;put chain origin away for reference push QUEN ;delay cleanup until ret DQ: ld bp,read ;read the next character call bp cmp al,#'"' ;test for double quotes jz DQ2 ;if so, chain finished cmp al,#'''' ;check for single quotes jnz DQ1 call SQ1 ;if so go after whole chain DQ1: xchg bx,dx sto al,[bx] xchg bx,dx ;otherwise keep on storing inc dx ;and advancing pointer jmp DQ ;go after next character DQ2: ret ; Cleanup for both quote compilers. QUEN: dw ENQU ;for the direct push ENQU: pop bx ;beginning of chain in (bx) mov cx,dx sub cx,bx sto cx,[bx-2] ;store length ret ; (') (") Execute single or double quote. QU: pop bx ;get call location off the 8080 stack ld cx,[bx] ;count inc bx ; inc bx ; mov si,bx ;save source origin add bx,cx ;calculate source end = return adress push bx call NARG ;check space, put dest. pointer in (bx) cld mov di,bx mov ax,ds mov es,ax mov ax,cs mov ds,ax rep movsb mov ax,es mov ds,ax sto di,PY ;record end of argument ret ; ------------------------------------------------------- ; Comments are enclosed in square brackets, which must be ; balanced. Code may be disabled by enclosing it in ; square brackets, but care must be taken that the ; expression so isolated does not contain individual ; brackets, such as arguments of arrobas or quoted ; brackets, which might disrupt the balance. Since ; comments are ignored by the compiler they are not ; executed. ; ------------------------------------------------------- ; Compile comments by ignoring them. RECCM: ld bp,read ;get next character call bp cmp al,#']' ;test for closing ] jz RECCX ;if so we're done cmp al,#'[' ;test for beginning of new level jnz RECCM ;otherwise keep on reading call RECCM ;if so go after it recursively jmp RECCM RECCX: ret ; ------------------------------------------------------- ; Sometimes, notably in compiling arroba as a call to a ; subroutine named by a single letter, a parameter will ; follow a subroutine call as its calling sequence. ; ------------------------------------------------------- ; Operator with one ASCII parameter. RECO1: call RECOP ;always compile the subroutine call ld bp,read ;read the parameter call bp mov bx,dx sto al,[bx] ;store as a 1-byte calling sequence inc dx ;always ready for next byte ret ; Predicate with one ASCII parameter. RECP1: call RECO1 ;compile as the analogous operator jmp RECYJ ;then take account of false exit ; ------------------------------------------------------- ; Decimal numbers are of such frequent occurrence in the ; form of counters, arguments, or just data that it is ; convenient to compile them on sight without requiring ; any special delimiters. Likewise, negative numbers are ; easier to designate using a minus sign than using their ; modular form, but this should not prevent the use of a ; minus sign as an operator. ; ------------------------------------------------------- ; Compile a minus sign. This involves determining whether ; it is followed immediately by a decimal digit, in which ; case it is compiled as part of a negative number. RECMS: ld bp,read ;read in one byte call bp call MS1 ;decide whether it is a digit push ax ;it was not, save it call RECOP ;compile call to binary minus pop ax ;recover the extra character jmp skp86 ;skip because we have next character MS1: call RND ;return if not digit inc sp ;erase call to ms1 inc sp ; call RECDS ;read and convert digit string ld cx,GNU ;fake that it was nu, not ms push ax ;save terminating character neg bx ;negate (bx) jmp DD1 ;continue as though positive number GNU: DW NU ; Compile a decimal digit, which requires reading any ; further digits which follow, and saving the terminator. RECDD: ror al ;undo multiplication by 4 ror al ; push cx ;save execution address call RECDS ;read and transform rest of digits pop cx ;recover execution address push ax ;recover terminating character DD1: call RECOP ;compile subroutine call xchg bx,dx ;(dx) and (bx) must be interchanged sto dx,[bx] ;put low order byte in calling sequence inc bx ; inc bx ;ready for next byte xchg bx,dx ;put (dx) and (bx) back as they were pop ax ;recover terminating character jmp skp86 ;skip over character read call ; Multiply (bx) by 10 and add A. (dx) is conserved. TXP: mov cx,bx ;transfer (bx) to (cx) add bx,bx ;multiply (bx) by 2 add bx,bx ;another 2 makes 4 add bx,cx ;the original (bx) makes 5 add bx,bx ;another 2 makes 10 add bx,ax ;add in the accumulator ret ; The heart of number compilation. RECDS: and al,#0FH ;mask ASCII down to binary value mov BL,al ;put it into register pair (bx) ld BH,#ZE ;fill out H with a zero RD1: ld bp,read ;read the next character call bp call RND ;quit if it is not another digit call TXP ;multiply (bx) by ten and add A jmp RD1 ;continuing while digits keep coming ; Execute a number, which means load it on pdl. NU: ld cx,#2 ;two bytes will be required call NARG ;close last argument, open new pop dx ;get beginning of calling sequence xchg bx,dx ld ax,[bx] xchg bx,dx sto ax,[bx] ;and copy it over inc dx ;on to the high order byte inc bx ;and the place to store it inc dx ;move on to program continuation inc bx ;always leave PDL ready for next byte push dx ;put back the return address sto bx,PY ;mark end of the argument ret ; (O) Transform an ASCII character string on the PDL into ; a two-byte number. Predicate - false if the argument ; is not a digit string or null, leaving the argument ; unchanged. UCO: ld cx,#2 ;two bytes are required call OARG ;check that they are available ld bx,PY ;fetch the end of the argument string stob #ZE,[bx] ;put a zero there to mark its end ld dx,PX ;load pointer to argument string ld bx,#ZE ;zero in (bx) to start the conversion O1: xchg bx,dx ld al,[bx] xchg bx,dx ;fetch one character inc dx ;get ready for next or al,al ;test for zero jz O2 ;go to accumulation phase call RND ;FALSE, chain unaltered if non-digit call TXP ;otherwise continue to work up value jmp O1 ;and keep on reading bytes O2: xchg bx,dx ;safeguard converted number in (dx) ld bx,PX ;get pointer to argument sto dx,[bx] ;store low byte inc bx ;increment pointer inc bx ;increment pointer again sto bx,PY ;store to close argument jmp SKP ;TRUE exit from predicate ; (#) Change two-byte binary number into a decimal-based ; ASCII string without sign. The special cases of a zero- ; byte or a one-byte argument are also considered. NS: ld cx,#05H ;five bytes may be required call OARG ;reuse the old argument ld cx,PY ld bx,PX sub cx,bx ld dx,#ZE ;put zero in (dx) for default jcxz NS1 ;load nothing ld dl,[bx] ;load low byte dec cx ;test for one byte jcxz NS1 ;only byte and it's loaded ld dh,[bx+1] ;load high byte NS1: push bx ;save pointer for ASCII string ld al,#'0' ;prepare to write a zero ld bx,#-10000 ;will there be 5 digits? add bx,dx ; jc NS2 ; ld bx,#-1000 ;will there be 4 digits? add bx,dx ; jc NS3 ; ld bx,#-100 ;will there be 3 digits? add bx,dx ; jc NS4 ; ld bx,#-10 ;will there be 2 digits? add bx,dx ; jc NS5 ; jmp NS6 ;write one no matter what NS2: ld cx,#-10000 ;ten thousands digit call NSA ; NS3: ld cx,#-1000 ;thousands digit call NSA ; NS4: ld cx,#-100 ;hundreds digit call NSA ; NS5: ld cx,#-10 ;tens digit call NSA ; NS6: add al,dl ;units digit pop bx ;recover pointer to PDL sto al,[bx] ;store the digit inc bx ;position pointer for next byte sto bx,PY ;done, store it as terminator ret NSA: mov bx,cx ;put power of ten in (bx) add bx,dx ;subtract it once jnc NSB ;can't subtract inc al ;increase the count xchg bx,dx ;put diminished number in (dx) jmp NSA ;repeat the cycle NSB: pop bp ;get return address pop bx sto al,[bx] ;store new digit inc bx ;advance pointer ld al,#'0' ;load a fresh ASCII zero push bx jmp bp ;return to the ; ======================================================= ; Some simple procedures to compile REC expressions into ; subroutines, deposit a reference to them in a symbol ; table, and eventually to recover the space and erase ; the symbol table reference. ; ======================================================= ; Table search. The table whose address is stored at fxt ; is consulted for its pair of addresses at position 4*A. ; Thus on entry, A holds the table index. This table ; alternates the address of a compiling subroutine with ; the execution address of the same entry. On exit, (cx) ; holds the execution address, (dx) is preserved, and a ; jump is made to the compiling address. rects: ld ah,#ZE add ax,ax add ax,ax ld bx,FXT ;load base address of table add bx,ax push [bx] ;put the first entry in (cx) ld cx,[bx+2] ;table pointer is going ret ;then off to the compilation ; Pick out left delimiters: (, {, or [. left: ld bp,read call bp cmp al,#'(' jz eft cmp al,#'{' jz eft cmp al,#'[' jnz left call reccm jmps left eft: ret ; A main program to compile characters one by one as ; they are read in from the console. Note that the ; compiling programs invoked by rects can generate skips ; when they have already read the following character. ; This occurs most notably when compiling digits. Also ; note that svc86 normalizes characters when it accepts ; them. recre: ld bp,read ;read a character from whereever call bp recrr: call svc86 ;check for space, control character jmp recre ;not valid, go back for another call rects ;look up in table and compile it jmp recre ;read another character and repeat jmp recrr ;repeat but next character already read ; A subroutine which will pass over comments, and wait ; for an opening left parenthesis or brace before compiling ; a REC expression. EMCE: call UCL ;entry here erases an argument from PDL EMCX: call left ;get a character from whereever ld dx,C1 ld bx,C1 mov bp,sp xchg bx,[bp] push bx call recrr ;compiling prgrm one char already read sto dx,C1 ret EMCU: pop dx pop bx push dx push bx ld dx,#EMCV push dx jmp bx EMCV: jmp EMCW pop C1 jmp skp86 EMCW: pop C1 ret ; ({) Introduce a series of definitions. LBR: xchg bx,dx stob #CA,[bx] ;insert a call to the executable subroutine xchg bx,dx inc dx mov cx,dx ;place to put call address - keep in BC inc dx ;make room inc dx call RECYJ ;link in the FALSE exit call RECJU push bx ;keep this address push XPD sto #ZE,XPD ;this is top level for ensuing subroutines ld bx,#ZE LB1: push dx ;record entry point to subroutine inc bx ;increment count of subroutines push bx ;keep it next to top on stack push cx ;jump address at entry - keep it on top call left call recrr ;compile at least one subroutine LB2: ld bp,read ;get possible name of subroutine call bp cmp al,#'}' ;no name - we execute this one jz LB3 call svc86 ;convert name into serial number jmp LB2 ;punctuation instead of name add al,#' ' ld ah,#ZE ld bx,VRT add bx,ax add bx,ax pop cx ;get this out of the way mov bp,sp xchg bx,[bp] ;store table address, put subr count in bx jmp LB1 LB3: cld mov ax,ds mov es,ax pop bx ;origin of brace compilation mov di,dx sto di,[bx] ;store displacement at initial jump sub [bx],bx dec [bx] dec [bx] pop cx ;number of subroutines + 1 push cx ;we'll need it again later mov bp,cx ;put it in bp too dec bp add bp,bp add bp,bp add bp,sp ld al,#POBX stosb jmp LB5 LB4: ld ax,#PUME ;for each defined symbol we insert the stos ld ax,[bp] stos ld ax,#LDME ; stos ld ax,[bp] stos ld ax,[bp+2] stos sub bp,#4 ;we read the stack backwards LB5: loop LB4 ld al,#PUBX stosb ld al,#CA stosb pop cx pop ax sub ax,di dec ax dec ax stos push cx ld al,#JU ; jmp $+6 stosb push di ; inx h inc di ; inx h inc di ; inx h ld al,#POBX stosb ld al,#INBX stosb stosb stosb ld al,#PUBX stosb pop bx sto di,[bx] sub [bx],bx dec [bx] dec [bx] ld al,#POBX stosb pop cx jmp LB7 LB6: ld ax,#POME ;after an expression in braces is stos pop ax stos inc sp inc sp LB7: loop LB6 ld ax,#JUBX ;the whole thing is finished off by a return stos mov dx,di pop XPD pop bx cmp XPD,#ZE jz LB8 sto dx,[bx] sub [bx],bx dec [bx] dec [bx] ret LB8: ld cx,[bx] sto #SKP,[bx] sub [bx],bx dec [bx] dec [bx] sub bx,cx sto dx,[bx] sub [bx],bx dec [bx] dec [bx] xchg bx,dx sto #JUBX,[bx] inc bx inc bx xchg bx,dx inc sp inc sp ret ; (@) Subroutine which will transform an ASCII character ; into a table reference, and then jump to the address ; so encountered. This is essentially REC's subroutine ; call mechanism, necessarily a predicate since it calls ; a REC expression, which is itself a predicate. AR: pop bx ;entry if name is a parameter ld al,[bx] ;read the calling sequence inc bx ;advance pointer for return push bx ;put it back on 8080 stack cmp al,#'@' jnz XAR NAR: ld bx,PX ;entry if subroutine index is argument ld al,[bx] call UCL XAR: ld ah,#ZE add ax,ax mov di,ax ld bx,VRT ;entry when index is in register A jmp [bx+di] ;then use it as jump address ; ======================================================= ; Some general service routines. ; ======================================================= ; Skip on valid character, meaning, not control symbol. ; If valid, 20H (space) is subtracted, making A = 1, etc. svc86: cmp al,#'!' ;reject space, excl is lower limit jc sv cmp al,#7FH ;seven bits is upper limit jnc sv sub al,#' ' ;normalize to begin with (excl) = 1 pop bp inc bp inc bp jmp bp sv: ret ;don't skip for control or flag bit ; Return if not decimal. A unchanged if not decimal, else ; reduced to binary. RND: cmp al,#':' ;colon follows 9 in ASCII alphabet jnc RTN cmp al,#'0' ;ASCII zero is lower limit jc RTN sub al,#'0' ;normalize to get binary values ld ah,#ze ;zero for uncomplicated arithmetic ret RTN: inc sp inc sp ret ; Second level return on error. RR2: pop bx ;entry to clear two items from PDL mov bp,sp xchg bx,[bp] ; RR1: pop bx ;entry to clear one item from PDL mov bp,sp xchg bx,[bp] ; RER: pop ax ;site where ther error occurred cmp ER,#ZE ;only record the first error jnz RRR sto ax,ER RRR: ret ; (?) Test whether an error has been reported: predicate ; which is true if er is nonzero, in which case it will ; reset er. It will also, if TRUE, place the calling ; address of the last reported error on the pushdown ; list. If false, only a FALSE return is generated. Note ; the ironic circumstance that, if PDL is exhausted, qm ; can generate an error trying to report an error - but ; the TRUE result will still be valid. QM: cmp ER,#ZE ;test the error cell jz QQ ;FALSE return if no error ld cx,#2 ;we want two bytes for error address call NARG ;check space, prepare for new argument ld ax,ER ;fetch error address sto ax,[bx] ;transfer it to REC PDL inc bx ; inc bx ;pointer must always advance sto bx,PY ;end of the argument sto #ZE,ER ;reset ER jmp SKP ;TRUE return - there was an error QQ: ret ; Generate a skip (skp), which is often combined with the ; erasure of an argument on the pushdown list (cucl). CUCL: call UCL ;erase the top argument SKP: xchg bx,sp inc [bx] ;assume the skip will be over a inc [bx] ;three-byte instruction, such as a jump inc [bx] ; xchg bx,sp ret ;return to the altered address skp86: xchg bx,sp inc [bx] inc [bx] xchg bx,sp ret ; Test PDL space beginning at top argument. On entry (cx) ; contains the total space required. On exit, (cx) stays ; unchanged, (dx) holds pz, while (bx) holds px+(cx). ; If the space is not available, return is made from the ; calling program after noting the error. Otherwise ; normal return to the calling program occurs. The likely ; use of oarg is to record a result without having to go ; through ucl, NARG. OARG: ld dx,PZ ;load limit of PDL dec dx ;keep one byte margin ld bx,PX ;load beginning of current argument add bx,cx sub dx,bx jc oar ;no, note error, quit calling program ret ;yes, continue normally oar: call RER ;this must be here to get a short jump ; Check space for, and then set up, a new argument. On ; entry, (cx) should contain the amount of additional ; space required. The program will automatically add ; two more bytes for the pointer which would close the ; argument and then, if the required space is available, ; close it, define the new px, and leave its value in ; (bx). (dx) will contain the old value of px to be used ; in case the superseded argument is still interesting. ; When space is not available, the error return rer is ; taken. ; ; The entry RARG can be taken when it is known that ; sufficient space is available but the pointers still ; have to be set up. NARG: mov di,cx ld bx,PY ;load end of current argument lea ax,[bx+di+3] cmp ax,PZ jnc NRER ;check available space RARG: ld dx,PX ;entry if no space check needed ld bx,PY sto dx,[bx] ;low byte of closing link inc bx ;on to high byte inc bx ;beginning of new space sto bx,PX ;which is recorded by px ret ;and remains in (bx) NRER: call RER ; (L) Remove argument from pushdown list. There are no ; requirements for entry to ucl. On exit, (cx) remains ; unchanged, (dx) holds the end of the former argument ; and (bx) holds the beginning of the former argument - ; the one that was exposed when the current argument was ; erased. Erasing non-existent arguments creates an error ; condition which is noted and ignored. UCL: ld bx,PX ;pointer to current argument dec bx ;just behind the present dec bx ld dx,[bx] ;argument is the address or dx,dx ;so we always test out of caution jz ULE sto bx,PY ;(bx) now holds end of previous arg. sto dx,PX ;pointer to beginning of prev. arg. xchg bx,dx ret ULE: call RER ;record error if pointer was zero ; Null program for undefined operators. NOOP: ret ; ======================================================= ; ; Some of the service routines, which might be external ; references in other modules, are: ; ; oarg space when reusing an argument ; NARG close old argument, space for new ; rarg same as NARG when space is assured ; skp generic skip ; rer return on error ; rr2 rer after popping two addresses ; rtn generic return ; ucl lift argument from PDL (L) ; cucl lift argument, then skip ; ; Three entry points can be used according to the variant ; of the compiling operator C desired. One of them could ; also be used by a main program. ; ; emce lift pushdown, open block, compile ; emcx compile a sequence of subroutines ; ; ======================================================= LINK PDL86.ASM