; Program: ALIAS ; Author: Richard Conn ; Version: 1.1 ; Date: 10 June 84 ; Previous Versions: 1.0 (5 Mar 84) ; ;version equ 11 ; Don't know the date ; ; Module: ALIAS0.Z80 ; Author: Joe Wright ; Date: 24 August 90 ; Version: 1.2 ; version equ 17 ; 21 Jan 91 ; Add call to CAPS in EVAL: and shortened FIN: Thanks Bruce Morgen ;version equ 16 21 November 1990 (Now eight records long) ; Added new '$Rn' command to expand the contents of Registers ;version equ 15 ; 1 September 1990 ; Added new Pointer parameter support. See description ; at the label POINT: ;version equ 14 ; 30 August 1990 ; ; Squeezed a few more bytes out of it by eliminating checks for ; whether MCL or EXTFCB exist. If they don't, you can't run the ; program anyway. ; ;version equ 13 ; August 26, 1990 ; ; Rob Friefeld added ACLBUF and RECUR as PUBLIC items for SALIAS ; and shortened the code by the judicious use of the LDI instruction ; and two JP's converted to JR's. Rob saved nine bytes and chided ; me mildly for not squeezing more out of it. I have now taken ; an additional 29 bytes out of it. Back to you Rob. Thanks. ; ;version equ 12 ; August 24, 1990 ; ; At 1.2, ALIAS has been changed such that the module which ; is written to disk as the alias (this one) is the first module ; in the link order. ; ; Assemble ALIAS0.Z80 and ALIAS1.Z80 to REL with your favorite ; M80, ZMAC or Z80ASM. Then link as follows: ; ; ZML ALIAS=ALIAS0,ALIAS1 ; or SLRNKP ALIAS/N,/A:100/J,ALIAS0,ALIAS1,/E ; ; The purpose of ALIAS0 is to load the Command Line Buffer with ; a command line stored within ALIAS0, extracting parameters using ; the SUBMIT file convention ($n) as they are referenced in the ; new command line. Upon successful build, ALIAS0 runs the new ; command line by simply returning to ZCPR3. ; ; Basic Equates ; no equ 0 yes equ not no ; ; ALIAS0 can be assembled and linked to a COM file creating a ; stand-alone alias for testing or some other purpose. ; alone equ no ; tbuff equ 80h ; Command tail if not alone public alias0, start, z3eadr, aclbuf, recur extrn alias1 endif ; not alone ; alias0: if alone jp start ; Execute this module else jp alias1 ; Execute ALIAS1 endif db 'Z3ENV' ; This is a ZCPR3 Utility db 1 ; External Environment Descriptor z3eadr: dw 0 ; Installed by Z33+ db 'F' ; Id for VALIAS recursion (history) recur: db 0 ; Normal Mode (0ffh is Recursive) start: ld hl,(z3eadr) ; Pt to ZCPR3 environment ld a,h or a ret z ; Not installed, return jp start1 ; Skip command line buffer ; ; ALIAS ID at START+9 ; db 'Z3 ALIAS' ; ; Internal Command Line Buffer. This buffer address can be ; determined from START+17, where the value of START is ; obtained from the 2nd and 3rd bytes of ALIAS0. ; aclbuf: clbuf: db 0 ; Set to empty ds 256-($-clbuf) ; Allow 256 bytes ; ; Resume ALIAS0 ; start1: ld hl,clbuf ; Pt to buffer ld de,endcod ; Pt to free space in which to build new line ; ; Process Next Char from Target Command Line ; nxtchar: ld a,(hl) ; Get next char or a ; End of line? jp z,done ldi ; (hl) to (de), increment HL and DE cp '$' ; Possible passed parameter? ld bc,nxtchar ; Establish return address push bc ; ... on stack ret nz ; Not a '$' parameter, 'ret' to NXTCHAR ; ; Process Parameter ; dec de ; Back up to '$' for overwriting ld a,(hl) ; Get parameter character inc hl ; Advance HL to next character call caps ; Capitalize it cp 'R' jp z,paramr ; Registers? cp '.' jp z,point ; Pointers? cp '*' ; Entire tail? jp z,paraml cp 'D' ; Current disk? jp z,paramd cp 'U' ; Current user? jp z,paramu cp 'F' ; System Filename.Typ? jr z,paramf cp 'N' ; System Filename? jr z,paramn cp 'T' ; System Typ? jr z,paramt ; ; The A register has remained unchanged until now. ; sub '0' ; Convert to binary jr z,oname ; Parameter $0 = original name cp 10 jp nc,noparam ; Something other than 0..9 ; ; Get a command line token, $1..$9 ; ld b,a ; Count in B (1..9) push hl ; Save ptr ld hl,tbuff+1 ; Pt to input line (a Space) ; ; Advance to Desired Token ; token1: call skip ; Advance HL to next token or a ; Check for Null jr z,tokenx ; End of the line djnz token1 ; Advance HL until B=0 ; ; Extract Token Into Target Buffer ; token2: ld a,(hl) ; Get char cp ' '+1 ; Cy if Space or Null jr c,tokenx ; Quit ldi ; (hl) to (de), increment HL and DE jr token2 ; ; $0 - ALIAS Command Name ; oname: push hl ; Save ptr call getefcb ; Pt to EXTFCB inc hl ; Pt to first char call putn ; Put Name ; ; Resume Processing of Target Command ; tokenx: pop hl ; Restore ptr to next char ret ; Resume at next char ; ; Parameter is for System Filename.typ ; paramf: call getfnum ; Get file number push hl ; Save ptr to next char call ptfn ; Set ptr to file name call putn ; Put file name ld a,'.' ; Dot ld (de),a inc de call putt ; Put file type pop hl ; Restore ptr ret ; ; Parameter is for System Filename ; paramn: call getfnum ; Get file number push hl ; Save ptr to next char call ptfn ; Set ptr to file name call putn ; Put file name pop hl ; Restore ptr ret ; ; Parameter is for System Typ ; paramt: call getfnum ; Get file number push hl ; Save ptr to next char call ptfn ; Set ptr to file name ld a,8 ; Add 8 to get to file type call addah ; Point to file type call putt ; Put file type pop hl ; Restore ptr ret ; ; Get File Number (0 to 4) Now includes SH.VAR as File 0. ; If valid number, return with value in A and HL pting to next char ; If not valid, return with Z and HL pting to next char (the number) ; getfnum: ld a,(hl) ; Get char sub '0' ; Convert to binary (0..4) cp 5 ; Range? jr nc,getfne ; Error if more inc hl ; Pt to next char ret ; NZ and Cy from CP 5 (Ok) getfne: pop bc ; Pop this CALL off the stack ret ; 'Return' to NXTCHAR: ; ; Pt to File Name whose Number (0..4) is in A ; ptfn: ld b,a ; Save file number call getfn0 ; Pt to SH.VAR entry ld a,b ; Get file number or a ret z ; File 0 ld bc,11 ; Size of file name and type ptfn1: add hl,bc ; Pt to next dec a ; Count down jr nz,ptfn1 ret ; ; Put File Name pted to by HL ; putn: ld b,8 ; 8 chars jr putc ; ; Put File Type pted to by HL ; putt: ld b,3 ; 3 chars ; ; Copy chars from HL to DE for up to B bytes - flush if space ; putc: ld a,(hl) ; Get next char cp ' ' ; Skip spaces jr z,putc1 ld (de),a ; Put next char inc de ; Pt to next putc1: inc hl ; Pt to next djnz putc ret ; ; Parameter is for Disk Letter ; paramd: call retud ; Get DU in BC ld a,b ; Get disk letter add a,'A' ; Convert to ASCII jr ldinc ; ; Parameter is for User Number ; paramu: call retud ; Get DU in BC ld a,c ; Get user number ; ; MAFDC is similar the its namesake in SYSLIB. It will convert A ; to ASCII and place 1, 2 or 3 ASCII digits in memory at DE, advancing DE. ; It is used here and by PARAMR: It corrupts registers A and B. ; mafdc: push hl ld b,255 ; Suppress leading zeros ld h,100 call pac ; Check 100's ld h,10 call pac ; Check 10's call paco ; Put 1's digit in any case pop hl ret pac: ld l,-1 ; Preset a counter pac0: sub h ; Divide by H inc l ; L = successful divisions jr nc,pac0 ; Until underflow add a,h ; Correct underflow ld h,a ; Save accumulator ld a,l ; Get the digit or a jr nz,paco ; Put it in memory ; ; Digit was 0. Check for suppression ; ld a,h ; Get accumulator again bit 7,b ; Check if B is corrupted yet ret nz ; Not yet, No leading zeros ; ; This is an intermediate 0, i.e. '101', put it in memory ; xor a ; Clear A ; ; Convert A to to an ASCII digit and put it in memory ; paco: call noparam ; Use existing subroutine ld b,a ; Corrupt B for leading zero check ld a,h ; Get the remainder in A ret ; ; Form assumed to be '$$', however any unrecognized form after the ; first '$' is placed in the command tail. ; noparam: add a,'0' ; Restore the character ; ; General purpose routine. Store A at (de) and increment DE ; ldinc: ld (de),a ; Store char inc de ; Pt to next ret ; ; Parameter is command line tail ; paraml: push hl ; Save ptr to parameter ld hl,tbuff+1 ; Pt to tail ld a,(hl) or a jr z,paramt2 ; No tail, Quit inc hl ; Skip the first space paramt1: ld a,(hl) ; Copy tail into line ldi ; (hl) to (de), increment HL and DE or a ; Check for Null jr nz,paramt1 ; Again dec de ; Point to ending Null paramt2: pop hl ; Pt to next char in script ret ; Continue processing ; ; Pointers take the form '$.ADDR[[.OFF]+OFF]'. ADDR is the address ; of a pointer to a known data structure. For example, '$.109' is ; the Z3EADR pointer and will expand with the Z3ENV address. Using ; an offset, we get pointers from the environment. For example ; '$.109.22' expands the Z3MSG address and '$.109.22.4' expands ; the Error Address at Z3MSG+4. The '+' operator is used to add an ; offset to the last pointer retrieved. For example, we can point ; to the Program Error Flag with '$.109.22+6'. ; point: push de ; Save destination pointer call evals ; Evaluate hex string at HL to ex de,hl ; binary in DE call lhlhl ; Get the pointer ex de,hl ; Put it in DE ; inter: ld a,(hl) ; Get next character ; ; We are only interested in '.', '+' or '-'. Reject others. ; cp '+' jr c,fin ; Less than '+' cp ',' jr z,fin cp '/' jr nc,fin ; '/' or greater cp '.' ; Is it another '.' or a sign push af ; Save the flag, Z to load new pointer push de ; Save intermediate result call evalh ; Evaluate the offset to DE pop bc ; Get intermediate result pop af ; Get the flag back push hl ; Save HL pointer ex de,hl ; Offset to HL add hl,bc ; Add last intermediate call z,lhlhl ; Get whatever is there if pointer ex de,hl ; Put it in DE pop hl ; Restore HL pointer jr inter ; Try again ; ; End of our string, put it in memory. ; fin: ld a,d ; Final result, High order in A ld c,e ; Low order to C pop de ; Restore destination pointer call puta ; Put two characters ld a,c ; Low order ; ; Put A at destination as two hexadecimal characters ; puta: push af ; Save A rra rra rra rra ; Shift Hi nybble Lo call puth ; Put it in as hex pop af ; Restore A ; ; Put A at destination as a hex character 0..9, A..F ; puth: and 0fh ; Clear Hi nybble add a,90h ; Add BCD bias daa ; Adjust it adc a,40h ; Add hex bias daa ; Adjust it ld (de),a ; Put it in memory inc de ; Advance destination pointer ret ; ; Evaluate the string at HL as hexadecimal, Result to DE ; evalh: inc hl ; Advance HL in any case cp '.' ; Cy if '+' or '-' jr c,sign ; evals: ld a,(hl) ; Character following '.' cp '.' jr nc,eval ; Not a sign, evaluate as positive inc hl ; Skip past the sign sign: cp '-' jr nz,eval ; Must be '+' call eval ; Get positive number push hl ; Save pointer ld hl,0 ; Zeros or a sbc hl,de ; Two's complement of DE ex de,hl ; To DE pop hl ; Restore pointer ret ; eval: ld de,0 ; Clear an accumulator eval0: ld a,(hl) ; Get a character call caps ; Ensure A..F not a..f if hex sub '0' ; Remove ascii bias cp 10 ; Check 0..9 jr c,eval1 ; Ok sub 7 ; Remove hex bias cp 10 ret c ; Less than A cp 16 ; Check A..F ret nc ; Finished if not, result in DE eval1: inc hl ; Advance the pointer ex de,hl ; Partial to HL add hl,hl add hl,hl add hl,hl add hl,hl ; Multiply by 16 add a,l ; Add the digit ld l,a ; Put it back ex de,hl ; Partial to DE jr eval0 ; Again ; ; Return Decimal value from Decimal Register address (0..31) ; paramr: call d2b ; Decimal to Binary (0..FFh) push hl ; Save pointer call getreg ; Get the contents of the register call mafdc ; Put it at DE in Decimal ascii pop hl ret ; ; Convert Decimal string at HL ('0'..'255') to Binary in C (0..FFh) ; advancing HL as it goes. Corrupts BC and AF. ; d2b: ld c,0 ; Clear an accumulator d2b0: ld a,(hl) ; Get a potential digit sub '0' ; Remove ascii bias cp 10 ; Must be 0..9 ret nc ; It is not, result in C inc hl ; Advance pointer ld b,a ; Save A ld a,c ; Get accumulator add a,a ; x2 add a,a ; x4 add a,c ; x5 add a,a ; x10 add a,b ; Plus new digit ld c,a ; Back to C jr d2b0 ; Again.. ; ; Done -- Buffer is Loaded ; done: ld (de),a ; Store ending 0 ld hl,endcod ; Pt to beginning of buffer ld a,(hl) ; Skip if empty line or a ret z ; Simply return ; ex de,hl ; DE points to Our new line call getmcl ; Get Z3CL pointer call putcl ; Put expanded command to MCL ret nz ; Return to ZCPR3 for processing if OK ld de,ovfl ; Overflow message ld c,9 ; Print string function jp 5 ; Let the BDOS do it ovfl: db 'Ovfl$' ; ; PUTCL stores a command line in the ZCPR3 command line buffer. ; This command line is pted to by DE. CL Buffer is pted to by HL. ; Size in A. On return, A=0 and Zero flag Set if command line ; overflow is possible (no change to command line). ; putcl: inc hl inc hl ; Point to Z3CLS ld b,(hl) ; MCL length (usually 203 characters) ex de,hl ; HL pts to new line push hl ; Save ptr to new line ; ; Go to the end of our line, counting characters as we go. ; We also test the length of each expansion to see that it ; won't overflow TBUFF when it is eventually run. ; ; PCL1 advances HL past the command verb as it is not moved to ; the TBUFF when the command is run. The exit of this seemingly ; interminable loop is through PCL2A when a Null is encountered. ; pcl1: call pcl2a ; Get a character and advance HL semi: dec b ; Decrement MCL count jr z,pclx ; Longer than the MCL cp ' ' jr nz,pcl1 ; Keep going until Space ; ; PCL2 advances HL until the next ';' checking that the tail ; does not exceed 126 characters. ; ld c,126 ; Maximum tail length pcl2: call pcl2a ; Get a character and advance HL cp ';' jr z,semi ; End of tail, start over dec c ; Tail count jr z,pclx ; Tail is too long djnz pcl2 ; Decrement MCL count ; ; Expanded command tail is longer than the MCL. ; pclx: pop hl ; Clear stack ; ; Command Line Buffer Overflow ; nocl: xor a ; Error return ret ; pcl2a: ld a,(hl) ; Get a character inc hl ; Advance HL or a ; Check for Null ret nz ; No ; ; This is the exit from PCL1 or PCL2 when a Null is encountered ; dec hl ; Back up the pointer to the null pop de ; Adjust stack and fall into PCL3 ; ; HL pts to ending 0 of new command line ; B = number of chars remaining before overflow of Z3 command line ; pcl3: push hl ; Save ptr to last byte in case of error call getmcl ; Pt to tail of command line call lhlhl ; Get NXTCHR pointer in HL pop de ; Restore ptr to last byte of command line push de ; Save it again ; ; Check for Recursion and clear existing MCL if so. ; ld a,(recur) ; Get recursion flag or a jr z,pcl3a ; Normal Mode ld (hl),0 ; Clear the MCL pcl3a: ld a,(hl) ; Get next character of MCL cp ';' ; Continuation? jr z,pcl4 or a ; Done? jr z,pcl4 ld a,';' ; Set continuation char ld (de),a inc de dec b ; Count down jr z,pcl5 ; Overflow ; ; Copy current MCL onto end of new command line. Enter with B equal ; to the number of bytes remaining free in the MCL after the new ; Command Line. ; pcl4: ld a,(hl) ; Get next char of MCL ld (de),a ; Store it in our line inc hl ; Pt to next inc de or a ; Done? jr z,pcl6 ; Yes djnz pcl4 ; Keep it up ; ; Command Line too Long ; pcl5: pop hl ; Get ptr to end of old line pop af ; Clear stack jr nocl ; Report Ovfl ; ; New Command Line OK ; pcl6: pop af ; Clear stack call getmcl ; Pt to command line buffer ld de,4 ; Pt to first char in buffer ex de,hl ; Swap 'em add hl,de ex de,hl ; Swap 'em again ld (hl),e ; Store Z3CL+4 at Z3CL inc hl ld (hl),d ; DE pts to first char of buffer pop hl ; HL pts to first char of new line ; ; Copy New Command Line into Buffer ; pcl7: ld a,(hl) ; Copy ldi ; (hl) to (de), inc HL and DE or a ; EOL? jr nz,pcl7 ; ; Exit with OK Code ; dec a ; Set NZ, Ok ret ; ; ALIAS0 does not use the libraries. Z3LIB and SYSLIB routines ; are emulated here. ; ; Return in A, the contents of the Register in C. ; getreg: call getmsg ; Point to Z3MSG ld a,30h ; Offset to Register 0 add a,c ; Plus desired Register number call addah ; Point HL to it ld a,(hl) ; Get the register contents ret getmsg: ld a,22h ; Offset to Z3MSG jr getenv ; Return HL with Z3MSG address getefcb: ld a,24h ; Offset to EXTFCB jr getenv ; Return HL with EXTFCB address ; ; Get pointer to the Multiple Command Line in HL, A = Z if none. ; getmcl: ld a,18h ; Offset to Z3CL ; ; Given an offset in A, go to the Environment for a segment address ; or other data. Return data in L, the address in HL. ; getenv: call offenv ; Point HL to the address in Z3ENV ; ; Load HL with the next two bytes it is pointing to. ; lhlhl: ld a,(hl) ; Low order address inc hl ld h,(hl) ; High order address ld l,a ret ; ; Point HL to the prototype SH.VAR system filename in Z3ENV ; getfn0: ld a,47h ; Offset to SH.VAR entry ; ; Given an offset in A, point HL to an item in Z3ENV ; offenv: ld hl,(z3eadr) ; Z3ENV address ; ; Add the value in A to HL ; addah: add a,l ; Add L to A ld l,a ; New L ret nc ; Sum LT 256 inc h ; New H ret ; ; Skip to next token or Null ; skip: ld a,(hl) or a ret z ; Null inc hl cp ' ' jr nz,skip ; ; Skip Spaces - Point HL to first non-Space ; sksp: ld a,(hl) cp ' ' ret nz inc hl jr sksp ; ; Return current Drive in B (rel 0) and current user in C. ; retud: push hl push de ; Save the registers ld c,25 call 5 ; Current disk push af ld e,255 ld c,32 call 5 ; Current user ld c,a ; User to C pop af ld b,a ; Drive to B pop de pop hl ; Restore the registers ret ; ; Capitalize the character in A, if necessary. ; caps: cp 'a' ret c ; Less than 'a' cp 'z'+1 ret nc ; Greater than 'z' and 5fh ; Force Upper case ret ; ; Buffer ; endcod: ds 128-($-alias0) and 127 ; Record boundary for ALIAS0 end ; ; End of ALIAS0.Z80