; ; PROGRAM: DU2 ; AUTHOR: RICHARD CONN ; VERSION: 1.8 ; DATE: 18 Mar 84 ; PREVIOUS VERSIONS: 1.7 (25 Jan 84) ; 1.5 (03 Jan 84) 1.4 (25 Oct 83) 1.3 (14 Sep 83) ; 1.2 (25 Aug 83) 1.1 (12 Feb 83) 1.0 (10 Jan 83) ; ; REVISED FOR CP/M 3, CP/M-86, AND CCP/M-86 BY: ; CRAIG D. MILLER ; ; ; DERIVATION: DUTIL is derived from DU Version 7.5 ; DU2 is derived from DUTIL Version 1.1 ; VERS EQU 1$8 ; ; NOTE: This version does not work with CP/M 1.4 ; ; ; DU2 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 DU2 follow -- ; ;This version of DU is compatible with CP/M 2.x and 3.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,22,60,120 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 2 for systems using a 2 mHz clock or 4 for 4 mHz ;clock. This only affects the time delay used in ;the 'sleep' command. ; ;For DU2, 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 DU2, all output is paged, and this ;determines the page limit. ; ;************************************************* ;* * ;* This program has been heavily modified * ;* to allow it to work without modification * ;* on, hopefully, all versions of CP/M 2.x. * ;* 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. * ;* 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 to note are -- ; ; 03/18/84 - 1.) Restored line from V1.0 to initialize ; the stack pointer on initial program ; entry. Direct BIOS calls are used to ; put up the hello message, and using the ; CCP provided stack for this can be a ; problem on some systems. ; 2.) Restored Richard Conn's standard opening ; data block so this new version will be ; recognized by GENINS for installing under ; ZCPR2. ; (ver 1.8 Fred Viles) ; ; 01/24/84 - 1.) Changed 'K' command to stop output ; until prompt or error. ; (ver 1.7 CDM) ; ; 01/19/84 - 1.) fixed 'QS' and 'QR' to reselect the ; proper drive after executing (this ; was a problem under CP/M +). ; (ver 1.6 CDM) ; 2.) made the queue not be zeroed if ; the physical sector size between two ; differently selected disks was the ; same. ; 3.) made 'Total Groups' in '#' command ; print the proper number. ; 4.) fixed out of range error to display ; proper range on group numbers. ; ; 01/03/84 - 1.) fixed 'QS' and 'QR' to allow lower ; case drive letters. ; (ver 1.5 CDM) ; 2.) Made 'K' a toggle, it will now turn ; display on and off. ; 3.) Made 'F' return all extents of files ; greater than 1/2 Meg. ; 4.) Make 'M' ignore time/date stamps, XFCBs, ; and disk labels. 'M' will now not work ; with user numbers > 15. Also 'M' will ; seek the correct sector when complete. ; 5.) Macros infinitely expanded ; 6.) Made 'ME' command map of erased files ; it is coded simular to the way DU-V83 ; is coded. ; ; 10/07/83 - 1.) Changed 'B' to 'S'. ; (ver 1.4 CDM) ; 2.) Fixed 'X' so that it works correctly when ; a 'L' command is used and under CP/M + ; 3.) Fixed to work with drives larger than 8 Meg. ; 4.) Fixed 'F' and 'M' to get Bdos in sync with ; Bios. ; ; 09/14/83 - 1.) Made Bdos get back into sync with bios ; on all file related bdos calls (Ver 1.3 CDM) ; (Bdos sometimes looked on the wrong drive) ; 2.) Made 'F' (find file) show all extents ; 3.) Made 'D' (dump) 'from-to' values to be in hex ; ; 08/25/83 - 1.) Changed to use 8080 code so that translating ; to 8086 would be easy (ver 1.2 CDM) ; 2.) Changed to work with CP/M Plus ; 3.) Added Queue read ('QRfilename.typ') command ; 4.) Fixed command ' 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: CALL SINBUF ;Save old INBUF into PINBUF CALL RDBUF ;Read Input Line PRMPT1: XRA A STA IHFLG ;Set No Initial Help and macro flag STA KILCON ;Force console output to console CALL EXMAC ;Expand Macros LDA IHFLG ORA A JNZ PRMPT1 ;Not done expanding yet ; ;Begin Processing Command Line in INBUF ; At this point, HL points to next character to process ; PRMPTI: DCR A ;SET INFINITE LOOP COUNT (A=0 FROM ABOVE) STA TOGO ;LOOP COUNT FOR MULTIPLE LOOPS 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. ; PROMPT: SETSTK: LXI SP,DUTSTK ;RESET STACK XRA A ;ZERO 2-UP PRINT FOR DUAL-COLUMN PRINT STA TWOUP ;..SWITCH STA DMPRET ;TELL DUMP NOT TO TRY AGAIN (SEE POSFIL) INR A 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 ; ;Begin Command Evaluation -- Check for EOL and Capitalize ; CALL GETCHR ;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 EOLCH ;END OF LINE LOGICALLY? JZ PROMPT ;PROCESS NEXT ELEMENT IF SO ; ;Command dispatcher ; If command not found, abort with error message. ; If command file, process command with HL pointing ; 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 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 STA IHFLG ;SET FOUND MACRO FLAG 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 EOLCH ;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: CALL ILPERR DB CR,LF DB 'Error -- Macro Expanded Command Line too Long' DB 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: POP H ; RESTORE HL CALL ILPERR DB 'Invalid Command at or after ',0 MOV A,B ;GET COMMAND LETTER CALL TYPEC ;PRINT IT JMP PRMPTR ; ;Memory full error ; MEMFUL: CALL ILPERR 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 CALL ILPERR DB CR,LF DB 'Warning: Remainder of Command Line ' DB 'after "@" Deleted' DB 0 PCMD1: CALL ILPERR 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 TYPEC ;PRINT CHAR POP PSW ;GET FLAG JNZ PCMD2 MVI A,LF ; CALL TYPEC 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: CALL GETCHR ;GET NEXT CHAR CPI 'P' ;PRINT MACRO? JNZ MACROD ;IF NOT, DEFINE MACRO INX H ;PT TO MACRO NUMBER CALL GETCHR ;GET IT CPI '@' ;PRINT PREVIOUS COMMAND? JZ PCPR PUSH PSW ;SAVE A 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 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 CALL CRLF ;PRINT HEADER MOV A,D ;GET NUMBER ADI '0' ;CONVERT TO ASCII CALL TYPEC ;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 TYPEC ;PRINT CHAR POP PSW ;GET FLAG JNZ MPRINT MVI A,LF ; CALL TYPEC 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: CALL ILPERR DB CR,LF DB 'Invalid Macro Number Specified in Command' DB 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 '| -- Queue Information -- |',CR,LF DB '+-----------------------------+',CR,LF DB 0 CALL QSTATS ;PRINT STATUS INFO CALL ILPRT DB CR,LF DB '+=============================+',CR,LF DB '| -- Disk Information -- |',CR,LF DB '+-----------------------------+',CR,LF DB 'Disk Drive: ',0 LDA DRIVE ADI 'A' ;CONVERT TO ASCII CALL TYPEC ;PRINT DRIVE LETTER MVI A,':' CALL TYPEC MVI A,0FFH ;SET FLAG TO TYPE LEADING SPACES STA SPFLG CALL ILPRT DB CR,LF,'Tracks: ',0 LHLD MAXTRK ;PRINT NUMBER OF TRACKS 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,'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 ' Sectors/Group' DB CR,LF,'Total Groups: ',0 LHLD DSM ;PRINT TOTAL NUMBER OF GROUPS ON A DISK INX H CALL DEC CALL ILPRT DB ' (',0 LHLD DSM ;NOW PRINT IT IN HEX MOV B,H MOV C,L CALL HEXB1 CALL ILPRT DB ' Hex)',CR,LF,'Directory Entries: ',0 LHLD DRM ;PRINT NUMBER OF DIRECTORY ENTRIES INX H CALL DEC CALL ILPRT DB ' (',0 CALL DIRGRP ;GET NUMBER OF DIRECTORY GROUPS MOV H,B MOV L,C XRA A ;SET FLAG FOR NO LEADING SPACES STA SPFLG CALL DEC MVI A,0FFH ;SET FLAG TO TYPE LEADING SPACES STA SPFLG CALL ILPRT DB ' Groups)',0 LDA VERFLG ORA A JZ STATS2 CALL ILPRT DB CR,LF,'Physical sector size:',0 LDA PHM ;A = NUMBER OF SECTORS PER INR A ; PHYSICAL SECTOR LXI H,0 LXI D,128 STATS1: DAD D DCR A JNZ STATS1 CALL DEC STATS2: XRA A ;CLEAR LEADING SPACES FLAG STA SPFLG 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 MVI E,0 ;FIRST TIME SELECT CALL SELECT JMP PROMPT ; ;COMMAND: Q ;Queue Control ; QUEUER: CALL GETCHR ;GET 2ND ARGUMENT CALL CHKEOL ;END OF LINE? JZ QSTAT ;STATUS REPORT INX H ;PT TO AFTER KEY CHAR PUSH H ;SAVE PTR CPI 'Z' ;ZERO QUEUE? JZ QZERO CPI 'S' ;SAVE QUEUE? JZ QFSAVE CPI 'R' ;RESTORE QUEUE? JZ QFREAD POP H ;GET PTR CALL ILPERR DB 'Invalid Queue Command',CR,LF,0 JMP PRMPTR ;ABORT LINE ON ERROR ; ; Zero the Queue ; QZERO: LHLD DIRECT ;ZERO QUEUE SHLD QNXT ;SET NEXT SHLD QLST ;SET LAST LXI H,0 ;ZERO COUNT SHLD QCNT POP H ;GET PTR AND FALL THRU TO QSTAT ; ; Print Status of Queue ; QSTAT: PUSH H ;SAVE PTR TO NEXT CHAR CALL ILPRT DB '** Queue Status Summary **',CR,LF,0 CALL QSTATS ;PRINT STATUS POP H ;RESTORE PTR JMP PROMPT QSTATS: LHLD QCNT ;GET SIZE OF QUEUE CALL PRQCNT ;PRINT DATA CALL PRQSPAC ;PRINT SPACE AVAILABLE INFO CALL ILPRT DB 'Address of Head of Queue: ',0 LHLD QNXT ;PRINT ADDRESS OF HEAD OF QUEUE MOV B,H ;... ADDRESS IN BC MOV C,L CALL HEXB1 ;PRINT IN HEX CALL ILPRT DB ' Hex',CR,LF DB 'Address of Tail of Queue: ',0 LHLD QLST ;PRINT ADDRESS OF TAIL OF QUEUE MOV B,H MOV C,L CALL HEXB1 CALL ILPRT DB ' Hex',CR,LF,0 RET ; ; Print Amount of Space Left in Queue ; PRQSPAC: LXI B,-1 ;SET COUNT LHLD QLST ;GET PTR TO QUEUE TAIL QSTAT1: INX B ;INCREMENT COUNT LXI D,80H ;PT TO NEXT QUEUE ELEMENT DAD D XCHG ;WRAP AROUND CALL QWRAP LHLD QNXT ;GET PTR TO FIRST ELEMENT XCHG MOV A,H ;COMPARE CMP D JNZ QSTAT1 MOV A,L CMP E JNZ QSTAT1 MOV H,B ;HL=SECTOR COUNT MOV L,C CALL DEC ;PRINT AS DECIMAL CALL ILPRT DB ' Sectors Free in Queue',CR,LF,0 RET ; ; Save Queue as a File ; QFSAVE: CALL FILFCB ;FILL IN THE FCB PUSH H ;SAVE PTR TO NEXT CHAR LHLD QCNT ;ANY ELEMENTS IN QUEUE? MOV A,H ORA L JZ QEMPTY PUSH H ;SAVE QUEUE COUNT CALL NORITE ;CAN'T WRITE NOW CALL FCBINIT ;INIT FCB CALL DRVINIT ;INIT DRIVE LXI D,FCB ;PT TO FCB MVI C,DELF ;DELETE FILE PUSH D ;SAVE DE CALL BDOS POP D CALL FCBINIT ;INIT FCB AGAIN MVI C,MAKEF ;CREATE FILE CALL BDOS INR A ;CHECK FOR ERRORS JZ QFS2 POP B ;GET QUEUE COUNT IN BC LHLD QNXT ;PT TO NEXT SECTOR IN QUEUE QFS1: PUSH B ;SAVE COUNT LXI D,TBUFF ;COPY INTO TBUFF MVI B,128 ;128 BYTES CALL MOVE XCHG ;PT TO NEXT QUEUE SECTOR IN DE CALL QWRAP ;WRAP AROUND PUSH D ;SAVE PTRS LXI D,FCB ;PT TO FCB MVI C,WRITEF ;WRITE SECTOR TO FILE CALL BDOS ORA A JNZ QFS2 POP H ;GET PTR TO NEXT SECTOR POP B ;GET COUNT DCX B ;COUNT DOWN MOV A,B ;DONE? ORA C JNZ QFS1 LXI D,FCB ;CLOSE FILE MVI C,CLOSEF CALL BDOS INR A JZ QFS2 CALL ILPRT DB 'Queue Saved in File',CR,LF,0 POP H ;PT TO NEXT CHAR JMP QFS3 QFS2: CALL ILPERR DB 'Error in saving Queue',CR,LF,0 QFS3: LDA DRIVE MOV C,A CALL SELECT ;RESELECT DRIVE IN USE JMP PROMPT DRVINIT: ;GET BDOS BACK INTO SYNC WITH BIOS LDA DRIVE MVI C,SELDK ;SELECT CURRENT DRIVE MOV E,A JMP BDOS ; ; Read Queue from a File ; QFREAD: CALL FILFCB ;FILL IN THE FCB PUSH H ;SAVE PTR TO NEXT CHAR LXI H,0 ;HL=0 SHLD QCNT ;SET NO ELEMENTS IN QUEUE LHLD DIRECT ;SET FIRST AND LAST QUEUE ELEMENT PTRS SHLD QNXT SHLD QLST CALL NORITE ;CAN'T WRITE NOW CALL DRVINIT ;INIT DRIVE LXI D,FCB ;PT TO FCB CALL FCBINIT ;INIT FCB MVI C,OPENF ;OPEN FILE CALL BDOS INR A JZ QFR3 QFR1: LHLD QLST ;SEE IF ANOTHER SAVE WILL FILL QUEUE LXI D,128 ;SET HL TO PT TO END OF NEXT DAD D ; SECTOR IN QUEUE XCHG LHLD QNXT ;SEE IF QUEUE IS FULL NOW MOV A,H ;MAY BE SAME CMP D JNZ QFR2 MOV A,L ;MAY NOT BE SAME CMP E JZ QSAV2 ;QUEUE IS FULL, SO ABORT QFR2: LXI D,FCB ;PT TO FCB MVI C,READF ;READ SECTOR FROM FILE CALL BDOS PUSH PSW LHLD QLST ;GET PTR TO LAST QUEUE ELEMENT XCHG ;... IN DE LXI H,TBUFF ;COPY FROM TBUFF MVI B,128 ;128 BYTES CALL MOVE CALL QWRAP ;CHECK FOR WRAP AROUND XCHG ;HL PTS TO NEW LAST QUEUE POSITION SHLD QLST ;SAVE IT LHLD QCNT ;INCREMENT SECTOR COUNT INX H SHLD QCNT POP PSW ORA A JZ QFR1 LHLD QCNT ;DECREMENT SECTOR COUNT DCX H ;BECAUSE LAST READ DIDN'T SHLD QCNT ;DO A READ LXI D,FCB ;CLOSE FILE MVI C,CLOSEF CALL BDOS CALL ILPRT DB 'Queue Read from File',CR,LF,0 LHLD QCNT CALL PRQCNT ;PRINT COUNT POP H ;PT TO NEXT CHAR JMP QFS3 QFR3: CALL ILPERR DB 'Error in reading file into Queue',CR,LF,0 JMP QFS3 ; ; Subroutine to initialize FCB ; FCBINIT: PUSH D ;SAVE PTR LXI H,12 ;SKIP TO EX FIELD DAD D MVI B,24 ;ZERO 36 BYTES XRA A ;A=0 FCBIN1: MOV M,A ;STORE ZEROES INX H DCR B JNZ FCBIN1 POP D ;RESTORE PTR RET ; ;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: CALL DECIN ;GET REQUESTED USER NO. LDA MUSER ;GET MAX USER MOV B,A ;... IN B MOV A,E CMP B ;VALID? JNC USRERR MOV A,D ;HIGH-ORDER BYTE MUST BE ZERO ORA A ; FOR VALID NUMBER JNZ USRERR 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 USRERR: CALL ILPERR DB 'User Number Out of Range',CR,LF,0 JMP PRMPTR ; ;COMMAND: P ;Toggle print flag ; PRNTFF: LDA PFLAG ;TOGGLE PRINT FLAG CMA 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 HEXAD ;CLEAR ADDRESS STA HEXAD+1 STA PFLAG ;SET NO PRINT STA SAVEFL ;SET NO SAVE DONE STA WRFLG ;MAY NOT WRITE INR A ;A=1 STA FTSW ;SET SEARCH WITHOUT INCREMENT STA NOTPOS ;NOT POSITIONED LXI H,0 ;HL=0 SHLD QCNT ;SET NO ELEMENTS IN QUEUE 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 LHLD DIRECT ;SET FIRST AND LAST QUEUE ELEMENT PTRS SHLD QNXT SHLD QLST LXI H,0FFFFH ;FORCE FIRST TIME READ SHLD DBKSEC LXI H,MTABL ;CLEAR MACRO TABLE MVI B,10 ;10 ENTRIES INITP1: MVI M,CR ;STORE INR H ;PT TO NEXT PAGE DCR B 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 MVI C,SUSER ;GET USER NUMBER MVI E,0FFH ;GET USER CALL BDOS STA UNUM ;SET USER NUMBER LXI B,TBUFF ;SET DMA TO TBUFF CALL SETDMA MVI A,0FFH ;NO DRIVE YET STA DRIVE MVI C,GETDSK CALL BDOS ;GET CURRENT DISK MOV C,A ;WE HAVE TO SELECT JMP SELECT ;TO GET THE DPH ; ;COMMAND: K ;Stops console output, except for errors ; CONKIL: MVI A,0FFH ;SET CONSOLE FLAG STA KILCON JMP PROMPT ; ;COMMAND: L ;Log in the selected disk ; LOGIN: CALL DOLOG JMP PROMPT ; DOLOG: CALL GETCHR ;DISK REQUESTED? LXI D,0 CALL CHKEOL ;NO REQUEST IF EOL JZ LGNODK INX H ;POINT TO NEXT CHAR SUI 'A' ;CONVERT TO 0-15 MOV C,A ;DISK NUMBER IN C LDA MDISK ;GET MAX DISK MOV B,A ;... IN B MOV A,C CMP B JC SELECT CALL ILPERR DB 'Disk Letter Out of Range',CR,LF,0 JMP PRMPTR ; ;Select Disk Whose Number is in C (A=0, B=1, etc) ; SELECT: PUSH H ;SAVE PTR TO NEXT COMMAND LETTER LDA DRIVE CMP C JZ SELDK0 MVI E,0 ;FIRST TIME SELECT SELDK0: MOV A,C STA DRIVE ;REMEMBER LATER WHERE WE ARE ; VSELDK: MVI A,BSELDK CALL BIOS 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 LDA VERFLG ORA A JZ SELDK1 LXI H,10 ;OFFSET TO DPBPTR IN CP/M PLUS JMP SELDK2 SELDK1: LXI H,8 ;OFFSET TO DPBPTR IN CP/M 2.2 SELDK2: DAD D MOV A,M ;PICK UP DPB POINTER INX H ; TO USE MOV H,M ; AS PARAMETER MOV L,A ; TO LOGIT 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 SECTORS TO READ MOV C,L LHLD DIRECT ;DMA ADDR XCHG ;DE=DMA ADDR ; ;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 RUNNING 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 SECTOR 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 SECTORS MOV A,B ORA C JNZ RDIRLP LXI B,TBUFF ;RESET DMA ADDRESS TO TBUFF CALL SETDMA POP H ;GET PTR TO NEXT CHAR JMP DIRGRP ;GET NUMBER OF DIRECTORY GROUPS ;RET ; ;COMMAND: M ;Map the directory ; MAP: PUSH H ;SAVE PTR LHLD CURTRK SHLD SAVTRK ;save track ... LHLD CURSEC SHLD SAVSEC ; ... and sector LHLD QCNT ;GET COUNT MOV A,H ORA L POP H JZ MAP1 ;PROCEED IF QUEUE EMPTY CALL ILPERR ;PRINT ABORT MESSAGE DB CR,LF DB 'MAP not permitted -- Sector Queue ' DB 'would be overlaid',0 JMP PRMPTR MAP1: CALL GETCHR ;CHECK IF ERASED ALSO SHOWED CPI 'E' ;SIGNIFIED BY AN 'E' MVI A,0 ;CLEAR ERASED FLAG JNZ MAP1A ;NOT 'E' ? INR A ;SET ERASED FLAG INX H ;BYPASS 'E' MAP1A: STA TOGE ;SET/CLEAR ERASED FLAG CALL PAGSET ;SET PAGING COUNTER XRA A STA ONLY1 ;SET FLAG FOR ALL GROUPS (NOT ONLY 1) CALL REDDIR ;READ IN DIRECTORY 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 LHLD DSM ;CHECK IF GROUP TOO LARGE INX H CALL SUBDE JC OUTLIM ;GROUP TOO LARGE MOV H,B ;GET VALUE IN HL MOV L,C CALL SUBDE ;SEE IF GROUP TOO SMALL JNC OUTLIM MAP2: MOV B,D ;GET VALUE IN BC MOV C,E ; MAPDF: CALL HEXB ;PRINT FIRST GROUP NUMBER MVI A,'-' ;PRINT SEPARATOR CALL TYPEC 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 PUSH H ; ; ; End of map - reposition to previous group ; MAPND2: CALL CRLF LHLD SYSTRK XCHG LHLD SAVTRK CALL SUBDE ;within a group ? JNC MAPND3 ;yes, POSGP2 can handle it LHLD SAVSEC ;no, use track/sector positioning SHLD CURTRK XCHG CALL SETSEC LHLD SAVTRK SHLD CURTRK XCHG CALL SETTRK CALL READ XRA A STA NOTPOS POP H JMP INQ MAPND3: LHLD GROUP 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 TYPEC ;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 TYPEC MVI B,3 ;PRINT FILE TYPE CALL MAPN2 LDA DUPFLG ;DUPLICATE? CALL TYPEC ;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 TYPEC ;")" 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 0FFH STA TWOUP JZ PAGER ;NEW LINE WITH PAGING IF REQUIRED ; DELIM: MVI A,':' ;PRINT DELIMITER BETWEEN ADJACENT CALL TYPEC ; ENTRIES ON LINE 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 TYPEC ;PRINT AS CHAR ; MAPN2Z: DCR B 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 LHLD DIRECT ;PT TO DIRECTORY ; GETGLP: PUSH H ;SAVE POINTER TO NAME PUSH H LDA TOGE ORA A JZ SKERA INX H SKERA: MOV A,M ;PICK UP DN BYTE POP H CPI 0E5H ;NEVER USED? JZ GETGNF MOV A,M ;IGNORE XFCB, TIME/DATE STAMP ANI 0F0H ;AND DIR. LABEL CPI 20H ;DIR. LABEL OR TIME STAMP? JZ GETGNF CPI 10H ;XFCB ? 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 ; Special Form of G) ; it is used to extract the group number, check it, ; and position DU2 to it. ; On exit, GROUP = Group Number, GRPDIS = 0, and DU2 is positioned ; COMG: INX H ;PT TO CHAR AFTER 'G' OF ' ;Restore the current sector ; Special Form >S gets next sector from queue ; Special Form >G gets next group from queue ; RESTOR: CALL GETCHR ;CHECK FOR SPECIAL FORM CPI 'S' ;SECTOR SAVE? JZ QRESTOR CPI 'G' ;GROUP SAVE? JZ RESTRG 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 ; ; Restore Sector from Queue ; QRESTOR: INX H ;PT TO NEXT CHAR PUSH H ;SAVE PTR ON STACK LHLD QCNT ;GET ELEMENT COUNT MOV A,H ;EMPTY? ORA L JZ QEMPTY ;ABORT IF EMPTY DCX H ;COUNT DOWN SHLD QCNT CALL PRQCNT ;PRINT COUNT LHLD QNXT ;PT TO NEXT ELEMENT IN QUEUE LXI D,TBUFF ;COPY INTO TBUFF MVI B,128 ;128 BYTES CALL MOVE XCHG ;DE=PTR TO NEXT ELEMENT IN QUEUE CALL QWRAP ;CHECK FOR WRAP AROUND XCHG ;HL PTS TO NEXT ELEMENT IN QUEUE SHLD QNXT ;SAVE PTR POP H ;RESTORE PTR JMP PROMPT QEMPTY: CALL ILPERR DB 'Error -- Queue Empty',CR,LF,0 POP H ;RESTORE NEXT CHAR PTR JMP PRMPTR ; ;Write Group Loaded in GBUFF to Disk ; RESTRG: CALL COMG ;GET GROUP NUMBER FROM PUSH H ; COMMAND LINE AND POS 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 CRLF LHLD QNXT ;NEXT PTR USED FOR WRITE SHLD QPTR POP H MVI A,0FFH ;WRITE FUNCTION STA CPYFCT ;COPY FUNCTION FOR GROUP COPY ROUTINE JMP COPYG ;GROUP COPY ROUTINE ; NOSAVE: CALL ILPERR 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 EOLCH ;LOGICAL EOL? JNZ SRCHL ; ;Got match ; SREQU: 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 SECTOR ; 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 TYPEC ; 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, D, or H ;Dump in HEX, ASCII or BOTH ; DUMP: STA DUMTYP ;TYPE OF DUMP (A,D,H) LDA WRFLG ORA A JNZ DUMPOK ; BADDMP: CALL ILPERR DB '++ Can''t dump, no sector read ++',CR,LF,0 ; EXPL: CALL ILPERR 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 EOLCH ;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 CALL CHKEOL JZ DUMP1 INX H ;SKIP SEPCH 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 TYPEC ;PRINT CHAR MOV A,E CMP L JZ DEND INX H MOV A,L ANI 0FH JNZ DCHR ; DEND: CALL ASTER ;PRINT ENDING ASTERISK LDA DMPRET ;CALLED BU POSFIL? ORA A PUSH PSW CNZ PAGER ;YES, PAGE OUTPUT POP PSW CZ CRLF ;NEW LINE PUSH D CALL CTLCS ;ABORT? POP D JZ PRMPTR MOV A,E CMP L JNZ DUMPLP LDA DMPRET ;CALLED BU POSFIL? ORA A RNZ ;YES, RETURN TO POSFIL ROUTINE POP H JMP PROMPT ; DNOAS: POP B CALL CRLF MOV A,E CMP L JNZ DUMPLP POP H JMP PROMPT ; ;COMMAND: G, S, & T ;Position ; POS: CALL CKEOLC ; EOL? JNZ POSOK ; ; ;Tell what group, displacement, track, sector, physical sector ;Position inquiry routine ;Executed via: G S or T (with no operands) ; ; INQ: PUSH H LHLD SYSTRK ;CHECK IF IN SYSTEM TRACKS XCHG LHLD CURTRK CALL SUBDE JC NOGRP CALL ILPRT ;PRINT GROUP NUMBER IF NOT SYSTEM TRACKS DB 'Group = ',0 LHLD GROUP MOV B,H MOV C,L CALL HEXB ;PRINT GROUP NUMBER IN BC MVI A,':' CALL TYPEC LDA GRPDIS CALL HEX ;PRINT GROUP DISPLACEMENT IN A MVI A,',' CALL TYPEC ; 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 JMP PROMPT ; POSOK: MOV A,B ;RESTORE COMMAND 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 ;COMPUTE RELATIVE TRACK NUMBER XCHG ; (SKIP SYSTEM TRACKS) LHLD CURTRK CALL SUBDE XCHG LHLD SPT ;MULTIPLY BY NUMBER OF SECTORS/TRACK CALL MULWW ; (DE,HL) = DE * HL PUSH H ; (DE,HL)=TOTAL NUMBER OF SECTORS LHLD CURSEC ;GET SECTOR OFFSET FROM START OF TRACK DCX H MOV B,H ;BC = CURSEC - 1 MOV C,L POP H DAD B ;ADD SECTOR OFFSET JNC CLCSB1 ;CARRY? INX D ;YES CARRY CARRY TO TOP 16-BITS CLCSB1: LDA BLM MOV B,A MOV A,L ANA B STA GRPDIS ;DISPLACEMENT WITHIN GROUP LDA BLM ;A = SECTORS PER GROUP -1 INR A ;A = SECTORS PER GROUP MVI B,0 MOV C,A CALL DIVLW ; DE = TOTAL SECTORS / (BSH*2) XCHG ; HL = GROUP NUMBER SHLD GROUP ;GROUP NUMBER POP H RET ; ;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 SECTOR 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: LDA BLM ;GET NUMBER OF SECTORS IN GROUP INR A MVI H,0 MOV L,A ; HL = SECTORS IN GROUP CALL MULWW ; (DE,HL) = GROUP * BSH MVI B,0 LDA GRPDIS ;ADD IN DISPLACEMENT WITHIN GROUP MOV C,A DAD B JNC GTKSC1 INX D ;ADD CARRY TO HIGH 16 BITS GTKSC1: PUSH H LHLD SPT ;GET NUMBER OF SECTORS PER TRACK MOV B,H MOV C,L ; BC = SECTORS PER TRACK POP H CALL DIVLW ; DE = (DE,HL) / BC, HL = (DE,HL) MOD BC PUSH H LHLD SYSTRK ;ADD IN NUMBER OF SYSTEM TRACKS DAD D XCHG ;DE=TRACK NUMBER POP H INX H ;HL=SECTOR NUMBER RET ; ;COMMAND: F ;Find Directory Entry for specified file ; POSFIL: CALL NORITE CALL PAGSET ;SETUP PAGER CALL FILFCB ;FILL IN THE FCB MVI A,'?' STA FCB+12 ;SEARCH ALL EXTENTS STA FCB+14 ;EVEN OF FILES GREATER THAN 1/2 MEG PUSH H CALL DRVINIT ;SYNC BDOS WITH BIOS DRIVE LXI D,FCB MVI C,SRCHF CALL BDOS INR A JNZ FLOK CALL ILPERR DB '++ File Not Found ++',CR,LF,0 FLDNE: POP H JMP PROMPT ; FLOK: PUSH PSW MVI A,0FFH ;SET RETURN FLAG FOR DUMP STA DMPRET POP PSW FLOK1: CALL FCBDMP ;DUMP ONE FCB EXTENT MVI C,SRCHN ;FIND NEXT EXTENT CALL BDOS INR A JZ FLDNE ;DONE, IF NO MORE EXTENTS JMP FLOK1 ;GO PRINT IT FCBDMP: DCR A 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 ;PRINT OUT FCB EXTENT ; ;Fill in the default FCB ; FILFCB: CALL CKEOLC ;GET CHARACTER, CHECK FOR EOL? JZ WHAT INX H ;SEE IF DRIVE NAME THERE MOV A,M CPI ':' DCX H MVI A,0 ;SELECT DEFAULT DRIVE JNZ FLFCB1 CALL GETCHR ;GET DRIVE LETTER SUI '@' INX H ;PAST DRIVE LETTER INX H ;PAST COLON (':') FLFCB1: LXI D,FCB ;START TO FILL FCB STAX D ;SAVE DRIVE NUMBER INX D ;PT TO FILE NAME MVI B,8 ;SAVE FILE NAME CALL MVNAME MVI B,3 ;SAVE FILE TYPE MVNAME: CALL GETCHR ;GET NEXT CHAR OF FILE NAME/TYPE CPI '.' ;END OF FILE NAME? JZ MVIPAD ;PAD OUT IF SO CALL CHKEOL ;END OF ENTRY? JZ PAD ;PAD OUT IF SO STAX D ;STORE INX H ;PT TO NEXT INX D DCR B JNZ MVNAME CALL CKEOLC ;CHECK FOR ERROR, OK IF 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 CALL CKEOLC ;GET NEXT CHAR, CR? JZ PLUSGO ;..YES, DFLT TO 1 CALL DECIN ;GET # MOV A,D ORA E JNZ PLUSGO LXI D,1 ;SET 1 IF VALUE OF ZERO ; 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 CALL CKEOLC ;GET CHAR, CR? JZ MINGO ;..YES, DFLT=1 CALL DECIN ;..NO, GET ## MOV A,D ORA E JNZ MINGO LXI D,1 ;ASSUME 1 ; 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 SECTOR ; ;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, INX H ; SO INCREMENT CURRENT TRACK 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 ; ;COMMAND: C ;Change Contents of Current Sector ; CHG: CALL GETCHR ;GET TYPE (HEX, ASCII) PUSH PSW ;SAVE "H" OR "A" INX H CALL DISP ;GET DISP IN HEX INX H LXI B,0 ;SHOW NO 'THRU' ADDR CPI '-' ;TEST DELIM FR. DISP JNZ CHGNTH ;NO THRU PUSH D ;SAVE FROM CALL DISP ;GET THRU INX H ;SKIP END DELIM MOV B,D MOV C,E ;BC = THRU POP D ;GET FROM JMP CHGAH ; CHGNTH: CPI SEPCH JNZ WHAT ; CHGAH: POP PSW CPI 'H' ;HEX? JZ CHGHEX CPI 'A' ;ASCII? JNZ WHAT ; ;Change ASCII ; CHGALP: CALL CKEOLC ;GET CHAR, EOL? 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 TYPEC ;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 CALL CKEOLC ;NEXT CHAR, EOL? JZ PROMPT JMP WHAT ; ;Change hex ; CHGHCM: INX H ; CHGHEX: CALL CKEOLC ;GET NEXT CHAR, EOL? JZ PROMPT CPI SEPCH ;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 CALL CKEOLC ;CHECK IF NEXT CHAR EOL JZ PROMPT JMP WHAT ; ;COMMAND: R ;Read Current Sector into TBUFF ;COMMAND: RG ;Read Specified Group into GBUFF ; DOREAD: LDA NOTPOS ;POSITIONED? ORA A JNZ CANTRD CALL READ ;READ SECTOR JMP PROMPT ; CANTRD: CALL ILPERR 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 Sector to Disk ;COMMAND: WG ;Write Specified Group from GBUFF ; DORITE: CALL WRITE ;DO WRITE JMP PROMPT ; ;Print Byte in A as Hex Digits ; BHEX: PUSH PSW MVI A,'<' CALL TYPEC POP PSW CALL HEX MVI A,'>' CALL TYPEC RET ; ;Print Number in BC as Hex Digits ; HEXB does not print MS Byte if DSM shows small disk size ; HEXB1 prints BC regardless ; HEXB: LDA DSM+1 ORA A JZ HEXX HEXB1: 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 TYPEC ;PRINT IT ; ;Decimal output routine ; Print Number in HL as decimal digits ; DEC: PUSH B PUSH D PUSH H XRA A ;SET NO LEADING DIGIT STA DDIG LXI B,10000 CALL DPRT DAD B LXI B,1000 CALL DPRT DAD B LXI B,100 CALL DPRT DAD B LXI B,10 CALL DPRT DAD B MOV A,L ;ALWAYS PRINT LSD ADI '0' ;ASCII CALL TYPEC POP H POP D POP B RET DPRT: PUSH B ;SAVE BC MVI D,0FFH ;SET -1 DPRTL: INR D ;ADD 1 TO OUTPUT DIGIT MOV A,L ;L-C SUB C MOV L,A MOV A,H ;H-B SBB B MOV H,A JNC DPRTL POP B ;RESTORE BC LDA DDIG ;GET LEADING DIGIT FLAG ORA D ;CHECK FOR ZERO STILL STA DDIG ;SET FLAG MOV A,D ;GET DIGIT TO PRINT JZ DPRTSP ;IF BOTH ZERO ADI '0' ;ASCII JMP TYPEC DPRTSP: LDA SPFLG ;TYPE SPACE? ORA A RZ ;NO JUST RETURN MVI A,' ' JMP TYPEC DDIG: DS 1 ;TEMP FOR DEC USE ONLY SPFLG: DB 0 ;LEADING SPACE FLAG ; ;Print ; SPACE: MVI A,' ' JMP TYPEC ; ;Print '*' ; ASTER: MVI A,'*' JMP TYPEC ; ;Inline error print routine ; ILPERR: XRA A STA KILCON ;CLEAR FLAG ; ;Inline print routine ; Print Chars ending in 0 pointed to by Return Address ; return to byte after 0 ; 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 TYPEC ;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 HEXIN and validates a sector ;displacement, then converts it to an address ; DISP: CALL HEXIN 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: CALL ILPERR 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: CALL GETCHR ;GET CHAR CALL CHKEOL ;EOL? RZ CPI SEPCH 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: CALL GETCHR ;GET DIGIT CPI '0' ;RANGE? RC CPI '9'+1 ;RANGE? RNC SUI '0' ;CONVERT TO BINARY INX H ;PT TO NEXT 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 ILPERR ;PRINT PROMPT DB CR,LF,'DU2 ',0 LDA DRIVE ;GET DRIVE NUMBER ADI 'A' ;CONVERT TO ASCII CALL TYPEC LDA UNUM ;DISPLAY USER NUMBER MOV L,A ;VALUE IN HL MVI H,0 CALL DEC ;PRINT IN DECIMAL CALL ILPRT ;PRINT PROMPT DB '? ',0 LXI D,INBUF-2 ;USE CP/M READLN MVI C,READLN 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 TYPEC ;ECHO IT MVI A,LF ;ECHO.. CALL TYPEC ;..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 ILPERR DB CR,LF,'Type Any Character to Continue ' db '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 TYPEC MVI A,LF JMP TYPEC ; ;Get character and convert to Upper Case ; GETCHR: MOV A,M ANI 7FH ;MASK OUT MSB CPI 60H ;LESS THAN SMALL A? RC ;RETURN IF SO ANI 5FH ;MAKE UPPER CASE RET ; ;Get character and check for EOL terminator CKEOLC: MOV A,M ; ;Check for EOL terminator ; CHKEOL: CPI EOLCH RZ CPI CR RET ; ;CON: Status Routine ; CONST: PUSH B PUSH D PUSH H VCONST: MVI A,BCONST CALL BIOS POP H POP D POP B RET ; ;CON: Input Routine ; CONIN: PUSH B PUSH D PUSH H VCONIN: MVI A,BCONIN CALL BIOS POP H POP D POP B RET ; ;Console out with TAB expansion ; Char in A ; TYPEC: 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 TYPEC 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: ; ;CON: Output Routine ; VCONOT: LDA KILCON ORA A JNZ NOCON MVI A,BCONOT PUSH B CALL BIOS POP B NOCON: ; ;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 ORA A CNZ PRINTC ;FROM C REG ; TYPRET: POP H ;RESTORE REGS POP D POP B RET ; ;LST: Output Routine ; Char in C ; PRINTC: PUSH B ;SAVED REGS PUSH D PUSH H VLIST: MVI A,BLIST CALL BIOS POP H POP D POP B RET ; ;Home Disk Routine ; HOME: PUSH H VHOME: MVI A,BHOME CALL BIOS POP H RET ; ;Set track # in DE ; SETTRK: PUSH H LXI H,0FFFFH ; MARK DEBLOCK BUFFER EMPTY SHLD DBKSEC 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: MVI A,BSETRK CALL BIOS 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, NOT SYSTEM TRACKS LDA FIRST0 ;SEE IF FIRST SEC 0 ORA A JNZ ONSYS ;NO, JUMP AWAY DCX H ;YES, SO DECREMENT ONSYS: MOV B,H ; BC =HL MOV C,L LHLD CURTRK ;IS THE CURRENT TRACK ZERO ? MOV A,H ORA L JNZ ONSYS1 LDA PHM ;SIMPLE 128 BYTE PER SECTOR ORA A ;CASE? JZ ONSYS3 LHLD PRVTRK MOV A,H ;ALREADY ON TRACK 0? ORA L JZ ONSYS2 PUSH B ;CHECK IF TRACK 0 IS DEBLOCKED LXI B,1 MVI A,BSTSEC ; READ FIRST SECTOR CALL BIOS POP B LXI H,DATABU ;CHECK IF TRACK 0 LXI D,128 ; IS 128 BYTE SECTORS DAD D MVI M,0FFH ;SET FLAG AFTER 128 BYTES PUSH B PUSH H LXI B,DATABU CALL RSTDMA ;SET THE DMA LOCATION MVI A,BREAD CALL BIOS ORA A ;ERROR? JNZ ONSYS1 POP H POP B MVI A,0FFH ;STILL THERE?? CMP M JNZ ONSYS1 ;NO, TRACK 0 IS DEBLOCKED MVI M,0 ;SET DIFFERENT VALUE FOR FLAG PUSH B PUSH H LXI B,DATABU CALL RSTDMA ;SET THE DMA LOCATION MVI A,BREAD CALL BIOS ORA A ;ERROR? JNZ ONSYS1 POP H POP B XRA A ;STILL THERE??? ORA M ONSYS1: STA TRKZRO ;SET TRACK ZERO FLAG ; 0 MEANS 128 BYTE SECTORS ; NOT 0 MEANS (PHM+1)*128 BYTE SECTORS ONSYS2: LHLD CURTRK ONSYS3: SHLD PRVTRK MOV H,B ; HL = BC MOV L,C LDA TRKZRO ;ON TRACK 0 WITH 128 BYTE SECTORS? ORA A JZ GSTSEC LDA PHM ;SIMPLE 128 BYTE PER SECTOR ORA A ;CASE? JZ GSTSEC ;YES LDA FIRST0 ;SEE IF FIRST SEC 0 ORA A JZ ONSYS4 ;YES, JUMP AWAY DCX H ;NO, SO DECREMENT ONSYS4: LDA PHM INR A ; A = # SECTORS/PHYSICAL SECTOR LXI D,0 ; HIGH 16 BITS = 0 MVI B,0 MOV C,A CALL DIVLW ;DE=SECTOR MOV A,L ; MAX REMAINDER = 31 STA OFFSEC XCHG ;HL=SECTOR LDA FIRST0 ;SEE IF FIRST SEC 0 ORA A JZ GSTSEC ;YES, JUMP AWAY INX H ;NO, SO INCREMENT JMP GSTSEC ;REQUESTED, THEN GO ; ;Not in System Tracks, so Skew Factor is effective ; NOTSYS: MVI A,0FFH ;SET NON TRACK ZERO FLAG STA TRKZRO LHLD CURTRK ;SAVE CURRENT TRACK SHLD PRVTRK ;SET PRVTRK SO THAT TRKZRO FLAG WORKS DCX B ;DECREMENT SECTOR NUMBER BY 1 LDA PHM ;SIMPLE 128 BYTE PER SECTOR ORA A ;CASE? JZ VSCTRN ;YES, JUST SET SECTOR NUMBER MOV H,B ;NO, CALCULATE TRUE SECTOR NUMBER MOV L,C ; HL = BC INR A ; A = # SECTORS/PHYSICAL SECTOR LXI D,0 ;HIGH 16-BITS ZERO MVI B,0 MOV C,A CALL DIVLW ;DE=SECTOR MOV A,L ; MAX REMAINDER = 31 STA OFFSEC MOV B,D ;BC=SECTOR MOV C,E ; VSCTRN: LHLD SECTBL ;GET PTR TO SECTOR TABLE XCHG ;... IN DE MVI A,BSCTRN CALL BIOS LDA SPT+1 ;IF SPT<256 (HI-ORD = 0) ORA A ; THEN FORCE 8-BIT TRANSLATION JNZ GSTSEC ; ELSE KEEP ALL 16 BITS MOV H,A ; ZERO TOP 8 BITS GSTSEC: SHLD PHYSEC MOV B,H MOV C,L ; VSTSEC: MVI A,BSTSEC CALL BIOS POP H ;RESTORE PTR TO NEXT CHAR RET ; ;Out of Disk Track Limit ; OUTLIM: CALL ILPERR DB '++ Not Within Tracks 0-',0 PUSH H LHLD MAXTRK ;PRINT MAX TRACK NUMBER CALL DEC CALL ILPERR DB ' (',0 LHLD MAXTRK ;NOW IN HEX MOV B,H MOV C,L CALL HEXB1 CALL ILPERR DB ' Hex) or Groups 0-',0 LHLD DSM ;PRINT GROUP NUMBER CALL DEC CALL ILPERR DB ' (',0 LHLD DSM ;NOW IN HEX MOV B,H MOV C,L CALL HEXB1 CALL ILPERR DB ' Hex)',0 POP H CALL ILPERR DB ' ++',CR,LF,0 CALL NORITE ;NOT POSITIONED JMP PRMPTR ; ;Set Local DMA Address ; SETDMA: PUSH B POP H SHLD DMAADR ; ;Set true DMA Address ; RSTDMA: MVI A,BSTDMA JMP BIOS ; ;Read Next Sector into DMA Address ; READ: MVI A,1 ;SET FLAG STA WRFLG PUSH H ;SAVE PTR TO NEXT CHAR ; VREAD: LDA TRKZRO ;ON TRACK 0 WITH 128 BYTE SECTORS? ORA A JZ VREAD4 LDA PHM ;SIMPLE 128 BYTE READ ORA A JZ VREAD4 LHLD DBKSEC ;SAME SECTOR? XCHG LHLD PHYSEC SHLD DBKSEC MOV A,D CMP H JNZ VREAD0 MOV A,E CMP L JNZ VREAD0 LHLD PRVTRK ;SAME TRACK? XCHG LHLD CURTRK SHLD PRVTRK MOV A,D CMP H JNZ VREAD0 MOV A,E CMP L MVI A,0 ;SIGNAL NO ERRORS IF IN BUFFER JZ VREAD1 VREAD0: LXI B,DATABU ;SET DMA TO DEBLOCK BUFFER CALL RSTDMA MVI A,BREAD ;READ THE SECTOR CALL BIOS VREAD1: PUSH PSW LDA OFFSEC ;GET THE OFFSET LXI H,0 LXI D,128 VREAD2: ORA A JZ VREAD3 DAD D DCR A JMP VREAD2 VREAD3: LXI D,DATABU ;HL=DATABU+OFFSET DAD D XCHG LHLD DMAADR XCHG MVI B,128 CALL MOVE ;MOVE 128 BYTES TO BUFFER POP PSW ORA A ;ERROR? JNZ REDERR JMP READOK VREAD4: LHLD DMAADR PUSH H POP B CALL RSTDMA ;SET THE TRUE DMA ADDRESS MVI A,BREAD CALL BIOS ORA A ;ERROR? JZ READOK REDERR: CALL ILPERR DB '++ READ Failed, Sector may be Invalid ++' DB CR,LF,0 ; READOK: POP H ;GET PTR TO NEXT CHAR RET ; ;Write Sector in DMA Address to Disk ; WRITE: LDA WRFLG ;READ ALREADY PERFORMED? ORA A ;ERROR IF NOT JNZ PWRITE ; BADW: CALL ILPERR DB '++ Cannot Write Unless Read Issued ++' DB CR,LF,0 JMP EXPL ; ;Do Write ; PWRITE: PUSH H ;SAVE PTR TO NEXT CHAR ; VWRITE: LDA TRKZRO ;ON TRACK 0 WITH 128 BYTE SECTORS? ORA A JZ VWRTE3 LDA PHM ;SIMPLE 128 BYTE READ ORA A JZ VWRTE3 LXI B,DATABU ;SET DMA TO DEBLOCK BUFFER CALL RSTDMA LDA OFFSEC ;GET THE OFFSET LXI H,0 LXI D,128 VWRTE1: ORA A JZ VWRTE2 DAD D DCR A JMP VWRTE1 VWRTE2: LXI D,DATABU ;HL=DATABU+OFFSET DAD D XCHG LHLD DMAADR MVI B,128 CALL MOVE ;MOVE 128 BYTES FROM BUFFER JMP VWRTE4 VWRTE3: LHLD DMAADR PUSH H POP B CALL RSTDMA ;SET THE TRUE DMA ADDRESS VWRTE4: MVI C,1 ;FORCE WRITE TYPE 1,IF DEBLOCK USED MVI A,BWRITE CALL BIOS ORA A ;ERROR? JZ WRITOK CALL ILPERR 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: CALL ILPERR DB 'Introductory HELP on DU2 (Disk Utility)',CR,LF DB ' The DU2 program is designed to provide the ' DB 'user with' DB CR,LF DB 'the ability to manipulate information on the ' DB 'disk as easily' DB CR,LF DB 'as the DDT and SID utilities allow the user to ' DB 'manipulate' DB CR,LF DB 'information in memory.',CR,LF DB ' The following is a summary of the commands ' DB 'available to' DB CR,LF DB 'the DU2 user. This same list is invoked ' DB 'internally by the' DB CR,LF DB '? Command of DU2. For additional information ' DB 'on disk' DB CR,LF DB 'structures and how to use DU2 in general, refer ' DB 'to the' DB CR,LF DB 'files DU2.DOC and DU2.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: CALL ILPERR DB CR,LF DB '============================' DB '===========================' DB CR,LF DB ' -- Command Summary --' DB CR,LF DB '----------------------------' DB '---------------------------' DB CR,LF,LF DB 'Operands in brackets [...] are optional' DB CR,LF,LF DB '@ Repeat Previous Non-@ Command Line' DB CR,LF DB '+[nn] Step In [nn (decimal)] Sectors' DB CR,LF DB '-[nn] Step Out Sectors' DB CR,LF DB ' Note: + or - commands force a read.' DB CR,LF DB '< Save Current Sector into Temp' DB CR,LF DB '> Restore Sector from Temp' 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 ' Note: Upper/lower case matters. ' DB 'Use for hex:' DB CR,LF DB ' To find "IN 0" use: =<0>' DB ' or' DB CR,LF DB ' "(tab)H,0(CR)(LF)" use: ' DB '=<9>H,0' DB CR,LF DB '*[nn] Repeat [nn (decimal) times]' DB CR,LF DB '! Pause for user input' DB CR,LF,LF,0 CALL WAIT CALL ILPERR DB CR,LF DB '----------------------------' DB '---------------------------' DB CR,LF,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. ' DB 'ch0-7f e5' DB CR,LF DB ' or CAfrom-thru data (Ascii)' DB CR,LF DB 'D[ff,tt] Dump (Hex and ASCII)' DB CR,LF DB 'Ffn.ft 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 'K Kill console output' DB CR,LF DB 'L[d] Log in Current Drive or Drive d' DB CR,LF DB 'M[nn] Map [from group nn (hex)], ' DB 'use ME to show erased' DB CR,LF DB 'N Load New Disk' DB CR,LF DB 'P Toggle Printer Switch' DB CR,LF DB 'Snn Read Sector nn (decimal)' DB CR,LF DB 'Tnn Set Track nn (decimal)' DB CR,LF DB 'Unn Set User nn (decimal) for Find command' DB CR,LF DB 'V[nn] View [nn (decimal)] ASCII Sectors' DB CR,LF,0 CALL WAIT CALL ILPERR DB CR,LF DB '----------------------------' DB '---------------------------' DB CR,LF,LF DB 'R Read Current Sector' DB CR,LF DB 'W Write Current Sector' DB CR,LF DB 'X Exit Program' DB CR,LF DB 'Z[nn] Sleep [nn (decimal) seconds]' DB CR,LF,LF DB '--- QUEUE OPERATIONS:' DB CR,LF DB 'Q Queue Status' DB CR,LF DB 'QZ Zero (Empty) Queue' DB CR,LF DB 'QSd:fn.ft Save Queue as a File on Disk' DB CR,LF DB 'QRd:fn.ft Read Queue from a File on Disk' DB CR,LF DB 'S Restore Sector from Queue' DB CR,LF DB 'G[n] Restore Group from Queue' DB CR,LF,LF DB '--- MACRO OPERATIONS:' DB CR,LF DB ':ntext Define ''text'' to be Macro n' DB CR,LF DB 'n Perform Macro 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 ' DB 'Prev Line if x=@' DB CR,LF,0 CALL WAIT CALL ILPERR DB CR,LF DB '----------------------------' DB '---------------------------' DB CR,LF,LF DB 'Command Line is of the form: DU2 Commands' DB CR,LF DB ' Where "Commands" is any valid command ' DB 'line' DB CR,LF,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,LF DB '"nn" usage varies with command as follows:' DB CR,LF DB ' +, -, *, T, S, U, V, Z nn in Decimal' DB CR,LF DB ' ' DB '(use #nn for Hex)' DB CR,LF DB ' G, M ' DB 'nn in Hexadecimal' DB CR,LF DB ' ' DB '(use #nn for Decimal)' DB CR,LF DB '"ff" and "tt" are in Hexadecimal ' DB '(use #ff or #tt for Decimal)' DB CR,LF,LF DB '============================' DB '===========================' DB CR,LF,0 CALL WAIT CALL ILPERR DB '============================' DB '===========================' DB CR,LF DB 'DU2 Status Information' DB CR,LF DB '----------------------------' DB '---------------------------' DB CR,LF,LF DB 'Processor Speed: ',0 LDA CLOCK ;GET CLOCK SPEED ADI '0' ;CONVERT TO ASCII CALL TYPEC ;PRINT CALL ILPERR 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 ILPERR DB CR,LF,'Group Save Buffer Address: ',0 LHLD GBUFF MOV B,H ;BC=ADDRESS MOV C,L CALL HEXB1 ;PRINT AS HEX CALL ILPERR DB ' Hex',CR,LF DB '============================' DB '===========================' 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: MVI C,RESETDK ;GET BDOS BACK INTO CALL BDOS ; SYNC WITH BIOS JMP BASE ;JUST WARM BOOT ; ;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 ; ;Get number of directory groups ; DIRGRP: MVI C,0 ;INIT START GRP # LDA AL0 ;READ DIR GRP BITS CALL COLECT ;COLLECT COUNT OF DIR GRPS.. LDA AL1 ;..IN REGISTER C ; ;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 ; ;Multiply HL*DE, 32-bit product in DE (high) and HL (low) ; MULWW: MOV B,H ;MULTIPLICAND TO BC MOV C,L LXI H,0 ;INITIALIZE PRODUCT MVI A,16 ;COUNT 16-BIT MULTIPLIER MULWW1: STA DIVSTR ;SAVE A XRA A ;CLEAR CARRY MOV A,L ;SHIFT DE,HL LEFT ONE RAL MOV L,A MOV A,H ; REG H RAL MOV H,A MOV A,E ; REG E RAL MOV E,A MOV A,D ; REG D RAL MOV D,A LDA DIVSTR ;RESTORE A JNC MULWW2 ;JUMP IF NO MULTIPLIER BIT DAD B ;ELSE ADD MULTIPLICAND TO RESULT JNC MULWW2 ;IF LOW ORDER WORD OVERFLOWS, INX D ; CARRY INTO HIGH ORDER MULWW2: DCR A ;CONTINUE FOR 16 BITS JNZ MULWW1 RET ; ;Divide (DE,HL) by BC, quotient to DE, remainder to HL ; DIVLW: XCHG ;HIGH ORDER TO HL, LOW TO DE MVI A,16 ;COUNT 16-BIT QUOTIENT DIVLW1: STA DIVSTR ;SAVE A XRA A ;CLEAR CARRY MOV A,E ;SHIFT ONE BIT FROM DE TO HL RAL MOV E,A MOV A,D ; REG D RAL MOV D,A MOV A,L ; REG L RAL MOV L,A MOV A,H ; REG H RAL MOV H,A MOV A,L ; HL = HL - BC WITH CARRY SBB C MOV L,A MOV A,H SBB B MOV H,A ;WILL DIVISOR GO IN YET? LDA DIVSTR ;RESTORE A JNC DIVLW3 DAD B ;NO - RESTORE HL DIVLW2: DCR A ;CONTINUE FOR 16 BITS JNZ DIVLW1 RET ; DE = (DE,HL) / BC, ; HL = (DE,HL) MOD BC DIVLW3: INX D ;INCREMENT QUOTIENT JMP DIVLW2 ; ;Routine to fill in disk params ;with every drive change ; LOGIT: LDA PHM PUSH PSW ;SAVE PHM FOR LATER COMPARE LXI D,DPB ; THEN MOVE TO LOCAL MVI B,DPBLEN ; WORKSPACE CALL MOVE 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 LDA VERFLG ORA A JNZ LOGIT1 ;JUMP IF CP/M PLUS XRA A ;CP/M 2.2 DOES STA PSH ;DEBLOCKING IN THE BIOS STA PHM ;MARK AS 128 BYTE SECTORS LOGIT1: LDA PHM ORA A LXI H,0 JZ LOGIT3 ;JUMP IF 128 BYTE SECTORS INR A ;A=# SEC PER PHYSICAL SECTOR LXI D,128 ;GO PAST DATA BUFFER LOGIT2: DAD D DCR A JNZ LOGIT2 LOGIT3: LXI D,DATABU DAD D ;HL=DATABU +OFFSET SHLD DIRECT LDA PHM ;GET NEW PHM MOV B,A POP PSW ;GET OLD PHM CMP B ;DID IT CHANGE? JZ LOGIT4 ;IF SAME DON'T CLEAR QUEUE LXI H,0 ;CLEAR THE QUEUE BECAUSE ;IT IS AFTER DEBLOCKING BUFFER SHLD QCNT ;SET NO ELEMENTS IN QUEUE LHLD DIRECT ;SET FIRST AND LAST QUEUE ELEMENT PTRS SHLD QNXT SHLD QLST LOGIT4: LXI H,0FFFFH ; NEW DISK SHLD DBKSEC RET ; ; Routine will call either CP/M 2.2 BIOS or ; CP/M Plus BIOS via function 50 ; BIOS: STA BIOSPB LDA VERFLG ORA A JNZ BIOS30 PUSH H ;SAVE HL PUSH B ;SAVE BC LDA BIOSPB ;CALL 2.2 BIOS ; NOTE: THE FOLLOWING IS CODED ; SO THAT IT WILL WORK WITH ; PROGRAMS THAT DON'T SAVE ; THE JUMP TABLE AT THE ; BEGINNING OF A PAGE. ; (LIKE WORDSTAR) MOV H,A ADD H ;*2 ADD H ;*3 MOV C,A LHLD BASE+1 ;GET WARM BOOT ADDRESS MVI B,0 ;ADD OFFSET DAD B LXI B,-3 ;SUBTRACT WARM BOOT JUMP DAD B POP B ;RESTORE BC XTHL ;RESTORE HL RET ;GOTO BIOS ROUTINE BIOS30: XCHG ;SET UP REGISTERS SHLD BIOSDE MOV H,B MOV L,C SHLD BIOSBC MVI C,BIOSCL ;GOTO 3.0 BIOS ROUTINE LXI D,BIOSPB JMP BDOS BIOSPB: DS 1 ;BIOS FN NUMBER DB 0 ;REGISTER A BIOSBC: DS 2 ;REGISTER BC BIOSDE: DS 2 ;REGISTER DE ;*********************************** ; ; DU2 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 MULCH 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 'K' DW CONKIL ; DB 'L' DW LOGIN ; DB 'M' DW MAP ; DB 'N' DW NEWDSK ; DB 'P' DW PRNTFF ; DB 'Q' DW QUEUER ; 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-ELEMENT STACK DUTSTK: DS 2 ;OLD CP/M STACK POINTER BUFAD: DS 2 ;FORCES INITIAL READ QCNT: DS 2 ;NUMBER OF SECTORS IN QUEUE QNXT: DS 2 ;PTR TO NEXT SECTOR IN QUEUE QLST: DS 2 ;PTR TO LAST SECTOR IN QUEUE QPTR: DS 2 ;G-P QUEUE PTR 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 CPYFCT: DS 1 ;GROUP COPY FUNCTION; 0=READ, 0FFH=WRITE FILECT: DS 2 ;FILE COUNT FTSW: DS 1 ;SEARCH W/O INCREMENT NOTPOS: DS 1 ;INITIALLY NOT POSITIONED WRFLG: DS 1 ;MAY NOT WRITE UNTIL '+', '-', ; 'S', OR 'G' COMMAND TGRP: DS 2 ;TEMPORARY GROUP FLAG 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 SECTBL: DS 2 ;POINTER TO SECTOR SKEW TABLE IHFLG: DS 1 ;0=NOT AT INITIAL HELP, ~=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 VERFLG: DS 1 ;VERSION FLAG 0=CP/M 2.2 , NON-0=CP/M 3 GBUFF: ;ADDRESS OF GROUP BUFFER DIRECT: DS 2 ;ADDRESS OF DIRECTORY BUFFER TRKZRO: DS 1 ;TRACK ZERO FLAG PRVTRK: DS 2 ;PREVIOUS TRACK DMPRET: DS 1 ;FLAG FOR POSFIL ROUTINE DIVSTR: DS 1 ;TEMP STORAGE AREA FOR DIVIDE ROUTINE KILCON: DS 1 ;FLAG FOR NO CONSOLE OUTPUT TOGE: DS 1 ;FLAG TO SHOW ERASED FILES SAVTRK: DS 2 ;TRACK SAVE AREA FOR MAP COMMAND SAVSEC: DS 2 ;SECTOR SAVE AREA FOR MAP COMMAND ; ;The disk parameter block ;is moved here from bios ; DPB: ;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 PSH: DS 1 PHM: DS 1 ; ;End of disk parameter block ; ; ; DEBLOCKING data areas ; DMAADR: DS 2 OFFSEC: DS 1 DBKSEC: DS 2 ; ; SAVE buffer ; 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 ; ; Macros ; MTABL: DS 100H*10 ;10 PAGES FOR 10 MACROS ; ; BEGINNING OF FREE SPACE ; DATABU EQU $ ;Used for CP/M + deblocking buffer ; and Queue storage ; END