;============================================================================ ; ; ZCMDRSP ; Resident command processor for ZCMDxx ; ; RSP generation: Gene Nolan ; DATE : 2/25/88 ; This collections of routines along with the RSP handler was ; assembled with the need in mind of the limitiations ; imposed by most CPM 2.2 installations requireing the CCP to be held ; to 2k byte size. ; The RSP handler and its implementation are made possible by sharing ; a small but powerfull data base with ZCMD. This data base is ; responsible for holding the entry point of the RSP along with ; a check word to verify it's previous loading upon a warmboot. ; This data base will reside immediatly above the ZCMD BDOS revector. ; IE: ; ADDR Contents ; 5 JP RSPVEC OUR BDOS REVECTOR ; . ; normal CPM reserved and TPA ; . ; RSPVEC JP BUFSPAC ZCMD BDOS REVECTOR ; . ; we are in here ; . ; BUFSPAC JP BDOS ORIG BDOS VECTOR ; " +3 JP RSPENTRY ZCMD ENTERS US HERE ; " +6 ADDR OF RSPVECTOR+1 THIS WILL CONTAIN ADDR ; OF BUFSPAC IF WE ARE LOADED ; Assembly/linkage/relocation: ; ; This program was meant to be assembled with M80 and linked by ; L80 (M80,L80 copyright MICROSOFT CORP.) ; ; Relocation is assumed to be done by RELOC23(Ashby Method) ;============================================================================= ; ; D E F I N I T I O N S ; ;============================================================================= zcmrspv equ 01 ; version 0.1 numbase equ 'H' ; Character to switch to hexadecimal numbers ; Offsets into array holding addr of error messages ecambig equ 0 ; Ambiguious file name ecbadnum equ 02 ; bad number ecfull equ 04 ; Directory or disk full error tfcb equ 005ch tfcb2 equ 006ch tail equ 0080H tpa equ 0100h bdos equ 0005h bell equ 07h tab equ 09h lf equ 0ah cr equ 0dh drvmax equ 3dh whladr equ 3eh usrmax equ 3fh ;============================================================================= ; ; M A I N C O D E ; ;============================================================================= .z80 cseg $MEMRY:: ds 2 ;for the RELOC23 code jp entry ; ditto entry: ld hl,(6) ;MUST point to BUFSPAC in ZCMD30 ld (rcpvec+1),hl ;bdos vector to here inc hl ;skip over ZCMD vector inc hl inc hl ld (hl),0c3h ;put a jmp instr in ISGO in zcmd inc hl ld de,rcpentry ;put our entry address in to ZCMD ld (hl),e inc hl ld (hl),d inc hl ld de,rcpvec ;BDOS vector to our vector(steal from ld (6),de ; TPA) inc de ld (hl),e ;ZMC will check the addr in this to see inc hl ;if it points to bufspac on a warmboot ld (hl),d ;to see if rcp was loaded. ld de,rcpmes ld c,9 ;print string call 5 ret ;back from RCP instalation rcpmes: db cr,lf db ' ******* ZCMDRCP ' db zcmrspv/10+'0' db '.' db zcmrspv mod 10 + '0' db ' has been loaded, available commands are:' db cr,lf db lf,' SAVE ##(h) UFN (S) where' db cr db lf,' ##(h) is decimal(hex) number of pages to save and' db cr db lf,' UFN is an unambiguous file name and' db cr db lf,' S is a flag to write ## sectors, not pages' db cr,lf db lf,' NDR NAME D: ' db cr,lf db lf,' NORCP Terminate RCP (Cannot be used with BYE)' db cr,lf,lf,'$' rcpvec: jp 0 ;0 will be filled in with BDOS revector rcpentry: ;enter here with DE=addr of command string ;entered,check if we can handle it ld (cms1+1),de ld a,(curccnt) ; Set command counter ld c,a ld hl,cmdtbl ; CMS1: ld de,0 ; Point to stored command name ld b,nchars ; Number of chars/command (8 max) ; CMS2: ld a,(de) ; Compare against table entry cp (hl) jr nz,cms3 ; No match inc de ; Point to next character inc hl djnz cms2 ; Count down ld a,(de) ; Next char must be a space cp ' ' jr nz,cms4 ld c,(hl) ; return bc with value in cmd table inc hl ;if non-zero B, then C holds DU spec and ;will be processed by CMDSER in ZCMD ld b,(hl) inc hl ld a,(hl) ;get lsbyt of addr inc hl ld h,(hl) ;get msbyt of adr ld l,a ;hl=addr of routine pop ix ;return address to CMDSER in ZCMD pop de ;return adr FOR CMDSER in ZCMD push hl ;exec addr expected below return addr ;will be popped by JUMP1: in ZCMD push de push ix xor a ;set z flag for return ret ; Command is RCP-resident ; ; CMS3: inc hl ; Skip to next command table entry djnz CMS3 ; CMS4: inc hl ; Skip DU inc hl inc hl ; skip ADDR inc hl dec c ; Decrement table entry number jr nz,cms1 ; CMS5: xor a ;set nz to say we can't handle it inc a ret nchars equ 8 ;max num chars in a command ;macro used as a cmdtbl extender blcmd macro db ' ' ;name its know as dw 0 ;not a DU entry dw nocmd ;execution addr endm ;table of current commands supported by RCP ; ; Form is for each command ; ; ENTRY USE ; ; 1 NAME of length nchars used to invoke command ; 2 WORD with high byte<>=0 means lowbyte is DU ; 3 WORD specifying execution address of command(not used if entry 2 ; high byte non-zero) cmdtbl: db 'SAVE ' ;command is known as dw 0 ;not a DU in low byte dw save ;execution address db 'NDR ' dw 0 dw NDR db 'NORCP ' dw 0 dw norcp dB 'HOME ' ;command is known as DW 100H ;is a DU with D=A, U=0 DW NOCMD ;a dont care here DB 'LIBRARY ' DW 1E1H ;is a DU with D=B, U=14 DW NOCMD lstcmd: blcmd ;current last entry in table blcmd blcmd blcmd blcmd blcmd numcmds equ ($-cmdtbl)/(nchars+4) ;total # commands table can hold maxcmd: db numcmds ;total number of commands tbl will ;hold lstcmdp: dw lstcmd ;pointer to last entry in table ; curccnt is the number of commands currently recognized curccnt: db (lstcmd-cmdtbl)/(nchars+4) ; a dummy ret for commands whose execution address is a don't care nocmd: ret ;**************************************** ; ; Program NORCP ; ; Remove the RCP from the TPA high memory ; ; NOTE: The will raise holy heck if BYE is running, and should be removed ; from the command table if used with BYE ; NORCP: ld hl,(rcpvec+1) ;this point to ZCMD3 BDOS revector ld de,3 ;data base for the RCP is after the ;JMP at the revector ld b,5 ;5 bytes in the data base add hl,de ;point to the data base xor a norcp1: ld (hl),a ;and zero it out inc hl djnz norcp1 jp 0 ;force a warm boot, which will ;cause ZCMD3 to reset the BDOS revector ;***************************************** ; ; Program NDR ; ; Given NAME DU: ; ; Where if D or U is missing, the current will apply ; ; Add entry to command table(if room) that if executed will cause ; Automatic selection of the DU as specified when defined NDR: ; ld hl,5ch ;debug code ; ld de,jnkl1 ; ld bc,128 ; ldir ld a,(whladr) ;if no wheel, can't name directories or a jr nz,okndr call print db cr,lf,'Command not available',cr,lf,0 okndr: ld a,(curccnt) ;is cmd tbl full? cp numcmds jr c,cmdroom call print db cr,lf,lf,'CMD TABLE FULL, request ignored',cr,lf,0 ret cmdroom: ld a,(4) ;get current DU push af and 0fh inc a ld (tempdr),a ;save away D pop af srl a srl a srl a srl a ld (tempusr1),a ;save away U call scaner ;Scan TBUF for for valid DU spec jr z,okscaner ;z=good scan jp p,ndrret ;p=user goofed ret ;get here if was m, the bad number or disk ndrret: call print db cr,lf,'Usage= NDR NAME D: ',cr,lf,0 ret okscaner: ld a,(curccnt) ;one more cmd in table inc a ld (curccnt),a ld hl,tfcb+1 ;point to FCB set up by ZCMD ld de,(lstcmdp) ;put nchars of name into cmdtbl ld bc,nchars ldir ;move to cmdtbl ex de,hl ld (lstcmdp),de ld a,(tempdr) ;form UD dec a ld c,a ld a,(tempusr1) sla a sla a sla a sla a or c ld (hl),a ;and put in tbl inc hl ld (hl),1 ;byte byte of word says cmd in a named directory inc hl inc hl ;skip over execution address inc hl ld (lstcmdp),hl ;update pointer to next free table entry ret ; scaner: ld hl,tail ;point to what the user entered as a cmd tail ld a,(hl) ;byte cnt 0 or a jr nz,scaner1 inc a ;yes, set NZ,P and return to put up usage ret ;message scaner1: ld hl,tail+2 ; scansp: ld a,(hl) ;search for first non-space,non-zero char cp ' ' jr z,fspace or a jr z,whoknow inc hl jr scansp fspace: ld a,(hl) or a jr z,whoknow cp ' ' jr nz,ishere inc hl jr fspace whoknow: xor a ;who knows, return and put up useage message inc a ret ishere: ; ; Scan token for DU: form, which means we have a user/disk specification ; DE points to next character in line, HL points to FCBDN. ; ex de,hl PUSH DE ; Save pointer to first character CALL SDELM1 ; Check for delimiter and get first char CP 'A' ; In letter range? JR C,SCAN1 CP 'P'+1 ; In letter range? JR C,SCAN1A ; SCAN1: CP '0' ; Check for digit range JR C,SCAN2 CP '9'+1 ; In digit range? JR NC,SCAN2 ; SCAN1A: INC DE ; Pt to next char CALL SDELM1 ; Check for delimiter, else digit JR SCAN1 ; SCAN2: POP DE ; Restore ptr to first char CP ':' ; Was delimiter a colon? JR NZ,whoknow ; Done if no colon ; STA COLON ; Set colon found ; ; ; Scan for and extract user/disk info - on entry, HL point to FCBDN, DE ; points to first character and A-register contains the first character. ; LD A,(DE) ; Get first character CP 'A' ; Convert possible drive spec to number JR C,SUD1 ; If less than 'a', must be digit ; ; ; Set disk number (A=1) ; SUB 'A'-1 ; PUSH BC ; Save 'BC' PUSH AF ; Save drive request LD A,(DRVMAX) ; Get maximum legal drive ADD a,2 ; Bump it two for the compare LD B,A ; Save maximum drive in 'B' POP AF ; Restore drive request CP B ; See if illegal drive POP BC ; Restore bc Jr NC,dERROR ; Invalid disk number ld (tempdr),a ; Set temporary drive number INC DE ; Pt to next char LD A,(DE) ; See if it is a colon (:) CP ':' JR Z,SUD2 ; Done if no user number (it is a colon) ; ; ; Set user number ; SUD1: EX DE,HL ; Hl pts to first digit CALL NUM0A ; Get number ret m EX DE,HL ; De pts to terminating colon LD HL,USRMAX CP (HL) Jr NC,uERROR ld (tempusr1),a ; Save user number SUD2: xor a ret ; NUM0A: ld bc,1100H ; C=accumulated value, b=char count ; ; (c=0, b=11) NUM1: ld A,(hl) ; Get character CP ' ' ; Done if JR Z,NUM2 CP ':' ; Done if colon JR Z,NUM2 inc hl ; Pt to next char SUb '0' ; Convert to binary CP 10 ; Error if >= 10 JR NC,NUMERR ld D,A ; Digit in d ld A,C ; New value = old value * 10 sla a ; *2 JR C,NUMERR sla a ; *4 JR C,NUMERR sla a ; *8 JR C,NUMERR ADD a,C ; *9 JR C,NUMERR ADD a,C ; *10 JR C,NUMERR ADD a,D ; New value = old value * 10 + digit JR C,NUMERR ; Check for range error ld C,A ; Set new value DJNZ NUM1 ; Count down ; ; ; Return from number ; NUM2: ld A,C ; Get accumulated value RET uerror: call print db cr,lf,'*** User number out of range',cr,lf,0 jr ndrerr derror: call print db cr,lf,'*** Drive number out of range',cr,lf,0 jr ndrerr numerr: call print db cr,lf,'*** Bad number',cr,lf,0 ndrerr: xor a dec a ret ;..... ; ; ; Number error routine for space conservation ; SDELM1: LD a,(de) CP ' '+1 ; Delim if <= JR C,ZERO PUSH Hl ; SAVE PUSH Bc Ld Hl,DELMARY ; point to delimiter array Ld Bc,DELMCNT ; count of number of delimeters CPIR ; compare them ; Ret Z=1 if a match found POP Bc POP Hl RET ; ZERO: Xor A ; Set zero flag RET DELMARY: DB '=_.:,;<>' DELMCNT EQU $ - DELMARY ;Count of Number of delimeters to check tempusr1: db 0 tempdr: db 0 ; END scaner ;******************************************** ; ; PROGRAM: ZCMDSAVE ; MODIFIER: GENE NOLAN ; DATE: 2/21/87 ; FROM THE ORIG SAVE04.Z80 BY: ; AUTHOR: Jay Sage ; DATE: May 19, 1987 ; VERSION: 0.4 ; DERIVATION: ZCPR33 resident SAVE command sversnm equ 01 ; Version number of ZCMDSAVE secflg equ 'S' ; Character used at end of tail to say ; to num is sector count rather than page cnt ; Display version message SAVE: ld hl,tail ;if no cmd tail was entered put up usage message ld a,(hl) or a jp nz,onsave call print db cr,lf db lf,' SAVE ##(h) UFN (S) where' db cr db lf,' ##(h) is decimal(hex) number of pages to save and' db cr db lf,' UFN is an unambiguous file name and' db cr db lf,' S is a flag to write ## sectors, not pages' db cr,lf,0 ret onsave: ; ld hl,5ch ;debug code ; ld de,jnkl1 ; ld bc,128 ; ldir call print db cr,lf db ' ZCMDSAVE Version ' db sversnm/10+'0' db '.' db sversnm mod 10 + '0' db cr,lf,0 main: ld a,(whladr) ;only allow exec if wheel is on or a jr nz,main1 ; Branch if OK call print db bell,' Not Wheel',0 ret main1: ld (stack),sp ; Save system stack pointer ld sp,stack ; ..and set up a local stack ld hl,TFCB+1 ; Point to first character in command tail call number ; Extract number from command line jp c,badnumber ; Invoke error handler if bad number push bc ; number in bc ld hl,tail ; point to tail ld c,(hl) ; get char cnt ld b,0 add hl,bc ; point to last char chktail: ld a,(hl) cp ' ' ; space char? jr nz,chkend dec hl ; was space,keep going backwards dec bc ld a,b or c jr nz,chktail ; nz=not end of tail jr nosecflg chkend: cp secflg ; was first non-blank the sec trigger? jr z,wsecflg and 5fh cp secflg jr nz,nosecflg wsecflg: dec bc ; yes, but was it the first char? ld a,b or c jr z,nosecflg dec hl ld a,(hl) ; no, not first char, was char before it cp ' ' ; a space? jr nz,nosecflg pop hl ; yes, number specified sector, not page cnt ld a,3 ; Maximum allowed value in H if sectors jr save0 nosecflg: pop hl add hl,hl ; Double page count to get sector count ld a,1 ; Maximum allowed value in H if pages save0: cp h ; Make sure count < 64K jp c,badnumber ; If >64k, invoke error handler push hl ; Save sector count ld hl,tfcb2 ; move fcb2 to fcb1 ld de,tfcb ld bc,10h ldir call ambchk ; Check for ambiguous file spec (vectors to ; ..error handler if so) call extest ; Test for existence of file and abort if so ld c,16h ; BDOS make file function call bdostest jr z,save3 ; Branch if error in creating file pop bc ; Get sector count into BC ld hl,tpa-128 ; Set pointer to one record before TPA save1: ld a,b ; Check for BC = 0 or c dec bc ; Count down on sectors ; ..B=0ffh if all records written successfully jr z,save2 ; If so, save is done so branch push bc ; Save sector count ld de,128 ; Advance address by 128 bytes add hl,de push hl ; Save address on stack ex de,hl ; Put address into DE for BDOS call call dmaset ; Set DMA address for write ld de,tfcb ; Write sector ld c,15h ; BDOS write sector function call bdossave pop hl ; Get address back into HL pop bc ; Get sector count back into BC jr Z,save1 ; If write successful, go back for more ld b,0 ; B=0 if write failed save2: call close ; Close file even if last write failed and b ; Combine close return code with ; ..write success flag jr nz,save4 ; If all writes successful and close OK, branch save3: ; Disk must be full ld a,ecfull jr error save4: call print db ' ',0 ld hl,tfcb+1 call prfn call print db ' saved' db 0 jr done ambchk: push hl ; Save pointer to FCB inc hl ; Point to first character in file name ld a,(hl) ; See if first character is a space cp ' ' ld a,'?' ; Set up for scan for question mark jr nz,ambchk1 ; Skip if first character not a space ld (hl),a ; Substitute '?' for space ambchk1: ld bc,11 ; Scan 11 characters cpir pop de ; Restore pointer to FCB in DE ret nz ; Return if no '?' found pop hl ; Pop subroutine return address pop hl ; Pop sector count ld a,ecambig ; Error code for ambiguous file name jr error badnumber: ld a,ecbadnum ; Error code for bad number value jr error loaderr: ld a,ecfull error: ld e,a ld d,0 ld hl,ermary add hl,de ld e,(hl) inc hl ld d,(hl) ld c,9 ;Print string ftn call 5 ; Fall through to RET done: ld sp,(stack) ; Restore system stack pointer ret ermary: dw BADFILEN dw NUMBAD dw DODFULL BADFILEN: DB cr,lf,' *** File must be unambiguous ***',cr,lf,lf,'$' NUMBAD: DB cr,lf DB ' *** Not valid NUMBER or >511 pages ***',cr,lf,lf,'$' DODFULL: DB cr,lf db ' *** DISK or DIRECTORY FULL ***',cr,lf,lf,'$' ;----------------------------------------------------------------------------- ; Get uppercase character from console (with ^S processing). Registers B, ; D, H, and L are preserved. The character is returned in A. conin: ld c,1 ; BDOS conin function call bdossave ; Fall through to UCASE ;----------------------------------------------------------------------------- ; Convert character in A to upper case. All registers except A are preserved. ucase: and 7fh ; Mask out msb cp 61h ; Less than lower-case 'a'? ret c ; If so, return cp 7bh ; Greater than lower-case 'z'? ret nc ; If so, return and 5fh ; Otherwise capitalize ret ;----------------------------------------------------------------------------- ; Output character in A to the console. All registers are preserved. conout: push de push bc ld c,2 ; BDOS conout function ld e,a call bdossave pop bc pop de ret ;---------------------------------------- ; Print the character string immediately following the call to this routine. ; The string terminates with a character whose high bit is set or with a null. ; All registers are preserved except A. print: ex (sp),hl ; Get pointer to string call printhl ; Print string ex (sp),hl ; Restore HL and set return address ret ;---------------------------------------- ; Print the character string pointed to by HL. Terminate on character with ; the high bit set or on a null character. On return HL points to the byte ; after the last character displayed. All other registers except A are ; preserved. printhl: ld a,(hl) ; Get a character inc hl ; Point to next byte or a ; End of string null? ret z push af ; Save flags and 7fh ; Mask out msb call conout ; Print character pop af ; Get flags ret m ; String terminated by msb set jr printhl ;----------------------------------------------------------------------------- ; Test File in FCB for existence, ask user to delete if so, and abort if he ; choses not to extest: ld de,tfcb ; Point to FCB push de ; ..and save it for later call fcblog ; Log into specified directory call srchfst1 ; Look for specified file pop de ; Restore pointer ret z ; OK if not found, so return call print defb bell defb ' Erase',' '+80h ld hl,tfcb+1 ; Point to file name field call prfn ; Print it call print ; Add question mark defb '?' or 80h call conin ; Get user response cp 'Y' ; Test for permission to erase file jp nz,done ; If not, flush the entire command line jr delete ; Delete the file ;----------------------------------------------------------------------------- ; Print file name pointed to by HL prfn: ld b,8 ; Display 8 characters in name call prfn1 call print ; Put in dot defb '.' or 80h ld b,3 ; Display 3 characters in type prfn1: ld a,(hl) ; Get character inc hl ; Point to next call conout ; Print character djnz prfn1 ; Loop through them all ret ;---------------------------------------- ; Search for first matching file. At entry point SRCHFST1 the first default FCB ; is used. At entry point SRCHFST the FCB pointed to by DE is used. srchfst1: ld de,tfcb ; Use first default FCB srchfst: ld c,11h jr bdostest ;-------------------- ; Call BDOS for read and write operations. The flags are set appropriately. ; The BC, DE, and HL registers are preserved. bdossave: push hl push de push bc call bdos pop bc pop de pop hl or a ; Set flags ret ;----------------------------------------------------------------------------- ; Set DMA address. At the entry point DMASET it is set to the value passed ; in the DE registers. dmaset: ld c,1ah jr bdossave ;----------------------------------------------------------------------------- ; Log in the drive value passed in the A register (A=0). setdrive: ld e,a ld c,0eh jr bdossave ;----------------------------------------------------------------------------- ; Open a file. At entry point OPEN the file whose FCB is pointed to by DE ; is used. open: ld c,0fh ; BDOS open function ; Fall through to BDOSTEST ;-------------------- ; Invoke BDOS for disk functions. This routine increments the return code in ; register A so that the zero flag is set if there was an error. Registers ; BC, DE, and HL are preserved. bdostest: call bdossave inc a ; Set zero flag for error return ret ;----------------------------------------------------------------------------- ; Close file whose FCB is pointed to by DE. close: ld c,10h jr bdostest ;----------------------------------------------------------------------------- ; Delete file whose FCB is pointed to by DE. delete: ld c,13h jr bdossave ;----------------------------------------------------------------------------- ; Get and set user number. Registers B, D, H, and L are preserved. Register ; E is also preserved at entry point SETUSER1. getuser: ld a,0ffh ; Get current user number setuser: ld e,a ; User number in E setuser1: ld c,20h ; Get/Set BDOS function jr bdossave ;----------------------------------------------------------------------------- ; The routine NUMBER evaluates a string in the first FCB as either a decimal ; or, if terminated with the NUMBASE hexadecimal marker, a HEX number. If the ; conversion is successful, the value is returned as a 16-bit quantity in BC. ; If the number is less than 256, then the zero flag is set. If an invalid ; character is encountered in the string, the routine returns with the carry ; flag set and HL pointing to the offending character. number: ld hl,tfcb+1 ; Set pointer to beginning of number string ld bc,8 ; Number of characters to scan add hl,bc ; Point to end of character string dec hl ld a,numbase ; Scan for HEX identifier cpdr ; Do the search jr nz,decimal ; Branch if HEX identifier not found inc hl ; Point to HEX marker ld (hl),' ' ; Replace HEX marker with valid terminator ; ..and fall through to HEXNUM ;---------------------------------------- ; At this entry point the character string in the first default FCB is ; converted as a hexadecimal number (there must NOT be a HEX marker). hexnum: ld hl,tfcb+1 ; Point to string in first FCB ; At this entry point the character string pointed to by HL is converted ; as a hexadecimal number (there must be NO HEX marker at the end). hexnum1: ld de,16 ; HEX radix base jr radbin ; Invoke the generalized conversion routine ;---------------------------------------- ; This entry point performs decimal conversion of the string in the first ; default FCB. decimal: ld hl,tfcb+1 ; Set pointer to number string ; This entry point performs decimal conversion of the string pointed to ; by HL. decimal1: ld de,10 ; Decimal radix base ; Fall through to generalized ; ..radix conversion routine ; This routine converts the string pointed to by the entry on the top of ; the stack using the radix passed in DE. If the conversion is successful, ; the value is returned in BC. HL points to the character that terminated ; the number, and A contains that character. If an invalid character is ; encountered, the routine returns with the carry flag set and nonzero, and ; HL points to the offending character. radbin: ld bc,0 ; Initialize result radbin1: or a ; Make sure carry is reset call sdelm ; Test for delimiter (returns Z if delimiter) ret z ; Return if delimiter encountered sub '0' ; See if less than '0' ret c ; Return with carry set if so cp 10 ; See if in range '0'..'9' jr c,radbin2 ; Branch if it is valid cp 'A'-'0' ; Bad character if < 'A' ret c ; ..so we return with carry set sub 7 ; Convert to range 10..15 radbin2: cp e ; Compare to radix in E ccf ; Carry should be set; this will clear it ret c ; If carry now set, we have an error inc hl ; Point to next character push bc ; Push the result we are forming onto the stack ex (sp),hl ; Now HL=result, (sp)=source pointer call mpy16 ; HLBC = previous$result * radix ld h,0 ; Discard high 16 bits and ld l,a ; ..move current digit into HL add hl,bc ; Form new result ld c,l ; Move it into BC ld b,h pop hl ; Get string pointer back jr radbin1 ; Loop until delimiter ;----------------------------------------------------------------------------- ; This routine multiplies the 16-bit values in DE and HL and returns the ; 32-bit result in HLBC (HL has high 16 bits; BC has low 16 bits). Register ; pair AF is preserved. mpy16: ex af,af' ; Save AF ld a,h ; Transfer factor in HL to A and C ld c,l ld hl,0 ; Initialize product ld b,16 ; Set bit counter rra ; Shift AC right so first multiplier bit rr c ; ..is in carry flag mp161: jr nc,mp162 ; If carry not set, skip the addition add hl,de ; Add multiplicand mp162: rr h ; Rotate HL right, low bit into carry rr l rra ; Continue rotating through AC, with rr c ; ..next multiplier bit moving into carry djnz mp161 ; Loop through 16 bits ld b,a ; Move A to B so result is in HLBC ex af,af' ; Restore original AF registers ret ;----------------------------------------------------------------------------- ; This routine checks for a delimiter character pointed to by HL. It returns ; with the character in A and the zero flag set if it is a delimiter. All ; registers are preserved except A. sdelm: ld a,(hl) ; Get the character exx ; Use alternate register set (shorter code) ld hl,deldat ; Point to delimiter list ld bc,delend-deldat; Length of delimiter list cpir ; Scan for match exx ; Restore registers ret ; Returns Z if delimiter deldat: ; List of delimiter characters db ' ' db '=' db '_' db '.' db ':' db ';' db '<' db '>' db ',' db 0 delend: ;----------------------------------------------------------------------------- ; Log into DU contained in FCB pointed to by DE. Registers DE are preserved; ; all others are changed. fcblog: push de ; Save pointer to FCB ex de,hl ld a,(hl) ; Get drive ld bc,13 ; Offset to S1 field add hl,bc ld c,(hl) ; Get user into C ld b,a ; Get drive into B ld (tempusr),bc ; Set up temporary DU values call logtemp ; ..and log into it pop de ; Restore pointer to FCB ret tempusr: ds 2 ;----------------------------------------------------------------------------- ; Log into the temporary directory. Registers B, H, and L are preserved. logtemp: ld de,(tempusr) ; Set D = tempdr, E = tempusr call setuser1 ; Register D is preserved during this call ld a,d ; Log in drive or a ; Is TEMPDR the current drive (0)? jr nz,logtemp1 ; If not, branch ld a,(4) ; Else substitute the current drive and 0fh inc a ; Add 1 for following decrement logtemp1: dec a ; Adjust for drive range 0..15 jp setdrive ; Log in new drive and return defs 40 ; Room for 20-element stack stack: defs 2 ; Place to keep system stack address ; next 3 lines are debug code ; ; db '***********************' ;jnkl1: ; defs 160 end entry