* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 1984,85,86 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.0 E7.15 RJB Original version ; 1.1 F1.30 RBL Now includes Shugart controller option ; 1.2 F4.04 RBL Now includes Seagate 225N ; Program version, and current version date VERS EQU 12 ; Current version THIS$MONTH EQU 4 ; Today's month THIS$DAY EQU 04 ; . 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 20 ; Minimum bios version allowed CMD$LINE$OK EQU TRUE ; 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) 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 'PARK' ; <-- Insert name here NAMELN: EQU $-NAME 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 'To park a hard disk drive:',CR,LF,LF DB TAB,'PARK scsiaddr logicalunit controller [blocknumber],.' DB CR,LF,LF DB 'Where ''controller'' is one of the following:',CR,LF,LF DB TAB DB 'G - Generic SCSI Controller ',CR,LF DB ' (Adaptec, Seagate 225N, Shugart 1610-4, etc.) ',CR,LF DB 'O - Xebec Owl ' DB '# - all other controllers ',CR,LF,LF DB '$',CR,' ',CR,EOF SIGNON$MSG: DB 'The PARK utility is used to move the heads of a ' DB 'hard disk drive to the landing or shipping zone. ' DB CR,LF,LF DB 'NOTE: This program will NOT return to CP/M when ' DB 'you terminate the parking session with an escape or ' DB 'control-c.' DB CR,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: LDA BIOS$VERSION ; Get current bios version CPI 30 ; Is it version 3? DB JRC,CHECK2-$-1 ; No, check for version 2 ; Put version 3 init code here ... CHECK2: CPI 20 ; Is it version 2? DB JRC,AGAIN-$-1 ; No, assume version 1 ; Put version 2 init code here ... AGAIN: ; ; Program main loop . . . ; LXI H,0 ; Set block to zero SHLD SCSI$BLOCK ; . SHLD SCSI$BLOCK+2 ; . LXI D,S$ADDR$MSG ; Get scsi address LXI H,S$ADDR$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . STA RESULT$ID ; Plug id in messages STA NO$UNITS$ID ; . MOV A,B ; . DCR A ; . ANI 07H ; Convert binary to SCSI ID INR A ; . MOV B,A ; . XRA A ; . STC ; . NEXT$BIT: RAL ; . DCR B ; . JNZ NEXT$BIT ; . STA SCSI$ADDR ; . LXI D,L$UNIT$MSG ; Get logical unit # LXI H,L$UNIT$OKC ; . CALL PROMPT ; . JZ AGAIN ; . STA RESULT$LUN ; . STA NO$UNITS$UNIT ; . MOV A,B ; . DCR A ; Convert unit # to 0-3 RRC ; Rotate to bits 7,6,5 RRC ; . RRC ; . STA DRV$LUN ; . LXI D,HDC$MSG ; Get controller LXI H,HDC$OKC ; . CALL PROMPT ; . JZ AGAIN ; . MOV A,B ; . DCR A ; Check for block number (#) STA CONTROLLER ; . (save controller anyway) JZ GET$BLOCK ; . LXI D,RET$TO$PARK ; Accept on other LXI H,RET$TO$PARK$C ; . controllers CALL PROMPT ; . JMP SKIP$BLOCK ; . GET$BLOCK: LXI D,BLOCK$MSG ; Get block to park on CALL PROMPT$DECIMAL ; . JZ AGAIN ; . LXI H,SCSI$BLOCK ; Save block in buffer area MOV M,B ; . INX H ; . MOV M,C ; . INX H ; . MOV M,D ; . INX H ; . MOV M,E ; . SKIP$BLOCK: CALL PARK ; Perform park JMP AGAIN ; Go back for more ... ALL$DONE: ; Don't return to CP/M LXI D,ALL$PARKED ; Give 'all parked message' CALL CENTER$OUTPUT ; . LOONY$TUNES: JMP LOONY$TUNES ; Th-th-th-that's all, folks! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * General purpose routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DO$SCSI: INX H LDA DRV$LUN ORA M MOV M,A DCX H LDA SCSI$ADDR CALL LB$SCSIDRV ORA A RET PARK: MVI A,0FFH ; Set "No response" flag STA DRV$RESPONSE ; . LXI H,HDC$COMMAND ; Clear HDC command bytes LXI D,HDC$COMMAND+1 ; . LXI B,9 ; . MVI M,0 ; . DW LDIR80 ; . LDA CONTROLLER ; Init HDC for this drive LXI H,PARK$EXE ; . JMP GO$TABLE ; . PARK$EXE: DW PARK$AT$BLOCK ; Park at block # DW PARK$OWL ; Xebec Owl DW PARK$GENERIC ; PARK Generic SCSI drive DW PARK$GENERIC ; PARK Generic SCSI drive (Old Adaptec letter) DW PARK$GENERIC ; PARK Generic SCSI drive (Old Shugart letter) PARK$AT$BLOCK: LXI H,SCSI$BLOCK+1 ; Move block number into cmd LXI D,HDC$COMMAND+1 ; . LXI B,3 ; . DW LDIR80 ; . MVI A,0BH ; Set seek command JMP PERFORM$CMD ; Do it PARK$GENERIC: MVI A,1BH ; Set park command JMP PERFORM$CMD ; Do it PARK$OWL: MVI A,011H ; Set seek command CALL PERFORM$CMD ; Do it LXI H,BLINK$LED ; Set blink LED command JMP DO$SCSI ; . BLINK$LED: DB 0EDH,0,0,0,0,0 ; Blink LED SCSI cmd PERFORM$CMD: LXI H,HDC$COMMAND ; Point to park command MOV M,A ; . (plug cmd) LXI D,HEAP ; Set DE to dummy buffer CALL DO$SCSI ; Execute park command DB JRNZ,NEXT$UNIT-$-1 LXI D,PARK$RESULT ; Display result message CALL CENTER$OUTPUT ; . XRA A ; Indicate that a unit responded STA DRV$RESPONSE ; . NEXT$UNIT: LDA DRV$RESPONSE ; See if any units responded ORA A ; . LXI D,NO$UNITS ; . CNZ CENTER$OUTPUT ; . RET HDC$COMMAND: DB 0,0,0,0,0,0,0,0,0,0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Various messages . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * S$ADDR$MSG: DB CR,LF DB 'What is the SCSI address of your controller ' DB '(0-7)? ','$' S$ADDR$OKC: DB '01234567',0 L$UNIT$MSG: DB CR,LF DB 'What is the logical unit number of the drive ' DB 'you wish to park (0-3)? ','$' L$UNIT$OKC: DB '0123',0 ; Logical unit numbers HDC$MSG: DB CR,LF DB 'Of the following list of controllers:',CR,LF DB TAB,'G - Generic SCSI controller',CR,LF DB TAB,' (Adaptec, Seagate 225N, Shugart 1610-4, etc.)',CR,LF DB TAB,'O - Xebec Owl',CR,LF DB TAB,'# - Any other hard disk controller',CR,LF DB 'Which controller are you using (A,O,S, or #)? ','$' HDC$OKC: DB '#OGAS',0 BLOCK$MSG: DB CR,LF DB 'Park the drive at which block address? ','$' RET$TO$PARK: DB CR,LF DB 'Press the key to park this drive: ','$' RET$TO$PARK$C: DB CR,'0' SCSI$BLOCK: DW 0,0 UNIT$ID: DB 0 CONTROLLER: DB 0 DRV$RESPONSE: DB 0 PARK$RESULT: DB '>>> SCSI ID ' RESULT$ID: DB 'x, Logical unit ' RESULT$LUN: DB 'x parked. <<<',CR,LF,'$' NO$UNITS: DB '>>> SCSI ID ' NO$UNITS$ID: DB 'x, Logical unit ' NO$UNITS$UNIT: DB 'x did not respond. <<<',CR,LF,'$' ALL$PARKED: DB CR,LF,LF,'>>> You may now turn off ' DB 'your system. <<<',CR,LF,'$' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Library routines . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 DISPLAY$FDEV: ; ; [E2.15] ; ; LXI D,D$FDEV$HDR ; Print header CALL CENTER$OUTPUT ; . LXI H,ACTIVE$FDEV ; Get pointer to active floppies LXI B,0004H ; Set starting and maximum # D$NEXT$FDEV: MOV A,M ; Get unit # ORA A ; If zero, skip to next JZ D$BUMP$PTR ; . PUSH H ; Copy output line to output buffer PUSH B ; . LXI H,D$FDEV$LIN ; . LXI D,OUTBUF ; . LXI B,D$FDEV$HLEN ; . DW LDIR80 ; . POP B ; . POP H ; . PUSH PSW ; update 'current' drive letter ANI 07FH ; . (mask out 'E-disk' bit) STA OUTBUF+D$CURRENT; . POP PSW ; . ANI 080H ; get 'E-disk' bit back JNZ D$EDISK$OK ; 'E-disk' present, leave E: alone PUSH H ; . PUSH B ; . LXI H,D$EBLANK ; . LXI D,OUTBUF+D$EDISK; . LXI B,4 ; . DW LDIR80 ; . POP B ; . POP H ; . D$EDISK$OK: PUSH B ; PUSH H ; MOV A,B ; update floppy device number LXI H,FNAMES ; . LXI B,FNAMES$LEN ; . CALL INDEX$TABLE ; . LXI D,OUTBUF+D$FNAME; . LXI B,FNAMES$LEN ; . DW LDIR80 ; . POP H ; . POP B ; . LXI D,OUTBUF ; and output the line CALL CENTER$OUTPUT ; . D$BUMP$PTR: INX H ; Bump pointer to next device, INR B ; increment letter count, MOV A,B ; Compare to maximum, CMP C ; . JNZ D$NEXT$FDEV ; Do another if we're not done. RET D$FDEV$HDR: DB '- FLOPPY DISK ASSIGNMENTS -',CR,LF DB 'CP/M drive ' DB 'Floppy disk',CR,LF DB '------------------------',CR,LF,'$' D$FDEV$LIN: DB ' ' D$CURRENT: EQU $-D$FDEV$LIN DB 'x ' D$EDISK: EQU $-D$FDEV$LIN DB '(E)' D$EBLANK: DB ' ' D$FNAME: EQU $-D$FDEV$LIN DB ' ' DB CR,LF,'$' D$FDEV$HLEN EQU $-D$FDEV$LIN ; Line length FNAMES: DB 'First ' DB 'Second' DB 'Third ' DB 'Fourth' FNAMES$LEN: EQU 6 ; end of DISPLAY$FDEV DISPLAY$SDEV: ; ; [E2.15] ; ; Display the hard disk data in the active device table. ; ; Entry ; Active devices in the ACTIVE$SDEV area ; ; Exit ; Devices are displayed on the console ; ; Modifies ; All ; LDA TOTAL$ACTIVE ; Check how many devices ORA A ; . RZ ; Return if no devices active LXI D,FD$HEADER ; Display table header CALL CENTER$OUTPUT ; . LXI D,LAST$SCSI ; Init local data LXI B,04FFH ; . (fill last info with 0FFh's) CALL FILL$BLOCK ; . XRA A ; Current logical D$GET$UNIT: STA LOGICAL$UNIT ; save current unit # CALL INDEX$ACTIVE ; HL = address of active table entry LXI D,LAST$SCSI ; See if same device MVI B,4 ; Compare for 4 bytes CALL STR$COMP ; . JZ D$ADD$LETTER ; Same device -- add CP/M letter LDAX D ; Not same device, check to see ORA A ; . if last device was 'FF'. If so, JM D$DONT$DISP ; . don't display the line. CALL D$SEND$LINE ; Send line to console D$DONT$DISP: LXI B,4 ; Since DE & HL still point to last & DW LDIR80 ; . current, move current to last. PUSH H ; save ptr LXI H,DRIVE$INFO ; Copy template to output buffer LXI D,OUTBUF ; . LXI B,DRIVE$LEN ; . DW LDIR80 ; . LXI H,OUTBUF+DRIVE$LETTERS ; Setup CP/M letter ptr SHLD CPM$PTR ; . POP H ; Restore ptr to current PUSH H ; . and save it back MOV A,M ; Plug output buffer w/device info CALL SCSI$TO$BIN ; SCSI address ADI '0' ; . (convert to ascii 0-7) STA OUTBUF+DRIVE$ADDRESS ; . INX H ; . MOV A,M ; Controller type PUSH H ; . LXI H,CT$NAME ; . LXI B,CT$NLEN ; . CALL INDEX$TABLE ; . LXI D,OUTBUF+DRIVE$CTRL ; . DW LDIR80 ; . POP H ; . INX H ; . INX H MOV A,M ; Logical unit # RLC ; . (move bits 7-5 to 2-0) RLC ; . RLC ; . ANI 07H ; . (mask out other bits) ADI '0' ; . (convert to ascii 0-7) STA OUTBUF+DRIVE$UNIT ; . POP H ; Restore ptr so we agree D$ADD$LETTER: INX H ; Get CP/M letter for this device INX H ; . INX H ; . INX H ; . INX H ; . MOV B,M ; . LHLD CPM$PTR ; Get ptr to CP/M letter area MOV M,B ; Save CP/M drive letter INX H ; Bump pointer INX H ; . SHLD CPM$PTR ; Save CP/M letter area pointer back D$NEXT$UNIT: LDA TOTAL$ACTIVE ; Get count of active units MOV B,A ; Save for a moment LDA LOGICAL$UNIT ; Get unit we're working on INR A ; Bump to next CMP B ; Are we done? JNZ D$GET$UNIT ; No -- go do another CALL D$SEND$LINE ; Send this line out, too. CALL DO$CRLF ; Send out an extra CR+LF. RET ; all done D$SEND$LINE: PUSH PSW ; Save A & flags and PUSH D ; . D just in case LXI D,OUTBUF ; Send line to console CALL CENTER$OUTPUT ; . D$SEND$DONE: POP D ; POP PSW RET LOGICAL$UNIT: DB 0 ; Current logical unit LAST$SCSI: DB 0 ; Last hard disk scsi address LAST$CTRL: DB 0 ; Last hard disk controller LAST$DTYPE: DB 0 ; Last hard disk drive type LAST$DRIVE: DB 0 ; Last hard disk drive CPM$PTR: DW 0 ; Pointer to next CP/M letter pos CT$NAME: DB 'Xebec ' DB 'Adaptec ' ; DB 'Data Technology' ; Supported later ; DB 'Western Digital' ; Supported later DB 'UNKNOWN ' CT$UN: DB 'UNKNOWN ' CT$NLEN: EQU $-CT$UN FD$HEADER: DB '- HARD DISK ASSIGNMENTS -',CR,LF DB 'CP/M drive(s) Controller ' DB 'Addr Unit #',CR,LF DB '-------------------------------------' DB '-------------',CR,LF,'$' DRIVE$INFO: EQU $ DRIVE$LETTERS: EQU $-DRIVE$INFO DB ' ' DRIVE$CTRL: EQU $-DRIVE$INFO DB ' ' DRIVE$ADDRESS: EQU $-DRIVE$INFO DB '0 ' DRIVE$UNIT: EQU $-DRIVE$INFO DB '0' DB CR,LF,'$' DRIVE$LEN EQU $-DRIVE$INFO ; end of DISP$SDEV 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$ACTIVE$DEV: ; ; [E2.15] ; ; Get the active devices. Store the floppy and E-disk descriptions ; in the ACTIVE$FDEV table, and store the SCSI descriptions in the ; ACTIVE$SDEV table. ; ; Entry: ; none ; ; Exit: ; The active device descriptions will be in tables. ; ; ACTIVE$FDEV (4 entries, 1 byte each) format: ; ; +0: Current CP/M letter and "E" disk status. ; (Bit 7 set indicates this device is the "E" disk) ; ; ACTIVE$SDEV (11 entries, 8 bytes each) format: ; ; +0: SCSI Address ; +1: Controller type (0-3) ; +2: Drive type (0-3) ; +3: Drive unit (0-7) -- shifted for use with SCSI commands ; +4: Logical partition (0-7) ; +5: Current CP/M letter ; +6: spare ; +7: spare ; ; Calls: ; LB$GET$LDTE Get logical drive table entry address ; ; Modifies: All ; LXI D,ACTIVE$SDEV ; Clear the table LXI B,ACTIVE$TLEN SHL 8 ; CALL FILL$BLOCK XCHG ; SHLD NEXT$POS ; Save pointer for later XRA A ; Clear count of active SCSI devices STA TOTAL$ACTIVE ; . G$GET$INFO: PUSH PSW ; Save current logical unit # CALL LB$GETLOGICAL ; Get logical drive table entry addr MOV A,M ; If zero, non-active device ORA A ; . JZ G$NEXT$DEV ; . CPI 8 ; Only driver codes 0-7 supported JP G$NEXT$DEV ; . LXI D,OUTBUF ; Clear scratch area LXI B,0800H ; Fill with 00h for length of 8 bytes CALL FILL$BLOCK ; . XCHG ; Save pointer to drive entry LXI H,G$EXE$TBL ; JMP GO$TABLE ; Jump to proper routine G$EXE$TBL: DW G$NEXT$DEV ; Driver code = 00.html DW G$FLOPPY ; Driver code = 01.html DW G$EDISK ; Driver code = 02.html DW G$HARD ; Driver code = 03.html DW G$NEXT$DEV ; Driver code = 04.html DW G$NEXT$DEV ; Driver code = 05.html DW G$NEXT$DEV ; Driver code = 06.html DW G$NEXT$DEV ; Driver code = 07.html G$F$DRIVE: ; Convert floppy drive to ptr INX H ; Get byte MOV A,M ; Floppy drive byte ANI 03h ; Isolate drive bits LXI H,ACTIVE$FDEV ; Compute table addr MOV C,A ; . MVI B,0 ; . DAD B ; . RET ; and return G$FLOPPY: XCHG ; Get pointer to drive entry back CALL G$F$DRIVE ; Convert floppy drive POP PSW ; Get logical unit PUSH PSW ; . ADI 'A' ; Convert 0-15 to A-P ORA M ; Or in E-disk, if present MOV M,A ; Save new disk letter JMP G$NEXT$DEV ; Get next device info G$EDISK: XCHG ; Get pointer to drive entry back CALL G$F$DRIVE ; Convert floppy drive MOV A,M ; Set high bit for E disk ORI 80h ; . MOV M,A ; . JMP G$NEXT$DEV ; Get next device info G$HARD: XCHG ; Get pointer to drive entry back INX H ; (+1) Controller, Drive, Partition MOV A,M ; Get controller (0-3) RRC ; . RRC ; . ANI 03H ; . STA CTRL$TYPE ; . MOV A,M ; Get drive type (0-3) ANI 03H ; . STA DRV$TYPE ; . MOV A,M ; Get partition ANI 0F0H ; . STA PARTITION ; . INX H ; (+2) Unit # MOV A,M ; . ANI 0E0H ; . STA DRV$LUN ; . INX H ; (+3) SCSI address MOV A,M ; . STA SCSI$ADDR ; . POP PSW ; PUSH PSW ; Get CP/M letter ADI 'A' ; . STA CPM$LETTER ; . LHLD NEXT$POS ; Get next buffer position LXI D,SCSI$ADDR ; . and current scratch area XCHG ; Now DE=buffer, HL=scratch LXI B,8 ; Save data DW LDIR80 ; . LXI H,8 ; Update buffer pointer DAD D ; . SHLD NEXT$POS ; . LXI H,TOTAL$ACTIVE ; Bump count of SCSI devices INR M ; . G$NEXT$DEV: POP PSW ; Get logical back INR A ; Bump to next logical CPI 16 ; Do maximum of 16 logicals JNZ G$GET$INFO ; Not done -- go get more RET DS 16-($ MOD 16) ACTIVE$SDEV: DS 12*8 ; SCSI device info ACTIVE$FDEV: DS 4 ; Floppy CP/M letters ACTIVE$TLEN: EQU $-ACTIVE$SDEV ; Active table length TOTAL$ACTIVE: DB 0 ; # of active SCSI devices NEXT$POS: DW 0 ; Next SCSI buffer position SCSI$ADDR: DB 0 ; SCSI Address (0-7) CTRL$TYPE: DB 0 ; Controller type (0-3) DRV$TYPE: DB 0 ; Drive type (0-3) DRV$LUN: DB 0 ; Drive logical unit # (0-7) PARTITION: DB 0 ; Drive partition (0-7) CPM$LETTER: DB 0 ; CP/M letter (0-15) UNIT: DB 0 ; Unit number (1-8) SPARE: DB 0 ; Spare ; end of GET$ACTIVE$DEV routine GET$BIOS$VERS: ; ; [E5.24] ; ; 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 LXI B,LB$LEN ; . (length of bios area) DW LDIR80 ; . (LDIR) MVI A,10 ; 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 LXI B,LB$XLEN ; . (length of extra table) DW LDIR80 ; . (LDIR) MVI A,0FFH ; Set NZ to indicate bios ORA A ; ... version 2.1+ RET ; ... and return. BIOS$VERSION: DB 0 ; Current bios version 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$ACTIVE: ; ; [E2.10] ; ; Compute index into active table ; ; Entry: ; A = Table entry number (00h-0fh) ; ; Exit: ; HL = Address of table entry ; ; Modifies: ; PSW, DE, HL ; LXI B,8 ; Length of active device entry LXI H,ACTIVE$SDEV ; Table base address JMP INDEX$TABLE ; Return through INDEX$TABLE 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: ; ; [E1.30] ; ; 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 E1.30, 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 JMP E$JUSTIFY ; 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 STAX D ; Save in output buffer CPI CR ; CR? CZ J$FLUSH$LINE ; . Yes, flush output line, CZ J$LITERAL ; . . output CR & bump ptr JZ J$NEXT$LINE ; . . and do another. CPI LF ; LF? CZ J$LITERAL ; . Yes, output LF & bump ptr JZ J$CHECK$CHR ; . . and check next chr. CPI '$' ; End of string? CZ J$FLUSH$LINE ; . Yes, flush output line, RZ ; . . and return to caller CPI ' ' ; Blank? JNZ J$NOT$A$BLANK ; . 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$NOT$A$BLANK: INR B ; Increment counter INX H ; . and 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 CM J$ADD$BLANKS ; . right-justify flag is non-zero 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 ' ' ; . JZ J$SKIP$BLANKS ; . JMP J$NEXT$LINE ; and check next segment J$FLUSH$LINE: ; Flush line when CR or EOS encountered PUSH PSW ; Save chr & zero flag MVI A,'$' ; Plug current position with EOS ($) STAX D ; . CALL J$SEND$BUFFER ; Send this line of data POP PSW ; Restore chr & zero flag RET ; and return J$LITERAL: ; Send the chr in A PUSH PSW ; Save zero flag CALL CON$CHR ; Console chr out through CP/M INX H ; Bump chr pointer POP PSW ; Restore zero flag RET 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 PROMPT: ; ; [E2.19] ; ; 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 ; ; When the command line is used for input, the following characters ; are translated to new values or new functions: ; ; Character New character or new function ; -------------- ------------------------------------ ; , (comma) key ; . (period) key ; @ (at-sign) repeat existing command line ; _ (underscore) Prompt and get character 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 RE$PROMPT: 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 ' ' ; Ignore blanks JZ RE$PROMPT ; . CPI '@' ; Repeat existing cmd line? JNZ P$NOT$REPEAT ; . (check other chrs if not) PUSH H ; . Save oklist pointer LXI H,INBUF+2 ; . Set command line pointer back SHLD CMD$LINE$PTR ; . . to the beginning POP H ; . Restore oklist pointer MVI A,07FH ; . Set the count of chars to 127 STA CMD$LINE$CHRS ; . . (the most it could be) JMP RE$PROMPT ; . and get the next character P$NOT$REPEAT: 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 10 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: ; BC,DE = Value entered by the user, 0-0FFFFFFFFH ; ; 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 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 10 ; . JC ADD$DIGIT ; 10 digits or less, add to string LXI D,BLOT ; More than 10 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$BCDE ; Convert string to BC & DE regs XRA A ; Insure NZ flag DCR A ; . PUSH PSW ; Check mode flag to see if a LDA PROMPT$D$MODE ; . CR+LF should be sent after ANI 01H ; . the user's input CNZ DO$CRLF ; . POP PSW ; . ESCRTN: POP H ; Get original HL back RET ; and return with result in BC & DE 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 10 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 SORT$ACTIVE: ; ; [E2.21] ; ; Sort active device table entries ; ; Entry: ; none ; ; Exit: ; Active device table is sorted. ; ; Modifies: ; All ; LDA TOTAL$ACTIVE ; Get # of items to sort CPI 02 ; Less than two items? RM ; . yes -- no sort necessary S$NEXT$BLOCK: ; . STA SORT$MAX ; Save # to sort LXI B,0001H ; Setup initial compare pointers S$NEXT$ELEM: PUSH B ; Save compare pointers MOV A,C ; Convert pointers to address CALL INDEX$ACTIVE ; . in DE and HL PUSH H ; . MOV A,B ; . CALL INDEX$ACTIVE ; . POP D ; . MVI B,8 ; Compare length = 8 chrs CALL STR$COMP ; Compare string in [DE] to [HL] MVI B,8 ; Swap length = 8 chrs CM STR$SWAP ; Swap if first is less than second POP B ; Get pointers back INR B ; Point to next item INR C ; LDA SORT$MAX ; Compare with max CMP C ; . JNZ S$NEXT$ELEM ; Not done with this pass ... DCR A ; Make sort limit one smaller CPI 03H ; Done if only 2 elements JP S$NEXT$BLOCK ; Not done if > 2 elements RET SORTMAX: DW 0 ; Sort max range (0-255 elements) 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$BCDE: ; ; [E7.09] ; ; Converts the string pointed to by HL to a 32-bit number in the ; DE and A registers. 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 4,294,967,295 ; (0FFFFFFFFH), inaccurate results will occur. ; ; Entry: ; HL = ptr to string ; ; Exit: ; BC,DE = value of string (BC = high word, DE = low word) ; HL = next character to process ; ; Modifies: ; A, DE, HL ; ; DW LXIX,0 ; Clear totals LXI D,0 ; . STN$NEXT$CHR: MOV B,A ; Save current high byte MOV A,M ; Get next 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' ; Convert to 0-9 PUSH H ; Save pointer to input string PUSH PSW ; Save converted digit PUSH D ; Setup regs for X10 POP B ; . DW PUSHIX ; . BC = p3, p2 Previous 32-bit POP D ; . DE = p1, p0 number LXI H,0 ; . HL = t3, t2 Current 32-bit DW LXIX,0 ; . IX = t1, t0 total MVI A,10 ; Set up count STN$NEXT$X10: ; Add next digit DB 0DDH,019H ; . (ADD IX,DE) DB 0EDH,04AH ; . (ADC HL,BC) DCR A ; . JNZ STN$NEXT$X10 ; . POP PSW ; Get new digit back MOV E,A ; Setup to add new digit MVI D,0 ; . LXI B,0 ; . DB 0DDH,019H ; Add new digit DB 0EDH,04AH ; . XCHG ; Move total in HL 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: PUSH D ; Move high word to BC POP B ; . DW PUSHIX ; Move low word to DE POP D ; . RET ; Return with results in BC and 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$WINDRV DS 3 ; Set/get win drive parameters LB$PHYTAB DS 3 ; Set/get phytab access LB$GETLOGICAL DS 3 ; Get logical device table entry LB$RESERVED DS 3 ; Reserved entry LB$XLEN EQU $-LB$XTBL ; Length of extra table LB$VERS: DS 1 ; Bios version number DS 64 ; 32-level stack STACK: DS 2 ; Old stack pointer INBUF: DS 128 ; Command line input buffer IBUFL: EQU 128 ; Input buffer length OBPLUG: DS 1 ; Start of outbuf ('$') OUTBUF: DS PWIDTH ; Output buffer OBUFL: EQU PWIDTH ; Output buffer length HEAPPTR: DS 2 ; Pointer to next area HEAPLEN: DS 2 ; Length of next area HEAP: EQU $ ; Start of heap END START