* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hard Disk Format Utility * * Copyright (C) 1985, 1986 AMPRO COMPUTERS, INC. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Assemble with asm.com or equivalent. ; All Z80 opcodes are defined with DB or DW statements. ; Revision history: ; ; Ver Date Who Description ; --- ----- --- ------------------------------------------ ; 1.5 F4.02 RJB Added Seagate 225N. Changed minimum bios ; requirement to 3.7 for Seagate support. ; ; 1.4 E7.18 RJB Added DTC format, changed minimum bios ; requirement to 3.1 for Shugart support. ; ; 1.3 E6.27 RJB Changed to support bios 3.0 to allow the ; user to manually enter the drive descriptor. ; ; 1.2 E3.14 RJB Modified signon message to include known ; rom revision limitations. ; ; E3.13 RJB Updated XEBEC and ADAPTEC step routines. ; ; 1.1 E2.14 RJB Added message reminding users to run the ; FINDBAD program to find any bad sectors ; on the disk they just formatted. ; ; E2.12 RJB Added message for those don't have any ; hard disk units defined and run the hard ; disk format program anyway. ; ; E1.30 RJB Fixed Xebec directory clear, Adaptec step, ; device select. Added additional user ; interface, error reporting routines. ; ; 1.0 DC.14 RLD Original version. ; ; Program version, and current version date VERS EQU 15 ; Current version THIS$MONTH EQU 4 ; Today's month THIS$DAY EQU 02 ; . day THIS$YEAR EQU 86 ; . year INT$REV EQU 99 ; Internal revision number ; TRUE and FALSE are defined here NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Operating characteristics MIN$VERSION EQU 37 ; Minimum bios version allowed CMD$LINE$OK EQU FALSE ; Allow command line input? RTN$VIA$WB EQU TRUE ; Return to CP/M via warm boot? INTERNAL EQU FALSE ; Internal (unreleased) revision? ; Include diagnostic messages? TEST EQU NO ; TEST diagnostics? ; Current screen width and output buffer width SWIDTH EQU 80 ; Screen width PWIDTH EQU 132 ; Output buffer width ; Z-80 opcode equates (reversed so we can use a DW to enter them) CPIR80 EQU 0B1EDH ; CPIR INIR80 EQU 0B2EDH ; INIR LDIR80 EQU 0B0EDH ; LDIR OTIR80 EQU 0B3EDH ; OTIR INI80 EQU 0A2EDH ; INI OUTI80 EQU 0A3EDH ; OUTI SBCD80 EQU 043EDH ; LD (dddd),BC LBCD80 EQU 04BEDH ; LD BC,(dddd) SDED80 EQU 053EDH ; LD (dddd),DE LDED80 EQU 05BEDH ; LD DE,(dddd) SSPD80 EQU 073EDH ; LD (dddd),SP LSPD80 EQU 07BEDH ; LD SP,(dddd) LXIX EQU 021DDH ; LXI IX,dddd SIXD80 EQU 022DDH ; LD (dddd),IX LIXD80 EQU 02ADDH ; LD IX,(dddd) POPIX EQU 0E1DDH ; POP IX PUSHIX EQU 0E5DDH ; PUSH IX LXIY EQU 021DDH ; LXI IY,dddd SIYD80 EQU 022FDH ; LD (dddd),IY LIYD80 EQU 02AFDH ; LD IY,(dddd) POPIY EQU 0E1FDH ; POP IY PUSHIY EQU 0E5FDH ; PUSH IY ; Bit SET/RESET/TEST Z-80 opcode equates (use DB to enter) ; Example: SET 7,D would be DB BIT,BSET+B7+ZD BIT EQU 0CBH ; Bit prefix BTST EQU 040H ; Bit test BRES EQU 080H ; Bit reset BSET EQU 0C0H ; Bit set B0 EQU 000H ; Bit 0 B1 EQU 008H ; Bit 1 B2 EQU 010H ; Bit 2 B3 EQU 018H ; Bit 3 B4 EQU 020H ; Bit 4 B5 EQU 028H ; Bit 5 B6 EQU 030H ; Bit 6 B7 EQU 038H ; Bit 7 ZB EQU 000H ; B Reg ZC EQU 001H ; C Reg ZD EQU 002H ; D Reg ZE EQU 003H ; E Reg ZH EQU 004H ; H Reg ZL EQU 005H ; L Reg ZM EQU 006H ; M Reg ZA EQU 007H ; A Reg ; Jump relative opcode equates (use DB to enter) ; Example: JR AGAIN would be DB JR,AGAIN-$-1 JR EQU 018H ; JR addr JRNZ EQU 020H ; JR NZ,addr JRZ EQU 028H ; JR Z,addr JRNC EQU 030H ; JR NC,addr JRC EQU 038H ; JR C,addr ; IX and IY prefixes (use DB to enter) IX EQU 0DDH ; IX prefix IY EQU 0FDH ; IY prefix ; Character equates CTRLC EQU 'C'-'@' ; Ctrl-C (ETX) BS EQU 'H'-'@' ; Ctrl-H (Backspace) TAB EQU 'I'-'@' ; Ctrl-I (Tab) LF EQU 'J'-'@' ; Ctrl-J (Line feed) FF EQU 'L'-'@' ; Ctrl-L (Form feed) CR EQU 'M'-'@' ; Ctrl-M (Carriage return) NAK EQU 'U'-'@' ; Ctrl-U CAN EQU 'X'-'@' ; Ctrl-X (Cancel) EOF EQU 'Z'-'@' ; Ctrl-Z (CP/M End-of-file) ESC EQU 1BH ; Ctrl-[ (Escape) EOS EQU '$' ; (CP/M End-of-string) DEL EQU 7FH ; (Delete) ; bdos equates BDOS EQU 5 ; bdos entry ; * * * * * * * * ; ; The code starts here ... ; ; * * * * * * * * ORG 0100H JMP START SCREEN$WIDTH: DB SWIDTH-1 ; 1 less than actual # SLOW$TERM: DB 10 ; Delay (ms) for slow term CMD$LINE$CHRS: DB 0 ; # of cmd line chrs left CMD$LINE$PTR: DW 0 ; Ptr to next cmd line chr NAME$MSG: DB 0DH,'AMPRO ' NAME: DB 'Hard Disk Format' DB ' Utility',CR,LF DB 'Copyright (C) 1985 AMPRO Computers, Inc.',CR,LF DB 'Version ',VERS/10+'0','.',VERS MOD 10+'0' IF INTERNAL ; Display internal revision number? DB 'x',INT$REV/10+'0',INT$REV MOD 10+'0' DB ' [',THIS$YEAR-80+'@' DB THIS$MONTH+'0'+((THIS$MONTH/10)*7) DB '.',THIS$DAY/10+'0',THIS$DAY MOD 10+'0',']' ENDIF DB CR,LF,LF,'$',CR HELP$MSG: DB 'Usage: HFORMAT' DB CR,LF,'$',CR,' ',EOF SIGNON$MSG: DB 'The AMPRO Hard Disk Format utility is used to ' DB 'format hard disk (winchester) drives, on the ' DB 'Adaptec ACB-4000, ' DB 'Data Technology 500 Series ' DB '(510A, 510B, 520A, 520B), ' DB 'Shugart 1610-4, ' DB 'Xebec 1410 (rom rev "E" or later), ' DB 'Xebec 1410A (rom rev "D" or later) ' DB 'controllers, or the ' DB 'Xebec Owl or Seagate 225N combination drive/controllers. ' DB 'You will be asked for the controller ' DB 'type, controller SCSI address, drive unit number, ' DB 'and the drive descriptor information.' DB CR,LF,LF,'$' WARNING$MSG: DB 'CAUTION: ALL DATA WILL BE ERASED ON THE DRIVE YOU ' DB 'SELECT TO FORMAT.',CR,LF,LF,LF DB '$' ; ; Initialize the command line input pointer. ; START: IF CMD$LINE$OK ; If we want to allow cmd line input, LXI H,0080H ; . Save the command line. LXI D,INBUF ; . . LXI B,128 ; . . DW LDIR80 ; . . LXI H,INBUF ; . Set up ptrs and count of chrs MOV A,M ; . . STA CMD$LINE$CHRS ; . Save count of characters, INX H ; . Bump line ptr, SHLD CMD$LINE$PTR ; . . and save ptr to cmd line ENDIF IF NOT CMD$LINE$OK ; If no command line input allowed, MVI A,0 ; . Clear the count of characters, STA CMD$LINE$CHRS ; . . LXI H,0 ; . but set the ptr up anyway. SHLD CMD$LINE$PTR ; . . ENDIF IF NOT RTN$VIA$WB ; If we're returning without warm boot, LXI H,0000H ; . then get the old stack ptr, DAD SP ; . and SHLD STACK ; . save it for later, ENDIF ; ; This is where you can jump to start the program over. ; TOP$MENU: LXI SP,STACK ; Stuff SP with our stack. CALL GET$BIOS$VERS ; Copy the jmp tbl to a local area ; ; Display the name and signon message ; IF CMD$LINE$OK ; If cmd line input is possible LDA CMD$LINE$CHRS ; Check for any input and skip ORA A ; . the initial messages if any JNZ CHECK$B$VERS ; . chrs in the cmd line. ENDIF CALL CLEAR$SCREEN ; Clear the screen LXI D,NAME$MSG ; Display the name, version, etc. CALL CENTER$OUTPUT ; . LXI D,SIGNON$MSG ; and the initial message CALL JUSTIFY ; . ; ; Check the version of the bios against the minimum version allowed. ; If the bios is not at least the minimum, display an error message ; and exit to the operating system. ; CHECK$B$VERS: LDA BIOS$VERSION ; Get bios version # CPI MIN$VERSION ; Check against minimum version JNC WHICH$BIOS ; At least minimum version . . . LXI D,BIOS$PLUS ; Not minimum, display error message CALL JUSTIFY ; . JMP LB$WBOOT ; and exit. BIOS$PLUS: DB 'This program requires AMPRO bios version ' VERSION$REQ: DB MIN$VERSION/10,'.',MIN$VERSION MOD 10,' or later.' DB CR,LF,'$' ; ; Perform any initialization particular to each version of the Ampro ; bios, if necessary. ; WHICH$BIOS: CALL RET$TO$CONT JZ ALL$DONE ; ; Program main loop . . . ; AGAIN: CALL CLEAR$SCREEN ; Clear the screen LXI D,SCSI$ADDR$MSG ; Prompt for SCSI address LXI H,SCSI$ADDR$OKC ; . CALL PROMPT ; . [ 0 1 2 3 4 5 6 7 ] JZ TOP$MENU ; . MOV A,B ; . DCR A ; . CALL BIN$TO$SCSI ; . STA SCSI$ADDR ; . CALL A$TO$HL$HEX ; . SHLD S$ADDR$CODE ; . LXI D,HDC$TYPE$MSG ; Prompt for controller type LXI H,HDC$TYPE$OKC ; . CALL PROMPT ; . [ 1 2 3 4 ] JZ AGAIN ; . MOV A,B ; . DCR A ; . STA HDC$TYPE ; . MVI A,0 ; Set logical unit to 0 for SCSI drives STA DRV$LUN ; . LDA HDC$TYPE ; If Xebec OWL or Seagate 225n CPI XEBEC$OWL ; . skip the drive characteristics JZ SCSI$DRIVE ; . as these SCSI drives already CPI SEAGATE$225N ; . "know" what their characteristics JZ SCSI$DRIVE ; . are. LXI D,DRV$LUN$MSG ; Prompt for drive logical unit # LXI H,DRV$LUN$OKC ; . CALL PROMPT ; . [ 0 1 2 3 ] JZ AGAIN ; . MOV A,B ; . DCR A ; . RRC ; Shift response into high bits RRC ; . RRC ; . ANI 0E0H ; . STA DRV$LUN ; . LXI D,DRV$INFO$MSG ; Ask for drive characteristics CALL JUSTIFY ; . LXI D,DRV$CYLS$MSG ; . (Cylinders) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$CYLS ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$HEAD$MSG ; . (Heads) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . STA DRV$HEAD ; . LXI D,DRV$RWC$MSG ; . (RWC) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$RWC ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$WPC$MSG ; . (WPC) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$WPC ; . MOV M,D ; . INX H ; . MOV M,E ; . LDA HDC$TYPE ; . CPI SHUGART ; . JNZ NOT$SHUGART ; . LXI D,DRV$LZ$MSG ; . (Landing zone) CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,DRV$LZ ; . MOV M,D ; . INX H ; . MOV M,E ; . NOT$SHUGART: CALL GET$STEP ; Get step rate table for this HDC CALL PROMPT ; Get step rate factor JZ AGAIN ; . MOV A,B ; . DCR A ; . STA STEP$RATE ; . SCSI$DRIVE: LXI D,INTL$MSG ; Prompt for interleave factor LXI H,INTL$OKC ; . CALL PROMPT ; . [ 1 2 3 4 5 6 7 8 9 ] STA INTL$ECHO ; . MOV A,B ; . STA INTERLEAVE ; . (& save the interleave) JZ AGAIN ; . if , back to main loop LXI D,RTF$MSG ; Prompt for return key, to LXI H,RTF$OKC ; . main menu, exits CALL PROMPT ; . [ ESC CR ] JZ AGAIN ; . if , back to main loop CALL HD$FORMAT ; Do the format routine LXI D,AGAIN$MSG ; Display successful msg & prompt LXI H,AGAIN$OKC ; . for another drive to format. CALL PROMPT ; . [ ESC N Y CR ] JZ ALL$DONE ; . if , all done CPI 'N' ; If yes or CR, play it again, sam, JNZ AGAIN ; . otherwise fall into ALL$DONE ; ; Restore the old stack pointer and exit the program. ; ALL$DONE: JMP LB$WBOOT ; Return via warm boot SCSI$ADDR$MSG: DB 'What is the SCSI address of your ' DB 'controller (0-7)? ','$' SCSI$ADDR$OKC: DB '01234567',0 HDC$TYPE$MSG: DB CR,LF,'Which type of controller are you using:' DB CR,LF,TAB,'1 - Adaptec ACB 4000' DB CR,LF,TAB,'2 - Shugart 1610-4' DB CR,LF,TAB,'3 - Xebec OWL (drive/controller)' DB CR,LF,TAB,'4 - Xebec 1410, 1410A' DB CR,LF,TAB,'5 - Seagate 225N (drive/controller)' DB CR,LF,TAB,'6 - Data Technology 500 Series' DB CR,LF,LF DB 'Choose one (1-6): ','$' HDC$TYPE$OKC: DB '123456',0 ADAPTEC: EQU 0 SHUGART: EQU 1 XEBEC$OWL: EQU 2 XEBEC$1410: EQU 3 SEAGATE$225N: EQU 4 DTC$500: EQU 5 DRV$LUN$MSG: DB CR,LF DB 'Which drive on the controller do you want ' DB 'to format (0-3)? ','$' DRV$LUN$OKC: DB '0123',0 DRV$INFO$MSG: DB CR,LF DB 'Please enter the characteristics of the ' DB 'drive you wish to format:',CR,LF,'$' DRV$CYLS$MSG: DB ' Number of cylinders: ','$' DRV$HEAD$MSG: DB ' Number of heads: ','$' DRV$RWC$MSG: DB 'Starting cylinder for RWC: ','$' DRV$WPC$MSG: DB 'Starting cylinder for WPC: ','$' DRV$LZ$MSG: DB ' Landing zone cylinder: ','$' ADP$STEP$MSG: DB CR,LF,LF DB 'Adaptec step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 28us buffered step',CR,LF DB TAB,'2 - 12us buffered step',CR,LF DB 'Which step mode (0, 1, or 2)? ','$' ADP$STEP$OKC: DB '012',0 DTC$STEP$MSG: DB CR,LF,LF DB 'Data Technology step mode (choose ' DB 'from the following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 200us buffered step',CR,LF DB TAB,'2 - 150us buffered step',CR,LF DB TAB,'3 - 100us buffered step',CR,LF DB TAB,'4 - 50us buffered step',CR,LF DB 'Which step mode (0, 1, 2, 3, or 4)? ','$' DTC$STEP$OKC: DB '01234',0 SHU$STEP$MSG: DB CR,LF,LF DB 'Shugart step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'1 - 1ms step',CR,LF DB TAB,'2 - 200us buffered step',CR,LF DB TAB,'3 - 70us buffered step',CR,LF DB TAB,'4 - 50us buffered step',CR,LF DB 'Which step mode (0, 1, 2, 3, or 4)? ','$' SHU$STEP$OKC: DB '012345',0 XEB$STEP$MSG: DB CR,LF,LF DB 'Xebec step mode (choose from the ' DB 'following table):',CR,LF DB TAB,'0 - 3ms step',CR,LF DB TAB,'4 - 200us buffered step',CR,LF DB TAB,'5 - 70us buffered step',CR,LF DB TAB,'6 - 30us buffered step',CR,LF DB TAB,'7 - 15us buffered step',CR,LF DB 'Which step mode (0, 4, 5, 6, or 7)? ','$' XEB$STEP$OKC: DB '04567',0 INTL$MSG: DB CR,LF,'What sector interleave factor ' DB '(1-9, usually 2)? ','$' INTL$OKC: DB '123456789',0 SCSI$ADDR: DB 01H HDC$TYPE: DB 0 DRV$LUN: DB 0 DRV$CYLS: DW 0 ; Keep these four items in order DRV$HEAD: DB 0 ; . (cyls, head, rwc, wpc) DRV$RWC: DW 0 ; . DRV$WPC: DW 0 ; . DRV$LZ: DW 0 STEP$RATE: DB 0 INTERLEAVE: DB 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Hard disk display & format routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * GET$STEP: LDA HDC$TYPE ; Get controller type LXI H,H$STEP$EXE ; & step table starting addr JMP GO$TABLE ; Jump to controller routine H$STEP$EXE: DW H$STEP$ADP ; Adaptec ACB 4000 DW H$STEP$SHU ; Shugart 1610-4 DW H$STEP$XEB ; Xebec Owl DW H$STEP$XEB ; Xebec 1410, 1410A DW H$STEP$ADP ; Seagate 225N DW H$STEP$DTC ; Data Technology 500 Series H$STEP$ADP: LXI D,ADP$STEP$MSG LXI H,ADP$STEP$OKC RET H$STEP$DTC: LXI D,DTC$STEP$MSG LXI H,DTC$STEP$OKC RET H$STEP$SHU: LXI D,SHU$STEP$MSG LXI H,SHU$STEP$OKC RET H$STEP$XEB: LXI D,XEB$STEP$MSG LXI H,XEB$STEP$OKC RET DO$SCSI: ; ; Entry: ; HL = Command string ; DE = Data string ; SCSI address in SCSI$ADDR ; Drive logical unit in DRV$LUN ; ; Exit: ; HL = unchanged ; DE = unchanged if no error, else ptr to error data ; A = SCSI error code (0 = no error) ; INX H ; Point to drive unit LDA DRV$LUN ; 'OR' it in ORA M ; . MOV M,A ; . DCX H ; Set HL back LDA SCSI$ADDR ; Get SCSI address CALL LB$SCSIDRV ; Call SCSI driver, ORA A ; set status, RET ; and return. HD$FORMAT: ; ; [E6.07] ; ; Format a hard disk unit. ; ; Entry: ; Drive format information in the following locations: ; ; SCSI address of controller SCSI$ADDR ; Hard disk controller type HDC$TYPE ; Disk drive logical unit # DRV$LUN ; Interleave factor INTERLEAVE ; ; Exit: ; designated disk unit is formatted ; ; Modifies: ; all ; LXI D,E5BUF ; Fill first 256 bytes of buffer LXI B,00E5H ; . with 0E5H, used to clear unit CALL FILL$BLOCK ; . if HDC doesn't clear by itself. LXI H,E5BUF ; Fill next 8K with 0E5H. When the LXI D,E5BUF+256 ; . HD$DIR$CLEAN routine is run, the LXI B,8192-256 ; . unit will be cleared "track" by DB 0EDH,0B0H ; . "track". LDA HDC$TYPE ; Get controller type LXI H,H$FMT$EXE ; & table starting address JMP GO$TABLE ; Jump to controller routine H$FMT$EXE: DW H$DO$ADAPTEC ; Adaptec 4000 DW H$DO$SHUGART ; Shugart 1610-4 DW H$DO$OWL ; Xebec Owl DW H$DO$XEBEC ; Xebec 1410 rev e, 1410A rev d DW H$DO$SEAGATE ; Seagate 225N DW H$DO$DTC ; Data Technology 500 Series H$DO$UNKNOWN: LXI D,NEW$CTRL ; Print error message for an unknown CALL JUSTIFY ; . controller type MVI A,0FFH ; Set error status JMP H$FMT$DONE ; . H$DO$ADAPTEC: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LXI D,A$MD ; Where to put the fmt data LXI H,DRV$CYLS ; . LXI B,7 ; . DB 0EDH,0B0H ; . (LDIR) LDA STEP$RATE ; Get step rate STA A$STEP ; Save step rate XRA A ; Set stop position to beyond the STA A$LZ ; last track LDA INTERLEAVE ; Set interleave STA A$INTL ; . LXI H,A$MSC ; Mode Select LXI D,A$MSD ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,A$FC ; Format unit LXI D,A$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . ; CALL H$DIR$CLEAN ; Clean directory area (not needed) ; JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine A$MSC: DB 15H,0,0 ; Mode select DB 0,16H,0 ; . A$MSD: DB 0,0,0,8 ; Mode select data DB 0,0,0,0,0 ; . DB 0,2 ; . (block size = 512 bytes) DB 0 ; . DB 1 ; . A$MD: DB 0,0,0,0,0,0,0 ; . (format data from bios) A$LZ: DB 0 ; . (landing zone position) A$STEP: DB 0 ; . (adaptec step rate) A$FC: DB 04H ; Format unit DB 06H ; . (use next byte for fill) DB 0E5H ; . (fill byte) DB 00 ; . A$INTL: DB 00 ; . (interleave) DB 00 ; . H$DO$DTC: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LHLD DRV$CYLS ; Move the data to the proper area SHLD D$CYL ; . LDA DRV$RWC+1 ; . STA D$RWC ; . LDA DRV$HEAD ; . DCR A ; . STA D$HD ; . LDA STEP$RATE ; Get step rate MOV E,A ; Translate from table MVI D,0 ; . LXI H,D$TRAN$TBL ; . DAD D ; . MOV A,M ; . STA D$STPR ; . LDA INTERLEAVE ; Set interleave STA D$INTL ; . LXI H,D$MSC ; Mode Select LXI D,D$MSD ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,D$FC ; Format unit LXI D,D$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . ; CALL H$DIR$CLEAN ; Clean directory area ; JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine D$TRAN$TBL: DB 60 ; 3ms DB 4 ; 200us DB 3 ; 150us DB 2 ; 100us DB 1 ; 50us D$MSC: DB 0C2H,0,0 ; Mode select DB 0,0,0 ; . D$MSD: DB 11 ; Mode select data D$STPR: DB 0 ; . (Step rate) DB 0 ; . (Step mode) D$HD: DB 0 ; . (# of heads) D$CYL: DB 0,0 ; . (# of cylinders) D$RWC: DB 0 ; . (RWC starting cylinder) DB 0,0 D$FC: DB 04H ; Format unit DB 0 ; . DB 0 ; . DB 0 ; . D$INTL: DB 0 ; . (interleave) DB 0 ; . H$DO$SHUGART: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LHLD DRV$CYLS ; Move the data to the proper area SHLD S$CYL ; . LHLD DRV$LZ ; . SHLD S$LZ ; . LHLD DRV$RWC ; . SHLD S$RWC ; . LHLD DRV$WPC ; . SHLD S$WPC ; . LDA DRV$HEAD ; . STA S$HD ; . LDA STEP$RATE ; Get step rate MOV E,A ; Translate from table MVI D,0 ; . LXI H,S$TRAN$TBL ; . DAD D ; . DAD D ; . MOV A,M ; . STA S$STPR ; . INX H ; . MOV A,M ; . STA S$STPR+1 ; . LDA INTERLEAVE ; Set interleave STA S$INTL ; . LXI H,S$MSC ; Mode Select LXI D,S$MSD ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,S$FC ; Format unit LXI D,S$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . CALL H$DIR$CLEAN ; Clean directory area JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine S$TRAN$TBL: DB 4,3 ; 3ms DB 4,1 ; 1ms DB 0,200 ; 200us DB 0,70 ; 70us DB 0,50 ; 50us S$MSC: DB 15H,0,0 ; Mode select DB 0,20H,0 ; . S$MSD: DB 0,0,0,8 ; Mode select data DB 0,0,0,0,0 ; . DB 0,2,0 ; . (Block size = 512 bytes) DB 80H,0 ; . S$HD: DB 0 ; . (# of heads) S$SPW: DB 16 ; . (Step pulse width = 16us) S$STPR: DB 0,0 ; . (Step rate) S$CYL: DB 0,0 ; . (# of cylinders) S$RWC: DB 0,0 ; . (RWC starting cylinder) S$WPC: DB 0,0 ; . (WPC starting cylinder) S$LZ: DB 0,0 ; . (Landing zone cylinder) DB 0,0,0,0,0,0 ; . S$FC: DB 04H ; Format unit DB 0 ; . DB 0 ; . DB 0 ; . S$INTL: DB 0 ; . (interleave) DB 0 ; . H$DO$OWL: MVI A,7 ; Set step rate to '7' for OWL STA STEP$RATE ; (and fall through to H$DO$XEBEC ... H$DO$XEBEC: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LXI D,X$ID ; Where to put the fmt data LXI H,DRV$CYLS ; . LXI B,7 ; . DB 0EDH,0B0H ; . (LDIR) LDA STEP$RATE ; Get step rate ORI 20H ; . (set sector buffer bit) STA X$STEP ; . LDA INTERLEAVE ; Set interleave STA X$INTL ; . LXI H,X$IC ; Init Drive Characteristics LXI D,X$ID ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,X$WBC ; Write Sector Buffer LXI D,E5BUF ; . CALL DO$SCSI ; . JNZ FMT$ERROR ; . LXI H,X$FC ; Format unit LXI D,X$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . ; CALL H$DIR$CLEAN ; Clean directory area (not needed) ; JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine X$IC: DB 0CH ; Init Drive Characteristics DB 0,0,0,0,0 ; . X$ID: DB 0,0,0,0,0,0,0,0 ; Init Drive data X$WBC: DB 0FH ; Write sector buffer DB 0,0,0,0,0 ; . X$FC: DB 04H ; Format unit DB 0,0,0 ; . X$INTL: DB 0 ; . X$STEP: DB 0 ; . H$DO$SEAGATE: LXI D,FMT$START$MSG ; Show the user we're going ... CALL JUSTIFY ; . LDA INTERLEAVE ; Set interleave STA SEA$INTL ; . LXI H,SEA$RSD ; Read sense data LXI D,SEA$RSD ; . (to clear reset error) CALL DO$SCSI ; JNZ FMT$ERROR ; LXI H,SEA$FC ; Format unit LXI D,SEA$FC ; . (no data required) CALL DO$SCSI ; . JNZ FMT$ERROR ; . CALL H$DIR$CLEAN ; Clean directory area JNZ FMT$ERROR ; . JMP H$FMT$DONE ; and jump to the end routine SEA$RSD: DB 03H ; Read sense data DB 0,0,0,0,0 ; SEA$FC: DB 04H ; Format unit DB 00H ; . DB 00H ; . DB 00 ; . SEA$INTL: DB 00 ; . (interleave) DB 00 ; . H$DIR$CLEAN: ; ; This routine clears the drive by reading the capacity of the unit ; and writing sectors filled with 0E5H. This clears all directory ; areas on the unit, no matter what partitioning is provided. This ; clearing is done 'track by track' by sending the data in 8Kb ; blocks. ; LXI D,CLR$DIR$MSG ; Tell 'em we're clearing his drive CALL JUSTIFY ; . LXI H,RD$CAP$CMD ; Read the capacity of the drive LXI D,RD$CAP$DATA ; . CALL DO$SCSI ; . RNZ ; Return with status if error ; Convert from blocks to tracks by multiplying HL and ; A by 16 and throwing away the low 8 bits (which is ; the same as dividing by 16, only this is easier). ; HL reg pair C A reg ORA A ; ---------------- - -------- LDA RD$CAP$DATA+1 ; ---------------- - -------- MOV H,A ; xxxxFEDC-------- - -------- LDA RD$CAP$DATA+2 ; xxxxFEDC-------- - -------- MOV L,A ; xxxxFEDCBA987654 - -------- LDA RD$CAP$DATA+3 ; xxxxFEDCBA987654 - 3210xxxx ADD A ; xxxxFEDCBA987654 3 210xxxx- DB 0EDH,06AH ; xxxFEDCBA9876543 - 210xxxx- ADD A ; xxxFEDCBA9876543 2 10xxxx-- DB 0EDH,06AH ; xxFEDCBA98765432 - 10xxxx-- ADD A ; xxFEDCBA98765432 1 0xxxx--- DB 0EDH,06AH ; xFEDCBA987654321 - 0xxxx--- ADD A ; xFEDCBA987654321 0 xxxx---- DB 0EDH,06AH ; FEDCBA9876543210 - xxxx---- ; HL now contains the number of 'tracks' CLR$NEXT$TRACK: DCX H ; Decrement the track count SHLD CUR$TRACK ; Save current track XRA A ; Convert back to 20-bit ; A reg C HL reg pair ; -------- - FEDCBA9876543210 DAD H ; -------- F EDCBA9876543210- RAL ; -------F - EDCBA9876543210- DAD H ; -------F E DCBA9876543210-- RAL ; ------FE - DCBA9876543210-- DAD H ; ------FE D CBA9876543210--- RAL ; -----FED - CBA9876543210--- DAD H ; -----FED C BA9876543210---- RAL ; ----FEDC - BA9876543210---- XCHG ; Save block # in write command LXI H,WRITE$BLK$CMD ; . INX H ; . MOV M,A ; Move high byte INX H ; . MOV M,D ; Move middle byte INX H ; . MOV M,E ; Move low byte LXI H,WRITE$BLK$CMD ; Write 8K block to disk at this blk LXI D,E5BUF ; . CALL DO$SCSI ; . RNZ ; Return with status if error LHLD CUR$TRACK ; Get track number back MOV A,H ; See if we're done ORA L ; . JNZ CLR$NEXT$TRACK ; Non-zero means more to do . . . XRA A ; Set A-OK status RET RD$CAP$CMD: DB 025H,0 ; Read capacity command DB 0,0,0,0,0,0,0,0 ; . RD$CAP$DATA: DB 0,0,0,0,0,0,0,0 ; Read capacity data area WRITE$BLK$CMD: DB 00AH,0 ; Write data cmd DB 0,0,16,0 ; . CUR$TRACK: DW 0 ; Current track data FMT$ERROR: LXI D,NO$RESPONSE ; CPI 0FFH ; Timeout error? JZ FMT$TIMEOUT ; Yes, print timeout msg MOV A,M ; No, Get last command CALL A$TO$HL$HEX ; Convert to hex SHLD ERR$CMD ; Save in output line LXI H,FMT$ERR$CMD ; Get status from controller LXI D,FMT$ERR$DATA ; . CALL DO$SCSI ; . LDAX D ; Get error code CALL A$TO$HL$HEX ; Convert to hex SHLD ERR$CODE LXI D,FORMAT$ERROR ; Print error message FMT$TIMEOUT: CALL JUSTIFY ; . MVI A,0FFH ; Set error status JMP H$FMT$DONE ; and return FMT$ERR$CMD DB 3,0,0 ; Request sense DB 0,4,0 FMT$ERR$DATA DB 0,0,0,0 ; Sense data H$FMT$DONE: ORA A ; Set status LXI D,FMT$ERR$MSG ; Print "Errors occurred during PUSH PSW ; . formatting" if NOT ZERO CNZ JUSTIFY ; POP PSW ; LXI D,FMT$OK$MSG ; Print "Format complete. No CZ JUSTIFY ; . errors" if ZERO RET ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Library routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * A$TO$HL$HEX: ; ; [DC.27] ; ; Converts the number in A to the hex digits in HL ; ; Entry: ; A = number to convert ; ; Exit: ; HL = the hex equivalent of the number (L=high, H=low) ; (use shld to store the converted number) ; ; Modifies: none ; PUSH PSW ; Save original number RRC ; Get high nybble RRC ; . RRC ; . RRC ; . ANI 0FH ; . CALL A$TO$HEX ; Convert to hex MOV L,A ; Save in L register POP PSW ; Get original number back PUSH PSW ; Save again for later ANI 0FH ; Get low nybble CALL A$TO$HEX ; Convert to hex MOV H,A ; Save in H register POP PSW ; Get original number back RET ; and return A$TO$HEX: ; Convert A to a hex digit CPI 0AH ; If 0-9, we don't need to JM A$TO$HEX$2 ; . add any offset ADI 07H ; Offset for A-F A$TO$HEX$2: ; . ADI 30H ; ASCII bias RET ; and return BIN$TO$SCSI: ; Converts binary 0-7 to SCSI address ; ; Entry: ; A = number to convert (0-7) ; ; Exit: ; A = converted SCSI address [00H = error] ; ; Modifies: B ; ANI 07H ; Mask out all but addrs 0-7 INR A ; Bump A to shift at least one MOV B,A ; . bit and move to B register XRA A ; Clear A register STC ; Set carry for shift BIN$NEXT$BIT: RAL ; Shift left one bit DCR B ; Decrement count JNZ BIN$NEXT$BIT ; Zero means all done RET CENTER$OUTPUT: ; ; [DC.20] ; ; Automatically centers the output line(s) based on the line width ; stored in SCREEN$WIDTH. Each line is delimited with CR+LF. This ; routine will return to the caller when it encounters the string ; terminator, '$'. Any additional LF characters after a CR+LF pair ; will be passed through. ; ; Entry: ; DE = Pointer to output string(s), terminated with CR+LF. ; ; Exit: ; The output string(s) are sent to the screen ; ; Modifies: DE ; PUSH PSW ; Save registers PUSH B ; . PUSH H ; . CENTER$NEXT$LN: CALL GET$STRLEN ; Get length to next CR or '$' in B MOV A,B ; Check for zero length ORA A ; . JZ NEXT$DELIM ; If so, output the CR, LF, etc. LDA SCREEN$WIDTH ; Compute offset needed to center line STC ; . SBB B ; . (if there are too many chrs, just JC NO$BLANKS ; . print the line as is . . . ) ANI 0FEh ; Clear least significant bit RRC ; . and rotate to divide by two MVI C,' ' ; Output enough blanks to center line CNZ CON$CHR$AC ; . (only if count is non-zero) NO$BLANKS: MOV A,M ; Save CR for later MVI M,'$' ; Plug position with '$' for cp/m CALL CON$MSG ; . print string function MOV M,A ; Restore saved CR NEXT$DELIM: MOV A,M ; Get character CPI CR ; Print it if CR JZ OUTPUT$DELIM ; . CPI LF ; Print it if LF JZ OUTPUT$DELIM ; . CPI EOS ; Stop processing if EOS ('$') JZ CENTER$DONE ; . XCHG ; Put new pointer in DE JMP CENTER$NEXT$LN ; Go & do the next line OUTPUT$DELIM: CALL CON$CHR ; Output delimiter CPI LF ; Wait 10ms if we have a line feed CZ WAIT ; . INX H ; Point to next chr JMP NEXT$DELIM ; and check that one, also CENTER$DONE: POP H ; Restore registers POP B ; . POP PSW ; . RET ; and return CLEAR$SCREEN: ; ; [E1.28] ; ; This routine clears the screen by calling DO$CRLF 26 times. ; ; Entry: ; None ; ; Exit: ; The screen is cleared ; ; Modifies: ; None ; PUSH PSW ; Save just in case MVI A,26 ; 26 CRLF's C$NEXT$LINE: CALL DO$CRLF ; Next line DCR A ; Done? JNZ C$NEXT$LINE ; Nope. POP PSW ; Restore original AF RET ; and return CON$CHR: ; ; [DC.20] ; ; This routine sends the character in the "A" register to the console ; through the BDOS conout call. ; ; Entry: ; A = character to send ; ; Exit: ; character is sent to the console ; ; Modifies: ; None ; PUSH PSW PUSH B PUSH D PUSH H MVI C,2 MOV E,A CALL BDOS POP H POP D POP B POP PSW RET CON$CHR$AC: ; ; [DC.20] ; ; This routine sends the character in the C register to the console ; the number of times in the A register. ; ; Entry: ; A = Number of times to send character ; C = Character to send ; ; Exit: ; Same ; ; Modifies: ; None ; PUSH PSW ; Save all registers PUSH B ; . PUSH D ; . PUSH H ; . MOV B,A ; Move data to accomodate CP/M MOV A,C ; . NEXT$CHR$OUT: CALL CON$CHR ; Send 1 chr DCR B ; Decrement counter JNZ NEXT$CHR$OUT ; Done? POP H ; Restore all registers POP D ; . POP B ; . POP PSW ; . RET ; and return CONIN$NE$XC: ; ; [E1.28] ; ; Console input, no echo, exit on ctrl-c ; ; Entry: ; none ; ; Exit: ; A = character from console, except for ctrl-c, which causes ; an immediate jump to ALL$DONE ; ; Modifies: ; A ; CALL LB$CONIN CPI CTRLC JZ ALL$DONE RET CON$MSG: ; ; [DC.20] ; ; Console message ; ; Entry: ; DE = pointer to message string, terminated with '$' ; ; Exit: ; message printed on console ; ; Modifies: A, BC ; PUSH PSW ; Save registers PUSH B ; . PUSH D ; . PUSH H ; . MVI C,9 ; BDOS print string command CALL BDOS ; . POP H ; Restore registers POP D ; . POP B ; . POP PSW ; . RET ; and return DO$CRLF: ; ; [DC.27] ; ; This routine sends a carriage return and a line feed to the terminal, ; and then waits 'SLOW$TERM' ms for a slow terminal to catch up. ; ; Entry: ; none ; ; Exit: ; CR + LF is sent to the screen. ; ; Modifies: ; none ; PUSH PSW ; Save AF MVI A,0Dh ; Send carriage return CALL CON$CHR ; . MVI A,0Ah ; and line feed CALL CON$CHR ; . LDA SLOW$TERM ; Check slow flag ORA A ; . CNZ WAIT ; wait for the s-l-o-w terminals POP PSW ; recover original AF RET ; and return FILL$BLOCK: ; ; [E1.29] ; ; Fills the buffer pointed to by DE with the character in C for ; a length of B bytes (max 256). ; ; Entry: ; B = Length to fill ; C = Character to fill with ; DE = Start of buffer ; ; Exit: ; Buffer filled ; ; Modifies: ; Only buffer area affected ; PUSH B ; Save registers PUSH D ; . XCHG ; Set up M register use F$NEXT: MOV M,C ; Stuff a char INX H ; Bump pointer DCR B ; Bounce counter JNZ F$NEXT ; Do it again if not done XCHG ; We are done. restore old HL POP D ; Restore other registers POP B ; . RET ; and return GET$BIOS$VERS: ; ; [DC.20] ; ; Get bios version -- Copies the current BIOS jump tables (starting ; at warm boot) to a local area for ease of utility access. If the ; BIOS is version 2.0 or greater, the secondary jump table is copied ; also. ; ; Entry: ; none ; ; Exit: ; Z = bios 1.0 - 1.4 (floppy only bios) ; NZ = bios 2.0 or greater (floppy & fixed disk bios) ; ; Modifies: All registers ; LHLD 1 ; Get start of bios jump table LXI D,LB$BIOS$TBL ; Move bios to local storage MVI B,LB$LEN ; . (length of bios area) CALL MOVE$BLOCK ; . (move routine) MVI A,0 ; Test CP/M version CALL LB$GETNXT ; Get next jump table STA BIOS$VERSION ; Save bios version INX H ; See if HL is 0FFFFh MOV A,H ; . ORA L ; . RZ ; If so, then old version DCX H ; Fix HL as it has the table addr LXI D,LB$XTBL ; Move extra table to local storage MVI B,LB$XLEN ; . (length of extra table) CALL MOVE$BLOCK ; . (move routine) MVI A,0FFH ; Set NZ to indicate bios ORA A ; ... version 2.1+ RET ; ... and return. GET$HL$PTR: ; ; [DC.20] ; ; Gets the pointer pointed to by HL and puts it in HL ; ; Entry: ; HL = pointer to put in HL ; ; Exit: ; HL = pointer ; ; Modifies: none ; PUSH PSW ; Save A register MOV A,M ; Get low byte of pointer INX H ; . MOV H,M ; Get high byte of pointer MOV L,A ; Pointer is now together POP PSW ; Restore A register RET ; and return GET$STRLEN ; Searches the string pointed to by HL and returns the string length ; to the next carriage return. The length is returned in B. PUSH D ; Save start of string MVI B,0 ; Clear counter TRY$NEXT$CHR: LDAX D ; Get character CPI CR ; Is it CR? JZ EOS$FOUND ; . CPI '$' ; Is it '$'? JZ EOS$FOUND ; . INR B ; No -- increment count and INX D ; . point to the next character JMP TRY$NEXT$CHR ; . EOS$FOUND: POP H ; CR or '$' found, recall orig ptr XCHG ; DE=orginial, HL=current RET ; and return GO$TABLE: ; ; [E2.05] ; ; Jump to a routine based on a table of pointers ; ; Entry: ; A = index into table ; HL = table base address ; ; Exit: ; Routine at (A*2)+HL is executed ; ; Modifies: ; B, HL ; LXI B,2 ; Compute offset to table of routines CALL INDEX$TABLE ; . CALL GET$HL$PTR ; . PCHL ; Jump to proper routine INDEX$TABLE: ; ; [E1.30] ; ; Computes offset to table given base address, entry length, and entry ; requested. ; ; Entry: ; A = entry # ; BC = table entry length ; HL = base address ; ; Exit: ; HL = address to entry ; ; Modifies: ; A, BC, HL ; ORA A ; Set up flags for first check I$TBL$ADD: RZ ; If A=0, we're done DAD B ; Otherwise add length to base, DCR A ; . decrement counter, JMP I$TBL$ADD ; . and check again. IS$IT$OK: ; ; [E1.28] ; ; Check the character in A against the list of "OK" chrs pointed ; to by HL ; ; Entry: ; A = character to check ; HL = pointer to list of "OK" characters ; ; Exit: ; A = original character if ok, 0ffh if not in list ; B = position of character in list ; ; Modifies: ; BC ; PUSH H ; Save original "OK" pointer MOV C,A ; Save chr to check against MVI B,0 ; Clear counter CPI ESC ; If chr is JZ I$CHR$OK ; . then automatically ok INR B ; . otherwise start counting at 1 I$CHK$NEXT: MOV A,M ; Get chr to check against ORA A ; End of table? JNZ I$NOT$EOT ; No, check chr DCR A ; Decrement to get 0ffh MOV B,A ; Stuff for later move JMP I$CHR$OK ; And exit I$NOT$EOT: CMP C ; Chrs match? JZ I$CHR$OK ; . Yes, return INX H ; . No, bump pointer INR B ; . . and bump counter JMP I$CHK$NEXT ; . . and check next chr I$CHR$OK: MOV A,B ; Set status based on ORA A ; . position counter MOV A,C ; Get user chr back I$DONE: POP H ; and original "OK" pointer RET ; and return JUSTIFY: ; ; [E6.11] ; ; This routine will send a data stream to the console, with each line ; justified based on the SCREEN$WIDTH value. The stream must terminate ; with the CP/M end of string character ($) and may contain imbedded ; CR,LF pairs to separate paragraphs. ; ; NOTE: To insure proper operation, the LF character should only follow ; a CR character or another LF character, as the CR character is used to ; flush the current line without justification. ; ; Two entry points are provided: ; JUSTIFY Justify output, flush right ; JUSTIFY$RAGGED Justify output, ragged right ; ; As of E6.11, the flush right routine was not installed, so either ; entry point will provide the same results. ; ; Entry: ; DE = pointer to line(s) to output ; ; Exit: ; The data is sent to the screen. ; ; Modifies: All registers ; MVI A,80H ; Set flush right mode DB JR,E$JUSTIFY-$-1; Jump to routine entry JUSTIFY$RAGGED: MVI A,00H ; Set ragged right mode E$JUSTIFY: STA J$MODE ; Save mode byte MVI A,'$' ; Mark start of buffer STA OUTBUF-1 ; . XCHG ; DE is usually print source J$NEXT$LINE: XRA A ; Clear character counter STA BLANK$LEN ; . MOV B,A ; . LXI D,OUTBUF ; Set up buffer pointer J$CHECK$CHR: MOV A,M ; Get character CPI CR ; CR? DB JRNZ,J$NO$R-$-1 ; . CALL J$FLUSH$LINE ; . Flush output line, MVI A,CR ; . Output CR, CALL CON$CHR ; . . INX H ; . bump ptr & check next DB JR,J$NEXT$LINE-$-1 and 255 J$NO$R: CPI LF ; LF? DB JRNZ,J$NO$L-$-1 ; . CALL CON$CHR ; . Output LF, INX H ; . bump ptr & check next DB JR,J$CHECK$CHR-$-1 and 255 J$NO$L: CPI FF ; FF? DB JRNZ,J$NO$F-$-1 ; . CALL CLEAR$SCREEN ; . Clear screen, INX H ; . bump ptr & check next DB JR,J$CHECK$CHR-$-1 and 255 J$NO$F: CPI '$' ; End of string? DB JRNZ,J$NO$S-$-1 ; . CALL J$FLUSH$LINE ; . Flush output line, and RET ; . return to caller. J$NO$S: STAX D ; Not a special chr, save in buffer CPI ' ' ; Blank? DB JRNZ,J$NO$B-$-1 ; . No, don't save position SHLD BLANK$POS ; Save position for later XCHG ; and save corresponding position SHLD OUTBUF$BLANK ; . of the blank we just saved XCHG ; . in the output buffer MOV A,B ; . STA BLANK$LEN ; Save current length also J$NO$B: INR B ; Increment counter INX H ; . and input pointer INX D ; . and output pointer LDA SCREEN$WIDTH ; Compare counter against screen width SUB B ; . JP J$CHECK$CHR ; And continue checking if not past end ; ; Screen width exceeded, send this line to the screen. ; LHLD OUTBUF$BLANK ; Get pos of last blank in output buf MVI M,'$' ; and plug with eos ('$') LDA J$MODE ; Justify right edge only if the ORA A ; . right-justify flag is non-zero CM J$ADD$BLANKS ; . CALL J$SEND$BUFFER ; Output the line to the screen CALL DO$CRLF ; and a CR / LF LHLD BLANK$POS ; Get pointer to where we left off J$SKIP$BLANKS: INX H ; Bump pointer past blank(s) MOV A,M ; . CPI ' ' ; . DB JRZ,J$SKIP$BLANKS-$-1 and 255 JMP J$NEXT$LINE ; and check next segment J$FLUSH$LINE: ; Flush line when CR or EOS encountered MVI A,'$' ; Plug current position with EOS ($) STAX D ; . CALL J$SEND$BUFFER ; Send this line of data RET ; and return J$ADD$BLANKS: RET ; At a later time, this routine will ; justify the right margin by inserting ; extra blanks in the output line. J$SEND$BUFFER: MOV A,B ; If line to output is of zero length, ORA A ; . then don't output the line. RZ ; . LXI D,OUTBUF ; Get address of output buffer CALL CON$MSG ; and call our print message routine RET ; return J$MODE DB 0 ; Current right justify mode BLANK$POS DW 0 ; Last blank on this line OUTBUF$BLANK DW 0 ; Last blank in the output buffer BLANK$LEN DB 0 ; Length of line to the blank ; NOTE: OUTBUF is defined to be after the stack and before the heap. ; end of justify$output data area MOVE$BLOCK: ; ; [DC.20] ; ; Move a block of memory (up to 256 bytes) ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; data moved ; ; Modifies: ; none ; PUSH PSW ; Save registers PUSH B ; . PUSH D ; . PUSH H ; . M$NEXT$CHR: MOV A,M ; Get next byte, STAX D ; Save in destination. INX H ; Increment pointer to source. INX D ; Increment pointer to dest. DCR B ; Decrement counter. JNZ M$NEXT$CHR ; Loop back if we're not done. POP H ; Restore registers POP D ; . POP B ; . POP PSW ; . RET ; No more to do -- return. PROMPT: ; ; [E1.30] ; ; Prompt the user or the command line for input. ; ; Two entry points are provided: ; PROMPT standard entry, CRLF after chr from user ; PROMPT$NOLF special entry, No CRLF after chr from user ; ; Entry: ; DE = pointer to prompt string ; HL = pointer to list of valid chars (terminated with 00H) ; ; Exit: ; A = char from the user ; B = position of this character (0, 1, 2, ... n) ; ; Z = char was the escape key ; NZ = char was not the escape key ; ; Modifies: ; A, BC ; MVI A,01H ; Set CRLF after chr JMP E$PROMPT ; Jump to entry point PROMPT$NOLF: MVI A,00H ; Set no CRLF after chr E$PROMPT: STA PROMPT$MODE ; Save prompt mode flag LDA CMD$LINE$CHRS ; Are there any characters left from ORA A ; . the command line? JZ P$DISP ; No -- display & get chr from bios PUSH H ; Yes, save oklist pointer LHLD CMD$LINE$PTR ; and get chr from command line DCR A ; . Reduce count of chrs by one STA CMD$LINE$CHRS ; . . MOV A,M ; . Get command line character INX H ; . Increment pointer SHLD CMD$LINE$PTR ; . . POP H ; . Restore oklist pointer CPI ',' ; Change ',' to CR JNZ P$NOT$COMMA ; . MVI A,CR ; . P$NOT$COMMA: CPI '.' ; Change '.' to ESC JNZ P$NOT$DOT ; . MVI A,ESC ; . P$NOT$DOT: CPI '_' ; Underline means prompt & get chr JZ P$DISP ; . from bios anyway (user input) CALL TO$UPPER ; Convert the chr to upper case CALL IS$IT$OK ; Check the chr against the ok list RP ; If ok, return XRA A ; Otherwise, cancel the cmd line STA CMD$LINE$CHRS ; . buffer and fall through to p$disp P$DISP: PUSH H ; Save pointer to ok-chrs CALL JUSTIFY ; and call justify routine POP H ; . P$TRY$AGAIN: CALL CONIN$NE$XC ; Console input, no echo, except ^C CALL TO$UPPER ; Convert the chr to upper case CALL IS$IT$OK ; If the character is not "OK" JM P$TRY$AGAIN ; . get another CNZ CON$CHR ; . otherwise display it PUSH PSW ; Check mode flag in case we need LDA PROMPT$MODE ; . to send a CR+LF after the ANI 01H ; . user's input CNZ DO$CRLF ; . POP PSW ; . RET ; and return PROMPT$MODE: DB 0 ; Prompt mode flag PROMPT$DECIMAL: ; ; [E6.11] ; ; Prompt the user for a decimal input of up to 5 digits. The Z flag ; indicates the termination character: Z = ESC key, results may not ; be valid; NZ = RETURN key, results valid. ; ; Entry: ; DE = pointer to prompt string ; ; Exit: ; DE = Value entered by the user, 0-0FFFFH ; A = Low byte of value entered by the user, 0-0FFH ; ; Z = ESC key pressed, ignore results ; NZ = RETURN key pressed, results valid ; ; Modifies: ; A, PSW ; MVI A,01H ; Set CRLF after entry DB JR,E$PR$DEC-$-1 ; Jump to entry point PROMPT$DEC$NOLF: MVI A,00H ; Set no CRLF after entry E$PR$DEC: STA PROMPT$D$MODE ; Save prompt mode flag PUSH H ; Save original HL reigster PUSH B ; . and the BC register, too. XRA A ; Clear the current digit pointer MOV C,A ; Setup count in C register LXI H,SCRATCH ; Initialize the scratch pointer NEXT$DIGIT: PUSH B ; Save digit counter PUSH H ; . and string pointer LXI H,DEC$INPUT$OKC ; Get current 'ok' chrs CALL PROMPT$NOLF ; Prompt for digit POP H ; Get string pointer back POP B ; . along with digit counter DB JRZ,ESCRTN-$-1 ; Return if ESC key hit CPI CR ; Check for return JZ CONVERT$STRING ; Convert string to decimal, if so CPI BS ; Check for backspace JZ BACKUP$DIGIT ; Back up 1 digit, if we can MOV B,A ; Save character for a moment MOV A,C ; Check digit count CPI 5 ; . JC ADD$DIGIT ; 5 digits or less, add to string LXI D,BLOT ; More than 5 digits, blot this one CALL CON$MSG ; . JMP DE$FOR$NEXT ; . ADD$DIGIT: INR C ; Otherwise bump digit count MOV M,B ; Save digit INX H ; Bump digit pointer JMP DE$FOR$NEXT ; Setup DE for next prompt BACKUP$DIGIT: MVI A,' ' ; Bump forward to clear digit CALL CON$CHR ; . XRA A ; Are we at the beginning? CMP C ; . JZ DE$FOR$NEXT ; Yes, don't backup MVI A,BS ; Backup to correct position CALL CON$CHR ; . DCX H ; Backup pointer 1 chr DCR C ; Push count 1 back, also DE$FOR$NEXT: LXI D,DEC$INPUT$MSG ; Setup for next prompt JMP NEXT$DIGIT ; . CONVERT$STRING: MVI M,0 ; Mark end of string LXI H,SCRATCH ; Get beginning of string CALL STR$TO$DE ; Convert string to DE register MVI A,0FFH ; Insure NZ flag ORA A ; . MOV A,E ; Move low byte to A PUSH PSW ; Check mode flag to see if a LDA PROMPT$D$MODE ; . CR+LF should be sent after ORA A ; . the user's input CNZ DO$CRLF ; . POP PSW ; . ESCRTN: POP B ; Get old BC register back POP H ; . and original HL, as well. RET ; and return with result in HL & A DEC$INPUT$MSG: DB '$' ; Decimal input message DEC$INPUT$OKC: DB '0123456789' ; Decimal input 'ok' chrs DB BS,CR,0 ; . BLOT: DB BS,' ',BS,'$' ; Blot out digit SCRATCH: DB ' ',0 ; Max 5 digits PROMPT$D$MODE: DB 0 ; CRLF flag RET$TO$CONT: ; ; [E2.19] ; ; Prompts and waits for the RETURN key to be pressed. ; ; Entry: ; none ; ; Exit: ; Display message and wait for a RETURN key. ; ; Modifies: ; all ; LXI D,RTC$MSG ; Press RETURN to continue ... CALL CENTER$OUTPUT ; . LXI D,NO$MSG ; . LXI H,RTC$OKC ; . CALL PROMPT ; . [ CR ] RET RTC$MSG: DB 'Press the RETURN key to continue ... ' NO$MSG: DB '$' RTC$OKC: DB CR,0 ; end of RET$TO$CONT routine SCSI$TO$BIN: ; ; [DC.27] ; ; Converts SCSI address to binary 0-7 ; ; Entry: ; A = SCSI address to convert ; ; Exit: ; A = converted address (0-7) [0FFH = error] ; ; Modifies: B ; MVI B,0FFH ; Set up B register for possible error ORA A ; If A=0, error JZ SCSI$CONVERTED ; . SCSI$NEXT$BIT: INR B ; Increment count RRC ; Shift address right 1 bit JNC SCSI$NEXT$BIT ; Bit shifted to cary means we're done SCSI$CONVERTED: MOV A,B ; Move converted addr to A RET STR$COMP: ; ; [E1.18] ; ; Compare two strings ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; Z = two strings equal ; M = source < dest ; P = source >= dest ; ; Modifies: all ; PUSH D PUSH H NEXT$COMPARE: MOV C,M INX H LDAX D INX D CMP C JNZ NOT$EQUAL DCR B JNZ NEXT$COMPARE NOT$EQUAL: POP H POP D RET STR$SWAP: ; Swap two strings ; ; [E1.18] ; ; Entry: ; HL = source ; DE = destination ; B = count ; Exit: ; data moved ; ; Modifies: all ; MOV C,M LDAX D MOV M,A MOV A,C STAX D INX H INX D DCR B JNZ STR$SWAP RET STR$TO$DE: ; ; [E5.26] ; ; Converts the string pointed to by HL to a number in the DE reg. ; The conversion will continue until the first non-numeric chr ; found. ; ; NOTE: a test for register overflow is not made. If HL points ; to a string whose numerical value is greater than 65535 (64K), ; inaccurate results will occur. ; ; Entry: ; HL = ptr to string ; ; Exit: ; DE = value of string in HL ; HL = next character to process ; ; Modifies: ; DE, HL ; ; PUSH PSW ; We need the A register PUSH B ; . and the BC register LXI D,0 ; Clear totals STN$NEXT$CHR: PUSH D ; Save results so far . . . POP B ; . MOV A,M ; Get chr CPI '0' ; Less than '0'? DB JRC,NO$DIG-$-1 ; Yes, finished CPI '9'+1 ; Greater than '9' DB JRNC,NO$DIG-$-1 ; Yes, finished SUI '0' ; No, convert to 0-9 MOV E,A ; Save this digit MVI D,0 ; . PUSH H ; Save ptr to input string LXI H,0 ; Multiply previous by 10 DAD B ; x1 DAD H ; x2 DAD H ; x4 DAD B ; x5 DAD H ; x10 DAD D ; Add in new digit XCHG ; Save results back to DE POP H ; Get input string ptr back INX H ; Bump to next chr JMP STN$NEXT$CHR ; Go back for another digit NO$DIG: POP B ; Get old BC reg back POP PSW ; . and old A reg as well. RET ; Return with results in DE. TO$UPPER: ; ; [E1.08] ; ; Convert the character in A to upper case. ; ; Entry: ; A = character to convert ; ; Exit: ; A = upper case character (if alpha) ; ; Modifies: ; A ; CPI 'z'+1 ; Convert to upper case JP UPPER$ALREADY ; . CPI 'a' ; . JM UPPER$ALREADY ; . ANI 5FH ; . UPPER$ALREADY: RET ; and return WAIT: ; Wait A ms ; ; Entry: ; milliseconds in A ; ; Exit: ; time waited ; ; Modifies: A ; PUSH PSW MVI A,221 WAIT$2: DCR A JNZ WAIT$2 POP PSW DCR A JNZ WAIT RET * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Data area . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; Replicated BIOS to make direct calls easier . . . LB$BIOS$TBL: LB$WBOOT DS 3 ; Warm boot LB$CONST DS 3 ; Console status LB$CONIN DS 3 ; Console input LB$CONOUT DS 3 ; Console output LB$LISTOUT DS 3 ; List output LB$PUNCH DS 3 ; Punch output LB$READER DS 3 ; Reader input LB$HOMDSK DS 3 ; Home disk (move to track 00) LB$SELDSK DS 3 ; Select disk drive LB$SETTRK DS 3 ; Select track number LB$SETSEC DS 3 ; Select sector number LB$SETDMA DS 3 ; Set DMA address LB$DSKREAD DS 3 ; Disk read LB$DSKWRITE DS 3 ; Disk write LB$LISTST DS 3 ; List status LB$SECTRN DS 3 ; Sector translate routine ; AMPRO-specific BIOS calls LB$GETNXT DS 3 ; Get bios ver & next tbl address LB$GETEDSK DS 3 ; Get pointer to E-disk storage LB$IOINIT DS 3 ; Set new I/O parameters LB$SCSIDRV DS 3 ; SCSI direct driver LB$LEN EQU $-LB$WBOOT ; Length of bios table LB$XTBL: ; 'Extra' table definitions ... LB$SWAP$DRV DS 3 ; Swap two logical drives LB$GET$WDP DS 3 ; Set/get win drive parameters LB$PHYTAB DS 3 ; Set/get phytab access LB$GET$LDTE DS 3 ; Get logical device table entry LB$RESERVED DS 3 ; Reserved entry LB$XLEN EQU $-LB$XTBL ; Length of extra table BIOS$VERSION DB 0 ; Bios version number YOU$ARE$ABT DB 'You are about to format the following ' DB 'drive with an interleave of (' INTL$ECHO DB '1):',CR,LF,LF,'$' RTF$MSG DB CR,LF DB 'Press to format, ' DB ' to start over, ' DB ' to quit: ','$' RTF$OKC DB CR,0 FMT$START$MSG DB CR,LF,LF,'Formatting . . . ','$' CLR$DIR$MSG: DB CR,'Setting drive data to 0E5H . . . ','$' FMT$OK$MSG DB CR,'Drive formatted -- no errors. ' DB 'Be sure to use FINDBAD on each of the CP/M ' DB 'letters listed above to find any bad sectors ' DB 'in the drive you just formatted.',CR,LF DB 'Example: FINDBAD F:; FINDBAD G:',CR,LF,LF,'$' FMT$ERR$MSG DB 'Drive NOT FORMATTED due to error(s).' DB CR,LF,LF,'$' NO$RESPONSE DB CR,'FORMAT ERROR: your controller ' DB 'is not responding. Possible causes:',CR,LF,LF DB TAB,'Controller set to wrong SCSI address',CR,LF DB TAB,'Controller malfunctioning or not connected',CR,LF DB TAB,'SCSI host adapter malfunctioning',CR,LF DB TAB,'Cables (SCSI to controller or controller ' DB 'to drive) are bad',CR,LF,LF,'$' FORMAT$ERROR DB CR,'FORMAT ERROR: Error code ' ERR$CODE DB 'xx (hex), SCSI command ' ERR$CMD DB 'xx (hex).',CR,LF DB 'SCSI address bit code ' S$ADDR$CODE DB 'xx (hex).',CR,LF DB '(See your hard disk controller ' DB 'manual for details).',CR,LF,LF,'$' AGAIN$MSG DB CR,LF,'Do another (Y/N)? ','$' AGAIN$OKC DB 'NY',CR,0 NEW$CTRL DB CR,LF,'Cannot format -- you have an ' DB 'unknown or unsupported hard disk ' DB 'controller.',CR,LF,LF,'$' OLDSP: DS 2 ; old stack pointer STACK: EQU OLDSP+64 ; 31-level stack INBUF: EQU STACK+1 ; Command line input buffer INBUFL: EQU 0 ; Input buffer length OBPLUG: EQU INBUF+INBUFL ; Start of outbuf ('$') OUTBUF: EQU OBPLUG+1 ; Output buffer OBUFL: EQU PWIDTH ; Output buffer length HEAP: EQU OUTBUF+OBUFL ; Next available area for data E5BUF: EQU HEAP END