;======================================================================= ; ; LUX - Remote Z80 LBR/ARC Utility - Version 5.3 for TurboDOS ; ;======================================================================= ; this version of LUX is for TurboDOS users only, if you have a CP/M ; or ZCPR2/3 system then use LUX52 - this version is not for you. ;======================================================================= ; ; LUX Version 1.2B was Copyright 1983 by Steven R. Holtzclaw ; and entered into the public domain. ; ;======================================================================= ; ; LUX now supports IBM-DOS archive (.ARC) files as well as CP/M or ; IBM-DOS library (.LBR) type files. ; ; Modifications: ; ; 07-07-86 Modified LUX52 for TurboDOS minus the ARC member file transfer ; LUX53TD capability (we can't use KMD20+ under TurboDOS as we don't use ; BYE5 or any regular BYE programs.) ; ; Cleaned up the rest of the LUX code and removed all non-TurboDOS ; equates and referrences to KMD, ZCPR, and other stuff we don't ; use. Also removed the BYECMD (sub file) routines and added the ; REMFIL conditional equate for users of Remote Access (c) Lodden ; Technology. Remote Access uses only .REM extents for remote ; command files. ; ; Added ^S and ^Q commandline error handling for those times ; when a remote caller's modem program sends a stop/start ; request (it used to be an invisible error which confused ; the user as ^S and ^Q didn't print in the error msg.) ; ; Added a special help menu of LUX commands while attached ; to an ARC file which is dif than display if attached to ; to a LBR file. Changed coding to allow a ^C to be used ; for exiting LUX as well as "X", "x", or "EXIT". ; ; - Steve Sanders ; ;---------------------------------------------------------------------------- ; ; author ; ; 11/26/83 Original release. Adapted from ATTACH program. ; ; - Steven R Holtzclaw, Dallas, Texas ; ;---------------------------------------------------------------------------- ; .Z80 ; if you don't have a Z80, then LUX isn't for you ... ; ; TRUE EQU -1 FALSE EQU NOT TRUE ; ;------------------------------------------------------------------------- ; ; Conditional equates ; REMFIL EQU TRUE ; TRUE, LUX modules have .REM extent ; FALSE, LUX modules have .COM extent ; AUTODR EQU TRUE ; TRUE, show dir when LUX first attaches ; HLPERS EQU 3 ; show help menu after this many errors ; MAXDRV EQU 15 ; max drive (0-15) 0=a: 1=b:, etc... ; MAXUSR EQU 31 ; max user area (0-31) ; ;------------------------------------------------------------------------- ; ; Define the drive and user number for each auxiliary lux module. ; ( drives 0=a, 1=b, etc) ; TYPDRV EQU 0 ; drive number for LUXTYP TYPUSR EQU 2 ; user number for LUXTYP ; DIRDRV EQU 0 ; drive number for LUXDIR/UNARC DIRUSR EQU 2 ; user number for LUXDIR/UNARC ; FILDRV EQU 0 ; drive number for DIR.COM FILUSR EQU 2 ; user number for DIR.COM ; CHKDRV EQU 0 ; drive number for LUXCHEK CHKUSR EQU 2 ; user number for LUXCHEK ; CRCDRV EQU 0 ; drive number for LUXCRC CRCUSR EQU 2 ; user number for LUXCRC ; ; note: the file transfer program is called LUXMODEM.COM/REM ; XMDRV EQU 0 ; drive number for LUXMODEM XMDUSR EQU 2 ; user number for LUXMODEM ; ;------------------------------------------------------------------------- ; VERS EQU 53 ; version number MODLVL EQU ' ' ; modification level BDOS EQU 0005H TPA EQU 0100H ; cpm program area FCB1 EQU 005CH ; first file control block FCB2 EQU 006CH ; second file control block REBOOT EQU 0000H ; CBUFF EQU 080H ; default command buffer ; ; Macros used ; DRVUSR MACRO DRIVNO,USERNO,FNCNAM,RTN1,RTN2,RTN3,RTN4 CALL FILTYP DEFB DRIVNO+'A' USERN1 DEFL USERNO IF USERN1 GT 9 DEFB (USERN1 /10)+'0' USERN1 DEFL USERN1-10 ENDIF DEFB USERN1+'0' DEFB ':' DEFB FNCNAM DEFB 0 IF NOT NUL RTN1 CALL RTN1 ENDIF IF NOT NUL RTN2 CALL RTN2 ENDIF IF NOT NUL RTN3 CALL RTN3 ENDIF IF NOT NUL RTN4 CALL RTN4 ENDIF JP PROCES ENDM ; DVUS MACRO DRIVNO,USERNO,FNCNAM,RTN1,RTN2,RTN3,RTN4 CALL FILTYP DEFB DRIVNO+'A' USERN2 DEFL USERNO IF USERN2 GT 9 DEFB (USERN2 /10)+'0' USERN2 DEFL USERN2-10 ENDIF DEFB USERN2+'0' DEFB ':' DEFB FNCNAM DEFB 0 ENDM ; CMDJMP MACRO VERB,VECTOR CALL ILCMP DEFB VERB DEFB 0 JP NC,VECTOR ENDM ; START EQU $ LD SP,SSTACK ; starting stack CALL ILPRT DEFB 13,10,'LUX v' DEFB (VERS/10)+'0' DEFB '.' DEFB (VERS MOD 10)+'0',MODLVL DEFB ' for TurboDOS [sls]',13,10,0 ; ; set up max drive/user ; LD A,MAXDRV ; max drive LD (DRVMAX),A LD A,MAXUSR ; max user LD (USRMAX),A ; ; check for a blank or null command line ; LD A,(CBUFF+1) ; get byte from default command buffer OR A ; if non-zero then there is a possible JP NZ,GTDVUS ; file specified ; SPCERR: CALL ILPRT ; print short help message DEFB 13,10 DEFB 'LUX is an online utility for manipulating library (.LBR)' DEFB 13,10 DEFB 'and archive (.ARC) type files. These two types of files' DEFB 13,10 DEFB 'contain two or more internal "member" files.' DEFB 13,10,10 DEFB ' Usage:',13,10,10 DEFB ' LUX filename <-Attach to specified LBR or ARC file.' DEFB 13,10,10,0 JP REBOOT ; reboot since we have destroyed the ccp ; BDDRUS: CALL ILPRT DEFB 13,10,10 DEFB '++ Invalid drive/user requested ++',7,13,10,0 JP SPCERR ; ; GTDVUS: LD HL,CBUFF+2 ; index default key buffer CALL DRUSR ; get requested drive/user JP C,SPCERR ; ; test for drive/user within range ; PUSH BC ; save drive/user spec PUSH HL ; save command line pointer LD A,(DRVMAX) ; get max drive CP C JP C,BDDRUS LD A,(USRMAX) CP B JP C,BDDRUS POP HL POP BC LD (RQDDRV),BC ; set the requested drive/user EX DE,HL ; de is source address to create new fcb LD HL,FCB1 ; index fcb CALL SCANR1 ; create the new fcb ; ; force the default file type ; LD HL,'BL' ; set 'LB' into first two bytes of file type LD (FCB1+9),HL LD A,'R' ; set 'R' into last byte of file type LD (FCB1+11),A ; ; get the library name from the fcb and store it ; LD HL,FCB1 ; source for move LD DE,LBRNAM-1 ; destination for move LD BC,9 ; max 8 character filename LDIR ; move to local name ; AMBTST: LD HL,FCB1+1 ; first byte of filename LD A,'?' ; character to look for LD BC,11 ; search thru 11 bytes CPIR ; do search JR NZ,LOOKUP ; no ? found - continue ; NOAMBG: CALL ILPRT ; print the error message DEFB 13,10,10 DEFB '++ Ambiguous filenames are not allowed ++',7,13,10,0 JP SPCERR ; ; look for the filename on directory ; LOOKUP: CALL GETOLD ; get the current drive/user CALL SETNEW ; set requested drive/user LD DE,080H ; default dma address LD C,26 CALL BDOS ; set the dma address LD DE,FCB1 ; index filename specified LD C,17 CALL BDOS ; search for first INC A ; does file exist? JP NZ,PGMSTR ; jump to start of lux LD HL,'RA' ; try ARC file LD (FCB1+9),HL LD A,'C' ;set ARC LD (FCB1+11),A LD DE,FCB1 LD C,17 ; see if it exists CALL BDOS INC A JP Z,NOFILE ; nope, LD A,0FFH ; yes, set arcflg true LD (ARCFLG),A LD HL,FCB1 LD DE,LBRNAM-1 ; update library name (arc) LD BC,12 LDIR JP PGMSTR ; go do it ; NOFILE: CALL SETOLD CALL ILPRT ; print the error message DEFB 13,10,10,'Can''t find ',0 CALL DVUPRT CALL NAMPRT ; print the filename CALL ILPRT DEFB ' - check the DIR',7,13,10,0 JP 0 ; reboot since we have destroyed the ccp ; DEFS 64 ; 32 level stack for here SSTACK EQU $ FINIS EQU $ ; finish of program loader ; LODLEN EQU FINIS-START ; length of loader ; keep the program in line DEFS 300H-LODLEN ; add extra bytes here to make ; 'PGMSTR' start on a 100h byte boundary ; ;----------------------------------------------------------------------- ; this is the start of the relocated program - all of the code ; from 'START' to here is thrown away once lux begins execution ;----------------------------------------------------------------------- ; ; set up the bdos and bios patches ; PGMSTR: JP INIT ; jump to start of this module DEFB 'LUX V5.3' ; the name 'LUX' is a clue to other ; programs that enables them to determine ; if lux is resident. 'L' is at bdos+3 when ; lux is resident ; ; ; this is the LUX intialization ; INIT: LD HL,(1) ; get warm boot vector LD (BIOS3),HL ; save old warm boot vector LD HL,(6) ; get bdos start LD (PGMSTR+1),HL ; set new jump to bdos LD HL,PGMSTR ; get local bdos vector LD (6),HL ; set it in low memory LD SP,STACK ; reset stack ; ; save the old bios vectors ; LD HL,(BIOS3) ; bios wboot address LD DE,OWBOOT ; local wboot address LD BC,12 ; 12 bytes to move LDIR ; move the block ; ; set up the new bios vectors ; LD HL,WBOOT ; source is local table LD DE,(BIOS3) ; destination is old bios LD BC,12 ; 12 bytes to move LDIR ; move the block ; LD A,0FFH ; set the auto-dir byte LD (DOADIR),A ; XOR A ; reset the error count LD (HLPCNT),A ; ; JP ENTRY ; initialize ; OWBOOT: DEFB 0,0,0 ; old wboot vector is moved to here OCONST: DEFB 0,0,0 ; old constat vector is moved to here OCONIN: DEFB 0,0,0 ; old conin vector is moved to here OCONOU: DEFB 0,0,0 ; old conout vector is moved to here ; WBOOT: JP ENTRY ; vector warm boot to entry CONST: JP VCONST ; check for carrier CONIN: JP VCONIN ; vector conin to vconin CONOU: JP VCONOU ; vector to conout ; VCONST: JP OCONST ; jump to old constat routine ; VCONOU: JP OCONOU ; jump to old conout routine ; VCONIN: CALL OCONIN ; get a byte CP 3 ; control c ? RET NZ ; nope - let bios have it ; ; exit routine, return to TurboDOS ; EXIT: LD A,(ACTIVE) ; is lux segment active? OR A LD A,3 RET Z ; not active - let bios have it LD SP,TPA ; re initialize the stack CALL ILPRT ; turn up a couple of lines DEFB 13,10,10,0 ; UNPATH: LD HL,OWBOOT ; index old warm boot vector LD DE,(BIOS3) ; bios jump table LD BC,12 ; 12 bytes to move LDIR ; move the old table back CALL SETOLD ; set old drive/user JP 0 ; warm boot - end of program ; ; This is the LUX entry point ; ENTRY: LD SP,STACK ; set up local stack LD HL,PGMSTR ; dummy bdos vector LD (6),HL ; set it LD HL,(BIOS3) ; bios warm boot vector LD (1),HL ; set it LD A,0C3H ; (jmp) LD (0),A ; reset warm boot jump LD (5),A ; ...and bdos jump ; CALL OCONST ; see if character waiting OR A ; test result JR Z,ENTR1 ; ...if no character is waiting CALL OCONIN ; get the console character ; this is done to gobble any ; possible garbage character ; ENTR1: LD A,0FFH LD (ACTIVE),A ; set LUX active ; GETCMD: CALL SETNEW ; reset drive/user LD IX,CBUFF+1 ; place to put command string LD IY,CBUFF+0 ; length of command XOR A LD (IY+0),A ; IF AUTODR ; LD A,(DOADIR) ; shall we do a directory? OR A ; JR Z,PROMPT ; guess not XOR A ; else zap the byte LD (DOADIR),A ; LD A,3 ; fake a DIR command LD (CMDLEN),A ; LD HL,'ID' ; LD (CMDLIN+2),HL ; LD L,'R' ; LD H,0 ; LD (CMDLIN+4),HL ; JP GOCNV ; and do it ; ENDIF ;AUTODR ; PROMPT: CALL ILPRT ; print the entry message DEFB 13,10,'[ in LUX X exits, ? for Menu ]',13,10,0 PRMPT2: CALL CRLF call ilprt defb '{lux} ',0 call dvuprt ;print the du: CALL NAMPRT ;and library/archive name CALL ILPRT DEFB ' >>',0 LD DE,CMDLIN ; index command line LD C,10 CALL BDOS ; read console buffer LD A,(CMDLEN) ; get command length OR A ; test it JR Z,GETCMD ; if null command LD A,(CMDLIN+2) ; get first character CP ';' ; semicolon ok JP Z,PRMPT2 ; CP 3 ; control C ? JP Z,EXIT ; then exit lux GOCNV: CALL CNVBUF ; convert the command line to upper case ; GETCM4: LD DE,CMDLIN+2 ; index data from the command line ; ; this section for the ARC commands ; LD A,(ARCFLG) ; are we looking at arc files? OR A JR Z,LBRCMD ; nope, do .lbr cmds ; CMDJMP 'TYPE',ATYPE CMDJMP 'DIR',UNARC CMDJMP 'D',UNARC CMDJMP 'SD',UNARC CMDJMP 'FILES',AFILES CMDJMP 'CHEK',NOARC1 CMDJMP 'CRCK',NOARC1 CMDJMP 'XMODEM',NOTYET ; not supported yet by LUXMODEM CMDJMP 'SEND',NOTYET CMDJMP 'SENDK',NOTYET CMDJMP 'X',EXIT ; JR HLP1 ; ; this section for the LBR commands ; LBRCMD: CMDJMP 'TYPE',TIPE ; file type command process CMDJMP 'DIR',DIR ; dir command process CMDJMP 'D',DIR ; alternate for DIR CMDJMP 'SD',DIR ; alternate for DIR CMDJMP 'FILES',FILES ; run dir.com in "*.lbr" mode CMDJMP 'CHEK',CHEK ; run luxchk CMDJMP 'CRCK',CRCK ; run luxcrc ; ; this section for common commands ; HLP1: CMDJMP 'X',EXIT ; exit LUX CMDJMP 'EXIT',EXIT ; exit CMDJMP '?',QKHELP ; HELP - display menu CMDJMP 'HELP',QKHELP ; HELP CMDJMP 'MENU',QKHELP ; HELP CMDJMP 'LUX',LUX ; lux command process CMDJMP 'XMODEM',XMD ; XMODEM command process CMDJMP 'SEND',SEND ; synonym for XMODEM S CMDJMP 'SENDK',SENDK ; synonym for XMODEM SK ; ; place any commands here normally used in your operating system ; but not available while in LUX - tell user to exit LUX first. ; NOGOTC: CMDJMP 'CHAT',NOGOT ; tell user NOGOT CHAT here CMDJMP 'BYE',NOGOT ; tell user NOGOT BYE here CMDJMP 'TOS',NOGOT CMDJMP 'RBBS',NOGOT CMDJMP 'CD',NOGOT ; ; CMDERR: CALL ILPRT ; ; this will actually print the command in error like this ; ; ERROR --> dur <-- is not a valid LUX command. ; DEFB 13,10,13,10,'ERROR --> ',0 ; point at command error CALL PRTERR ; print the command just entered LD A,' ' ; and a space CALL CTYPE LD HL,HLPCNT ; address the error count INC (HL) ; bump it LD A,HLPERS ; have we reached the limit? CP (HL) ; JR NZ,KPTRYN ; no, jump around the rest LD (HL),0 ; else reset the count JP QKHELP ; and give him help anyway KPTRYN: CALL ILPRT ; tell them it's no good DEFB '<-- Is not a valid LUX command.',7,13,10,0 JP GETCMD ; PRTERR: LD HL,CMDLIN+2 ; index command just entered LD A,(CMDLEN) ; get the length LD B,A ; into 'B' GETCM5: LD A,(HL) ; get a byte CP 011H ; ctrl-q ? JR Z,QERR ; complain CP 013H ; ctrl-s ? JR Z,SERR ; complain CP 020H ; space ? JR Z,GETCM6 ; yes - dont print it CP 000H ; null JR Z,GETCM6 ; yes - all done CALL CTYPE ; print the character INC HL ; next character DJNZ GETCM5 ; loop for the rest GETCM6: RET ; ; echo back ^S or ^Q as an error if the remote user's modem ; program sent either of these due to an open capture buffer ; requesting start/stop of host output (a very common occurrence). ; SERR: LD A,'^' ; print ^S CALL CTYPE LD A,'S' CALL CTYPE RET ; QERR: LD A,'^' ; print ^Q CALL CTYPE LD A,'Q' CALL CTYPE RET ; ;------------------------------------------------------------------------- ; ; COMMAND TRANSLATION VECTORS ; ; 'FILTYP' installs the following 'DEFB' into new command line ; specify the drive and user area for each command as in ; the vectors below. remember each 'DEFB' must end with a ; zero. ; ; 'FILNAM' installs the current .lbr/.arc name into the new command line ; ; 'FILSPC' installs a space character into the new command line ; ; 'FILMEM' installs the requested member name into the new command line ; ; TIPE: DRVUSR TYPDRV,TYPUSR,'LUXTYP ',FILNAM,FILSPC,FILMEM ATYPE: DRVUSR TYPDRV,TYPUSR,'UNARC ',FILNAM,FILSPC,FILMEM ; DIR: DRVUSR DIRDRV,DIRUSR,'LUXDIR ',FILNAM,FILSPC,FILMEM UNARC: DRVUSR DIRDRV,DIRUSR,'UNARC ',FILNAM ; FILES: DRVUSR FILDRV,FILUSR,'DIR *.LBR' AFILES: DRVUSR FILDRV,FILUSR,'DIR *.ARC' ; CHEK: DRVUSR CHKDRV,CHKUSR,'LUXCHK ',FILNAM,FILSPC,FILMEM CRCK: DRVUSR CRCDRV,CRCUSR,'LUXCRC ',FILNAM,FILSPC,FILMEM ; ; ; Note: the SENDA and SENDAK options are not yet implimented ; SEND: DRVUSR XMDRV,XMDUSR,'LUXMODEM L ',FILNAM,FILSPC,FILMEM ; SENDA: DRVUSR XMDRV,XMDUSR,'LUXMODEM L ',FILNAM,FILSPC,FILMEM ; SENDK: DRVUSR XMDRV,XMDUSR,'LUXMODEM LK ',FILNAM,FILSPC,FILMEM ; SENDAK: DRVUSR XMDRV,XMDUSR,'LUXMODEM LK ',FILNAM,FILSPC,FILMEM ; ;------------------------------------------------------------------------- ; ; LUX Help Menus ; QKHELP: LD A,(ARCFLG) ; are we looking at arc files? OR A JP Z,LBRHLP ; nope, do .lbr help ; ARCHLP: ; ARChive command help CALL ILPRT DEFB 13,10,13,10 DEFB 'You are presently attached to an ARChive file with the',13,10 DEFB 'LUX utility. These are the available commands:',13,10,10 DEFB 'LUX filename - Attach to another ARC file.',13,10 DEFB 'DIR, D, or SD - Re-Display ARC directory.',13,10 DEFB 'FILES - Display ARC files in current area.',13,10 DEFB 'TYPE filename.ext - Display ASCII file contents.',13,10 DEFB 'SEND filename.ext - (not implimented yet)',13,10 DEFB 'SENDK filename.ext - (not implimented yet)',13,10 DEFB 'XMODEM S filename.ext - (not implimented yet)',13,10 DEFB 'XMODEM SK filename.ext - (not implimented yet)',13,10 DEFB 'X or x - Exit LUX, return to TurboDOS.',13,10 DEFB '? or HELP - Displays this menu.',13,10,0 JP GETCMD ; LBRHLP: ; LBR command help CALL ILPRT DEFB 13,10,13,10 DEFB 'You are presently attached to a LiBRary file with the',13,10 DEFB 'LUX utility. These are the available commands:',13,10,10 DEFB 'LUX filename - Attach to another LBR file.',13,10 DEFB 'DIR, D, or SD - Re-Display LBR directory.',13,10 DEFB 'FILES - Display LBR files in current area.',13,10 DEFB 'TYPE filename.ext - Display ASCII file contents.',13,10 DEFB 'CHEK filename.ext - Run CHEK on requested LBR member.',13,10 DEFB 'CRCK filename.ext - Run CRCK on requested LBR member.',13,10 DEFB 'SEND filename.ext - Send file with CRC protocol.',13,10 DEFB 'SENDK filename.ext - Send using 1k (YMODEM) protocol.',13,10 DEFB 'XMODEM S filename.ext - Same as SEND command.',13,10 DEFB 'XMODEM SK filename.ext - Same as SENDK command.',13,10 DEFB 'X or x - Exit LUX, return to TurboDOS.',13,10 DEFB '? or HELP - Displays this menu.',13,10,0 JP GETCMD ; ;------------------------------------------------------------------------- ; ; Special Error Handlers ; ; Tried entering CHAT or BYE - tell user to exit LUX first. ; Add other commands as you wish. ; NOGOT: CALL CRLF CALL CRLF CALL PRTERR ;print the command CALL ILPRT ;and then this DEFB ' <-- command used in TurboDOS, must exit LUX first.' DEFB 7,13,10,10 ;ring bell and point at the error DEFB 0 JP GETCMD ;go back for another command ; ; LUXCHK and LUXCRC can not be used on ARC files ; NOARC1: CALL CRLF CALL CRLF CALL ILPRT DEFB 'Use DIR command for CRC values' DEFB 7,13,10 DEFB 0 JP GETCMD ; ; Individual .ARC member file transfer not yet implimented ; NOTYET: CALL CRLF CALL CRLF CALL ILPRT DEFB 13,10 DEFB ' ARC member file transfer not yet enabled - you must send' DEFB 13,10 DEFB ' the entire .ARC file and use ARC.EXE on your end.' DEFB 13,10,13,10 DEFB ' This feature will be implimented soon...' DEFB 13,10,0 JP GETCMD ; ; XMODEM is a special case since the 'R' and 'L' options ; are invalid here ; XMD: CALL ADVANC ; go to next character LD A,(HL) ; get the character CP 'S' ; if 's' check for JR Z,XMD1 ; following 'k' CP 'R' ; not legal here JR Z,XMD2 ; execute error routine CP 'L' ; not legal here JR Z,XMD3 ; execute error routine DRVUSR XMDRV,XMDUSR,'LUXMODEM' ; XMD1: INC HL ; get next chr LD A,(HL) CP 020H ; is it a space? JR Z,XMD1A CP 'K' ; or packet request? JR Z,XMDK ; XMD1A: CALL NXTSPC LD A,(ARCFLG) ; Are we in an arc file? OR A JP Z,SEND ; Nope, send regular JP SENDA ; Yes, send arc ; XMDK: CALL NXTSPC LD A,(ARCFLG) ; Are we in an arc file? OR A JP Z,SENDK ; Nope, send regular JP SENDAK ; Yes, send arc XMD2: CALL CRLF CALL ILPRT ; print the following DEFB 13,10,'Error - XMODEM can''t (R)eceive while in LUX' DEFB 7,13,10,0 JP GETCMD ; return to command XMD3: CALL CRLF CALL ILPRT ; print the following DEFB 13,10,'Error - XMODEM uses only S or SK options in LUX' DEFB 7,13,10,0 JP GETCMD ; ;------------------------------------------------------------------------- ; ; 'LUX' command process ; LUX: LD A,(CMDLEN) ; get the length of the command line CP 3 ; was input only 'LUX' JP Z,LUX04 ; error... CALL FNDSPC ; find a space in command line JP C,LUX05 ; error if no space found CALL ADVANC ; search for the next non-blank character JP C,LUX05 ; error if no more characters left CALL DRUSR ; get drive/user JP C,LUX05 ; if drive/user specification error LD (TMPDRV),BC ; save the temporary drive/user PUSH BC ; save drive/user spec PUSH HL ; save command line pointer LD A,(DRVMAX) ; get max drive CP C JP C,LUX03 ; if out of range LD A,(USRMAX) CP B JP C,LUX03 ; if out of range POP HL POP BC EX DE,HL ; de is source address to create new fcb LD HL,TMPFCB ; index temporary fcb CALL SCANR1 ; create the new fcb LD HL,'BL' ; set 'LB' into first two bytes of file type LD (TMPFCB+9),HL LD A,'R' ; set 'R' into last byte of file type LD (TMPFCB+11),A CALL SETTMP ; log into the requested drive/user LD DE,080H LD C,26 ; bdos set dma function CALL 5 ; set dma address to 80h LD DE,TMPFCB ; index temporary fcb LD C,17 ; bdos search first function CALL 5 INC A ; test for existence JR NZ,LUX01 ; OK, go LD HL,'RA' ; check for arc file LD (TMPFCB+9),HL LD A,'C' LD (TMPFCB+11),A LD DE,080H LD C,26 CALL 5 LD DE,TMPFCB LD C,17 CALL 5 INC A JR Z,LUX05 ; cant find file LD A,0FFH ; set arc flag true LD (ARCFLG),A JP LUX02 LUX01: LD A,0 ; set arc flag false LD (ARCFLG),A LUX02: LD HL,(TMPDRV) ; get temporary drive/user LD (RQDDRV),HL ; set new drive/user LD HL,TMPFCB+1 ; source address of new name LD DE,LBRNAM ; current .lbr name LD BC,12 ; 8 character file name LDIR ; move it CALL ILPRT ; for display neatness DEFB 13,10,0 ; LD A,0FFH ; set the auto-directory flag LD (DOADIR),A ; JP GETCMD ; ; LUX03: POP HL POP BC LUX04: CALL ILPRT DEFB 13,10,10,'++ Invalid LUX request ++',7,13,10,0 JP GETCMD ; LUX05: CALL ILPRT DEFB 13,10,10,'Can''t find ',0 CALL DVUPR1 LD B,8 LD HL,TMPFCB+1 CALL NAMPR1 ; print the file name CALL ILPRT DEFB ' - check your spelling',7,13,10,0 JP GETCMD ; PROCES: XOR A ; zero last byte of new command line LD (IX+0),A LD HL,CBUFF+1 LD (HLPCNT),A ; reset the error count CALL DRUSR ; get drive/user LD (COMDRV),BC ; set the com drive/user EX DE,HL ; de is source address to create new fcb CALL SCANER ; create the new fcb EX DE,HL ; into 'HL' LD DE,CBUFF+1 ; start of command buffer PUSH HL PUSH DE OR A ; clear any cy SBC HL,DE ; calculate length of move LD A,(CBUFF) ; get command line length SUB L ; calculate new length LD (CBUFF),A ; put new length LD A,07EH ; calculate length of block move SUB L LD C,A ; set into c LD B,0 ; 'b' gets zero POP DE ; restore destination POP HL ; and source LDIR ; move the block down LD HL,FCB1 ; set up first fcb LD DE,CBUFF+1 CALL SCANR1 LD HL,FCB2 ; set up second fcb CALL SCANR1 ; ; force the default file type (.rem or .com) ; IF REMFIL LD HL,'ER' ; 'REM' extents ELSE LD HL,'OC' ; 'COM' extents ENDIF ; REMFIL ; LD (DEFFCB+9),HL LD A,'M' ; 'M' LD (DEFFCB+11),A XOR A ; zero the record count and ; the extent number LD (DEFFCB+15),A LD (DEFFCB+32),A CALL SETCOM ; set module drive/user ; LD DE,TPA LD C,01AH CALL BDOS ; set dma to tpa ; LD DE,DEFFCB LD C,011H CALL BDOS ; search for first INC A JR NZ,PROCE1 ; file found ; CALL ILPRT DEFB 13,10,'Can''t find ',0 LD B,8 LD HL,DEFFCB+1 CALL NAMPR1 ; print the file name CALL ILPRT ; cr/lf DEFB 7,13,10,0 JP ENTRY ; go for more commands ; PROCE1: LD DE,TPA LD C,01AH CALL BDOS ; set dma to tpa ; LD DE,DEFFCB LD C,00FH CALL BDOS ; open file INC A JR NZ,PROCE2 ; CALL ILPRT DEFB 13,10,'Internal LUX module error - please notify SYSOP' DEFB 7,7,13,10,0 JP ENTRY ; ; load the module into memory @100h and call it ; PROCE2: LD HL,080H LD DE,080H LODCOM: ADD HL,DE ; add record size offset EX DE,HL ; get dma address into 'DE' PUSH DE ; save 'DE' and 'HL' PUSH HL LD C,01AH CALL BDOS ; set dma LD DE,DEFFCB ; index .com file name LD C,014H CALL BDOS ; read a record POP HL ; restore 'DE' and 'HL' POP DE EX DE,HL ; 'HL' is dma address again OR A ; end of file ? JR Z,LODCOM ; no - read another record ; LD C,13 CALL BDOS ; reset drive system CALL SETNEW ; set new drive/user XOR A LD (ACTIVE),A ; clear command mode active CALL CRLF CALL TPA ; call the loaded file @100h LD DE,35 ; zero out fcb1 LD HL,FCB1 ZEROFCB: LD (HL),0 INC HL DJNZ ZEROFCB JP ENTRY ; go for more commands ; NAMPRT: LD B,8 ; 8 character file name LD HL,LBRNAM ; index .lbr name NAMPR1: LD A,(HL) ; get a byte CP 020H ; space? JR Z,NAMPR2 ; yes - dont print CALL CTYPE ; else print the character NAMPR2: INC HL ; next character DJNZ NAMPR1 ; process 8 characters LD A,'.' ; print a seperator CALL CTYPE LD B,3 ; 3 character file type NAMPR3: LD A,(HL) ; get a character CALL CTYPE ; print it INC HL ; next character DJNZ NAMPR3 ; process 3 characters RET ; ; write a string of characters to the crt ; ILPRT: EX (SP),HL ; save return address/get character pointer ILPRT1: LD A,(HL) ; get a byte OR A ; test it JR Z,ILPRT2 ; null - end of string CALL CTYPE ; else type the character INC HL ; next character JR ILPRT1 ; loop for more ILPRT2: INC HL EX (SP),HL ; restore return address RET ; return to caller ; ; write a string of characters to the command line ; ; works like ilprt above ; FILTYP: EX (SP),HL FILTY1: LD A,(HL) OR A JR Z,FILTY2 CALL PUTIN INC HL JR FILTY1 FILTY2: EX (SP),HL RET ; ; fill command line with a space ; FILSPC: LD A,020H ; space character CALL PUTIN ; fill in RET ; ; fill command line with .lbr name ; FILNAM: LD B,8 ; 8 character file name LD HL,LBRNAM ; index .lbr name FILNA1: LD A,(HL) ; get a character CP 020H ; space ? JR Z,FILNA2 ; yes - dont add to command line CALL PUTIN ; put character into command line FILNA2: INC HL ; next character DJNZ FILNA1 ; process 8 characters LD A,'.' ; put in a seperator character CALL PUTIN LD B,3 ; 3 character file type FILNA3: LD A,(HL) ; get a character CALL PUTIN ; put in command line INC HL ; next character DJNZ FILNA3 ; process 3 characters RET ; return to caller ; ; fill command line with member name ; FILMEM: CALL PARSER ; parse member name LD HL,MEMBER ; index member name LD B,12 ; 12 character max FILME1: LD A,(HL) ; get a byte OR A ; end of input RET Z ; yes - return CALL PUTIN ; fill in one character INC HL ; next character DJNZ FILME1 ; continue looping RET ; done ; PUTIN: LD (IX+0),A ; stuff the character into command line INC IX ; get ready for next character INC (IY+0) ; bump command line length RET ; return to caller ; ; parse out a member name ; PARSER: LD HL,MEMBER ; index member name LD B,12 ; max 12 character filename PARSE1: LD (HL),0 ; zero character INC HL ; next character DJNZ PARSE1 ; clear the entire member name CALL ADVANC ; advance to the next non blank character RET C ; if at the end of the line LD DE,MEMBER ; de is index to member (hl set by advanc) LD HL,(NXTWRD) PARSE2: LD A,(HL) ; get source byte OR A ; end of input line ? RET Z ; yes - return LD (DE),A ; put byte INC HL ; next source INC DE ; next destination JR PARSE2 ; continue looping ; ; advanc - advance the word at nxtwrd to the next non blank address ; of the command line. set carry if no more characters available ; ADVANC: LD HL,(NXTWRD) ; get pointer to next word ADVAN1: LD A,(HL) ; get a byte OR A ; test flags JR Z,ADVAN3 ; error - null character CP 020H ; space ? JR NZ,ADVAN2 ; yes - done INC HL LD (NXTWRD),HL ; put pointer back JR ADVAN1 ; loop for more ADVAN2: OR A ; clear any carry RET ADVAN3: SCF ; set error condition RET ; FNDSPC: LD HL,CMDLIN+2 ; index command line FND01: LD A,(HL) ; get a byte from command line OR A ; eol ? JP Z,FNDER ; error... CP 020H ; space? JP Z,FNDEX ; ...yes - go find requested file name INC HL ; next character JR FND01 ; else continue the search FNDER: SCF ; all characters scanned and no space found RET ; FNDEX: LD (NXTWRD),HL ; set character location OR A ; assure carry reset RET ; ; NXTSPC: LD HL,(NXTWRD) ; get pointer to next word NXTSP1: LD A,(HL) ; get a byte OR A ; is it a null? JR Z,NXTSP2 ; ...yes - return CP 020H ; if at a space? JR Z,NXTSP2 ; ...yes - return INC HL ; next character JR NXTSP1 ; and continue looking NXTSP2: LD (NXTWRD),HL RET ; ; in-line compare. compares string addressed by 'de' to string after ; call (ends with zero). return with carry set means strings not the ; same. all registers except 'a'-reg are unaffected. ; ILCMP: EX (SP),HL PUSH DE ILCMP1: LD A,(HL) ; get a byte from source OR A ; null JR Z,SAME1 ; yes - same so far - test next char LD A,(DE) ; get a byte from command string CP (HL) ; same as source JR NZ,NOTSAM ; no - not the same INC HL ; next source INC DE ; next compare JR ILCMP1 ; loop again NOTSAM: XOR A ; zero for the test NSLP: INC HL ; next immediate byte CP (HL) ; null yet ? JR NZ,NSLP ; no - continue SAME2: SCF ; set error condition SAME: EX DE,HL ; get command string pointer LD (NXTWRD),HL ; store it EX DE,HL ; restore return address POP DE ; restore source address INC HL ; adjust to stack EX (SP),HL ; replace return address/ RET ; return SAME1: LD A,(DE) ; get the next byte from command line OR A ; null ? JR Z,SAME ; yes - its ok CP 020H ; space ? JR Z,SAME ; yes - thats ok too... JR SAME2 ; not ok- must be another character ; CTYPE: PUSH AF ; save all registers PUSH BC PUSH DE PUSH HL AND 07FH ; be sure its ascii LD E,A ; into 'E' LD C,2 ; cpm console function CALL BDOS POP HL ; restore all registers POP DE POP BC POP AF RET ; return to caller ; CRLF: LD A,13 CALL CTYPE LD A,10 JR CTYPE ; ; ; get the drive and user number for a file from command string ; index by 'HL' ; ; on entry: ; ; 'HL' points to first byte of the command string ; ; on exit: ; ; 'HL' points to the byte following ':' in the command string if ; the ':' was found in the first 4 character positions. ; -or- ; 'HL' points to the first byte of the command string if no ':' ; was found. ; ; 'C' contains the requested drive number (0-15) ; ; 'B' contains the requested user number (0-15) ; ; 'AF' the number of characters thru the ':' in the command string. ; ; 'CY' is set if drive or user number is out of range (0-15) ; ;------------------------------------------------------------------------------ ; DRUSR: LD (TEMPHL),HL ; save the pointer address LD IX,(TEMPHL) ; 'IX' get the pointer address LD BC,5 ; LD A,':' CPIR ; search for the ':' LD A,C ; get 'B' result from 'CPIR' instruction LD (LENGTH),A ; keep for possible adjust EX DE,HL ; de points to the byte following ':' LD HL,VTABLE ; index address table ADD HL,BC ; add word offset ADD HL,BC LD A,(HL) ; get routine lsb INC HL LD H,(HL) ; get routine msb LD L,A LD BC,0 ; set up drive/user storage JP (HL) ; execute ; VTABLE: DEFW DRUS0 ; b=0 - filename.ext DEFW DRUS1 ; b=1 - a15:filename.ext DEFW DRUS2 ; b=2 - a1:filename.ext DEFW DRUS3 ; b=3 - a:filename.ext DEFW DRUS4 ; b=4 - :filename.ext ; ; format was - filename.ext ; DRUS0: CALL GETDFU ; get the default user CALL GETDFD ; get the default drive LD HL,(TEMPHL) ; get old buffer pointer back XOR A ; zero move length RET ; all done ; ; format was - duu:filename.ext ; DRUS1: CALL GETDRV ; get the drive parameter LD A,(IX+0) CP '0' JR C,ERROR CP '9'+1 JR NC,ERROR SUB '0' LD B,A ; put in drive number SLA B ; * 2 SLA B ; * 4 SLA B ; * 8 ADD A,A ; a * 2 ADD A,B ; + c LD B,A INC IX ; skip the tens digit JR GETUSR ; get the user number ; ; format was - du:filename.ext ; DRUS2: CALL GETDRV ; get the drive parameter JR GETUSR ; get the user number ; ; format was - d:filename.ext ; DRUS3: CALL GETDRV ; get the drive parameter CALL GETDFU ; get the default user ; ; format was - :filename.ext ; DRUS4: JR DRUS5 ; GETDRV: LD A,(IX+0) CP 'A' JR C,ERROR1 CP 'Q' JR NC,ERROR1 SUB 'A' LD C,A ; put in drive number INC IX RET ; GETUSR: LD A,(IX+0) CP '0' JR C,ERROR CP '9'+1 JR NC,ERROR SUB '0' ADD A,B LD B,A ; ; adjust the byte in 'LENGTH' ; DRUS5: EX DE,HL ; hl points to byte following ':' if any LD A,(LENGTH) ; get length of move OR A ; test it RET Z ; return if null/ clear carry LD E,A LD A,5 SUB E LD (LENGTH),A OR A ; clear any error RET ; ERROR1: POP DE ; kill return address from subroutine ERROR: SCF ; set error condition RET ; ; get default user ; GETDFU: PUSH BC PUSH DE PUSH HL LD C,020H LD E,0FFH CALL BDOS POP HL POP DE POP BC LD B,A ; set 'B' register to current user RET ; ; get default drive ; GETDFD: PUSH BC PUSH DE PUSH HL LD C,019H CALL BDOS POP HL POP DE POP BC LD C,A ; set 'C' register to current drive RET ; ; convert the drive/user bytes in BC to a BDS-C user/drive specification ; and place in command line ; BDSDVU: PUSH BC ; save drive/user spec PUSH HL ; save command line pointer LD A,(DRVMAX) ; check for max's exceeded CP C ; JP C,DUERR ; LD A,(USRMAX) ; CP B ; JP C,DUERR ; POP HL ; POP BC ; LD A,B ; get the user number CP 10 ; is it less than 10? JR C,BDS2 ; yes, don't insert the '1' LD A,'1' ; CALL PUTIN ; LD A,B ; reget the user number SUB 10 ; subtract 10 BDS2: ADD A,'0' ; add in ascii bias CALL PUTIN ; LD A,'/' ; now the stupid bds-c slash CALL PUTIN ; LD A,C ; get the drive number ADD A,'A' ; add ascii bias CALL PUTIN ; LD A,':' ; CALL PUTIN ; XOR A ; to indicate successfullness RET ; ; DUERR: POP HL ; justify the stack POP BC SCF ; error flag RET ; ; ; extract token from command line and place it into deffcb; ; format deffcb fcb if token resembles file name and type (filename.typ); ; on input, cibptr pts to char at which to start scan; ; on output, cibptr pts to char at which to continue and zero flag is reset ; if '?' is in token ; ; entry points: ; scaner - load token into first fcb ; scanr1 - load token into fcb pted to by hl ; ; SCANER: LD HL,DEFFCB ; point to deffcb SCANR1: XOR A ; set temporary drive number to default LD (TEMPDR),A CALL ADVNCE ; skip to non-blank or end of line LD (CIPTR),DE ; set ptr to non-blank or end of line LD A,(DE) OR A JR Z,SCANR2 SBC A,'A'-1 LD B,A INC DE LD A,(DE) CP ':' JR Z,SCANR3 DEC DE SCANR2: LD A,(TDRIVE) ; set 1st byte of deffcb as default drive LD (HL),A JR SCANR4 SCANR3: LD A,B LD (TEMPDR),A LD (HL),B INC DE SCANR4: XOR A ; a=0 LD (QMCNT),A ; init count of number of question marks in fcb LD B,8 ; max of 8 chars in file name CALL SCANF ; fill fcb file name ; ; extract file type from possible filename.typ ; LD B,3 ; prepare to extract type CP '.' ; if (de) delimiter is a '.', we have a type JR NZ,SCANR5 ; fill file type bytes with INC DE ; pt to char in command line after '.' CALL SCANF ; fill fcb file type JR SCANR6 ; skip to next processing SCANR5: CALL SCANF4 ; space fill ; ; fill in ex, s1, s2, and rc with zeroes ; SCANR6: LD B,4 ; 4 bytes SCANR7: INC HL ; pt to next byte in deffcb LD (HL),0 DJNZ SCANR7 ; ; scan complete -- de pts to delimiter byte after token ; LD (CIBPTR),DE ; ; set zero flag to indicate presence of '?' in filename.typ ; LD A,(QMCNT) ; get number of question marks OR A ; set zero flag to indicate any '?' RET ; ; scanf -- scan token pted to by de for a max of b bytes; place it into ; file name field pted to by hl; expand and interpret wild cards of ; '*' and '?'; on exit, de pts to terminating delimiter ; SCANF: CALL SDELM ; done if delimiter encountered - fill JR Z,SCANF4 INC HL ; pt to next byte in deffcb CP '*' ; is (de) a wild card? JR NZ,SCANF1 ; continue if not LD (HL),'?' ; place '?' in deffcb and dont advance de if so CALL SCQ ; scanner count question marks JR SCANF2 SCANF1: LD (HL),A ; store filename char in deffcb INC DE ; pt to next char in command line CP '?' ; check for question mark (wild) CALL Z,SCQ ; scanner count question marks SCANF2: DJNZ SCANF ; decrement char count until 8 elapsed SCANF3: CALL SDELM ; 8 chars or more - skip until delimiter RET Z ; zero flag set if delimiter found INC DE ; pt to next char in command line JR SCANF3 ; ; fill memory pointed to by hl with spaces for b bytes ; SCANF4: INC HL ; pt to next byte in deffcb LD (HL),' ' ; fill filename part with DJNZ SCANF4 RET ; ; increment question mark count for scanner ; this routine increments the count of the number of question marks in ; the current fcb entry ; SCQ: LD A,(QMCNT) ; get count INC A ; increment LD (QMCNT),A ; put count RET ; ; check to see if de pts to delimiter; if so, ret w/zero flag set ; SDELM: LD A,(DE) OR A ; 0=delimiter RET Z CP ' ' ; error if < RET Z ; =delimiter CP '=' ; '='=delimiter RET Z CP 5FH ; underscore=delimiter RET Z CP '.' ; '.'=delimiter RET Z CP ':' ; ':'=delimiter RET Z CP ';' ; ';'=delimiter RET Z CP '<' ; '<'=delimiter RET Z CP '>' ; '>'=delimiter RET ; ; advance input ptr to first non-blank and fall through to sblank ; ADVNCE: LD (CIBPTR),DE ; ; skip string pted to by de (string ends in 0) until end of string ; or non-blank encountered (beginning of token) ; SBLANK: LD A,(DE) OR A RET Z CP ' ' RET NZ INC DE JR SBLANK ; ; ; capitalize string (ending in 0) in cmdlin and set ptr for parsing ; CNVBUF: LD HL,CMDLIN+1 ; pt to users command LD B,(HL) ; char count in b INC B ; add 1 in case of zero CNVBF1: INC HL ; pt to 1st valid char LD A,(HL) ; capitalize command char CALL UCASE LD (HL),A DJNZ CNVBF1 ; continue to end of command line CNVBF2: LD (HL),0 ; store ending LD HL,CMDLIN+2 ; set command line ptr to 1st char LD (CIBPTR),HL RET ; ; convert char in a to upper case ; UCASE: CP 61H ; lower-case a RET C CP 7BH ; greater than lower-case z? RET NC AND 5FH ; capitalize RET ; GETOLD: CALL GETDFU ; get current user into 'B' CALL GETDFD ; get current driv into 'C' LD (OLDDRV),BC ; get the parameters RET ; SETTMP: LD BC,(TMPDRV) JR RESET ; SETOLD: LD BC,(OLDDRV) JR RESET ; SETNEW: LD BC,(RQDDRV) ; get the old drive number JR RESET ; SETCOM: LD BC,(COMDRV) ; get the old drive number ; RESET: PUSH BC ; save drive/user PUSH BC LD E,C ; get selected drive LD C,14 ; bdos function CALL BDOS POP BC ; restore drive/user LD E,B ; get selected user LD C,32 ; bdis set user function CALL BDOS ; ; set up byte at 4h - some programs may look at it ; POP BC LD A,B ; get user number RLA RLA RLA RLA AND 0F0H OR C LD (4),A RET ; DVUPR1: LD A,(TMPUSR) PUSH AF LD A,(TMPDRV) JR DVUPR3 ; DVUPRT: LD A,(RQDUSR) ; get requested drive PUSH AF LD A,(RQDDRV) ; get the requested user DVUPR3: ADD A,'A' CALL CTYPE ; print the drive 'A'-'P' POP AF CP 10 ; less that 10? JR C,DVUPR2 ; yes - dont print the '1' PUSH AF LD A,'1' CALL CTYPE POP AF SUB 10 DVUPR2: ADD A,'0' CALL CTYPE LD A,':' JP CTYPE ; ; storage area ; ARCFLG: DEFB 0 DOADIR: DEFB 0 HLPCNT: DEFB 0 BIOS3: DEFW 0 DRVMAX: DEFB 0 USRMAX: DEFB 0 TEMPDR: DEFB 0 CIPTR: DEFW 0 TDRIVE: DEFB 0 QMCNT: DEFB 0 CIBPTR: DEFW 0 TEMPHL: DEFW 0 LENGTH: DEFB 0 OLDDRV: DEFB 0 OLDUSR: DEFB 0 RQDDRV: DEFB 0 ; requested drive RQDUSR: DEFB 0 ; requested user COMDRV: DEFB 0 ; drive to load com file COMUSR: DEFB 0 ; user to load com file TMPDRV: DEFB 0 ; temporary drive number TMPUSR: DEFB 0 ; temporary user number ACTIVE: DEFB 0 ; attach command mode active NXTWRD: DEFW 0 DEFW 0 CMDLIN: DEFB 79 CMDLEN: DEFB 0 DEFS 79 DEFB 0 ; MEMBER: DEFB ' ' DEFB 0 DEFB 0 LBRNAM: DEFB ' ' ; library file name DEFB 'LBR' ; TMPFCB: DEFS 36 DEFFCB: DEFS 36 DEFS 80 STACK EQU $ ; ; END