* PROGRAM: PROTECT * AUTHOR: RICHARD CONN * VERSION: 1.0 * DATE: 26 OCT 81 * PREVIOUS VERSIONS: None VERS EQU 10 ; Version Number * * PROTECT -- * PROTECT is used to set the file protection and tag attribute bits * for CP/M 2.x files. It is invoked via command lines of the following * forms: * * PROTECT afn keys <-- Set unconditionally * PROTECT afn keys /I <-- Inspect Mode * PROTECT afn /S <-- Set Each ufn Individually * * In the above examples, the reference 'keys' is a string of zero or * more characters which may be any of the following: * * R <-- Set R/O Attribute * S <-- Set SYS Attribute * n <-- Set Tag Attribute (1 <= n <= 4) * * Examples: * PROTECT *.COM RS <-- Sets all COM files to R/O System * PROTECT *.COM RS /I <-- Same, with user approval * PROTECT *.COM /S <-- Allow user to specify for each file * PROTECT MYPROG.COM 1R <-- Sets Tag Bit 1 and R/O Attribute * * * PROT CONSTANTS * DELIM EQU '/' ; OPTION DELIMITER CHAR INSP$OPT EQU 'I' ; OPTION LETTER FOR INSPECTION SET$OPT EQU 'S' ; OPTION LETTER FOR SETTING INDIVIDUAL 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 ; LF EQU 0AH ; 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 'PROTECT 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 CK$VERS 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 * * CHECK FOR CP/M VERSION NUMBER -- MUST BE 2.X * CK$VERS: MVI C,12 ; GET VERSION NUMBER CALL BDOS MOV A,H ; HL=0 IF 1.4 ORA L JNZ PROT1 CALL PRINT$MESSAGE DB CR,LF,'ERROR -- PROT must run under CP/M 2.x',0 * * 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 * PROT1: XRA A ; A=0 STA SET$FLAG ; CLEAR SET FLAG STA INSP$FLAG ; CLEAR INSPECT 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 PROT2 * * PRINT PROT HELP MESSAGE * PROT$HELP: CALL PRINT$MESSAGE DB CR,LF,'PROT is invoked by a command of the form:' DB CR,LF,' PROT afn keys <-- Set unconditional' DB CR,LF,' PROT afn keys /I <-- Inspect mode' DB CR,LF,' PROT afn /S <-- Set individual' DB CR,LF,'where "keys" may be any combination of:' DB CR,LF,' R S 1 2 3 4' DB CR,LF,'to indicate R/O, SYS, or Tag Bits 1, 2, 3, or 4' DB CR,LF,' Any bit not specified is cleared (R/W, DIR, etc)' DB CR,LF DB CR,LF,' Examples:' DB CR,LF,' PROT *.COM RS <-- Set *.COM to R/O SYS' DB CR,LF,' PROT *.COM 1R <-- Set *.COM to R/O DIR' DB CR,LF,' with Tag Bit 1 Set' DB CR,LF,' PROT *.COM /I <-- Set *.COM to R/W DIR' DB CR,LF,' with user inspection' DB CR,LF,' PROT *.COM /S <-- Set *.COM to user-selected' DB CR,LF,' attributes on ' DB 'individual basis' DB 0 JMP RETURN * * CONTINUE PROCESSING * PROT2: INX H ; PT TO FIRST CHAR MOV A,M ; GET IT CPI DELIM ; OPTION? JNZ PROT3 DCR B ; COUNT DOWN JZ PROT$HELP INX H ; PT TO NEXT MOV A,M ; GET OPTION CHAR CPI SET$OPT ; SET INDIVIDUAL FILES? JZ PROT2$SET CPI INSP$OPT ; INSPECT? JZ PROT2$INSP JMP PROT$HELP ; HELP OTHERWISE PROT2$SET: MVI A,0FFH ; SET FLAG STA SET$FLAG JMP PROT3 PROT2$INSP: MVI A,0FFH ; SET FLAG STA INSP$FLAG PROT3: DCR B ; COUNT DOWN JNZ PROT2 * * 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? JNZ PROT4 FN$ERR: CALL PRINT$MESSAGE DB CR,LF,'ERROR -- File Name not specified',0 JMP RETURN KEY$ERR: CALL PRINT$MESSAGE DB CR,LF,'ERROR -- Invalid Key specified',0 JMP RETURN * * CHECK FOR KEYS AND CLEAR IF OPTION CAUGHT * PROT4: LXI H,FCB2+1 ; PT TO FIRST BYTE OF NAME MOV A,M ; GET IT CPI DELIM ; OPTION CAUGHT? JNZ PROT5 MVI B,11 ; CLEAR ALL OF FILE NAME/TYPE MVI A,' ' ; FILL CALL FILL * * COPY 2ND FCB INTO KEY$BUFFER * PROT5: LXI D,FCB2+1 ; PT TO FCB2 CALL SET$KEY$BUFFER ; LOAD KEY$BUFFER IN SORTED ORDER * * ALL SET TO GO -- * FCB CONTAINS FILE NAME/TYPE * FCB2 AND KEY$BUFFER CONTAIN KEYS * SET$FLAG IS SET CORRECTLY * 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 ; 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',0 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 PROTECTION ATTRIBUTES CALL PROTECT * RETURN TO CP/M JMP RETURN * * LOAD KEY$BUFFER WITH KEYS PTED TO BY DE * SET$KEY$BUFFER: MVI B,'1' ; CHECK FOR KEYS 1-4 LXI H,KEY$BUFFER ; PT TO KEY BUFFER PROT5A: CALL PROT$SCAN ; SCAN FOR/SET KEY INR B ; INCREMENT COUNTER MOV A,B ; GET B INTO A FOR TEST CPI '5' ; DONE? JNZ PROT5A MVI B,'R' ; CHECK FOR R/O KEY CALL PROT$SCAN ; SCAN FOR/SET KEY MVI B,'S' ; CHECK FOR SYS KEY CALL PROT$SCAN ; SCAN FOR/SET KEY * CHECK FOR ERROR MVI B,11 ; 11 BYTES (ALL MUST BE ) PROT5B: LDAX D ; GET CHAR CPI ' ' ; JNZ KEY$ERR INX D ; PT TO NEXT DCR B ; COUNT DOWN JNZ PROT5B RET * * SCAN FCB2+1 FOR KEY IN REG B * PROT$SCAN: PUSH D ; SAVE PTR MVI C,11 ; 11 BYTES MVI M,' ' ; SET INITIAL PS1: LDAX D ; GET KEY CALL CAPS ; CAPITALIZE CMP B ; MATCH? JNZ PS2 MOV M,A ; SET KEY MVI A,' ' ; KILL STAX D PS2: INX D ; PT TO NEXT DCR C ; COUNT DOWN JNZ PS1 POP D ; RESTORE PTR INX H ; PT TO NEXT ATTRIBUTE 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+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 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,'PROTECT 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 JZ PKEY1 MVI A,0FFH ; SET FLAG STA TAG$FLAG PKEY1: DCR B ; COUNT DOWN JNZ PKEY0 LDA TAG$FLAG ; ANY SET? ORA A ; 0=NO JZ PKEY2 CALL PRINT$MESSAGE DB 'Tags: ',0 LXI H,KEY$BUFFER ; PT TO BUFFERS MVI B,4 ; PRINT 4 CHARS (1-4) CALL PRFN ; USE COMMON ROUTINE PKEY2: CALL PRINT$MESSAGE DB ' R/',0 MOV A,M ; CHECK R/O FLAG INX H ; PT TO NEXT CPI 'R' ; R/O? JZ PKRO MVI A,'W' ; R/W DB 1 ; LXI B,XXXX PKRO: MVI A,'O' ; R/O (PART OF LXI B,XXXX IF R/W SELECTED) CALL CHAR$OUT MOV A,M ; CHECK SYS FLAG POP H ; RESTORE PTR CPI 'S' ; SYS? JZ PKSYS CALL PRINT$MESSAGE DB ' DIR',0 RET PKSYS: CALL PRINT$MESSAGE DB ' SYS',0 RET * * PERFORM INSPECTION IF OPTION SET -- ELSE, SET KEYS * INSPECT: LDA INSP$FLAG ; GET FLAG ORA A ; 0=NO JZ SET$KEYS ; SET KEYS IF NOT CALL PRINT$MESSAGE DB ' -- Ok (Y/N)? ',0 INSP1: CALL CHAR$IN CALL CAPS ; CAPITALIZE CPI 'Y' ; OK? JZ SET$KEYS ; SET IF SO CPI 'N' ; NOT OK? RZ CALL PRINT$MESSAGE DB CR,LF,'Type Y or N -- Ok (Y/N)? ',0 JMP INSP1 * * SET KEYS OF FILE PTED TO BY HL * SET$KEYS: PUSH H ; SAVE PTR PUSH D ; SAVE COUNTER INX H ; PT TO 1ST BYTE OF SOURCE FILE NAME LXI D,FCBX ; 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 FCBX MVI B,24 ; CLEAR REST OF FCBX XRA A ; A=0 CALL FILL LXI D,KEY$BUFFER ; PT TO BUFFER LXI H,FCBX+1 ; PT TO TYPE ATTRIBUTE MVI B,4 ; SET TAG BITS CALL SK$SET INX H ; PT TO R/O BYTE INX H INX H INX H MVI B,2 ; SET R/O AND SYS BITS CALL SK$SET LXI D,FCBX ; PT TO FCB MVI C,30 ; SET FILE ATTRIBUTES CALL BDOS POP D ; RESTORE COUNTER POP H ; RESTORE PTR RET SK$SET: MOV A,M ; CLEAR ATTRIBUTE ANI 7FH MOV M,A LDAX D ; GET TAG CPI ' ' ; NO SET? JZ SK$SET1 MOV A,M ; SET ATTRIBUTE ORI 80H MOV M,A SK$SET1: INX D ; PT TO NEXT INX H DCR B ; COUNT DOWN JNZ SK$SET RET * * GET KEYS SPECIFIED BY USER * RETURN WITH ZERO FLAG SET IF ABORT GIVEN * GET$KEYS: PUSH H ; SAVE PTR PUSH D ; SAVE COUNTER CALL PRINT$MESSAGE DB ' Keys (1/2/3/4/R/S/A=Abort)? ',0 LXI H,INLINE+2 ; PT TO INPUT BUFFER MVI A,' ' ; FILL MVI B,LINELEN ; NUMBER OF CHARS POSSIBLE CALL FILL LXI D,INLINE ; READ INTO BUFFER MVI C,10 ; BDOS READLN CALL BDOS LXI H,INLINE+2 ; SCAN FOR ABORT FLAG MVI B,11 ; SCAN 11 BYTES GK1: MOV A,M ; GET IT INX H ; PT TO NEXT CALL CAPS ; CAPITALIZE CPI 'A' ; ABORT? JZ GK2 DCR B ; COUNT DOWN JNZ GK1 LXI D,INLINE+2 ; PUT INTO KEY$BUFFER CALL SET$KEY$BUFFER POP D ; RESTORE COUNTER POP H ; RESTORE PTR MVI A,0FFH ; CLEAR ZERO FLAG ORA A RET GK2: POP D ; RESTORE COUNTER POP H ; RESTORE PTR XRA A ; SET ZERO FLAG 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 LINELEN EQU 20 ; 20 CHARS/INPUT LINE INLINE: DB LINELEN ; LENGTH OF INPUT LINE DS 1 ; RETURNED LINE LENGTH DS LINELEN ; INPUT LINE DS 4 ; BUFFER DIR1: DS 2 ; DIR1 PTR ORDER: DS 2 ; ORDER TABLE PTR UDRIVE: DS 1 ; USER-SELECTED DRIVE NUMBER (1-16) TAG$FLAG: DS 1 ; TAG FLAG (0=NO TAGS SPECIFIED) INSP$FLAG: DS 1 ; INSPECT FLAG (0=NO) SET$FLAG: DS 1 ; MANUALLY SET ATTRIBUTES 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 KEY$BUFFER: DS 11 ; KEY BUFFER HOLD: DS ENTRY$SIZE ; HOLD BUFFER FOR SORT FCBX: DS 40 ; FCB BUFFER FOR CHANGE * * BEGINNING OF DYNAMIC BUFFER REGION * ENDALL EQU $/256*256+256 ; PAGE BOUNDARY END