* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * HInit.asm Version 1 * * * * Copyright (C) 1984,1985 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.2 EA.22 RJB Changed space algorithm to be based on the ; # of tracks required to avoid allocating an ; extra block, therby overwriting the next ; partition. ; ; 1.1 E8.22 RJB Corrected calculation of lowest available ; byte when Z3ENV is available. ; ; 1.0 E7.02 RJB Hard disk init for PHYTAB, DPH, DPB, and ; drive initialization, if necessary. ; Program version, and current version date VERS EQU 12 ; Current version THIS$MONTH EQU 10 ; Today's month THIS$DAY EQU 22 ; . day THIS$YEAR EQU 85 ; . 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 31 ; 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) LDIR80 EQU 0B0EDH ; LDIR (0edh,0b0h) CPIR80 EQU 0B1EDH ; CPIR (0edh,0b1h) INIR80 EQU 0B2EDH ; INIR (0edh,0b2h) OTIR80 EQU 0B3EDH ; OTIR (0edh,0b3h) SBCD80 EQU 043EDH ; SBCD (0edh,043h) LBCD80 EQU 04BEDH ; LBCD (0edh,04bh) SDED80 EQU 053EDH ; SDED (0edh,053h) LDED80 EQU 05BEDH ; LDED (0edh,05bh) SSPD80 EQU 073EDH ; SSPD (0edh,073h) LSPD80 EQU 07BEDH ; LSPD (0edh,07bh) SIXD80 EQU 022DDH ; SIXD (0ddh,022h) LIXD80 EQU 02ADDH ; LIXD (0ddh,02ah) SIYD80 EQU 022FDH ; SIYD (0fdh,022h) LIYD80 EQU 02AFDH ; LIYD (0fdh,02ah) ; 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 AND 255 DJNZ EQU 010H ; DJNZ addr 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, new pg) CR EQU 'M'-'@' ; Ctrl-M (Carriage return) NAK EQU 'U'-'@' ; Ctrl-U (Negative ack) CAN EQU 'X'-'@' ; Ctrl-X (Cancel) EOF EQU 'Z'-'@' ; Ctrl-Z (CP/M End-of-file) ESC EQU 1BH ; Ctrl-[ (Escape) EXP EQU 21H ; (Exclamation point) 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 ZCPR3$TYPE: DB 1 ; External ZCPR3 ENV ZCPR3$PTR: DW 0FE00H ; Pointer to ZCPR3 ENV NAME$MSG: DB 0DH,'Ampro ' NAME: DB 'Hard Disk System Initialization'; <-- Insert name here DB 00H,' Utility',CR,LF ; (Zero marks end-of-name) 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: HINIT scsiaddr ctrlr lun ' DB '[cyl,head,rwc,wpc, steprate] ' DB 'cpmletter partition [,cpmletter partition] ...' DB CR,LF,LF BIOS$PLUS: DB 'This program requires AMPRO bios version ' DB MIN$VERSION/10+'0','.',MIN$VERSION MOD 10+'0' DB ' or later.',CR,LF,'$' DB CR,' ',CR,EOF SIGNON$MSG: DB 'The Hard Disk System Initialization utility ' DB 'prepares your Ampro 3.1+ bios ' DB 'to access a hard disk unit. The following ' DB 'information is required to initialize the bios ' DB 'for your hard disk unit:',CR,LF,LF DB TAB,'Controller: SCSI address' DB TAB,'* Drive: starting RWC cylinder',CR,LF DB TAB,'Controller: type/model* ' DB TAB,'* Drive: starting WPC cylinder',CR,LF DB TAB,'Drive: logical unit number' DB TAB,'* Drive: step rate code',CR,LF DB TAB,'* Drive: number of cylinders' DB TAB,'CP/M drive letter',CR,LF DB TAB,'* Drive: number of heads' DB TAB,'Size (Kb) of each partition',CR,LF,LF DB '(*) Only required on non-"SCSI generic" ' DB 'Hard disk controllers',CR,LF,LF,LF DB 'NOTE: Your drive''s total formatted ' DB 'capacity in Kbytes is:',CR,LF,TAB DB '(CYLS-1) * HEADS * SECTORS per TRACK * 0.5',CR,LF DB 'where SECTORS per TRACK depends on your controller ' DB 'and is usually 17 or 18.',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, DW SSPD80,STACK ; . then save old stack ptr. ENDIF ; ; Stuff the stack with our pointer and get the bios JMP table. ; LXI SP,STACK ; Set up the stack pointer CALL GET$BIOS$VERS ; Copy the jmp tbl to a local area ; ; Special first-time initialization goes here . . . ; ; Find the lowest Z3 address of the RCP, IOP, FCP, or NDR. The CL, ; ENV, SH, PATH, MSG, FCB, STK, and WHL are all assumed to be either ; in low memory (below 0080H) or above the loadable packages. The ; address found becomes the upper limit to the hard disk allocation ; vectors. If no Z3 environment descriptor is found, the top of ; memory (0FFFFH) is returned as the upper limit to the hard disk ; allocation vectors. LHLD ZCPR3$PTR ; Get pointer to Z3 stuff ... INX H ; Bump to where 'Z3ENV' literal INX H ; . is supposed to be INX H ; . LXI D,Z3$CHECK$V ; Make sure this is a Z3 env MVI B,5 ; . descriptor CHECK$NEXT$Z3: LDAX D ; . CMP M ; . DB JRNZ,NO$Z3-$-1 ; Not equal, no Z3 env INX D ; . INX H ; . DB DJNZ,CHECK$NEXT$Z3-$-1 and 255 LXI B,4 ; Bump HL to RCP DAD B ; . LXI D,0FD00H ; Check the next four Z3 elements MVI B,4 ; . (RCP, IOP, FCP, and NDR). keep NEXT$PACKAGE: ; . the smallest non-zero value as PUSH H ; . the upper limit to the hard CALL GET$HL$PTR ; . disk allocation vectors. We MOV A,L ; . assume that we are using at CMP H ; . least 0FD00H and up, as that DB JRZ,SKIP0-$-1 ; . is the standard Ampro Z3 env. CALL CMP$HL$DE ; . DB JRNC,SKIP0-$-1 ; . XCHG ; . SKIP0: POP H ; . INX H ; . INX H ; . INX H ; . DB DJNZ,NEXT$PACKAGE-$-1 and 255 XCHG ; Save lowest byte found DB JR,SAVE$Z3-$-1 ; . Z3$CHECK$V: DB 'Z3ENV' ; Z3 environment check vector NO$Z3: LXI H,0FFFFH ; Indicate the last available byte SAVE$Z3: ; . is the top of memory, and fall SHLD LAST$BYTE ; . throught to check bios version. ; ; Get the base of ALV storage ; CALL LB$HD$INFO ; Get bottom of ALV storage CALL GET$HL$PTR ; . SHLD HDC$ALV ; . ; ; Compute the DPH base ; LHLD 1 ; Get bios base from wboot vector MVI L,80H ; Assume DPH starts at BIOS+80H SHLD DPH$BASE ; Save it. ; ; 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 DB JRNC,MINBIOS-$-1; At least minimum version . . . LXI D,BIOS$PLUS ; Not minimum, display error message CALL JUSTIFY ; . JMP ALL$DONE ; and exit. ; ; Perform any initialization particular to each version of the Ampro ; bios, if necessary. ; MINBIOS: LDA BIOS$VERSION ; Get current bios version CPI 30 ; Is it version 3? DB JRC,CHECK2-$-1 ; No, check for version 2 ; Bios version 3 init code ... CHECK2: CPI 20 ; Is it version 2? DB JRC,TOPMENU-$-1 ; No, assume version 1 ; Bios version 2 init code ... ; ; This is where you can jump to start the program over. ; TOPMENU: LXI SP,STACK ; Stuff SP with our stack. ; ; 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 DB JRNZ,CLRHD-$-1 ; . 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 ; . CLRHD: LXI D,CLEAR$HDC$MSG ; Clear prior hdc assignments? LXI H,CLEAR$HDC$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . (end if the ESC key pressed). CPI 'Y' ; . CZ CLEAR$HDC ; . AGAIN: ; ; Program main loop . . . ; IF CMD$LINE$OK ; If cmd line input is possible LDA CMD$LINE$CHRS ; Check for any input and skip ORA A ; . the current status messages DB JRNZ,NOSTAT-$-1 ; . if any chrs in the cmd line. ENDIF CALL SHOW$STAT ; Show "current drive" + partitions NOSTAT: LXI D,MAIN$MENU$MSG ; Get main choices LXI H,MAIN$MENU$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . MOV A,B ; . DCR A ; . LXI H,MAIN$MENU$EXE ; . JMP GO$TABLE ; . MAIN$MENU$EXE: DW DEFINE$CDRIVE ; Define "Current Drive" DW DEFINE$PART ; Define parition on "Current Drive" DEFINE$CDRIVE LXI D,SCSI$ADDR$MSG ; Get SCSI address LXI H,SCSI$ADDR$OKC ; . CALL PROMPT ; . JZ ALL$DONE ; . ESC -- finished STA CD$ID ; . (Save ID in msg) MOV A,B ; . DCR A ; . CALL BIN$TO$SCSI ; . STA SCSI$ADDR ; . LXI D,HD$CTRL$MSG ; Get HD controller name LXI H,HD$CTRL$OKC ; . CALL PROMPT ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section STA CD$TYPE ; . (Save type in msg) MOV A,B ; . DCR A ; . STA HD$CTRL ; . LXI D,DRV$LUN$MSG ; Get drive logical unit # LXI H,DRV$LUN$OKC ; . CALL PROMPT ; . JZ DEFINE$CDRIVE ; . ESC -- back up 1 section STA CD$LUN ; . (Save logical unit # in msg) MOV A,B ; . DCR A ; Convert 0-3 to SCSI lun RRC ; . RRC ; . RRC ; . ANI 1110$0000B ; . mask all but bits 7-6-5 STA DRV$LUN ; . XRA A ; Clear step rate value, in case STA STEP$RATE ; . we've got a non-Xebec HDC LDA HD$CTRL ; Check for Xebec HDC CPI NON$GENERIC ; If generic, then no need to DB JRC,DCDONE-$-1 ; . get drive info ... LXI D,DRV$CYLS$MSG ; Get # of cylinders CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section LXI H,DRV$CYLS ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$HEAD$MSG ; Get # of heads CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section STA DRV$HEAD ; . LXI D,DRV$RWC$MSG ; Get reduced write current cylinder CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section LXI H,DRV$RWC ; . MOV M,D ; . INX H ; . MOV M,E ; . LXI D,DRV$WPC$MSG ; Get write precomp cylinder CALL PROMPT$DECIMAL ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section LXI H,DRV$WPC ; . MOV M,D ; . INX H ; . MOV M,E ; . LDA HD$CTRL ; Check for Xebec HDC CPI NON$GENERIC ; . JNZ DTC LXI D,XEB$STEP$MSG ; Get step rate mode value LXI H,XEB$STEP$OKC ; . CALL PROMPT ; . JZ DEFINE$CDRIVE ; . ESC -- top of this section MOV A,B ; . DCR A ; . STA STEP$RATE ; . JMP DCDONE ; . DTC: LXI D,DTC$STEP$MSG LXI H,DTC$STEP$OKC CALL PROMPT JZ DEFINE$CDRIVE MOV A,B DCR A STA STEP$RATE DCDONE: CALL INIT$HDC ; Initialize HDC for this drive ... ; . (ok or error msg returned in DE) LXI H,2 ; Setup initial reserved tracks value SHLD RESERVED ; . MVI A,0FFH ; Mark drive STA CDRIVE$OK ; . CALL CENTER$OUTPUT ; Display either error or ok msg JMP AGAIN ; Go back for more DEFINE$PART: LDA CDRIVE$OK ; Check for drive defined ORA A ; . DB JRNZ,DPOK-$-1 ; Drive defined -- continue LXI D,NO$DRIVE$MSG ; No drive defined -- display message LXI H,NO$DRIVE$OKC ; . and return to main menu. CALL PROMPT ; . JMP AGAIN ; . DPOK: LXI D,CPM$LTR$MSG ; Get CP/M drive letter DB JR,GET$PART-$-1 ; . GET$PART$2: ; . LXI D,CPM$LTR$MSG2 ; . GET$PART: ; . LXI H,CPM$LTR$OKC ; . CALL PROMPT ; . JZ AGAIN ; . ESC -- next physical drive MOV A,B ; . ADI 4 ; . (bump 1-11 to 5-15) STA CPM$LTR ; . ADI 'A' ; Put current CP/M drive letter STA CPM$LTR$ECHO ; . in prompt STA DRIVE$INST ; . and install message GET$PART$AGAIN: LXI D,PARTITION$MSG ; Get partition size (Kb) CALL PROMPT$DEC$NOLF ; . DB JRZ,GET$PART$2-$-1 and 255 ; Go back for more MOV E,A ; . ORA D ; . DB JRZ,GET$PART$AGAIN-$-1 and 255 ; Zero, try again DW SDED80,PARTITION; Save partition value CALL ADD$PARTITION ; Add another drive partition JMP AGAIN ; . ALL$DONE: ; Exit the program ... IF RTN$VIA$WB ; If we're returning via warm boot, JMP LB$WBOOT ; . then JUMP! ENDIF IF NOT RTN$VIA$WB ; Otherwise, DW LSPD80,STACK ; . Get old stack ptr back. RET ; . and return to cp/m ENDIF * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Routines specific to this program . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CLEAR$HDC: CALL LB$HD$INFO ; Get pointer to HD info PUSH H ; Bump to absolute start INX H ; . INX H ; . MOV E,M ; Get absolute start vector INX H ; . MOV D,M ; . POP H ; Plug in current HD info MOV M,E ; . INX H ; . MOV M,D ; . XRA A ; Clear all "driver 3" entries NXTDVR: PUSH PSW ; CALL LB$GET$LOGICAL ; MOV A,M ; CPI 03H ; Driver 3? DB JRNZ,BUMPD-$-1 ; No, skip it MVI M,0 ; Yes, clear it BUMPD POP PSW ; Bump to next unit INR A ; . CPI 16 ; done? DB JRNC,NXTDVR-$-1 and 255 ; No, do another CALL LB$HD$INFO ; Reset pointer to ALV CALL GET$HL$PTR ; . SHLD HDC$ALV ; . LXI D,HDC$CLR$DONE ; Tell 'em we cleared prior assignments CALL CENTER$OUTPUT ; . RET SHOW$STAT: RET DO$SCSI: INX H LDA DRV$LUN ORA M MOV M,A DCX H LDA SCSI$ADDR CALL LB$SCSIDRV ORA A RET INIT$HDC: LDA HD$CTRL ; Init HDC for this drive LXI H,HDC$INIT$EXE ; . JMP GO$TABLE ; . HDC$INIT$EXE: DW NO$INIT$NEEDED ; Generic SCSI HDC (burst mode) DW INIT$SHUGART ; Generic SCSI HDC (byte mode) DW INIT$XEBEC ; Xebec 1410(A) DW INIT$DTC ; Data Technology 500 Series NO$INIT$NEEDED: LXI D,CD$MSG ; Just set message RET INIT$SHUGART: CALL LB$HD$INFO ; Get HD info pointer LXI D,4 ; Bump to byte/block ptr DAD D ; . CALL GET$HL$PTR ; Get pointer in HL MVI M,1 ; Set byte mode LXI D,CD$MSG ; Set A-OK message RET INIT$XEBEC: LXI D,X$ID ; Where to put the fmt data LXI H,DRV$CYLS ; . LXI B,7 ; . DB 0EDH,0B0H ; . (LDIR) LXI H,X$IC ; Init Drive Characteristics LXI D,X$ID ; . CALL DO$SCSI ; . LXI D,CD$MSG ; Init ok -- load A-OK msg RZ ; LXI D,INIT$ERR$MSG ; Init failed -- load error msg RET X$IC: DB 0CH DB 0,0,0,0,0 X$ID: DB 0,0,0,0,0,0,0,11 INIT$DTC: LHLD DRV$CYLS SHLD D$CYL LDA DRV$RWC+1 STA D$RWC LDA DRV$HEAD DCR A STA D$HD LDA STEP$RATE MOV E,A MVI D,0 LXI H,D$TRAN$TBL DAD D MOV A,M STA D$STPR XRA A STA STEP$RATE LXI H,D$MSC LXI D,D$MSD CALL DO$SCSI LXI D,CD$MSG ; Init ok -- load A-OK msg RZ ; LXI D,INIT$ERR$MSG ; Init failed -- load error msg RET D$TRAN$TBL: DB 60 DB 4 DB 3 DB 2 DB 1 D$MSC: DB 0C2H,0,0 DB 0,0,0 D$MSD: DB 11 D$STPR: DB 0 DB 0 D$HD: DB 0 D$CYL: DB 0,0 D$RWC: DB 0,0,0 ADD$PARTITION: LHLD PARTITION ; Get partition size (in K) CALL HL$DIV$2 ; Divide K by 8 to get number CALL HL$DIV$2 ; . of tracks in this partition CALL HL$DIV$2 ; . SHLD NTRACKS ; . DAD H ; Multiply tracks by 2 to get # SHLD NBLOCKS ; . of blocks in this partition LHLD NBLOCKS ; Compute the number of ALV bytes LXI B,7 ; . required for this partition DAD B ; . CALL HL$DIV$2 ; . ( the actual formula is: ) CALL HL$DIV$2 ; . ( ALVB = [NBLOCK + 7] / 8 ) CALL HL$DIV$2 ; . SHLD ALVBYTES ; Allocation vector size (in bytes) DW LBCD80,HDC$ALV ; Get ALV base in BC DAD B ; Add to HL DW LDED80,LAST$BYTE; Compare to last available byte CALL CMP$HL$DE ; . DB JRC,ENOUGH-$-1 ; Enough room? LXI D,NOT$ENOUGH$RM ; No ... tell 'em. CALL JUSTIFY ; . CALL RET$TO$CONT ; . RET ENOUGH: ; ; Update PHYTAB ... ; LDA CPM$LTR ; Get data for this logical drive CALL LB$GETLOGICAL ; . MVI M,03H ; Set Hard Disk driver INX H ; . MOV A,M ; Get DPH offset ANI 0F0H ; . STA DPHOFS ; . (save in order to update DPH) MOV B,A ; Or in step rate code LDA STEP$RATE ; . ANI 00FH ; . ORA B ; . MOV M,A ; Save as DPH/step rate INX H ; . LDA DRV$LUN ; . ORI 0AH ; Set to 512 byte sectors, 4K AU MOV M,A ; . INX H ; . LDA SCSI$ADDR ; Update SCSI address MOV M,A ; . ; ; Update DPH ; LHLD DPH$BASE ; Get DPH base LDA DPHOFS ; Add offset for this unit MOV C,A ; . MVI B,0 ; . DAD B ; . LXI B,0AH ; Add offset to DPB DAD B ; . SHLD HDC$UNIT$DPB ; . INX H ; Bump to CKV INX H ; . DW LDED80,HDC$ALV ; Get allocation vector to DE MOV M,E ; Save in CKV INX H ; . MOV M,D ; . INX H ; Save in ALV MOV M,E ; . INX H ; . MOV M,D ; . INX H ; . LHLD ALVBYTES ; Add ALV to ALVBYTES DAD D ; . SHLD HDC$ALV ; Save updated ALV base XCHG ; . CALL LB$HD$INFO ; Get pointer in BIOS to ALV base MOV M,E ; Save updated ALV base in BIOS INX H ; . MOV M,D ; . ; ; Update HDC DPB ... ; LHLD HDC$UNIT$DPB ; Get ptr to unit DPB ptr back CALL GET$HL$PTR ; Get actual unit DPB ptr in HL PUSH H ; . LXI B,5 ; Bump to disk size DAD B ; . DW LDED80,NBLOCKS ; Save # of blocks (CP/M wants DCX D ; . [blocks - 1] stored here) MOV M,E ; . INX H ; . MOV M,D ; . POP H ; . LXI B,13 ; Bump to reserved tracks DAD B ; . DW LDED80,RESERVED ; Save reserved tracks MOV M,E ; . INX H ; . MOV M,D ; . LHLD NTRACKS ; Update reserved tracks DAD D ; . SHLD RESERVED ; . ; ; Display "successful" message ; XRA A ; Clear carry LHLD HDC$ALV ; Compute space remaining XCHG ; . LHLD LAST$BYTE ; . DB 0EDH,052H ; SBC HL,DE DCX H ; . XCHG ; Convert to decimal LXI H,SPACE$LEFT$DEC; . CALL DE$TO$HL$DEC ; . LXI D,SPACE$LEFT ; . CALL CENTER$OUTPUT ; . RET * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Misc data and messages . . . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CLEAR$HDC$MSG: DB 'Do you want to clear the existing hard ' DB 'disk assignments (Y/N)? ','$' CLEAR$HDC$OKC: DB 'YN',0 HDC$CLR$DONE: DB '*** Previous hard disk assignments have ' DB 'been cleared ***',CR,LF,'$' MAIN$MENU$MSG: DB CR,LF,LF DB 'Options available:',CR,LF DB TAB,' (D) Define the Current Drive',CR,LF DB TAB,' (A) Add a partition to the ' DB 'Current Drive',CR,LF,LF DB TAB,'(ESC) Exit the program',CR,LF,LF DB 'What next (D/A/ESC)? ','$' MAIN$MENU$OKC: DB 'DA',0 SCSI$ADDR$MSG: DB CR,LF,LF,LF,LF,LF DB 'Defining the Current Drive ...' DB CR,LF DB '======================================' DB CR,LF DB LF,LF DB 'The Current Drive is the physical hard ' DB 'disk drive you are currently working ' DB 'with. The definition of the drive ' DB 'consists of the SCSI ID and type of ' DB 'the hard disk controller connected to ' DB 'the drive, ' DB 'the logical unit number (LUN) of the ' DB 'drive, and the drive characteristics if ' DB 'you are using a Xebec 1410(A) controller.' DB CR,LF,LF,'Hard disk controller SCSI ID:' DB CR,LF,'-----------------------------' DB CR,LF,LF DB 'Each hard disk controller must be set ' DB 'to one of the eight SCSI bus ID''s. These ' DB 'ID''s range from zero (0) to seven (7). ' DB 'If you have only one hard disk controller ' DB 'in your system, it''s ID is usually zero ' DB '(0). If you have more than one hard disk ' DB 'controller, make sure they are set to ' DB 'different ID''s.',CR,LF,LF DB 'What is the SCSI ID of the current drive''s ' DB 'controller (0-7, ESC to quit)? ','$' SCSI$ADDR$OKC: DB '01234567',0 HD$CTRL$MSG: DB CR,LF,'Hard disk controller type:' DB CR,LF,'--------------------------' DB CR,LF,LF DB 'Of the following controllers ...',CR,LF,LF DB TAB,'1 - Generic burst-mode SCSI controller ' DB '(Adaptec ACB4000, Xebec Owl)',CR,LF DB TAB,'2 - Generic byte-mode SCSI controller ' DB '(Shugart 1610-4)',CR,LF DB TAB,'3 - Xebec 1410 or 1410A',CR,LF DB TAB,'4 - Data Technology 500 Series',CR,LF DB LF DB 'Which one is the Current Drive connected ' DB 'to (1, 2, 3, or 4)? ','$' HD$CTRL$OKC: DB '12' NON$GENERIC: EQU $-HD$CTRL$OKC DB '34',0 DRV$LUN$MSG: DB CR,LF,'Current Drive logical unit number:' DB CR,LF,'----------------------------------' DB CR,LF,LF DB 'Each disk connected to a hard disk ' DB 'controller has a unique number, called ' DB 'the Logical Unit Number, or LUN. ' DB 'The first LUN on a hard disk ' DB 'controller is zero (0), and the numbers ' DB 'increase at that point to a maximum of ' DB 'seven (7). Most hard disk ' DB 'controllers can support two drives, which ' DB 'means the LUN''s for the controller are ' DB 'either zero (0) or one (1). If only ' DB 'one drive is connected, the logical ' DB 'unit number (LUN) is normally zero (0). ' DB CR,LF,LF DB 'What is the Current Drive''s logical ' DB 'unit number (0-7)? ','$' DRV$LUN$OKC: DB '01234567',0 DRV$CYLS$MSG: DB CR,LF DB 'For the controller you indicated, ' DB 'the following additional information on ' DB 'the Current Drive is required:' DB CR,LF,LF 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? ','$' XEB$STEP$MSG: DB CR,LF DB 'Xebec 1410(A) 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 '0',0FFH,0FFH,0FFH,'4567',0 DTC$STEP$MSG: DB CR,LF DB 'Data Technology 500 Series step mode ' DB '(choose from the ' DB '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 NO$DRIVE$MSG: DB 'Please define the Current Drive before ' DB 'adding a partition to it.',CR,LF DB 'Press the RETURN key to continue. ','$' NO$DRIVE$OKC: DB CR,LF,0 CPM$LTR$MSG: DB CR,LF,LF DB 'Add a CP/M partition ' DB 'on the Current Drive ...',CR,LF DB '========================' DB '========================' DB CR,LF CPM$LTR$MSG2: DB CR,LF DB 'CP/M letter to use for this partition ' DB '(F-P, ESC for new Current Drive)? ','$' CPM$LTR$OKC: DB 'FGHIJKLMNOP',0 PARTITION$MSG: DB CR,LF DB 'Size of the ' CPM$LTR$ECHO: DB 'x: partition (in K bytes)? ','$' NOT$ENOUGH$RM: DB CR,LF,'There is not enough space left to ' DB 'install a hard disk partition of the ' DB 'size you indicated.' DB CR,LF,'$' INIT$ERR$MSG: DB '*** Cannot initialize HDC for the Current ' DB 'Drive -- check you disk parameters. ***' DB CR,LF,'$' CD$MSG: DB CR,LF,'*** Current Drive set to: SCSI ID [' CD$ID: DB 'x], HDC type: [' CD$TYPE: DB 'x], logical unit [' CD$LUN: DB 'x]. ***',CR,'$' SPACE$LEFT: DB CR,LF,'<<< Drive ' DRIVE$INST: DB 'x: installed -- ' SPACE$LEFT$DEC: DB 'xxxxx bytes of bios buffer area ' DB 'remaining. >>>',CR,'$' SCSI$ADDR: DB 0 HD$CTRL: DB 0 DRV$LUN: DB 0 DRV$CYLS: DW 0 DRV$HEAD: DB 0 DRV$RWC: DW 0 DRV$WPC: DW 0 STEP$RATE: DB 0 CPM$LTR: DB 0 PARTITION: DW 0 LAST$BYTE: DW 0 RESERVED: DW 0 HDC$INIT: DB 0 NBLOCKS: DW 0 NTRACKS: DW 0 ALVBYTES: DW 0 DPHOFS: DB 0 HDC$UNIT$DPB: DW 0 HDC$ALV: DW 0 ; HDC CSV & HDC ALV DPH$BASE: DW 0 CDRIVE$OK DB 0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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: ; ; [E6.10] ; ; 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 bit MOV B,A ; . and move to the B register XRA A ; Clear A register STC ; Set carry for shift BIN$NEXT$BIT: RAL ; Shift left one bit DCR B ; Decrement count DB JRNZ,BIN$NEXT$BIT-$-1 and 255 RET ; Z = all done 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 CMP$HL$DE: ; ; [E6.10] ; ; Compare register pair HL with register pair DE. ; ; Entry: ; DE,HL = data to compare ; ; Exit: ; Z: DE = HL, NZ: DE # HL ; C: HL < DE, NC: HL >= DE ; ; Modifies: ; PSW ; MOV A,H ; Compare high bytes CMP D ; . RNZ ; If not zero, flags are set MOV A,L ; Compare low bytes CMP E ; . RET ; Return with flags set 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 DE$TO$HL$DEC: ; ; [E6.25] ; ; Convert the 16-bit number in the DE register pair to a 5-digit ; decimal number. Store this number starting in the memory pointed ; to by the HL register pair. Optionally convert leading zeroes to ; blanks. ; ; Call DE$TO$HL$DEC to convert leading zeroes to blanks ; Call DE$TO$HL$DEC$0 to leave leading zeroes alone ; ; Entry: ; DE = number to convert ; HL = ptr to target memory location ; ; Exit: ; DE,HL unchanged ; ; Modifies: ; PSW ; MVI A,1 ; Set "clear leading 0's" mode DB JR,E$DETOHL-$-1 ; Jump to entry point DE$TO$HL$DEC$0: XRA A ; Set "leave leading 0's alone" mode E$DETOHL: STA DE$TO$HL$MODE ; Save mode flag PUSH B ; Save registers PUSH D ; . PUSH H ; . LXI B,-10000 ; Convert ten-thousands digit CALL TODEC ; . LXI B,-1000 ; Convert thousands digit CALL TODEC ; . LXI B,-100 ; Convert hundreds digit CALL TODEC ; . LXI B,-10 ; Convert tens digit CALL TODEC ; . MOV A,E ; Convert ones digit ADI '0' ; . (Leave zero intact) MOV M,A ; . XRA A ; Clear PSW POP H ; Restore registers POP D ; . POP B ; . RET ; and return ... TODEC: MVI A,'0' ; Start with an ASCII zero PUSH H ; Save target string pointer XCHG ; Move number to convert to HL TODEC1: MOV E,L ; Save a copy of current HL in DE MOV D,H ; . (in case we're done) INR A ; Bump digit DAD B ; Add "negative" BC to HL DB JRC,TODEC1-$-1 and 255 ; Continue while Carry set DCR A ; Get rid of extra bump POP H ; Restore target string pointer MOV M,A ; Save digit CPI '0' ; Is the digit an ascii zero? DB JRNZ,NOZERO-$-1 ; No, Turn off leading 0's flag LDA DE$TO$HL$MODE ; Yes, Check leading 0's flag ORA A ; Convert leading 0's to blanks? DB JRZ,NOBLANK-$-1 ; No, Leave digit alone MVI M,' ' ; Yes, Change digit to a blank DB JR,NOBLANK-$-1 ; All done for now NOZERO: XRA A ; Turn off leading 0's flag STA DE$TO$HL$MODE ; . NOBLANK: INX H ; Bump digit pointer RET ; and return ... DE$TO$HL$MODE: DB 0 ; "Convert 0's to blanks" flag 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 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 HL$DIV$2: PUSH PSW XRA A MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A POP PSW RET 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 PROMPT: ; ; [E6.14] ; ; 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 ; -------------- ------------------------------------ ; (space) Ignored ; , (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: ; PSW, BC ; MVI A,01H ; Set CRLF after chr DB JR,E$PROMPT-$-1 ; Jump to entry point PROMPT$NOLF: MVI A,00H ; Set no CRLF after chr E$PROMPT: STA PROMPT$MODE ; Save prompt mode flag SHLD USER$OKLIST ; . and user 'ok' chr list RE$PROMPT: LDA CMD$LINE$CHRS ; Are there any characters left from ORA A ; . the command line? DB JRZ,P$DISP-$-1 ; No, dsp text & get chr from bios DCR A ; Yes, reduce # of chrs by one STA CMD$LINE$CHRS ; . LHLD CMD$LINE$PTR ; Get command line character MOV A,M ; . INX H ; . SHLD CMD$LINE$PTR ; . CPI ' ' ; Ignore spaces DB JRZ,RE$PROMPT-$-1 and 255 CPI '@' ; @ = repeat cmd line DB JRNZ,P$NO$R-$-1 ; . LXI H,INBUF+2 ; Set command line pointer back SHLD CMD$LINE$PTR ; . to the beginning MVI A,07FH ; Set count of chrs to 127 STA CMD$LINE$CHRS ; . (the most it could be) DB JR,RE$PROMPT-$-1 and 255 P$NO$R: CPI ',' ; Change ',' to CR DB JRNZ,P$NO$C-$-1 ; . MVI A,CR ; . P$NO$C: CPI '.' ; Change '.' to ESC DB JRNZ,P$NO$D-$-1 ; . MVI A,ESC ; . P$NO$D: CPI '_' ; Underscore = prompt anyway DB JRZ,P$DISP-$-1 ; . CALL TO$UPPER ; Convert the chr to upper case LHLD USER$OKLIST ; Get the user ok chr list 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: CALL JUSTIFY ; and call justify routine P$TRY$AGAIN: CALL CONIN$NE$XC ; Console input, no echo, except ^C CALL TO$UPPER ; Convert the chr to upper case LHLD USER$OKLIST ; Get the user ok chr list CALL IS$IT$OK ; Check the chr against the ok list JM P$TRY$AGAIN ; Not there, try again CNZ CON$CHR ; Display chr we found PUSH PSW ; Check mode flag in case we need LDA PROMPT$MODE ; . to send a CR+LF after the DB BIT,BTST+B0+ZA ; . user's input CNZ DO$CRLF ; . POP PSW ; . RET ; and return PROMPT$MODE: DB 0 ; Prompt mode flag USER$OKLIST: DW 0 ; User 'ok' chr list ; end of PROMPT routine 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 STR$TO$A: ; ; [E5.26] ; ; Converts the string pointed to by HL to a number in the A 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 255, inaccurate ; results will occur. ; ; Entry: ; HL = ptr to string ; ; Exit: ; A = value of string in HL ; HL = next character to process ; ; Modifies: ; A, HL ; PUSH B ; We need the BC register XRA A ; Clear totals STA$NEXT$CHR: MOV C,A ; Save results so far . . . MOV A,M ; Get chr CPI '0' ; Less than '0'? DB JRC,NA$DIG-$-1 ; Yes, finished CPI '9'+1 ; Greater than '9' DB JRNC,NA$DIG-$-1 ; Yes, finished SUI '0' ; No, convert to 0-9 MOV B,A ; Save this digit XRA A ; Multiply previous by 10 ADD C ; x1 ADD A ; x2 ADD A ; x4 ADD C ; x5 ADD A ; x10 ADD B ; Add in new digit INX H ; Bump to next chr JMP STA$NEXT$CHR ; Go back for another digit NA$DIG: MOV A,C ; Get totals ORA A ; Set Z/NZ flag POP B ; Get old BC reg back RET ; Return with results in A. 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 (use DS or EQU) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; 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$BIOS$TBL LB$XTBL: ; 'Extra' table definitions ... LB$SWAP$DRV DS 3 ; Swap two logical drives LB$HD$INFO DS 3 ; Get HD pointers 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 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 $-OUTBUF; Output buffer length HEAPPTR: DS 2 ; Pointer to next area HEAPLEN: DS 2 ; Length of next area HEAP: EQU $ ; Start of heap END START