; CHG v1.1 04/04/86 ; ; This program is a kludge of UNERA and MAKE utilities. ; It allows you to change the user area of a program without ; re-copying the entire file. It can also unerase a file. ; ; Warmboots on exit to force buffer flush/write ; ; Steve Sanders, TBKUG/DataCOM Super Systems ; (813) 791-1454/55 modem 300/1200/2400 ; ;....... ; ; To use: ; ; A>CHG d:ufn.typ olduser# newuser# ; ; A>CHG C:GAME.BAS 6 0 ; ; Will move GAME.BAS from user area 6 to user area 0 ; ; CHG supports user#'s 0 thru 31. ; ; A>CHG C:GAME.BAS ? 4 ; ; Will unerase GAME.BAS from the directory of drive C ; and restore it to C4: ; ; * * * * * * * * * * * * * * * * * * ; Conditional assembly constants FALSE: EQU 0 TRUE: EQU NOT FALSE ; System configuration equates: ORG 100H ;START AT BASE OF TPA JMP BEGIN ;GET AROUND THE MESSAGES & DATA AREAS ; EQUATES BOOT: EQU 0000H ;CP/M WARM BOOT JUMP VECTOR BDOS: EQU 0005H ;CP/M BDOS CALL JUMP VECTOR CR: EQU 'M'-40H ;CARRIAGE RETURN FCB: EQU BOOT+5CH ;DEFAULT FILE CONTROL BLOCK LF: EQU 'J'-40H ;LINE FEED BELL: EQU 07H ;CONSOLE BELL ; CONSOLE MESSAGES SONMSG: DB CR,LF,LF DB 'CHG v1.1 - CP/M Change User# / Unerase Utility' DB CR,LF,LF,'$' BMSG: DB CR,LF,'Done -- Please file before using.' DB bell,CR,LF,'$' ILMSG: DB CR,LF,'ABORT -- Illegal drive requested.',bell,CR,LF,'$' NOFMSG: DB CR,LF,'ABORT -- No file name was specified.',CR,LF,LF DB 'Usage: chg d:ufn.typ olduser# newuser#',CR,LF,LF DB ' chg d:ufn.typ ? user#',CR,LF,LF DB 'Current drive is assumed if no d: parameter (optional).',CR,LF DB 'Change unambiguous filename from olduser# to newuser#.',CR,LF DB '"?" parameter unerases filename to specified user number.' DB bell,CR,LF,'$' NFMSG: DB CR,LF,'Specified filename was NOT found.',bell,CR,LF,'$' WMSG: DB CR,LF,'Error during disk Write - ABORTING.',bell,CR,LF,'$' BADUSR: DB CR,LF,'Bad commandline syntax - ABORTING.' DB bell,CR,LF,'$' ; DATA AREAS BLN: DB 0 ;'CPMCHK' STORES CP/M VERSION DIRMAX: Dw 16 ;NUMBER OF SECTORS IN DIRECTORY = ; MAXIMUM NUMBER OF DIRECTORY ENTRIES ; DIVIDED BY 4 (ENTRIES PER SECTOR) temp: dw 0 ;Temp storage for FCB print maxsec: db 26 ;Maximum number of sectors/track FIXCNT: DB 0 ;NUMBER OF ENTRIES FIXED REWRT: DB 0 ;REWRITE FLAG 0=NO, F=YES SECTOR: DB 0 ;CURRENT SECTOR NUMBER TRACK: Dw 2 ;TRACK NUMBER OF DIRECTORY OLDUSR DB 0 ;Olduser # storage NEWUSR DB 0 ;Newuser # storage ; ADDRESS OF THE TRANSLATE TABLE DPH: DW XLTO DS 14 ; STANDARD TRANSLATE TABLE XLTO: DB 1,7,13,19,25,5,11,17,23,3,9,15,21 DB 2,8,14,20,26,6,12,18,24,4,10,16,22 ;------------------------- main program ------------------------------ BEGIN: LXI SP,STACK ;SET STACK POINTER CALL HELLO ;SIGN ON MESSAGE CALL PCHECK ;CHECK PARAMETERS CALL GETUSR ;GET USER #'S FROM CMD LINE CALL CHANGE ;DO THE RECOVERY CALL BYE ;SIGN OFF MESSAGE JMP BOOT ;RETURN TO CP/M ;------------------------- subroutines ------------------------------- ; SIGN OFF AND RESET SYSTEM BYE: MVI C,13 ;SYSTEM RESET CALL BDOS LDA FIXCNT ;CHECK FOR ACTIVITY ORA A JZ NOFIND ;SAY NONE FOUND LXI D,BMSG ;WARN FOUND CALL PRINT RET ; CHECKS THE CURRENT 4 DIRECTORY ENTRIES AGAINST ARGUMENT ; IF MATCH, REWRITES SECTOR WITH NEWUSER 1ST BYTES CHKENT: XRA A ;ASSUME NO REWRITE STA REWRT MVI B,4 ;NUMBER OF ENTRIES PER SECTOR LXI H,80H ;BEGINNING OF BUFFER CKLUP: push b MOV A,M PUSH H LXI H,OLDUSR ;GET USER # TO COMPARE TO CMP M POP H JNZ CKINC PUSH H ;SAVE BEGINNING ADDRESS CALL COMPAR ;COMPARE WITH ARGUMENT POP H JNZ CKINC ;NO MATCH LDA NEWUSR ;GET NEWUSER # MOV M,A ;POKE IN TO DIRECTORY ENTRY MVI A,0FH ;SAY NEED REWRITE STA REWRT LDA FIXCNT INR A ;BUMP COUNT OF CHANGES STA FIXCNT CKINC: pop b LXI D,32 ;LENGTH OF ENTRY DAD D DCR B JNZ CKLUP LDA REWRT ;SEE IF NEED REWRITE ORA A JZ CKDONE ;NO - DONE ; WRITE THE DIRECTORY SECTOR BACK TO THE DISK lhld TRACK ;SET TRACK MOV C,l Mov B,h CALL SETTRK LDA SECTOR ;SET SECTOR MOV C,A CALL TRNSLT MVI B,0 CALL SETSEC CALL WRITE ;WRITE THE SECTOR BACK ORA A JNZ ERRWRT ;ABORT IF ERROR CKDONE: lhld DIRMAX dcx h ;REDUCE SECTORS LEFT shld DIRMAX LDA SECTOR ;POINT TO NEXT SECTOR INR A STA SECTOR mov b,a lda maxsec dcr a cmp b rnc lhld track inx h shld track RET ; COMPARE 11 BYTES OF DIRECTORY ENTRY AGAINST ARGUMENT COMPAR: shld temp ;Hold pointer in case of match INX H LXI D,FCB+1 XCHG MVI C,11 CMPR1: LDAX D ;GET DIRECTORY ENTRY CHARACTER ANI 7FH ;STRIP ANY FLAGS CMP M jnz cmpckam cmpr2: INX D INX H ;BUMP TO NEXT CHARACTER DCR C JNZ CMPR1 ;LOOP FOR 11 CHARACTERS lhld temp call printfcb xra a RET ;RETURNS 'ZERO' FLAG SET FOR MATCH cmpckam: ldax d cpi 0e5h ;Non-allocated entry? jz skip mov a,m cpi '?' rnz jmp cmpr2 skip: ora a ret ;set NZ flag ; SET THINGS UP FOR CURRENT SYSTEM ; CP/M 2.X ONLY CPMCHK: LXI D,80H ;SET DMA TO TBUFF MVI C,26 CALL BDOS CALL CPM22 ;IF 2.2 GO SET THINGS CALL GTBIOS ;ESTABLISH BIOS JUMP VECTOR ; SELECT DISK AND SETUP DISK PARAMETER HEADER LDA FCB ;GET THE DISK mov e,a mvi c,14 call bdos lda fcb MOV C,A MVI B,0 CALL SELDSK ;MAKE SURE DRIVE IS MOV A,H ; SELECTED ORA L JZ ILDISK MOV E,M ;GET THE ADDRESS INX H ; OF THE XLTO MOV D,M XCHG SHLD DPH ;SAVE THE ADDRESS RET ; DETERMINE NUMBER OF DIRECTORY ENTRIES ALSO CPM22: MVI C,31 ;GET DISK PARAMETERS ADDRESS CALL BDOS ;DPB ADDRESS IN 'HL' ON RETURN mov a,m ;Number of sectors/track sta maxsec LXI D,7 ;OFFSET TO DRM DAD D MOV E,M ;GET NUMBER OF INX H ; DIRECTORY ENTRIES MOV D,M XCHG INX H ;ACCOUNT FOR - 1 CALL SHFHL2 ;SHIFT 'HL' RIGHT 2 shld DIRMAX ;SAVE NUMBER DIRECTORY SECTORS LXI H,5 ;NOW POINT TO SYSTEM DAD D ; TRACK OFFSET MOV A,M inx h mov h,m mov l,a shld track MVI A,48 ;SET MOVE LENGTH STA BLN RET ; ERROR OCCURED DURING DISK WRITE - ABORT ERRWRT: LXI D,WMSG CALL PRINT JMP BOOT ;ABORT ; MAKE SURE A LEGAL FILENAME IS SPECIFIED FCBCHK: LDA FCB ;GET DRIVE SPECIFICATION ORA A ;SEE IF DEFAULT JNZ FCBCK1 ;NO, GO CHECK FILENAME MVI C,25 ;ASK FOR CURRENT DRIVE CALL BDOS INR A ;OFFSET FOR NEXT INSTRUCTION FCBCK1: DCR A ;CURRENT DRIVE NUMBER STA FCB ;SAVE IT LDA FCB+1 ;GET 1ST BYTE OF FILENAME CPI ' ' ;MAKE SURE IT IS NON-BLANK RNZ ;OK - KEEP GOING ; IF NO FILE NAME IS SPECIFIED, ABORT WITH NOTICE LXI D,NOFMSG CALL PRINT JMP BOOT ;ABORT ; GET BIOS JUMPS VECTORS FOR EASY REFERENCE GTBIOS: LHLD BOOT+1 ;POINTS TO BIOS JUMP TABLE+3 LXI D,WBOOT ;WHERE WE WILL KEEP A COPY LDA BLN ;NUMBER OF BYTES TO MOVE MOV B,A ;MOVE LIKES IT IN REGISTER 'B' CALL MOVE ;MOVE THE TABLE RET ; SAY WHO WE ARE HELLO: LXI D,SONMSG ;POINT TO HELLO MESSAGE CALL PRINT RET ;SPECIFIED AN ILLEGAL DISK DRIVE - ABORT ILDISK: LXI D,ILMSG CALL PRINT JMP BOOT ;ABORT ; GENERAL PURPOSE MOVE ROUTINE ; FROM 'HL' TO 'DE' FOR COUNT OF 8 MOVE: MOV A,M ;GET A BYTE STAX D ;PUT A BYTE INX D ;INCREMENT TO NEXT INX H DCR B ;COUNT DOWN JNZ MOVE RET NOFIND: LXI D,NFMSG CALL PRINT RET ; READS NEXT SECTOR (GROUP OF FOUR DIRECTORY ENTRIES) ; RETURNS WITH ZERO FLAG SET IF NO MORE NXTSEC: lhld DIRMAX ;SEE IF MORE SECTORS mov a,h ORA l RZ ;RETURNS ZERO FLAG IF NO MORE lhld TRACK ;SET TRACK MOV C,l Mov B,h CALL SETTRK LDA SECTOR ;SET SECTOR MOV C,A CALL TRNSLT MVI B,0 CALL SETSEC CALL READ ;READ A SECTOR ANI 1 ;REVERSE SENSE OF ERROR FLAG XRI 1 ;RETURNS WITH ZERO FLAG SET RET ;IF BAD READ ; CHECK FOR VALID PARAMETERS AND SAY WHICH CP/M VERSION PCHECK: CALL FCBCHK ;MAKE SURE FILE SPECIFIED CALL CPMCHK ;ESTABLISH CP/M INFO RET ; GENERAL PURPOSE PRINT ROUTINE - ENTER WITH REGS 'DE' POINTING TO MSG ; RETURNS TO CALLER FROM BDOS PRINT: MVI C,9 ;BDOS PRINT STRING COMMAND JMP BDOS ;GO DO THE PRINT ; FCB printing routine printfcb: push h lxi d,1+8+3 dad d mov a,m ora a pop h rnz ;Print only first extent inx h mvi b,8 call pr1 mvi e,'.' mvi c,2 push h call bdos pop h mvi b,3 call pr1 crlf: mvi e,13 ;Send CR/LF mvi c,2 call bdos mvi e,10 mvi c,2 call bdos ret pr1: mov a,m ani 127 cpi ' ' ;Check for blanks jz pr2 mov e,a push h push b mvi c,2 call bdos pop b pop h pr2: inx h dcr b jnz pr1 ret ; SHIFT REGS 'HL' RIGHT 2 BITS LOGICAL SHFHL2: CALL SHFHL ;CLEAR CARRY SHFHL: XRA A MOV A,H RAR ;SHIFTED BIT IN CARRY MOV H,A MOV A,L RAR MOV L,A RET ; TRANSLATE REG 'C' FROM LOGICAL TO PHYSICAL SECTOR NUMBER TRNSLT: LHLD DPH ;GET ADDRESS OF XLTO XCHG CALL SECTRAN MOV C,L RET ; LOOK THROUGH DIRECTORY CHANGE: CALL NXTSEC ;GET A DIRECTORY SECTOR RZ ;RETURNS ZERO FLAG IF NO MORE CALL CHKENT ;CHECK IT OUT AND MAYBE FIX JMP CHANGE ;KEEP IT UP TILL DONE ; This routine get the user# from the command line. getusr: push h lxi h,81h ;Start of tail string of cmd line call skipsp ;Skip space(s) loop1: mov a,m ;Skip filename cpi 20h ;by looking for space inx h jnz loop1 call skipsp ;Skip space(s) if necessary call convert ;Get olduser# to use sta oldusr call skipsp call convert ;Get newuser # sta newusr pop h ret skipsp: mov a,m ;move past space(s) in line cpi 20h rnz ;Return if not a space inx h jmp skipsp convert: mov a,m ;Check for Unera operation cpi '?' ;Check for UNERA operation jz unera sui 30h ;convert to binary mov c,a ;save it inx h mov a,m cpi 20h ;done with olduser #? jz legal cpi 00h ;done with newuser #? jz legal inx h ;bump pointer past second char sui 30h ;convert second digit mov b,a mov a,c ;test for add value cpi 1 jnz add20 add10: ;Is 9 < user# <20 mov a,b adi 10 mov c,a jmp legal add20: ;Is 19 < user# < 30 cpi 2 jnz add30 mov a,b adi 20 mov c,a jmp legal add30: ;Is 29 < user# mov a,b adi 30 mov c,a legal: ;Check for user number between 0 and 31 mov a,c cpi 32 rc lxi d,badusr ;Bad user number try again call print jmp boot ; If unera operation use E5 as the user# unera: mvi a,0e5h inx h ret ; THIS IS THE WORKING COPY OF THE BIOS JUMP TABLE WBOOT: DS 3 CONST: DS 3 CONIN: DS 3 CONOUT: DS 3 LIST: DS 3 PUNCH: DS 3 READER: DS 3 HOME: DS 3 SELDSK: DS 3 SETTRK: DS 3 SETSEC: DS 3 SETDMA: DS 3 READ: DS 3 WRITE: DS 3 LISTST: DS 3 SECTRAN: JMP STRAN ; SECTOR TRANSLATION ROUTINE FOR ; CP/M VERSIONS EARLIER THAN 2.0 ; REGS 'DE' CONTAIN ADDRESS OF 'XLTO' ; REGS 'BC' CONTAIN THE LOGICAL SECTOR NUMBER ; RETURNS PHYSICAL SECTOR NUMBER IN 'HL' REGISTERS STRAN: MVI B,0 XCHG DAD B MOV L,M RET DS 100 ;STACK DEPTH STACK: DS 0 ;LOCATION OF STACK END 100H