home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols000
/
vol066
/
sd-44.asm
< prev
next >
Wrap
Assembly Source File
|
1983-08-29
|
49KB
|
1,843 lines
* SD.ASM Ver 4.4
* (Rev 28feb82)
* SUPER DIRECTORY PROGRAM
* by Bruce R. Ratoff
* Based on 'DIRS' by Keith Petersen, W8SDZ
* Displays the directory of a CP/M disk, sorted alphabetically,
* with the file size in K, rounded to the nearest CP/M block size.
* This latest variation on a common theme will automatically adjust
* itself for any block size and directory length under CP/M 1.4 or 2.x
* or MP/M (any version). Provisions are made for (1) automatic pauses
* when the screen fills up; (2) searching individual or multiple drives
* and/or user areas; (3) unconditional or optionally resetting the disk
* system before execution begins; (4) directing output to a disk file
* and appending to it on subsequent runs; (5) summary line output giving
* drive and user information, # of files matched and how much space they
* consume, and the amount of free space remaining on the disk; (6) displaying
* or suppressing CP/M 2 "system" files; (7) accepting ambiguous filenames
* with or without a drive name; and (8) printer output.
* See SD.DOC for detailed instructions on configuring and running SD.ASM
* ==========================================================================
* NOTE: If you add improvements or otherwise update
* this program, please modem a copy of the new file
* to "TECHNICAL CBBS" in Dearborn, Michigan - phone
* 313-846-6127 (110, 300, 450 or 600 baud). Use the
* filename SD-NEW.NEW. (KBP)
* As SD-41.ASM is "officially experimental", the author
* of the latest revisions would appreciate feedback on
* any bugs which may be discovered. CC bug notes to
* me c/o Hyde Park RCP/M (phone 312-955-4493). (DB)
* ==========================================================================
* Fixes/updates (in reverse order to minimize reading time):
* 28feb82 Ignore attribute bits in compare subrs, equate CRT leadin char.,
* add Hazeltine 1500 equates. (4.4 - W.Earnest)
* 06feb82 Modified use of BDOS error vector table for DOPT. The previous
* arrangement would change BDOS without checking whether it really was
* pointing to the right location. In addition, the trap vectors are
* swapped only when called for.
* Modified RMAC/MAC options: ASEG statement added for using RMAC
* without the requirement for external defs.
* Also added dim for ADM-31 (works same as reverse video), and cleared
* high^order bit when being sent to printer. (4.3 - R.B.STRAND)
* 11-1-81 Eliminated LF after "More" message. Allows yet another
* line per more.
* 10-25-81 Incorporated 10/21 updates renamed version. -CAF
* Also made display more compact by eliminating unneeded lf's
* This allows two more lines per "more"
* 10-21-81 Added conditionals for REVIDEO for reverse video
* display of tags ($SYS, etc.) -CAF
* 10/20/81 minor update to correct (1) malfunction when appending to an
* existing sd.dir file with the directory from an empty disk;
* (2) clobbering data area while closing sd.dir when last record
* appended is completely full; and (3) equate interaction error
* between fopt and aopt. (ver 4.1 - david boruff)
* 10/19/81 MAJOR UPDATE adding new command line options for
* (1) disk system reset; (2) page pause override;
* (3) displaying directory of a specified user area;
* (4) searching ranges of drives or user areas; and
* (5) printer output. Corrected previous name error
* to avoid confusion with Keith Petersen's old SDIR.
* Corrected obscure file output bug which occurred only
* when appending on even record or extent boundaries.
* Reset equate of Ver 2.3 deleted as redundant now that
* command line reset available as an option or default.
* Shortened page-pause message to free-up program space.
* Optimized much of the existing code to compact the load
* module and gain space for new features. Option scanner
* rewritten to permit free form input and to simplify any
* future expansions of the command line option set.
* Reclaimed uninitialized data memory to further compact
* load module size. By popular demand, restored code to
* display free space remaining when no files are found.
* Added tables to allow restricting directory searches
* to a range of drives and/or user areas. BDOS intercept
* added to facilitate searching drives that may not be
* on-line (more generally applicable than the FILEFIND
* method if your BIOS returns control to BDOS on fatal disk
* errors). General cosmetic overhaul including tabular
* realignment and additional comments to enhance readability
* at the cost of a "few" extra bytes. (Ver 4.0 - David Boruff)
* 09/23/81 MINOR UPDATE to add equate to allow suppressing user
* numbers in the directory output. (Ver 3.1 - David Boruff)
* 09/18/81 MAJOR UPDATE implementing new file output option allowing
* directory output to be appended to a disk file. Changed
* option specification format to require a dollar sign
* preceding the option specifiers. Changed header format
* to display drive & user #'s for each line less than 4
* files wide. Changed code for no files found to display
* error message only. (Ver 3.0 - David Boruff)
* 09/12/81 MINOR UPDATE deleting WIDE and NOT WIDE code in deference
* to equating screen size. Added Flashwriter II equate to
* allow display of file attributes in reverse video. Changed
* tag-line to display drive and user #. Added equate to allow
* disk system reset on startup. Other cosmetic changes made
* to open up display format and give output a less cramped
* appearance. (Ver 2.3 - David Boruff)
* 06/05/81 Added PGPAWZ (page pause) conditional for remote
* CP/M systems where pausing may not be wanted.
* Setting PGPAWZ and REPSIZ to FALSE will result in
* a display like DIR, but sorted and with the stat
* of space remaining. Rearranged equates to allow
* 15 lines per page when narrow display is chosen.
* (Ver 2.2 - KBP)
* 06/01/81 Added version number, restored CTL-C break, added
* CTL-C test to allow break at page pause, added
* routine to gobble up any waiting console character
* at EXIT, added conditional assembly to allow no
* report of file sizes, added conditional assembly
* for direct console I/O for remote CP/M systems
* where phone line noise would garbage display. (KBP)
* 05/06/81 Corrected double printing of drive name in CALLB.
* Error only occurred with narrow display when file
* wasn't found. (Tim Nicholas)
* 02/06/81 Changed sort to have odd gap (KBP say its faster)
* 01/06/81 Changed sort from bubble sort to shell sort
* for faster speed.
* 12/24/80 Changed BIOS conout to BDOS conout to allow
* printing of directory with CTL-P. Also added
* print of remaining space even if file not
* found. (Steve Nossen)
* 12/15/80 Added space suppression when printing file
* totals. (KBP)
* 12/14/80 Added logic to print space remaining on disk.
* Changed ^C test so that interrupting character is
* not echoed (makes remote use cleaner). (BRR)
* 12/02/80 Fixed bug in print routine which compared last file
* against garbage before printing. (BRR)
* 11/29/80 Changed to allow printing 4 file names. (Ben Bronson
* and Keith Petersen)
* 11/22/80 Fixed bug in handling >256 files. Changed abort test
* in print routine to only abort on control-c. (BRR)
* ==========================================================================
* Set 'RMAC' TRUE to assemble with relocating assembler
* (requires link with PAGE 0 equates in separate file).
* ==========================================================================
FALSE EQU 0
TRUE EQU NOT FALSE
******************************
* *
* USER OPTION SPECIFICATIONS *
* *
******************************
ALTCPM EQU FALSE ;True for H8 or TRS-80
DIRCON EQU FALSE ;True for direct console output
OPTION EQU TRUE ;True if allowing ANY command line options
AOPT EQU TRUE ;True to allow searching all user areas
DOPT EQU TRUE ;True to allow searching all drives on-line
FOPT EQU TRUE ;True to allow file output option
NOPT EQU TRUE ;True to allow disabling page pause option
PGPAWZ EQU TRUE ;True for pause after each page
POPT EQU TRUE ;True to allow printer option
REPERR EQU TRUE ;True to report command line option errors
REPSIZ EQU TRUE ;True to report file sizes
REPUSR EQU TRUE ;True to report user numbers
RMAC EQU FALSE ;True for RMAC assembly and external def link
ROPT EQU TRUE ;True to allow reset option
SOPT EQU TRUE ;True to allow system file option
UOPT EQU TRUE ;True to allow user number option
REVIDEO EQU TRUE ;Use reverse video sequences
* LEADIN EQU 033Q ;Esc. leadin for ADM-31 & Z19
* INTOREV EQU 051Q ;Seq. to enter dim mode (for ADM-31)
* OUTAREV EQU 050Q ; and to get out
* INTOREV EQU 0160Q ;Sequence to enter reverse video (Z19)
* OUTAREV EQU 0161Q ;And to get out
LEADIN EQU 07EH ;Tilda leadin for hazeltine 1500
INTOREV EQU 19H ;Background mode on Hazeltine 1500
OUTAREV EQU 1FH ;Foreground mode on Hazeltine 1500
VECTOR EQU FALSE ;True to display attributes on Flashwriter II
VIDEO EQU 0E009H ;Entry to Flashwriter video driver PROM
* DELIM EQU 7CH ;Fence (delimiter) character (vertical bar)
DELIM EQU 20H ;Space
NPL EQU 4 ;# of names per line (max of 3 for 64x16)
* (max of 4 for 80x24)
LPS EQU 23 ;# of lines per screen (max of 14 for 64x16)
* (max of 23 for 80x24)
IF NOPT AND NOT PGPAWZ
++++ NOPT OPTION REDUNDANT WITHOUT PGPAWZ ++++
ENDIF
IF REPERR AND NOT OPTION
++++ REPERR EQUATE USELESS WITHOUT OPTION ++++
ENDIF
* BDOS equates
RDCHR EQU 1 ;Read char from console
WRCHR EQU 2 ;Write char to console
CONST EQU 11 ;Check cons stat
RESET EQU 13 ;Reset disk system
SELDSK EQU 14 ;Select disk
OPEN EQU 15 ;0FFH=not found
CLOSE EQU 16 ; " "
SEARCH EQU 17 ; " "
NEXT EQU 18 ; " "
READ EQU 20 ;not 0 = EOF
WRITE EQU 21 ;not 0 = disk full
MAKE EQU 22 ;0FFH = directory full
CURDSK EQU 25 ;Get currently logged disk name
SETDMA EQU 26 ;Set current DMA
GALLOC EQU 27 ;Get address of allocation vector
CURDPB EQU 31 ;Get current disk parameters
CURUSR EQU 32 ;Get currently logged user number (2.x only)
IF ALTCPM
BASE EQU 4200H
TPA EQU 4300H
ENDIF
IF (NOT ALTCPM) AND (NOT RMAC)
BASE EQU 0 ;Default to 0 (or 100H with MAC +R option)
TPA EQU 100H
ENDIF
IF RMAC
EXTRN BASE,FCB,BDOS ;Make base external
ELSE
FCB EQU BASE+5CH
BDOS EQU BASE+5
ASEG ; Ignore this error if using MAC
ORG TPA
ENDIF
FILL MACRO BYTES,WITH
REPT BYTES
DB WITH
ENDM
ENDM
$-MACRO
PAGE 60
TITLE 'SD Version 4.4 - 28 feb 1982'
*********************************
* *
* BEGIN EXECUTABLE PROGRAM CODE *
* *
*********************************
JMP START
DB 'SD 4.4 - 28feb82'
START: LXI H,0
DAD SP ;HL=old stack
SHLD STACK ;Save it
LXI SP,STACK ;Get new stack
MVI C,12 ;Get and save the CP/M version #
CALL BDOS
MOV A,L
STA VERFLG
CPI 20H ;Set carry if CP/M 1.4
MVI E,0FFH ;Get current user number if using CP/M 2, or
MVI C,CURUSR ;Fall through with A=0 if not
CNC CPM
STA OLDUSR ;Initialize startup user number
STA NEWUSR ;..and make new user match it
IF DOPT
STA BASUSR ;Save extra copy for multi-disk directories
ENDIF
MVI C,CURDSK
CALL CPM ;Get current disk nr
STA OLDDSK ;Save for reset if needed
IF FOPT
INR A
STA OUTFCB ;Set directory output file drive
ENDIF
LXI H,FCB
MOV A,M ;Get drive name for directory search
ORA A ;Any specified?
JNZ START2 ;Yes skip next routine
LDA OLDDSK ;Otherwise, get default disk
INR A
START2: MOV M,A ;Put the absolute drive code in directory FCB
* If at least one option is allowed, scan the command line for the
* option field delimiter. The option field delimiter is considered
* valid only if it is preceded by at least 1 space (otherwise, it
* may be part of the directory filename). Any unrecognized options
* or illegal user numbers will be flagged or ignored (see REPERR).
* (We scan the command line buffer rather than the 2nd default FCB
* because all 8 options plus a 2 digit user number won't fit in
* the FCB name field).
IF OPTION
LXI H,80H ;Set command line buffer pointer
MOV B,M ;Get length of command line buffer
* Search for the command line delimiter. If not found, assume no options.
SCNDOL: INX H
DCR B
JM CKREST ;Exit if command line buffer empty
MOV A,M
CPI '$'
JNZ SCNDOL
DCX H ;'$' found - make sure space precedes it
MOV A,M
INX H
CPI ' '
JNZ SCNDOL ;No space - ignore "$" and search again
* Valid delimiter found. Scan the rest of the buffer for options. Errors
* past this point will cause an abort if the command line error option is
* enabled. Otherwise, the dud option will be ignored and SD will attempt
* to continue stumbling through the rest of the field.
XCHG ;Get option field pointer to DE
SCNOPT: INX D ;Bump to next option field character
DCR B ;Dock characters left in option field
JM CKREST ;If option field exhausted, exit
SCNAGN: LDAX D ;Get the next option character
CPI ' ' ;Do we have a space?
JZ SCNOPT ;Ignore it if so
LXI H,OTBL-1 ;Get base of option lookup table
MVI C,OEND-OTBL+1 ;Get length of option lookup table
NOMACH: INX H ;Bump to next option table character
DCR C ;Are we out of the table?
JZ CK4USR ;If so, check for user option
CMP M ;Compare our character with option table
JNZ NOMACH ;Exit if no match
MVI M,0 ;Otherwise, activate the flag
JMP SCNOPT ;..and go get the next option character
* If option character doesn't match the table, see if we have a User
* option.
CK4USR: IF UOPT ;Check for user number option
CPI 'U'
JNZ CLERR ;Last option, so bad deal if that ain't it
UAGN: INX D ;Bump to user number digit
DCR B
JM CLERR ;Error if nothing left
LDAX D ;Get decimal digit
CPI ' ' ;Ignore leading spaces
JZ UAGN
SUI 30H ;Subtract ASCII BIAS
JC CLERR ;Error if < 0
CPI 10
JNC CLERR ;Error if > 9
STA NEWUSR ;Save user number as it may be only 1 digit
IF DOPT
STA BASUSR ;Duplicate it if multi-disk mode
ENDIF
INX D ;Bump to possible 2nd digit of user number
DCR B
JM CKREST ;If no more buffer, exit with complete user #
LDAX D ;Else, check for another digit
SUI 30H
JC SCNAGN ;If next char not numeric, its not part of
CPI 10 ;..user number so go check for another option
JNC SCNAGN
MOV L,A ;Save units digit
LDA NEWUSR ;Get tens digit
ADD A ;Multiply by 10
MOV H,A
ADD A
ADD A
ADD H
ADD L ;Combine with units digit
STA NEWUSR ;Save the total user number
IF DOPT
STA BASUSR ;Duplicate it if multi-disk mode
ENDIF
JMP SCNOPT ;Continue scanning
ENDIF ;Balance UOPT
* If command line error option enabled, playback the command line up
* to the character that we gagged on and exit. If REPERR is not enabled,
* then continue as if nothing were amiss to avoid acknowledging that
* some options are available.
CLERR: IF REPERR
XRA A
INX D ;Tag end of command line with terminator
STAX D
CALL CRLF
LXI D,ERRMS2
CALL PRINT
LXI D,ERRTAG
CALL PRINT
LXI H,81H ;Playback bad command line to error point
CLELP: MOV A,M
ORA A
JZ CLEX
CALL TYPE
INX H
JMP CLELP
CLEX: MVI A,'?' ;Tag line with a '?' field
CALL TYPE
CALL CRLF ;Space down 1 more line
JMP EXIT ;..and return to CP/M
ELSE
JMP SCNOPT ;If not reporting errors, ignore the dud
ENDIF ;Balance REPERR
ENDIF ;Balance OPTION
* Options input or not specified. If reset option specified, reset
* the disk system now. Its important that the reset be done OUTSIDE
* the multiple drive loop if the file output option is enabled because
* CP/M 1.4 clobbers the DMA buffer on reset.
CKREST: IF ROPT
LDA ROPFLG ;If reset flag set, reset disk system before
ORA A ;..starting to update allocation vectors
MVI C,RESET
CZ CPM
ENDIF ;Balance ROPT
IF DOPT
LDA DOPFLG ;If multi-disk flag set,
ORA A ;..need to set error traps
CZ SWAPEM ;Swap BDOS error vector tables
ENDIF
* Validate drive code and user area numbers from the drive table.
NOOPT: LXI D,DREMSG ;Get the drive/user error message
PUSH D
LDA FCB ;Get directory drive code
DCR A ;Normalize to range of 0-15
CPI HIDRV-LODRV ;Compare with maximum drives on-line
JNC ERXIT ;Take drive error exit if out of range
LXI H,USRMSG ;Switch to user # error message
XTHL
MOV E,A ;Use drive code as index into table
MVI D,0
LXI H,LODRV ;Point to base of drive/user table
DAD D
MOV A,M ;Get the maximum user # for this drive
ANI 0FH ;Make sure its in range 0 - 15
STA MAXUSR ;Save it for later
LXI H,NEWUSR ;Point to the directory user area
CMP M ;Compare it with the maximum
JC ERXIT ;Take error exit if user number illegal
POP D ;Destroy error message pointer
LXI H,FCB+1 ;Point to name
MOV A,M ;Any specified?
CPI ' '
JNZ GOTFCB
* No FCB - make FCB all '?'
MVI B,11 ;FN+FT count
QLOOP: MVI M,'?' ;Store '?' IN FCB
INX H
DCR B
JNZ QLOOP
GOTFCB: MVI A,'?' ;Force wild extent
STA FCB+12
CALL SETSRC ;Set DMA for BDOS media change check
LXI H,FCB ;Point to FCB drive code for directory
MOV E,M ;Get the drive code out of the FCB
DCR E ;Normalize drive code for SELECT
MVI C,SELDSK ;Select the directory drive to retrieve
CALL CPM ;..the proper allocation vector
CALL CKVER ;Check version
JC V14 ;Pre-2.X...get params the 1.4 way
MVI C,CURDPB ;It's 2.X or MP/M...request DPB
CALL BDOS
INX H
INX H
MOV A,M ;Get block shift
STA BLKSHF
INX H ;Bump to block mask
MOV A,M
STA BLKMSK ;Get it
INX H
INX H
MOV E,M ;Get max block #
INX H
MOV D,M
XCHG
SHLD BLKMAX ;Save it
XCHG
INX H
MOV E,M ;Get directory size
INX H
MOV D,M
XCHG
JMP FREE ;Let FREE save it and setup order table
V14: LHLD BDOS+1 ;Get params 1.4 style
MVI L,3BH ;Point to directory size
MOV E,M ;Get it
MVI D,0 ;Force high order to 0
PUSH D ;Save for later
INX H ;Point to block shift
MOV A,M ;Fetch
STA BLKSHF ;Save
INX H ;Point to block mask
MOV A,M ;Fetch it
STA BLKMSK ;And save it
INX H
MOV E,M ;Get max. block no.
MVI D,0
XCHG
SHLD BLKMAX ;Save it
POP H ;Restore directory size
* Calculate # of K free on selected drive now so that the FREE figure
* will not reflect either the creation or additions to the SD.DIR
* file (which we would probably erase or move anyway).
FREE: SHLD DIRMAX ;Save max # of entries in directory
MVI C,GALLOC ;Get address of allocation vector
CALL BDOS
XCHG
LHLD BLKMAX ;Get its length
INX H
LXI B,0 ;Init block count to 0
GSPBYT: PUSH D ;Save alloc address
LDAX D
MVI E,8 ;Set to process 8 blocks
GSPLUP: RAL ;Test bit
JC NOTFRE
INX B
NOTFRE: MOV D,A ;Save bits
DCX H ;Count down blocks
MOV A,L
ORA H
JZ ENDALC ;Quit if out of blocks
MOV A,D ;Restore bits
DCR E ;Count down 8 bits
JNZ GSPLUP ;Do another bit
POP D ;Bump to next byte..
INX D ;..of alloc. vector
JMP GSPBYT ;Process it
ENDALC: POP D ;Clear allocation vector pointer from stack
MOV L,C ;Copy blocks to hl
MOV H,B
LDA BLKSHF ;Get block shift factor
SUI 3 ;Convert from sectors to K
JZ SAVFRE ;Skip shifts if 1K blocks - return free in HL
FREKLP: DAD H ;Multiply blocks by K/BLK
DCR A
JNZ FREKLP
SAVFRE: SHLD FREEBY ;Save the free space for output later
* Reenter here on subsequent passes while in the all-users mode
SETTBL: LHLD DIRMAX ;Get directory maximum again
INX H ;Directory size is DIRMAX+1
DAD H ;Double directory size
LXI D,ORDER ;To get size of order table
DAD D ;Allocate order table
SHLD TBLOC ;Name table begins where order table ends
SHLD NEXTT
XCHG
LHLD BDOS+1 ;Make sure we have room to continue
MOV A,E
SUB L
MOV A,D
SBB H
JNC OUTMEM
IF UOPT
CALL CKVER ;Set carry if pre-CP/M 2
LDA NEWUSR ;Get user area for directory
MOV E,A
MVI C,CURUSR ;Get the user function
CNC CPM ;..and set new user number if CP/M 2
ENDIF
* Look up the FCB in the directory
SFIRST: LXI H,0
SHLD COUNT ;Initialize match counter
SHLD TOTFIL ; " total file counter
SHLD TOTSIZ ; " total size counter
CALL SETSRC ;Set DMA for directory search
MVI C,SEARCH ;Get 'search first' function
JMP LOOK ;..and go search for 1st match
* Read more directory entries
MORDIR: MVI C,NEXT ;Search next
LOOK: LXI D,FCB
CALL CPM ;Read directory entry
INR A ;Check for end (0FFH)
JZ SPRINT ;If no more, sort & print what we have
* Point to directory entry
SOME: DCR A ;Undo prev 'INR A'
ANI 3 ;Make modulus 4
ADD A ;Multiply...
ADD A ;..by 32 because
ADD A ;..each directory
ADD A ;..entry is 32
ADD A ;..bytes long
LXI H,BASE+81H ;Point to buffer
;(skip to FN/FT)
ADD L ;Point to entry
ADI 9 ;Point to SYS byte
MOV L,A ;Save (can't carry to H)
IF SOPT
LDA SOPFLG ;Did user request SYS files?
ORA A
JZ SYSFOK
ENDIF
MOV A,M ;Get SYS byte
ORA A ;Check bit 7
JM MORDIR ;Skip that file
SYSFOK: MOV A,L ;Go back now
SUI 10 ;Back to user number (alloc flag)
MOV L,A ;HL points to entry now
LDA NEWUSR ;Get current user
CMP M
JNZ MORDIR ;Ignore if different
INX H
* Move entry to table
XCHG ;Entry to DE
LHLD NEXTT ;Next table entry to HL
MVI B,12 ;Entry length (name, type, extent)
TMOVE: LDAX D ;Get entry char
IF NOT (VECTOR OR REVIDEO)
ANI 7FH ;Remove attributes
ENDIF
MOV M,A ;Store in table
INX D
INX H
DCR B ;More?
JNZ TMOVE
INX D
INX D ;Point to sector count
LDAX D ;Get it
MOV M,A ;Store in table
INX H
SHLD NEXTT ;Save updated table addr
XCHG
LHLD COUNT ;Bump the # of matches made
INX H
SHLD COUNT
LXI H,13 ;Size of next entry
DAD D
XCHG ;Future NEXTT is in DE
LHLD BDOS+1 ;Pick up TPA end
MOV A,E
SUB L ;Compare NEXTT-TPA end
MOV A,D
SBB H
JC MORDIR ;If TPA END > NEXTT then loop back for more
OUTMEM: CALL ERXIT ;Exit if directory too large
DB 'MEMOR','Y' OR 80H
* Sort and print
SPRINT: IF AOPT OR FOPT OR UOPT
CALL SETFOP ;Return to file output DMA & user #
ENDIF
LHLD COUNT ;Get file name count
MOV A,L
ORA H ;Any found?
JZ PRTOTL ;Exit if no files found
PUSH H ;Save file count
STA SUPSPC ;Enable leading zero suppression
* Initialize the order table
LHLD TBLOC ;Get start of name table
XCHG ;Into DE
LXI H,ORDER ;Point to order table
LXI B,13 ;Entry length
BLDORD: MOV M,E ;Save low order address
INX H
MOV M,D ;Save high order address
INX H
XCHG ;Table addr to HL
DAD B ;Point to next entry
XCHG
XTHL ;Save tbl addr, fetch loop counter
DCX H ;Count down loop
MOV A,L
ORA H ;More?
XTHL ;(restore tbl addr, save counter)
JNZ BLDORD ;Yes, go do another one
POP H ;Clean loop counter off stack
LHLD COUNT ;Get count
SHLD SCOUNT ;Save as # to sort
DCX H ;Only 1 entry?
MOV A,L
ORA H
JZ DONE ;Yes, so skip sort
* This sort routine is adapted from SOFTWARE TOOLS
* by Kernigan and Plaugher.
SORT: LHLD SCOUNT ;Number of entries
L0: ORA A ;Clear carry
MOV A,H ;GAP=GAP/2
RAR
MOV H,A
MOV A,L
RAR
MOV L,A
ORA H ;Is it zero?
JZ DONE ;Then none left
MOV A,L ;Make gap odd
ORI 01
MOV L,A
SHLD GAP
INX H ;I=GAP+1
L2: SHLD I
XCHG
LHLD GAP
MOV A,E ;J=I-GAP
SUB L
MOV L,A
MOV A,D
SBB H
MOV H,A
L3: SHLD J
XCHG
LHLD GAP ;JG=J+GAP
DAD D
SHLD JG
MVI A,12 ;Compare 12 chars
CALL COMPARE ;Compare (J) and (JG)
JP L5 ;If A(J)<=A(JG)
LHLD J
XCHG
LHLD JG
CALL SWAP ;Exchange A(J) and A(JG)
LHLD J ;J=J-GAP
XCHG
LHLD GAP
MOV A,E
SUB L
MOV L,A
MOV A,D
SBB H
MOV H,A
JM L5 ;If J>0 GOTO L3
ORA L ;Check for zero
JZ L5
JMP L3
L5: LHLD SCOUNT ;For later
XCHG
LHLD I ;I=I+1
INX H
MOV A,E ;IF I<=N GOTO L2
SUB L
MOV A,D
SBB H
JP L2
LHLD GAP
JMP L0
* Sort is all done - print entries
DONE: IF FOPT ;If output option wanted, prepare file
LDA FOPFLG
ORA A
JNZ NOOUT ;If file output, fall through with A=0
* IF ALL USER OPTION ENABLED, AND WE'RE NOT ON THE FIRST PASS, THEN THE
* OUTPUT FILE IS ALREADY OPEN AND POSITIONED, SO WE CAN SKIP THE OPEN.
; IF AOPT
lxi h,opnflg ;point to output file open flag
cmp m ;a=0, set z if opnflg=0 also
jnz noout ;if opnflg not zero, skip open
dcr m ;else, make opnflg not zero and open
; ENDIF ;Balance AOPT
* First pass on file append - prepare SD.DIR to receive new or appended
* output.
LXI D,OUTFCB ;Does output file already exist?
MVI C,SEARCH
CALL CPM
INR A
JNZ OPENIT ;If it does, then open it for processing
MVI C,MAKE ;Otherwise, create the output file
CALL CPM
INR A
JNZ NOOUT ;Continue if open successful
* If make or open fails, declare error
OPNERR: CALL ERXIT
DB 'OPE','N' OR 80H
WRTERR: CALL ERXIT
DB 'WRIT','E' OR 80H
* Output file already exists - open it and position to the last
* record of the last extent.
OPENIT: MVI C,OPEN ;Open 1st extent of output file
CALL CPM
INR A
JZ OPNERR ;Bad deal if 1st won't open
OPNMOR: LDA OUTFCB+15
CPI 128
JC LSTEXT ;If RC<128, this is last extent
LXI H,OUTFCB+12
INR M ;Else, bump to next extent
MVI C,OPEN ;..and try to open it
CALL CPM
INR A
JNZ OPNMOR ;Continue opening extents til no more
DCR M ;Then, reopen preceding extent
MVI C,OPEN
CALL CPM
LDA OUTFCB+15 ;Get RC for the last extent
* At this point, OUTFCB is opened to the last extent of the file,
* so read in the last record in the last extent.
LSTEXT: ORA A ;Is this extent empty?
JZ NOOUT ;If so, then we're starting a clean slate
DCR A ;Normalize record count
STA OUTFCB+32 ;Set record number to read
MVI C,READ ;..and read last record of file
CALL CPM
ORA A ;Was read successful?
JZ RDOK ;If so, proceed to scan for EOF mark
APERR: CALL ERXIT
DB 'APPEN','D' OR 80H
* We now have the last record in the file in our buffer.
* Scan the last record for the EOF mark, indicating where
* we can start adding data.
RDOK: LXI H,OUTBUF ;Point to start of output buffer
MVI B,128 ;Get length of output buffer
SCAN: MOV A,M
CPI 'Z'-40H ;Have we found end of file?
JZ RESCR ;If so, save pointers and reset CR
INX H
DCR B
JNZ SCAN ;Otherwise, keep looking til end of buffer
* If we find an explicit EOF mark in the last buffer (or an implied EOF
* if the last record is full), move the FCB record and extent pointers
* back to correct for the read operation so that our first write operation
* will effectively replace the last record of the SD.DIR file.
RESCR: PUSH H ;Save EOF buffer pointer
PUSH B ;Save EOF buffer remaining
LXI H,OUTFCB+32 ;Get current record again
DCR M ;Dock it
JP SAMEXT ;If CR >=0, we're still in same extent
LXI H,OUTFCB+12 ;Else, move to previous extent
DCR M
MVI C,OPEN ;Then, reopen the previous extent
CALL CPM
INR A
JZ APERR ;Append position error if we can't reopen
LDA OUTFCB+15 ;Else, position to last record of extent
DCR A
STA OUTFCB+32
SAMEXT: POP PSW ;Recall where EOF is in buffer
STA BUFCNT ;..and set buffer counter
POP H ;Recall next buffer pointer
SHLD BUFPNT ;.. and set pointer for first addition
ENDIF ;Balance FOPT
NOOUT: LXI H,ORDER ;Initialize order table pointer
SHLD NEXTT
JMP NEWLIN ;Start new line and output the files
* Output the directory files we've matched.
ENTRY: LHLD COUNT
DCX H ;Dock file count
SHLD COUNT
MOV A,H ;Is this the last file?
ORA L
JZ OKPRNT ;If COUNT=0, last file so skip compare
* Compare each entry to make sure that it isn't part of a multiple
* extent file. Go only when we have the last extent of the file.
PUSH B ;Save NPL
CALL CKABRT ;Check for abort code from keyboard
LHLD NEXTT
MVI A,11
CALL COMPR ;Does this entry match next one?
POP B ;Recall NPL
JNZ OKPRNT ;No, print it
INX H
INX H ;Skip since highest extent comes last in list
SHLD NEXTT
JMP ENTRY ;Loop back for next lowest extent
* Valid entry obtained - spit it out.
OKPRNT: LHLD NEXTT ;Get order table pointer
MOV E,M ;Get low order address
INX H
MOV D,M ;Get high order address
INX H
SHLD NEXTT ;Save updated table pointer
XCHG ;Table entry to HL
MVI B,8 ;File name length
CALL TYPEIT ;Type filename
MVI A,'.' ;Period after FN
CALL TYPE
MVI B,3 ;Display 3 characters of filetype
CALL TYPEIT
* Compute the size of the file and update our summary datum.
MOV E,M ;Get extent #
MVI D,0
INX H
MOV A,M ;Get sector count of last extent
XCHG
DAD H ;# of extents times 16k
DAD H
DAD H
DAD H
XCHG ;Save in DE
LXI H,BLKMSK
ADD M ;Round last extent to block size
RRC
RRC ;Convert from sectors to K
RRC
ANI 1FH
MOV L,A ;Add to total K
MVI H,0
DAD D
LDA BLKMSK ;Get SECTORS/BLK-1
RRC
RRC ;Convert to K/BLK
RRC
ANI 1FH
CMA ;Use to finish rounding
ANA L
MOV L,A
XCHG ;Save file size in DE
LHLD TOTSIZ
DAD D ;Add to total used
SHLD TOTSIZ
LHLD TOTFIL ;Increment file count
INX H
SHLD TOTFIL
XCHG ;Get back file size
* If report size enabled, output the size of the individual file.
IF REPSIZ ;If file size report wanted
CALL DECPRT ;..go print it
MVI A,'k' ;..and follow with K size
CALL TYPE
ENDIF
* One file output - test to see if we have to output another one.
LHLD COUNT ;Get current file counter and test it
MOV A,H
ORA L
JZ PRTOTL ;If no more files, exit to summary output
* At least one more file to output - can we put it on the current line?
DCR C
PUSH PSW
CNZ FENCE ;If room left, output the fence character
POP PSW
JNZ ENTRY ;.. and go output another file
* Current line full, start a new one.
NEWLIN: MVI C,NPL ;Reset names per line counter
CALL CRLF ;Space down to next line
IF NPL LT 4 ;If printing less than 4 wide ..
LDA FCB ;.. precede new line with drive name
ADI 'A'-1
CALL TYPE
IF REPUSR ;If reporting user numbers and running under
CALL CKVER ;..CP/M 2, output the user number too
CNC TYPUSR
ENDIF ;Balance REPUSR
MVI A,':' ;Tag header with a colon and a space
CALL FPAD ;..and exit back to ENTRY
ENDIF ;Balance NPL GT 3
JMP ENTRY ;Go back and output another file
* Print HL in decimal with leading zero suppression
DECPRT: SUB A ;Clear leading zero flag
STA LZFLG
LXI D,-1000 ;Print 1000's digit
CALL DIGIT
LXI D,-100 ;Etc.
CALL DIGIT
LXI D,-10
CALL DIGIT
MVI A,'0' ;Get 1's digit
ADD L
JMP TYPE
DIGIT: MVI B,'0' ;Start off with ASCII 0
DIGLP: PUSH H ;Save current remainder
DAD D ;Subtract
JNC DIGEX ;Quit on overflow
POP PSW ;Throw away remainder
INR B ;Bump digit
JMP DIGLP ;Loop back
DIGEX: POP H ;Restore pointer
MOV A,B
CPI '0' ;Zero digit?
JNZ DIGNZ ;No, type it
LDA LZFLG ;Leading zero?
ORA A
MVI A,'0'
JNZ TYPE ;Print digit
LDA SUPSPC ;Get space suppression flag
ORA A ;See if printing file totals
RZ ;Yes, don't give leading spaces
JMP SPACE ;Leading zero...print space
DIGNZ: STA LZFLG ;Set leading zero flag so next zero prints
JMP TYPE ;And print digit
* Show total space and files used
PRTOTL: XRA A ;Get a zero to...
STA SUPSPC ;Suppress leading spaces in totals
LHLD TOTFIL ;How many files did we match?
MOV A,H
ORA L
JZ NXTUSR ;Skip the summary if we didn't find any
PUSH H ;Save TOTFIL
STA FNDFLG ;Set file found flag
LXI D,TOTMS1 ;Print [CR,LF,LF]"DRIVE "
CALL PRINT
LDA FCB
ADI 'A'-1
CALL TYPE ;Output the drive code
IF REPUSR
CALL CKVER
JC NOUSER
LXI D,TOTMS2 ;Print ", USER "
CALL PRINT
CALL TYPUSR ;Output the user number
ENDIF
NOUSER: LXI D,TOTMS3 ;Print " CONTAINS "
CALL PRINT
LHLD TOTSIZ ;Print total K used by files matched
CALL DECPRT
LXI D,TOTMS4 ;PRINT "K IN "
CALL PRINT
POP H ;Recall TOTFIL
CALL DECPRT ;Print number of files matched
LXI D,TOTMS5 ;Print " FILES WITH "
CALL PRINT
CALL PRTFRE ;Output free space remaining & " FREE."
* Directory for one user area completed. If all users option is
* selected, then go do another directory on the next user number
* until we exceed the maximum user # for the selected drive.
NXTUSR: IF AOPT ;If all users option enabled
LDA AOPFLG ;If not all users mode - skip next
ORA A
JNZ GOCLZ
CALL CKVER ;Are we running CP/M 2?
JC GOCLZ ;Skip user increment if not
CALL CKABRT ;Check for user abort first
LDA MAXUSR ;No abort - get maximum user number
LXI H,NEWUSR ;Bump directory user number
INR M
CMP M ;Does next user # exceed maximum?
JNC SETTBL ;Continue if more user areas to go
IF DOPT ;If multi-disk option enabled
LDA BASUSR ;Reset base user number for the
MOV M,A ;..next directory search
ENDIF ;Balance DOPT
ENDIF ;Balance AOPT
* We've finished all of our outputting. Flush the remainder of the
* output buffer and close the file before going to exit routine.
GOCLZ: IF FOPT
; LDA FOPFLG ;Is file output mode active?
; ORA A
; JNZ NXTDSK ;If not, move to next drive, otherwise ..
; STA PASS1 ;Make PASS1=0 to force reopen on next drive
lxi h,opnflg ;get file open status then reset flag to
mov a,m ;..force reopen on next pass
mvi m,0
ora a
jz nxtdsk ;skip closing sd.dir if it wasn't opened
LXI H,BUFCNT
MOV A,M ;RETRIEVE # OF UNFLUSHED CHARACTERS IN BUFFER
MVI M,128 ;FORCE BUFCNT TO EMPTY STATUS FOR NEXT DRIVE
ORA A ;IF BUFCNT=128, BUFFER EMPTY SO SET SIGN BIT
jm cloze ;close sd.dir if buffer is empty
jz flush ;write last record to sd.dir if buffer full
LHLD BUFPNT ;OTHERWISE, PAD UNUSED BUFFER WITH CTRL-ZS
PUTAGN: MVI M,'Z'-40H
INX H
DCR A
JNZ PUTAGN ;CONTINUE PADDING TIL BUFFER FILLED OUT
; MVI A,128
; STA BUFCNT ;Force buffer to empty status
FLUSH: LXI D,OUTFCB ;FLUSH THE LAST OUTPUT BUFFER
MVI C,WRITE
CALL CPM
ORA A
JNZ WRTERR
CLOZE: LXI D,OUTFCB ;CLOSE THE OUTPUT FILE
MVI C,CLOSE
CALL CPM
ENDIF ;BALANCE FOPT
* Directory for all user areas completed. If the multi-disk option
* is enabled and selected, reset to the base user area and repeat
* the directory for next drive on-line until we either exceed the
* drives in our LODRV-HIDRV table, or the BDOS shuts us down with
* a select or bad sector error, which will be intercepted back to
* the EXIT module.
NXTDSK: LXI H,FNDFLG ;Get file found flag
MOV A,M
MVI M,0 ;Clear file found flag for next drive
ORA A
JNZ NDSK ;Continue if at least 1 file found
IF FOPT ;If file output enabled, disable temporarily
LXI H,FOPFLG
DCR M
PUSH H
ENDIF
LDA FCB ;Stash ASCII directory drive in NO FILE msg
ADI 'A'-1
STA NOFMS2
LXI D,NOFMS1 ;Print "NO FILE ON ? - "
CALL PRINT
CALL PRTFRE ;Tag with free message
IF FOPT ;Restore original file output mode
POP H
INR M
ENDIF
NDSK: IF DOPT ;If multi-disk option enabled
LDA DOPFLG ;If multi-disk not selected - skip next
ORA A
JNZ EXIT
CALL CKABRT ;Check for user abort first
MVI A,HIDRV-LODRV ;Get maximum drive code to search
LXI H,FCB ;Bump directory FCB drive code
INR M
CMP M ;Does next disk exceed maximum?
JNC NOOPT ;Search next disk if not
ENDIF ;Balance DOPT
JMP EXIT ;All done - exit to CCP
* Print the user number of the directory in decimal
TYPUSR: IF REPUSR
LDA NEWUSR
CPI 10 ;If user no. > 9 print leading 1
JC DUX
MVI A,'1'
CALL TYPE
LDA NEWUSR ;Print low digit of user no.
SUI 10
DUX: ADI '0'
JMP TYPE
ENDIF
* Force new line on video and check for page pause
CRLF: MVI A,0DH ;Send CR
CALL TYPE
MVI A,0AH ;Send LF
JMP TYPE ;Exit to caller from TYPE
* Separate the directory output on a line with a space, the delimiter,
* followed by another space.
FENCE: CALL SPACE
MVI A,DELIM ;Fence character
FPAD: CALL TYPE ;Print it, fall into space
SPACE: MVI A,' ' ;Fall through to TYPE
* Output character in A to console, and optionally to printer and/or
* the output file.
TYPE: PUSH B
PUSH D
PUSH H
PUSH PSW ;Save the character to output
CALL TYPE1 ;Send it to console
POP PSW ;Restore the output character
ANI 7FH ;Strip parity bit on character
* Test file output mode and skip to page pause test if not active.
IF FOPT
MOV B,A ;Save stripped character to B
LDA FOPFLG ;Is file output active?
ORA A
JNZ NOWRIT ;Go check for page pause if not
* File output mode active - make sure we have room in buffer to add
* next character. If buffer full, write out current record first
* and then start a new record with current character.
LHLD BUFPNT ;Get current buffer pointer
LDA BUFCNT ;Get buffer capacity remaining
ORA A
JNZ PUTBUF ;Continue if buffer not full
LXI D,OUTFCB ;Otherwise, write the current buffer out
MVI C,WRITE
CALL CPM ;(Note call must save character in B)
ORA A
JNZ WRTERR ;Take write error exit if disk full or R/O
LXI H,OUTBUF ;Reset buffer pointer
MVI A,128 ;Reset buffer capacity
PUTBUF: MOV M,B ;Shove character to next buffer position
INX H ;Bump buffer pointer
SHLD BUFPNT ;.. and save it
DCR A ;Dock count of characters left in buffer
STA BUFCNT ;..and save it
NOWRIT: MOV A,B ;Recall stripped character
ENDIF ;Balance FOPT
IF POPT ;If printer option
ANI 7FH ;Strip parity bit on character
MOV E,A ;Setup list output call
MVI C,5
LDA POPFLG ;Test printer flag
ORA A
CZ CPM ;Print character if flag true
MOV A,E ;Recall character
ENDIF
IF PGPAWZ
CPI 0AH ;Do we have a LF?
JNZ TYPRET ;Exit if not
ENDIF
IF NOPT AND PGPAWZ
LDA NOPFLG ;Is the page pause function disabled?
ORA A
JZ TYPRET ;Exit if so
ENDIF
IF PGPAWZ
LDA LINCNT ;Get line count
INR A ;Bump it
CPI LPS ;Are we at the end of the screen?
JC NOTEOS ;Skip if not
LXI D,EOSMSG ;Else, display pause message
MVI C,9 ;..without checking for LFs
CALL BDOS
CALL CINPUT ;Wait for character
CPI 'C'-40H
JZ EXIT ;Abort on CTRL-C
XRA A ;Reset line count
NOTEOS: STA LINCNT ;Save new line count
ENDIF
TYPRET: POP H ;Exit from TYPE
POP D
POP B
RET
* Output character
TYPE1: IF VECTOR
ORA A ;Set sign flag if MS bit is on
JP TYPE2 ;If character is ASCII, continue
MOV B,A ;Else, get character to B
MVI A,5 ;Set video driver function for direct output
JMP VIDEO ;Display in reverse video and exit from VIDEO
ENDIF
TYPE2: IF REVIDEO
ORA A
JP TYPE99
PUSH PSW
MVI A,LEADIN ;Send ESC char first
CALL TYPE99
MVI A,INTOREV
CALL TYPE99
POP PSW ;Retrieve character
ANI 7FH
CALL TYPE99
MVI A,LEADIN
CALL TYPE99
MVI A,OUTAREV
TYPE99:
ENDIF ;Balance REVIDEO
IF DIRCON
MOV C,A ;Get character into BIOS entry register
LHLD BASE+1 ;Get page base of BIOS
MVI L,12 ;Get entry vector to CONOUT
JMP GOHL ;Call CONOUT direct through the BIOS
ELSE
MOV E,A ;Get character into BDOS entry register
MVI C,WRCHR
JMP BDOS ;Call CONOUT via the BDOS
ENDIF ;Balance DIRCON
* Print a string at HL of length B
TYPEIT: MOV A,M
CALL TYPE
INX H
DCR B
JNZ TYPEIT
RET
* Print string terminated with last byte high on console.
PRINT: LDAX D
PUSH PSW
ANI 7FH
CALL TYPE
POP PSW
ORA A
RM
INX D
JMP PRINT
* Fetch character from console (without echo)
CINPUT: LHLD BASE+1
MVI L,9
CALL GOHL
ANI 7FH
RET
* Check for a CTRL-C or CTRL-S entered from the keyboard. Jump to
* exit if CTRL-C, pause on CTRL-S.
CKABRT: LHLD BASE+1
MVI L,6 ;Check status of keyboard
CALL GOHL ;Any key pressed?
ORA A
RZ ;No, return to caller
CALL CINPUT ;Get character
CPI 'C'-40H ;CTRL-C?
JZ EXIT ;If CTRL-C then quit
CPI 'S'-40H ;CTRL-S?
RNZ ;No, return to caller
CALL CINPUT ;Yes, wait for another char.
CPI 'C'-40H ;Might be CTRL-C
JZ EXIT ;Exit if CTRL-C, else fall thru and continue
RET
* Kludge to allow call to address in HL
GOHL: PCHL
* Entry to BDOS saving all extended registers
CPM: PUSH B
PUSH D
PUSH H
CALL BDOS
POP H
POP D
POP B
RET
* For file output mode, return to old user area and set DMA for
* the file output buffer.
SETFOP: IF UOPT OR AOPT
CALL CKVER ;Clear carry if CP/M 2 or later
LDA OLDUSR ;Get user number at startup
MOV E,A
MVI C,CURUSR
CNC CPM ;Reset the old user number if CP/M 2
ENDIF
IF FOPT
LXI D,OUTBUF ;Move DMA from search buffer into the
JMP SET2 ;..output buffer
ENDIF
RET
* Move disk buffer DMA to default buffer for directory search operations
* and BDOS media change routines (necessary for pre-CP/M 2 systems while
* in file output mode with an active buffer).
SETSRC: LXI D,BASE+80H
SET2: MVI C,SETDMA
JMP CPM
* Print the amount of free space remaining on the selected drive
PRTFRE: LHLD FREEBY ;Get space left before adding to MASTER.DIR
CALL DECPRT ;Print K free
LXI D,TOTMS6 ;Print " FREE."
JMP PRINT
* Compare routine for sort
COMPR: PUSH H ;Save table addr
MOV E,M ;Load low order
INX H
MOV D,M ;Load high order
INX H
MOV C,M
INX H
MOV B,M
* BC, DE now point to entries to be compared
XCHG
MOV E,A ;Get count
CMPLP: MOV A,M
ANI 7FH
MOV D,A
LDAX B
ANI 7FH
CMP D
INX H
INX B
JNZ NOTEQL ;Quit on mismatch
DCR E ;Or end of count
JNZ CMPLP
NOTEQL: POP H
RET ;Cond code tells all
* Swap entries in the order table
SWAP: LXI B,ORDER-2 ;Table base
DAD H ;*2
DAD B ;+ base
XCHG
DAD H ;*2
DAD B ;+ base
MOV C,M
LDAX D
XCHG
MOV M,C
STAX D
INX H
INX D
MOV C,M
LDAX D
XCHG
MOV M,C
STAX D
RET
* New compare routine
COMPARE:LXI B,ORDER-2
DAD H
DAD B
XCHG
DAD H
DAD B
XCHG
MOV C,M
INX H
MOV B,M
XCHG
MOV E,M
INX H
MOV D,M
XCHG
MOV E,A ;Count
CMPLPE: MOV A,M
ANI 7FH
MOV D,A
LDAX B
ANI 7FH
CMP D
INX B
INX H
RNZ
DCR E
JNZ CMPLPE
RET
* Error exit
ERXIT: IF FOPT
MVI A,-1
STA FOPFLG ;Disable file output mode on error
ENDIF
CALL CRLF ;Space down
POP D ;Get pointer to message string
CALL PRINT ;Print it
LXI D,ERRMS1 ;Print " ERROR"
CALL PRINT
CALL CRLF ;Space down
* Exit - all done, restore stack
EXIT: MVI C,CONST ;Check console status
CALL CPM
ORA A ;Char waiting?
MVI C,RDCHR
CNZ CPM ;Gobble up char
IF DOPT ;Restore BDOS intercept vectors
LDA DOPFLG ;..if they were swapped
ORA A
CZ SWAPEM
ENDIF
IF ROPT
LDA ROPFLG ;If disk system was reset
ORA A
LDA OLDDSK ;Get default disk at startup
MOV E,A
MVI C,SELDSK ;Reselect it
CZ CPM
ENDIF ;Balance ROPT
LHLD STACK ;Get old stack pointer
SPHL ;Move back to old stack
RET ;..and return to CCP
* Exchange BDOS select and sector error vectors for our own intercept
* vectors so we can catch a reference to an illegal drive.
IF DOPT
SWAPEM: LHLD BDOS+1 ;Get pointer to base of BDOS
MOV A,L
SUI 6 ;Check if pointing directly to BDOS
JZ SWAPOK ;Continue if true
MVI A,'d' ;Undo option request for multi-disk
STA DOPFLG
SWAPOK: MVI L,9 ;Point to sector error vector
LXI D,VECTBL ;Exchanging with our own vector table
MVI A,4 ;4 bytes to swap
SWAPLP: MOV B,M ;Get byte from HL
XCHG
MOV C,M ;Get byte from DE
MOV M,B ;Put byte from HL
XCHG
MOV M,C ;Put byte from DE
INX H ;Bump exchange pointers
INX D
DCR A ;Dock counter
JNZ SWAPLP ;Continue swapping til done
RET
ENDIF
* Check CP/M version number. Return carry flag set if pre-CP/M 2.
* If CP/M 2 or later or MP/M (any version), return carry clear.
CKVER: LDA VERFLG
CPI 20H
RET
* Recovery point from intercepted BDOS select and bad sector errors.
DSKERR: IF DOPT
LXI SP,STACK ;Get out of BDOS' stack
JMP EXIT ;..and exit back to CCP
ENDIF
***********************
* *
* End of Program Code *
* *
***********************
* Initialized data area
DREMSG: DB 'Driv','e' OR 80H
IF PGPAWZ
EOSMSG: DB '[ More ]',0DH,'$'
ENDIF
ERRMS1: DB ' '
ERRMS2: DB 'ERRO','R' OR 80H
IF REPERR
ERRTAG: DB ' -','>' OR 80H
ENDIF
NOFMS1: DB 0DH,0AH,'NO FILE on '
NOFMS2: DB ' -',' ' OR 80H
TOTMS1: DB 0DH,0AH,' Drive',' ' OR 80H
IF REPUSR
TOTMS2: DB ', user',' ' OR 80H
ENDIF
TOTMS3: DB ' contains',' ' OR 80H
TOTMS4: DB 'K in',' ' OR 80H
TOTMS5: DB ' files with',' ' OR 80H
TOTMS6: DB 'K fre','e' OR 80H
USRMSG: DB 'User ','#' OR 80H
FNDFLG: DB 0 ;Flag whether any files matched
IF PGPAWZ
LINCNT: DB 0 ;Count of lines printed on screen
ENDIF
* Drive code/user area lookup table
* Note that the LODRV-HIDRV table is included here fully configured.
* For your own use, you should change the maximum user areas as
* appropriate for each drive on your system, and then delete any
* DBs referencing drives that don't exist. Note also that there
* are only 16 user areas available under CP/M 2, so the highest
* legal user area you can specify is 15 (range 0-15 = 16 areas).
* The program will convert anything over 15 into mod 15.
LODRV EQU $ ;Mark beginning of drive/user table
DB 15 ;Maximum user area for Drive A
DB 15 ; " " " " " B
DB 15 ; " " " " " C
; DB 15 ; " " " " " D
; DB 15 ; " " " " " E
; DB 15 ; " " " " " F
; DB 15 ; " " " " " G
; DB 15 ; " " " " " H
; DB 15 ; " " " " " I
; DB 15 ; " " " " " J
; DB 15 ; " " " " " K
; DB 15 ; " " " " " L
; DB 15 ; " " " " " M
; DB 15 ; " " " " " N
; DB 15 ; " " " " " O
; DB 15 ; " " " " " P
HIDRV EQU $ ;Mark end of drive/user table
* Option field lookup table.
* Note that you can force any of these options as a DEFAULT by
* changing the letter for the option into a zero (assuming that
* its enabling equate is true). Each option that you hard-wire in
* this manner will no longer be recognized as a command line OPTION,
* and if you redundantly key it in, SD will flag it as unrecognized.
OTBL EQU $ ;Mark start of option table
IF AOPT ;All users-option flag
AOPFLG: DB 'A'
ENDIF
IF DOPT ;Multi-disk-option flag
DOPFLG: DB 'D'
ENDIF
IF FOPT ;File-output-option flag
FOPFLG: DB 'F'
ENDIF
IF NOPT AND PGPAWZ ;No page-pause option flag
NOPFLG: DB 'N'
ENDIF
IF POPT ;Printer option flag
POPFLG: DB 'P'
ENDIF
IF ROPT ;Reset option flag
ROPFLG: DB 'R'
ENDIF
IF SOPT ;System file option flag
SOPFLG: DB 'S'
ENDIF
OEND EQU $ ;Mark end of option table
* End of option lookup table
; IF AOPT
;PASS1: DB 0 ;First pass flag for all user file output
; ENDIF
IF DOPT
VECTBL: DW DSKERR ;BDOS SECTOR ERROR INTERCEPT VECTOR
DW DSKERR ;BDOS SELECT ERROR INTERCEPT VECTOR
ENDIF
IF FOPT
BUFPNT: DW OUTBUF ;POINTER TO NEXT LOCATION IN OUTPUT BUFFER
BUFCNT: DB 128 ;NUMBER OF BYTES LEFT IN OUTPUT BUFFER
opnflg: db 0 ;file open flag for all user file output
OUTFCB: DB 0,'SD DIR'
FILL 21,0 ;REST OF SD.DIR FCB
OUTBUF DS 128 ;OUTPUT FILE BUFFER
ENDIF
* Uninitialized data area
BASUSR DS 1 ;Dupe of original directory user # to search
BLKMAX DS 2 ;Highest block # on drive
BLKMSK DS 1 ;SEC/BLK - 1
BLKSHF DS 1 ;# shifts to mult by SEC/BLK
COUNT DS 2 ;Entry count
DIRMAX DS 2 ;Highest file # in directory
FREEBY DS 2 ;Contains number of K left on directory drive
GAP DS 2 ;Sort routine storage
I DS 2 ;Sort routine storage
J DS 2 ;Sort routine storage
JG DS 2 ;Sort routine storage
LZFLG DS 1 ;0 when printing leading zeros
MAXUSR DS 1 ;Maximum user # for drive from lookup table
NEWUSR DS 1 ;Contains user number selected by "$U" option
NEXTT DS 2 ;Next table entry
OLDDSK DS 1 ;Holder for currently logged-in drive
OLDUSR DS 1 ;Contains user number upon invocation
SCOUNT DS 2 ;# to sort
SUPSPC DS 1 ;Leading space flag for decimal routine
TBLOC DS 2 ;Pointer to start of name table
TEMP DS 2 ;Save dir entry
TOTFIL DS 2 ;Total number of files
TOTSIZ DS 2 ;Total size of all files
VERFLG DS 1 ;CP/M version number (0=pre-CP/M 2)
DS 60 ;Stack area
STACK DS 2 ;Save old stack pointer here
ORDER EQU $ ;Order table starts here
END