; ======================================================= ; ; REC module containing RAM storage, I/O programs, main ; program, and the directory. The complete set of modules ; comprises REC.MAC, PDL.MAC, MARKOV.MAC, RECLIB.MAC, and ; FXT.MAC. RECLIB.MAC may be omitted if the operator X ; isn't used, and must be substituted by another module ; if the collection of subroutines to be called by X is ; to be changed. ; ; FXT.MAC contains the following REC operators and ; predicates: ; ; C compile a REC expression ; i input from designated port ; k call CP/M without residue ; K call CP/M, preserve FCB, return value ; o output from designated port ; R read one character from console ; t type message given header ; T type argument on PDL ; W write argument on LST: ; x execute REC subroutine ; ` test if a character waits at keyboard ; ; REC version released during the 1980 Summer School of ; the Microcomputer Applications Group of the I.C.U.A.P. ; ; FXT86 - Copyright (C) 1982 ; Universidad Autonoma de Puebla ; 49 Poniente 1102 - Puebla, Puebla, Mexico ; All Rights Reserved ; ; [Harold V. McIntosh, 28 August 1980] ; ; Modification 1 - 1 January 1981. ; a) Main program derives the values of const, ; conin, conou from the address rst0 supposing ; that BIOS starts out with the standard jump ; vector. Thus, REC need not be reassembled ; to have fast access to I/O when CP/M varies. ; b) T protected by pushes and pops of dx and bx. ; c) Some changes made in memory allocation. ; 24 May 1981 - Zero flag to restrain L from too many pops ; 25 March 1982 - Y is now a predicate ; 29 May 1983 - ~ discontinued as argument exchange ; 29 May 1983 - ~ for unary negative or complement ; 29 May 1983 - N for numerical comparison on PDL ; 29 May 1983 - h discontinued, replaced by ''w ; 30 May 1983 - CPIR: jumps to BOOT rather than RER ; 8 July 1983 - C has object program origin as argument ; 8 July 1983 - C is an operator ; 8 July 1983 - C0 defined as lower bound of compile area ; 8 July 1983 - x moved from RECLIB ; 8 July 1983 - x is a predicate to call REC subroutines ; 9 July 1983 - Buffered CP/M input if no disk file given ; 14 July 1983 - W had its arguments reversed ; ======================================================= ; Absolute program locations used by CP/M. bdos equ 224 ;CP/M-86 software interrupt vector TFCB equ 005CH ;CP/M file control block TALT equ 006CH ;CP/M alternate file name FSIZ equ 0010H ;CP/M file name size HSIZ equ FSIZ/2 TBUF equ 0080H ;CP/M disk buffer location TSIZ equ 0080H ;CP/M disk buffer size ; ----------------------------------------------------- ; RAM memory which is required for the operation of REC ; ----------------------------------------------------- ; Relay area for input and output subroutines. read: dw chin ;character input for REC compiler tyin: dw chin ;single character input for R tyou: dw chou ;single character output for T ; Temporary storage used by the REC compiler. XPD: dw 0000 ;colon jump back to left parenthesis YPD: dw 0000 ;false predicate jump chain ZPD: dw 0000 ;semicolon exit chain ; Pointers to the directories. FXT: dw FT ;pointer to fixed operator directory VRT: dw VT ;pointer to subroutine directory ; Pointers to the area of compiled subroutines. C0: dw KA ;lower limit of compile area C1: dw KA ;beginning of present compilation C2: dw EK ;upper limit of compile area ; Pointers to REC/MARKOV pushdown list. PX: dw PD+2 ;beginning of pushdown text PY: dw PD+2 ;end of pushdown text PZ: dw EP ;end of available pushdown space ; Workspace pointers. P0: dw WS ;beginning of workspace P1: dw WS ;beginning of marked segment P2: dw WS ;end of marked segment P3: dw WS ;end of text P4: dw EW ;end of workspace ; I-O pointers. RX: dw 0000 RY: dw 0000 ; Linkage to input-output through ports. QIN: DB 0ECH ;8-bit static IN instruction QI: DB 00H ret QOU: DB 0E6H ;8-bit static OUT instruction QO: DB 00H ret ; Error flag. ER: dw 0000 ; ======================================================= ; Programs related to input-output and disk usage. ; ======================================================= ; bootstrap routine boot: ld cl,#ze ld dl,#ze int bdos ; Buffer read routine. PTY: push bx ;conserve (bx) ld bx,RX ;pointer to read buffer ld al,[bx] ;fetch byte inc bx ;advance pointer to next byte sto bx,RX ;update buffer pointer pop bx ;restore (bx) - preserve all reg pairs ret ; Console character read routine. Strict CP/M compatible ; version, which has characteristics undesirable for some ; applications, such as an automatic echo or preemption ; of some of the control characters for editing purposes. ; When it is used, programs must forego their own echo, ; and do their own editing when required. This version has ; direct access to CONIN. chin: push cx push dx chi: ld dl,#-1 ld cl,#6 int bdos or al,al jz chi pop dx pop cx ret ; Buffered console character read routine, which is more ; practical for use with CP/M. Up to 126 characters may be ; read using CP/M function 10, which is sufficient for ; bootstrapping or generating test programs. CHIN should be ; used for longer input sequences, which must be error-free ; - incoming through a modem for example. buin: push bx push cx push dx ld bx,RX cmp bx,RY jnz BI5 ld cl,#9 ;(09) write message ld dx,#bume int bdos ld cl,#10 ;(0A) buffered read ld dx,#TBUF int bdos ld cl,#9 ;(09) write message ld dx,#crlf int bdos ld bx,#TBUF+2 sto bx,RX ld al,[bx-1] ld ah,#0 add ax,bx sto ax,RY BI5: ld al,[bx] inc bx sto bx,RX pop dx pop cx pop bx ret logo: db 0DH,0AH,' REC(8086)/ICUAP',0DH,0AH db 'Universidad Autonoma de Puebla',0DH,0AH db ' July 20, 1983',0DH,0AH,'$' bume: db 0DH,0AH,'REC86> $' crlf: db 0DH,0AH,'$' bure: ld al,#TSIZ-2 sto al,TBUF ld ax,#buin sto ax,read ld ax,#TBUF sto ax,RX sto ax,RY ret ; Console character out routine. Strict CP/M compatible ; version, which unfortunately makes so many tests and ; jumps that it is unsuitable for programs such as the ; cursor editor which frequently write a full screen. ; This is a version with direct access to CONOUT. chou: push cx push dx mov dl,al ld cl,#6 ;direct console I-O int bdos pop dx pop cx ret ; (`) Test for presence of waiting character (FALSE if ; none waiting). This is a version with fast access to ; CONST, which is required when the fast CHIN is used, ; because CP/M has some internal buffers which will ; otherwise distort the results. chaw: push cx push dx ld dl,#-2 ld cl,#6 ;direct console I-O int bdos pop dx pop cx ror al jc chw ret chw: jmp SKP ; Printer output routine. PROU: push bx push dx push cx ld cl,#5 ;output through LST: MOV dl,al int bdos pop cx pop dx pop bx ret ; (R) REC read operator. UCR: ld cx,#1 ;one byte to be inserted call NARG ;close last arg, verify space push bx ld bx,tyin call bx ;get byte from console input pop bx sto al,[bx] ;store on PDL inc bx ;advance pointer sto bx,PY ;save as terminator ret ; (t) Write indirect operator. prints the ; indicated message, leaves no residue. LCT: ld bx,PX ;fetch argument pointer call TWOL ;move two args to 8080 stack pop dx ;second arg (org) into (dx) pop bx ;top arg (siz) into (bx) add bx,dx ;org+siz=end xchg bx,dx ;(dx)=end, (bx)=org jmp UT1 ;use write cycle in UCT ; (T) REC write operator. <'XXX' T> will write XXX on ; the console, leaving it on the PDL. uct: ld dx,py ;fetch terminal address ld bx,px ;beginning address to (bx) ut1: cmp dx,bx ; jz ut2 ;they match, we're done ld al,[bx] ;get byte out of memory push bx ;the registers dx and bx push dx ;are essential for the loop ld bx,tyou call bx pop dx ;recover the saved registers pop bx ; inc bx ;advance pointer jmp UT1 ;repeat ut2: ret ; (W) REC print operator. will print the ; indicated text on the list device, and then erase its ; arguments. UCW: ld bx,PX ;pointer to arguments call TWOL ;2 args from PDL to 8080 stack pop bx ;place text origin in (bx) pop dx ;place length in (dx) UWW: cmp dx,dx ;check for zero length jz UWX ;no more to print ld al,[bx] ;fetch a byte push bx ;we need to be sure that dx and bx are push dx ;preserved whatever the print routine call PROU ;send it to printer pop dx ;recover bx pop bx ;and dx dec dx ;diminish count inc bx ;advance pointer jmp UWW ;repeat UWX: ret ; (i) Input from designated port. leaves ; so that after disposing of , ; can be reused. LCI: ld bx,PX ;get pointer to top argument on PDL ld al,[bx] ;only the low order byte matters sto al,QI ;place it in teme IN instruction ld cx,#1 ;we're only going to read one byte call NARG ;prepare a place for it on the PDL call QIN ;execute the captive IN instruction sto al,[bx] ;storing the incoming byte on the PDL inc bx ;always ready for the next byte sto bx,PY ;close off the argument ret ;and we're through ; (o) Output from designated port - ; leaves , facilitating multiple OUTs through the ; same port. LCO: ld bx,PX ;pointer to last argument - output byte ld CH,[bx] ;tuck it into register b call UCL ;erase the top argument ld al,[bx] ;(bx) points to next argument - get it sto al,QO ;store in tame OUT instruction MOV al,CH ;output must be from accumulator jmp QOU ;execute the prepared OUT instruction ; ======================================================= ; ; Communication with CP/M takes two forms: ; which leaves on the pushdown list, or else ; which leaves nothing on the pushdown list. ; In either case - FCB is a two-byte parameter, usually ; the address of the file control block - but it could ; also be a DMA address or sometimes even null for the ; sake of uniformity. Approximately thirty options are ; available which are numbered serially, indicated by the ; argument n. The difference between K and k is that the ; former conserves the parameter FCB for possible use by ; a subsequent CP/M call, and reports a result in the ; one-byte result . This could be the character ; read by an input routine or an error code for the disk ; routines. ; ; The options are: ; ; num function "FCB" "code" ; --- -------- ----- ------ ; ; 0 system reset - - ; 1 read console - char ; 2 write console char - ; 3 read reader - char ; 4 write punch char - ; 5 write list char - ; 6 - - - ; 7 read i/o stat - stat ; 8 write i/ stat stat - ; 9 print buffer buffer - ; 10 read buffer buffer - ; 11 console status - stat ; ; 12 lift disk head - - ; 13 init disk only - - ; 14 select disk disk - ; 15 open file fcb code ; 16 close file fcb code ; 17 search once fcb code ; 18 search again fcb code ; 19 delete file fcb code ; 20 read 1 record fcb code ; 21 write 1 record fcb code ; 22 create file fcb code ; 23 rename file fcb code ; 24 read login - logv ; 25 read disklog - disk ; 26 set DMA address dma - ; 27 read bitmap - - ; ; Fuller details of all the CP/M options and the way they ; function can be obtained through consulting Digital ; Research's manuals for CP/M, especially their "CP/M ; Interface Guide." ; ; ======================================================= ; (K) Set up communication with CP/M - top into (bx), ; next into (dx). Preserve next, call BDOS, (Aze) into ; top. CPM: ld bx,PX ;fetch pointer to top argument ld cx,[bx] ;load C from low byte ld dx,[bx-2] ;load up (dx) with high byte xchg bx,dx ;pointer into (bx) ld dx,[bx] ;low byte of under argument int bdos ;call BDOS with args in (bx), (dx) ld bx,PX ;pointer to top argument again sto al,[bx] ;save low byte from A stob #ZE,[bx+1] ;make high byte a zero ret ; (k) Call to CP/M without any value returned. CPML: call CXLD ;load top arg into (cx), lift it call DXLD ;load next arg into (dx), lift it too int bdos ;execute indicated operation ret ; ------------------------------------------------------- ; Disk input-output routine working through CP/M. ; ------------------------------------------------------- ; Set up a file control block with a given file name and ; the default extension REC. The pushdown list contains ; the disk unit designation, then by the filename without ; any extension. No protection is afforded against an ; overly long file name, a nonexistent disk, or the like. ; Some errors of this type must be caught by CP/M since ; REC cannot know such things as the exact number of disk ; drives that there will be. DIIN: cld mov ax,ds mov es,ax ld cx,#21H ;FCB requires 33 bytes ld di,#TFCB ;use CP/M's transient FCB ld al,#00H ; ;fill it with zeroes rep stosb ld cx,#11 ;filename field is 11 bytes long ld di,#TFCB+1 ;field begins at second byte ld al,#' ' ;fill it with blanks rep stosb ld bx,PX ;fetch pointer to top argument ld al,[bx] ;load disk unit designator SUB al,#'@' ;normalize to uppercase letters sto al,TFCB ;store it in file control block call UCL ;pop top argument ld si,PX ;fetch pointer to file name ld cx,PY ;end of file name sub cx,si ;place py - px in (cx) ld di,#TFCB+1 ;destination origin rep movsb CPIN: ld cl,#15 ; ld dx,#TFCB ;file control block int bdos ; cmp al,#0FFH ;check for error jz CPIR ld bx,#TBUF ;origin of CP/M's sector buffer sto bx,RX ;initial address of pseudotty sto bx,RY ;provoke disk read ret CPIR: jmp boot ; Read from disk buffer, replenish buffer when empty. DIRE: push bx ;save 3 8080 register pairs push dx ; push cx ; ld bx,RX ;pointer to current byte cmp bx,RY ;skip if equal jnz DI5 ;still have bytes in the buffer ld cl,#20 ; ld dx,#TFCB ;file control block int bdos ; ld bx,#TBUF+TSIZ ;end of buffer sto bx,RY ;store it in ry ld bx,#TBUF ;beginning of buffer sto bx,RX ;store it in rx DI5: ld al,[bx] ;common continuation inc bx ;byte in acc, advance pointer sto bx,RX ;store position of next byte pop cx ;replace 3 register pairs pop dx ; pop bx ; ret ; ================ ; = main program = ; ================ MAIN: mov ax,cs mov ss,ax ld sp,#STAK ld bx,#ZE ; sto bx,PD ;mark bottom of pushdown list ld al,TFCB+1 cmp al,#' ' jz tylo ld cx,#HSIZ ld bx,#TALT pual: push [bx] ;save secondary file name inc bx inc bx loop pual call CPIN ;open disk file for REC program ld bx,#DIRE ;REC input through disk sto bx,read ;REC compiler's I-O linkage call INRE ;initialize REC compiler RAM call EMCX ;compile the program file ld cx,#HSIZ ld bx,#TFCB+FSIZ pop dx poal: dec bx dec bx pop [bx] loop poal push dx call EMCU ;execute the program file jmps bootie ;return to CP/M if false nop ;beware jump span bootie: jmp boot ;or even if it was true tylo: ld cl,#9 ;(09) write message ld dx,#logo int bdos nodi: call bure call INRE call EMCX call EMCU jmps nodi nop jmp nodi ; (C) REC compiling operator which takes the designation ; of the compiling source from the PDL. The alternatives ; are: ; ; ''C input program from console ; 'file' 'D' C take from disk D ; pC pushdown list ; qC workspace ; memory from address org onward ; ; In general, if the top argument is of length zero, the ; console is the source, if it is of length one the named ; disk is the source [@=current disk, A, B, ... up to the ; system's limit], and if the argument has length 2, the ; combination of from the memory applies. It ; is the programmer's responsibility to avoid nonexistent ; memory, disk units, and the like. UCC: ld ax,PY sub ax,PX jz UC2 ;zero means console cmp ax,#1 ;test for one byte jz UC1 ;one means disk designation cmp ax,#2 ;verify that we've got two bytes jnz UC7 ;no provision for other than 1, 2 bytes ld bx,#PTY ;setup readin from pseudoteletype sto bx,read ; call CXLD ;load two numerical arguments ld dx,[bx] ;bx contains PX for second argument sto dx,RX ;origin of REC source code add dx,cx ;length of source code sto dx,RY ;end of source code jmp UC4 ;compile once rx, ry set up UC1: call DIIN ;setup the CP/M FCB for given file ld bx,#DIRE ;setup input from disk reader jmp UC3 ;compile once input source set up UC2: ld bx,#CHIN ;input from the console UC3: sto bx,read ; UC4: call UCL ld cx,PY sub cx,PX cmp cx,#0 jnz UC5 ld dx,C1 jmp UC6 UC5: cmp cx,#2 jnz UC7 ld bx,PX ld dx,[bx] push dx call UCL pop dx cmp dx,C0 jc UC6 cmp C2,dx jc UC6 sto dx,C1 UC6: push dx call LEFT call RECRR push dx call PUTW call PUTW ret UC7: call RER ; (x) Call a REC subroutine go: ld bx,PX call onel ret ; ======= here is the table of definitions of REC operators ===== FT: dw NOOP ;blank dw NOOP dw RECOP ; ! binary to hex string dw HX dw RECDQ ; " quoted expression dw QU dw RECOP ; # binary to decimal string dw NS dw RECOP ; $ fetch a variable cell dw VBLE dw RECOP ; % restrict to one byte dw PE dw RECOP ; & exchange top numeric pair dw EXCH dw RECSQ ; ' quoted expression dw QU dw RECLP ; ( dw NOOP dw RECRP ; ) dw NOOP dw RECOP ; * multiply dw MPY dw RECOP ; + add dw SUM dw NOOP ; , separator like space dw NOOP dw RECMS ; - subtract dw DIF dw RECOP ; . dw NOOP dw RECOP ; / divide [remainder, quotient] dw DVD dw RECDD ; 0 number dw NU dw RECDD ; 1 number dw NU dw RECDD ; 2 number dw NU dw RECDD ; 3 number dw NU dw RECDD ; 4 number dw NU dw RECDD ; 5 number dw NU dw RECDD ; 6 number dw NU dw RECDD ; 7 number dw NU dw RECDD ; 8 number dw NU dw RECDD ; 9 number dw NU dw RECCO ; : dw NOOP dw RECSC ; ; dw NOOP dw RECOP ; < restrict workspace dw BRA dw RECPR ; = test equality of top pair dw EQL dw RECOP ; > open workspace dw KET dw RECPR ; ? test for error report dw QM dw RECP1 ; @ execute subroutine dw AR dw RECPR ; A advance pointer 1 dw UCA dw RECPR ; B retract pointer 1 dw UCB dw RECOP ; C compile dw UCC dw RECOP ; D delete text dw UCD dw RECPR ; E equality between WS and PD dw UCE dw RECPR ; F find specified text dw UCF dw RECOP ; G fetch a block from memory dw GA dw RECPR ; H ASCII hex to binary dw HE dw RECOP ; I insert dw UCI dw RECOP ; J jump to front dw UCJ dw RECOP ; K call CP/M, keep (dx), put value dw CPM dw RECOP ; L erase top of PDL dw UCL dw RECPR ; M compare PDL and workspace dw UCM dw RECPR ; N numerical comparison dw UCN dw RECPR ; O decimal ASCII string to binary dw UCO dw RECOP ; P put block into buffered memory dw UCP dw RECOP ; Q put workspace segment on PD dw UCQ dw RECOP ; R read from keyboard dw UCR dw RECOP ; S store block in memory dw SA dw RECOP ; T write on screen dw UCT dw RECPR ; U search, yielding interval dw UCU dw RECPR ; V U, including endpoints dw UCV dw RECOP ; W write on printer dw UCW dw RECOP ; X call library subroutine dw LIBR dw RECPR ; Y recover previous position of p1 dw UCY dw RECOP ; Z pointer 2 to end of text dw UCZ dw RECCM ; [ comment dw NOOP dw RECOP ; \ insert single byte in pair dw IP dw RECOP ; ] dw NOOP dw RECOP ; ^ increment top argument dw INCR dw RECOP ; _ exit to monitor dw boot dw RECPR ; ` true for waiting character dw CHAW dw RECPR ; a segment forward from p1 dw LCA dw RECPR ; b segment backward from p2 dw LCB dw RECOP ; c create block on PDL dw BLOK dw RECPR ; d decrement but skip on zero dw decR dw RECPR ; e extend workspace dw LCE dw RECPR ; f block fill dw LCF dw RECOP ; g non-incrementing byte fetch dw GB dw RECOP ; h dw NOOP dw RECOP ; i input from designated port dw LCI dw RECOP ; j null interval at p1 dw LCJ dw RECOP ; k call CP/M: no returned values dw CPML dw RECOP ; l put pz on PDL dw Lcl dw RECOP ; m set aside top argument dw LCM dw RECOP ; n recover set-aside argument dw LCN dw RECOP ; o output from designated port dw LCO dw RECOP ; p put px, py-px on PDL dw GXS dw RECOP ; q put p1, p2-p1 on PDL dw LCQ dw RECOP ; r indirect replacement of address dw IND dw RECOP ; s store block in memory wrt limit dw LCS dw RECOP ; t type out indicated interval dw LCT dw RECOP ; u incrementing byte fetch dw GBI dw RECOP ; v incrementing byte store dw SAI dw RECOP ; w store workspace header dw LCW dw RECPR ; x subroutine jump - no arguments dw GO dw RECOP ; y fetch byte pair to PDL incr org dw GWI dw RECOP ; z null interval at p2 dw LCZ dw LBR ; { start a definition string dw NOOP dw RECOP ; | concatinate top two arguments dw CONC dw RECOP ; } end a definition string dw NOOP dw RECOP ; ~ complement or negate top arg dw COMP dw RECOP ; del dw NOOP ENDREC: ds 0 ; Program locations and memory allocations. Here is where ; the memory is sectioned up into its functional components. STAK equ 0FFEEH ;8086's pushdown stack EW equ STAK-0800H ;end of the workspace WS equ 0C000H ;beginning of the workspace EK equ WS ;end of the compiling area KA equ 06000H ;beginning of the compiling area VT equ KA-0400H ;directory for programs compiled by REC EP equ VT ;end of the pushdown list PD equ ENDREC ;beginning of the pushdown list END MAIN