home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
UTILS
/
DIRUTL
/
RENAME.LBR
/
RENAME.AQM
/
RENAME.ASM
Wrap
Assembly Source File
|
2000-06-30
|
26KB
|
1,182 lines
* PROGRAM: RENAME
* AUTHOR: RICHARD CONN
* VERSION: 1.1
* DATE: 26 OCT 81
* PREVIOUS VERSIONS: 1.0 (26 OCT 81)
VERS EQU 11 ; Version Number
*
* RENAME --
* RENAME is used to change the name of one or more files. Unlike
* the CCP-resident REN function, RENAME permits ambiguous file names and
* supports an Inspect mode that allows the user to confirm each rename
* before it is done.
*
* RENAME supports the following forms:
* RENAME afn1=afn2 <-- Normal Rename
* RENAME afn1=afn2 /I <-- Rename with Inspect
* RENAME afn1=afn2 /S <-- Include System Files
* RENAME afn1=afn2 /I/S <-- Combine I and S Options
*
* Examples:
* RENAME *.MAC=*.ASM <-- Rename all ASM files to MAC
* RENAME *.MAC=*.* /I <-- Rename selected files to MAC
* RENAME *.OBJ=*.COM /S <-- Rename all COM files to OBJ
* (incl System Files)
*
*
* REN CONSTANTS
*
DELIM EQU '/' ; OPTION DELIMITER CHAR
INSP$OPT EQU 'I' ; OPTION LETTER FOR INSPECTION
SYS$OPT EQU 'S' ; OPTION LETTER FOR SYSTEM FILES
ENTRY$SIZE EQU 12 ; NUMBER OF BYTES/DIRECTORY ENTRY STORED
*
* CP/M CONSTANTS
*
BDOS EQU 5 ; BDOS ENTRY
FCB EQU 5CH ; FIRST FCB
FCB2 EQU 5CH+16 ; 2ND FCB
TBUFF EQU 80H ; INPUT LINE
CR EQU 0DH ; <CR>
LF EQU 0AH ; <LF>
ORG 100H
*
* SAVE OLD STACK PTR AND SET NEW
*
LXI H,0 ; SAVE STACK PTR
DAD SP
SHLD STACK ; SAVE SP IN BUFFER
LXI SP,STACK ; RESET STACK PTR
*
* PRINT PROGRAM NAME
*
CALL PRINT$MESSAGE
DB 'RENAME Version '
DB VERS/10+'0','.',(VERS MOD 10)+'0'
DB 0
*
* CHECK FOR USER-SPECIFIED DRIVE AND LOG IN IF SELECTED
*
LDA FCB ; GET FROM FCB BYTE
STA UDRIVE ; SET FLAG
ORA A ; 0=DEFAULT
JZ REN1
DCR A ; ADJUST FOR LOGIN
PUSH PSW ; SAVE A
MVI C,25 ; GET CURRENT DISK
CALL BDOS
INR A ; ADJUST TO 1-16
STA UDRIVE ; SET FLAG
POP PSW ; GET NEW DISK
MOV E,A ; NUMBER IN E
MVI C,14 ; SELECT DRIVE
CALL BDOS
JMP REN1
*
* RETURN TO OS
*
RETURN:
LDA UDRIVE ; GET SELECTED DRIVE
ORA A ; 0=DEFAULT
JZ RETURN1
DCR A ; ADJUST TO 0-15
MOV E,A
MVI C,14 ; SELECT DISK
CALL BDOS
RETURN1:
LHLD STACK ; GET ORIGINAL STACK PTR
SPHL ; SET IT
RET ; RETURN TO OS
*
* CONTINUE PROCESSING
*
REN1:
XRA A ; A=0
STA INSP$FLAG ; CLEAR INSPECT FLAG
STA SYS$FLAG ; CLEAR SYSTEM FLAG
LXI H,TBUFF ; PT TO INPUT LINE
MOV B,M ; CHAR COUNT IN B
MOV A,B ; CHECK FOR EMPTY LINE
ORA A ; 0 CHARS = HELP
JNZ REN2
*
* PRINT REN HELP MESSAGE
*
REN$HELP:
CALL PRINT$MESSAGE
DB CR,LF,'RENAME is invoked by a command of the form:'
DB CR,LF,' RENAME afn1=afn2 <-- Rename all matches'
DB CR,LF,' \ \__ Old File Name'
DB CR,LF,' \__ New File Name'
DB CR,LF,' RENAME afn1=afn2 /I <-- Inspect mode'
DB CR,LF,' RENAME afn1=afn2 /S <-- Include System Files'
DB CR,LF,'Note: /I and /S Options may be combined'
DB CR,LF
DB CR,LF,' Examples:'
DB CR,LF,' RENAME *.MAC=*.ASM <-- Rename *.ASM to *.MAC'
DB CR,LF,' RENAME *.MAC=*.* /I <-- Rename *.* to *.MAC with'
DB ' inspection'
DB CR,LF,' RENAME *.OBJ=*.COM /I/S <-- Rename *.COM to *.OBJ '
DB 'with both I and S'
DB 0
JMP RETURN
*
* CONTINUE PROCESSING
*
REN2:
INX H ; PT TO FIRST CHAR
MOV A,M ; GET IT
CPI DELIM ; OPTION?
JNZ REN3
DCR B ; COUNT DOWN
JZ REN$HELP
INX H ; PT TO NEXT
MOV A,M ; GET OPTION CHAR
CPI SYS$OPT ; INCLUDE SYSTEM FILES?
JZ REN$SYS
CPI INSP$OPT ; INSPECT?
JNZ REN$HELP ; HELP OTHERWISE
MVI A,0FFH ; SET FLAG
STA INSP$FLAG
JMP REN3
REN$SYS:
MVI A,0FFH ; SET FLAG
STA SYS$FLAG
REN3:
DCR B ; COUNT DOWN
JNZ REN2
INX H ; PT TO AFTER LAST CHAR
MVI M,0 ; STORE ENDING 0
CALL EXTRACT$SRC ; LOAD 2ND FILE NAME INTO FCB2
*
* CHECK FOR FILE NAME SPECIFIED
*
LDA FCB+1 ; GET FIRST LETTER OF FILE NAME
CPI DELIM ; DELIMITER CAUGHT?
JZ FN$ERR
CPI ' ' ; NO FILE SPECIFIED?
JZ FN$ERR
LDA FCB2+1 ; GET FIRST BYTE OF NAME
CPI DELIM ; OPTION CAUGHT?
JZ FN$ERR
CPI ' ' ; EMPTY?
JZ FN$ERR
JMP REN4
FN$ERR:
CALL PRINT$MESSAGE
DB CR,LF,'ERROR -- File Name not specified'
DB CR,LF,' Error FCB: ',0
LXI H,FCB ; PRINT ERROR
CALL PRINT$FN
MVI A,'='
CALL CHAR$OUT
LXI H,FCB2
CALL PRINT$FN
JMP RETURN
*
* COPY 2ND FCB INTO DESTINATION BUFFER
*
REN4:
LXI H,FCB2 ; PT TO FCB2
LXI D,FCB$SRC ; PT TO FCB$SRC
MVI B,12 ; COPY 12 BYTES
CALL MOVE
XCHG ; FILL IN REST
MVI B,24 ; EMPTY
XRA A ; A=0
CALL FILL
*
* ALL SET TO GO --
* FCB CONTAINS FILE NAME/TYPE
* FCB$SRC CONTAINS SOURCE FILE NAME/TYPE
* INSP$FLAG IS SET CORRECTLY
*
* LOAD DIRECTORY INTO DIR1 BUFFER
DIR:
LXI H,ENDALL ; PT TO END OF PROGRAM
SHLD DIR1 ; AND SET PTR TO DIR1
LXI H,0 ; HL=0
SHLD FILE$COUNT ; TOTAL SELECTED FILES = 0
DIR$USER:
MVI C,17 ; SEARCH FOR FILE
LXI D,FCB$SRC ; PT TO FILE NAME
CALL BDOS
CPI 255 ; NO MATCH?
JZ DIR$LOOP1
DIR$LOOP:
CALL PUT$ENTRY ; PLACE ENTRY IN DIR
MVI C,18 ; SEARCH FOR NEXT MATCH
CALL BDOS
CPI 255 ; DONE?
JNZ DIR$LOOP
* CHECK FOR ANY SELECTIONS
DIR$LOOP1:
LHLD FILE$COUNT ; GET COUNT
MOV A,H ; ZERO?
ORA L
JNZ COMP$ORDER
CALL PRINT$MESSAGE
DB CR,LF,'No Files Selected -- Aborting'
DB CR,LF,' Selected FCB: ',0
LXI H,FCB$SRC ; PRINT FILE NAME
CALL PRINT$FN
JMP RETURN
* COMPUTE POINTER TO ORDER TABLE
COMP$ORDER:
MVI B,ENTRY$SIZE-1 ; B=NUMBER OF BYTES/ENTRY-1
MOV D,H ; DE=HL=NUMBER OF ENTRIES
MOV E,L
COMP$ORDER$LOOP:
DAD D ; HL=HL+DE
DCR B ; COUNT DOWN
JNZ COMP$ORDER$LOOP
XCHG ; DE=NUMBER OF BYTES OCCUPIED BY ENTRIES
LHLD DIR1 ; HL PTS TO FIRST ENTRY
DAD D ; HL PTS TO AFTER LAST ENTRY
INR H ; HL PTS TO NEXT PAGE
MVI L,0
SHLD ORDER ; ORDER PTR SET
* ALPHABETIZE DIRECTORY ENTRIES
CALL ALPHABETIZE
* SET RENECTION ATTRIBUTES
CALL RENAME
* RETURN TO CP/M
JMP RETURN
*
* EXTRACT SOURCE FILE NAME AND PLACE IN FCB2
*
EXTRACT$SRC:
LXI H,FCB2 ; CLEAR FCB2
MVI M,0 ; STORE BEGINNING ZERO
INX H ; PT TO FIRST BYTE OF FILE NAME
MVI A,' ' ; <SP> FILL
MVI B,11 ; 11 BYTES
CALL FILL
* LOOK FOR = DELIMITER
LXI H,TBUFF ; LOOK FOR =
ES1:
MOV A,M ; GET CHAR
INX H ; PT TO NEXT
ORA A ; ERROR IF END OF LINE
JZ FORMAT$ERR
CPI '=' ; EQUAL?
JNZ ES1
* PLACE FILE NAME INTO FCB2
LXI D,FCB2+1 ; PT TO FIRST CHAR OF FCB2 FILE NAME
MVI B,8 ; UP TO 8 CHARS
CALL PUT$CHARS ; COPY HL TO DE
CPI '.' ; MUST BE SEPARATED BY DECIMAL DELIMITER
RNZ
LXI D,FCB2+9 ; PT TO FIRST CHAR OF FCB2 FILE TYPE
MVI B,3 ; UP TO 3 CHARS
CALL PUT$CHARS ; COPY HL TO DE
RET
* FORMAT ERROR MESSAGE
FORMAT$ERR:
CALL PRINT$MESSAGE
DB CR,LF,'Format Error -- Missing =',0
JMP RETURN
*
* COPY HL TO DE FOR UP TO B BYTES
* RECOGNIZE DELIMITERS OF '.', ' ', AND 0
* EXPAND *
*
PUT$CHARS:
MOV A,M ; GET CHAR
INX H ; PT TO NEXT
CPI '.' ; DELIMITER?
RZ
CPI ' ' ; DELIMITER?
RZ
ORA A ; DELIMITER?
RZ
CPI '*' ; EXPAND?
JZ PUT$CHARSX
STAX D ; STORE CHAR
INX D ; PT TO NEXT
DCR B ; COUNT DOWN
JNZ PUT$CHARS
MOV A,M ; GET NEXT CHAR
INX H ; PT TO CHAR AFTER
RET
PUT$CHARSX:
MVI A,'?' ; '?' FILL
STAX D ; PUT CHAR
INX D ; PT TO NEXT
DCR B ; COUNT DOWN
JNZ PUT$CHARSX
MOV A,M ; GET CHAR AFTER '*'
INX H ; PT TO CHAR AFTER THAT
RET
*
* PLACE ENTRY IN DIR1 IF:
* 1 -- NOT AN ERASED ENTRY
* 2 -- SELECTED USER NUMBER
* 3 -- MATCHES SPECIFICATION FCB
* 4 -- ATTRIBUTES CORRESPOND
*
* ON INPUT, A=0-3 FOR ADR INDEX IN BUFF OF ENTRY FCB
* FILE$COUNT=NUMBER OF SELECTED FILES
* ON OUTPUT, FILE$COUNT=NUMBER OF SELECTED FILES
*
PUT$ENTRY:
PUSH PSW ! PUSH B ! PUSH D ! PUSH H
RRC ; MULTIPLY BY 32 FOR OFFSET COMPUTATION
RRC
RRC
ANI 60H ; A=BYTE OFFSET
LXI D,TBUFF ; PT TO BUFFER ENTRY
MOV L,A ; LET HL=OFFSET
MVI H,0
DAD D ; HL=PTR TO FCB
MOV A,M ; GET USER NUMBER
CPI 0E5H ; DELETED?
JZ PE4 ; SKIP IT IF DELETED
XCHG ; DE=PTR TO FCB
PUSH D ; SAVE IT
LHLD FILE$COUNT ; GET NUMBER OF ENTRIES SO FAR
SHLD ECOUNTER
MOV A,H ; NONE?
ORA L ; ZERO FLAG SET IF SO
LHLD DIR1 ; PT TO DIR1
JZ PE2 ; IF NO ENTRIES, THIS IS THE FIRST
LXI D,ENTRY$SIZE ; HL PTS TO DIR1 BASE, DE=NUMBER OF BYTES/ENTRY
PE1:
DAD D ; PT TO NEXT
CALL ECOUNT ; ECOUNTER=ECOUNTER-1
JNZ PE1
PE2:
POP D ; DE PTS TO FCB TO PLACE IN DIR1
XCHG
*
* ON INPUT, DE=ADR TO PLACE ENTRY IN DIR1
* HL=ADR OF FCB IN BUFF
*
* COMPARE ENTRY AGAINST FILE SELECTION FCB
PE2$COMP:
PUSH H ; SAVE HL, DE PTRS
PUSH D
MVI B,11 ; 11 BYTES IN FILE NAME AND FILE TYPE
LXI D,FCB$SRC+1 ; PT TO FILE NAME IN FCB
INX H ; PT TO FILE NAME OF ENTRY
* COMPARISON LOOP
PE2$COMP1:
LDAX D ; GET FCB BYTE
ANI 7FH ; MASK MSB
CPI '?' ; WILD?
JZ PE2$COMP2
MOV C,A ; SAVE BYTE
MOV A,M ; GET ENTRY BYTE
ANI 7FH ; MASK MSB
CMP C ; COMPARE
JZ PE2$COMP2 ; MATCH
POP D ; RESTORE DE, HL
POP H
JMP PE4 ; ABORT
PE2$COMP2:
INX H ; PARTIAL MATCH -- PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ PE2$COMP1
POP D ; RESTORE DE, HL
POP H
* ENTRY COMPLETELY ACCEPTED -- HL PTS TO ENTRY, DE PTS TO DIRECTORY
PE2$COPY:
PUSH H ; SAVE PTR
LXI B,12 ; CHECK FOR ZERO EXTENT
DAD B ; HL PTS TO EXTENT
MOV A,M ; GET EXTENT
POP H ; RESTORE HL
ORA A ; ZERO?
JNZ PE4 ; ABORT IF NOT
* CHECK FOR SYSTEM FILE INCLUSION AND DO OR DON'T DEPENDING
LDA SYS$FLAG ; GET FLAG
ORA A ; 0=NO
JNZ PE3 ; INCLUDE ALL IF YES
PUSH H ; SAVE PTR
LXI B,10 ; CHECK FOR SYSTEM ATTRIBUTE
DAD B ; HL PTS TO SYSTEM ATTRIBUTE
MOV A,M ; GET ATTRIBUTE BYTE
POP H ; RESTORE PTR
ANI 80H ; SELECT BIT
JNZ PE4 ; ABORT IF SYSTEM ATTRIBUTE SET
* FILE ACCEPTED -- COPY IT
PE3:
MVI B,ENTRY$SIZE ; B=NUMBER OF BYTES/ENTRY
CALL MOVE ; COPY INTO DIRECTORY
* INCREMENT FILE COUNT
LHLD FILE$COUNT ; INCREMENT FILE COUNT
INX H
SHLD FILE$COUNT
* DONE WITH PUT$ENTRY
PE4:
POP H ! POP D ! POP B ! POP PSW
RET
*
* COUNT DOWN WITH 16-BIT COUNTER ECOUNTER; SET ZERO FLAG IF IT HITS ZERO
*
ECOUNT:
PUSH H ; SAVE HL
LHLD ECOUNTER ; GET COUNT
DCX H ; COUNT DOWN
SHLD ECOUNTER ; NEW COUNT
MOV A,H ; ZERO?
ORA L ; ZERO FLAG SET IF SO
POP H ; RESTORE HL
RET
*
* ALPHABETIZE -- ALPHABETIZES DIR1; FILE$COUNT CONTAINS
* THE NUMBER OF FILES IN DIR1
*
ALPHABETIZE:
LHLD FILE$COUNT ; GET FILE COUNT
MOV A,H ; ANY ENTRIES?
ORA L
RZ
*
* SHELL SORT --
* THIS SORT ROUTINE IS ADAPTED FROM "SOFTWARE TOOLS"
* BY KERNIGAN AND PLAUGHER, PAGE 106. COPYRIGHT, 1976, ADDISON-WESLEY.
* ON ENTRY, HL=NUMBER OF ENTRIES
*
SORT:
MOV B,H ; COUNT IN BC
MOV C,L
LHLD DIR1 ; SET UP POINTERS TO DIRECTORY ENTRIES
XCHG ; ... IN DE
LHLD ORDER ; PT TO ORDER TABLE
*
* SET UP ORDER TABLE; HL PTS TO NEXT ENTRY IN ORDER TABLE, DE PTS TO NEXT
* ENTRY IN DIRECTORY, BC = NUMBER OF ELEMENTS REMAINING
*
SORT1:
MOV M,E ; STORE LOW-ORDER ADDRESS
INX H ; PT TO NEXT ORDER BYTE
MOV M,D ; STORE HIGH-ORDER ADDRESS
INX H ; PT TO NEXT ORDER ENTRY
PUSH H ; SAVE PTR
LXI H,ENTRY$SIZE ; HL=NUMBER OF BYTES/ENTRY
DAD D ; PT TO NEXT DIR1 ENTRY
XCHG ; DE PTS TO NEXT ENTRY
POP H ; GET PTR TO ORDER TABLE
DCX B ; COUNT DOWN
MOV A,B ; DONE?
ORA C
JNZ SORT1
*
* THIS IS THE MAIN SORT LOOP FOR THE SHELL SORT IN "SOFTWARE TOOLS" BY K&P
*
*
* SHELL SORT FROM "SOFTWARE TOOLS" BY KERNINGHAN AND PLAUGER
*
LHLD FILE$COUNT ; NUMBER OF ITEMS TO SORT
SHLD GAP ; SET INITIAL GAP TO N FOR FIRST DIVISION BY 2
* FOR (GAP = N/2; GAP > 0; GAP = GAP/2)
SRT$LOOP0:
ORA A ; CLEAR CARRY
LHLD GAP ; GET PREVIOUS GAP
MOV A,H ; ROTATE RIGHT TO DIVIDE BY 2
RAR
MOV H,A
MOV A,L
RAR
MOV L,A
* TEST FOR ZERO
ORA H
JZ SORT$DONE ; DONE WITH SORT IF GAP = 0
SHLD GAP ; SET VALUE OF GAP
SHLD I ; SET I=GAP FOR FOLLOWING LOOP
* FOR (I = GAP + 1; I <= N; I = I + 1)
SRT$LOOP1:
LHLD I ; ADD 1 TO I
INX H
SHLD I
* TEST FOR I <= N
XCHG ; I IS IN DE
LHLD FILE$COUNT ; GET N
MOV A,L ; COMPARE BY SUBTRACTION
SUB E
MOV A,H
SBB D ; CARRY SET MEANS I > N
JC SRT$LOOP0 ; DON'T DO FOR LOOP IF I > N
LHLD I ; SET J = I INITIALLY FOR FIRST SUBTRACTION OF GAP
SHLD J
* FOR (J = I - GAP; J > 0; J = J - GAP)
SRT$LOOP2:
LHLD GAP ; GET GAP
XCHG ; ... IN DE
LHLD J ; GET J
MOV A,L ; COMPUTE J - GAP
SUB E
MOV L,A
MOV A,H
SBB D
MOV H,A
SHLD J ; J = J - GAP
JC SRT$LOOP1 ; IF CARRY FROM SUBTRACTIONS, J < 0 AND ABORT
MOV A,H ; J=0?
ORA L
JZ SRT$LOOP1 ; IF ZERO, J=0 AND ABORT
* SET JG = J + GAP
XCHG ; J IN DE
LHLD GAP ; GET GAP
DAD D ; J + GAP
SHLD JG ; JG = J + GAP
* IF (V(J) <= V(JG))
CALL ICOMPARE ; J IN DE, JG IN HL
* ... THEN BREAK
JC SRT$LOOP1
* ... ELSE EXCHANGE
LHLD J ; SWAP J, JG
XCHG
LHLD JG
CALL ISWAP ; J IN DE, JG IN HL
* END OF INNER-MOST FOR LOOP
JMP SRT$LOOP2
*
* SORT IS DONE -- RESTRUCTURE DIR1 IN SORTED ORDER IN PLACE
*
SORT$DONE:
LHLD FILE$COUNT ; NUMBER OF ENTRIES
MOV B,H ; ... IN BC
MOV C,L
LHLD ORDER ; PTR TO ORDERED POINTER TABLE
SHLD PTPTR ; SET PTR PTR
LHLD DIR1 ; PTR TO UNORDERED DIRECTORY
SHLD PTDIR1 ; SET PTR DIR1
* FIND PTR TO NEXT DIR1 ENTRY
SRTDN:
LHLD PTPTR ; PT TO REMAINING POINTERS
XCHG ; ... IN DE
LHLD PTDIR1 ; HL PTS TO NEXT DIR1 ENTRY
PUSH B ; SAVE COUNT OF REMAINING ENTRIES
* FIND PTR TABLE ENTRY
SRTDN1:
LDAX D ; GET CURRENT POINTER TABLE ENTRY VALUE
INX D ; PT TO HIGH-ORDER POINTER BYTE
CMP L ; COMPARE AGAINST DIR1 ADDRESS LOW
JNZ SRTDN2 ; NOT FOUND YET
LDAX D ; LOW-ORDER BYTES MATCH -- GET HIGH-ORDER POINTER BYTE
CMP H ; COMPARE AGAINST DIR1 ADDRESS HIGH
JZ SRTDN3 ; MATCH FOUND
SRTDN2:
INX D ; PT TO NEXT PTR TABLE ENTRY
DCX B ; COUNT DOWN
MOV A,C ; END OF TABLE?
ORA B
JNZ SRTDN1 ; CONTINUE IF NOT
* FATAL ERROR -- INTERNAL XDIR ERROR; POINTER TABLE NOT CONSISTENT
FERR$PTR:
CALL PRINT$MESSAGE
DB CR,LF,'RENAME ERROR -- Pointer Table Not Consistent',0
JMP RETURN
* FOUND THE POINTER TABLE ENTRY WHICH POINTS TO THE NEXT UNORDERED DIR1 ENTRY
* MAKE BOTH POINTERS (PTR TO NEXT, PTR TO CURRENT UNORDERED DIR1 ENTRY)
* POINT TO SAME LOCATION (PTR TO NEXT DIR1 ENTRY TO BE ORDERED)
SRTDN3:
LHLD PTPTR ; GET PTR TO NEXT ORDERED ENTRY
DCX D ; DE PTS TO LOW-ORDER POINTER ADDRESS
MOV A,M ; MAKE PTR TO NEXT UNORDERED DIR1 PT TO BUFFER FOR
STAX D ; DIR1 ENTRY TO BE MOVED TO NEXT UNORDERED DIR1 POS
INX H ; PT TO NEXT PTR ADDRESS
INX D
MOV A,M ; MAKE HIGH POINT SIMILARLY
STAX D
* COPY NEXT UNORDERED DIR1 ENTRY TO HOLD BUFFER
MVI B,ENTRY$SIZE ; B=NUMBER OF BYTES/ENTRY
LHLD PTDIR1 ; PT TO ENTRY
LXI D,HOLD ; PT TO HOLD BUFFER
PUSH B ; SAVE B=NUMBER OF BYTES/ENTRY
CALL MOVE
POP B
* COPY TO-BE-ORDERED DIR1 ENTRY TO NEXT ORDERED DIR1 POSITION
LHLD PTPTR ; POINT TO ITS POINTER
MOV E,M ; GET LOW-ADDRESS POINTER
INX H
MOV D,M ; GET HIGH-ADDRESS POINTER
LHLD PTDIR1 ; DESTINATION ADDRESS FOR NEXT ORDERED DIR1 ENTRY
XCHG ; HL PTS TO ENTRY TO BE MOVED, DE PTS TO DEST
PUSH B ; SAVE B=NUMBER OF BYTES/ENTRY
CALL MOVE
POP B
XCHG ; HL PTS TO NEXT UNORDERED DIR1 ENTRY
SHLD PTDIR1 ; SET POINTER FOR NEXT LOOP
* COPY ENTRY IN HOLD BUFFER TO LOC PREVIOUSLY HELD BY LATEST ORDERED ENTRY
LHLD PTPTR ; GET PTR TO PTR TO THE DESTINATION
MOV E,M ; GET LOW-ADDRESS POINTER
INX H
MOV D,M ; HIGH-ADDRESS POINTER
LXI H,HOLD ; HL PTS TO HOLD BUFFER, DE PTS TO ENTRY DEST
CALL MOVE ; B=NUMBER OF BYTES/ENTRY
* POINT TO NEXT ENTRY IN POINTER TABLE
LHLD PTPTR ; POINTER TO CURRENT ENTRY
INX H ; SKIP OVER IT
INX H
SHLD PTPTR
* COUNT DOWN
POP B ; GET COUNTER
DCX B ; COUNT DOWN
MOV A,C ; DONE?
ORA B
JNZ SRTDN
RET ; DONE
*
* SWAP (Exchange) the pointers in the ORDER table whose indexes are in
* HL and DE
*
ISWAP:
PUSH H ; SAVE HL
LHLD ORDER ; ADDRESS OF ORDER TABLE - 2
MOV B,H ; ... IN BC
MOV C,L
POP H
DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N
DAD H ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX
; OF ORIGINAL HL (1, 2, ...)
DAD B ; HL NOW PTS TO POINTER INVOLVED
XCHG ; DE NOW PTS TO POINTER INDEXED BY HL
DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N
DAD H ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX
; OF ORIGINAL DE (1, 2, ...)
DAD B ; HL NOW PTS TO POINTER INVOLVED
MOV C,M ; EXCHANGE POINTERS -- GET OLD (DE)
LDAX D ; -- GET OLD (HL)
XCHG ; SWITCH
MOV M,C ; PUT NEW (HL)
STAX D ; PUT NEW (DE)
INX H ; PT TO NEXT BYTE OF POINTER
INX D
MOV C,M ; GET OLD (HL)
LDAX D ; GET OLD (DE)
XCHG ; SWITCH
MOV M,C ; PUT NEW (DE)
STAX D ; PUT NEW (HL)
RET
*
* ICOMPARE compares the entry pointed to by the pointer pointed to by HL
* with that pointed to by DE (1st level indirect addressing); on entry,
* HL and DE contain the numbers of the elements to compare (1, 2, ...);
* on exit, Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE)),
* and Non-Zero and No-Carry means ((DE)) > ((HL))
*
ICOMPARE:
PUSH H ; SAVE HL
LHLD ORDER ; ADDRESS OF ORDER - 2
MOV B,H ; ... IN BC
MOV C,L
POP H
DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N
DAD H ; DOUBLE THE ELEMENT NUMBER TO POINT TO THE PTR
DAD B ; ADD TO THIS THE BASE ADDRESS OF THE PTR TABLE
XCHG ; RESULT IN DE
DCX H ; ADJUST INDEX TO 0...N-1 FROM 1...N
DAD H ; DO THE SAME WITH THE ORIGINAL DE
DAD B
XCHG
*
* HL NOW POINTS TO THE POINTER WHOSE INDEX WAS IN HL TO BEGIN WITH
* DE NOW POINTS TO THE POINTER WHOSE INDEX WAS IN DE TO BEGIN WITH
* FOR EXAMPLE, IF DE=5 AND HL=4, DE NOW POINTS TO THE 5TH PTR AND HL
* TO THE 4TH POINTER
*
MOV C,M ; BC IS MADE TO POINT TO THE OBJECT INDEXED TO
INX H ; ... BY THE ORIGINAL HL
MOV B,M
XCHG
MOV E,M ; DE IS MADE TO POINT TO THE OBJECT INDEXED TO
INX H ; ... BY THE ORIGINAL DE
MOV D,M
MOV H,B ; SET HL = OBJECT PTED TO INDIRECTLY BY BC
MOV L,C
*
* COMPARE DIR ENTRY PTED TO BY HL WITH THAT PTED TO BY DE;
* NO NET EFFECT ON HL, DE; RET W/CARRY SET MEANS DE<HL
* RET W/ZERO SET MEANS DE=HL
*
CMP$ENTRY:
* COMPARE BY FILE NAME, FILE TYPE, EXTENSION, AND USER NUM (IN THAT ORDER)
CMP$FN$FT:
PUSH D ! PUSH H
INX H ; PT TO FN
INX D
MVI B,11 ; COMPARE FN, FT
CALL COMP
POP H ! POP D
RET
*
* COMP COMPARES DE W/HL FOR B BYTES; RET W/CARRY IF DE<HL
* MSB IS DISREGARDED
*
COMP:
MOV A,M ; GET (HL)
ANI 7FH ; MASK MSB
MOV C,A ; ... IN C
LDAX D ; COMPARE
ANI 7FH ; MASK MSB
CMP C
RNZ
INX H ; PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ COMP
RET
*
* PERFORM RENAME FUNCTION
*
RENAME:
LHLD FILE$COUNT ; HL=NUMBER OF FILES
XCHG
LHLD DIR1 ; HL PTS TO DIR1, DE=NUMBER OF FILES
* PRINT FILE NAMES AND PERFORM RENAME FUNCTION
RENAME$LOOP:
PUSH H ; SAVE PTR
PUSH D ; SAVE COUNTER
* BUILD NEW FILE NAME
INX H ; PT TO FIRST CHAR OF FILE NAME
LXI B,FCB+1 ; PT TO NEW FILE NAME FORMAT
LXI D,FCB$NEW+1 ; PT TO NEW FILE NAME TO BUILD
MVI A,11 ; COPY 11 CHARS WITH TRANSLATION
RENAME$LOOP1:
PUSH PSW ; SAVE COUNT
LDAX B ; GET CHAR OF NEW FILE NAME
CPI '?' ; CARRY OVER IF WILD
JNZ RENAME$LOOP2
MOV A,M ; GET CHAR FROM CURRENT FILE NAME
RENAME$LOOP2:
STAX D ; STORE INTO NEW FILE NAME
INX H ; PT TO NEXT
INX D
INX B
POP PSW ; GET COUNT
DCR A ; COUNT DOWN
JNZ RENAME$LOOP1
* PRINT RENAME FILES
POP D ; RESTORE COUNTER
CALL PRINT$MESSAGE
DB CR,LF,'Rename ',0
LXI H,FCB$NEW ; PT TO NEW FILE NAME
CALL PRINT$FN ; PRINT FILE NAME
CALL PRINT$MESSAGE ; SEPARATOR
DB ' from ',0
POP H ; GET PTR
CALL PRINT$FN ; PRINT FILE NAME
* PERFORM INSPECTION AND/OR RENAME FUNCTION
CALL INSPECT ; INSPECT IF FLAG SET ELSE SET ATTRIBUTES
* POINT TO NEXT ENTRY
LXI B,ENTRY$SIZE ; PT TO NEXT ENTRY
DAD B ; HL PTS TO NEXT ENTRY
DCX D ; COUNT DOWN
MOV A,D ; DONE?
ORA E
JNZ RENAME$LOOP
RET
*
* PRINT FILE NAME PTED TO BY HL+1
*
PRINT$FN:
PUSH H ; SAVE PTR
INX H ; PT TO FIRST CHAR
MVI B,8 ; 8 CHARS
CALL PRFN ; PRINT NAME PART
MVI A,'.' ; PRINT DOT
CALL CHAR$OUT
MVI B,3 ; 3 CHARS
CALL PRFN ; PRINT TYPE PART
POP H ; RESTORE PTR
RET
PRFN:
MOV A,M ; GET CHAR
ANI 7FH ; MASK OUT MSB
CALL CHAR$OUT
INX H ; PT TO NEXT
DCR B ; COUNT DOWN
JNZ PRFN
RET
*
* PERFORM INSPECTION IF OPTION SET -- ELSE, SET KEYS
*
INSPECT:
LDA INSP$FLAG ; GET FLAG
ORA A ; 0=NO
JZ RENAMEX ; RENAME IF NOT
CALL PRINT$MESSAGE
DB ' -- Ok (Y/N)? ',0
INSP1:
CALL CHAR$IN
CALL CAPS ; CAPITALIZE
CPI 'Y' ; OK?
JZ RENAMEX ; RENAME IF SO
CPI 'N' ; NOT OK?
RZ
CALL PRINT$MESSAGE
DB CR,LF,'Type Y or N -- Ok (Y/N)? ',0
JMP INSP1
*
* RENAME FILE PTED TO BY HL
*
RENAMEX:
PUSH H ; SAVE PTR
PUSH D ; SAVE COUNTER
INX H ; PT TO 1ST BYTE OF SOURCE FILE NAME
LXI D,FCB$OLD ; CLEAR DRIVE
XRA A ; A=0
STAX D
INX D ; PT TO 1ST BYTE OF DEST FILE NAME
MVI B,11 ; 11 BYTES
CALL MOVE
XCHG ; HL PTS TO FCB$NEW
MVI B,5 ; CLEAR REST OF FCB$OLD AND 1ST OF FCB$NEW
XRA A ; A=0
CALL FILL
LXI H,FCB$NEW+12 ; CLEAR REST OF FCB$NEW
MVI B,4
CALL FILL
LXI D,FCB$NEW ; CHECK FOR PRESENCE OF NEW FILE NAME
CALL FCHECK ; CHECK FOR FILE
JNZ FNEW$ERR ; FILE FOUND
CALL CLEAR$ATT ; CLEAR ATTRIBUTES (R/O, SYS, TAGS)
LXI D,FCB$OLD ; PT TO FCB
MVI C,23 ; RENAME FILE
CALL BDOS
CALL RESET$ATT ; RESET ATTRIBUTES (R/O, SYS, TAGS)
POP D ; RESTORE COUNTER
POP H ; RESTORE PTR
RET
FNEW$ERR:
CALL PRINT$MESSAGE
DB CR,LF,'ERROR -- Rename Destination File Exists -- Skipping'
DB CR,LF,' File in Error is: ',0
LXI H,FCB$NEW ; PRINT NEW FILE NAME
CALL PRINT$FN ; PRINT FILE NAME
POP D ; RESTORE COUNTER
POP H ; RESTORE PTR
RET
*
* IF CP/M 2.X, SAVE CURRENT ATTRIBUTES AND CLEAR THEM ON OBJECT FILE PTED
* TO BY HL
*
CLEAR$ATT:
MVI C,12 ; GET VERS NUMBER
CALL BDOS
MOV A,H ; CP/M 1.4?
ORA L
RZ ; DO NOTHING IF CP/M 1.4
* SAVE OLD ATTRIBUTES IN ATTR BUFFER
LXI H,FCB$OLD+1 ; PT TO OLD NAME
LXI D,ATTR ; SAVE ATTRIBUTES
MVI B,11 ; 11 BYTES
CLA1:
MOV A,M ; GET BYTE
ANI 80H ; MASK FOR ATTRIBUTE
STAX D ; PUT BYTE
MOV A,M ; PLACE CLEARED FILE NAME INTO FCB$OLD
ANI 7FH ; MASK
MOV M,A
INX H ; PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ CLA1
* SAVE NEW FILE NAME WITH PROPER ATTRIBUTES INTO ATTR
LXI H,FCB$NEW+1 ; PT TO NEW FILE NAME
LXI D,ATTR ; PT TO ATTRIBUTES
MVI B,11 ; 11 BYTES
CLA2:
MOV A,M ; CLEAR ATTRIBUTE BIT
ANI 7FH ; MASK
MOV M,A
LDAX D ; GET ATTRIBUTE BIT
ORA M ; MASK IN BIT
STAX D ; PUT NAME BACK
INX H ; PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ CLA2
* CREATE FCB FOR CLEARING ATTRIBUTES
LXI H,FCB$OLD ; PT TO OLD FILE NAME
LXI D,FCB$TEMP ; COPY INTO TEMP
XRA A ; A=0
STAX D ; SET FIRST BYTE
INX H ; PT TO FIRST BYTE OF FILE NAME
INX D
MVI B,11 ; COPY TO NAME
CLA3:
MOV A,M ; GET BYTE
ANI 7FH ; MASK
STAX D ; PUT BYTE
INX H ; PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ CLA3
XCHG ; HL PTS TO NEXT BYTE
MVI B,24 ; CLEAR REST OF FCB
XRA A ; A=0
CALL FILL
LXI D,FCB$TEMP ; CLEAR ALL ATTRIBUTES
MVI C,30 ; SET ATTRIBUTES
CALL BDOS
RET
*
* IF CP/M 2.X, RESTORE CURRENT ATTRIBUTES
*
RESET$ATT:
MVI C,12 ; GET VERS NUMBER
CALL BDOS
MOV A,H ; CP/M 1.4?
ORA L
RZ ; DO NOTHING IF CP/M 1.4
* COPY FILE NAME/TYPE IN ATTR INTO FCB$TEMP FOR RESET OF ATTRIBUTES
LXI D,FCB$TEMP ; COPY INTO TEMP
XRA A ; A=0
STAX D ; SET FIRST BYTE
LXI H,ATTR ; PT TO FIRST BYTE OF FILE NAME
INX D
MVI B,11 ; COPY TO NAME
RES1:
MOV A,M ; GET BYTE
STAX D ; PUT BYTE
INX H ; PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ RES1
XCHG ; HL PTS TO NEXT BYTE
MVI B,24 ; CLEAR REST OF FCB
XRA A ; A=0
CALL FILL
LXI D,FCB$TEMP ; RESET ALL ATTRIBUTES
MVI C,30 ; SET ATTRIBUTES
CALL BDOS
RET
*
* CHECK FOR PRESENCE OF FILE PTED TO BY DE
* ... RET W/ZERO SET IF NOT FOUND
*
FCHECK:
LXI H,FCB$TEMP ; COPY INTO TEMP
XCHG ; COPY FROM HL TO DE
MVI B,12 ; 12 BYTES
CALL MOVE
XCHG ; HL PTS TO TEMP FCB
MVI B,24 ; FILL OUT REST WITH ZEROES
XRA A ; A=0
CALL FILL
LXI D,FCB$TEMP ; FIND FILE
MVI C,17 ; SEARCH FOR FIRST
CALL BDOS
CPI 0FFH ; FILE FOUND?
RET
*
* CHARACTER INPUT ROUTINE
*
CHAR$IN:
PUSH H ; SAVE REGS
PUSH D
PUSH B
MVI C,1 ; CONSOLE INPUT
CALL BDOS
POP B ; RESTORE REGS
POP D
POP H
ANI 7FH ; MASK OUT MSB
RET
*
* CHARACTER OUTPUT ROUTINE
*
CHAR$OUT:
PUSH H ; SAVE REGS
PUSH D
PUSH B
PUSH PSW
MOV E,A ; CHAR IN E
MVI C,2 ; CONSOLE OUTPUT
CALL BDOS
POP PSW ; RESTORE REGS
POP B
POP D
POP H
RET
*
* PRINT MESSAGE PTED TO BY RET ADR ENDING IN 0
*
PRINT$MESSAGE:
XTHL ; SAVE HL AND SET HL TO MESSAGE
PM1:
MOV A,M ; GET BYTE
INX H ; PT TO NEXT
ORA A ; DONE?
JZ PM2
CALL CHAR$OUT ; PRINT
JMP PM1
PM2:
XTHL ; RESTORE HL AND RET ADR
RET
*
* CAPITALIZE CHAR IN A
*
CAPS:
ANI 7FH ; MASK OUT MSB
CPI 61H ; SMALL A
RC
CPI 7BH ; SMALL B + 1
RNC
ANI 5FH ; CAPITALIZE
RET
*
* MOVE (HL) TO (DE) FOR (B) BYTES
*
MOVE:
MOV A,M ; GET
STAX D ; PUT
INX H ; PT TO NEXT
INX D
DCR B ; COUNT DOWN
JNZ MOVE
RET
*
* FILL (HL) FOR (B) BYTES WITH (A)
*
FILL:
MOV M,A ; PUT
INX H ; PT TO NEXT
DCR B ; COUNT DOWN
JNZ FILL
RET
*
* BUFFERS
*
DS 100 ; WHY NOT?
STACK DS 2 ; OLD SP
ATTR:
DS 11 ; ATTRIBUTE BYTES/FILE NAME
DIR1:
DS 2 ; DIR1 PTR
ORDER:
DS 2 ; ORDER TABLE PTR
UDRIVE:
DS 1 ; USER-SELECTED DRIVE NUMBER (1-16)
INSP$FLAG:
DS 1 ; INSPECT FLAG (0=NO)
SYS$FLAG:
DS 1 ; SYSTEM FILE FLAG (0=NO)
FILE$COUNT:
DS 2 ; NUMBER OF FILES SELECTED
ECOUNTER:
DS 2 ; COUNTER FOR PUT$ENTRY
PTPTR:
DS 2 ; SORT PTR
PTDIR1:
DS 2 ; SORT DIR1 PTR
GAP:
DS 2 ; SORT NUMBER
I:
DS 2 ; SORT NUMBER
J:
DS 2 ; SORT NUMBER
JG:
DS 2 ; SORT NUMBER
HOLD:
DS ENTRY$SIZE ; HOLD BUFFER FOR SORT
FCB$SRC:
DS 36 ; SOURCE FCB
FCB$OLD:
DS 16 ; FCB BUFFER FOR OLD FILE NAME
FCB$NEW:
DS 16 ; FCB BUFFER FOR NEW FILE NAME
DS 4 ; ... FOR REST OF FCB
FCB$TEMP:
DS 36 ; FCB BUFFER FOR NEW FILE NAME SCAN
*
* BEGINNING OF DYNAMIC BUFFER REGION
*
ENDALL EQU $/256*256+256 ; PAGE BOUNDARY
END