;PROGRAM: DUTIL ;AUTHOR: RICHARD CONN ;DERIVATION: DUTIL is derived from DU Version 7.5 ;VERSION: 1.1 ;DATE: 21 NOV 81 ;PREVIOUS VERSIONS: 1.0 (1 JAN 81) VERS EQU 11 ; DUTIL Version Number ; ; DUTIL is derived from -- ; DU.ASM V7.5 Revised 1/23/81 ; DISK UTILITY - By Ward Christensen ; ; Principal Authors of DU V7.5 are -- ; WLC KBP RGF BRR ; ; Key comments from DU V7.5 and DUTIL follow -- ; ;This version of DU is compatible with CP/M 1.4 and 2.x ;and does not require alteration for various hardware ;configurations. It adjusts itself automatically to ;the correct number of sectors, tracks, directory size, ;etc. It has been tested on 5-1/4" and 8" floppy, and ;10 megabyte hard disk systems. ; ;Because of the automatic adaption feature, no conditional ;assembly options are included. The only alteration that ;needs to be done is to use DDT to set the byte at 103h ;to zero for systems using a 2 mHz clock or non-zero for ;4 mHz clock. This only affects the time delay used in ;the 'sleep' command. ; ;For DUTIL, the additional value of PAGSIZ at 104h should ;be set for the size of the display (in lines) on the user's ;CON: device. Under DUTIL, all output is paged, and this ;determines the page limit. ; ;************************************************* ;* * ;* This program has been heavily modified * ;* to allow it to work without modification * ;* on most versions of CP/M 1.4 and, hopefully, * ;* all versions of CP/M 2.x. * ;* If you have difficulty getting this program * ;* to run, AND if you are using CP/M 2.x, AND * ;* if you know your BIOS to be bug-free, leave * ;* a message on Technical CBBS of Dearborn, * ;* Michigan (313)-846-6127 with a description * ;* of the problem and a summary of your hard- * ;* ware configuration. * ;* One known possible problem involves the * ;* system tracks on some systems, and results * ;* from the system sectors being skewed. There * ;* is NO way for a program executing under CP/M * ;* to know about this. This program assumes the * ;* standard convention of no skew being used on * ;: the system tracks. This usually isn't a prob- * ;* lem because the SYSGEN program can be used to * ;* get the system from the disk so that it can * ;* be modified. * ;* This program should work under standard * ;* versions of CP/M 1.4. The only requirement * ;* is that the BIOS "SETSEC" routine not modify * ;* the sector number passed to it in the B * ;* register. Again, system tracks with skewed * ;* sectors will be a problem. * ;* If you add any features or make any useful * ;* changes to this program, please modem a copy * ;* to the above CBBS, so the currency of the * ;* program can be maintained. * ;* * ;* Ron Fowler * ;* * ;************************************************* ; ; The last few revision notes for note are -- ; ;01/23/81 Changed SETSEC to ignore high-order result of ; SECTRN if SPT<256. This fixes some translation ; problems where the BIOS leaves garbage in H. (BRR) ; ;01/15/81 Changed labels to be no more than 6 characters ; long. Moved stack. Cleaned up file. (KBP) ; ;01/13/81 Updated help messages for '#' and 'N' commands. ; Modified sign-on message. (RGF) ; ;01/12/81 Fixed problem with sector translation under ; CP/M 1.4. (RGF) ; ; ;System equates ; BASE EQU 0 ;SET TO 4200H FOR HEATH OR TRS-80 ALTCPM ; ;CP/M Key Areas ; FCB EQU BASE+5CH ;CP/M FCB BDOS EQU BASE+5 ;CP/M BDOS ENTRY POINT TBUFF EQU BASE+80H ;CP/M TEMPORARY DISK I/O BUFFER TPA EQU BASE+100H ;CP/M TRANSCIENT PROGRAM AREA ; ;CP/M BDOS Function Codes ; PRINT EQU 9 GVERS EQU 12 RESETDK EQU 13 SELDK EQU 14 SRCHF EQU 17 ;SEARCH FIRST SUSER EQU 32 GETDSK EQU 25 GETDPB EQU 31 ; ;CP/M 1.4 Offsets and Some Key Values ; TRNOFF EQU 15 ;CP/M 1.4 OFFSET FROM BASE ;OF BDOS TO SECTRAN ROUTINE SKWOFF EQU 1AH ;CP/M 1.4 OFFSET TO SKEW TABLE S2OFF EQU 14 ;OFFSET INTO FCB FOR S2 BYTE DPBOFF EQU 3AH ;CP/M 1.4 OFFSET TO DPB WITHIN BDOS S2MASK EQU 0FH ;MASK FOR EXTENDED RC BITS OF S2 DPBLEN EQU 15 ;SIZE OF CP/M 2.x DISK PARM BLOCK ; ;Define ASCII characters ; CR EQU 0DH ;CARRIAGE RETURN LF EQU 0AH ;LINE FEED TAB EQU 09H ;TAB BS EQU 08H ;BACKSPACE ; ;Beginning of Program ; ORG TPA JMP START ;JUMP OVER CLOCK BYTE AND I.D. ; CLOCK: DB 2 ;<---Put Processor Speed Here (1=1MHZ, 2=2MHZ, etc) PAGSIZ: DB 24 ;<---Put CRT Screen Size Here (24 Lines default) DB 'DUTIL.COM from DU.COM ver 7.5 1/23/81 by RLC' ; START: LXI H,0 ;GET PTR TO CP/M STACK DAD SP ;HL=SP SHLD DUTSTK ;SAVE IT ; LXI SP,DUTSTK ; SET STACK ; MVI C,GVERS ;GET CP/M VERSION NR CALL BDOS MOV A,H ;COMBINE THE TWO BYTE... ORA L ;...VERSION NR FOR A FLAG STA VER2FL ;SAVE IT ; ;Set up local jumps to BIOS ; LHLD BASE+1 ;WARM BOOT POINTER LXI D,3 ;READY FOR ADD DAD D SHLD VCONST+1 ;CON: Status DAD D SHLD VCONIN+1 ;CON: Input DAD D SHLD VCONOT+1 ;CON: Output DAD D SHLD VLIST+1 ;LST: Output DAD D ;Skip PUNCH DAD D ;Skip RDR DAD D SHLD VHOME+1 ;Home Disk DAD D SHLD VSELDK+1 ;Select Disk DAD D SHLD VSETRK+1 ;Set Track DAD D SHLD VSTSEC+1 ;Set Sector DAD D SHLD SETDMA+1 ;Set DMA Address DAD D SHLD VREAD+1 ;Read Block From Disk DAD D SHLD VWRITE+1 ;Write Block To Disk LDA VER2FL ORA A JZ DOCPM1 DAD D ;Skip LISTST DAD D SHLD VSCTRN+1 ;CP/M 2.x Sector Translation Table JMP HELLO ; ;Set up CP/M 1.4 Parameters ; DOCPM1: LHLD BDOS+1 MVI L,0 ;BDOS ON PAGE BOUNDARY PUSH H LXI D,TRNOFF ;CP/M 1.4 SECTRAN ROUTINE OFFSET DAD D SHLD VSCTRN+1 ;CP/M 1.4 Sector Translation Table POP H LXI D,SKWOFF ;CP/M 1.4 SKEW TABLE OFFSET DAD D SHLD SECTBL ;SET UP SKEW TABLE POINTER ; ;Initialization Complete -- Print Signon Message and Begin Command Processing ; HELLO: CALL GETSTP ;SET UP CP/M PARAMETERS CALL INITP ;INITIALIZE BUFFER PARAMETERS XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB CR,LF,'DUTIL - Disk Utility, Version ' DB VERS/10+'0','.',(VERS MOD 10)+'0' DB CR,LF,' Derived From DISK UTILITY ver 7.5, ' DB 'Universal Version',CR,LF DB CR,LF DB 'Type ? for Help' DB CR,LF,0 LXI H,TBUFF ;TO INPUT BUFF MOV A,M ORA A JZ PRMPTR ;NO INITIAL COMMAND FROM COMMAND LINE ; ;Got initial command, set it up ; MOV B,A ;SAVE LENGTH DCR B ;JUST A SPACE? JZ PRMPTR ;GOTO COMMAND PROCESSOR IF SO LXI D,INBUF ;PT TO INLINE BUFFER INX H ;SKIP LEN INX H ;SKIP ' ' MOV A,M ;GET FIRST CHAR CPI '/' ;IF SLASH, PRINT INITIAL HELP (TOOLSET CONVENTION) JZ IHELP ;PRINT INITIAL HELP INFO CALL MOVE ;COPY INPUT LINE INTO INLINE BUFFER MVI A,CR ;STORE ENDING STAX D LXI H,INBUF ;PT TO FIRST BYTE OF INLINE BUFFER JMP PRMPTI ;PROCESS AS THOUGH IT WAS TYPED ; ;Input Command Line From User at Console ; PRMPTR: XRA A ;A=0 STA QFLAG ;Set Not Quiet CALL SINBUF ;Save old INBUF into PINBUF CALL RDBUF ;Read Input Line CALL EXMAC ;Expand Macros ; ;Begin Processing Command Line in INBUF ; At this point, HL points to next character to process ; PRMPTI: MVI A,0FFH ;SET INFINITE LOOP COUNT STA TOGO ;LOOP COUNT FOR "/" STA TOGO+1 ; ;Minor Command Loop; This is the entry point for each individual command in ; a Command Line; Commands may be separated by semicolons in this manner ; PROMPT EQU $ SETSTK: LXI SP,DUTSTK ;RESET STACK XRA A ;ZERO 2-UP PRINT FOR DUAL-COLUMN PRINT STA TWOUP ;..SWITCH MVI A,1 STA FTSW ;TELL SEARCH NOT TO INCR PUSH H LXI H,TBUFF ;SET NO-READ INPUT BUFFER ADDRESS SHLD BUFAD ;FOR RDBYTE POP H CALL CTLCS ;ABORT? JZ PRMPTR ;..YES, READ BUFFER ; ;Do we have to position in directory after find? ; LDA FINDFL ORA A JNZ POSDIR ;POSITION IN DIRECTORY ; ;Begin Command Evaluation -- Check for EOL and Capitalize ; MOV A,M ;GET NEXT CHAR IN COMMAND LINE INX H ;POINT TO FOLLOWING CHAR CPI CR ;END OF LINE PHYSICALLY? JZ PRMPTR ;INPUT NEW COMMAND LINE IF SO CPI ';' ;END OF LINE LOGICALLY? JZ PROMPT ;PROCESS NEXT ELEMENT IF SO CALL UPCASE ;CAPITALIZE COMMAND STA DUMTYP ;TYPE OF DUMP (A,D,H) ; ;Command dispatcher ; If command not found, abort with error message ; If command file, process command with HL pting to next command char and ; A containing command letter ; PUSH H ;SAVE HL MOV B,A ;COMMAND IN B LXI H,CMDTBL ;SCAN COMMAND TABLE FOR USER COMMAND CMDLP: MOV A,M ;GET COMMAND ORA A ;0=END OF TABLE JZ WHAT CMP B ;COMPARE COMMAND JZ CMDGO INX H ;PT TO ADR INX H INX H ;PT TO NEXT CMND JMP CMDLP CMDGO: INX H ;PT TO ADDRESS LOW MOV E,M INX H ;PT TO ADDRESS HIGH MOV D,M MOV A,B ;COMMAND BACK INTO A POP H ;RESTORE HL PUSH D ;PLACE ADDRESS ON STACK RET ;"RUN COMMAND" ; ;Macro Expansion Routine -- Expand Macros ; EXMAC: LXI H,INBUF ;PT TO INPUT LINE LXI D,CTEMP ;BUILD INTO TEMPORARY BUFFER EXMAC1: MOV A,M ;GET CHAR CPI '0' ;SKIP IF LESS THAN '0' JC EXMAC2 CPI '9'+1 ;CHECK FOR RANGE JNC EXMAC2 INX H ;PT TO NEXT CHAR PUSH H ;SAVE PTR TO NEXT CHAR IN LINE SUI '0' ;CONVERT TO BINARY (0-9) MOV B,A ;RESULT IN B MVI C,0 LXI H,MTABL ;PT TO BASE OF MACROS DAD B ;PT TO MACRO CALL COPYM ;COPY MACRO INTO LINE DCX D ;BACK UP OVER POP H ;GET PTR TO NEXT CHAR IN COMMAND LINE EXMAC2: MOV A,M ;GET CHAR STAX D ;PUT CHAR INX H ;PT TO NEXT INX D CALL MTEST ;TEST FOR END OF BUFFER CPI CR ;DONE? JZ EXMAC3 CPI ';' ;LOGICAL EOL? JNZ EXMAC2 JMP EXMAC1 ;PROCESS NEXT COMMAND EXMAC3: LXI H,CTEMP ;COPY COMMAND LINE BACK LXI D,INBUF ;INTO INBUF CALL COPYCR ;COPY TO LXI H,INBUF ;PT TO INBUF RET ;EXPANSION COMPLETE ; ;Copy Macro Into Command Line Buffer ; COPYM: MOV A,M ;GET CHAR STAX D ;PUT CHAR INX H ;PT TO NEXT INX D CALL MTEST ;CHECK FOR LIMIT CPI CR ;END OF MACRO? JNZ COPYM RET ; ;Test for Buffer Full ; MTEST: PUSH H ;SAVE HL PUSH PSW ;SAVE A LXI H,CTEMPX ;CHECK FOR END OF BUFFER MOV A,H ;GET PAGE CMP D ;CHECK PAGE JZ MACERR POP PSW ;GET A POP H ;GET HL RET ; ;Macro Command Expansion Error ; MACERR: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB CR,LF,'Error -- Macro Expanded Command Line too Long',0 JMP PRMPTR ;NEW COMMAND ; ;Save INBUF into PINBUF for later processing by '@' command ; SINBUF: LXI H,INBUF ;PT TO INBUF LXI D,PINBUF ;PT TO PINBUF (PREVIOUS INBUF) ; ;Copy (HL) to (DE) until Encountered ; COPYCR: MOV A,M ;GET CHAR STAX D ;PUT CHAR INX H ;PT TO NEXT INX D CPI CR ;DONE? JNZ COPYCR RET ; ;Command Not Found Error ; WHAT: XRA A ;TURN OFF QUIET FLAG STA QFLAG POP H ; RESTORE HL CALL ILPRT DB 'Invalid Command -- ',0 MOV A,B ;GET COMMAND LETTER CALL TYPE ;PRINT IT JMP PRMPTR ; ;Memory full error ; MEMFUL: XRA A ;TURN OFF QUIET FLAG STA QFLAG CALL ILPRT DB '+++ Out of memory +++' DB CR,LF,0 JMP PRMPTR ; ;COMMAND: @ ;Repeat Previous Command Line ; PCMD: MOV A,M ;GET NEXT CHAR CPI CR ;SHOULD BE JZ PCMD1 XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB CR,LF,'Warning: Remainder of Command Line after "@" Deleted',0 PCMD1: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB CR,LF,'Command --',CR,LF,0 LXI H,PINBUF ;GET PREVIOUS COMMAND LXI D,INBUF ;COPY INTO INBUF PCMD2: MOV A,M ;GET CHAR STAX D ;PUT CHAR INX H ;PT TO NEXT INX D CPI CR ;END OF LINE? PUSH PSW ;SAVE FLAG CALL TYPE ;PRINT CHAR POP PSW ;GET FLAG JNZ PCMD2 MVI A,LF ; CALL TYPE LXI H,INBUF ;RESTART COMMAND PROCESSING JMP PRMPTI ;INCLUDE LOOP CAPABILITY ; ;COMMAND: : ;Define or Print Macro ;:n Defines Macro n, 0<=n<=9; ::n Prints Macro n, 0<=n<=9 ; MAC: MOV A,M ;GET NEXT CHAR CALL UPCASE ;CAPITALIZE CPI 'P' ;PRINT MACRO? JNZ MACROD ;IF NOT, DEFINE MACRO INX H ;PT TO MACRO NUMBER MOV A,M ;GET IT CALL UPCASE ;CAPITALIZE CPI '@' ;PRINT PREVIOUS COMMAND? JZ PCPR PUSH PSW ;SAVE A XRA A ;A=0 STA QFLAG ;NOT QUIET CALL ILPRT DB 'Macro Definitions --',0 POP PSW ;GET A CPI 'A' ;PRINT ALL MACROS? JZ AMACPR CALL MNUM ;CHECK FOR VALID NUMBER AND RETURN # IN D INX H ;PT TO CHAR AFTER MACRO NUMBER CALL MACPR ;PRINT MACRO WHOSE NUMBER IS IN D JMP PROMPT ; ;Print Previous Command ; PCPR: INX H ;PT TO CHAR AFTER '@' LXI D,PROMPT ;SET UP RET ADR PUSH D ;RETURN ADR ON STACK PUSH H ;SAVE PTR XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB 'Previous Command Line Definition --' DB CR,LF,'@: ',0 LXI H,PINBUF ;PT TO PREVIOUS COMMAND JMP MPRINT ;USE MACRO PRINT FACILITY ; ;Print All Macros ; AMACPR: INX H ;PT TO CHAR AFTER 'A' MVI D,0 ;SET FOR FIRST MACRO AMPRL: CALL MACPR ;PRINT MACRO WHOSE NUMBER IS IN D INR D ;INCREMENT MACRO NUMBER MOV A,D ;GET VALUE CPI 10 ;DONE? JNZ AMPRL JMP PROMPT ;CONTINUE PROCESSING ; ;Print Macro Whose Number (0-9) is in D ; MACPR: PUSH H ;SAVE PTR XRA A ;NOT QUIET STA QFLAG CALL ILPRT ;PRINT HEADER DB CR,LF,0 MOV A,D ;GET NUMBER ADI '0' ;CONVERT TO ASCII CALL TYPE ;PRINT CALL ILPRT DB ': ',0 LXI H,MTABL ;PT TO TABLE OF MACROS MVI E,0 ;PAGE OFFSET OF ZERO; MACRO NUMBER ALREADY IN D DAD D ;PT TO MACRO MPRINT: MOV A,M ;GET CHAR INX H ;PT TO NEXT CPI CR ;END OF MACRO? PUSH PSW ;SAVE FLAG CALL TYPE ;PRINT CHAR POP PSW ;GET FLAG JNZ MPRINT MVI A,LF ; CALL TYPE POP H ;GET PTR TO NEXT CHAR RET ; ;Check char in A for valid Macro Number (0-9), print error message if ; not, return number in D if so ; MNUM: SUI '0' ;CONVERT TO 0-9 JC MNERR ;ERROR IF LESS CPI 10 ;RANGE? JNC MNERR MOV D,A ;RESULT IN D RET MNERR: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB CR,LF,'Invalid Macro Number Specified in Command',0 JMP PRMPTR ;NEW COMMAND ; ;Define Macro ; MACROD: CALL MNUM ;CHECK NUMBER AND RETURN IN D INX H ;PT TO CHAR AFTER MACRO NUMBER PUSH H ;SAVE PTR LXI H,MTABL ;PT TO MACRO TABLE MVI E,0 ;SET EVEN PAGE DAD D ;PT TO MACRO ENTRY IN HL XCHG ;... IN DE POP H ;PT TO MACRO TEXT CALL COPYCR ;COPY TO JMP PRMPTR ;NEW COMMAND ; ;COMMAND: ! ;Delay for user input ; UWAIT: CALL WAIT ; USE WAIT ROUTINE JMP PROMPT ; ;COMMAND: # ;Print disk statistics ; STATS: PUSH H ;SAVE POINTER TO NEXT COMMAND CALL ILPRT DB '===============================',CR,LF DB ' -- Disk Information -- ',CR,LF DB '-------------------------------',CR,LF DB CR,LF,'Disk Drive: ',0 LDA DRIVE ADI 'A' ;CONVERT TO ASCII CALL TYPE ;PRINT DRIVE LETTER CALL ILPRT DB CR,LF,'Tracks: ',0 LHLD MAXTRK ;PRINT NUMBER OF TRACKS INX H CALL DEC CALL ILPRT DB CR,LF,'Sectors/Track: ',0 LHLD SPT ;PRINT NUMBER OF SECTORS/TRACK CALL DEC CALL ILPRT DB CR,LF,'Group Size: ',0 LDA BLM ;PRINT SIZE OF A GROUP INR A MOV L,A MVI H,0 CALL DEC CALL ILPRT DB ' Blocks/Group' DB CR,LF,'Total Groups: ',0 LHLD DSM ;PRINT TOTAL NUMBER OF GROUPS ON A DISK CALL DEC CALL ILPRT DB CR,LF,'Directory Entries: ',0 LHLD DRM ;PRINT NUMBER OF DIRECTORY ENTRIES INX H CALL DEC CALL ILPRT DB CR,LF,'System Tracks: ',0 LHLD SYSTRK ;PRINT NUMBER OF SYSTEM TRACKS CALL DEC CALL ILPRT DB CR,LF DB '===============================',CR,LF,0 POP H ;RESTORE POINTER TO NEXT COMMAND JMP PROMPT ; ;COMMAND: N ;The following command resets the disk ;system thru CP/M, and may be usable for ;changing the disk density or format. ;This can only be done if your BIOS resets ;the auto-density select parameters at ;every track-zero access. ; NEWDSK: PUSH H ;SAVE POINTER TO NEXT LETTER MVI C,RESETDK ;BDOS RESET DISK FUNCTION CALL BDOS LDA DRIVE ;RESELECT CURRENT DRIVE MOV C,A POP H CALL SELECT JMP PROMPT ; ;COMMAND: Q ;Quite mode ; QUIET: STA QFLAG ;NOW QUIET (FLAG IS NON-ZERO) JMP PROMPT ; ;COMMAND: / ;Repeat buffer contents ; REPEAT: CALL DECIN ;NN SPECIFIED? MOV A,D ORA E JZ NNN ;NO -- SET FOR INFINITE LOOP OR SIMPLE REPEAT LHLD TOGO ;LOAD LOOP FLAG INX H ;TEST FOR FIRST TIME MOV A,H ORA L ;WAS IT 0FFFFH?; IF SO, WE HAVE NEW VALUE JNZ NNN ;NO: COUNTING XCHG ;GET COUNT SHLD TOGO ;SET COUNT ; NNN: LHLD TOGO ;GET CURRENT COUNT XCHG ;DE=CURRENT COUNT, HL=COUNT LIMIT LXI H,INBUF ;PT TO FIRST CHAR FOR REPEAT INX D ;TEST FOR 0FFFFH MOV A,D ;IF 0FFFFH, INX D MADE DE=0 ORA E JZ PROMPT ;CONTINOUS LOOP IF 0FFFFH DCX D ;COUNT DOWN DCX D ;MAKE UP FOR PREV INX D XCHG SHLD TOGO ;SET NEW COUNT (1 LESS THAN BEFORE) MOV A,H ;ALL DONE? ORA L XCHG ;GET BACK INBUF PTR IN HL JNZ PROMPT ;KEEP GOING IF NOT YET ZERO JMP PRMPTR ;ALL DONE ; ;COMMAND: U ;Set CP/M 2.x user number ; USER: LDA VER2FL ;CP/M 2.X? ORA A JZ WHAT ;ERROR IF NOT CALL DECIN ;GET REQUESTED USER NO. MOV A,E CPI 32 ;VALID? JNC WHAT MOV A,D ;HIGH-ORDER BYTE MUST BE ZERO FOR VALID NUMBER ORA A JNZ WHAT MOV A,E ;SAVE USER NUMBER STA UNUM MVI C,SUSER ;SET USER NUMBER PUSH H ;SAVE CHAR POINTER CALL BDOS ;SET USER NO. POP H JMP PROMPT ; ;COMMAND: P ;Toggle print flag ; PRNTFF: LDA PFLAG ;TOGGLE PRINT FLAG XRI 1 STA PFLAG JMP PROMPT ; ;COMMAND: Z ;Sleep routine, in seconds ; SLEEP: CALL DECIN ;GET COUNT IF ANY MOV A,E ;ANY? ORA A JNZ SLEPLP MVI E,1 ; 1 SEC DEFAULT ; SLEPLP: LDA CLOCK ; GET CLOCK SPEED MOV D,A ; SLEEP1: LXI B,41700 ; APPROX 1 SEC @ 1MHz ; SLEEP2: DCX B ;COUNT DOWN FOR 1 MHz [5 CYCLES] MOV A,B ;[5 CYCLES] <-- TOTAL TIME: 24 CYCLES ORA C ;[4 CYCLES] <-- (24 MU-SECS AT 1MHz) JNZ SLEEP2 ;[10 CYCLES] PUSH D CALL CTLCS ;ABORT? POP D JZ PRMPTR DCR D ;COUNT DOWN FOR CLOCK SPEED JNZ SLEEP1 DCR E ;COUNT DOWN NUMBER OF REQUESTED SECONDS JNZ SLEPLP JMP PROMPT ; ;Check for control-C or S ; CTLCS: CALL CONST ;CHAR AVAILABLE? ORA A JNZ GETC ORI 1 ;NO CHAR, RETURN NZ RET ; GETC: CALL CONIN ;INPUT CHAR ANI 1FH ;ALLOW ASCII CPI 'S'-40H ;WAIT FOR NEXT CHAR IF ^S OR S OR s CZ CONIN CPI 'C'-40H ;CHECK FOR ^C OR C OR c RET ;0 SET IF CTL-C ; ;Initialize Memory Buffers ; INITP: XRA A ;A=0 STA GLFLAG ;CLEAR GROUP LOADED FLAG (GROUP NOT LOADED) STA HEXAD ;CLEAR ADDRESS STA HEXAD+1 STA PFLAG ;SET NO PRINT STA SAVEFL ;SET NO SAVE DONE STA WRFLG ;MAY NOT WRITE STA DIRPOS ;SET NO DIRECTORY POSITION STA FINDFL ;SET NO POSITION INR A ;A=1 STA FTSW ;SET SEARCH WITHOUT INCREMENT STA NOTPOS ;NOT POSITIONED LXI H,0 ;HL=0 SHLD MFPTR ;SET NO MULTI FILE PTR SHLD CURTRK ;SET TRACK 0 INX H ;HL=1 SHLD CURSEC ;SET LOGICAL SECTOR 1 SHLD PHYSEC ;SET PHYSICAL SECTOR 1 MVI A,CR ;CLEAR PREVIOUS COMMAND STA PINBUF ;SET PREVIOUS COMMAND TO NIL LXI H,MTABL ;CLEAR MACRO TABLE MVI B,10 ;10 ENTRIES INITP1: MVI M,CR ;STORE INR H ;PT TO NEXT PAGE DCR B ;COUNT DOWN JNZ INITP1 RET ; ;Set up flags, etc, at initialization ;Find our way at initialization ; GETSTP: MVI A,CR ;INITIALIZE INPUT BUFFER STA INBUF ;EMPTY BUFFER LDA VER2FL ;VERSION 2 OR BETTER? ORA A ;0=1.X JZ GSTP1 MVI C,SUSER ;GET USER NUMBER MVI E,0FFH ;GET USER CALL BDOS STA UNUM ;SET USER NUMBER GSTP1: MVI C,GETDSK CALL BDOS ;GET CURRENT DISK MOV C,A ;WE HAVE TO SELECT JMP SELECT ;TO GET THE DPH ; ;COMMAND: L ;Log in the selected disk ; LOGIN: CALL DOLOG JMP PROMPT ; DOLOG: MOV A,M ;DISK REQUESTED? LXI D,0 CPI CR ;NO REQUEST OF PHYSICAL EOL JZ LGNODK CPI ';' ;NO REQUEST IF LOGICAL EOL JZ LGNODK CALL UPCASE ;CAPITALIZE INX H ;POINT TO NEXT CHAR SUI 'A' ;CONVERT TO 0-15 MOV C,A ;DISK NUMBER IN C ; ;Select Disk Whose Number is in C (A=0, B=1, etc) ; SELECT: PUSH H ;SAVE PTR TO NEXT COMMAND LETTER MOV A,C STA DRIVE ;REMEMBER LATER WHERE WE ARE ; VSELDK: CALL $-$ ;ADDR FILLED IN BY 'INIT' LDA VER2FL ORA A ;IF NOT CP/M 2.x ... JZ SELSKP ;..THEN SKIP THIS JUNK MOV A,H ORA L JZ WHAT ;SELECT ERROR MOV E,M ;GET THE SECTOR TABLE PNTR INX H MOV D,M INX H XCHG SHLD SECTBL ;SET THE SECTOR TABLE PTR LXI H,8 ;OFFSET TO DPBPTR DAD D MOV A,M ;PICK UP DPB POINTER INX H ; TO USE MOV H,M ; AS PARAMETER MOV L,A ; TO LOGIT ; SELSKP: CALL LOGIT LHLD SYSTRK ;RESET TRACK AND SECTOR XCHG ; TO DIRECTORY CALL SETTRK ; ON EVERY LXI D,1 ; LOGIN CALL SETSEC ; CHANGE LHLD PHYSEC ;THIS LOGIC WILL TELL MOV A,H ; IF FIRST SEC ORA L ; IS PHYSICAL 0 STA FIRST0 CALL CLCSUB ;CALCULATE WHAT GROUP/GRPDISP WE ARE IN POP H ;GET PTR TO NEXT LETTER ; LGNODK: CALL NORITE ;SET NO DISK I/O DONE (NO POSITION) RET ; ;Read in the disk directory ; REDDIR: PUSH H ;SAVE PTR TO NEXT LETTER CALL NORITE ;POSITIONING LOST LHLD SYSTRK ;SAVE CURRENT TRACK SHLD CURTRK LXI H,1 ;SET SECTOR 1 SHLD CURSEC LHLD DRM ;GET DIR SIZE FROM DPB INX H ;MAKE 1-RELATIVE CALL ROTRHL CALL ROTRHL ;DIVIDE BY 4 (4 NAMES/SECTOR) MOV B,H ;BC=NUMBER OF BLOCKS TO READ MOV C,L LXI D,DIRECT ;DMA ADDR XRA A ;A=0 STA GLFLAG ;SAY NO GROUP LOADED ; ;Read Disk Directory Loop ; RDIRLP: PUSH B ;SAVE REGS PUSH D MOV B,D ;BC=DMA ADDRESS MOV C,E LDA BDOS+2 ;CHECK MEM AVAIL DCR A ;ARE WE RNNING INTO BDOS? CMP D JC MEMFUL ;MEMORY FULL ERROR IF SO CALL SETDMA ;SET DMA ADDRESS TO THAT IN BC LHLD CURTRK ;SET TRACK XCHG CALL SETTRK LHLD CURSEC ;SET SECTOR XCHG CALL SETSEC CALL READ ;READ DIRECTORY BLOCK CALL NXTSEC ;INCREMENT TO NEXT SECTOR POP D POP B LXI H,80H ;ADVANCE TO NEXT DMA ADDRESS DAD D XCHG ;DE=NEXT DMA ADDRESS DCX B ;COUNT DOWN DIRECTORY BLOCKS MOV A,B ORA C JNZ RDIRLP LXI B,TBUFF ;RESET DMA ADDRESS TO TBUFF CALL SETDMA POP H ;GET PTR TO NEXT CHAR RET ; ;COMMAND: M ;Map the directory ; MAP: CALL PAGSET ;SET PAGING COUNTER XRA A STA ONLY1 ;SET FLAG FOR ALL GROUPS (NOT ONLY 1) CALL REDDIR ;READ IN DIRECTORY MVI C,0 ;INIT START GRP # LDA AL0 ;READ DIR GRP BITS CALL COLECT ;COLLECT COUNT OF DIR GRPS.. LDA AL1 ;..IN REGISTER C CALL COLECT MVI B,0 ;BC NOW HAS A DEFAULT START GRP # CALL HEXIN ;GET SPECIFIED GROUP IF ANY PUSH H ;SAVE INBUF PTR MOV A,E ;GET START ORA D ;NOTHING? JZ MAPDF ;..YES, DFLT MVI A,0FFH ;SET FLAG FOR ONLY 1 GROUP STA ONLY1 MOV B,D ;GET VALUE IN BC MOV C,E ; MAPDF: CALL HEXB ;PRINT FIRST GROUP NUMBER MVI A,'-' ;PRINT SEPARATOR CALL TYPE MVI A,' ' ;SET NO DUPLICATES STA DUPFLG CALL GETGRP ;GET GRP(C) TO HL ; MAPCNT: INX B ;NEXT GRP # PUSH H LHLD DSM ;GET HIGHEST GRP # INX H ;PLUS 1 FOR COMPARISON MOV A,L ;WHEN BC REACHES DSM+1.. CMP C ;..THEN WE HAVE EXCEEDED.. JNZ MAPC1 ;..THE DISK CAPACITY.. MOV A,H CMP B ; MAPC1: POP H JZ MAPEND ;..AND WE ARE DONE PUSH H CALL GETGRP ;GET ANOTHER POP D ;SEE IF SAME CALL CTLCS ;ABORT? JZ MAPND2 MOV A,D CMP H JNZ MAPDIF MOV A,E CMP L JZ MAPCNT ;SAME, CONTINUE ; ;Different file encountered ; MAPDIF: DCX B CALL HEXB ;PRINT ENDING GROUP NUMBER INX B XCHG CALL MAPNAM ;PRINT FILE NAME LDA ONLY1 ;ONLY 1 NAME TO BE PRINTED? ORA A ;0=NO JNZ MAPND1 JMP MAPDF ; ;End of map ; MAPEND: DCX B ;GET LAST CALL HEXB ;PRINT LAST GROUP NUMBER CALL MAPNAM ;PRINT FILE NAME CALL WAIT ;DELAY FOR USER MAPND1: POP H CALL CRLF ;NEW LINE ; ;End of map - reposition to previous group ; MAPND2: PUSH H LHLD GROUP ;POINT TO GROUP IN DE XCHG JMP POSGP2 ; ;Print file name pointed to by HL ; MAPNAM: CALL SPACE ;LEADING SPACE MOV A,H ORA L ;NONE? JZ NONAME MOV A,M ;SEE IF ALLOC CPI 0E5H ;FREE? MVI A,' ' ;MARK ALLOCATED JNZ MPNSP1 MVI A,'(' ;MARK NOT ALLOCATED (ERASED FILE) ; MPNSP1: CALL TYPE ;PRINT ALLOCATION INDICATOR (SPACE OR '(') PUSH H ;SAVE POINTER MOV A,M CALL HEX ;SHOW USER NUMBER CALL SPACE INX H ;SKIP USER BYTE PUSH B MVI B,8 ;PRINT FILE NAME CALL MAPN2 MVI A,'.' ;PRINT DECIMAL SEPARATOR CALL TYPE MVI B,3 ;PRINT FILE TYPE CALL MAPN2 LDA DUPFLG ;DUPLICATE? CALL TYPE ;SPACE OR STAR POP B MOV A,M ;GET EXT CALL HEX ;PRINT EXTENT NUMBER POP H MOV A,M CPI 0E5H ;DELETED ENTRY? MVI A,' ' ;PRINT ENDING SPACE JNZ MPNSP2 MVI A,')' ;PRINT ALLOCATION FLAG ; MPNSP2: CALL TYPE ;")" IF ERASED FILE OR SPACE IF NOT JMP FLIP ; NONAME: CALL ILPRT DB ' ++ Free ++ ',0 ; FLIP: LDA TWOUP ;FLIP FLAG FOR TWO ENTRIES PER LINE XRI 1 STA TWOUP JZ PAGER ;NEW LINE WITH PAGING IF REQUIRED ; DELIM: MVI A,':' ;PRINT DELIMITER BETWEEN ADJACENT ENTRIES ON LINE CALL TYPE JMP SPACE ; ;Print name pted to by HL, length in B ; MAPN2: MOV A,M ANI 7FH ;STRIP POSSIBLE 2.x ATTRIBUTE BIT INX H CPI ' ' ;PRINTABLE? JC MAPN2H ;..NO, IN HEX CPI 7EH ;7E IS LEADIN ON SOME CRTS JC MAPN2A ; MAPN2H: CALL BHEX ;PRINT A AS HEX CHARS JMP MAPN2Z ; MAPN2A: CALL TYPE ;PRINT AS CHAR ; MAPN2Z: DCR B ;COUNT DOWN JNZ MAPN2 RET ; ;Find which file group (BC) belongs to ; GETGRP: LHLD DRM ;MAX DIR ENTRY # INX H ;MAKE 1-RELATIVE SHLD FILECT LXI H,0 SHLD MFPTR ;SET MULTI-FILE (MORE THAN ONE USER) PTR LXI H,DIRECT ;PT TO DIRECTORY XRA A ;SAY NO GROUP LOADED STA GLFLAG ; GETGLP: PUSH H ;SAVE POINTER TO NAME MOV A,M ;PICK UP DN BYTE CPI 0E5H ;ERASED? JZ GETGNF LXI D,14 ;NOW GET RECORD COUNT DAD D ; S2 PORTION .. MOV A,M ; IS 0 IN CP/M 1.4 ANI 0FH MOV E,A INX H MOV A,M ORA E JZ GETGNF MVI E,16 ;FIRST SET FOR 8-BIT GRPS LDA DSM+1 ORA A JZ SMALGP MVI E,8 ;NOPE, BIG GROUPS ; SMALGP: MOV D,A ;SAVE GRP SIZE INDICATOR ; GETGL2: INX H ;POINTING INTO DM FIELD CALL GRPCMP ;COMPARE BC GP # AGAINST 1 DM FLD JNZ NOTGOT ;JUMP IF NOT FOUND ; ;Found the file ; PUSH H ;SAVE GROUP PTR LHLD MFPTR MOV A,H ;ANY ENTRIES? ORA L POP H ;GET PTR XTHL ;SAVE ENTRY START AND SAVE PTR JZ MPFRST ;IF ZERO, THEN FIRST ENTRY MVI A,'*' ;SET MULTI FLAG STA DUPFLG MPFRST: SHLD MFPTR ;SAVE POINTER XTHL ;RESTORE ENTRY START AND GET PTR NOTGOT: DCR E ;COUNT DOWN JNZ GETGL2 ;GO TEST SOME MORE ; GETGNF: POP H ;NOT THIS ONE LXI D,32 ;SO GO TO NEXT DAD D XCHG LHLD FILECT ;THERE IS LIMIT TO EVERYTHING DCX H SHLD FILECT MOV A,H ORA L XCHG ;RE-ALIGN JNZ GETGLP ; ;Set the allocation address, if any ; LHLD MFPTR ;GET ADDRESS RET ; ;COMMAND: < ;Save the current sector ; SAVE: LDA WRFLG ;READ DONE? ORA A JZ BADW ;NONE TO SAVE PUSH H LXI H,TBUFF ;COPY FROM TBUFF LXI D,SAVBUF ;INTO SAVBUF MVI B,128 ;128 BYTES CALL MOVE MVI A,1 ;..SHOW STA SAVEFL ;..SAVED EXISTS POP H ;GET PTR TO NEXT CHAR JMP PROMPT ; ;This routine is common to Save Group (RG) and Write Group (WG); it is used ; to extract the group number, check it, and position DUTIL to it ; On exit, GROUP = Group Number, GRPDIS = 0, and DUTIL is positioned ; COMG: INX H ;PT TO CHAR AFTER 'G' OF ' ;Restore the current sector ; RESTOR: LDA SAVEFL ;SAVE DONE PREVIOUSLY? ORA A JZ NOSAVE ;NONE TO SAVE PUSH H LXI H,SAVBUF ;COPY FROM SAVBUF LXI D,TBUFF ;INTO TBUFF MVI B,128 ;128 BYTES CALL MOVE POP H ;GET PTR TO NEXT CHAR JMP PROMPT ; ;Write Group Loaded in GBUFF to Disk ; RESTRG: LDA GLFLAG ;GROUP ALREADY LOADED? ORA A ;0=NO JZ RGERR CALL COMG ;GET GROUP NUMBER FROM COMMAND LINE AND POS PUSH H CALL ILPRT DB 'Writing to Group ',0 LHLD GROUP ;GET GROUP NUMBER MOV B,H ;VALUE IN BC MOV C,L CALL HEXB ;PRINT IN HEX CALL ILPRT DB CR,LF,0 POP H MVI A,0FFH ;WRITE FUNCTION STA CPYFCT ;COPY FUNCTION FOR GROUP COPY ROUTINE JMP COPYG ;GROUP COPY ROUTINE ; RGERR: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '++ No "RG" Read Group Command Issued or Loaded Group' DB ' Trashed ++' DB CR,LF,0 JMP PRMPTR ; NOSAVE: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '++ No "<" Save Command Issued ++' DB CR,LF,0 JMP PRMPTR ; ;Move (HL) to (DE) length in B ; MOVE: MOV A,M STAX D INX H INX D DCR B JNZ MOVE RET ; NORITE: XRA A ;GET 0 STA WRFLG ;CAN'T WRITE NOW RET ; ;No match in search, try next char ; SRNOMT: POP H CALL CTLCS ;ABORT? JNZ SEARCH ;..YES LXI H,INBUF MVI M,CR JMP CLCGRP ;SHOW WHERE STOPPED ; ;COMMAND: = ;Search for character string ; SEARCH: PUSH H ;SAVE STRING POINTER ; SRCHL: CALL RDBYTE ;GET A BYTE MOV B,A ;SAVE IT MOV A,M ;CHECK NEXT MATCH CHAR. CPI '<' ;WILL IT BE HEX? MOV A,B ;RESTORE DISK CHAR JZ SRCHL1 ANI 7FH ;NEXT CHAR IS ASCII...STRIP BIT 7 ; SRCHL1: PUSH PSW CALL GETVAL ;GET SEARCH VALUE MOV B,A POP PSW CMP B ;MATCH? JNZ SRNOMT ;NO MATCH INX H MOV A,M ;DONE? CPI CR ;END OF LINE? JZ SREQU CPI ';' ;LOGICAL EOL? JNZ SRCHL ; ;Got match ; SREQU: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '= at ',0 LDA BUFAD ANI 7FH CALL HEX CALL CRLF JMP CLCGRP ; ;Get value from input buffer ; GETVAL: MOV A,M ;GET NEXT CHAR CPI '<' ;HEX ESCAPE? RNZ ;NO, RETURN ;"<<" means one "<" INX H MOV A,M CPI '<' RZ ;Got hex PUSH D CALL HEXIN ;GET VALUE CPI '>' ;PROPER DELIM? MOV A,E ;GET VALUE POP D JNZ WHAT ;ERROR RET ; ;Read a byte at a time from disk ; RDBYTE: PUSH H LDA FTSW ;FIRST READ? ORA A JNZ READ1 LHLD BUFAD MOV A,L ORA A ;IN BUFFER? JM NORD ;YES, SKIP READ ; ;Have to read ; CALL NXTSEC ;ADVANCE TO NEXT BLOCK ; READ1: XRA A STA FTSW ;NOT FIRST READ LHLD CURSEC XCHG CALL SETSEC LHLD CURTRK XCHG CALL SETTRK CALL READ CALL CLCSUB LXI H,TBUFF ; NORD: MOV A,M INX H SHLD BUFAD POP H RET ; ;COMMAND: V ;View the file in ASCII starting at ;current sector, stepping thru the disk ; VIEW: LDA WRFLG ORA A JZ BADDMP CALL DECIN ;GET DISPL IF ANY PUSH H MOV A,E ORA A JNZ VIEWLP INR E ;DFLT=1 ; VIEWLP: LXI H,TBUFF ;TO DATA ; VEWCHR: CALL CTLCS ;ABORT? JZ VEWEND MOV A,M ;GET NEXT CHAR CPI 1AH ;EOF? JZ VEWEOF ANI 7FH ;MASK CPI 7EH ;ESC CHAR FOR H1500 JNC VIEWHX ;SHOW RUBOUT AND TILDE AS HEX CPI ' ' JNC VIEWPR CPI CR ;CR PASS JZ VIEWPR CPI LF ;LF PASS JZ VIEWPR CPI TAB ;TAB PASS JZ VIEWPR ; VIEWHX: MOV A,M ;NOT ASCII...PRINT AS CALL BHEX JMP VIEWNP ; VIEWPR: CALL TYPE ; VIEWNP: INR L JNZ VEWCHR DCR E JZ VEWEND PUSH D ;SAVE COUNT CALL NXTSEC LHLD CURSEC XCHG CALL SETSEC LHLD CURTRK XCHG CALL SETTRK CALL READ POP D ;RESTORE COUNT JMP VIEWLP ; VEWEOF: CALL ILPRT DB CR,LF,' ++ EOF ++',CR,LF,0 ; VEWEND: POP H CALL CRLF JMP CLCGRP ; ;COMMAND: A or D ;Dump in hex or ASCII ; DUMP: LDA WRFLG ORA A JNZ DUMPOK ; BADDMP: XRA A STA QFLAG ;NOT QUIET CALL ILPRT DB '++ Can''t dump, no sector read ++',CR,LF,0 ; EXPL: XRA A STA QFLAG ;NOT QUIET CALL ILPRT DB 'Use G command following F,',CR,LF DB 'or R or S following T',CR,LF,0 JMP PRMPTR ; DUMPOK: MOV A,M ;GET NEXT CHAR CPI ';' ;LOGICAL EOL? JZ DUMPDF ;DFLT CPI CR ;PHYSICAL EOL? JNZ DMPNDF ; ;Use default ; DUMPDF: LXI B,TBUFF LXI D,0FFH JMP DUMP1 ; DMPNDF: CALL DISP MOV B,D MOV C,E CPI CR JZ DUMP1 CPI ';' JZ DUMP1 INX H ;SKIP ',' CALL DISP ; ;BC = start, DE = end ; DUMP1: PUSH H ;SAVE COMMAND POINTER MOV H,B MOV L,C ; DUMPLP: MOV A,L ANI 7FH CALL HEX ;PRINT HEX VALUE CALL SPACE CALL SPACE LDA DUMTYP CPI 'A' JZ DUMPAS PUSH H ;SAVE START ; DHEX: MOV A,M CALL HEX ;PRINT HEX VALUE PTED TO BY HL MOV A,L ANI 3 CPI 3 ;EXTRA SPACE EVERY 4 CZ SPACE MOV A,L ANI 7 CPI 7 ;TWO EXTRA SPACES EVERY 8 CZ SPACE MOV A,E CMP L JZ DPOP INX H MOV A,L ANI 0FH JNZ DHEX ; DPOP: CALL CTLCS ;ABORT? JZ PRMPTR LDA DUMTYP CPI 'H' JZ DNOAS ;HEX ONLY POP H ;GET START ADDR ; DUMPAS: CALL ASTER ;PRINT FIRST ASTERISK TO SEPARATE TEXT ; DCHR: MOV A,M ;GET CHAR ANI 7FH CPI ' ' JC DPER CPI 7EH ;TRAP ESC FOR H1500 JC DOK ; DPER: MVI A,'.' ;PRINT PRINTING CHAR ; DOK: CALL TYPE ;PRINT CHAR MOV A,E CMP L JZ DEND INX H MOV A,L ANI 0FH JNZ DCHR ; DEND: CALL ASTER ;PRINT ENDING ASTERISK CALL CRLF ;NEW LINE PUSH D CALL CTLCS ;ABORT? POP D JZ PRMPTR MOV A,E CMP L JNZ DUMPLP POP H JMP PROMPT ; DNOAS: POP B CALL CRLF MOV A,E CMP L JNZ DUMPLP POP H JMP PROMPT ; ;COMMAND: G ;Position ; POS: PUSH PSW MOV A,M CPI ';' ;LOGICAL EOL? JZ POSINQ CPI CR ;PHYSICAL EOL? JNZ POSOK ; POSINQ: POP PSW JMP INQ ; POSOK: POP PSW CPI 'T' ;TRACK? JZ POSTKD CPI 'S' ;SECTOR? JZ POSSCD CPI 'G' ;GROUP? JZ POSGPH JMP WHAT ;ERROR OTHERWISE ; ;Position to Track ; POSTKD: CALL DECIN ;GET NUMBER IN DECIMAL ; POSTRK: PUSH H LHLD MAXTRK ;CHECK FOR BEYOND END OF DISK CALL SUBDE POP H JC OUTLIM CALL SETTRK ;SET TRACK CALL NORITE ;TRACK DOESN'T READ MVI A,1 STA NOTPOS ;SHOW NOT POSITIONED JMP CLCGRP ; ;Position to Sector ; POSSCD: CALL DECIN ;GET NUMBER IN DECIMAL MOV A,D ORA E JZ WHAT ;DON'T ALLOW SECTOR 0 ; POSSEC: PUSH H LHLD SPT ;CHECK FOR WITHIN RANGE CALL SUBDE POP H JC WHAT CALL SETSEC ;SET SECTOR CALL READ ;READ XRA A STA NOTPOS ;POSITIONED OK ; ;Calculate Group Number/Group Displacement and Print ; CLCGRP: CALL CLCSUB JMP INQ ; ;Calculate group from track and sector ; On exit, GROUP = Group Number and GRPDIS = Displacement within Group ; CLCSUB: PUSH H LHLD SYSTRK XCHG LHLD CURTRK CALL SUBDE ;COMPUTE RELATIVE TRACK NUMBER (SKIP SYSTEM TRACKS) XCHG LHLD SPT ;MULTIPLY BY NUMBER OF SECTORS/TRACK CALL MULT XCHG ;DE=TOTAL NUMBER OF SECTORS IN TRACKS LHLD CURSEC ;GET SECTOR OFFSET FROM BEGINNING OF TRACK DCX H DAD D ;HL=TOTAL NUMBER OF SECTORS WITH OFFSET LDA BLM MOV B,A MOV A,L ANA B STA GRPDIS ;DISPLACEMENT WITHIN GROUP LDA BSH MOV B,A ; CLCLOP: CALL ROTRHL DCR B JNZ CLCLOP SHLD GROUP ;GROUP NUMBER POP H RET ; ;Position in the directory after a find ;(Does not work in CP/M-2.x) ; POSDIR: PUSH H ;SAVE INBUF LHLD BSH XRA A STA FINDFL ;CANCEL POS REQ LDA DIRPOS ;GET POSITION RAR RAR PUSH PSW ANA H STA GRPDIS POP PSW ; POSDLP: RAR DCR L JNZ POSDLP ANI 1 ;GET GROUP MOV L,A ;SETUP FOR POSGP2 MVI H,0 SHLD GROUP XCHG JMP POSGP2 ;POSITION TO IT ; ;Position to Group ; POSGPH: CALL HEXIN ;GET PARAMETER ; ;Position to Group Numbered in DE and Print Position ; POSGRP: PUSH H LHLD DSM ;CHECK FOR WITHIN BOUNDS CALL SUBDE POP H JC OUTLIM XCHG SHLD GROUP ;SET GROUP NUMBER XCHG XRA A STA GRPDIS ;SET ZERO DISPLACEMENT PUSH H ; POSGP2: CALL GTKSEC ;CONVERT GROUP TO SECTOR/TRACK CALL SETTRK ;SET TRACK XCHG CALL SETSEC ;SET SECTOR CALL READ ;READ BLOCK XRA A STA NOTPOS ;NOW POSITIONED POP H JMP INQ ; ;Convert Group Number in DE to Sector and Track; also, GRPDIS = Offset in Grp ; On exit, DE = Track Number, HL = Sector Number ; GTKSEC: MOV H,D ;HL=GROUP NUMBER MOV L,E LDA BSH ;GET NUMBER OF SECTORS IN GROUP ; GLOOP: DAD H DCR A JNZ GLOOP LDA GRPDIS ;ADD IN DISPLACEMENT WITHIN GROUP ADD L ;CAN'T CARRY MOV L,A ; ;Divide by number of sectors, quotient=track, remainder=sector ; XCHG ;DE=TOTAL NUMBER OF SECTORS LHLD SPT ;GET NUMBER OF SECTORS/TRACK CALL NEG ;HL = -SECTORS/TRACK XCHG LXI B,0 ;SET TRACK COUNTER TO ZERO ; DIVLP: INX B ;INCREMENT TRACK COUNT DAD D ;SUBTRACT SECTORS/TRACK FROM SECTORS TOTAL JC DIVLP DCX B ;ADJUST TRACK COUNT XCHG LHLD SPT ;ADD SECTORS/TRACK BACK IN TO ADJUST DAD D ;HL=NUMBER OF SECTORS ON LAST TRACK OF GROUP PUSH H LHLD SYSTRK ;ADD IN NUMBER OF SYSTEM TRACKS DAD B XCHG ;DE=TRACK NUMBER POP H INX H ;HL=SECTOR NUMBER RET ; ;COMMAND: F ;Find Directory Entry for specified file ; POSFIL: CALL NORITE MVI A,1 STA FINDFL ;SO WE POSITION LATER LXI D,FCB XRA A ;LOGGED IN DISK STAX D INX D MVI B,8 CALL MVNAME MVI B,3 CALL MVNAME LXI D,FCB MVI C,SRCHF PUSH H CALL BDOS INR A JNZ FLOK STA DIRPOS ;GRP 0 IF NOT FOUND CALL ILPRT DB '++ File Not Found ++',CR,LF,0 POP H JMP PROMPT ; FLOK: DCR A STA DIRPOS ;SAVE POS. IN DIR ANI 3 MOV L,A MVI H,0 DAD H ;X32 BYTES/ENTRY DAD H DAD H DAD H DAD H LXI D,TBUFF DAD D ;HL POINTS TO ENTRY LXI D,32 XCHG DAD D XCHG MVI A,'D' STA DUMTYP JMP DUMPLP ;WHICH POPS H ; MVNAME: MOV A,M ;GET NEXT CHAR OF FILE NAME/TYPE CPI '.' ;END OF FILE NAME? JZ MVIPAD ;PAD OUT IF SO CPI CR ;END OF ENTRY? JZ PAD ;PAD OUT IF SO CPI ';' ;END OF ENTRY? JZ PAD ;PAD OUT IF SO CALL UPCASE ;CAPITALIZE STAX D ;STORE INX H ;PT TO NEXT INX D DCR B ;COUNT DOWN JNZ MVNAME MOV A,M ;CHECK FOR ERROR CPI CR ;OK IF EOL RZ CPI ';' ;OK IF LOGICAL EOL RZ INX H CPI '.' ;OK IF DECIMAL RZ JMP WHAT ; MVIPAD: INX H ; PAD: MVI A,' ' ;PRINT PADDING SPACES STAX D INX D DCR B JNZ PAD RET ; ;COMMAND: + ;Advance to Next Logical Sector ; PLUS: LXI D,1 ;DFLT TO 1 SECT MOV A,M ;GET NEXT CHAR CPI CR ;CR? JZ PLUSGO ;..YES, DFLT TO 1 CPI ';' JZ PLUSGO CALL DECIN ;GET # MOV A,D ORA E JZ WHAT ; PLUSGO: CALL NXTSEC ;ADVANCE TO NEXT LOGICAL SECTOR DCX D ;MORE TO GO? MOV A,D ORA E JNZ PLUSGO ;..YES ; ;Ok, incremented to sector. Setup and read ; PLUSMI: PUSH H LHLD CURSEC XCHG CALL SETSEC ;SET SECTOR LHLD CURTRK XCHG CALL SETTRK ;SET TRACK POP H CALL READ ;READ IT JMP CLCGRP ;CALCULATE GROUP AND DISPLAY ; ;COMMAND: - ;Back up to previous sector ; MINUS: LXI D,1 ;SET DFLT MOV A,M ;GET CHAR CPI CR ;CR? JZ MINGO ;..YES, DFLT=1 CPI ';' JZ MINGO CALL DECIN ;..NO, GET ## MOV A,D ORA E JZ WHAT ; MINGO: PUSH H LHLD CURSEC ;BACK UP SECTOR DCX H MOV A,H ORA L JNZ MINOK LHLD CURTRK ;BEYOND SECTOR ZERO, SO BACK UP TRACK MOV A,H ORA L JNZ SEASH LHLD MAXTRK ;WRAP TO END OF DISK SHLD CURTRK LHLD MAXSEC JMP MINOK ; SEASH: DCX H SHLD CURTRK LHLD SPT ;GET NUMBER OF SECTORS/TRACK ; MINOK: SHLD CURSEC ;SET NEW CURRENT SECTOR POP H DCX D ;COUNT DOWN ON NUMBER OF TIMES TO BACKUP MOV A,D ORA E JNZ MINGO JMP PLUSMI ;READ BLOCK ; ;Go to next sector ; On exit, CURSEC = Current Sector and CURTRK = Current Track ; NXTSEC: PUSH H PUSH D LHLD CURSEC ;INCREMENT CURRENT SECTOR INX H XCHG LHLD SPT ;CHECK TO SEE IF BEYOND END OF TRACK CALL SUBDE XCHG JNC NEXTOK LHLD CURTRK ;BEYOND END OF TRACK, SO INCR CURRENT TRACK INX H XCHG LHLD MAXTRK ;SEE IF BEYOND END OF DISK CALL SUBDE JNC TRASK LXI D,0 ;WRAP TO START OF DISK ; TRASK: XCHG SHLD CURTRK ;SET NEW CURRENT TRACK LXI H,1 ;SET SECTOR 1 ; NEXTOK: SHLD CURSEC ;SET NEW CURRENT SECTOR POP D POP H RET ; ;Tell what group, displacement, track, sector, physical sector ; INQ: CALL INQSUB JMP PROMPT ; ;Position inquiry subroutine ;Executed via: G S or T (with no operands) ; INQSUB: PUSH H LHLD SYSTRK ;CHECK IF IN SYSTEM TRACKS XCHG LHLD CURTRK CALL SUBDE JC NOGRP CALL ILPRT ;PRINT GROUP NUMBER IF NOT IN SYSTEM TRACKS DB 'Group = ',0 LHLD GROUP MOV B,H MOV C,L CALL HEXB ;PRINT GROUP NUMBER IN BC MVI A,':' CALL TYPE LDA GRPDIS CALL HEX ;PRINT GROUP DISPLACEMENT IN A MVI A,',' CALL TYPE ; NOGRP: CALL ILPRT ;PRINT TRACK NUMBER DB ' Track = ',0 LHLD CURTRK CALL DEC ;TRACK NUMBER IN DECIMAL CALL ILPRT ;PRINT SECTOR NUMBER DB ', Sector = ',0 LHLD CURSEC CALL DEC ;SECTOR NUMBER IN DECIMAL CALL ILPRT ;PRINT PHYSCIAL SECTOR NUMBER DB ', Physical Sector = ',0 LHLD PHYSEC CALL DEC ;PHYSICAL SECTOR NUMBER IN DECIMAL CALL CRLF POP H RET ; ;COMMAND: C ;Change Contents of Current Block ; CHG: MOV A,M ;GET TYPE (HEX, ASCII) CALL UPCASE PUSH PSW ;SAVE "H" OR "A" INX H CALL HEXIN ;GET DISP IN HEX CALL DISP1 ;VALIDATE DISP TO DE INX H LXI B,0 ;SHOW NO 'THRU' ADDR CPI '-' ;TEST DELIM FR. DISP JNZ CHGNTH ;NO THRU PUSH D ;SAVE FROM CALL HEXIN CALL DISP1 ;GET THRU INX H ;SKIP END DELIM MOV B,D MOV C,E ;BC = THRU POP D ;GET FROM JMP CHGAH ; CHGNTH: CPI ',' JNZ WHAT ; CHGAH: POP PSW CPI 'H' ;HEX? JZ CHGHEX CPI 'A' ;ASCII? JNZ WHAT ; ;Change ASCII ; CHGALP: MOV A,M ;GET CHAR CPI CR JZ PROMPT CPI ';' JZ PROMPT ; ;The following print of the deleted byte is commented out; if leading ; semicolons are removed, deleted bytes will be printed ; ; LDAX D ;GET BYTE THAT IS REPLACED ; CPI ' ' ; JC CHGAHX ; CPI 7EH ;DON'T PRINT ESC CHAR FOR H1500 ; JNC CHGAHX ; JMP CHGA2 ; ;CHGAHX: ; CALL BHEX ; JMP CHGA3 ; ;CHGA2: ; CALL TYPE ; ;End of print of delete bytes ; CHGA3: SHLD BACK ;IN CASE "THRU" CALL GETVAL ;GET ASCII OR VALUE STAX D ;UPDATE BYTE INX H ;PT TO NEXT INPUT CHAR ; ;See if 'THRU' requested ; MOV A,C ORA A JZ CHANTH CMP E ;DONE?.. JZ PROMPT ;..YES LHLD BACK ; CHANTH: INR E JNZ CHGALP MOV A,M CPI CR JZ PROMPT CPI ';' JZ PROMPT JMP WHAT ; ;Change hex ; CHGHCM: INX H ; CHGHEX: MOV A,M ;GET HEX DIGIT CPI CR JZ PROMPT CPI ';' JZ PROMPT CPI ',' ;DELIM? JZ CHGHCM PUSH D SHLD HEXAD ;IN CASE 'THRU' CALL HEXIN ;POSITIONS TO DELIM MOV A,E ;GET VALUE POP D ;..ADDR ; ;The following comments out the echo of the deleted byte; removing the ; leading semicolons restores the echo ; ; PUSH PSW ;SAVE VALUE ; LDAX D ;GET OLD ; CALL HEX ;ECHO IN HEX ; POP PSW ;GET NEW ; ;End of echo of bytes ; STAX D ;SAVE NEW BYTE MOV A,C ;SEE IF 'THRU' ORA A JZ CHHNTH ;..NO. CMP E ;..YES, DONE? JZ PROMPT LHLD HEXAD ;..NO: MORE ; CHHNTH: INR E JNZ CHGHEX MOV A,M CPI CR JZ PROMPT CPI ';' JZ PROMPT JMP WHAT ; ;COMMAND: R ;Read Current Block into TBUFF ;COMMAND: RG ;Read Specified Group into GBUFF ; DOREAD: MOV A,M ;GET CHAR AFTER R CALL UPCASE ;CAPITALIZE CPI 'G' ;READ GROUP? JZ SAVEG ;SAVE GROUP IF SO LDA NOTPOS ;POSITIONED? ORA A JNZ CANTRD CALL READ ;READ BLOCK JMP PROMPT ; CANTRD: XRA A STA QFLAG ;NOT QUIET CALL ILPRT DB '++ Can''t read - not positioned ++',CR,LF DB 'Position by:',CR,LF DB ' Track then Sector, or',CR,LF DB ' Group',CR,LF,0 JMP PROMPT ; ;COMMAND: W ;Write Current Block to Disk ;COMMAND: WG ;Write Specified Group from GBUFF ; DORITE: MOV A,M ;GET NEXT CHAR CALL UPCASE ;CAPITALIZE CPI 'G' ;GROUP? JZ RESTRG ;DO GROUP WRITE CALL WRITE ;DO WRITE JMP PROMPT ; ;Print Byte in A as Hex Digits ; BHEX: PUSH PSW MVI A,'<' CALL TYPE POP PSW CALL HEX MVI A,'>' CALL TYPE RET ; ;Print Number in BC as Hex Digits ; HEXB: LDA DSM+1 ORA A JZ HEXX MOV A,B CALL HEX ; HEXX: MOV A,C ; ;Print Byte in A as 2 Hex Digits ; HEX: PUSH PSW RAR ;GET HIGH NYBBLE RAR RAR RAR CALL NIBBL ;PRINT IT POP PSW ;GET LOW NYBBLE ; NIBBL: ANI 0FH ;MASK LOW NYBBLE CPI 10 ;0-9? JC HEXNU ADI 7 ;CONVERT TO A-F ; HEXNU: ADI '0' ;CONVERT TO ASCII JMP TYPE ;PRINT IT ; ;Decimal output routine ; Print Number in HL as decimal digits (HL<100) ; DEC: PUSH B PUSH D PUSH H LXI B,-10 LXI D,-1 ; DECOU2: DAD B INX D JC DECOU2 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DEC MOV A,E ADI '0' CALL TYPE POP H POP D POP B RET ; ;Print ; SPACE: MVI A,' ' JMP TYPE ; ;Print '*' ; ASTER: MVI A,'*' JMP TYPE ; ;Inline print routine ; Print Chars ending in 0 pted to by Return Address; return to byte after ; ILPRT: XTHL ;GET PTR AND SAVE HL ; ILPLP: CALL CTLCS ;ABORT? JZ PRMPTR MOV A,M ;GET CHAR CPI 1 ;PAUSE? -- ^A JNZ ILPOK CALL CONIN ;WAIT FOR ANY CHAR CPI 3 ;ABORT? JZ PRMPTR JMP ILPNX ; ILPOK: CALL TYPE ;PRINT CHAR ; ILPNX: INX H ;PT TO NEXT MOV A,M ;GET IT ORA A ;DONE? JNZ ILPLP INX H ;PT TO BYTE AFTER ENDING 0 XTHL ;RESTORE HL AND RET ADR RET ; ;DISP calls DECIN, and validates a sector ;displacement, then converts it to an address ; DISP: CALL DECIN DISP1: PUSH PSW ;SAVE DELIMITER MOV A,D ORA A JNZ BADISP MOV A,E ORA A JM BADISP ADI 80H ;TO POINT TO BUFFER AT BASE+80H MOV E,A MVI D,BASE/256 POP PSW ;GET DELIM RET ; BADISP: XRA A STA QFLAG ;NOT QUIET CALL ILPRT DB '++ Bad Displacement (Not 0-7FH) ++' DB CR,LF,0 JMP PRMPTR ; ;Input Number from Command Line -- Assume it to be Hex ; Number returned in DE ; HEXIN: LXI D,0 MOV A,M CPI '#' ;DECIMAL? JZ HDIN ;MAKE DECIMAL ; HINLP: MOV A,M ;GET CHAR CALL UPCASE ;CAPITALIZE CPI CR ;EOL? RZ CPI ';' ;EOL? RZ CPI ',' RZ CPI '-' ;'THRU'? RZ CPI '>' RZ INX H ;PT TO NEXT CHAR CPI '0' ;RANGE? JC WHAT CPI '9'+1 ;RANGE? JC HINNUM CPI 'A' ;RANGE? JC WHAT CPI 'F'+1 ;RANGE? JNC WHAT SUI 7 ;ADJUST FROM A-F TO 10-15 ; HINNUM: SUI '0' ;CONVERT FROM ASCII TO BINARY XCHG DAD H ;MULT PREVIOUS VALUE BY 16 DAD H DAD H DAD H ADD L ;ADD IN NEW DIGIT MOV L,A XCHG JMP HINLP ; HDIN: INX H ;SKIP '.' ; ;Input Number in Command Line as Decimal ; Number is returned in DE ; DECIN: LXI D,0 MOV A,M ; GET 1ST CHAR CPI '#' ; HEX? JNZ DINLP INX H ; PT TO DIGIT JMP HINLP ; DO HEX PROCESSING ; DINLP: MOV A,M ;GET DIGIT CALL UPCASE ;CAPITALIZE CPI CR ;EOL? RZ CPI ';' ;EOL? RZ CPI ',' RZ CPI '-' ;'THRU'? RZ INX H ;PT TO NEXT CPI '0' ;RANGE? JC WHAT CPI '9'+1 ;RANGE? JNC WHAT SUI '0' ;CONVERT TO BINARY PUSH H MOV H,D MOV L,E DAD H ;X2 DAD H ;X4 DAD D ;X5 DAD H ;X10 ADD L ;ADD IN DIGIT MOV L,A MOV A,H ACI 0 MOV H,A XCHG ;RESULT IN DE POP H JMP DINLP ; ;Read in a console buffer ; RDBUF: CALL ILPRT ;PRINT PROMPT DB CR,LF,'DUTIL ',0 LDA DRIVE ;GET DRIVE NUMBER ADI 'A' ;CONVERT TO ASCII CALL TYPE LDA VER2FL ;VERSION 2 OR BETTER? ORA A ;0=NO JZ RDBUF1 MVI A,'/' CALL TYPE LDA UNUM ;DISPLAY USER NUMBER MOV L,A ;VALUE IN HL MVI H,0 CALL DEC ;PRINT IN DECIMAL RDBUF1: CALL ILPRT ;PRINT PROMPT DB '? ',0 LXI D,INBUF-2 ;USE CP/M READLN MVI C,10 CALL BDOS LDA INBUF-1 ;GET CHAR COUNT MOV B,A ;CHAR COUNT IN B LXI H,INBUF ;STORE ENDING ADD L ;ADD CHAR COUNT TO HL MOV L,A MOV A,H ACI 0 MOV H,A MVI A,CR ;STORE ENDING MOV M,A ;SAVE IT CALL TYPE ;ECHO IT MVI A,LF ;ECHO.. CALL TYPE ;..LF LXI H,INBUF ;SET PTR TO FIRST CHAR IN LINE RET ; ;Set paging flag for page routine ; PAGSET: LDA PAGSIZ ;GET SIZE OF PAGE STA PAGFLG ;SET FLAG RET ; ;Page output ; PAGER: LDA PAGFLG ;GET FLAG CPI 2 ;2 LINES LEFT? JZ WAIT ;SAME AS USER DELAY DCR A ;COUNT DOWN STA PAGFLG JMP CRLF ; ;Delay Routine ; WAIT: PUSH H CALL ILPRT DB CR,LF,'Type Any Character to Continue or ^C to Abort - ',0 POP H CALL CONIN ;GET RESPONSE CPI 'C'-40H ;^C? JZ WAIT1 CALL CRLF ;NEW LINE CALL PAGSET ;RESET PAGE COUNT RET WAIT1: LDA IHFLG ;INITIAL HELP? ORA A ;0=NO JZ PRMPTR ;ABORT TO COMMAND PROMPT JMP EXIT1 ;ABORT TO CP/M ; ;CRLF Routine ; CRLF: MVI A,CR CALL TYPE MVI A,LF JMP TYPE ; ;Convert to Upper Case ; UPCASE: ANI 7FH ;MASK OUT MSB CPI 60H ;LESS THAN SMALL A? RC ;RETURN IF SO ANI 5FH ;MAKE UPPER CASE RET ; ;CON: Status Routine ; CONST: PUSH B PUSH D PUSH H VCONST: CALL $-$ ;ADDR FILLED IN BY 'INIT' POP H POP D POP B RET ; ;CON: Input Routine ; CONIN: PUSH B PUSH D PUSH H VCONIN: CALL $-$ ;ADDR FILLED IN BY 'INIT' POP H POP D POP B RET ; ;Console out with TAB expansion ; Char in A ; TYPE: PUSH B ;SAVE REGS PUSH D PUSH H MOV C,A ;FOR OUTPUT ROUTINE CPI TAB JNZ TYPE2 ;Tabulate TYPTAB: MVI A,' ' ;PRINT SPACE CALL TYPE LDA TABCOL ;GET COL COUNT ANI 7 ;DONE? JNZ TYPTAB JMP TYPRET ; ;Filter out control characters to ;prevent garbage during view of file ; TYPE2: CPI ' ' JNC TYPEQ CPI CR JZ TYPEQ CPI LF JNZ TYPNCR ; TYPEQ: LDA QFLAG ;CHECK QUIET FLAG FOR NO MESSAGES ORA A ; ;CON: Output Routine ; VCONOT: CZ $-$ ;ADDR FILLED IN BY 'INIT' ; ;Update column used in tab expansion ; MOV A,C ;GET CHAR CPI CR JNZ TYPNCR MVI A,0 ;RESET TAB COLUMN IF STA TABCOL JMP TYPLST ; TYPNCR: CPI ' ' ;CTL CHAR? JC TYPLST ;..NO CHANGE IN COL LDA TABCOL ;INCR TAB COUNT INR A STA TABCOL ; TYPLST: LDA PFLAG ;CHECK FOR PRINTER OUTPUT ANI 1 CNZ LIST ;FROM C REG ; TYPRET: POP H ;RESTORE REGS POP D POP B RET ; ;LST: Output Routine ; Char in C ; LIST: PUSH B ;SAVED REGS PUSH D PUSH H VLIST: CALL $-$ ;ADDR FILLED IN BY 'INIT' POP H POP D POP B RET ; ;Home Disk Routine ; HOME: PUSH H VHOME: CALL $-$ ;ADDR FILLED IN BY 'INIT' POP H RET ; ;Set track # in DE ; SETTRK: PUSH H LHLD MAXTRK ;CHECK FOR WITHIN BOUNDS CALL SUBDE ;IF TRACK # IN DE > MAX, THEN ERROR POP H JC OUTLIM XCHG ;RESET CURRENT TRACK SHLD CURTRK XCHG MOV B,D ;BC=TRACK NUMBER MOV C,E PUSH H ; VSETRK: CALL $-$ ;ADDR FILLED IN BY 'INIT' POP H RET ; ;Set Sector Number in DE ; SETSEC: PUSH H PUSH D LHLD SYSTRK ;GET NUMBER OF SYSTEM TRACKS XCHG SHLD CURSEC ;SET CURRENT SECTOR LHLD CURTRK ;GET CURRENT TRACK CALL SUBDE ;SEE IF WE ARE IN THE SYSTEM TRACKS POP B ;BC=SECTOR NUMBER MOV H,B ;HL=SECTOR NUMBER MOV L,C JNC NOTSYS ;IF NO CARRY FOR SUBDE, WE ARE NOT IN SYSTEM TRACKS LDA FIRST0 ;SEE IF FIRST SEC 0 ORA A JNZ GSTSEC ;NO, JUMP AWAY DCX H ;YES, SO DECREMENT JMP GSTSEC ;REQUESTED, THEN GO ; ;Not in System Tracks, so Skew Factor is effective ; NOTSYS: LHLD SECTBL ;GET PTR TO SECTOR TABLE XCHG ;... IN DE DCX B ;DECREMENT SECTOR NUMBER BY 1 ; VSCTRN: CALL $-$ ;ADDR FILLED IN BY 'INIT' LDA SPT+1 ;IF SPT<256 (HI-ORD = 0) ORA A ; THEN FORCE 8-BIT TRANSLATION JNZ VSCTR1 ; ELSE KEEP ALL 16 BITS MOV H,A VSCTR1: LDA VER2FL ;SEE IF VERSION 2.x ORA A ;SET FLAGS JNZ GSTSEC ;JUMP IF CP/M 2.x MVI H,0 ;CP/M 1.4 GOOD TO ONLY 8 BITS MOV L,C ;MOST BIOS'S RETURN THE ; PHYSICAL SEC # IN REG C GSTSEC: SHLD PHYSEC ;THIS MAY BE REDUNTANT IN ; MOST 1.4 VERSIONS, BUT ; SHOULD CAUSE NO PROBLEMS MOV B,H MOV C,L ; VSTSEC: CALL $-$ ;ADDR FILLED IN BY 'INIT' POP H ;RESTORE PTR TO NEXT CHAR RET ; ;Out of Disk Track Limit ; OUTLIM: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '++ Not Within Tracks 0-',0 PUSH H LHLD MAXTRK ;PRINT MAX TRACK NUMBER CALL DEC POP H CALL ILPRT DB ' ++',CR,LF,0 CALL NORITE ;NOT POSITIONED JMP PRMPTR ; ;Set DMA Address ; SETDMA: JMP $-$ ;ADDR FILLED IN BY 'INIT' ; ;Read Next Block into DMA Address ; READ: MVI A,1 ;SET FLAG STA WRFLG PUSH H ;SAVE PTR TO NEXT CHAR ; VREAD: CALL $-$ ;ADDR FILLED IN BY 'INIT' ORA A ;ERROR? JZ READOK XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '++ READ Failed, Sector may be Invalid ++' DB CR,LF,0 ; READOK: POP H ;GET PTR TO NEXT CHAR RET ; ;Write Block in DMA Address to Disk ; WRITE: LDA WRFLG ;READ ALREADY PERFORMED? ORA A ;ERROR IF NOT JNZ PWRITE ; BADW: XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '++ Cannot Write Unless Read Issued ++' DB CR,LF,0 JMP EXPL ; ;Do Write ; PWRITE: PUSH H ;SAVE PTR TO NEXT CHAR MVI C,1 ;FORCE WRITE TYPE 1 IN CASE 2.x DEBLOCK USED ; VWRITE: CALL $-$ ;ADDR FILLED IN BY 'INIT' ORA A ;ERROR? JZ WRITOK XRA A ;NOT QUIET STA QFLAG CALL ILPRT DB '++ WRITE Failed ++',CR,LF,0 ; WRITOK: POP H RET ; ;Help; HELP is entry point for HELP (?) command, HELP1 is entry point for ; Initial Help Command, and IHELP is entry point for HELP (/) from command ; line ; IHELP: XRA A STA QFLAG ;NOT QUIET CALL ILPRT DB 'Introductory HELP on DUTIL (Disk Utility)',CR,LF DB ' The DUTIL program is designed to provide the user with' DB CR,LF DB 'the ability to manipulate information on the disk as easily' DB CR,LF DB 'as the DDT and SID utilities allow the user to manipulate' DB CR,LF DB 'information in memory.',CR,LF DB ' The following is a summary of the commands available to' DB CR,LF DB 'the DUTIL user. This same list is invoked internally by the' DB CR,LF DB '? Command of DUTIL. For additional information on disk' DB CR,LF DB 'structures and how to use DUTIL in general, refer to the' DB CR,LF DB 'files DUTIL.DOC and DUTIL.HLP.',CR,LF,0 MVI A,0FFH ;A=0FFH STA IHFLG ;SET INITIAL HELP CALL WAIT JMP HELP1 ;PROCESS NORMALLY HELP: XRA A ;A=0 STA IHFLG ;SET NO INITIAL HELP HELP1: XRA A STA QFLAG ;NOT QUIET CALL ILPRT DB '=======================================================',CR,LF DB ' -- Command Summary -- ',CR,LF DB '-------------------------------------------------------',CR,LF DB CR,LF DB 'Operands in brackets [...] are optional' DB CR,LF,CR,LF DB '@ Repeat Previous Non-@ Command Line' DB CR,LF DB '+[nn] Step In [nn (decimal)] Sectors; -[nn] Step Out ' DB 'Sectors' DB CR,LF DB '# Print Disk Parameters for Current Drive' DB CR,LF DB '=xxx Search for ASCII xxx from Current Sector' DB CR,LF DB ' Caution: upper/lower case matters.' DB CR,LF DB ' Use for hex:' DB CR,LF DB ' To find "IN 0" use: =<0> or' DB CR,LF DB ' "(tab)H,0(CR)(LF)" use: =<9>H,0' DB CR,LF DB '< Save Current Sector; > Restore Saved Sector' DB CR,LF DB '/[nn] Repeat [nn (decimal) times]; ! Pause for User' DB CR,LF DB ':ntext Define ''text'' to be Macro n; n Perform Macro' DB ' n, 0<=n<=9' DB CR,LF DB ':Pn Print Macro n, 0<=n<=9' DB CR,LF DB ':Px Print All Macros if x=A or Print Prev Line if x=@' DB CR,LF,CR,LF,0 CALL WAIT CALL ILPRT DB '-------------------------------------------------------',CR,LF DB 'A[ff,tt] ASCII Dump' DB CR,LF DB 'C Change:' DB CR,LF DB ' CHaddr,byte,byte... (hex)' DB CR,LF DB ' or CAaddr,data... (Ascii)' DB CR,LF DB ' Allowed for imbedded hex.' DB CR,LF DB ' or CHfrom-thru,byte e.g. ch0-7f,e5' DB CR,LF DB ' or CAfrom-thru,byte' DB CR,LF DB 'D[ff,tt] Dump (Hex and ASCII)' DB CR,LF DB 'Fn.t Find File' DB CR,LF DB 'Gnn CP/M Allocation Group nn (hex)' DB CR,LF DB 'H[ff,tt] Hex Dump' DB CR,LF DB 'L Log in drive; Lx Log in drive x' DB CR,LF DB 'M[nn] Map [from group nn (hex)]' DB CR,LF,CR,LF,0 CALL WAIT CALL ILPRT DB '-------------------------------------------------------',CR,LF DB CR,LF DB 'N Load New Disk; P Toggle Printer Switch' DB CR,LF DB 'Q Quiet Mode (no messages)' DB CR,LF DB 'R Read Current Sector; RG Read Specified Group' DB CR,LF DB 'Snn Sector nn (decimal)' DB CR,LF DB 'Tnn Track nn (decimal)' DB CR,LF DB 'Unn Set User nn (decimal) for Find command (CP/M-2 only)' DB CR,LF DB 'V[nn] View [nn (decimal)] ASCII Sectors' DB CR,LF DB 'W Write Current Sector; WG Write Specified Group' DB CR,LF DB 'X Exit Program' DB CR,LF DB 'Z[nn] Sleep [nn (decimal) seconds]' DB CR,LF,CR,LF,0 CALL WAIT CALL ILPRT DB '-------------------------------------------------------',CR,LF DB CR,LF DB 'Command Line is of the form: DUTIL d/u?',CR,LF DB ' "d" is Logged-In Disk, "u" is Current User',CR,LF DB CR,LF DB 'Cancel a function with C or Ctrl-C.' DB CR,LF DB 'Suspend output with S or Ctrl-S.' DB CR,LF DB 'Separate commands with ";".' DB CR,LF DB ' Example: g0' DB CR,LF DB ' +;d;z2;/' DB CR,LF DB ' would step in, dump, sleep 2 sec, ' DB CR,LF DB ' and repeat until control-c typed.' DB CR,LF DB '"nn" usage varies with command as follows:',CR,LF DB ' +, -, /, T, S, U, V, Z nn in Decimal',CR,LF DB ' (use #nn for Hex)',CR,LF DB ' G, M nn in Hexadecimal',CR,LF DB ' (use #nn for Decimal)' DB CR,LF DB '"ff" and "tt" are in Hexadecimal (use #ff or #tt for Decimal)' DB CR,LF,CR,LF DB '=======================================================',CR,LF DB 0 CALL WAIT CALL ILPRT DB '=======================================================',CR,LF DB 'DUTIL Status Information',CR,LF DB '-------------------------------------------------------',CR,LF DB 'Processor Speed: ',0 LDA CLOCK ;GET CLOCK SPEED ADI '0' ;CONVERT TO ASCII CALL TYPE ;PRINT CALL ILPRT DB ' MHz',CR,LF DB 'Number of Lines on CON: ',0 LDA PAGSIZ ;GET PAGE SIZE MOV L,A ;NUMBER IN HL MVI H,0 CALL DEC ;PRINT NUMBER IN DECIMAL CALL ILPRT DB CR,LF,'Group Save Buffer Address: ',0 LXI B,GBUFF ;BC=ADDRESS CALL HEXB ;PRINT AS HEX CALL ILPRT DB ' Hex',CR,LF DB '=======================================================',CR,LF DB 0 LDA IHFLG ;INITIAL HELP? ORA A ;0=NO JNZ EXIT1 ;RETURN TO CP/M IF SO JMP PRMPTR ;NEW LINE INPUT IF NOT ; ;COMMAND: X ;Exit to CP/M ; EXIT: XRA A STA QFLAG ;NOT QUIET CALL ILPRT ;PRINT DB CR,LF,'Exit to CP/M -- Do you wish to Warm Boot (Y/N/=N)?' DB ' ',0 CALL CONIN ;GET RESPONSE CALL UPCASE ;CAPITALIZE CPI 'Y' ;YES? JZ BASE ;WARM BOOT IF SO ; ;Quick Exit to CP/M ; EXIT1: LHLD DUTSTK ;GET CP/M STACK PTR SPHL ;SET SP RET ; ;******************************** ;* * ;* Utility Subroutines * ;* * ;******************************** ; GRPCMP: MOV A,C INR D DCR D JZ CMP8 CMP M INX H RNZ MOV A,B ; CMP8: CMP M RET ; ;2's complement HL ==> HL ; NEG: MOV A,L CMA MOV L,A MOV A,H CMA MOV H,A INX H RET ; ;HL/2 ==> HL ; ROTRHL: ORA A MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A RET ; ;Collect the number of '1' bits ;in A as a count in C ; COLECT: MVI B,8 ;NUMBER OF BITS ; COLOP: RAL JNC COSKIP INR C ; COSKIP: DCR B JNZ COLOP RET ; ;HL-DE ==> HL ; Carry Flag is Significant ; SUBDE: MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A RET ; ;Quick Kludge multiply ;HL*DE ==> HL ; MULT: PUSH B PUSH D XCHG MOV B,D MOV C,E MOV A,B ORA C JNZ MULCON LXI H,0 ;FILTER SPECIAL CASE JMP MLDONE ; OF MULTIPLY BY 0 ; MULCON: DCX B MOV D,H MOV E,L ; MULTLP: MOV A,B ORA C JZ MLDONE DAD D DCX B JMP MULTLP ; MLDONE: POP D POP B RET ; ;Routine to fill in disk params ;with every drive change ; LOGIT: LDA VER2FL ORA A ;IF NOT CP/M 2.x THEN JZ LOG14 ; DO IT AS 1.4 ; ;CP/M 2.x ; LXI D,DPB ; THEN MOVE TO LOCAL MVI B,DPBLEN ; WORKSPACE CALL MOVE JMP LOGCAL ; ;CP/M 1.4 ; LOG14: LHLD BDOS+1 ;FIRST FIND 1.4 BDOS MVI L,0 LXI D,DPBOFF ;THEN OFFSET TO 1.4'S DPB DAD D MVI D,0 ;SO 8 BIT PARMS WILL BE 16 MOV E,M ;NOW MOVE PARMS INX H XCHG SHLD SPT XCHG MOV E,M INX H XCHG SHLD DRM XCHG MOV A,M INX H STA BSH MOV A,M INX H STA BLM MOV E,M INX H XCHG SHLD DSM XCHG MOV E,M INX H XCHG SHLD AL0 XCHG MOV E,M XCHG SHLD SYSTRK ; LOGCAL: LXI H,GRPDIS MOV A,M PUSH PSW LDA BLM MOV M,A PUSH H LHLD DSM XCHG CALL GTKSEC SHLD MAXSEC XCHG SHLD MAXTRK POP H POP PSW MOV M,A RET ;*********************************** ; ; DUTIL Command Table ; ;*********************************** CMDTBL: DB ':' DW MAC ; DB '@' DW PCMD ; DB '+' DW PLUS ; DB '-' DW MINUS ; DB '=' DW SEARCH ; DB '<' DW SAVE ; DB '>' DW RESTOR ; DB '#' DW STATS ; DB '?' DW HELP ; DB '/' DW REPEAT ; DB '!' DW UWAIT ; DB 'A' DW DUMP ; DB 'C' DW CHG ; DB 'D' DW DUMP ; DB 'F' DW POSFIL ; DB 'G' DW POS ; DB 'H' DW DUMP ; DB 'L' DW LOGIN ; DB 'M' DW MAP ; DB 'N' DW NEWDSK ; DB 'P' DW PRNTFF ; DB 'Q' DW QUIET ; DB 'R' DW DOREAD ; DB 'S' DW POS ; DB 'T' DW POS ; DB 'U' ;******CP/M 2.x ONLY****** DW USER ; DB 'V' DW VIEW ; DB 'W' DW DORITE ; DB 'X' DW EXIT ; DB 'Z' DW SLEEP ; DB 0 ; End of Table ;************************************* ; ;Temporary storage area ; DS 100 ;50-ELT STACK DUTSTK: DS 2 ;OLD CP/M STACK POINTER; TOP OF DUTIL STACK BUFAD: DS 2 ;FORCES INITIAL READ HEXAD: DS 2 ;TO RE-FETCH A VALUE TOGO: DS 2 ;REPEAT COUNT (FFFF=CONT) TWOUP: DS 1 UNUM: DS 1 ;NUMBER OF CURRENT USER ONLY1: DS 1 ;FLAG TO PRINT ONLY 1 MAP ENTRY (0=NO) MFPTR: DS 2 ;MULTI FILE PTR FOR GETGRP PAGFLG: DS 1 ;LINE COUNTER FOR PAGING PFLAG: DS 1 ;1=PRINT GROUP: DS 2 ;GROUP NUMBER GRPDIS: DS 1 ;DISPLACEMENT INTO GROUP SAVEFL: DS 1 ;SAVE FLAG CURTRK: DS 2 ;CURRENT TRACK NUMBER CURSEC: DS 2 ;CURRENT SECTOR NUMBER PHYSEC: DS 2 ;CURRENT PHYSICAL SECTOR NUMBER TABCOL: DS 1 ;TAB COLUMN GLFLAG: DS 1 ;GROUP LOADED FLAG; 0=NO CPYFCT: DS 1 ;GROUP COPY FUNCTION; 0=READ, 0FFH=WRITE FILECT: DS 2 ;FILE COUNT DIRPOS: DS 1 ;POSITION IN DIRECTORY FINDFL: DS 1 ;1=MUST POSITION AFTER FIND FTSW: DS 1 ;SEARCH W/O INCREMENT NOTPOS: DS 1 ;INITIALLY NOT POSITIONED WRFLG: DS 1 ;MAY NOT WRITE UNTIL '+', '-', ; OR 'G' COMMAND TGRP: DS 2 ;TEMPORARY GROUP FLAG QFLAG: DS 1 ;QUIET? (0=NO) FIRST0: DS 1 ;SETS TO 0 IF FIRST SEC # IS 0 DRIVE: DS 1 ;DRIVE NUMBER MAXTRK: DS 2 ;MAX TRACK NUMBER MAXSEC: DS 2 ;MAX SECTOR NUMBER VER2FL: DS 1 ;CP/M VERSION 2.X FLAG SECTBL: DS 2 ;POINTER TO SECTOR SKEW TABLE ; IHFLG: DS 1 ;0=NOT AT INITIAL HELP, 0FFH=AT INITIAL HELP DUPFLG: DS 1 ;SPACE OR STAR TO INDICATE MULTIPLE USERS BACK: DS 2 ;TO BACK UP IN "CA0-7F,X" DUMTYP: DS 1 ; ;The disk parameter block ;is moved here from CP/M ; DPB EQU $ ;DISK PARAMETER BLOCK (COPY) SPT: DS 2 BSH: DS 1 BLM: DS 1 EXM: DS 1 DSM: DS 2 DRM: DS 2 AL0: DS 1 AL1: DS 1 CKS: DS 2 SYSTRK: DS 2 ; ;End of disk parameter block ; SAVBUF: DS 128+2+2 ; ;Set INBUF to a Page Boundary ; ORG $/100H*100H+100H-2 DB 126 ; SIZE OF BUFFER FOR CP/M DS 1 INBUF: DS 400H ;EXTRA SPACE FOR MACRO EXPANSION PINBUF: DS 400H ;PREVIOUS CONTENTS OF INPUT BUFFER CTEMP: DS 400H ;BUILD NEW COMMAND LINE BUFFER CTEMPX EQU $ ;END OF CTEMP ; ;Directory read in here; also loaded group area and Macros ; MTABL: DS 100H*10 ;10 PAGES FOR 10 MACROS GBUFF EQU $ DIRECT EQU $ ; END