; PROGRAM: VFILER ; VERSION: 2.0 ; DATE: 18 Sep 83 ; AUTHOR: Richard Conn ; PREVIOUS VERSIONS: 1.8 (17 Sep 83) ; PREVIOUS VERSIONS: 1.7 (9 Sep 83), 1.6 (18 Aug 83), 1.5 (20 July 83) ; PREVIOUS VERSIONS: 1.4 (19 July 83), 1.3 (18 July 83) ; PREVIOUS VERSIONS: 1.2 (18 July 83), 1.1 (17 July 83), 1.0 (16 July 83) ; DERIVATION: From FILER (Version 1.6) by Richard Conn ; FILER from DISK7, Version 7.6C, by Frank Gaude' ; VERS EQU 20 ;version number ; VFILER is copyright (c) 1983 by Richard Conn ; All Rights Reserved ; VFILER may be used freely by the ZCPR2 Community ; VFILER is a screen-oriented, ZCPR2-specific file utility. It can be ; installed to run under conventional CP/M by turning all of the ZCPR2-specific ; options off, but it is highly recommended to obtain ZCPR2 (or the 8080 ; version called ZC8080) and use VFILER in conjunction with it. VFILER ; extensively employs cursor addressing to position a pointer on the ; screen, allow the user to manipulate the pointer (up, down, right, left, ; next screen, previous screen, GOTO file). The pointer points to files ; in the current user directory and displays the user's position dynamically ; on the screen. Once pointing to a file, user commands can be used to ; manipulate the file (delete, copy, view on console, print on printer, tag ; for later copy or delete, and untag). In the way of being ZCPR2-specific, ; VFILER can chain to external programs via the MCL and then return (ala ; MENU), and it recognizes Named Directories (so the user can log into B:, B4:, ; and MYDIR:, for example). ; VFILER is installed by GENINS. ; VFILER works with CP/M 2.2 or ZCPR2 only, with 32k or more of RAM. File copy ; functions are faster with large amounts of RAM. It occupies 8K of RAM. ; VFILER can be assembled for use with a Z80 or 8080 microprocessor. ; starting definitions VFNAME MACRO ;;Name of VFILER DB 'VFILER' ENDM VFNFILL MACRO ;;Spaces to fill out name to 8 chars DB ' ' ENDM TRUE EQU 0FFH ;define true and.. FALSE EQU 0 ;..false. Z80 EQU TRUE ;TRUE to use Z80 Instructions WARMBOOT EQU FALSE ;set TRUE to warmboot on exit DEFALPHA EQU TRUE ;set TRUE to alpha by name and type, FALSE for ; ... type and name by default FPESC EQU '%' ;escape char FPDISK EQU 'D' ;disk only (D) FPUSER EQU 'U' ;user only (U) FPFILE EQU 'F' ;file name only MNOTE EQU '*' ;denotes comment area in macro file UIN1 EQU 27H ;single quote for user input UIN2 EQU 22H ;double quote for user input CPM$BASE EQU 000H ;cp/m system base.. TPA EQU 100H ;..'transient program area' start.. CCP EQU 800H ;..and 'ccp' length in bytes. GET EQU 0FFH ;get user area e-reg value EPS EQU 16*4 ;16 lines x 4 cols per screen ; EPS = Entries Per Screen ; cursor positioning as per the user's particular terminal ; this is set for the TVI 950 function keys USER$UP EQU 0BH ;^K USER$DOWN EQU 16H ;^V USER$RIGHT EQU 0CH ;^L USER$LEFT EQU 08H ;^H SCR$FOR EQU 06H ;^F SCR$BACK EQU 01H ;^A ; cursor positioning addresses EPSLINE EQU (EPS/4)+5 ;position of last line of EPS BANADR EQU 1*256+24 ;banner address SDMADR EQU 3*256+30 ;screen directory message CURHOME EQU 4*256+1 ;home address of cursor BOTADR EQU 23*256+1 ;bottom of screen CPMADR EQU EPSLINE*256+1 ;command prompt message CPADR EQU EPSLINE*256+30 ;command prompt ERADR EQU (EPSLINE+1)*256+30 ;error message FSADR EQU ERADR ;file size message FNADR EQU (EPSLINE+1)*256+15 ;address of file name ; ASCII definitions CTRLC EQU 'C'-'@' ;..control-C.. CTRLD EQU 'D'-'@' CTRLE EQU 'E'-'@' CTRLR EQU 'R'-'@' CTRLS EQU 'S'-'@' ;..XOFF.. CTRLX EQU 'X'-'@' BS EQU 08H ;..backspace.. TAB EQU 09H ;..tab.. LF EQU 0AH ;..linefeed.. CR EQU 0DH ;..carriage return.. CAN EQU 18H ;..cancel.. EOFCHAR EQU 1AH ;..end-of-file.. CTRLZ EQU 1AH ;..clear screen.. ESC EQU 1BH ;..and escape character. ; ; MACROS TO PROVIDE Z80 EXTENSIONS ; MACROS INCLUDE: ; $-MACRO ;FIRST TURN OFF THE EXPANSIONS ; ; JR - JUMP RELATIVE ; JRC - JUMP RELATIVE IF CARRY ; JRNC - JUMP RELATIVE IF NO CARRY ; JRZ - JUMP RELATIVE IF ZERO ; JRNZ - JUMP RELATIVE IF NO ZERO ; DJNZ - DECREMENT B AND JUMP RELATIVE IF NO ZERO ; ; ; ; @GENDD MACRO USED FOR CHECKING AND GENERATING ; 8-BIT JUMP RELATIVE DISPLACEMENTS ; @GENDD MACRO ?DD ;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS IF (?DD GT 7FH) AND (?DD LT 0FF80H) DB 100H ;Displacement Range Error on Jump Relative ELSE DB ?DD ENDIF ENDM ; ; ; Z80 MACRO EXTENSIONS ; JR MACRO ?N ;;JUMP RELATIVE IF Z80 DB 18H @GENDD ?N-$-1 ELSE JMP ?N ENDIF ENDM ; JRC MACRO ?N ;;JUMP RELATIVE ON CARRY IF Z80 DB 38H @GENDD ?N-$-1 ELSE JC ?N ENDIF ENDM ; JRNC MACRO ?N ;;JUMP RELATIVE ON NO CARRY IF Z80 DB 30H @GENDD ?N-$-1 ELSE JNC ?N ENDIF ENDM ; JRZ MACRO ?N ;;JUMP RELATIVE ON ZERO IF Z80 DB 28H @GENDD ?N-$-1 ELSE JZ ?N ENDIF ENDM ; JRNZ MACRO ?N ;;JUMP RELATIVE ON NO ZERO IF Z80 DB 20H @GENDD ?N-$-1 ELSE JNZ ?N ENDIF ENDM ; DJNZ MACRO ?N ;;DECREMENT B AND JUMP RELATIVE ON NO ZERO IF Z80 DB 10H @GENDD ?N-$-1 ELSE DCR B JNZ ?N ENDIF ENDM ; ; END OF Z80 MACRO EXTENSIONS ; ; assembly origin (load address) and program beginning ORG CPM$BASE+TPA SOURCE: JMP FILER ; ; ZCPR2 and its utilities, including this one, are released ; to the public domain. Anyone who wishes to USE them may do so with ; no strings attached. The author assumes no responsibility or ; liability for the use of ZCPR2 and its utilities. ; ; ;****************************************************************** ; ; SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format ; ; This data block precisely defines the data format for ; initial features of a ZCPR2 system which are required for proper ; initialization of the ZCPR2-Specific Routines in SYSLIB. ; ; ; EXTERNAL PATH DATA ; EPAVAIL: DB 0FFH ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES) EPADR: DW 40H ; ADDRESS OF EXTERNAL PATH IF AVAILABLE ; ; INTERNAL PATH DATA ; INTPATH: DB 0,0 ; DISK, USER FOR FIRST PATH ELEMENT ; DISK = 1 FOR A, '$' FOR CURRENT ; USER = NUMBER, '$' FOR CURRENT DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 ; DISK, USER FOR 8TH PATH ELEMENT DB 0 ; END OF PATH ; ; MULTIPLE COMMAND LINE BUFFER DATA ; MCAVAIL: DB 0FFH ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE? MCADR: DW 0FF00H ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE ; ; DISK/USER LIMITS ; MDISK: DB 4 ; MAXIMUM NUMBER OF DISKS MUSER: DB 31 ; MAXIMUM USER NUMBER ; ; FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK ; DOK: DB 0FFH ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES) UOK: DB 0FFH ; ALLOW USER CHANGE? (0=NO, 0FFH=YES) ; ; PRIVILEGED USER DATA ; PUSER: DB 10 ; BEGINNING OF PRIVILEGED USER AREAS PPASS: DB 'chdir',0 ; PASSWORD FOR MOVING INTO PRIV USER AREAS DS 41-($-PPASS) ; 40 CHARS MAX IN BUFFER + 1 for ending NULL ; ; CURRENT USER/DISK INDICATOR ; CINDIC: DB '$' ; USUAL VALUE (FOR PATH EXPRESSIONS) ; ; DMA ADDRESS FOR DISK TRANSFERS ; DMADR: DW 80H ; TBUFF AREA ; ; NAMED DIRECTORY INFORMATION ; NDRADR: DW 00000H ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY NDNAMES: DB 64 ; MAX NUMBER OF DIRECTORY NAMES DNFILE: DB 'NAMES ' ; NAME OF DISK NAME FILE DB 'DIR' ; TYPE OF DISK NAME FILE ; ; REQUIREMENTS FLAGS ; EPREQD: DB 0FFH ; EXTERNAL PATH? MCREQD: DB 0FFH ; MULTIPLE COMMAND LINE? MXREQD: DB 0FFH ; MAX USER/DISK? UDREQD: DB 0FFH ; ALLOW USER/DISK CHANGE? PUREQD: DB 000H ; PRIVILEGED USER? CDREQD: DB 0FFH ; CURRENT INDIC AND DMA? NDREQD: DB 0FFH ; NAMED DIRECTORIES? Z2CLASS: DB 11 ; CLASS 11 DB 'ZCPR2' DS 10 ; RESERVED ; ; END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA ; ;****************************************************************** ; ; ; **** Special Initial Value Area for GENINS ; LWIDTH: DB 132 ; WIDTH OF LINE LTPP: DB 44 ; LINES OF TEXT PER PAGE LSPP: DB 5 ; LINES TO SKIP PER PAGE CWIDTH: DB 80 ; WIDTH OF SCREEN CTPP: DB 22 ; LINES OF TEXT PER SCREEN CSPP: DB 1 ; LINES TO SKIP PER SCREEN ; ; Screen Routines ; org 200h ; base page for screen routines CLS: JMP CLS950 GOTOXY: JMP GOTO950 EREOL: JMP EREOL950 CURTABLE: DB USER$UP,USER$DOWN,USER$RIGHT,USER$LEFT ; up, down, right, left DB SCR$FOR,SCR$BACK ; screen forward, back ; screen routines (for TVI 950) ; clear screen CLS950: MVI A,CTRLZ ;clear screen JMP TYPE ; position cursor (H=row, L=col) where 1,1=upper left GOTO950: MVI A,ESC ;ESCape CALL TYPE MVI A,'=' CALL TYPE MOV A,H ;row ADI ' ' CALL TYPE MOV A,L ;column ADI ' ' JMP TYPE ; erase to end of line EREOL950: MVI A,ESC ;ESCape CALL TYPE MVI A,'T' JMP TYPE ; start of program org 300h ;page address JMP TYPE ;I/O support for package FILER: IF NOT WARMBOOT LXI H,0 ;clear hl-pair then.. DAD SP ;..add cp/m's stack address. SHLD STACK ENDIF ;not warmboot LXI SP,STACK ;start local stack MVI A,DEFALPHA ;alpha by name and type STA ALPHA LXI H,CURTABLE ;init cursor commands LXI D,CTABLE ;pt to area MVI B,6 ;6 commands CURINIT: MOV A,M ;get command STAX D ;put it INX H ;pt to next INX D INX D INX D DJNZ CURINIT CALL IDU ;set initial disk/user LDA NDNAMES ;size of disk-based named directory LXI H,0 MOV E,A ;... in DE MVI D,0 DAD D ;*1 DAD H ;*2 DAD H ;*4 DAD D ;*5 DAD H ;*10 INR H ;next page MVI L,0 LXI D,BUFENTRY ;base address DAD D SHLD RING ;beginning of ring XCHG ;HL pts to BUFENTRY CALL ZDNAME ;scan for and load NAMES.DIR JRZ FILERPASS MOV A,C ;set count STA BUFENTRY-1 FILERPASS: LDA MDISK ;get max disk number ADI 'A'-1 STA MAXDR ;set letter LDA FCB+1 ;check for initial help CPI '/' JRZ FILERH LDA FCB2+1 ;check for wait CPI 'W' JRNZ FILER0 FILERSAK: CALL ILPRT DB CR,LF,'Strike Any Key to Enter VFILER -- ',0 CALL DKEYIN JR FILER0 FILERH: CALL HELPMSG ;print help message JR FILERSAK FILER0: CALL HELPCHK ;check for availability of HELP Files JMP EMBARK ; set initial disk/user IDU: LXI H,FCB+1 ;check for DU specification MOV A,M CPI ' ' ;=none JRZ IDU1 CALL DEF$DU0 ;extrace drive/user MOV A,C ;get current user CALL IDU$USET ;set it MOV A,B ;get current disk CALL IDU$DSET ;set it JMP LOG1Z ;log it in IDU1: MVI E,GET ;determine.. CALL GET$USR ;..user area then.. CALL IDU$USET ;set current user MVI C,INQDISK ;determine current disk CALL BDOS CALL IDU$DSET ;set current disk JMP LOG1Z ;set current user and disk IDU$USET: STA C$U$A ;..store as current and.. STA O$USR ;..as original for exit. STA R$U$A ;..requested user area RET IDU$DSET: STA C$DR STA R$DR ;requested disk RET ; check for availability of HELP Files (HELP.COM and FILER.HLP) HELPCHK: XRA A ;assume NO STA HELPFLG ;set flag LXI D,HELPFCB CALL FILECHK ;check for file ORA A ;0=no RZ MVI A,0FFH ;set flag STA HELPFLG RET ; check for existance of file whose first 12 FCB bytes are pted to by DE ; return with A=0 if not found, A=0FFH if found FILECHK: LXI H,S$FCB ;copy into FCB XCHG MVI B,12 ;12 bytes CALL MOVE ;copied into S$FCB XCHG ;HL pts to FCB CALL INITFCB ;init FCB LXI D,S$FCB ;pt to FCB JMP FFIND ; determine if specific file(s) requested -- show remaining storage EMBARK: CALL FRESTOR ;get bytes remaining on drive (decode default) LXI H,JOKER ;..treat as '*.*' with 'joker'.. LXI D,FCB+1 ;..loaded here. MVI B,11 ; # of characters to move CALL MOVE ;set field to *.* ; build 'ring' with filename positioned in default 'fcb' area PLUNGE: MVI C,SETDMA ;initialize dma address.. LXI D,TBUF ;..to default buffer. CALL BDOS XRA A ;clear search 'fcb'.. STA FCBEXT ;extent byte.. STA FCBRNO ;..and record number. CMA STA CANFLG ;make cancel flag true LXI D,FCB ;default 'fcb' for search.. MVI C,SRCHF ;..of first occurrence. CALL BDOS INR A ; 0ffh --> 00h if no file found JNZ SETRING ;if found, branch and build ring. STA CANFLG ;make log-cancel toggle false CALL ERMSG ;else say none found, fall thru to log. DB 'No File Found',0 ;fall thru to LOG ; l o g ; select drive and user area (system reset for disk change on-the-fly) LOG: CALL CPRMPT ;prompt to get drive/user selection DB 'Login DIR: ',0 CALL DEF$D$U LOG1: CALL LOG1Z ;set current and log in CALL CRLF ;new line JMP EMBARK ;..restart ; set current user and disk LOG1X: LXI H,LOG$DU$MSG LDA R$DR ;set prompt message ADI 'A' ;adjust to letter MOV M,A INX H MVI M,' ' ;prep for user < 10 LDA R$U$A ;get user CPI 10 ;less than 10? JRC LOG2 MVI B,'1' ;set digits LOG1A: SUI 10 ;adjust user CPI 10 ;less? JRC LOG1B INR B ;incr 10's JR LOG1A LOG1B: MOV M,B ;set 10's LOG2: INX H ;pt to 1's ADI '0' ;to ASCII MOV M,A RET ; actually log into DU requested LOG1Y: LDA R$DR ;make requested disk current STA C$DR CALL RESET ;reset disk system LDA R$U$A ;establish requested area.. STA C$U$A ;..as current area. CALL SET$USR LXI H,0 ;initialize tagged.. SHLD TAG$TOT ;..file size accumulator. RET ; set current DU and log into it LOG1Z: CALL LOG1X ;set current JR LOG1Y ;actually log in ; routine to define current drive and user area with full error trapping. ; (check validity of user area entry first, then drive validity, then proceed ; with implementation.) DEF$D$U: LXI H,CMDBUF+2 MVI B,20 ; # of blanks to.. CALL FILL ;..clear 'cmdbuf'. LXI D,CMDBUF ;get DU selection from.. MVI C,RDBUF ;..console buffer read. CALL BDOS CALL CONVERT ;make sure alpha is upper case LXI H,CMDBUF+2 ;pt to possible drive DEF$DU0: CALL ZDNFIND ;look for DU or DIR form and return DU JRZ ERRET ;error MOV A,B ;return disk and user STA R$DR INR A ;set FCB STA FCB MOV A,C STA R$U$A RET ; error return and recovery from command cancellation ERRET: CALL ERMSG DB 'DIR Entry Error',0 JMP NEUTRAL COMCAN: LXI SP,STACK ;reset stack.. LDA CANFLG ORA A ;..from.. JZ PLUNGE CALL REFRESH ;refresh screen JMP LOOPFN ;..error/command abort. ; find file along path (file FCB pted to by DE) ; on return, A=0FFH if found, A=0 if not found, and flags set FFIND: PUSH D ;save ptr to FCB MVI E,GET ;get and save current DU CALL GET$USR STA C$U$A STA Z$U$A MVI C,INQDISK CALL BDOS STA C$DR STA Z$DR POP D ;get ptr to FCB CALL GETPATH ;HL pts to current path FFINDL: CALL SEARF ;look for file JRNZ FFOUND ;found file LDA CINDIC ;get current indictor MOV C,A ;... in C MOV A,M ;get drive ORA A ;0=done=not found JRZ FNFOUND CMP C ;current disk? JRNZ FF1 LDA C$DR ;get current disk INR A ;increment for following DCR FF1: DCR A ;adjust to 0 for A MOV B,A ;disk in B STA Z$DR ;note disk INX H ;pt to user MOV A,M ;user in A CMP C ;current? JRNZ FF2 LDA C$U$A ;get current user FF2: MOV C,A ;user in C STA Z$U$A ;note user INX H ;pt to next entry CALL SLOGIN ;log in DU JR FFINDL FFOUND: CALL DLOGIN ;log in default MVI A,0FFH ;set flag ORA A RET FNFOUND: CALL DLOGIN ;log in default XRA A ;set flag RET ; get starting address of path in HL GETPATH: LDA EPAVAIL ;external path available? ORA A JRZ GPINT LHLD EPADR ;get address of external path RET GPINT: LXI H,INTPATH ;internal path RET ; search for file pted to by DE; don't affect DE or HL; ret code in A SEARF: PUSH H ;save regs PUSH D MVI C,SRCHF ;search for file CALL BDOS INR A ;set flags POP D ;get regs POP H RET ; log in default directory DLOGIN: LDA C$DR ;disk in B MOV B,A LDA C$U$A ;user in C MOV C,A ;fall thru to SLOGIN ; log in DU in BC SLOGIN: PUSH H ;save regs PUSH D PUSH B MOV A,C ;set user CALL SET$USR POP B MOV A,B ;set disk CALL SET$DR POP D ;restore regs POP H RET ; e x i t ; return to cp/m ccp CPM$CCP: LDA O$USR ;get and set original.. CALL SET$USR ;..user area and.. LXI D,TBUF ;..tidy up.. MVI C,SETDMA ;..before going home. CALL BDOS CALL CLS IF WARMBOOT JMP CPM$BASE ENDIF ;warmboot IF NOT WARMBOOT LHLD STACK ;put cp/m's pointer.. SPHL ;..back to 'sp'. RET ;return to cp/m ccp ENDIF ;not warmboot ; establish ring (circular list) of filenames SETRING: LHLD RING ;initialize ring pointer SHLD RINGPOS ;start --> current position of ring ; put each found name in ring. a-reg --> offset into 'tbuf' name storage TO$RING: DCR A ;un-do 'inr' from above and below ADD A ;times 32 --> position index ADD A ADD A ADD A ADD A ADI TBUF ;add page offset and.. MOV L,A ;..put address into.. MVI H,0 ;..hl-pair. LDA FCB ;get drive/user designator and.. MOV M,A ;..put into 'fcb' buffer. XCHG LHLD RINGPOS ;pointer to current load point in ring XCHG MVI B,12 ;move drive designator and name to ring CALL MOVE XCHG ;de-pair contains next load point address MVI M,' ' ;space for potential.. INX H ;..tagging of files for mass copy. SHLD RINGPOS ;store and search.. MVI C,SRCHN ;..for next occurrence. LXI D,FCB ;filename address field CALL BDOS INR A ;if all done, 0ffh --> 00h. JRNZ TO$RING ;if not, put next name into ring. ; all filenames in ring -- setup ring size and copy-buffer start point LHLD RINGPOS ;next load point of ring is start of buffer SHLD RINGEND ;set ring end.. SHLD BUFSTART ;..and copy-buffer start. PUSH H LHLD RING LXI D,13 ;compare 'ringend' (tab base+13) DAD D XCHG POP H CALL CMPDEHL JZ CMDLOOP ;go to command loop, if no sort. ; sort ring of filenames SORT: LHLD RING ;initialize 'i' sort variable and.. SHLD RINGI LXI D,13 ;..also 'j' variable. DAD D SHLD RINGJ SORTLP: LHLD RINGJ ;compare names 'i & j' XCHG LHLD RINGI PUSH H ;save position pointers.. PUSH D ;..for potential swap. LDA ALPHA ;check for type of alphabetization ORA A ;if Zero, alpha by type and name JZ SORTTN ; sort by file name and type MVI B,12 ; # of characters to compare CALL CMPSTR ;do comparison JR NOCMP ;final test ; sort by file type and name SORTTN: PUSH H ;save ptrs PUSH D LXI B,9 ;pt to type DAD B XCHG DAD B XCHG MVI B,3 ;3 chars in file type CALL CMPSTR ;compare type POP D ;get ptrs POP H JRNZ NOCMP ;final test PUSH H PUSH D MVI B,8 ;8 chars in file name INX H ;pt to first INX D CALL CMPSTR ;compare name POP D ;get ptrs POP H JRNZ NOCMP ;final test LDAX D ;final compare and fall thru CMP M ; final test for swapping purposes NOCMP: POP D POP H MVI B,13 JRNC NOSWAP ; swap if 'j' string larger than 'i' SWAP: MOV C,M ;get character from one string.. LDAX D ;..and one from other string. MOV M,A ;second into first MOV A,C ;first into second STAX D INX H ;bump swap pointers INX D DJNZ SWAP NOSWAP: LHLD RINGJ ;increment 'j' pointer LXI D,13 DAD D SHLD RINGJ XCHG ;see if end of 'j' loop LHLD RINGEND CALL CMPDEHL JNZ SORTLP ;no, so more 'j' looping. LHLD RINGI ;bump 'i' pointer LXI D,13 DAD D SHLD RINGI DAD D ;set start over 'j' pointer SHLD RINGJ XCHG ;see if end of 'i' loop LHLD RINGEND CALL CMPDEHL JNZ SORTLP ;must be more 'i' loop to do ; sort done -- initialize tables for fast crc calculations CALL INITCRC ; calculate buffer maximum available record capacity B$SIZE: LXI B,0 ;count records LHLD BDOS+1 ;get 'bdos' entry (fbase) IF NOT WARMBOOT LXI D,-(CCP) DAD D ENDIF ;not warmboot DCX H XCHG ;de-pair --> highest address of buffer LHLD BUFSTART ;start address of buffer (end of ring list) B$SIZE2: INX B ;increase record count by one PUSH D LXI D,128 ; 128-byte record DAD D ;buffer address + record size POP D CALL CMPDEHL ;compare for all done JRNC B$SIZE2 ;more will fit? DCX B ;set maximum record count less one MOV A,B ;memory available for copy? ORA C JRNZ B$SIZE3 ;yes, buffer memory space available. CALL ERMSG DB 'No Room for Buffer',0 JMP NEUTRAL B$SIZE3: MOV L,C ;store.. MOV H,B ;..maximum.. SHLD REC$MAX ;..record count. JMP CMDLOOP ; left to right compare of two strings (de-pair points to 'a' string; ; hl-pair, to 'b'; b-reg contains string length.) CMPSTR: LDAX D ;get an 'a' string character and.. CMP M ;..check against 'b' string character. RNZ ;if not equal, set flag. INX H ;bump compare.. INX D ;..pointers and.. DCR B ; (if compare, set as equal.) JRNZ CMPSTR ;..do next character. RET ; buffer size suitable -- process file/display loop LOOPFN: LXI H,FNADR ;position cursor for file name print CALL GOTOXY LHLD RINGPOS ;pt to current file name INX H ;pt to first char CALL PRFN ;print file name LOOP: CALL ATCMD ;position at command prompt CALL DKEYIN ;wait for character from keyboard PUSH PSW ;save command LDA ERMFLG ;error message? ORA A ;0=no JRZ CPROC CALL ERCLR ;erase old error message CPROC: POP PSW ;get command CPI MNOTE ;command summary JRZ CPROCM CPI '0' ;macro? JC CPROC0 CPI '9'+1 ;macro? JNC CPROC0 CPROCM: CALL CMACRO ;process macro command MOV B,A ;save command CALL ERMSG DB 'No U Fct ',0 MOV A,B ;get macro digit CALL TYPE JR LOOP CPROC0: CALL CTPROC ;process command or return if not found CALL ERMSG DB 'Bad Cmd: ',0 MOV A,B ;get char CPI ' ' ;expand if less than space JRNC CPROC1 MVI A,'^' ;control CALL TYPE MOV A,B ;get byte ADI '@' ;convert to letter CPROC1: CALL TYPE NEUTRAL: JR LOOP ;..position. ; process macro command CMACRO: PUSH PSW ;save digit LXI H,MACFCB ;set up name of macro file LXI D,S$FCB PUSH D MVI B,12 ;12 chars CALL MOVE XCHG CALL INITFCB ;init FCB POP D ;pt to FCB CALL FFIND ;search along path for file JRNZ CMFOUND ;file found POP PSW ;clear stack POP D ;clear ret address CALL ERMSG DB 'File ',0 LXI H,MACFCB+1 CALL PRFN ;print file name CALL ILPRT DB ' NOT Found',0 JMP LOOP ; found macro file CMFOUND: LDA Z$DR ;set DU for temp login MOV B,A LDA Z$U$A MOV C,A CALL SLOGIN ;log in LXI D,S$FCB ;pt to FCB CALL FIOPEN ;open for byte-oriented input POP PSW ;get command MOV C,A ;... in C ; new line CMFL1: CALL FGET ;get first char of line JNZ CMFLERR ;EOF encountered CMP C ;match? JRZ CMFL3 ; skip out line CMFL2: CALL FGET ;skip out line JNZ CMFLERR CPI LF ;EOL? JRNZ CMFL2 JR CMFL1 ; found command CMFL3: MVI A,MNOTE ;macro note? CMP C JZ CMFLNOTE LXI H,CRCTBL+256 ;buffer for command line ; skip leading blanks in command line CMFL4: CALL FGET ;get next char JRNZ CMFL6 ;mark end of buffer and go CPI ' ' ;skip leading blanks JRZ CMFL4 ; load command line into buffer CMFL5: ANI 7FH ;mask char CPI CR ;EOL? JRZ CMFL6 CPI CTRLZ ;EOF? JRZ CMFL6 CPI FPESC ;file pted to? JZ CMFLIF CPI UIN1 ;user input? JRZ CMFL5A CPI UIN2 JRZ CMFL5A CMFL50: MOV M,A ;store char INX H ;pt to next CMFL51: CALL FGET ;get next char JRZ CMFL5 JR CMFL6 ; print prompt to user and get input CMFL5A: PUSH H ;save buffer ptr CALL CPRMPT ;at command prompt line DB 0 CMFL5B: CALL FGET ;get next char JRNZ CMFL5B1 ANI 7FH ;mask CPI UIN1 ;end of prompt? JRZ CMFL5C CPI UIN2 JRZ CMFL5C CPI CR ;end of prompt? JRZ CMFL5C CPI CTRLZ ;eof? JRZ CMFL5C CALL TYPE ;send char JR CMFL5B CMFL5B1: MVI A,CTRLZ ;eof CMFL5C: PUSH PSW ;save offending char LHLD BUFSTART ;pt to buffer MVI M,128 ;128 char in line INX H ;set no chars MVI M,0 DCX H ;pt to count XCHG MVI C,RDBUF ;get line from user CALL BDOS LHLD BUFSTART INX H ;pt to count MOV A,M ;get count INX H ;pt to first char PUSH H ;save ptr ADD L MOV L,A MOV A,H ACI 0 MOV H,A ;HL pts to after last char MVI M,0 ;store ending zero POP H ;HL pts to first char of line POP PSW ;get char POP D ;DE pts to next char in buffer MOV B,A ;char in B CMFL5D: MOV A,M ;copy chars ORA A ;zero=done JRZ CMFL5E STAX D ;put char INX H ;pt to next INX D JR CMFL5D CMFL5E: XCHG ;HL pts to next char in buffer MOV A,B ;check offending char CPI UIN1 ;user input? JRZ CMFL51 CPI UIN2 JRNZ CMFL5 JR CMFL51 ; complete buffer and test for content CMFL6: MVI M,0 ;store ending 0 LXI H,CRCTBL+256 ;pt to first char in line MOV A,M ;get first char ORA A ;empty? JRNZ CMFL7 ; error return CMFLERR: PUSH B CALL DLOGIN ;return home POP B MOV A,C ;invalid command RET ; try to chain CMFL7: PUSH H ;save ptr CALL DLOGIN ;return home POP H CALL CHAIN ;chain to command line pted to by HL POP D ;if here, then error JMP LOOP ; print documentation built into the macro file CMFLNOTE: CALL CLS ;new screen MVI C,0 ;set tab counter CMFLN1: CALL FGET ;get next char JRNZ CMFLN4 ;done ANI 7FH ;mask CPI CTRLZ ;eof=done JRZ CMFLN4 CPI TAB ;tab process JRZ CMFLN2 CALL TYPE ;print char CPI CR ;new line? JRZ CMFLN3 INR C ;new pos CPI LF JRNZ CMFLN1 DCR C ;back up position count JR CMFLN1 CMFLN2: MVI A,' ' ;space over for tab CALL TYPE INR C ;incr position MOV A,C ;done? ANI 7 ;every 8 JRNZ CMFLN2 JR CMFLN1 CMFLN3: MVI C,0 ;bol for CR JR CMFLN1 CMFLN4: LHLD RINGPOS ;print current file INX H CALL PRFN CALL ILPRT DB ' Enter Digit or RETURN - ',0 CALL DKEYIN POP D ;get ret address CPI '0' ;in range? JC SCREFRESH CPI '9'+1 ;in range? JNC SCREFRESH PUSH D ;replace ret address JMP CMACRO ; insert file pted to CMFLIF: CALL FGET ;get next char JNZ CMFL6 CPI FPESC ;doubled? JZ CMFL50 LXI D,CMFL51 ;return address PUSH D ;... on stack ANI 5FH ;capitalize CPI FPFILE ;file only? JRZ CMFLIF1 CPI FPDISK ;disk only? JRZ CMFLIF2 CPI FPUSER ;user only? JRZ CMFLIF3 CALL CMFLIF2 ;Disk CALL CMFLIF3 ;User MVI M,':' ;separator INX H ;fall thru to file name CMFLIF1: XCHG ;save ptr to next LHLD RINGPOS ;pt to current file INX H MVI B,8 ;8 chars in filename CALL CMFL5X MVI A,'.' ;put dot STAX D INX D MVI B,3 ;3 chars max CALL CMFL5X XCHG ;HL pts to next RET CMFLIF2: LDA C$DR ;store disk ADI 'A' MOV M,A INX H ;pt to next RET CMFLIF3: LDA C$U$A ;user CPI 10 ;less than 10? JRC CMFLIF32 MVI B,'0' ;compute 10's CMFLIF30: SUI 10 ;subtract 10's JRC CMFLIF31 INR B JR CMFLIF30 CMFLIF31: ADI 10 MOV M,B ;store 10's INX H CMFLIF32: ADI '0' ;store 1's MOV M,A INX H RET CMFL5X: MOV A,M ;get char CPI ' ' JRZ CMFL5X1 STAX D ;put char INX H ;pt to next INX D DJNZ CMFL5X RET CMFL5X1: INX H ;advance DJNZ CMFL5X1 RET ; process command from table CTPROC: LXI H,CTABLE ;pt to table MOV B,A ;command in B CTPR1: MOV A,M ;get table command char ORA A ;end of table? RZ ;done if so CMP B ;match? JRZ CTPR2 INX H ;skip to next entry INX H INX H JR CTPR1 CTPR2: INX H ;pt to address MOV A,M ;get low INX H MOV H,M ;get high MOV L,A XTHL ;address on stack RET ;"jump" to routine ; Command Table CTABLE: DB 0 ;user cursor positioning DW UP DB 0 DW DOWN DB 0 DW FORWARD DB 0 DW REVERSE DB 0 ;user screen jumps DW JUMPF DB 0 DW JUMPB DB CTRLE ;system cursor positioning DW UP DB CTRLX DW DOWN DB CTRLD DW FORWARD DB CTRLS DW REVERSE DB CTRLR ;screen refresh? DW SCREFRESH DB '+' ;jump forward DW JUMPF DB '-' ;jump backward DW JUMPB DB ' ' ;go forward DW FORWARD DB BS ;back up? DW REVERSE DB 'A' ;alphabetize? DW ALPHASW DB 'B' ;back up? DW REVERSE DB 'C' ;copy a file? DW COPY DB 'D' ;delete a file? DW DELETE DB 'F' ;show file size? DW FIL$SIZ DB 'G' ;goto a file? DW GOTO DB 'H' ;external help? DW EXTHELP DB 'L' ;log-in another drive? DW LOG DB 'M' ;tagged multiple file copy? DW MASS$COPY DB 'N' ;go forward DW FORWARD DB 'P' ;output file to 'list' device? DW LSTFILE DB 'Q' ;screen refresh? DW SCREFRESH DB 'R' ;rename? DW RENAME DB 'S' ;free bytes on.. DW R$DR$ST ;..requested drive? DB 'T' ;if tag, put '*' in.. DW TAG$EM ;..front of cursor. DB 'U' ;remove '*' from.. DW UNTAG ;..in front of cursor? DB 'W' ;mass tag/untag? DW MASS$TU DB 'V' ; 'view' file at console? DW VIEW DB 'X' ;if exit, then to cp/m ccp. DW CPM$CCP DB 'Y' ;mass delete? DW MASS$DEL DB 'Z' ;run ZCPR2 command DW RUNZCPR2 DB ESC ; 'esc' exits to cp/m ccp also. DW CPM$CCP DB '?' ;help DW HELP DB '/' ;help also DW HELP DB 0 ;end of table ; h e l p (menu) HELPMSG: CALL CLS CALL ILPRT DB '-- VFILER Commands --' DB CR,LF,CR,LF DB '-- Tagging Commands -- --------- File Operat' DB 'ions ----------',CR,LF DB ' T - Tag File C - Copy File ' DB 'M - Mass Copy',CR,LF DB ' U - Untag File D - Delete File ' DB 'R - Rename File',CR,LF DB ' W - Mass Tag/Untag F - File Size ' DB 'Y - Mass Delete',CR,LF DB CR,LF DB '-- File Print & View -- --- User Functions ---',CR,LF DB 'P - Print V - View 0-9 - Execute * - Help',CR,LF DB CR,LF DB '-- Movement Commands -- ---- Miscellaneous ----',CR,LF DB ' - File Forward A - Toggle Alpha Sort',CR,LF DB ' N - File Forward H - Help File',CR,LF DB ' - File Backward L - Login DIR',CR,LF DB ' B - File Backward Q - Refresh Screen',CR,LF DB ' G - Go To a File S - Disk Status',CR,LF DB ' + - Screen Forward X - Exit',CR,LF DB ' - - Screen Backward Z - ZCPR2 Command',CR,LF DB CR,LF DB ' -- Screen Movement --',CR,LF DB 'File: ^S=LEFT ^D=RIGHT ^E=UP ^X=DOWN',CR,LF DB 'Screen: ^A=LEFT ^F=RIGHT',CR,LF DB 0 CALL BOTTOM RET HELP: CALL HELPMSG ;print message CALL REFRESH ;refresh screen JMP LOOPFN EXTHELP: LDA HELPFLG ;check for further help ORA A ;0=no JRZ EHLP1 CALL CLS CALL ILPRT DB 'Chaining to Help File ...',0 LXI H,MOREHELP ;run HELP Command CALL CHAIN ;chain to it JMP LOOPFN EHLP1: CALL ERMSG DB 'No HELP File',0 JMP LOOPFN ; refresh screen SCREFRESH: CALL REFRESH ;do it JMP LOOPFN ;reprint name ; a l p h a ; toggle alphabetize switch and reload files ALPHASW: LDA ALPHA ;toggle flag CMA STA ALPHA CALL CPRMPT ;tell user what is happening DB 'Alphabetizing by File ',0 LDA ALPHA ;get flag ORA A ;check it JRZ ASWTN CALL ILPRT DB 'Name and Type',0 JMP EMBARK ASWTN: CALL ILPRT DB 'Type and Name',0 JMP EMBARK ;reload files ; execute ZCPR2 command line RUNZCPR2: CALL ERMSG DB 'ZCPR2 Command? ',0 LXI H,CRCTBL+256 ;use last half of CRC Table MVI M,126 ;store length of line INX H MVI M,0 ;store count DCX H ;pt to beginning XCHG ;DE pts to buffer PUSH D MVI C,RDBUF ;Read Line from User CALL BDOS POP H INX H ;pt to char count MOV E,M INX H ;pt to first char MVI D,0 ;set no high-order offset PUSH H ;save current ptr for later DAD D MVI M,0 ;store ending zero POP H ;pt to first char of command line MOV A,M ;abort if no first char ORA A JZ NEUTRAL CALL CHAIN ;chain to it JMP LOOPFN ;continue ; chain to command pted to by HL ending in zero CHAIN: LDA MCAVAIL ;multiple commands available? ORA A ;Z=NO JRNZ CHAIN1 CALL ERMSG DB 'No MCL',0 RET CHAIN1: PUSH H LHLD MCADR ;pt to MCL XCHG LXI H,4 DAD D XCHG MOV C,M ;save ptr to next command MOV M,E ;store address of first character INX H MOV B,M MOV M,D PUSH B ;ptr to next command saved INX H ;get buffer size MOV B,M ;...in B XCHG ;HL pts to first character POP D ;get ptr to rest of current command PUSH H ;save HL LXI H,CRCTBL ;save command in CRCTBL CHAIN2: LDAX D ;copy rest of command line for later MOV M,A INX H INX D ORA A JRNZ CHAIN2 POP H ;get ptr to beginning of buffer POP D ;DE pts to new command ; ; Copy Desired Command into MCL ; CALL CHAINC ;copy into command line ; ; Copy Command to Return to FILER into MCL ; PUSH H ;save ptr to MCL PUSH B ;save B counter LDA C$DR ;get current disk ADI 'A' ;convert disk to letter STA FILE$D ;store in proper place LDA C$U$A ;get current user LXI H,FILE$U ;store user number MVI C,'0' ;set char CHAIN3: SUI 10 ;convert to ASCII JRC CHAIN4 INR C ;increment 10's char JR CHAIN3 CHAIN4: MOV M,C ;store 10's digit char INX H ;pt to 1's digit ADI 10+'0' ;add back for 1's digit MOV M,A ;store 1's digit char POP B ;restore B counter POP H ;restore ptr and continue LXI D,FILERCMD ;return to VFILER CALL CHAINC ;copy rest ; ; Copy Rest of Original Command Line into MCL ; LXI D,CRCTBL ;pt to rest of original command line CALL CHAINC ;copy it in ; ; Close MCL and Run New Command Line ; MVI M,0 ;store ending zero JMP CPM$CCP CHAINC: LDAX D ;copy into MCL ORA A ;done? RZ DCR B ;check for buffer overflow JRZ CHAINERR MOV M,A INX D INX H JR CHAINC CHAINERR: POP D ;clear stack CALL ERMSG DB 'MCL Overflow',0 LHLD MCADR ;clear command line LXI D,4 DAD D MVI M,0 ;no command left RET ; mass tag or untag MASS$TU: MVI A,TRUE ;update file totals STA FS$FLG ;of tagged/untagged files CALL CPRMPT DB 'Mass Tag or Untag (T/U)? ',0 CALL KEYIN ;get response CPI 'T' JRZ MASS$TAG CPI 'U' JNZ NEUTRAL ;fall thru to MASS$UNTAG ; mass u n t a g MASS$UNTAG: XRA A ;set tag/untag.. STA T$UN$FG ;..flag to untag STA FSDFLG ;no file size display CALL WORKMSG MUTLOOP: LHLD RINGPOS ;move to tag LXI D,12 DAD D MOV A,M ;get tag CPI '*' ;check for tag MVI M,' ' ;clear tag CZ FSIZ ;adjust sizes LHLD RINGPOS ;advance to next LXI D,13 DAD D SHLD RINGPOS XCHG ;done? LHLD LOCEND CALL CMPDEHL JRNZ MUTLOOP LXI H,CURHOME ;reset cursor SHLD CURAT LHLD LOCBEG ;set ring position JMP JFW0 ; mass t a g MASS$TAG: XRA A STA FSDFLG ;no file size display MVI A,TRUE ;set tag/untag.. STA T$UN$FG ;..flag to untag CALL WORKMSG MTLOOP: LHLD RINGPOS ;move to tag LXI D,12 DAD D MOV A,M ;get tag CPI '*' ;check for tag MVI M,'*' ;clear tag CNZ FSIZ ;adjust sizes LHLD RINGPOS ;advance to next LXI D,13 DAD D SHLD RINGPOS XCHG ;done? LHLD LOCEND CALL CMPDEHL JRNZ MTLOOP LXI H,CURHOME ;reset cursor SHLD CURAT LHLD LOCBEG ;set ring position JMP JFW0 ; u n t a g UNTAG: XRA A ;set tag/untag.. STA T$UN$FG ;..flag to untag. CMA STA FS$FLG ;set flag to compute file size LHLD RINGPOS ;move back one.. LXI D,12 ;..character position.. DAD D ;..and check tagging status. MOV A,M ;if file previously tagged, remove.. CPI '*' ;..size from.. PUSH PSW ;save flag MVI M,' ' ; (untag character, to next ring position.) CALL REFFN ;refresh file name POP PSW ;get flag JRZ FS2 ;..summation. JMP FORWARD ; t a g TAG$EM: MVI A,TRUE ;set.. STA T$UN$FG ;..tag/untag and.. STA FS$FLG ;..file size flags to tag. LHLD RINGPOS LXI D,12 ;move back one.. DAD D ;..position.. MOV A,M ; (if file CPI '*' ; already tagged, skip JZ FORWARD ; to next file.) MVI M,'*' ;..and store a '*' tag character. CALL REFFN ;refresh file name JR FS2 ;get file size ; refresh file name with new tag REFFN: CALL CLRCUR ;clear cursor MVI A,' ' ;one more space CALL TYPE LHLD RINGPOS ;reprint file name INX H CALL PRFN MOV A,M ;print tag JMP TYPE ; f i l e s i z e ; determine and display file size in kilobytes -- round up to next disk ; allocation block -- accumulate tagged file summation FIL$SIZ: XRA A ;set file size/tagged.. STA FS$FLG ;..file flag to file size. CMA STA FSDFLG ;display file size CALL FSIZ ;compute and print file size JMP LOOPFN FS2: MVI A,TRUE STA FSDFLG ;display file size CALL FSIZ ;compute and print file size JMP FORWARD ; print file size FSIZ: LDA FSDFLG ;display file size? ORA A ;0=no CNZ FSNOTE CALL RINGFCB ;move name to 's$fcb' ; determine file record count and save in 'rcnt' MVI C,COMPSZ LXI D,S$FCB CALL BDOS LHLD S$FCB+33 SHLD RCNT ;save record count and.. LXI H,0 SHLD S$FCB+33 ;..reset cp/m. ; round up to next disk allocation block LDA B$MASK ;sectors/block - 1 PUSH PSW ;save 'blm' MOV L,A XCHG LHLD RCNT ;..use here. DAD D ;round up to next block MVI B,3+1 ;convert from.. CALL SHIFTLP ;..records to kilobytes. POP PSW ;retrieve 'blm' RRC ;convert.. RRC ;..to.. RRC ;..kilobytes/block. ANI 1FH CMA ;finish rounding ANA L MOV L,A ;hl-pair contains # of kilobytes LDA FS$FLG ORA A JRZ D$F$SIZ ;branch if 'f' function ; tagged file size summation XCHG ;file size to de-pair LDA T$UN$FG ORA A JRZ TAKE ;if untag, take size from total. LHLD TAG$TOT ;accumulate.. DAD D ;..sum of.. SHLD TAG$TOT ;..tagged file sizes. XCHG ;file size to hl-pair JR D$F$SIZ ;branch to display sizes TAKE: LHLD TAG$TOT ;subtract.. MOV A,L ;..file.. SUB E ;..size.. MOV L,A ;..from.. MOV A,H ;..summation.. SBB D ;..total. MOV H,A ;then put.. SHLD TAG$TOT ; (save total) XCHG ;..file size in hl-pair. ; display file size in kilobytes -- right justify tagged file total D$F$SIZ: LDA FSDFLG ;display file size? ORA A ;0=no RZ PUSH H ;save value CALL ATFS ;position for file size print LHLD RINGPOS ;print file name of current file INX H CALL PRFN MVI A,':' CALL TYPE POP H ;get value CALL DECOUT ;print individual file size MVI A,'K' CALL TYPE ; determine # of digits in tagged summation LHLD TAG$TOT ;get present summation CALL ILPRT DB ' Tagged:',0 CALL DECOUT ;print tagged file summation MVI A,'K' JMP TYPE ; j u m p ; backward JUMPB: LXI H,CURHOME ;set cursor home SHLD CURAT LHLD RING ;at front? XCHG LHLD LOCBEG CALL CMPDEHL JRZ JUMPBW ;back up and wrap around SHLD LOCEND ;set new end LXI D,-EPS*13 ;back up DAD D SHLD LOCBEG ;new beginning SHLD RINGPOS ;new position CALL REFRESH ;refresh screen JMP LOOPFN JUMPBW: LHLD LOCBEG ;at first screen? XCHG LHLD RING ;pt to first element of ring CALL CMPDEHL JRZ JBW0 ;advance to end LXI H,-EPS*13 ;back up DAD D JR JFW0 JBW0: LXI D,EPS*13 ;pt to next screen DAD D XCHG LHLD RINGEND CALL CMPDEHL XCHG JRZ JBW1 JRC JBW0 JBW1: LXI D,-EPS*13 DAD D ;pt to first element of local ring JR JFW0 JUMPF: LXI H,CURHOME ;set cursor to home SHLD CURAT LHLD LOCEND ;see if Local End <= Ring End XCHG LHLD RINGEND CALL CMPDEHL JRZ CMDLOOP LHLD LOCEND ;new screen JR JFW0 CMDLOOP: LXI H,CURHOME ;set cursor home SHLD CURAT LHLD RING ;set ring position JFW0: SHLD RINGPOS JFW0A: SHLD LOCBEG ;front of ring LXI D,EPS*13 ;new end? DAD D XCHG LHLD RINGEND ;end of ring XCHG CALL CMPDEHL JRC JFW1 XCHG JFW1: XCHG SHLD LOCEND CALL REFRESH JMP LOOPFN ; f o r w a r d FORWARD: CALL CLRCUR ;clear cursor CALL FOR0 ;position on screen and in ring CALL SETCUR ;set cursor JMP LOOPFN ; advance routine FOR0: LHLD RINGPOS ;at end of loop yet? LXI D,13 ;i.e., will we be at end of loop? DAD D XCHG LHLD LOCEND CALL CMPDEHL ;compare 'present' to 'end' JRNZ FORW ;to next print position CALL CUR$FIRST ;position cursor LHLD LOCBEG ;set position pointer to beginning and.. SHLD RINGPOS RET FORW: LHLD RINGPOS ;advance in ring LXI D,13 DAD D SHLD RINGPOS ;new position CALL CUR$NEXT ;position cursor RET ; r e v e r s e REVERSE: CALL CLRCUR ;clear cursor CALL REV0 ;position on screen and in ring CALL SETCUR ;set cursor JMP LOOPFN ; Back Up Routine REV0: LHLD LOCBEG XCHG LHLD RINGPOS ;see if at beginning of ring CALL CMPDEHL JRNZ REV1 ;skip position pointer reset if not.. CALL CUR$LAST ;end of local ring LHLD LOCEND ;set to end +1 to backup to end LXI D,-13 DAD D SHLD RINGPOS RET REV1: CALL CUR$BACK ;back up 1 REV2: LHLD RINGPOS LXI D,-13 ;one ring position.. DAD D ;..backwards. SHLD RINGPOS RET ; u p UP: CALL CLRCUR ;clear cursor LHLD RINGPOS ;see if wrap around LXI D,-13*4 ;4 entries DAD D XCHG LHLD LOCBEG ;beginning of local screen CALL CMPDEHL JRC UP2 ;wrap around MVI B,4 ;back up 4 entries UP1: PUSH B ;save count CALL REV0 ;back up in ring and on screen (no print) POP B ;get count DJNZ UP1 JR DOWN1A UP2: LHLD RINGPOS ;advance to beyond end LXI D,13*4 DAD D XCHG LHLD LOCEND ;compare to local end XCHG CALL CMPDEHL JRZ DOWN1A ;at end, so too far JRC DOWN1A ;beyond end, so back up SHLD RINGPOS ;new ring position LHLD CURAT ;advance cursor INR H ;next line SHLD CURAT JR UP2 ; d o w n DOWN: CALL CLRCUR ;clear cursor LHLD RINGPOS ;see if wrap around LXI D,13*4 ;4 entries DAD D XCHG LHLD LOCEND ;end of local screen XCHG CALL CMPDEHL JRZ DOWN2 ;wrap around JRC DOWN2 ;wrap around MVI B,4 ;forward 4 entries DOWN1: PUSH B ;save count CALL FOR0 ;advance in ring and on screen (no print) POP B ;get count DJNZ DOWN1 DOWN1A: CALL SETCUR ;set cursor JMP LOOPFN DOWN2: LHLD CURAT ;preserve column MOV B,L ;column number in B LXI H,CURHOME ;home position SHLD CURAT ;set new position LHLD LOCBEG ;beginning of local ring SHLD RINGPOS ;new ring position DOWN3: LHLD CURAT ;check for at top of column MOV A,L ;get col CMP B ;there? JRZ DOWN1A LHLD RINGPOS ;advance in ring LXI D,13 ;13 bytes/entry DAD D SHLD RINGPOS LHLD CURAT ;get cursor position LXI D,19 ;advance 19 bytes/screen entry DAD D SHLD CURAT JR DOWN3 ; s t a t ; determine remaining storage on requested disk R$DR$ST: CALL CPRMPT DB 'Status of Disk: ',0 CALL KEYIN ;get char PUSH PSW CALL CRLF POP PSW SUI 'A' ;convert to number JC NEUTRAL MOV B,A ;... in B LDA MAXDR ;compare to max SUI 'A' CMP B JC LOOPFN MOV A,B ;get disk STA R$DR ;requested drive CALL RESET ;..login as current. CALL FRESTOR ;determine free space remaining CALL PRINT$FRE ;print value LDA C$DR ;login original as.. CALL SET$DR ;..current drive. ; compute file count LXI H,0 ;set count SHLD LOCPOS ;dummy area LHLD RING ;pt to ring R$DR1: XCHG ;position in DE LHLD RINGEND CALL CMPDEHL ;at end of ring? JRZ R$DR2 LHLD LOCPOS ;increment count INX H SHLD LOCPOS LXI H,13 ;advance to next ring element DAD D ;HL pts to next JR R$DR1 R$DR2: LHLD LOCPOS ;get count CALL DECOUT ;print count CALL ILPRT DB ' Files in DIR',0 JMP LOOPFN ; d e l e t e ; mass delete MASS$DEL: CALL CPRMPT DB 'Mass Delete (Y/N/V=Verify Each)? ',0 CALL KEYIN ;get response CPI 'Y' JRZ MD1 CPI 'V' JNZ NEUTRAL ;return to position MD1: STA MDFLG ;set flag XRA A ;set for mass delete STA MFLAG LHLD RING SHLD RINGPOS ;set ring position MD$LP: LHLD RINGPOS ;get current position LXI D,12 ;pt to tag DAD D MOV A,M ;get tag CPI '*' JRNZ MD$LOOP CALL RINGFCB ;set up file name LDA MDFLG ;verify? CPI 'V' JRZ MDEL1 ;delete with verify JR DEL1 ;delete without verify MD$LOOP: LHLD RINGPOS ;re-entry point for next file mass-copy LXI D,13 ;advance to next DAD D SHLD RINGPOS MD1$LOOP: XCHG ;at ring.. LHLD RINGEND ;..end yet? CALL CMPDEHL ; (compare present position with end) JRNZ MD$LP ;no, loop 'till thru ring list. MD$EXIT: MVI A,TRUE ;set no STA MFLAG ;..mass-delete request. JMP CMDLOOP ;jump to 'ring' beginning ; set up to delete filename at cursor position DELETE: MVI A,TRUE ;set for no mass delete STA MFLAG STA MDFLG MDELETE: CALL RINGFCB ;move file name MDEL1: CALL CPRMPT DB 'Delete ',0 CALL PRFNS ;print file name in S$FCB CALL ILPRT DB ' (Y/N)? ',0 CALL KEYIN CPI 'Y' JRZ DEL1 LDA MFLAG ;mass delete? ORA A JRZ MD$LOOP MDEL2: LHLD LOCEND ;move in end LXI D,-13 DAD D SHLD LOCEND XCHG LHLD RINGPOS ;position beyond end of ring? CALL CMPDEHL JRNZ MDEL3 CALL CUR$BACK ;back up cursor LHLD LOCEND ;reset position LXI D,-13 DAD D SHLD RINGPOS LHLD LOCEND ;get end XCHG MDEL3: LHLD LOCBEG ;erased all local files? CALL CMPDEHL JZ CMDLOOP ;reset JMP JFW0A ;rescreen ; delete file DEL1: LDA MDFLG ;Y option on Mass Delete? CPI 'Y' JRNZ DEL1A CALL ERMSG DB 'Deleting File ',0 CALL PRFNS DEL1A: LXI H,S$FCB ;set file to R/W CALL ATTRIB JZ DEL1B ;abort LXI D,S$FCB ;point at delete 'fcb' MVI C,ERASE ;erase function CALL BDOS INR A JRZ FNF$MSG ;print error message CALL DEL2 ;close up erased position DEL1B: LDA MFLAG ;check for mass delete ORA A JRNZ MDEL2 LHLD RINGPOS ;no advance because of close up JMP MD1$LOOP FNF$MSG: CALL ERMSG ;show error message DB 'No File Found',0 JMP LOOPFN ; reverse ring to close up erased position DEL2: LHLD RINGPOS ;prepare move up pointers PUSH H LXI D,13 ;13 bytes/entry DAD D ;de-pair = 'to' location POP D ;hl-pair = 'from' location MOVUP: XCHG PUSH H ;check if at end LHLD RINGEND ;get old end pointer CALL CMPDEHL ;check against current end location POP H XCHG JRZ MOVDONE ;must be at end of ring MVI B,13 ;one name size CALL MOVE ;move one name up JR MOVUP ;go check end parameters MOVDONE: LHLD RING ;see if ring is empty XCHG SHLD RINGEND ;set new ring end if all moved CALL CMPDEHL ;..(listend --> listpos --> ring) RNZ LHLD RINGPOS CALL CMPDEHL RNZ ;neither equal so not empty LXI SP,STACK ;reset stack CALL ERMSG DB 'List Empty',0 JMP LOG ;go to drive/user area with files ; r e n a m e ; set-up to rename file at cursor position -- scan keyboard buffer and ; move filename to 'rename' destination 'fcb' (dfcb) RENAME: LHLD RINGPOS ;move name from ring to rename 'fcb' LXI D,D$FCB ;place to move name MVI B,12 ;amount to move CALL MOVE CALL CPRMPT ;new name prompt DB 'Rename File to: ',0 LXI D,D$FCB+16 ;pt to FCB to fill CALL FILENAME ;get file name LXI H,D$FCB+1 ;check for any wild cards -- none permitted MVI B,11 ;11 bytes WILDCHK: MOV A,M ;get char INX H ;pt to next CPI '?' ;wild? JRZ WILDFND DJNZ WILDCHK ; copy old file status bit ($r/o or $sys) to new filename CPYBITS: LXI D,D$FCB+1 ;first character of old name.. LXI H,D$FCB+17 ;..and of new name. MVI B,11 ; # of bytes with tag bits CBITS1: LDAX D ;fetch bit of old name character ANI 128 ;strip upper bit and.. MOV C,A ;..save in b-reg. MVI A,7FH ;mask for character only ANA M ;put masked character into a-reg ORA C ;add old bit MOV M,A ;copy new byte back INX H ;bump copy pointers INX D DJNZ CBITS1 ; check if new filename already exists. if so, say so. then go ; to command loop without moving ring position LDA D$FCB ;copy new name to source 'fcb' STA S$FCB MVI B,11 LXI H,D$FCB+17 ;copy new name to.. LXI D,S$FCB+1 ;..source 'fcb' for existence check. CALL MOVE LXI H,S$FCB+12 ;clear cp/m 'fcb' system.. CALL INITFCB ;..fields. LXI D,S$FCB ;search to see if this file exists MVI C,SRCHF ;search first function CALL BDOS INR A ; 0ffh --> 00h if file not found JRZ RENFILE ;to rename, if duplicate doesn't exists. CALL ERMSG ;announce the situation DB 'File Already Exists',0 JMP COMCAN ;try again? ; wild char found in file name -- error WILDFND: CALL ERMSG DB 'Ambiguous File Name NOT Allowed',0 JMP COMCAN ; copy new name into ring position RENFILE: LHLD RINGPOS ;get ring position pointer INX H ;pt to name PUSH H ;save ptr XCHG LXI H,D$FCB+17 ;point at new name and.. MVI B,11 CALL MOVE ;..move. LHLD CURAT ;get current position on screen LXI D,4 ;advance 4 chars DAD D CALL GOTOXY POP H ;get ptr CALL PRFN ;print file name MOV A,M ;print tag CALL TYPE LXI D,D$FCB ;rename 'fcb' location MVI C,REN ;rename function CALL BDOS INR A ; 0ffh --> 00h if rename error JNZ NEUTRAL ;if okay, proceed, else.. JMP FNF$MSG ;..show no-file msg. ; get file name from user and process into FCB pted to by DE FILENAME: PUSH D ;save ptr LXI D,CMDBUF ;command line location MVI C,RDBUF ;console read-buffer function CALL BDOS CALL CONVERT ;capitalize alpha POP H ;set to null drive MVI M,0 ;..required by 'bdos'. INX H ; initialize new filename field with spaces PUSH H ;save start pointer MVI B,11 ; # of spaces to 'blank' CALL FILL POP H XCHG LXI H,CMDBUF+1 ;put length.. MOV C,M ;..in c-reg. INX H XCHG ;de-pair --> buffer pointer and hl-pair.. CALL UNSPACE ;..--> 'fcb' pointer. remove leading spaces. ; extend buffer to spaces beyond command length EXTEND: PUSH H MOV L,C ;double-byte remaining length MVI H,0 DAD D ;to buffer end +1 MVI M,' ' ;force illegal character end POP H ; start filename scan SCAN: MVI B,8 ; 8 characters in filename SCAN1: CALL CKLEGAL ;get and see if legal character JC COMCAN ;all of command line? CPI ' ' ;see if end of parameter field RZ ;rename file CPI '.' ;at end of filename JRZ SCAN2 ;process filetype field CPI '*' ;rest wild? JRZ SCAN1B MOV M,A ;put character into destination 'fcb' INX H DJNZ SCAN1 ; entry if eight characters without a 'period' SCAN1A: CALL CKLEGAL ;scan buffer up to period or end RC ;no extent if not legal CPI ' ' ;end of parameter field? RZ CPI '.' JRNZ SCAN1A ;do till end or period JR SCAN2A ;continue at correct place ; make rest of entry wild SCAN1B: MVI M,'?' ;fill with ?'s INX H DJNZ SCAN1B LDAX D ;get next char INX D ;pt to after dot CPI '.' ;must be dot JNZ COMCAN ;cancel if not JR SCAN2A ; build filetype field SCAN2: INX H ;advance ptr to file type field DJNZ SCAN2 SCAN2A: MVI B,3 ;length of filetype field SCAN3: CALL CKLEGAL ;get and check character JRC SCAN4 ;name done if illegal CPI ' ' ;end of parameter field? JRZ SCAN4 CPI '.' ;check if another period JRZ SCAN4 CPI '*' ;rest wild? JRZ SCAN4B MOV M,A INX H DJNZ SCAN3 ;get next character JR SCAN4A SCAN4: INX H ;advance to end of type field DJNZ SCAN4 SCAN4A: CALL INITFCB ;..and zero counter fields. RET SCAN4B: MVI M,'?' ;make wild INX H DJNZ SCAN4B JR SCAN4A ;complete rest ; goto file GOTO: CALL CPRMPT DB 'Goto Filename: ',0 LXI D,D$FCB ;pt to FCB CALL FILENAME ;get file name LHLD RING ;pt to first element of ring SHLD RINGPOS ;set position SHLD LOCBEG ;set local beginning XRA A ;set local counter STA CRCTBL ;use this buffer GOTOL: CALL GOTOCOMP ;compare JRZ GOTOF ;we are there LDA CRCTBL ;increment count INR A STA CRCTBL CPI EPS JRNZ GOTOL1 XRA A ;reset count STA CRCTBL LHLD LOCBEG ;reset local beginning LXI D,EPS*13 DAD D SHLD LOCBEG GOTOL1: LHLD RINGPOS ;advance to next entry LXI D,13 DAD D SHLD RINGPOS ;new position XCHG ;position in DE LHLD RINGEND ;check for completion CALL CMPDEHL ;compare current position with end of ring JRNZ GOTOL LHLD RING ;pt to first element SHLD RINGPOS ;set position CALL ERMSG DB 'File NOT Found',0 JMP CMDLOOP GOTOF: LHLD LOCBEG ;we have local beginning PUSH H XCHG ;ring location in DE LXI H,CURHOME ;set cursor ptr SHLD CURAT GOTOF0: LHLD RINGPOS ;at position? CALL CMPDEHL JRZ GOTOF1 LXI H,13 ;advance location DAD D PUSH H CALL CUR$NEXT ;advance cursor POP D ;pt to next ring position JR GOTOF0 GOTOF1: POP H ;pt to local ring JMP JFW0A ;process GOTOCOMP: LHLD RINGPOS ;pt to current entry INX H ;pt to first char of file name LXI D,D$FCB+1 ;pt to first char of new file MVI B,11 ;11 bytes GOTOC1: LDAX D ;get char CPI '?' ;match? JRZ GOTOC2 CMP M ;match? RNZ ;no match GOTOC2: INX D ;pt to next INX H DJNZ GOTOC1 RET ; v i e w ; type file to console with pagination set to 'lps' -- single-line scroll ; using bar , to cancel, any other key to page screen. VIEW: CALL CLS CALL ILPRT DB CR,LF,' Cancels, Turns Up One Line, ' DB 'Other Keys Page Screen',CR,LF,LF,0 MVI A,1 ;initialize.. STA LPSCNT ;..lines-per-screen counter. STA VIEWFLG ; 'view' paginate if not zero MVI A,WRCON ;write console out function JR CURRENT ;to common i/o processing ; p r i n t e r ; send file to logical list device -- any keypress cancels LSTFILE: CALL CPRMPT DB 'Print on LST Device (Y/N)? ',0 CALL KEYIN ;get response CPI 'Y' JNZ NEUTRAL CALL ERMSG DB 'Printing ',0 LHLD RINGPOS ;pt to file name INX H CALL PRFN ;print it MVI A,1 ;one for.. STA VIEWFLG ;..output to printer. DCR A ;zero for.. STA LPSCNT ;..lines-per-page counter MVI A,LIST ;out to 'list' device function and fall thru ; output character for console/list/punch processing CURRENT: STA CON$LST ;save bdos function ; output file to console/printer/punch CALL RINGFCB ;position name to 'fcb' XCHG ;HL pts to S$FCB CALL INITFCB ;set 'fcb' for use LXI D,TBUF ;set to use default cp/m dma buffer MVI C,SETDMA ;address set function CALL BDOS LXI D,S$FCB ;open file for reading MVI C,OPEN ;file open function code CALL BDOS INR A ; 0ffh --> 00h if open not okay JRNZ ZEROCR ;if not okay, show error message. CALL ERMSG DB 'Unable to Open File',0 JMP NEUTRAL ZEROCR: XRA A STA S$FCB+32 ;zero file 'current record' field STA CHARCNT ;zero char count for tabbing CALL PHEAD ;print heading if output to LST device READMR: LXI D,S$FCB ;point at file 'fcb' for reading MVI C,READ ;record read function CALL BDOS ORA A ;check if read okay JRNZ CURDONE ;eof? LXI H,TBUF ;point at record just read MVI B,128 ;set record character counter to output READLP: MOV A,M ;get a character ANI 7FH ;force to 'ascii' CPI EOFCHAR ;see if end-of-file JRZ CURDONE ;back to ring loop if 'eof' MOV E,A ;put character for 'bdos' call PUSH B PUSH H PUSH D ; (character in e-reg) LDA CON$LST ;get function for punch/list/console output MOV C,A MOV A,E ;check char CPI TAB ;tabulate? JRNZ NOTAB MVI E,' ' ;space over TABL: PUSH B ;save key regs PUSH D CALL BDOS POP D ;get key regs POP B CALL INCCCNT ;increment char count ANI 7 ;check for done at every 8 JRNZ TABL JR TABDN NOTAB: CALL BDOS ;send character CALL INCCCNT ;increment char count TABDN: LDA VIEWFLG ;if 'view'.. ORA A POP D ;get char in E in case PAGER is called CNZ PAGER ;..check for 'lf'. MVI E,GET ;get status or char MVI C,DIRCON ;console status function CALL BDOS ;status? POP H POP B ANI 7FH ;if character there, then abort.. CNZ CANVIEW ;already got char INX H ;if not, bump buffer pointer. DJNZ READLP ;no, more in present record. JR READMR ;yes, get next record. CURDONE: LDA CON$LST ;console? CPI WRCON CZ BOTTOM ;prompt for user CALL REFRESH ;refresh screen JMP LOOPFN PAGER: MOV A,E ; (character in e-reg) CPI LF RNZ XRA A ;zero char count STA CHARCNT LDA CON$LST ;printer or console? CPI LIST ;check for printer JRZ PAGEP LDA CTPP ;get number of lines of text per screen MOV B,A ;... in B LDA LPSCNT ;is counter.. INR A ;..at.. STA LPSCNT ;..limit.. CMP B ;..of lines-per-screen? RC ;no, return. XRA A ;yes, initialize.. STA LPSCNT ;..for next screen full. CALL ILPRT DB ' [View More...]',CR,0 ;show msg line CALL DKEYIN ;wait for keyboard input CPI ' ' ;see if bar.. PUSH PSW CALL ILPRT DB ' ',CR,0 ;clear above msg line POP PSW JRNZ CANVIEW ;..if not, see if cancel. LDA CTPP ;set for single line DCR A STA LPSCNT ;..scroll and.. RET ;..return for one more line. PAGEP: LDA LTPP ;get number of lines of text per page MOV B,A ;... in B LDA LPSCNT ;is counter.. INR A ;..at.. STA LPSCNT ;..limit.. CMP B ;..of lines-per-screen? RC ;no, return. XRA A ;zero for.. STA LPSCNT ;..lines-per-page counter LDA LSPP ;number of lines to skip MOV B,A ;number of lines to skip MVI C,LIST ;LST output PAGELST: CALL LCRLF ;new line on LST DJNZ PAGELST CALL PHEAD ;print heading RET ;done! CANVIEW: CPI CTRLC ;^C? JZ COMCAN RET ;return for another page INCCCNT: LDA CHARCNT ;increment char count INR A STA CHARCNT RET PHEAD: LDA CON$LST ;printing to printer? CPI LIST RNZ LXI H,HEADMSG ;print heading PHEAD1: MOV A,M ;get char ORA A ;done? JRZ PHEAD2 CALL LOUT ;send to printer INX H ;pt to next JR PHEAD1 PHEAD2: LXI H,S$FCB+1 ;pt to file name MVI B,8 ;8 chars CALL PHEAD3 MVI A,'.' ;dot CALL LOUT MVI B,3 ;3 more chars CALL PHEAD3 CALL LCRLF ;new line CALL LCRLF ;blank line RET PHEAD3: MOV A,M ;get char CALL LOUT ;LST it INX H ;pt to next DJNZ PHEAD3 RET ; m a s s c o p y ; copy files tagged using the 't' command. auto-erase if file exists ; on requested destination drive or in user area. MASS$COPY: CALL ERMSG DB 'Mass Copy',0 LHLD RINGPOS ;save position SHLD SRINGPOS LHLD RING SHLD RINGPOS ;set position MASS$LP: LHLD RINGPOS ;get position LXI D,12 ;get 1st possible tag location DAD D MOV A,M ;get tag CPI '*' JRZ MCOPY ;copy filename with tag character (*) M$LP: LHLD RINGPOS ;re-entry point for next file mass-copy LXI D,13 ;advance to next DAD D SHLD RINGPOS XCHG ;at ring.. LHLD RINGEND ;..end yet? CALL CMPDEHL ; (compare present position with end) JRNZ MASS$LP ;loop 'till thru ring list. MF$EXIT: XRA A ;reset flags.. STA FIRST$M ;..for.. CMA ;..next.. STA MFLAG ;..mass-copy request. LHLD SRINGPOS ;reset ring position SHLD RINGPOS LHLD LOCBEG ;local ring JMP JFW0A ;rescreen ; c o p y ; copy source file at current 'ring' position to another drive. set-up ; fcb's and buffer area and check for correct keyboard inputs. contains ; auto-crc file copy verification. MCOPY: XRA A ;zero flag to.. STA MFLAG ;..mass copy. COPY: LXI H,0 ;initialize storage for.. SHLD CRCVAL ;..'crc' working value. CALL RINGFCB ;move from 'ring' to 'sfcb' LXI H,S$FCB+12 ;set pointer to source extent field CALL INITFCB MVI B,32 ;copy source 'fcb' to destination 'fcb' LXI H,S$FCB+1 ;from point.. LXI D,D$FCB+1 ;..to point.. CALL MOVE ;..move across. LXI D,S$FCB ;open file for reading MVI C,OPEN ;open function CALL BDOS INR A ; 0ffh --> 00h if bad open JRNZ COPY2 ;if okay, skip error message. CALL ERMSG DB 'No Source',0 JMP NEUTRAL COPY2: LDA FIRST$M ;by-pass prompt, drive/user compatibility.. ORA A ;..test, and disk reset after.. JRNZ COPY3M ;..1st time thru in mass-copy mode. CALL CPRMPT ;prompt for drive selection DB 'Copy to DIR: ',0 CALL DEF$D$U ; either drives or user areas must be different LDA DOK ;OK to change drive? ORA A JRNZ CDOK LDA S$FCB ;make source and destination the same STA FCB CDOK: LDA UOK ;OK to change user? ORA A JRNZ CUOK LDA C$U$A ;make current user and requested the same STA R$U$A CUOK: LDA FCB ;get requested drive from 'fcb' and.. MOV B,A ;..put into b-reg for.. LDA S$FCB ;..comparison CMP B JRNZ COPY3 ;branch if different LDA R$U$A ;requested user area --> rua MOV B,A LDA C$U$A ;current user area --> cua CMP B JRNZ COPY3 CALL ERMSG ;if not, show error condition: DB 'DU must be different',0 JMP NEUTRAL ;try again? COPY3: CALL RESET ;make sure disk is read/write CALL DLOGIN ;return home COPY3M: LDA FCB ;put requested drive into.. STA D$FCB ;..place in destination fcb (used to log in) LDA R$U$A ;toggle to.. CALL SET$USR ;..requested user area. LXI D,D$FCB ;search for duplicate MVI C,SRCHF ; 'search first' function CALL BDOS INR A ;if not found, 0ffh --> 00h. then.. JRZ COPY5 ;go to 'make' function for new file. LDA MFLAG ;auto-erase.. ORA A ;..if.. JRZ COPY4M ;..in mass-copy mode. CALL ERMSG ;CPR2 - if found, ask to replace: DB 0 LXI H,D$FCB+1 CALL PRFN CALL ILPRT DB ' Exists on Dest -- Erase (Y/N)? ',0 CALL KEYIN ;get answer CPI 'Y' ;if yes, then.. JRZ COPY4M ;..delete and overlay. CALL CUA$LOG ;current user area JMP FORWARD ;if re-copy not wanted, to next position. CUA$LOG: LDA C$U$A ;reset to current user area JMP SET$USR ; Erase destination file and proceed COPY4M: LXI H,D$FCB ;pt to FCB CALL ATTRIB ;clear bytes in FCB and set attr of file JZ MCL$TEST ;continue LXI D,D$FCB ;delete file already existing MVI C,ERASE ;erase function CALL BDOS COPY5: LXI D,D$FCB ;create new file and open for writing MVI C,MAKE ;make function CALL BDOS INR A ;if directory full, 0ffh --> 00h. JRNZ COPY6 ;if not, branch. CALL ERMSG DB 'Destination Dir Full',0 JMP LOOPFN ;if error, back to ring processor. COPY6: CALL CPRMPT DB 'Copying File ',0 LXI H,D$FCB+1 ;print file name CALL PRFNSX XRA A ;clear 'eof'.. STA EOFLAG ;..flag. COPY6A: CALL CUA$LOG ;current user area LXI H,0 ;clear current-record.. SHLD REC$CNT ;..counter. LHLD BUFSTART ;set buffer start pointer.. SHLD BUF$PT ;..to begin pointer. ; read source file -- fill buffer memory or stop on 'eof' -- update 'crc' ; on-the-fly COPY7: LHLD BUF$PT ;set dma address to buffer pointer XCHG ; de-pair --> dma address MVI C,SETDMA CALL BDOS LXI D,S$FCB ;source 'fcb' for reading MVI C,READ ;record read function CALL BDOS ORA A ; 00h --> read okay JRZ S$RD$OK DCR A ;eof? JRZ COPY8 ;yes, end-of-file, set 'eof' flag. CALL ERMSG DB 'Read Error',0 JMP LOOPFN S$RD$OK: LHLD BUF$PT MVI B,128 COPY7A: MOV A,M ;get character and.. CALL UPDCRC ;..add to 'crc' value. INX H DCR B JRNZ COPY7A ;loop 'till record read finished LHLD BUF$PT ;bump buffer pointer.. LXI D,128 ;..by.. DAD D ;..one.. SHLD BUF$PT ;..record. LHLD REC$CNT ;bump buffer.. INX H ;..record count and.. SHLD REC$CNT ;..store. XCHG ;ready to compare to.. LHLD REC$MAX ;..maximum record count (full-buffer). CALL CMPDEHL ;compare JRNZ COPY7 ;if not full, get next record. JR COPY9 ;full, start first write session. ; indicate end-of-file read COPY8: MVI A,TRUE ;set 'eof' flag STA EOFLAG ; write 'read-file' from memory buffer to destination 'written-file' COPY9: LDA R$U$A ;set user to requested.. CALL SET$USR ;..area. LHLD BUFSTART ;adjust buffer pointer.. SHLD BUF$PT ;..to start address. COPY10: LHLD REC$CNT ;buffer empty? MOV A,H ORA L JRZ COPY11 ;buffer empty, check 'eof' flag. DCX H ;dec buffer record count for each write SHLD REC$CNT LHLD BUF$PT ;set up dma address PUSH H ;save for size bump XCHG ;pointer in de-pair MVI C,SETDMA CALL BDOS POP H LXI D,128 ;bump pointer one record length DAD D SHLD BUF$PT LXI D,D$FCB ;destination file 'fcb' MVI C,WRITE ;write record function CALL BDOS ORA A ; 00h --> write okay JRZ COPY10 ;okay, do next record. else.. CALL ERMSG ;..say disk write error. DB 'Copy Disk Full',0 C$ERA: LXI D,D$FCB ;delete.. MVI C,ERASE ;..partial.. CALL BDOS ;..from directory. XRA A ;reset 1st-time-thru tag flag.. STA FIRST$M ;..for continuation of mass copying. JMP LOOPFN ;back to ring COPY11: LDA EOFLAG ;buffer all written, check for 'eof'. ORA A JZ COPY6A ;branch to read next buffer full LXI D,D$FCB ;point at 'fcb' for file closure MVI C,CLOSE CALL BDOS INR A ;if no-close-error then.. JRNZ CRC$CMP ;..compare file crc's. CALL ERMSG DB 'Copy Close Error',0 JMP C$ERA ; read destination 'written-file' and compare crc's CRC$CMP: LHLD CRCVAL ;transfer 'crc' value to.. SHLD CRCVAL2 ;..new storage area. LXI H,0 ;clear working storage.. SHLD CRCVAL ;..to continue. LXI D,TBUF MVI C,SETDMA CALL BDOS LXI H,D$FCB+12 CALL INITFCB LXI D,D$FCB MVI C,OPEN CALL BDOS INR A ; 0ffh --> 00h if bad open JZ BADCRC ;if bad open, just say 'bad-crc'. XRA A ;zero 'fcb'.. STA D$FCB+32 ;..'cr' field. CRCWF1: LXI D,D$FCB MVI C,READ CALL BDOS ORA A ;read okay? JRZ D$RD$OK ;yes, read more. DCR A ;eof? JZ FINCRC ;yes, finish up and make 'crc' comparison. CALL ERMSG DB 'Copy Read Error',0 JMP NEUTRAL D$RD$OK: LXI H,TBUF MVI B,128 CRCWF2: MOV A,M ;get character to.. CALL UPDCRC ;..add to 'crc' value. INX H DJNZ CRCWF2 JR CRCWF1 ; clear attributes of file (HL) and set attributes on disk ; return code of 0FFH (NZ) indicates OK to proceed, 0 (Z) indicates abort ATTRIB: PUSH H ;save regs PUSH H LXI H,DUM$FCB+12 ;init FCB CALL INITFCB POP H LXI D,DUM$FCB ;copy to dummy FCB MVI B,12 ;copy PUSH D CALL MOVE POP D ;pt to FCB MVI C,SRCHF ;look for file CALL BDOS INR A ;file not found = 0 POP H RZ ;abort if no file PUSH H ;save ptr DCR A ;adjust RRC ;right 3 bits to indicate offset into BUFF RRC RRC LXI H,TBUF ;pt to buffer ADD L ;pt to FCB of file ADI 9 ;pt to R/O Byte MOV L,A MOV A,M ;get byte ANI 80H ;extract R/O Bit JRZ ATTRIB0 CALL ERMSG DB 0 POP H PUSH H INX H ;pt to file name CALL PRFN ;print file name CALL ILPRT DB ' is R/O -- Erase (Y/N)? ',0 CALL KEYIN CPI 'Y' JZ ATTRIB0 POP H XRA A ;error return RET ATTRIB0: POP H ;get ptr PUSH H ;save ptr INX H ;pt to first char MVI B,11 ;11 Bytes ATTRIB1: MOV A,M ;get byte ANI 7FH ;mask it MOV M,A ;put byte INX H ;pt to next DCR B ;count down JNZ ATTRIB1 POP D ;pt to FCB MVI C,ATTR CALL BDOS MVI A,0FFH ;no error return ORA A RET ; crc subroutines ; initialize tables for fast crc calculations INITCRC: LXI H,CRCTBL MVI C,0 ;table index GLOOP: XCHG LXI H,0 ;initialize crc register pair MOV A,C PUSH B ;save index in c-reg MVI B,8 XRA H MOV H,A LLOOP: DAD H JRNC LSKIP MVI A,10H ;generator is x^16 + x^12 + x^5 + x^0 as.. XRA H ;..recommended by ccitt for asynchronous.. MOV H,A ;..communications. produces the same.. MVI A,21H ;..results as public domain programs.. XRA L ;..chek, comm7, mdm7, and modem7. MOV L,A LSKIP: DJNZ LLOOP POP B XCHG ;de-pair now has crc, hl pointing into table. MOV M,D ;store high byte of crc.. INR H MOV M,E ;..and store low byte. DCR H INX H ;move to next table entry INR C ;next index JRNZ GLOOP RET UPDCRC: PUSH B ;update 'crc'.. PUSH H ;..accumulator.. LHLD CRCVAL ;pick up partial remainder XCHG ;de-pair now has partial MVI B,0 XRA D MOV C,A LXI H,CRCTBL DAD B MOV A,M XRA E MOV D,A INR H MOV E,M XCHG SHLD CRCVAL POP H POP B RET FINCRC: LHLD CRCVAL ;put written-file 'crc' into.. XCHG ;..de-pair. LHLD CRCVAL2 ;put read-file 'crc' and.. CALL CMPDEHL ;..compare 'de/hl' for equality. JRNZ BADCRC ;if not zero, show copy-error message. CALL ILPRT ;if zero, show 'verified' message. DB ' -- CRC Verified',0 MCL$TEST: CALL CUA$LOG ;return to current user LDA MFLAG ;if not mass-copy mode, return.. ORA A ;..to next 'ring' position. JNZ FORWARD ;else.. CMA ;..set 1st-time-thru flag.. STA FIRST$M ;..and.. JMP M$LP ;..get next file to copy, if one. BADCRC: CALL CUA$LOG ;return to current user CALL ERMSG DB 'Error on CRC compare',0 JMP FORWARD ;move to next 'ring' position ; w o r k h o r s e r o u t i n e s ; inline print of message ILPRT: XTHL ;save hl, get msg pointer. ILPLP: MOV A,M ;get character INX H ;pt to next ANI 7FH ;strip type bits JRZ ILPLP1 CALL TYPE ;show on console JR ILPLP ILPLP1: XTHL ;set hl-pair and.. RET ;..return past message. ; output 'crlf' to console CRLF: MVI A,CR CALL TYPE MVI A,LF ; conout routine TYPE: PUSH PSW PUSH B PUSH D PUSH H PUSH PSW ; check for flow control CALL CST ; BIOS console status ORA A ; 0 means nothing JRZ TYPE1 CALL CIN ; BIOS console input CPI CTRLS ; pause? JRNZ TYPE1 CALL CIN ; BIOS console input TYPE1: POP PSW ; get char MOV E,A MVI C,WRCON CALL BDOS POP H POP D POP B POP PSW RET ; direct BIOS I/O CST: LHLD CPM$BASE+1 ; get BIOS Base Address MVI L,6 ; console status routine PCHL ; jump to it CIN: LHLD CPM$BASE+1 ; get BIOS Base Address MVI L,9 ; console input routine PCHL ; output 'crlf' to printer LCRLF: MVI A,CR CALL LOUT MVI A,LF ; printer routine LOUT: PUSH PSW PUSH B PUSH D PUSH H MOV E,A MVI C,LIST CALL BDOS POP H POP D POP B POP PSW RET ; crt clear-line function CLR$L: MVI A,CR CALL TYPE MVI B,30 ;blank # of characters on line MVI A,' ' CL$LP: CALL TYPE DJNZ CL$LP RET ; conin routine (waits for response) KEYIN: MVI C,RDCON CALL BDOS ; convert character in a-reg to upper case UCASE: CPI 61H ;less than small 'a'? RC ;if so, no convert needed. CPI 7AH+1 ; >small 'z'? RNC ;if so, ignore. ANI 5FH ;otherwise convert RET ; direct console input w/o echo (waits for input) DKEYIN: CALL CIN ;get char from BIOS ANI 7FH ;mask MSB JR UCASE ;capitalize ; convert keyboard input to upper case CONVERT: LXI H,CMDBUF+1 ; 'current keyboard buffer length'.. MOV B,M ;..to b-reg. MOV A,B ORA A ;if zero length, skip conversion. JZ COMCAN CONVLP: INX H ;point at character to capitalize MOV A,M CALL UCASE MOV M,A ;put back into buffer DJNZ CONVLP RET ; fill buffer with 'spaces' with count in b-reg FILL: MVI M,' ' ;put in space character INX H DJNZ FILL ;no, branch. RET ; ignore leading spaces (ls) in buffer, length in c-reg. UNSPACE: LDAX D ;get character CPI ' ' RNZ ;not blank, a file is entered. INX D ;to next character DCR C JZ COMCAN ;all spaces --> command recovery error JR UNSPACE ; check for legal cp/m filename character -- return with carry set if illegal CKLEGAL: LDAX D ;get character from de-pair INX D ;point at next character CPI ' ' ;less than space? RC ;return carry if unpermitted character PUSH H PUSH B CPI '[' ;if greater than 'z', exit with.. JRNC CKERR ;..carry set. MVI B,CHR$TEND-CHR$TBL LXI H,CHR$TBL CHR$LP: CMP M JRZ CKERR INX H DJNZ CHR$LP ORA A ;clear carry for good character POP B POP H RET CKERR: POP B POP H STC ;error exit with carry set RET CHR$TBL: DB ',',':',';','<','=','>' ;invalid character table CHR$TEND: DS 0 ; print file name in S$FCB PRFNSX: PUSH H ;save regs PUSH B JR PRFNS0 PRFNS: PUSH H ;affect only PSW PUSH B LXI H,S$FCB+1 PRFNS0: CALL PRFN ;print file name POP B ;restore POP H RET ; print file name pted to by HL PRFN: MVI B,8 ;8 chars CALL PRFNS1 MVI A,'.' CALL TYPE MVI B,3 ;file type and fall thru PRFNS1: MOV A,M ;get char CALL TYPE INX H ;pt to next DJNZ PRFNS1 RET ; filename from 'ring' to 'sfcb' RINGFCB: LHLD RINGPOS ;move name from ring to source 'fcb' LXI D,S$FCB ;place to move filename and.. MVI B,12 ;..amount to move. ; move subroutine -- move b-reg # of bytes from hl-pair to de-pair MOVE: MOV A,M ;get hl-pair referenced source byte ANI 7FH ;strip cp/m 2.x attributes STAX D ;put to de-pair referenced destination INX H ;fix pointers for next search INX D DJNZ MOVE RET ; initialize 'fcb' cp/m system fields (entry with hl-pair pointing to 'fcb') INITFCB: MVI B,21 ;fill ex, s1, s2, rc, cr counters with zeros. INITLP: MVI M,0 ;put zero (null) in memory INX H DJNZ INITLP RET ; disk system reset -- login requested drive RESET: MVI C,RESETDK ;reset system CALL BDOS LDA R$DR ;make requested drive.. SET$DR: MOV E,A ;..current MVI C,LOGIN JMP BDOS ;return to caller ; set/reset (or get) user area (call with binary user area in a-reg) SET$USR: MOV E,A ;user number in E GET$USR: MVI C,SGUSER JMP BDOS ;return to caller ; compare de-pair to hl-pair and set flags accordingly CMPDEHL: MOV A,D ;see if high bytes set flags CMP H RNZ ;return if not equal MOV A,E CMP L ;low bytes set flags instead RET ; shift hl-pair b-reg bits (-1) to right (divider routine) SHIFTLP: DCR B RZ MOV A,H ORA A RAR MOV H,A MOV A,L RAR MOV L,A JR SHIFTLP ; decimal pretty print (h-reg contains msb; l-reg, the lsb.) DECOUT: MVI A,5 ;set leading space count STA LDSP DECOU1: PUSH PSW PUSH B PUSH D PUSH H LDA LDSP ;count down DCR A STA LDSP LXI B,-10 ;radix LXI D,-1 DECOU2: DAD B ;sets.. INX D JRC DECOU2 ;..carry. LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DECOU1 ; (recursive) LDA LDSP ; any spaces? ORA A ; 0=none JRZ DECOU4 MOV B,A ; count in B MVI A,' ' DECOU3: CALL TYPE DJNZ DECOU3 XRA A ;A=0 STA LDSP ;set flag DECOU4: MOV A,E ADI '0' ;make ascii CALL TYPE POP H POP D POP B POP PSW RET ; determine free storage remaining on selected drive FRESTOR: MVI C,INQDISK ;determine current drive CALL BDOS ;returns 0 as a:, 1 as b:, etc. INR A ;make 1 --> a:, 2 --> b:, etc. STA FCB MVI C,GETPARM ;current disk parameter block CALL BDOS INX H ;bump to.. INX H MOV A,M ;..block shift factor. STA BSHIFTF ; 'bsh' INX H ;bump to.. MOV A,M ;..block mask. STA B$MASK ; 'blm' INX H ;bump to.. INX H ;..get.. MOV E,M ;..maximum block number.. INX H ;..double.. MOV D,M ;..byte. XCHG SHLD B$MAX ; 'dsm' MVI C,INQALC ;address of cp/m allocation vector CALL BDOS XCHG ;get its length LHLD B$MAX INX H LXI B,0 ;initialize block count to zero GSPBYT: PUSH D ;save allocation address LDAX D MVI E,8 ;set to process 8 bits (blocks) GSPLUP: RAL ;test bit JRC NOT$FRE INX B NOT$FRE: MOV D,A ;save bits DCX H MOV A,L ORA H JRZ END$ALC ;quit if out of blocks MOV A,D ;restore bits DCR E ;count down 8 bits JRNZ GSPLUP ;branch to do another bit POP D ;bump to next count.. INX D ;..of allocation vector. JR GSPBYT ;process it END$ALC: POP D ;clear alloc vector pointer from stack MOV L,C ;copy # blocks to hl-pair MOV H,B LDA BSHIFTF ;get block shift factor SUI 3 ;convert from sectors to thousands (k) JRZ PRT$FRE ;skip shifts if 1k blocks FREK$LP: DAD H ;multiply blocks by k-bytes per block DCR A ;multiply by 2, 4, 8, or 16. JRNZ FREK$LP PRT$FRE: SHLD DISKSP ;save disk space RET ; ; Print free space on disk ; PRINT$FRE: CALL ERMSG ;position and set flags DB 0 LHLD DISKSP CALL DECOUT ; # of free k-bytes in hl-pair CALL ILPRT DB 'K Bytes on Disk',0 RET ;* ;* ZDNAME -- LOAD THE CONTENTS OF THE NAMES.DIR FILE INTO THE MEMORY ;* BUFFER PTED TO BY HL ;* ON ENTRY, HL PTS TO THE MEMORY BUFFER EXTENDING TO THE BASE OF ;* THE BDOS ;* ON EXIT, BC IS THE NUMBER OF VALID ENTRIES, A IS THE ERROR FLAG (A=0FFH ;* AND NZ IF NO ERROR, A=0 AND Z IF ERROR) ;* ERRORS MAY BE EITHER MEMORY OVERFLOW OR NAMES.DIR ;* NOT FOUND ;* EACH NAMES.DIR ENTRY IS 10 BYTES LONG, STRUCTURED AS FOLLOWS: ;* BYTE 0: DISK NUMBER (A=0) ;* BYTE 1: USER NUMBER ;* BYTES 2-9: DIRECTORY NAME, 8 CHARS MAX, FILL AT END ;* ZDNAME: PUSH D ; SAVE UNCHANGED REG SHLD DIRNAME ; SAVE PTR TO BUFFER LXI H,DNFILE ; PT TO NAMES.DIR FILE NAME LXI D,S$FCB+1 ; PT TO FCB MVI B,11 ; 11 BYTES CALL MOVE XCHG ; HL PTS TO S$FCB+12 CALL INITFCB ; INIT FCB LXI H,S$FCB ; PT TO FCB MVI M,0 ; ZERO DRIVE XCHG ; DE PTS TO FCB CALL FFIND ; LOOK FOR NAMES.DIR FILE JZ DIRNERR ; FILE NOT FOUND ERROR ; ; FOUND NAMES.DIR, SO LOAD IT ; LDA Z$U$A ; GET USER MOV C,A LDA Z$DR ; GET DISK MOV B,A CALL SLOGIN ; LOG INTO IT LXI H,S$FCB+12 CALL INITFCB LXI D,S$FCB ; PT TO FCB XRA A STAX D ; ZERO FCB DISK STA S$FCB+32 ; ZERO CURRENT RECORD CALL FIOPEN ; OPEN FOR INPUT ORA A ; ERROR? JRNZ ZDNA3 ; ; LOAD NAMES.DIR FILE ; MVI C,0 ; SET ENTRY COUNT LDA NDNAMES ; GET MAX NUMBER OF NAMES MOV B,A ; ... IN B ZDNA1: LXI H,ENTRY ; PT TO ENTRY BUFFER CALL GETNAME ; GET NAME FROM DISK JRNZ ZDNA3 ; DONE? LDA ENTRY+2 ; LOOK AT FIRST LETTER OF DIR NAME ORA A ; NO ENTRY? JZ ZDNA2 LHLD DIRNAME ; PT TO BUFFER ENTRY LXI D,ENTRY ; PT TO NEW ENTRY INR C ; INCREMENT ENTRY COUNTER PUSH B ; SAVE COUNTERS XCHG ; HL PTS TO NEW ENTRY, DE PTS TO DEST MOV A,M ; GET DISK NUMBER STAX D ; STORE DISK NUMBER INX H ; PT TO USER NUMBER INX D MOV A,M ; GET USER STAX D ; PUT USER MVI B,8 ; AT MOST 8 MORE BYTES ZDNA1A: INX H ; PT TO NEXT BYTE INX D MOV A,M ; GET NEXT BYTE ORA A ; END OF NAME? JRZ ZDNA1B ; FILL STAX D ; PUT BYTE DJNZ ZDNA1A INX D ; PT TO FIRST BYTE OF NEXT ENTRY JR ZDNA1C ZDNA1B: MVI A,' ' ; FILL STAX D ; PLACE INX D ; PT TO NEXT DJNZ ZDNA1B ZDNA1C: POP B ; RESTORE COUNTERS XCHG ; HL PTS TO NEXT BUFFER POSITION SHLD DIRNAME ; SAVE PTR ; ; CONTINUE LOOPING ; ZDNA2: DJNZ ZDNA1 ; ; COMPLETION EXIT ; ZDNA3: PUSH B ; SAVE COUNTER CALL DLOGIN ; RESTORE USER/DISK POP B ; RESTORE COUNTER IN C MVI A,0FFH ; SET NO ERROR ORA A ; SET FLAGS STA DNLOAD ; SET LOAD FLAG POP D ; RESTORE DE RET ;* ;* ZDNFIND -- SCAN FOR POSSIBLE DISK DIRECTORY NAME ;* THIS ROUTINE EXAMINES THE DIR: PREFIX FOR EITHER A DIRECTORY NAME ;* OR THE DU FORM ;* ON ENTRY, HL PTS TO DIRECTORY NAME ENDING IN ANY VALID DELIMITER ;* RETURN DISK IN B, USER IN C, NZ IF OK, HL PTS TO COLON ;* DE IS NOT AFFECTED ;* ZDNFIND: PUSH D ; SAVE DE SHLD DIRNAME ; SAVE DIRECTORY NAME AWAY LDA C$DR ; GET CURRENT DISK STA DISK STA T$DR LDA C$U$A ; GET CURRENT USER STA USER LHLD DIRNAME ; PT TO NAME JMP SVDISK ; CHECK DU FORM FIRST ; ; LOOK FOR DIR: FORM ; DIRNXX: ; ; SCAN MEMORY-RESIDENT BUFFER IF ONE IS AVAILABLE ; LDA T$DR ; SET DISK STA DISK LHLD NDRADR ; GET ADDRESS MOV A,L ; CHECK FOR ZERO ORA H CNZ NBSCAN ; SCAN BUFFER JRNZ NAMEF ; FOUND LXI H,BUFENTRY-2 ; PT TO BYTE BEFORE ENTRY COUNT OF DIR BUFFER LDA DNLOAD ; LOADED? ORA A ; 0=NO CNZ NBSCAN ; SCAN BUFFER JRNZ NAMEF POP D ; RESTORE DE XRA A ; NOT FOUND RET NAMEF: POP D ; RESTORE DE MVI A,0FFH ; FOUND ORA A ; DISK/USER IN B/C RET ; ; SCAN MEMORY-RESIDENT BUFFER PTED TO BY HL ; ON EXIT, A=0 AND ZERO FLAG IF NOT FOUND ; IF FOUND, B=DISK AND C=USER ; NBSCAN: INX H ; PT TO ENTRY COUNT MOV A,M ; GET ENTRY COUNT ORA A ; CHECK FOR NO ENTRIES RZ ; ABORT IF NO ENTRIES MOV B,A ; ENTRY COUNT IN B INX H ; PT TO FIRST ENTRY XCHG ; DE PTS TO FIRST ENTRY IN MEMORY ; ; MAIN SCANNING LOOP FOR MEMORY-RESIDENT BUFFER ; NBS0: LHLD DIRNAME ; HL PTS TO DIR NAME PUSH D ; SAVE PTR TO CURRENT MEMORY ENTRY MVI C,8 ; SCAN UP TO 8 BYTES INX D ; SKIP DISK INX D ; SKIP USER XCHG ; SWITCH PTRS NBS1: LDAX D ; GET CHAR IN BUFFER CPI ':' ; COLON? JRNZ NBS1A MVI A,' ' ; SUBSTITUTE SPACE FOR IT NBS1A: CMP M ; COMPARE AGAINST TARGET NAME JRNZ NBS2 INX H ; PT TO NEXT INX D DCR C ; COUNT DOWN JRNZ NBS1 JR NBS3 ; FOUND NBS2: POP H ; GET PTR TO CURRENT BUFFER ENTRY LXI D,10 ; SKIP TO NEXT ENTRY DAD D ; HL PTS TO NEXT ENTRY XCHG ; DE PTS TO NEXT ENTRY DJNZ NBS0 ; ; ENTRY NOT FOUND ; XRA A ; A=0 AND ZERO FLAG SET RET ; ; ENTRY FOUND ; NBS3: POP H ; GET PTR TO ENTRY MOV B,M ; DISK IN B INX H ; PT TO USER MOV C,M ; USER IN C MVI A,0FFH ; SET FOUND FLAG ORA A RET ; ; LOOK AT START OF DU: FORM ; ON ENTRY, HL PTS TO FIRST CHAR OF DIRECTORY NAME ; SVDISK: LDA MDISK ; GET MAX DISK INR A ; +1 FOR LATER COMPARE MOV B,A ; ... IN B MOV A,M ; GET DISK LETTER CPI 'A' ; DIGIT? JRC USERCK ; IF NO DIGIT, MUST BE USER OR COLON SUI 'A' ; CONVERT TO NUMBER CMP B ; LIMIT? JNC DIRNXX ; NAME IF OUT OF LIMIT STA DISK ; SAVE DISK INX H ; PT TO NEXT CHAR ; ; CHECK FOR USER ; USERCK: MOV A,M ; GET POSSIBLE USER NUMBER CPI ':' ; NO USER NUMBER JRZ DIRNX ; EXIT IF SO CPI ' ' ; NO USER NUMBER JRZ DIRNX ORA A JRZ DIRNX XRA A ; ZERO USER NUMBER MOV B,A ; B=ACCUMULATOR FOR USER NUMBER USRLOOP: MOV A,M ; GET DIGIT INX H ; PT TO NEXT CPI ':' ; DONE? JRZ USRDN CPI ' ' ; DONE? JRZ USRDN SUI '0' ; CONVERT TO BINARY JC DIRNXX ; NAME IF USER NUMBER ERROR CPI 10 JNC DIRNXX MOV C,A ; NEXT DIGIT IN C MOV A,B ; OLD NUMBER IN A ADD A ; *2 ADD A ; *4 ADD B ; *5 ADD A ; *10 ADD C ; *10+NEW DIGIT MOV B,A ; RESULT IN B JR USRLOOP USRDN: MOV A,B ; GET NEW USER NUMBER CPI 32 ; WITHIN RANGE? JNC DIRNXX ; NAME IF OUT OF RANGE STA USER ; SAVE IN FLAG ; ; VALID EXIT -- FOUND IT, SO LOAD BC AND EXIT FLAG; ON ENTRY, HL PTS TO : ; DIRNX: LDA USER ; RETURN USER IN C, DISK IN B MOV C,A LDA DISK MOV B,A MVI A,0FFH ; SET NO ERROR ORA A ; SET FLAGS POP D ; RESTORE DE RET ; ; INVALID EXIT -- NOT FOUND OR ERROR ; NO VALID RETURN PARAMETERS (BC, HL) ; DIRNERR: XRA A ; ERROR CODE STA DNLOAD ; SET LOAD FLAG TO NO LOAD POP D ; RESTORE DE RET ; ; GET NAME FROM NAMES.DIR INTO BUFFER PTED TO BY HL ; DO NOT AFFECT BC OR HL; RET W/NZ IF ERROR ; GETNAME: PUSH B ; SAVE BC PUSH H ; SAVE HL CALL FGET ; GET DISK LETTER JRNZ GNERR ; ERROR? SUI 'A' ; CONVERT TO NUMBER MOV M,A ; STORE IT INX H ; PT TO NEXT MVI B,10 ; GET USER AND DIRECTORY NAME GETN1: CALL FGET ; GET BYTE JRNZ GNERR ; ERROR? MOV M,A ; STORE IT INX H ; PT TO NEXT DJNZ GETN1 XRA A ; OK GNERR: POP H ; RESTORE HL POP B ; RESTORE BC RET ; ; OPEN FILE FOR GET ; FIOPEN: PUSH D MVI C,OPEN ; OPEN FILE CALL BDOS POP D FIO1: MVI C,READ ; READ FIRST BLOCK CALL BDOS LXI H,TBUF ; SET PTR SHLD FIPTR RET ; ; GET NEXT BYTE FROM FILE ; FGET: PUSH H ; SAVE REGS PUSH D PUSH B LHLD FIPTR ; PT TO NEXT CHAR MOV A,M ; GET IT STA FICHAR ; SAVE IT INX H ; PT TO NEXT SHLD FIPTR ; SET PTR LXI D,TBUF+80H ; END OF BUFFER? CALL CMPDEHL ; COMPARE JRNZ FGETD ; DONE IF NOT LXI D,S$FCB ; PT TO FCB CALL FIO1 ; READ BLOCK AND SET PTR ORA A ; SET FLAG (NZ = ERROR) JR FGETD1 FGETD: XRA A ; NO ERROR (Z) FGETD1: POP B ; GET REGS POP D POP H LDA FICHAR ; GET CHAR RET ; message routines ; print VFILER banner BANNER: CALL CLS ;clear screen LXI H,BANADR CALL GOTOXY CALL ILPRT ;print banner DB 'VFILER, Version ' DB VERS/10+'0','.',(VERS MOD 10)+'0' IF Z80 DB ' [Z80 Code]' ELSE DB ' [8080 Code]' ENDIF DB 0 RET ; home the cursor CUR$FIRST: LXI H,CURHOME ; HOME ADDRESS SHLD CURAT ; SET CURSOR POSITION JMP GOTOXY ; last file position CUR$LAST: LHLD RINGPOS ; ADVANCE SHLD LOCPOS ; SET LOCAL POSITION CL0: LXI D,13 DAD D XCHG LHLD LOCEND ; END OF LOCAL RING? CALL CMPDEHL RZ XCHG ; NEW POSITION SHLD LOCPOS PUSH H ; SAVE POSITION CALL CUR$NEXT ; ADVANCE CURSOR POP H ; GET POSITION JR CL0 ; advance the cursor CUR$NEXT: LHLD CURAT ; COMPUTE NEW POSITION MOV A,L ; CHECK FOR NEW LINE ADI 19 ; SIZE OF EACH ENTRY CPI 70 JRNC CN1 ; ADVANCE TO NEXT LINE MOV L,A ; NEW POSITION SHLD CURAT JMP GOTOXY CN1: MOV A,H ; GET LINE LXI H,CURHOME ; GET COL MOV H,A ; SET LINE AND FALL GO TO CUR$DOWN SHLD CURAT JR CUR$DOWN ; back up the cursor CUR$BACK: LXI H,CURHOME ; GET HOME XCHG ; ... IN DE LHLD CURAT CALL CMPDEHL ; COMPARE JRZ CUR$LAST ; GOTO END IF LAST MOV A,L ; CHECK FOR FIRST COL CMP E JRZ CB1 SUI 19 ; BACK UP ONE COL MOV L,A SHLD CURAT ; NEW POS JMP GOTOXY CB1: MOV A,E ; GET HOME COL ADI 19*3 ; GET LAST COL MOV L,A DCR H ; PREV LINE SHLD CURAT JMP GOTOXY ; move cursor down one line CUR$DOWN: LXI H,CURHOME ; GET HOME ADDRESS MOV B,H ; LINE IN B LHLD CURAT ; GET CURRENT ADDRESS INR H ; MOVE DOWN MOV A,H ; CHECK FOR TOO FAR SUB B CPI EPS/4 JRNC CD1 SHLD CURAT ; OK, SO SET POSITION JMP GOTOXY CD1: MOV A,L ; GET COL LXI H,CURHOME MOV L,A SHLD CURAT JMP GOTOXY ; refresh screen REFRESH: LHLD CURAT ; SAVE CURSOR AND RING POSITIONS SHLD SCURAT LHLD RINGPOS SHLD SRINGPOS CALL BANNER ; PRINT BANNER CALL NEWPOS ; DISPLAY FILES LXI H,CPMADR ; COMMAND PROMPT MESSAGE CALL GOTOXY CALL ILPRT ; PROMPT WITH DRIVE PREFIX LOG$DU$MSG: DB ' : ' DB 'Command (? for Help)?',0 LXI H,SDMADR ; SCREEN DIRECTORY MESSAGE CALL GOTOXY CALL ILPRT DB '-- Screen Directory --',0 LXI H,FNADR ; PT TO WHERE FILE NAME IS PRINTED MVI L,1 ; COL 1 FOR THIS MESSAGE CALL GOTOXY ; GO THERE CALL ILPRT DB 'Current File:',0 LHLD SCURAT ; RESTORE CURSOR AND RING POSITIONS SHLD CURAT LHLD SRINGPOS SHLD RINGPOS CALL SETCUR ; RESTORE CURSOR ON SCREEN RET ; refresh file display NEWPOS: CALL CUR$FIRST ; POSITION CURSOR AT FIRST POSITION MVI B,EPS/4 ; # LINES NEWP0: PUSH B CALL EREOL ; ERASE TO EOL LHLD CURAT ; GET ADDRESS OF CURSOR INR H ; NEXT LINE SHLD CURAT CALL GOTOXY POP B DJNZ NEWP0 CALL CUR$FIRST ; POSITION CURSOR AT FIRST POSITION LHLD LOCBEG ; PT TO FIRST FILE NAME SHLD LOCPOS ; SAVE LOCAL POSITION NEWP1: LHLD LOCEND ; AT END? XCHG LHLD LOCPOS CALL CMPDEHL JZ CUR$FIRST ; POSITION AT FIRST ENTRY AND RETURN MVI B,4 ; 4 SPACE MVI A,' ' T4: CALL TYPE DJNZ T4 PUSH H ; SAVE CURRENT LOCAL POSITION IN RING INX H ; PT TO FILE NAME CALL PRFN ; PRINT FILE NAME MOV A,M ; PRINT TAG CALL TYPE POP H ; GET CURRENT LOCAL POSITION LXI D,13 DAD D SHLD LOCPOS CALL CUR$NEXT ; ADVANCE CURSOR JR NEWP1 ; position cursor at CURAT SETCUR: LHLD CURAT CALL GOTOXY CALL ILPRT DB '-->',0 RET ; clear cursor CLRCUR: LHLD CURAT CALL GOTOXY CALL ILPRT DB ' ',0 RET ; command prompt CPRMPT: LXI H,CPADR ; GET ADDRESS MPRINT: PUSH H ; SAVE ADDRESS CALL GOTOXY CALL EREOL ; ERASE TO EOL POP H ; GET ADDRESS CALL GOTOXY ; POSITION CURSOR JMP ILPRT ; PRINT MESSAGE AND RETURN ; working message WORKMSG: CALL ERMSG DB 'Working ...',0 RET ; error message ERMSG: MVI A,0FFH ; SET ERROR MESSAGE FLAG STA ERMFLG LXI H,ERADR ; GET ADDRESS JR MPRINT ; print file size info FSNOTE: CALL ERMSG ; USE THIS ROUTINE DB 'File Size of ',0 RET ; position for file size print ATFS: LXI H,FSADR+13 ; POSITION FOR PRINT OF FILE SIZE JMP GOTOXY ; clear error message ERCLR: XRA A ; CLEAR FLAG STA ERMFLG LXI H,ERADR ; POSITION CALL GOTOXY JMP EREOL ; ERASE TO EOL ; position at command prompt and clear it ATCMD: LXI H,CPADR ; POSITION CALL GOTOXY CALL EREOL ; CLEAR MESSAGE LXI H,CPADR ; REPOSITION JMP GOTOXY ; position at bottom of screen and prompt for continuation BOTTOM: LXI H,BOTADR ; POSITION CALL GOTOXY CALL ILPRT DB 'Strike Any Key to Continue -- ',0 JMP KEYIN ; s t o r a g e ; initialized HEADMSG: DB 'File: ',0 MOREHELP: DB 'HELP ' ;HELP Command for further info VFNAME ;VFILER Name DB 0 HELPFCB: DB 0,'HELP COM' MACFCB: DB 0 VFNAME ;VFILER Name VFNFILL ;Space Fill for VFILER Name DB 'CMD' FILERCMD: DB ';' VFNAME ;VFILER Name DB ' ' FILE$D: DB 'x' FILE$U: DB 'xx' DB ' W' ;WAIT option DB 0 JOKER: DB '???????????' ; *.* equivalent FIRST$M: DB FALSE ; 1st time thru in mass-copy mode MFLAG: DB TRUE ;multiple file copy flag --> 0 for mass copy TAG$TOT: DW 0 ;summation of tagged file sizes CMDBUF: DB 32,0 ;command buffer maximum length, usage, and.. ; uninitialized DS 100 ;..storage for buffer and local stack. STACK: DS 2 ;cp/m's stack pointer stored here ALPHA: DS 1 ;alphabetization flag (0=type and name, 0FFH= ;... name and type) B$MAX: DS 2 ;highest block number on drive B$MASK: DS 1 ;sec/blk - 1 BSHIFTF: DS 1 ; # of shifts to multiply by sec/blk BUF$PT: DS 2 ;copy buffer current pointer.. BUFSTART: DS 2 ;..and begin pointer. CANFLG: DS 1 ;no-file-found cancel flag C$DR: DS 1 ; 'current drive' CHARCNT: DS 1 ;character count for tab expansion CON$LST: DS 1 ;bdos function storage CRCTBL: DS 512 ;tables for 'crc' calculations CRCVAL: DS 2 ; 2-byte 'crc' value of working file and.. CRCVAL2: DS 2 ;..of finished source read-file. C$U$A: DS 1 ; 'current user area' CURAT: DS 2 ;current cursor position D$FCB: DS 33 ;fcb for destination file/new name if rename DIRNAME: DS 2 ;ptr to DIR prefix DISK: DS 1 ;selected disk for ZDNAME DISKSP: DS 2 ;space remaining on disk DNLOAD: DS 1 ;NAMES.DIR loaded flag DUM$FCB: DS 36 ;dummy FCB for file attributes DRLET: DS 1 ;scratch for drive letter ENTRY: DS 11 ;scratch for ZDNAME/ZDNFIND EOFLAG: DS 1 ;file copy loop 'eof' flag ERMFLG: DS 1 ;error message present flag FICHAR: DS 1 ;byte-oriented input char FIPTR: DS 2 ;byte-oriented input ptr FSDFLG: DS 1 ;display file size flag (yes/no) FS$FLG: DS 1 ;tag total versus file size flag HELPFLG: DS 1 ;is HELP available externally? 0=No LDSP: DS 1 ;leading space count for DECOUT LPSCNT: DS 1 ;lines-per-screen for 'view' LOCBEG: DS 2 ;local beginning of ring LOCEND: DS 2 ;local end of ring LOCPOS: DS 2 ;local ring position (temp) MAXDR: DS 1 ;max driver letter MDFLG: DS 1 ;mass delete verify flag O$USR: DS 1 ;store initial user area for exit R$DR: DS 1 ; 'requested drive' RCNT: DS 2 ; # of records in file and.. REC$CNT: DS 2 ;..currently in ram buffer. REC$MAX: DS 2 ;maximum 128-byte record capacity of buffer RING: DS 2 ;ptr to beginning of ring RINGI: DS 2 ;ring sort pointer RINGJ: DS 2 ;another ring sort pointer RINGEND: DS 2 ;current ring end pointer RINGPOS: DS 2 ;current ring position in scan R$U$A: DS 1 ; 'requested user area' SCURAT: DS 2 ;save cursor position S$FCB: DS 36 ;fcb for source (random record) file SRINGPOS: DS 2 ;save ring position T$DR: DS 1 ;temp disk TEST$RT: DS 1 ;intermediate right-justify data T$U$A: DS 1 ;temp user T$UN$FG: DS 1 ;tag/untag file summation switch USER: DS 1 ;temp user buffer VIEWFLG: DS 1 ; 00h --> to list/punch else to crt 'view' Z$DR: DS 1 ;disk for ZDNAME Z$U$A: DS 1 ;user area for ZDNAME ; cp/m system functions RDCON EQU 1 ;console input function WRCON EQU 2 ;write character to console.. PUNCH EQU 4 ;..punch and.. LIST EQU 5 ;..to list logical devices. DIRCON EQU 6 ;direct console i/o RDBUF EQU 10 ;read input string CONST EQU 11 ;get console status RESETDK EQU 13 ;reset disk system LOGIN EQU 14 ;log-in new drive OPEN EQU 15 ;open file CLOSE EQU 16 ;close file SRCHF EQU 17 ;search directory for first.. SRCHN EQU 18 ;..and next occurrence. ERASE EQU 19 ;erase file READ EQU 20 ;read and.. WRITE EQU 21 ;..write 128-record. MAKE EQU 22 ;make file REN EQU 23 ;rename file INQDISK EQU 25 ;get current (default) drive SETDMA EQU 26 ;set dma address INQALC EQU 27 ;allocation vector ATTR EQU 30 ;set file attributes GETPARM EQU 31 ;current drive parameters address SGUSER EQU 32 ;set or get user area COMPSZ EQU 35 ; # of records in file ; system addresses BDOS EQU CPM$BASE+05H ;bdos function entry address FCB EQU CPM$BASE+5CH ;default file control block FCBEXT EQU FCB+12 ;extent byte in 'fcb' FCBRNO EQU FCB+32 ;record number in 'fcb' FCB2 EQU CPM$BASE+6CH ;2nd FCB TBUF EQU CPM$BASE+80H ;default cp/m buffer ; assembled 'com' and 'ram-loaded' file size (0c00h = 3k) COMFILE EQU (CMDBUF+2)-256 ; 'prn' listing shows 'com'.. LAST: DS 1 ; 1 for byte before BUFENTRY ; even-page base of filename ring storage BUFENTRY EQU $/100H*100H+100H END ;..and loaded file size.