; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * AMPRO Computers, Inc. BIOS Version 3 * ; * * ; * Copyright (C) 1983,1984,1985,1986 * ; * AMPRO Computers, Inc. * ; * All rights reserved. * ; * * ; * This BIOS is designed for use with AMPRO hardware only. * ; * All other use is prohibited without prior written consent * ; * from AMPRO Computers, Inc. * ; * * ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; Floppy-related features: ; ; o Automatic sensing of 4 AMPRO formats ; ; o Automatic 48tpi (double-stepping) support in 96tpi drives. ; WARNING: writes are allowed, but not recommended. ; ; o "E-drive" supports wide variety of non-AMPRO formats ; ; o Console beeps on attempt to read/write a drive without a ; floppy in it, but only on first access since CTRL-C. ; ; o A step rate per physical drive is now supported, and 2ms and ; 3ms step rates are supported with the 1772 FDC. ; ; o 8" data rates implemented on a per logical drive basis. ; NOTE: Most 1770/1772 FDC chips cannot support this rate. ; ; Hard-disk related features: ; ; o Hard disk support is SCSI generic and installed by HINIT ; as a run-time option, or by boot EPROM after use of HGEN. ; ; o Bus (SCSI) arbitration is an assembly-time option. ; ; o Each hard disk partition has its own DPB, which allows ; flexible partitioning of any disk drive, up to 88Mb. ; ; o A direct SCSI call is available as an extended BIOS JMP. ; ; o A SCSI burst/byte option allows some controllers to ; operate at optimum rates. Set by HINIT. ; ; Other features: ; ; o Generic ZCPR3 support. Easily extendable via the AMPRO ; EXTENDED ZCPR3 PACKAGES. ; ; o Console input buffer, via software polling. ; ; o Real time clock, via software polling. ; ; * * * * * * * * ; ; Revision history: ; ; Ver Date Who Description ; --- ----- --- ------------------------------------------ ; 3.8 F4.10 FSW Now include Initiator SCSI ID in select. ; Added SCSI I/O retries. ; Added buffered console and RTC options. ; Added local stack for above two options. ; Fixed problem in SELEND routine. ; Various speed ups and clean ups. ; BEEP is now an option. ; improved WBOOT option for ZRDOS+. ; Now clear PUBLIC bytes for ZRDOS+. ; ; 3.5 - 3.7 Internal unreleased versions ; ; 3.4 EA.30 RJB Moved SCSI mode byte out of buffer area. ; RBL Added bios hooks for ZCPR3 IOP. Added ; floppy control parameters to cold boot ; code. Set "startup" as standard cold boot ; command (AUTOCMD). Modified SCSI routines ; to provide better internal status when in ; a multi-master environment. Changed SCSI ; bus reset to assembly option (new SCSI ; boot EPROM does it -- if BIOS does it ; again, problems can occur.) ; ; 3.3 E8.21 RJB Changed RESTORE logic to wait longer for ; the FDC to setup. Corrected sector number ; problem with Kaypro 4/10 format. Added ; routine to set memory from the end of BIOS ; thru 0FFFFH to zero in order to support ; ZCPR3 resident system segment options (RCP ; FCP, IOP, etc). ; ; 3.2 E7.30 RJB Removed 2 nested "IF" statements in the ; hard disk section which generated extra ; (unused) code in the floppy-only version. ; NO FUNCTIONAL CHANGES. ; ; 3.1 E7.02 RJB Production release of version 3 bios. ; ; 3.0 E5.21 RJB Beta release of version 3 bios. ; ; * * * * * * * * ; Bios version and date VERS EQU 38 ; Current version THIS$MONTH EQU 04 ; Today's month THIS$DAY EQU 11 ; day THIS$YEAR EQU 86 ; year INT$REV EQU 02 ; Internal revision number ; TRUE and FALSE NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Customization equates: ; The following equate determines the amount of system memory ; available for hard disk buffers and other options: MSIZE EQU 58 ; 60K = Floppy only (must set ZCPR3 and/or HARD$DISK to NO) ; 59K = Floppy + 10Mb Hard ; 58K = Floppy + 42Mb Hard ; 57K = Floppy + 74Mb Hard ; 56K = Floppy + 88Mb Hard ZCPR3 EQU YES ; enable ZCPR3? ;The following equate is YES, the BDOS will not be reloaded ;by warm boot. Great with ZRDOS version 1.4 and higher. ONLY$WBOOT$CCP EQU YES ; do not warm boot BDOS ;The following equate should be normally set to NO. MAX$DRIVE EQU NO ; enable max drive checking? HARD$DISK EQU YES ; enable hard disk routines? ARBITRATION EQU NO ; use arbitrated SCSI select? ;The following equate should be set to NO if you have the ;hard disk boot eprom. SCSI$RESET EQU NO ; reset SCSI bus if ID=7? ;Set the BEEP option to NO for systems without consoles. BEEP EQU YES ; beep on non-existent floppies ;When BUFFKBD is YES, a polled keyboard buffer allows type ;ahead, within the limitations of CP/M and CP/M applications. BUFFKBD EQU YES ; keyboard buffer BUFFSIZE EQU 80 ; size of keyboard buffer ;When CLOCK is YES, a polled real time clock is available. ;The LITTLE BOARD must CTC ZC/TC2 connected to CLK/TRG3: ; - On assemblies which have the SCSI option, whether ; populated or not, short JMP2 pins 1 and 2, ; and check that JMP2 pins 2 and 3 are unshorted. NOTE: ; some boards have a trace between JMP2 pins 2 and 3 on ; the bottom of the board, which must be cut. ; - On the original LITTLE BOARD, without the SCSI ; option, the trace to the CTC, U6, pin 20 must be cut. ; then wire U6 pin 20 to U6 pin 9. ; CLOCK EQU YES ; real time clock LOCAL$STACK EQU BUFFKBD OR CLOCK ; if those options are in, ; local stack is recommended. INTERNAL EQU NO ; internal (unreleased) bios? ; Z-80 opcode equates (reversed so we can use a DW to enter them) CPI80 EQU 0A1EDH ; CPI (0edh,0a1h) CPIR80 EQU 0B1EDH ; CPIR (0edh,0b1h) INIR80 EQU 0B2EDH ; INIR (0edh,0b2h) LDIR80 EQU 0B0EDH ; LDIR (0edh,0b0h) OTIR80 EQU 0B3EDH ; OTIR (0edh,0b3h) INI80 EQU 0A2EDH ; INI (0edh,0a2h) OUTI80 EQU 0A3EDH ; OUTI (0edh,0a3h) 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) SRLA EQU 02FCBH ; SRL A ; 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 DJNZ EQU 010H ; DCR, JR NZ addr ; IX and IY prefixes (use DB to enter) IX EQU 0DDH ; IX prefix IY EQU 0FDH ; IY prefix ; CP/M internals BIAS EQU (MSIZE-20)*1024 ; CP/M bias CCP EQU 3400H+BIAS ; CCP starting address BDOS EQU CCP+806H ; BDOS starting address BIOS EQU CCP+1600H ; BIOS starting address IF ONLY$WBOOT$CCP NSECTS EQU (BDOS-CCP)/128 ; warm boot sector count ENDIF ; just reload CCP IF NOT ONLY$WBOOT$CCP NSECTS EQU (BIOS-CCP)/128 ; Warm boot sector count ENDIF ; reload CCP and BDOS ; CP/M externals CDISK EQU 0004H ; Current disk: 0=A,...,15=P IOBYTE EQU 0003H ; Intel I/O byte ; CP/M to host disk constants WRALL EQU 0 ; Write to allocated WRDIR EQU 1 ; Write to directory WRUAL EQU 2 ; Write to unallocated ; Ampro hardware port equates CONT EQU 00H ; Ampro system control port PIO1 EQU 001H ; Parallel printer STBSET EQU 002H ; Set print strobe STBCLR EQU 003H ; Clear print strobe CTCA0 EQU 040H ; Clock/timer channel 0 CTCA1 EQU 050H ; Clock/timer channel 1 CTCA2 EQU 060H ; Clock/timer channel 2 CTCA3 EQU 070H ; Clock/timer channel 3 SIODPA EQU 080H ; Serial port A - data SIOCPA EQU 084H ; Serial port A - control SIODPB EQU 088H ; Serial port B - data SIOCPB EQU 08CH ; Serial port B - control CMND EQU 0C0H ; Disk controller command WTRK EQU CMND+1 ; Disk controller write track WSEC EQU CMND+2 ; Disk controller write sector WDAT EQU CMND+3 ; Disk controller write data STAT EQU 0C4H ; Disk controller status RTRK EQU STAT+1 ; Disk controller read track RSEC EQU STAT+2 ; Disk controller read sector RDAT EQU STAT+3 ; Disk controller read data ; DART masks RDA EQU 01H ; DART recieve data available TBE EQU 04H ; DART transmit buffer empty DCD EQU 08H ; DART data carrier detect CTS EQU 20H ; DART clear to send ; FDC masks, commands, and constants FRESTOR EQU 008H ; FDC restore FSEEKNV EQU 018H ; FDC seek, no verify FSEEK EQU 01CH ; FDC seek, with verify FREADS EQU 088H ; FDC read sector FWRITES EQU 0A8H ; FDC write sector FRDADDR EQU 0C8H ; FDC read address FNS EQU 008H ; 0=spin-up, 1=no spin-up FVF EQU 004H ; 0=no verify, 1=verify FNP EQU 002H ; 0=precomp, 1=no precomp FRETRY EQU 3 ; Number of floppy retries HLDELAY EQU 35 ; Head load delay (ms) DSBIAS EQU 16 ; Double sided sector bias ; Disk type byte definitions: ; Bit: 76543210 ; Density x 0=single 1=double ; Sides x 0=single 1=double ; Sector #'s x 0=same 1=continuous ; Track count x 0=down 1=down front, up back ; Alloc unit xx 00=1K 01=2K 10=4K 11=8K ; Sector size xx 00=128 01=256 10=512 11=1024 ; Bit: 76543210 SSDD48 EQU 10000110B ; DD,SS,same,down,2K,512 DSDD48 EQU 11000110B ; DD,DS,same,down,2K,512 SSDD96 EQU 10000111B ; DD,SS,same,down,2K,1024 DSDD96 EQU 11000111B ; DD,DS,same,down,2K,1024 ; NCR controller equates NCRBASE EQU 20H ; Base address of NCR 5380 NCRCSD EQU NCRBASE+0 ; (R) Current SCSI data register NCRODR EQU NCRBASE+0 ; (W) Output data register NCRICR EQU NCRBASE+1 ; (RW) Initiator command register NCRMR EQU NCRBASE+2 ; (RW) Mode register NCRTCR EQU NCRBASE+3 ; (RW) Target command register NCRCSBS EQU NCRBASE+4 ; (R) Current SCSI bus status NCRSER EQU NCRBASE+4 ; (W) Select enable register NCRBSR EQU NCRBASE+5 ; (R) Bus & status register NCRSDS EQU NCRBASE+5 ; (W) Start DMA send NCRIDR EQU NCRBASE+6 ; (R) Input data register NCRSDTR EQU NCRBASE+6 ; (W) Start DMA target receive NCRRPI EQU NCRBASE+7 ; (R) Reset parity/interrupt NCRSDIR EQU NCRBASE+7 ; (W) Start DMA initiator receive NCRDACK EQU NCRBASE+8 ; (RW) DACK pseudo-DMA register BSYBIT EQU 08H ERROR EQU 02H ; Current SCSI bus status (NCRCSBS) NCRRST EQU 10000000B ; Reset NCRBSY EQU 01000000B ; Busy NCRREQ EQU 00100000B ; Request NCRMSG EQU 00010000B ; Message NCRCD EQU 00001000B ; Control/Data NCRIO EQU 00000100B ; Input/Output NCRSEL EQU 00000010B ; Select NCRDBP EQU 00000001B ; Data bus parity ; Character equates CTRLC EQU 'C'-'@' ; Ctrl-C (Break, abort) BELL EQU 'G'-'@' ; Ctrl-G (Bell) BSP 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) ESC EQU 1BH ; Ctrl-[ (Escape) DEL EQU 7FH ; (Delete) ; Other equates DELSEND EQU 28 DDLSPT EQU 40 ; ZCPR3 equates IF ZCPR3 Z3REV EQU 30 ; ZCPR 3.0 Z3ENV EQU 0FE00H ; Z3 environment descriptor Z3ENVS EQU 2 ; Size in 128-byte blocks RCP EQU 00000H ; Resident command package RCPS EQU 0 ; Size in 128-byte blocks IOP EQU 00000H ; Redirectable I/O package IOPS EQU 0 ; Size in 128-byte blocks FCP EQU 00000H ; Flow command package FCPS EQU 0 ; Size in 128-byte blocks Z3NDIR EQU 00000H ; Named directory area Z3NDIRS EQU 0 ; Size in 18-byte blocks Z3CL EQU 0FF00H ; Z3 command line buffer Z3CLS EQU 200 ; Size in bytes SHSTK EQU 0FD00H ; Shell stack SHSTKS EQU 4 ; Number of shell elements SHSIZE EQU 32 ; Size of a shell entry EXTSTK EQU 0FFD0H ; Z3 external stack EXPATH EQU 40H ; Location of exernal path EXPATHS EQU 5 ; 5 2-byte path elements Z3WHL EQU 4BH ; Wheel byte location Z3MSG EQU 0FD80H ; Z3 message buffers EXTFCB EQU 0FDD0H ; Z3 external FCB ENDIF ; * * * * * * * * ; ; C A V E A T E M P T O R ; ; WARNING: The address offsets from BIOS thru BIOS+017FH must remain ; the same. Any changes in these offsets will cause incompatibility ; with the AMPRO system utilities. ; ; YOU HAVE BEEN WARNED ... ; ; * * * * * * * * ORG BIOS ; Bios starting location JMP BOOT ; Cold start WBOOTE: JMP WBOOT ; Warm start JMP CONST ; Console status JMP CONIN ; Console character in JMP CONOUT ; Console character out JMP LIST ; List character out JMP PUNCH ; Punch character out JMP READER ; Reader character in JMP HOME ; Seek to home position JMP SELDSK ; Select disk JMP SETTRK ; Set track number JMP SETSEC ; Set sector number JMP SETDMA ; Set DMA address JMP READ ; Read disk JMP WRITE ; Write disk JMP LISTST ; Return list status JMP SECTRAN ; Sector translate ; Ampro specific bios calls JMP GETTBL ; Point to more jumps JMP GETEDSK ; Get ptr to E-disk table JMP IOINIT ; Set new I/O parameters IF HARD$DISK JMP SCSI ; SCSI direct driver ENDIF IF NOT HARD$DISK MVI A,0FFH ; Dummy SCSI direct driver RET ENDIF ; * * * * * * * * ; ; Current system disk ; ; * * * * * * * * ORG BIOS+03FH SYSDSK DB 0 ; Assume A: for now ; NOTE: BIOS+040H thru BIOS+07FH modified by CONFIG ; * * * * * * * * ; ; Initialization parameters for the CTC and DART/SIO ; ; These values are set by the CONFIG program, and specify the ; CTC and DART/SIO initialization parameters. Each CTC channel ; requires two bytes of initialization, while each DART/SIO ; channel may have up to 10 bytes of initialization information. ; ; * * * * * * * * ORG BIOS+040H CTCVAL: IF CLOCK DB 47H,13,47H,13 ; ctc0,ctc1 DB 25H,125 ; ctc2 DB 55H,125 ; ctc3 ENDIF IF NOT CLOCK DB 47H,13,47H,13,3,3,3,3 ; CTC0,CTC1,CTC2,CTC3 ENDIF SIOAVAL: DB 4,46H,5,0EAH,3,0C1H,0,0,0,0 ; DART/SIO channel A SIOBVAL: DB 4,46H,5,0EAH,3,0C1H,0,0,0,0 ; DART/SIO channel B ; * * * * * * * * ; ; Maximum drive letter available: ; ; This value is installed by the CONFIG program, and is used by ; the SELECT routine (optionally) to avoid selecting any disk ; units above the maximum letter. This variable was previously ; the NDSKS (# of floppy disks available) parameter in the 1.x ; and 2.x bios. ; ; Note: Although this parameter is set with the CONFIG utility, ; it is ignored unless the MAX$DRIVE parameter is set to YES. ; ; * * * * * * * * ORG BIOS+05CH MAX$DRV$LTR: DB 'P'-'A' ; Allow drives A-P ; * * * * * * * * ; ; Step rates for the four floppy drives. ; ; These values are set by the CONFIG program. ; ; * * * * * * * * ORG BIOS+05DH STPRAT: DB 0,0,0,0 ; Initial rates = 6ms ; * * * * * * * * ; ; Initial IOBYTE value ; ; This value is set by the CONFIG program, and is used as the ; initial value for the IOBYTE (location 0003H) on cold boot. ; ; CP/M defines the following IOBYTE devices: ; ; Bit: 76543210 ; xx Console (0=TTY:, 1=CRT:, 2=BAT:, 3=UC1:) ; xx Reader (0=TTY:, 1=PTR:, 2=UR1:, 3=UR2:) ; xx Punch (0=TTY:, 1=PTP:, 2=UP1:, 3=UP2:) ; xx List (0=TTY:, 1=CRT:, 2=LPT:, 3=UL1:) ; ; In the AMPRO 3.0 bios, the devices are mapped to the serial ; and parallel ports as follows: ; ; CRT: Serial Port A All other devices are undefined. ; TTY: Serial Port B UC1: PTR: UR1: UR2: ; LPT: Parallel port UL1: PTP: UP1: UP2: ; ; * * * * * * * * ORG BIOS+061H IOBYT: DB 81H ; 10 00 00 01 Initial values: ; || || || || ; || || || \= CON: Serial port A (CRT:) ; || || \==== RDR: Serial port B (TTY:) ; || \======= PUN: Serial port B (TTY:) ; \========== LST: Parallel port (LPT:) ; * * * * * * * * ; ; AutoStart command ; ; This value is set by the CONFIG program, and is used as the ; initial command to ZCPR3. This command is ignored in the CP/M ; CCP. ; ; The format of this region is: ; ; Command length -- 1 byte ; Command text -- 8 bytes (max) ; Trailing zero -- 1 byte ; ; * * * * * * * * ORG BIOS+062H AUTOCMD: DB 8,'STARTUP ',0 ; Cmd length + cmd + 0 terminator ; * * * * * * * * ; ; Handshake required flags for DART/SIO channels A and B ; ; These values are set by the CONFIG program, and indicate if ; hardware handshaking is required for each of the two serial ; channels. Bit zero (0) of each flag should be set to a one ; to indicate hardware handshaking. ; ; * * * * * * * * ORG BIOS+06CH HSA: DB 0 ; Handshake flag for channel A HSB: DB 1 ; Handshake flag for channel B DB 0 ; (Reserved) DB 0 ; (Reserved) ; * * * * * * * * ; ; Drive select bytes ; ; These values are modified by the floppy drive select code, the ; warm boot code, and several "E" utilities. The format of each ; select byte is as follows: ; ; Bit: 76543210 ; Speed select x 0=normal, 1=double ; Step select x 0=single, 1=double ; Disk density x 0=double, 1=single ; Side select x 0=Side 0, 1=Side 1 ; Drive unit 4 x . ; Drive unit 3 x . Select drive ; Drive unit 2 x . unit 1,2,3,4 ; Drive unit 1 x . ; Bit: 76543210 ; ; * * * * * * * * ORG BIOS+070H DRIVE$TYPES: DB 01H ; Drive A: DD, Unit 1 DB 02H ; Drive B: DD, Unit 2 DB 04H ; Drive C: DD, Unit 3 DB 08H ; Drive D: DD, Unit 4 DB 02H ; Drive E: DD, Unit 2 DB 0,0,0 ; (Reserved) ; * * * * * * * * ; ; Reserved data area ; ; * * * * * * * * ORG BIOS+078H DW 0,0,0,0 ; (Reserved) ; * * * * * * * * ; ; Disk drive parameter headers ; ; Physical drives A,B,C,D Floppy drives ; ; Physical drive E Special E-disk ; ; Physical drives F,G,H,I ; J,K,L,M ; N,O,P Hard disk drives ; ; * * * * * * * * ORG BIOS+080H DPBASE: DW XLTSS,0,0,0,DIRBUF,SPARM,CSVA,ALVA DW XLTSS,0,0,0,DIRBUF,SPARM,CSVB,ALVB DW XLTSS,0,0,0,DIRBUF,SPARM,CSVC,ALVC DW XLTSS,0,0,0,DIRBUF,SPARM,CSVD,ALVD DW XLTE1,0,0,0,DIRBUF,EPARM,CSVE,ALVE IF HARD$DISK DW 0,0,0,0,DIRBUF,FPARM,CSVF,ALVF DW 0,0,0,0,DIRBUF,GPARM,CSVG,ALVG DW 0,0,0,0,DIRBUF,HPARM,CSVH,ALVH DW 0,0,0,0,DIRBUF,IPARM,CSVI,ALVI DW 0,0,0,0,DIRBUF,JPARM,CSVJ,ALVJ DW 0,0,0,0,DIRBUF,KPARM,CSVK,ALVK DW 0,0,0,0,DIRBUF,LPARM,CSVL,ALVL DW 0,0,0,0,DIRBUF,MPARM,CSVM,ALVM DW 0,0,0,0,DIRBUF,NPARM,CSVN,ALVN DW 0,0,0,0,DIRBUF,OPARM,CSVO,ALVO DW 0,0,0,0,DIRBUF,PPARM,CSVP,ALVP ENDIF IF NOT HARD$DISK DW 0,0,0,0,DIRBUF,SPARM,CSVF,ALVF DW 0,0,0,0,DIRBUF,SPARM,CSVG,ALVG DW 0,0,0,0,DIRBUF,SPARM,CSVH,ALVH DW 0,0,0,0,DIRBUF,SPARM,CSVI,ALVI DW 0,0,0,0,DIRBUF,SPARM,CSVJ,ALVJ DW 0,0,0,0,DIRBUF,SPARM,CSVK,ALVK DW 0,0,0,0,DIRBUF,SPARM,CSVL,ALVL DW 0,0,0,0,DIRBUF,SPARM,CSVM,ALVM DW 0,0,0,0,DIRBUF,SPARM,CSVN,ALVN DW 0,0,0,0,DIRBUF,SPARM,CSVO,ALVO DW 0,0,0,0,DIRBUF,SPARM,CSVP,ALVP ENDIF SPARM: ; Ampro single sided 48tpi DW 40 ; Sectors/track DB 4 ; Block shift DB 15 ; Block mask DB 1 ; Extent mask DW 94 ; Disk size -1 DW 63 ; Directory max DB 128 ; Allocation 0 DB 0 ; Allocation 1 DW 16 ; Check size DW 2 ; Offset DPARM: ; Ampro double sided 48tpi ; -or- single sided 96tpi DW 40 ; Sectors/track DB 4 ; Block shift DB 15 ; Block mask DB 1 ; Extent mask DW 194 ; Disk size -1 DW 127 ; Directory max DB 192 ; Allocation 0 DB 0 ; Allocation 1 DW 32 ; Check size DW 2 ; Offset QPARM: ; Ampro double sided 96tpi DW 40 ; Sectors/track DB 4 ; Block shift DB 15 ; Block mask DB 0 ; Extent mask DW 394 ; Disk size -1 DW 255 ; Directory max DB 240 ; Allocation 0 DB 0 ; Allocation 1 DW 64 ; Check size DW 2 ; Offset XLTSS: ; Single sided format skew table DB 1,2,3,4,5,6,7,8,9,10 XLTDS: ; Double sided format skew table DB 17,18,19,20,21,22,23,24,25,26 ; * * * * * * * * ; ; Special user-defined drive "E-disk" ; ; NOTE: The order of the following table must not be changed, ; since many existing utilities depend on this order. ; ; A call to the BIOS function GETEDISK (BIOS+030H) returns the ; address of EPARM. The type table entry is defined as the re- ; turned value -1, and the translate table value as the returned ; value +15. The translate table has space for 20 entries in ; bios version 1 or 2, 26 entries in bios version 3+. ; ; * * * * * * * * ETYPE DB 82H ; ETYPE must be at EPARM - 1 EPARM: ;Set for: KAYPRO DW 40 ;SEC/TRK KAYPRO IS 40 DB 3 ;BLOCK SHIFT DB 7 ;BLOCK MASK DB 0 ;EXTENT MASK DW 194 ;DISK SIZE -1 KAYPRO IS 194 DW 63 ;DIRECTORY MAX -1 DB 240 ;ALLOC 0 KAYPRO 240 DB 0 ;ALLOC 1 DW 16 ;CHECK SIZE DW 1 ;OFFSET KAYPRO IS 1 EDSD: DB 1 ;DEFAULT DRIVE B: XLTE1: ;TRANSLATE TABLE FOR E DISK DB 0,1,2,3,4,5,6,7,8,9 DW 0,0,0,0,0,0,0,0 ; 26 entries max in XLT1 ; End of E-disk user defined table ; * * * * * * * * ; ; GETTBL GET NEXT JUMP TABLE ADDRESS ; ; While this routine returns the address of NXTTBL, it can also ; be used to identify the BIOS version level. ; ; For pre-2.0 systems, the A register is returned unchanged, ; and the HL register pair returned 0FFFFH. ; ; For 2.0 and higher, HL returneds the address of NXTTBL (never ; equal to 0FFFFH), and A contains the assembly version of this ; bios code. ; ; * * * * * * * * GETTBL: LXI H,NXTTBL ; Get address of jump table MVI A,VERS ; . and version number NXTJMP: ; Label for dummy routines RET NXTTBL: JMP SWAP ; Swap two logical drives JMP HD$INFO ; Get HD table information JMP PHTBAC ; Get/set PhyTab access JMP PAGET ; Get PhyTab entry address IF CLOCK JMP TOD ; return address of clock tick ENDIF JMP NXTJMP ; RESERVED ENTRY ; * * * * * * * * ; ; GETEDSK Get E-disk pointer ; ; * * * * * * * * GETEDSK: LXI H,EPARM RET ; * * * * * * * * ; ; IOINIT ; ; Initialize the CTC and DART comm devices according to the ; information provided at BIOS + 40 hex. ; ; Entry from BOOT and also via the JMP IOINIT from the BIOS jump ; table. ; ; * * * * * * * * IOINIT: LXI H,CTCVAL LXI B,0240H ; Initialize CTC0 DW OTIR80 LXI B,0250H ; Initialize CTC1 DW OTIR80 LXI B,0260H ; Initialize CTC2 DW OTIR80 LXI B,0270H ; Initialize CTC3 DW OTIR80 LXI B,0A84H ; Initialize DART channel A DW OTIR80 LXI B,0A8CH ; Initialize DART channel B DW OTIR80 LDA IOBYT ; Initialize IOBYTE STA IOBYTE RET ; * * * * * * * * ; ; SWAP Swap logical drives ; ; Entry here causes the two LOGICAL drives identified in B and C ; to be swapped in position. This alters the current LOGICAL ; name of the target drive for all subsequent disk operations. ; ; For example, assume the CP/M logical drive A: is the left ; floppy disk drive, and the CP/M logical drive F: is the first ; hard disk partition. If on entry to the SWAP routine the B ; register contained 0 (logical drive A:) and the C register ; contained 5 (logical drive F:), after SWAP, all accesses to ; CP/M drive A: will be to the first hard disk partition, while ; all accesses to CP/M drive F: would be to the left floppy ; drive. ; ; * * * * * WARNING * * * * * ; ; To maintain compatibility with all utilities, and also for ; some special case coding within this BIOS, the user definable ; E-DISK must remain as Drive E logical. ; ; * * * * * * * * SWAP: MOV A,B ; Get first drive logical # CALL PAGET ; Compute current address in table PUSH H ; Save this address MOV A,C ; Get second drive logical # CALL PAGET ; Compute current address in table POP D ; Get pointer to first back in D MVI B,4 ; Set up to move 4 bytes PATMV: MOV C,M ; Get a byte from the first str LDAX D ; Get a byte from the second str MOV M,A ; Swap the bytes MOV A,C ; . STAX D ; . INX D ; Bump pointers INX H ; . DB DJNZ,255-($-PATMV) RET ; No -- all done ; * * * * * * * * ; ; HD$INFO Return HD information. ; ; Returns a pointer to various HD pointers. ; ; * * * * * * * * HD$INFO: LXI H,HD$ALV$AVAIL ; Get pointer to available area RET ; Provide addr if ptr zero HD$ALV$AVAIL: DW HD$CURRENT ; Current BIOS buffer area value DW HD$VECTORS ; Hard disk allocation vector base IF HARD$DISK DW HD$BYTE$BLOCK ; Location of SCSI byte/block flag ENDIF IF NOT HARD$DISK DW 0FFFFH ; "Safe" dummy byte/block location ENDIF DW 0 ; Reserved DW 0 ; Reserved DW 0 ; Reserved ; * * * * * * * * ; ; PHTBAC PHYSICAL TABLE ACCESS ; ; On entry, if the HL register pair is 0000H, the address of the ; Logical vs Physical table is returned in HL, with the length ; of that table in the A register. ; ; If on entry, the HL register pair is non-zero, the current ; table is replaced with the user data starting at the location ; pointed to by HL for the length of the table. ; ; Note: When updating the table, ALL bytes are re-written. No ; length parameter is accepted from the user, and no alternate ; starting address is recognized. ; ; Refer to the PHYTAB for byte definitions... ; ; * * * * * * * * PHTBAC: XCHG ; Save ptr in case we need it MOV A,D ; Test former HL ORA E ; MVI A,PHYEND-PHYTAB ; Get length of table LXI H,PHYTAB ; . and address, too. DB JRNZ,CPY$TBL-$-1; Copy new table if ptr non-zero RET ; Provide addr if ptr zero ; * * * * * * * * ; ; PAGET GET DRIVE LOGICAL INFORMATION ; ; Entry here from within the BIOS, as well as from the NXTTBL ; BIOS Jump entry. ; ; Enter with the Drive Logical address in the A reg. ; ; Exits with the Logical Table entry address in the HL reg pair. ; ; NOTE: Destroys DE and A ; ; * * * * * * * * PAGET: LXI H,PHYTAB ; Get base of physical table ADD A ; Compute offset to the descriptor ADD A ; . MOV E,A ; . MVI D,0 ; . DAD D ; . RET ; Return with ptr in HL CPY$TBL: ; Copy from DE to HL, length A XCHG ; . (exchange DE and HL) MOV C,A ; . (put length info in BC) MVI B,0 ; . (now it's a-ok for LDIR) DW LDIR80 ; . (use LDIR to move table) RET ; and return to caller ; * * * * * * * * ; ; PHYSICAL DRIVER TABLE ; ; This table contains information to identify the Logical vs ; Physical drives requested, and also the disk driver required ; for the particular drive. ; ; The table is built as 16 groups of 4-bytes each, arranged in ; the logical order of drives A thru P for a total of 64 bytes. ; ; The four bytes defining each Logical drive are as follows: ; ; +0 Disk Driver ID number, 0=reserved for error, Max=7 ; ; +1 Physical offset from DPBASE in DPH table, and the drive ; unit number. ; ; Bits 7654: Offset to DPBASE 0 - F ; ; Bits 3210: Physical device address to be passed ; to the respective driver. ; ; For floppies, this is the drive unit #. ; For hard disks, these are reserved. ; ; +2 Type identifier for this drive ; ; Floppy usage: ; ; Bit: 76543210 ; Density x 0=single 1=double ; Sides x 0=single 1=double ; Sector #'s x 0=same 1=continuous ; Track count x 0=down 1=down front, up back ; Alloc unit xx 00=1K 01=2K 10=4K 11=8K ; Sector size xx 00=128 01=256 10=512 11=1024 ; Bit: 76543210 ; ; Hard disk usage: ; ; Bit: 76543210 ; LUN xxx Logical unit number (0-7) ; Reserved x ; Alloc unit xx 00=1K 01=2K 10=4K 11=8K ; Sector size xx 00=128 01=256 10=512 11=1024 ; Bit: 76543210 ; ; Note: Allocation unit and sector size usage are the same ; for floppy and hard disk. ; ; ; +3 00 for floppy or SCSI bus address for hard disk. ; ; For normal SCSI operations, only one bit may be set. ; ; Bit: 76543210 ; SCSI address 0 x This is the actual ; SCSI address 1 x bit pattern supplied ; SCSI address 2 x during the SCSI ; SCSI address 3 x select routine. No ; SCSI address 4 x internal address ; SCSI address 5 x translation or bit ; SCSI address 6 x scaling is done. ; SCSI address 7 x ; ; +4 Duplicated for drives B thru P ; thru ; +63 ; ; Disk Driver Linkage +64 thru +79 ; ; Each disk driver is described by 2 bytes which point to the ; driver to be used, with driver 0 reserved for the SELECT ERROR ; trap for unused or deselected drive letters. ; ; * * * * * * * * PHYTAB: DB 1,000H, SSDD48, 0H ; Floppy drive A DB 1,011H, SSDD48, 0H ; Floppy drive B DB 1,022H, SSDD48, 0H ; Floppy drive C DB 1,033H, SSDD48, 0H ; Floppy drive D DB 2,044H, 0H, 0FFH ; Special floppy drive E DB 0,050H,0,0 ; Hard disk drive F DB 0,060H,0,0 ; Hard disk drive G DB 0,070H,0,0 ; Hard disk drive H DB 0,080H,0,0 ; Hard disk drive I DB 0,090H,0,0 ; Hard disk drive J DB 0,0A0H,0,0 ; Hard disk drive K DB 0,0B0H,0,0 ; Hard disk drive L DB 0,0C0H,0,0 ; Hard disk drive M DB 0,0D0H,0,0 ; Hard disk drive N DB 0,0E0H,0,0 ; Hard disk drive O DB 0,0F0H,0,0 ; Hard disk drive P DRVRADR: ; Driver table DW SELERR ; Driver 0 - select error DW FLOPPY ; Driver 1 - floppy DW DISKE ; Driver 2 - E-disk IF HARD$DISK DW DRVR3 ; Driver 3 - SCSI hard disk ENDIF IF NOT HARD$DISK DW SELERR ; Driver 3 - not defined ENDIF DW SELERR ; Driver 4 - not defined DW SELERR ; Driver 5 - not defined DW SELERR ; Driver 6 - not defined DW SELERR ; Driver 7 - not defined PHYEND EQU $ ; * * * * * * * * ; ; HOME Home the selected disk ; ; Entry via the bios jump table HOME ; ; * * * * * * * * HOME: LXI B,0 ; Set the current track to zero CALL SETTRK ; . LDA HSTWRT ; Check for pending write ORA A ; . DB JRNZ,HOMED-$-1 ; Pending write, leave active alone STA HSTACT ; No writes, clear host active HOMED: RET ; * * * * * * * * ; ; SETTRK Set track (BIOS JMP table entry) ; ; * * * * * * * * SETTRK: ; Set track given by BC DW SBCD80,SEKTRK DW SBCD80,CPMTRK RET ; * * * * * * * * ; ; SETSEC Set sector (BIOS JMP table entry) ; ; * * * * * * * * SETSEC: ; Set sector given by reg C MOV A,C STA SEKSEC STA CPMSEC RET ; * * * * * * * * ; ; SETDMA Set DMA (BIOS JMP table entry) ; ; * * * * * * * * SETDMA: ; Set DMA address given by BC DW SBCD80,DMAADR XRA A ; clear flags for ZRDOS RET ; * * * * * * * * ; ; SECTRAN Sector translation (BIOS JMP table entry) ; ; NOTE: No sector translation is done for the AMPRO disk formats. ; ; * * * * * * * * SECTRAN: ; Translate sector number in BC MOV H,B MOV L,C RET ; * * * * * * * * ; ; SELECT Select disk routine ; ; Entry via the BIOS Jump Table SELDSK ; ; Entry: ; C CP/M Logical drive ; E Bit zero is non-zero if this is the first select of the ; drive since warm or cold boot. ; ; * * * * * * * * SELDSK: MOV A,C ; Get logical disk requested STA LOGDSK ; Save it STA SEKDSK ; . IF MAX$DRIVE ; Is the max drive checking enabled? LDA MAX$DRV$LTR ; . Yes -- Is the letter requested CMP C ; . . greater than the max letter? JC SELERR ; . Yes -- return w/select error ENDIF ; . . IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF MVI A,0FFH ; No to either -- set SELECT cmd ; NOTE: we fall through to PDRVR ; * * * * * * * * ; ; PDRVR Physical driver linkage ; ; Enter with the Logical Disk Drive saved at (LOGDSK) and the ; Physical Command in the A reg as: ; ; FF Select Disk Routine ; 00 Write Disk ; 01 Read Disk ; ; The proper driver is computed and jumped to as needed. ; ; * * * * * * * * PDRVR: PUSH D ; Protect first select status STA PHYCMD ; Save cmd for later LDA LOGDSK ; Get logical drive requested CALL PAGET ; Get table address for this drive SHLD PHYTAG ; Save index to logical descriptor PUSH H ; Save start address LXI D,TAGDRV ; Move the four tag bytes LXI B,4 ; . DW LDIR80 ; (LDIR) POP H ; Restore starting address POP D ; Restore first select status MOV A,M ; Get driver number ORA A ; . If zero, then select error DB JRZ,SELERR-$-1 ; . MOV B,A ; save drive index INX H ; Bump to next position MOV A,M ; Get unit # and DPH offset ANI 0FH ; Mask all but unit # STA PHYDRV ; Save unit # MOV A,M ; Get unit # and DPH offset again ANI 0F0H ; Mask all but DPH offset STA DPHDRV ; Save DPH offset PDRVR1: MOV A,B ; get driver number back ADD A ; . MOV C,A ; Compute ptr within driver table MVI B,0 ; . LXI H,DRVRADR ; . DAD B ; . MOV A,M ; Get ptr to driver in HL INX H ; . MOV H,M ; . MOV L,A ; . LDA PHYCMD ; Get cmd back in the A reg PCHL ; Jump to the proper driver ; * * * * * * * * ; ; SELERR Indicate select error to the BDOS ; ; * * * * * * * * SELERR: LXI H,0 ; Zero indicates select failed RET ; * * * * * * * * ; ; FLOPPY Floppy disk dispatcher -- driver code (01) ; ; Enter with operation code in the A register as: ; ; FF Select Disk ; 00 Write Disk ; 01 Read Disk ; ; * * * * * * * * FLOPPY: INR A ; Code = select? JZ SELFLP ; Yes, perform a floppy select JMP FLOPPYIO ; No, perform host read or write ; * * * * * * * * ; ; DISKE E-disk dispatcher -- driver code (02) ; ; Enter with the operation code in the A register as: ; ; FF Select Disk ; 00 Write Disk ; 01 Read Disk ; ; * * * * * * * * DISKE: PUSH PSW ; Save command PUSH D ; . and first-select status LDA EDSD ; Get E-disk unit MOV E,A ; Get type byte for the drive MVI D,0 ; . we want to be the 'E' LXI H,DRIVE$TYPES ; . disk DAD D ; . MOV A,M ; . ANI 00FH ; Strip all but select bit MOV B,A ; Save select bit LXI H,DRIVE$TYPES+4 ; Point to the E-disk drive byte MOV A,M ; Get the E-disk drive byte ANI 0F0H ; Strip select bits ORA B ; OR in proper select bits MOV M,A ; Save new E-disk drive byte PUSH H ; Save pointer to E-disk drive byte LDA EPARM-1 ; Get type byte LHLD PHYTAG ; Save as PHYTAB type byte INX H ; . INX H ; . MOV M,A ; . STA TAGTYP ; and as current type byte POP H ; Get E-disk drive byte ptr back DB BIT,BRES+B5+ZM ; Indicate double density RLC ; See if E-disk is single density DB JRC,E$IS$DD-$-1 ; No, leave bit alone DB BIT,BSET+B5+ZM ; Yes, indicate single density E$IS$DD: POP D ; Recover first select status POP PSW ; . and command INR A ; Select? JZ SELEDSK ; Yes, perform select JMP FLOPPYIO ; Otherwise use R/W floppy host PUTS: ; Send zero-terminated str to con: MOV A,M ; Get char INX H ; Bump pointer ORA A ; Zero is end-of-string RZ ; Yes -- return MOV C,A ; Otherwise move to C register CALL CONOUT1 ; . and send to console JMP PUTS ; Repeat for remaining bytes ; * * * * * * * * ; ; WBOOT Warm boot system ; ; This routine reloads the CCP and BDOS from the current logical ; disk defined as SYSDSK. While SYSDSK is normally logical drive ; A:, any other drive may be referenced by setting SYSDSK to the ; desired logical drive address (0-15). ; ; The warm boot consists of reading 1600 hex bytes of code, from ; CP/M track 0, sector 1, for 44 CP/M records. The code is loaded ; starting with the CCP address, which is defined at the start of ; this BIOS listing. ; ; The disk currently assigned to SYSDSK must have a standard AMPRO ; system image residing on the boot tracks (tracks 0 and 1). ; ; If SYSDSK is not logical drive A:, the BDOS will still access the ; current logical drive A: after the GOCPM entry in order to build ; the directory allocation vector table for drive A: prior to the ; selection of the current disk. ; ; * * * * * * * * WBOOT: LXI SP,80H ; Set stack to below default DMA LDA SYSDSK ; Get current system disk MOV C,A ; . MVI E,0 ; Set autoselect bit CALL SELDSK ; Select the system disk MOV A,H ; Check for a select error ORA L ; . DB JRZ,WBERR-$-1 ; Select error -- try again LXI B,0 ; Start reading from track 0 CALL SETTRK ; . MVI B,NSECTS ; Set the # of sectors to read MVI C,1 ; Start from sector 1 of track 0 LXI H,CCP ; Where we want the data to go WBLOOP: PUSH B ; Save sector count PUSH H ; and DMA address CALL SETSEC ; Set the sector to read next POP B ; Get the DMA address PUSH B ; Save it back CALL SETDMA ; Set the current DMA CALL READ ; Read the sector ORA A ; Check for errors DB JRNZ,WBERR-$-1 ; Disp msg & try again on error POP H ; Get DMA address POP B ; Get count & current sector LXI D,128 ; Update DMA address DAD D ; . INR C ; Bump sector count LDA CPMSPT ; Get sectors/track for this disk CMP C ; Are we at the end of track yet? DB JRNZ,NXTSEC-$-1 ; No, just get the next sector PUSH B ; Yes, save count & current sector DW LBCD80,CPMTRK ; Bump current track INX B ; . CALL SETTRK ; . POP B ; Restore count MVI C,0 ; Reset sector to 0 NXTSEC: DB DJNZ,255-($-WBLOOP) ; NOTE: WBOOT falls through to GOCPM ; * * * * * * * * ; ; GOCPM Setup CP/M vectors and jump to CP/M ; ; * * * * * * * * GOCPM: LXI SP,80H ; set stack pointer XRA A STA HSTACT ; Clear the host active flag STA HSTWRT ; Clear the host written flag STA UNACNT ; clear unacnt value MVI A,0C3H ; Setup the warm boot vector STA 0 ; . LXI H,WBOOTE ; . SHLD 1 ; . STA 5 ; and the BDOS vector LXI H,BDOS ; . SHLD 6 ; . LXI B,80H ; Setup the default DMA CALL SETDMA ; . EI ; Enable interrupts LDA CDISK ; Get current disk/user MOV C,A ; . JMP CCP ; And go to CP/M ... ; * * * * * * * * ; ; WBERR Display error msg on warm boot error and try again. ; ; * * * * * * * * WBERR: LXI H,BOOTMSG ; Warm boot error -- display CALL PUTS ; . boot failed message and JMP WBOOT ; . try again ... BOOTMSG: DB CR,LF,'BOOT FAILED!',0 ; * * * * * * * * ; ; SELFLP Floppy disk select routine ; ; See if the disk has been accessed since the last warm or cold ; boot (bit 0 of the E register is non-zero). If true, seek to ; track 2 and read the ID to determine if the disk is SS or DS, ; 48tpi or 96tpi, and in a 48tpi or 96tpi drive. Set up the disk ; access tables as appropriate. ; ; * * * * * * * * SELFLP: DB BIT,BTST+B0+ZE ; Check for new mount since boot JNZ SELEND ; Not new, no need to determine type CALL MAPTYPE ; Clear double step bit DB BIT,BRES+B6+ZM ; . CALL GETTYPE ; Set type byte to SS48tpi MVI M,SSDD48 ; . CALL SETUP ; Setup parameters for SSDD CALL RESTORE ; Restore floppy to track 00 JNZ SELERR ; Error on restore, give select err MVI A,2 ; Seek to track 2 STA CPMTRK ; . CALL SEEK ; . LDA IDSAVE+3 ; Get sector size (bytes/sector) DB BIT,BTST+B1+ZA ; Bit 1 non-zero means 512 or 1024 JZ SELERR ; 128, 256 = err for AMPRO fmt ANI 01H ; Reset all but bit 0 ADD A ; *2 ... now 0=48tpi, 2=96tpi MOV C,A ; Save in C reg for the moment LDA IDSAVE+2 ; Get sector # CPI DSBIAS+1 ; Compare to DS bias (16+1) DB JRC,SELSS-$-1 INR C ; Bump pointer if DS SELSS: MVI B,0 ; BC now contains offset to type CALL GETTYPE ; Get ptr to type byte XCHG ; Save ptr in DE LXI H,TYPTBL ; Get type table base DAD B ; Add offset MOV A,M ; Get actual type byte STAX D ; Save in PHYTAB CALL GETDPT ; Get ptr to DPT LXI D,10 ; Bump by 10 to get ptr to DPB DAD D ; . XCHG ; Save ptr in DE LXI H,DPBTBL ; Get DPB ptr base DAD B ; Add offset DAD B ; Add offset again (word ptr) MOV A,M ; Get first byte of DPB ptr STAX D ; Save it INX D ; Bump pointers INX H ; . MOV A,M ; Get second byte of DPB ptr STAX D ; Save it LDA IDSAVE ; Get track number DCR A ; Track=1 means set double step DB JRZ,SET$DSTEP-$-1 DCR A ; Track=2 means normal step DB JRZ,SELEND-$-1 JMP SELERR ; Any other value = select err SET$DSTEP: CALL MAPTYPE ; Map byte for this drive, DB BIT,BSET+B6+ZM ; set the double step bit. ; NOTE: We fall through to SELEND ... ; * * * * * * * * ; ; SELEND Compute proper deblocker variables and DPT addr ; ; Driver types 1, 2, and 3 all re-enter here. ; ; * * * * * * * * SELEND: CALL GETTYPE ; Get current type byte MOV B,A ; Save for a moment ANI 3 ; Mask all but sector shift bits STA SECSHF ; Save sector shift value MOV E,A ; Get sector shift byte from table MVI D,0 ; . LXI H,SEC$MSK$TBL ; . DAD D ; . MOV A,M ; . STA SECMSK ; Save it MOV A,B ; Get type byte back RAR ; Shift over to move alloc size RAR ; . to the proper position ANI 3 ; Mask all but alloc bits MOV E,A ; Get initial unalloc from table ; MVI D,0 ; . already zero from start of routine LXI H,M$UNACT$TBL ; . DAD D ; . MOV A,M ; . STA MUNACT ; Save it ; NOTE: We fall through to GETDPT . . . ; * * * * * * * * ; ; GETDPT Calculate the DPH address for the current logical disk ; ; Entry with the DPH vector in DPHDRV, exits with the DPH address in ; the HL register pair, and the current DPB saved at CURDPB (primarily ; for the warm boot routine). ; ; * * * * * * * * GETDPT: LDA DPHDRV ; Get DPH pointer MOV E,A ; . MVI D,0 ; . **** LXI H,DPBASE ; . DAD D ; . XCHG ; save it (need to return it in 'hl') MVI L,10 ; add offset to dpb 'h'=zero from **** DAD D ; . MOV A,M ; Copy DPB ptr to HL INX H ; . MOV H,M ; . MOV L,A ; . SHLD CURDPB ; Save current DPB pointer MOV A,M ; Get sectors-per-track STA CPMSPT ; Save it XCHG ; get dph pointer RET ; Leave with DPH pointer in HL TYPTBL: ; Drive type bytes DB SSDD48 DB DSDD48 DB SSDD96 DB DSDD96 DPBTBL: ; DBP pointers DW SPARM DW DPARM DW DPARM DW QPARM SEC$MSK$TBL: ; Sector mask table DB 00H DB 01H DB 03H DB 07H M$UNACT$TBL: ; Initial unalloc count table DB 8 DB 16 DB 32 DB 64 ; * * * * * * * * ; ; SELECT THE E-DISK DRIVE ; ; Builds the appropriate tables, and attempts to re-zero the ; drive. Returns with Select Error if can't re-zero. ; ; * * * * * * * * SELEDSK: DB BIT,BTST+B0+ZE ; Check for new mount since boot JNZ SELEND ; Not new, no need to determine type CALL GETTYPE ; Get type byte & pointer DB BIT,BTST+B5+ZA ; Continuous sector numbers? DB JRZ,NOTCONT-$-1 ; No, don't bother computing E-adj ANI 03H ; Yes, compute E-adj from sector DW LBCD80,EPARM-1 ; . size and sectors per track MVI C,0FFH ; . (A=count,B=SPT,C=mask) SELESHIFT: ; . ORA A ; . Test if done & clear carry DB JRZ,SDONE-$-1 ; . Yes, save parameter DB 0CBH,019H ; . RR C (Rotate C right) DB 0CBH,018H ; . RR B (Rotate B right) DCR A ; . Decrement count JMP SELESHIFT ; . Next shift ... NOTCONT: LXI B,0 ; Not continuous, set adj to zero SDONE: MOV A,B ; Shifts done, get computed E-adj ANA C ; Mask extra bits off STA ESECADJ ; Save as proper adj CALL SETUP ; Setup parameters for the E-disk CALL RESTORE ; Restore floppy to track 00 INR A ; Check for error during restore JZ SELERR ; Error code, return with select err JMP SELEND ; No error, finish the select ;--------------------------------------------------------------- ; ; READ CP/M SECTOR FROM THE CURRENTLY SELECTED DISK ; ; ENTRY VIA THE BIOS JUMP TABLE ; ;-------------------------------------------------------------- READ: IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF LDA SECSHF ; secshf = 0 if 128 byte sector size ANA A JZ READHST ; IF 128 BYTES XRA A STA UNACNT INR A ; 'a' = 1 STA READOP ;READ OPERATION STA RSFLAG ;MUST READ DATA MVI A,WRUAL STA WRTYPE ;TREAT AS UNALLOC JMP RWOPER ;TO PERFORM THE READ ;--------------------------------------------------------------- ; ; WRITE CP/M SECTOR FROM THE CURRENTLY SELECTED DISK ; ; ENTRY VIA THE BIOS JUMP TABLE ; ;-------------------------------------------------------------- WRITE: IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save callers stack LXI SP,LOCSTK ; set local stack ENDIF LDA SECSHF ; secshf = 0 if 128 bytes ANA A JZ WRITEHST ; IF 128 BYTES XRA A ;0 TO ACCUMULATOR STA READOP ;NOT A READ OPERATION MOV A,C ;WRITE TYPE IN C STA WRTYPE CPI WRUAL ;WRITE UNALLOCATED? JNZ CHKUNA ; WRITE TO UNALLOCATED, SET PARAMETERS ; FOR EDSK, WE NEED TO CHECK TO SEE IF SEKDSK IS THE E DISK. ; IF SO, THEN UNACNT WILL BE SET ACCORDING TO THE DISK TYPE. ; FOR AMPRO DISKS, ITS ALWAYS BLKSIZ/128. LXI H,MUNACT LXI D,UNACNT LXI B,5 DW LDIR80 CHKUNA: ; CHECK FOR WRITE TO UNALLOCATED SECTOR ; MORE UNALLOCATED RECORDS REMAIN ; DISKS ARE THE SAME ; TRACKS ARE THE SAME XRA A ; clear 'a' LXI H,UNACNT ORA M ; see if any more remain DB JRZ,ALLOC-$-1 ; no more remain DCR M ; unacnt -1 INX H ; 'hl' = unadsk ; get ready for block match ; sekdsk=unadsk, sektrk=unatrk, seksec=unasec ? LXI D,SEKDSK ; LXI B,4 MORUNA: LDAX D DW CPI80 DB JRNZ,ALLOC-$-1 ; no match INX D ; next byte JPE MORUNA ; count not over DCX H ; correct 'hl' to unasec ; MATCH, MOVE TO NEXT SECTOR FOR FUTURE REF INR M ;UNASEC = UNASEC+1 MOV A,M ;END OF TRACK? XCHG ; save 'hl' for overflow LHLD CURDPB ; get current DPB as set by select EMATCH: CMP M ; see if end of track JC NOOVF ;SKIP IF NO OVERFLOW ; OVERFLOW TO NEXT TRACK XRA A STAX D ; unasec 'de' = 0 LHLD UNATRK INX H SHLD UNATRK ; MATCH FOUND, MARK AS UNNECESSARY READ NOOVF: XRA A ;0 TO ACCUMULATOR STA RSFLAG ;RSFLAG = 0 JMP RWOPER+1 ;TO PERFORM THE WRITE ; NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ ALLOC: XRA A ;0 TO ACCUM STA UNACNT ;UNACNT = 0 INR A ;1 TO ACCUM STA RSFLAG ;RSFLAG = 1 ; ENTER HERE TO PERFORM THE READ/WRITE RWOPER: XRA A ;ZERO TO ACCUM STA ERFLAG ;NO ERRORS (YET) IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get keyboard input ENDIF LDA SECSHF MOV B,A LDA SEKSEC ;COMPUTE HOST SECTOR SLOOP: DW SRLA ; shift 'a' right, msb = 0 DB DJNZ,255-($-SLOOP) STA SEKHST ;HOST SECTOR TO SEEK ; ACTIVE HOST SECTOR? LXI H,HSTACT ;HOST ACTIVE FLAG MOV A,M MVI M,1 ;ALWAYS BECOMES 1 ORA A ;WAS IT ALREADY? DB JRZ,FILHST-$-1 ; HOST BUFFER ACTIVE, SAME AS SEEK BUFFER? LXI D,SEKDSK LXI H,HSTDSK LXI B,3 MATMOR: LDAX D ; sekdsk=hstdsk, sektrk=hsttrk ? DW CPI80 DB JRNZ,NOMATCH-$-1 INX D JPE MATMOR LDA SEKHST ; sekhst=hstsec ? CMP M DB JRZ,MATCH-$-1 ; match NOMATCH: ; PROPER DISK, BUT NOT CORRECT SECTOR LDA HSTWRT ;HOST WRITTEN? ORA A LDA HSTDSK ;SELECT HOST AS DISK TO WORK ON STA LOGDSK ;SET LOGICAL DRIVE = BUFFERS DRIVE LHLD HSTTRK SHLD CPMTRK CNZ WRITEHST ;CLEAR HOST BUFF FILHST: LDA SEKDSK ; May have to fill host buffer STA HSTDSK STA LOGDSK ;UPDATE LOGICAL DRIVE LHLD SEKTRK SHLD HSTTRK SHLD CPMTRK LDA SEKHST STA HSTSEC LDA RSFLAG ;NEED TO READ? ORA A CNZ READHST ;YES, IF 1 XRA A ;0 TO ACCUM STA HSTWRT ;NO PENDING WRITE ; ; COPY DATA TO OR FROM BUFFER ; EDSK: SECTOR MASK (SECMSK) MUST BE A VARIABLE FOR EDISK AND ; IS CALCULATED BY (HSTSIZ/128)-1. THIS IS THREE FOR AMPRO, ; KAYPRO, AND OTHER 512-BYTE SECTORS, AND 1 FOR 256 BYTE SECTORS. ; MATCH: LDA SEKSEC ;MASK BUFFER NUMBER LXI H,SECMSK ANA M ;LEAST SIGNIF BITS ADD A ; x2 MOV L,A ; Get offset from table MVI H,0 LXI D,BUFTBL ; Buffer table DAD D ; 'hl' points to offset address MOV A,M INX H MOV H,M MOV L,A ; 'hl' has hostbuffer address DW LDED80,DMAADR ; load 'de' dmaadress LXI B,128 ; set 'bc' up form 128 byte transfer LDA READOP ;WHICH WAY? ORA A DB JRNZ,RWMOVE-$-1 ; WRITE OPERATION, MARK AND SWITCH DIRECTION MVI A,1 STA HSTWRT ;HSTWRT = 1 XCHG ;SOURCE/DEST SWAP RWMOVE: DW LDIR80 ; 'hl' = source, 'de' = destination ; DATA HAS BEEN MOVED TO/FROM HOST BUFFER LDA WRTYPE ;WRITE TYPE CPI WRDIR ;TO DIRECTORY? LDA ERFLAG ;IN CASE OF ERRORS RNZ ;NO FURTHER PROCESSING ; CLEAR HOST BUFFER FOR DIRECTORY WRITE ORA A ;ERRORS? RNZ ;SKIP IF SO XRA A ;0 TO ACCUM STA HSTWRT ;BUFFER WRITTEN LDA HSTDSK STA LOGDSK LHLD HSTTRK SHLD CPMTRK CALL WRITEHST LDA ERFLAG RET WRITEHST: ;LOGDSK = HOST DISK #, CPMTRK = HOST TRACK #, ;HSTSEC = HOST SECT #. WRITE "HSTSIZ" BYTES ;FROM HSTBUF AND RETURN ERROR FLAG IN ERFLAG. ;RETURN ERFLAG NON-ZERO IF ERROR XRA A JMP HOSTDO READHST: ;LOGDSK = HOST DISK #, CPMTRK = HOST TRACK #, ;HSTSEC = HOST SECT #. READ "HSTSIZ" BYTES ;INTO HSTBUF AND RETURN ERROR FLAG IN ERFLAG. MVI A,1 HOSTDO: STA RWHOST HOSTIO: JMP PDRVR ;SELECT NEXT ROUTINE VIA PDRVR ; ; Buffer table, determine start address of host buffer for read/write ; BUFTBL: DW HSTBUF+0000 DW HSTBUF+0128 DW HSTBUF+0256 DW HSTBUF+0384 DW HSTBUF+0512 DW HSTBUF+0640 DW HSTBUF+0768 DW HSTBUF+0896 ; ;-------------------------------------------------------------------- ; ; FLOPPY DISK READ/WRITE HOST ROUTINES ; ;------------------------------------------------------------------- FLOPPYIO: CALL SETUP ;OUTPUT TO DRIVE SELECT REGISTER MVI A,FRETRY STA TRIES WMI: CALL MAPTRK CALL GETTRK ;WHAT TRACK DO WE WANT? CMP M CNZ SEEK ;IF NOT THERE, GO THERE DB JRNZ,RWFAIL-$-1 ;IN CASE SEEK FAILS ; TRACK SHOULD BE OK, BUT WE NEED TO SEND IT ANYWAY IN ; CASE THE LAST UNIT WAS DIFFERENT (THERE'S ONLY ONE TRACK ; REGISTER). THEN LOAD SECTOR AND DO IT... CALL GETTRK OUT WTRK CALL GETSEC ; CHECK FOR DSDD TO ADD SECTOR BIAS IF NOT 'E' DRIVE MOV E,A ; Save sector # for a moment LDA PHYDRV ; See if this is the E-disk CPI 04H ; . (E-disk is drive "4") MVI A,0 ; Restore sector # DB JRZ,NOBIAS-$-1 ; No sector bias if 'E' drive PUSH H PUSH D CALL GETTYPE ; get disk type POP D POP H ANI 0C0H ; Isolate DD, DS indicators CPI 0C0H ; Check DD and DS MVI A,0 ; . DB JRNZ,NOBIAS-$-1 ; Not DSDD, no bias ADI DSBIAS ; Add double sided bias NOBIAS: ADD E ; Add sector # to bias OUT WSEC ; Send sector # to FDC CALL SETUP ; Select unit and real side CALL GETBUF ; MVI A,2 ; Set up internal retry counter STA INT$RETRIES ; . LDA RWHOST ; Test for read (1) or write (0) ORA A ; . JNZ RDS ; . CALL WRDAT ; Write sector DB JRNZ,RWFAIL-$-1 ; Try again if failed RET ; Otherwise return RDS: MVI A,FREADS ; Indicate read sector CALL RDATA ; Read sector DB JRNZ,RWFAIL-$-1 ; Try again if failed RET ; Otherwise return RWFAIL: CALL RESTORE ; Restore to track 00 on failure LXI H,TRIES ; Point to retry counter DCR M ; Bump it closer to doom DB JRNZ,255-($-WMI); and try again if we can . . . MVI A,01 ; No more tries left STA ERFLAG ; Set error flag RET ; and return to BDOS w/error ; * * * * * * * * ; ; WRDAT Floppy disk write data routine ; ; This routine writes the data pointed to by the HL register to ; the currently selected floppy. ; ; The number of bytes written is determined by the FDC controller ; and the ID string currently active for this sector. ; ; Multiple sector transfers are not supported. ; ; NOTE: The fastest way to test for busy and DRQ is to shift the ; status bit right into the carry bit. Therefore, when busy drops, ; the ending status is still shifted right. The checks for lost ; data, write protect, record not found, and crc error must take ; this bit shift into account. ; ; * * * * * * * * WRDAT: SHLD FRWPTR ; Save data ptr in case intr CALL DWAIT ; Wait until the FDC is ready WRDAT2: MVI A,FWRITES ; Set up write sector command LHLD FRWPTR ; . CALL OUTCMD ; Send the command to the FDC WR: IN STAT ; Get FDC status RAR ; Test busy bit DB JRNC,WRDONE-$-1 ; Exit if no busy bit RAR ; Test DRQ bit DB JRNC,255-($-WR) ; Test status again if no DRQ MOV A,M ; Busy & DRQ ==> OK to write data OUT WDAT ; . INX H ; Bump pointer to next byte JMP WR ; Repeat as long as busy is active WRDONE: DB BIT,BTST+B1+ZA ; Test for lost data DB JRNZ,255-($-WRDAT2) ; Lost data -- Try write cmd again WRNLD: ANI 02CH ; Test for WP, RNF, or CRC RZ ; All ok if zero LDA INT$RETRIES ; Get # of internal retries left DCR A ; Bump count one closer to doom! STA INT$RETRIES ; Save count JP WRDAT2 ; Try again if 0, 1, or 2 RET ; Otherwise return with NZ status ; * * * * * * * * ; ; RDATA Floppy disk read data routine ; ; This routine reads the data from the currently selected floppy ; into the area pointed to by the HL register. This data is from ; a read address or read data command. ; ; The number of bytes read is determined by the 1770 controller ; and the ID string currently active for this sector. ; ; Multiple sector transfers are not supported. ; ; NOTE: The fastest way to test for busy and DRQ is to shift the ; status bit right into the carry bit. Therefore, when busy drops, ; the ending status is still shifted right. The checks for lost ; data, record not found, and crc error must take this bit shift ; into account. ; ; * * * * * * * * RDATA: STA FRWCMD ; Save command and data pointer SHLD FRWPTR ; . (in case we're interrupted) CALL DWAIT ; Make sure FDC is ready RDATA2: LDA FRWCMD ; Get command and data pointer LHLD FRWPTR ; . CALL OUTCMD ; Send command to FDC RD: IN STAT ; Get FDC status RAR ; Test busy bit DB JRNC,RDDONE-$-1 ; Exit if no busy bit RAR ; Test DRQ bit DB JRNC,255-($-RD) ; Test status again if no DRQ IN RDAT ; Busy & DRQ ==> OK to read data MOV M,A ; . INX H ; Bump pointer to next byte JMP RD ; Repeat as long as busy is active RDDONE: DB BIT,BTST+B1+ZA ; Test for lost data DB JRNZ,255-($-RDATA2) ; Lost data -- try read again RDNCD: ANI 0CH ; Test for RNF or CRC RZ ; All ok if zero LDA INT$RETRIES ; Get # of internal retries left DCR A ; Bump count one closer to doom! STA INT$RETRIES ; Save count JP RDATA2 ; Try again if 0, 1, or 2 RET ; Otherwise return with NZ status ; * * * * * * * * ; ; OUTCMD Issue a cmd to the 1770 floppy controller ; ; * * * * * * * * OUTCMD: CALL MOTOR ; Insure motor on OC0: OUT CMND ; Send command to FDC MVI A,15 ; Wait 60 us for cmd to set up OC1: DCR A ; . 15 x 4 usec DB JRNZ,255-($-OC1) RET ; * * * * * * * * ; ; MOTOR Start floppy drive motor ; ; This routine starts the target motor by executing a read address ; command. The xTRK, xSEC, and xDAT registers of the 1770 must be ; saved as the read address command alters their contents. After ; the read address command, we have 2 seconds (10 index pulses) to ; execute the next command before the motor shuts off. ; ; * * * * * * * * MOTOR: PUSH PSW ; save command IN STAT ; Check motor on bit RAL ; . DB JRC,MOTOROK-$-1 ; Motor already on ... PUSH H ; Save buffer pointer IN RTRK ; Save 1770 trk, sec, & dta regs MOV B,A ; . IN RSEC ; . MOV C,A ; . IN RDAT ; . MOV D,A ; . CALL DWAIT ; Wait for the 1770 to get ready XRA A ; Setup seek to same track cmd OUT WTRK ; . OUT WDAT ; . MVI A,FSEEKNV ; . (seek, no verify) OUT CMND ; Send the cmd PUSH B ; Save BC reg for wait cmd MVI A,4 ; Wait 4 * 250ms = 1 second WAITMORE: PUSH PSW ; Wait 250ms MVI A,250 ; . CALL WAIT ; . POP PSW ; . DCR A ; More waiting? DB JRNZ,WAITMORE-$-1 and 255 POP B ; Get BC reg back MOV A,D ; Restore 1770 registers OUT WDAT ; . MOV A,C ; . OUT WSEC ; . MOV A,B ; . OUT WTRK ; . POP H ; restore buffer pointer MOTOROK: POP PSW ; Get command back RET ; and return ; * * * * * * * * ; ; DWAIT Wait for permission to write a register ; ; This routine will wait up to 5 seconds for permission to write ; to one of the FDC registers. After 5 seconds, a FORCE INTERRUPT ; command will be issued to the FDC. ; ; * * * * * * * * DWAIT: LXI H,TIMEOUT ; Point to timeout location MVI M,6 ; Set 6 major loops DLOOP: IN STAT ; Get FDC status DB BIT,BTST+B0+ZA ; Test bit 0 (BUSY), return with RZ ; . zero status if busy non-active DCX H ; See if enough minor loops MOV A,H ; . (Approx 58,000 times) ORA L ; . JNZ DLOOP ; Not done with minor loop IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get keyboard entry ENDIF LXI H,TIMEOUT ; Decrement major loop counter DCR M ; . (6 times) JNZ DLOOP ; . MVI A,0D0H ; Give up on waiting; issue a FORCED OUT CMND ; . INTERRUPT to the FDC XRA A ; Set A to 0FFH and status to NZ DCR A ; . RET ; Return to caller ; * * * * * * * * ; ; RESTORE Restore a floppy drive to track 00 ; ; This routine issues a re-zero command to the 1770 which causes ; the drive indicated by LOGDSK to slowly find its way back to ; the track zero (00) sensor. ; ; * * * * * * * * RESTORE: ; Restore the disk head to track 00 CALL DWAIT ; Wait for the 1770 to be ready RNZ ; Return to caller if timeout CALL GETSTEP ; Get the step rate for this drive ORI FRESTOR ; Add restore command to step rate CALL OUTCMD ; Send the cmd to the 1770 CALL DWAIT ; Wait for the cmd to finish MVI A,50 ; Wait 50 ms for the drive to settle CALL WAIT ; . CALL MAPTRK ; Set this drive's last track MVI M,0 ; . to track 00 NEXT$BLIP: MVI A,FRDADDR ; Send the READ ADDRESS cmd to the CALL OUTCMD ; . FDC and see if we get anything CALL DWAIT ; Return if cmd finished (implies IF BEEP RZ ; . both disk & drive present) MVI C,BELL ; Beep if no disk CALL CONOUT1 ; . CALL CONST1 ; Check console status CNZ CONIN1 ; Get char if one is there CPI CTRLC ; Was the char a ctrl-C? DB JRZ,UABORT-$-1 ; Yes, abort with error CPI ESC ; Was the char an ESC? DB JRNZ,255-($-NEXT$BLIP) ; No, try again UABORT: XRA A ; Return with error DCR A ; . ENDIF RET ; . ; * * * * * * * * ; ; SEEK Seek to track (floppy only) ; ; Moves the floppy head to the track indicated by CPMTRK if not ; already there. ; ; * * * * * * * * SEEK: CALL MOTOR ; Make sure the motor is on CALL GETTRK ; Get track we want PUSH PSW ; Save track # CALL SETUP ; Set up unit and real side POP PSW ; Get track # back CALL MAPTYPE ; Check double step bit DB BIT,BTST+B6+ZM ; . DB JRZ,NODS1-$-1 ; Skip double step if bit clear ADD A ; Otherwise multiply by 2 NODS1: OUT WDAT ; Tell the 1770 where we want to go CALL MAPTRK ; Get the current track # MOV A,M ; . CALL MAPTYPE ; Check double step bit DB BIT,BTST+B6+ZM ; . DB JRZ,NODS2-$-1 ; Skip double step if bit clear ADD A ; Otherwise multiply by 2 NODS2: OUT WTRK ; Tell the 1770 where we are CALL GETSTEP ; Get the step rate for this drive ORI FSEEKNV ; OR in seek without verify cmd PUSH PSW ; Save A reg as DWAIT destroys it CALL DWAIT ; Wait until 1770 is ready POP PSW ; Get A reg back (1770 cmd) CALL OUTCMD ; Send the cmd CALL DWAIT ; Wait for the cmd to finish ANI 18H ; Isolate possible failures RNZ ; Seek failed, return error MVI A,FRDADDR ; Read current track info LXI H,IDSAVE ; . CALL RDATA ; . CALL MAPTRK ; Get ptr to track save area CALL GETTRK ; Get track we were seeking MOV B,A ; Save it for a moment IN RSEC ; Get track # from the FDC CMP B ; Compare to what we wanted MOV M,A ; Save FDC track # in either case RET ; Z=ok, NZ=error ; * * * * * * * * ; ; GETSTEP Get the step rate for a particular drive ; ; Sets the HL register pair to the location which contains the ; step rate for the drive indicated by PHYDRV, and returns the ; actual step bits in the A register. ; ; Registers modified: A, PSW, DE, HL ; ; * * * * * * * * GETSTEP: LDA PHYDRV ; Get physical drive unit CPI 04 ; E-disk? DB JRNZ,GET$AD-$-1 ; No, use normal unit # LDA EDSD ; Yes, use E-disk unit # GET$AD: MOV E,A ; Get step rate from table MVI D,0 ; . (based on physical unit) LXI H,STPRAT ; . DAD D ; . MOV A,M ; . ANI 03H ; Mask off junk, just in case RET ; And return data in A and HL ; * * * * * * * * ; ; MAPTYPE Get pointer to physical type byte ; ; Sets the HL register pair to the location which contains the ; type byte for the physical drive indicated by PHYDRV. ; ; The format of the physical type bytes can be found in the ; description of DRIVE$TYPES at BIOS+070H. ; ; Registers modified: DE, HL ; ; * * * * * * * * MAPTYPE: PUSH PSW ; Save entry A reg and flags LDA PHYDRV ; Get physical drive unit MOV E,A ; Compute pointer to physical MVI D,0 ; . type byte LXI H,DRIVE$TYPES ; . DAD D ; . POP PSW ; Get A reg and flags back RET ; And return with ptr in HL ; * * * * * * * * ; ; MAPTRK Return pointer to current track ; ; Sets the HL register pair to the location which contains the ; current track for the drive indicated by PHYDRV. ; ; Registers modified: A, PSW, DE, HL ; ; * * * * * * * * MAPTRK: LDA PHYDRV ; Get physical drive unit CPI 04 ; E-disk? DB JRNZ,MAP$AD-$-1 ; No, use normal unit # LDA EDSD ; Yes, use E-disk unit # MAP$AD: MOV E,A ; Compute ptr to current track MVI D,0 ; . LXI H,LTRACK ; . DAD D ; . RET ; Return with pointer in HL ; * * * * * * * * ; ; SETUP Setup the system control register for a drive select ; ; This routine writes to the system control port register at I/O ; 00H, causing the associated drive to be selected, along with ; the head select signal line. ; ; NOTE: This routine always turns the internal system rom OFF, ; without any regard for those who just may have the rom enabled ; prior to entry. ; ; NOTE: This routine modifies all registers ; ; * * * * * * * * SETUP: CALL MAPTYPE ; Get pointer to drive type byte MOV A,M ; Get byte ORI 040H ; Turn off EPROM LXI H,HSTSID ; Point to side select ORA M ; Include proper side select MOV C,A ; Save control byte ANI 1000$1111B ; Mask out eprom, density, & side LXI H,CHGDSK ; Compare with previous results CMP M ; . MOV M,A ; . (save this result anyway) DB JRZ,SETUP2-$-1 ; Skip split & delay if same MOV A,C ; Get control byte back ANI 0F0H ; Mask out drive bits OUT CONT ; Send speed only to control port MOV A,C ; Get control byte back OUT CONT ; Send full byte to the control port MVI A,HLDELAY ; Delay HLDELAY ms (usually 30ms) CALL WAIT ; . RET SETUP2: MOV A,C ; Get control byte back OUT CONT ; Send to control port RET ; * * * * * * * * ; ; WAIT Wait "A" ms ; ; Modifies: A,PSW (A = 0, Z flag set) ; ; * * * * * * * * WAIT: PUSH B WAIT1: MVI B,199 WAIT2: CMP M DB 10H,WAIT2-$-1 and 255 ; DJNZ WAIT1 IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get keyboard entry ENDIF DCR A DB JRNZ,WAIT1-$-1 and 255 ; JRNZ WAIT2 POP B RET ; * * * * * * * * ; ; GETTYPE Get the type byte and ptr for a drive unit ; ; Returns the current disk type identifier in the A reg, and the ; address of the entry in the HL reg pair. ; ; See PHYTAB (driver table) for the definition of the type byte. ; ; Modifies: A, DE, HL ; ; * * * * * * * * GETTYPE: LDA LOGDSK ; Use logical disk entry value CALL PAGET ; Get ptr to table entry INX H ; Type byte is at table+2 INX H ; . MOV A,M ; Get type byte RET ; Return type in A, ptr in HL GETSEC: ;CONVERT LOGICAL SECTOR TO PHYSICAL SECTOR PUSH H CALL GETTYPE ANI 3 ; 128 BYTE SECTOR? LDA CPMSEC ;NOPE, DO MAPPING DB JRZ,GOTSDS-$-1 LDA HSTSEC GOTSDS: PUSH PSW ;SAVE SECTOR CALL GETDPT ;FETCH POINTER TO XLT TABLE MOV E,M INX H MOV D,M XCHG ;TRANSLATE TABLE NOW IN HL POP PSW ;RESTORE DESIRED SECTOR MOV E,A MVI D,0 DAD D MOV A,M ;GET PHYSICAL SECTOR FROM TABLE MOV E,A ;SAVE IT LDA PHYDRV CPI 04H ;WAS IT E DISK? DB JRNZ,GSEXIT-$-1 ;NO, THEN NO ADJUST LDA HSTSID ;WHICH SIDE? ORA A DB JRZ,GSEXIT-$-1 LDA ESECADJ ;ELSE GET ADJUSTMENT ADD E MOV E,A ;PUT IT BACK FOR MOVE GSEXIT: MOV A,E ;MOVE REAL SECTOR TO A POP H RET GETBUF: CALL GETTYPE ;GET THE DMA ADDRESS ANI 3 ; 128 BYTE SECTORS? LXI H,HSTBUF ;USED FOR DD RNZ LHLD DMAADR ;ELSE USE REAL DMA ADDRESS... RET ; * * * * * * * * ; ; GETTRK Convert logical to physical track ; ; This routine converts the logical track stored in CPMTRK to ; the actual physical track and side to be used. ; ; The physical track is returned in the A register, and the side ; to be used is returned in HSTSID. ; ; * * * * * * * * GETTRK: PUSH H ; Save HL register CALL GETTYPE ; Get the type byte for this disk LXI H,HSTSID ; Point to the side flag RAL ; Determine SS or DS RAL ; . LDA CPMTRK ; Get track # DB JRNC,GETTRK1-$-1 ; Not DS -- leave track alone RAR ; Divide track by 2 to get side DB JRNC,GETTRK1-$-1 ; No carry means side 0 MVI M,10H ; Indicate side 1 JMP GETTRK2 ; And finish up GETTRK1: MVI M,0 ; Indicate side 0 GETTRK2: POP H ; Get HL register back ANI 07FH ; Mask high bit, just in case RET ; ... and return ; LOGICAL DEVICE PHYSICAL DEVICE ASSIGNMENTS ; -------------- --------------------------- ; CON: CRT: OR TTY: ; READER: TTY: ; PUNCH: TTY: ; LIST: CRT: OR TTY: OR LPT: CONST: ;CONSOLE STATUS, RETURNS 0FFH IF CHARACTER READY, ELSE 00H IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF CONST1: IF CLOCK CALL CLKCHK ; tick ENDIF IF BUFFKBD CALL KEYSTAT ; see if char in buffer RNZ ; return with buffer status ENDIF LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11B ;KEEP CON BITS ONLY JZ TTYIST ; check appropriate input status JMP CRTIST ; no third or fourth choices CONIN: ;CONSOLE CHARACTER INTO REGISTER A IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; local stack ENDIF CONIN1: IF BUFFKBD CALL KEYSTAT JNZ KEYRET ; get character from buffer ENDIF LDA IOBYTE ANI 11B JZ TTYIN ; do ttyin JMP CRTIN ; do crtin CONOUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set local stack ENDIF CONOUT1: IF CLOCK CALL CLKCHK ; tick toc ENDIF IF BUFFKBD CALL KEYCHK ; get any keyboard character ENDIF LDA IOBYTE ANI 11B JZ TTYOUT ; out tty JMP CRTOUT ; out crt READER: ;READ CHARACTER INTO REGISTER A FROM READER DEVICE JMP TTYIN ; no choice supported PUNCH: ;PUNCH CHARACTER FROM REGISTER C JMP TTYOUT ; no choices supported LISTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; local stack ENDIF LSTST1: LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11000000B ;KEEP LST BITS ONLY DB JRNZ,LSTST2-$-1 ; tty ? JMP TTYOST ; check tty output status LSTST2: ANI 01000000B ;CRT? DB JRZ,LPTST-$-1 ; lpt DB JR,CRTOST-$-1 ; crt LIST: ;LIST CHARACTER OUTPUT FROM REGISTER C IF LOCAL$STACK DW 73EDH,OLDSTK ; ld(oldstk),sp - save old stack LXI SP,LOCSTK ; set stack pointer ENDIF LIST1: IF CLOCK CALL CLKCHK ; tic toc ENDIF IF BUFFKBD CALL KEYCHK ; get any keyboard characters ENDIF CALL LSTST1 ;WAIT TILL READY TO SEND DB JRZ,255-($-LIST1) LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11000000B ;KEEP LST BITS ONLY JZ TTYOUT CPI 01000000B DB JRZ,CRTOUT-$-1 DB JR,LPTOUT-$-1 ; no forth choice CRTOST: ; CRT OUTPUT STATUS, RETURN 0FFH IF READY TO SEND, 00H IF NOT ; CRT "ALL SENT" STATUS NOW CHECKED ; Note: This former routine (IN SIOCPA ANI TBE RZ) requires ; autoenables to be set to function correctly. The present ; routine uses the "ALL SENT" bit to determine if the previous ; byte has been sent from the DART. Under the previous routine, ; there is a possibility of a character getting into the output ; buffer when there is no ready signal. MVI A,01H ; Check "all sent" bit in register 1 OUT SIOCPA ; . IN SIOCPA ; . ANI 01H ; "ALL SENT" is bit 0 RZ ;TRANSMIT BUFFER NOT READY LDA HSA ;SEE IF CTS H/S REQUIRED ORA A DB JRZ,CRTRDY-$-1 ; if zero, no handshake needed ;CRT CTS HANDSHAKE SIGNAL STATUS NOW CHECKED MVI A,10H OUT SIOCPA ;UPDATE UART STATUS IN SIOCPA ;FETCH STATUS ANI CTS RZ ;CTS NOT ACTIVE CRTRDY: ORI 255 ;SHOW READY TO END RET CRTIST: ;CRT INPUT STATUS, RETURN 0FFH IF DATA READY, 00H IF NOT IN SIOCPA ;FETCH STATUS ANI RDA RZ ;NOT READY ORI 255 ;GOT SOMETHING, SIGNAL ITS PRESENCE RET CRTOUT: IF BUFFKBD ; Check for console input while waiting CALL KEYCHK ; to output a character (for handshake ENDIF ; intensive terminals, Versabrailles) IF CLOCK CALL CLKCHK ; tick toc ENDIF CALL CRTOST ;OK TO SEND? DB JRZ,255-($-CRTOUT) ; NO WAIT MOV A,C ;CHARACTER TO REGISTER A OUT SIODPA RET CRTIN: IF CLOCK CALL CLKCHK ; tick toc ENDIF CALL CRTIST ;READY? DB JRZ,255-($-CRTIN) ; NO, WAIT IN SIODPA ;ELSE FETCH IT ANI 7FH ;STRIP PARITY BIT RET LPTOUT: ;PRINTER CHARACTER OUTPUT FROM REGISTER C IF BUFFKBD CALL KEYCHK ; test for console input ENDIF IF CLOCK CALL CLKCHK ; tick toc ENDIF CALL LPTST ;PRINTER READY? DB JRZ,255-($-LPTOUT) ; not ready, wait MOV A,C OUT PIO1 ;SET UP THE DATA OUT STBSET ;SEND A DATA STROBE OUT STBCLR ; (DATA DOESN'T MATTER) RET LPTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H MVI A,10H ;UPDATE UART STATUS OUT SIOCPB IN SIOCPB ;READ PRINTER BUSY SIGNAL ANI 10H RZ ;NOT READY ORI 255 ;SHOW READY RET TTYOST: ;TTY OUTPUT STATUS, RETURN 0FFH IF READY TO SEND, 00H IF NOT ;TTY "ALL SENT" STATUS NOW CHECKED ; Note: This former routine (IN SIOCPB ANI TBE RZ) requires ; autoenables to be set to function correctly. The present ; routine uses the "ALL SENT" bit to determine if the previous ; byte has been sent from the DART. Under the previous routine, ; there is a possibility of a character getting into the output ; buffer when there is no ready signal. MVI A,01H ; Check "all sent" bit in register 1 OUT SIOCPB ; . IN SIOCPB ; . ANI 01H ; "ALL SENT" is bit 0 RZ ;TRANSMIT BUFFER NOT READY LDA HSB ;SEE IF CTS H/S REQUIRED ORA A DB JRZ,TTYRDY-$-1 ; if zero, no h/s needed ;TTY CTS HANDSHAKE SIGNAL STATUS NOW CHECKED MVI A,10H OUT SIOCPB ;UPDATE UART STATUS IN SIOCPB ;FETCH STATUS ANI CTS RZ ;CTS NOT ACTIVE TTYRDY: ORI 255 ;SHOW READY TO SEND RET TTYIST: ;TTY INPUT STATUS, RETURN 0FFH IF DATA READY, 00H IF NOT IN SIOCPB ;FETCH STATUS ANI RDA RZ ;NOT READY ORI 255 ;GOT SOMETHING, SIGNAL ITS PRESENCE RET TTYOUT: IF BUFFKBD CALL KEYCHK ; check for console char ENDIF IF CLOCK CALL CLKCHK ; tic toc ENDIF CALL TTYOST ;OK TO SEND? DB JRZ,255-($-TTYOUT) ;no wait MOV A,C ;CHARACTER TO REGISTER A OUT SIODPB RET TTYIN: IF CLOCK CALL CLKCHK ; tick toc ENDIF IN SIOCPB ANI RDA ;CHAR READY? DB JRZ,255-($-TTYIN) ; no, wait IN SIODPB ;ELSE FETCH IT ANI 7FH ;STRIP PARITY BIT RET ; Keychk will poll the console for characters during disk waits or ; during common read/write routines and during terminal output. ; this will prevent most loss of input characters during disk i/o ; when programs are not polling for input. ; NOTE: test for console input only (SIO A side). ; IF BUFFKBD KEYCHK: PUSH PSW IN SIOCPA ; get SIO status ANI RDA ; recive ready DB JRZ,KEYEXT-$ ; jrz,keyext+1 PUSH H ; save 'hl' LHLD KEYEND ; next character position in buffer LDA KEYLEN ; get low byte of max addr CMP L ; see if end of buffer DB JRZ,KEYEXT-$-1 ; no more buffer available IN SIODPA ; get data ANI 7FH ; strip parity MOV M,A ; save char INX H ; next position in buffer SHLD KEYEND ; save new pointer KEYEXT: POP H POP PSW RET ; Keystat is called by the console stat routine. returns non-zero if ; character in buffer or zero if no character. ; KEYSTAT: PUSH B ; save 'B' register LDA KEYEND ; get low byte of last byte in buffer MOV B,A ; save LDA KEYST ; get low byte of start of buffer CMP B MVI A,0 DB JRZ,KSTEX-$-1 ; if same exit, nothing in buffer ORI 255 ; show buffer has character KSTEX: POP B ; restore register RET ; zero flag has status ; ; Keyret returns the next character in buffer is called by conin ; KEYRET: PUSH H ; save registers PUSH B LHLD KEYST ; first character position MOV B,M ; get character INX H SHLD KEYST ; next character LDA KEYEND ; get low byte of last char in buffer CMP L ; same MOV A,B ; get character for exit DB JRNZ,KREX-$-1 ; LXI H,KEYBUF ; reset buffer start and end SHLD KEYST SHLD KEYEND KREX: POP B ; restore register POP H RET ENDIF ; clkchk will poll the ctc for change in count. Was adapted from ; the clock routines developed by Roger Ward. IF CLOCK CLKCHK: PUSH H ; save registers PUSH PSW LXI H,TICK IN CTCA3 ; get down count value CMP M ; see if count has rolled over JZ CLKEXT ; same count value MOV M,A ; save new count JC CLKEXT ; counter < last count, not rolled LXI H,SECNT ; seconds count INR M ; increment seconds MVI A,60 CMP M DB JRNZ,CLKEXT-$-1 ; exit if no rollover MVI M,0 ; seconds to 0 INX H ; point to min. INR M ; one more min. CMP M ; 'a' = 60 DB JRNZ,CLKEXT-$-1 ; exit if no rollover MVI M,0 ; min=0 INX H ; point to hours INR M ; one more hour MVI A,24 CMP M DB JRNZ,CLKEXT-$-1 ; exit, same day still MVI M,0 ; hours=0 LHLD DAYCNT INX H ; increment days SHLD DAYCNT DB BIT,BTST+B0+ZH ; test less than >256 DB JRZ,CLKEXT-$-1 ; MVI A,366-256 ; days in calendar(include leap year) CMP L DB JRNZ,CLKEXT-$-1 ; exit no wrap LXI H,0 SHLD DAYCNT ; day 0 LXI H,YRCNT INR M MVI A,100 CMP M ; year wrap ? DB JRNZ,CLKEXT-$-1 ; no, exit MVI M,0 ; else clear CLKEXT: POP PSW ; restore stack POP H RET TOD: LXI H,SECNT ; point to clock storage RET ; SECNT: DB 0 ; sec count MINCNT: DB 0 HRCNT: DB 0 DAYCNT DW 0 YRCNT: DB 0 TICK: DB 255 ; storage for clock tick ENDIF ; ; * * * * * * * * ; ; LOCSTK is the common return point for operations that use ; the local bios stack. Routine reatores the callers ; stack pointer and returns. ; * * * * * * * * ; local stack area IF LOCAL$STACK DS 32 ; 16 level stack LOCSTK: DW EXIT ; exit routine OLDSTK: DS 2 ; save area for old stack EXIT: DW 7BEDH,OLDSTK ; ld sp,(oldstk) - restore stack RET ENDIF ; ; Storage for buffered keyboard ; IF BUFFKBD KEYST: DW KEYBUF ; Storage for begining of buffer pointer KEYEND: DW KEYBUF ; Storage for end of buffer pointer KEYLEN: DW KEYSTOP ; last address in keyboard buffer ENDIF MAINEND EQU $ ; End of main body of code IF HARD$DISK ; * * * * * * * * ; ; HARD DISK Disk parameter blocks ; ; A system configuration entry, or a user defined assembly ; is to be used to set up the track offset entry to create ; various hard disk partitions. This is done to support drives ; with greater than 20Mbyte storage. ; ; * * * * * * * * HD$DPB$BASE: EQU $ ; ; # of --mask-- disk # of rsv'd chk trk ; sect bs bm em size d ent block siz ofs x FPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 GPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 HPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 IPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 JPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 KPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 LPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 MPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 NPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 OPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 PPARM: DB 64,0, 5,31, 1, 16,0, 255,3, 255,0, 0,0, 2,0, 0 HD$DPB$LEN: EQU $-HD$DPB$BASE MY$ID: DB 0 ; Board SCSI ID saved here HD$BYTE$BLOCK: DB 0 ; SCSI Byte/block mode saved here ; * * * * * * * * ; ; DIRECT SCSI DRIVER ; ; Entry here via the BIOS Jump tables as JMP SCSI ; ; Enter with the following registers set: ; ; A SCSI target address to be used ; HL Pointer to SCSI Command block ; DE Pointer to SCSI Data Block ; ; Exits with HL preserved. DE is preserved if the operation is ; successful, otherwise DE contains the sense data (error bytes). ; The A regisner contains the ending status of the SCSI command. ; ; NOTE: This routine saves and restores the previous TARGET ; address for normal operations. ; ; * * * * * * * * SCSI: MVI C,1 ; Set C register for 1 byte block SCSI$HSPEED: STA TARGET ; Save target address PUSH H ; Save entry command pointer PUSH D ; . and entry data pointer SHLD CMDPTR ; Set CMDPTR to what HL is pointing to XCHG ; Set the data pointer to HL CALL SCSI$CMD ; Do the direct routines JZ SCSI$END$OK ; If ok, restore cmd & data ptrs POP H ; Otherwise, throw away data ptr JMP SCSI$END$ERR ; . by popping cmd pointer twice SCSI$END$OK: POP D ; Restore data pointer SCSI$END$ERR: POP H ; Restore command pointer RET ; All done ; ; SCSI return sense data command (Cmd 03) ; SCSI$STAT$CMD: DB 3 ; 00 - REQUEST SENSE COMMAND DB 0 ; 01 - LOGICAL UNIT DB 0 ; 02 - RESERVED DB 0 ; 03 - RESERVED DB 4 ; 04 - NUMBER OF BYTES DB 0 ; 05 - RESERVED ; ; SCSI read/write command (Cmd 08/0A) ; SCSI$RD$CMD: EQU 08H ; 08 IS READ DATA SCSI$WR$CMD: EQU 0AH ; 0A IS WRITE DATA SCSI$RW$CMD: DB SCSI$RD$CMD ; 00 - 08=Read, 0A=Write HIGH$ADDR: DB 0 ; 01 - High address MED$ADDR: DB 0 ; 02 - Middle address LOW$ADDR: DB 0 ; 03 - Low address DB 1 ; 04 - Number of sectors STEP$RATE: DB 0 ; 05 - Step rate (Xebec) ; * * * * * * * * ; ; Hard disk driver (Driver #3) ; ; FF Select Disk ; 00 Write Disk ; 01 Read Disk ; ; * * * * * * * * DRVR3: INR A ; select command = 0ffh JZ SELEND ; do select LXI H,TAGPHY ; Get address & step rate MOV A,M ; . ANI 0FH ; Mask off address STA STEP$RATE ; Store step rate in SCSI cmd INX H ; Bump ptr to unit # MOV A,M ; Get unit # & type ANI 0E0H ; Mask off type STA LOGUNIT ; Save unit # INX H ; Bump ptr to SCSI addr MOV A,M ; Get SCSI address STA TARGET ; Save as target address ; fall through to SCSI$IO and do the read/write ; * * * * * * * * ; ; SCSI READ/WRITE HOST ROUTINES ; ; Entry here from the HOSTIO section of the blocking/deblock ; routines. Assumes that the TARGET address has been pre-set. ; ; Exits with the error code in (A), and the sense status saved ; at SCSI$STAT$DAT if an error occurs. ; ; * * * * * * * * SCSI$IO: LDA RWHOST ANA A ; write = 0 ; ; Write to the hard disk described by (TARGET) and (LOGUNIT) ; SCSI$WR: MVI A,SCSI$WR$CMD ; Set write command DB JRZ,SCSI$DO$RW-$-1 ; ; Read from the hard disk described by (TARGET) and (LOGUNIT) ; SCSI$RD: MVI A,SCSI$RD$CMD ; Set read command ; ; Do the rest of the setup for the read/write command ; SCSI$DO$RW: LXI H,SCSI$RW$CMD ; Get command string MOV M,A ; Plug the SCSI command SHLD CMDPTR ; Save the command pointer CALL BLD$SCSI$SCTR ; Build SCSI sector address LXI H,HSTBUF ; Get pointer to host buffer LDA HD$BYTE$BLOCK ; Get SCSI byte/block mode value MOV C,A ; Stuff in C register ; ; Enter here from the SCSI direct driver routine ; ; Exits with status in A. 0FFH = timeout error ; SCSI$CMD: SHLD DATPTR ; Save the data pointer LXI H,INT$RETRIES ; retry save area MVI M,2 ; retries LXI H,SCSI$IO$COUNT ; And the # of bytes to transfer MOV M,C ; . at one time (each block) SCSI$CMD$RETRY: CALL SELECT ; Perform the SCSI operation LDA STATUS ; Get the return status STA ERFLAG ; Save ending status CPI 0FFH ; Timeout? DB JRZ,SCSI$DONE-$-1 ; Yes, go save timeout status ANI 2 ; Check for SCSI error status RZ ; No error -- return LXI H,INT$RETRIES ; see if any retries remain DCR M DB JRZ,SCSI$DONE-$-1 ; NO more remain, exit and set flags ; Save current command and data pointers. LHLD DATPTR ; save old data pointer SHLD SAVE$DATPTR LHLD CMDPTR ; save old command pointer SHLD SAVE$CMDPTR MVI A,1 STA SCSI$IO$COUNT ; force byte xfer mode for retries.. ; Request SCSI sense. LXI H,SCSI$STAT$DAT ; . for request sense command SHLD DATPTR LXI H,SCSI$STAT$CMD ; Set up data and command pointers SHLD CMDPTR LDA LOGUNIT ; Get logical unit number INX H ; Stuff in proper location in MOV M,A ; . SCSI command CALL SELECT ; Execute request sense command ; Restore pointers and retry command LHLD SAVE$DATPTR ; restore old data pointer SHLD DATPTR LHLD SAVE$CMDPTR ; restore old command pointer SHLD CMDPTR JMP SCSI$CMD$RETRY ; command retry SCSI$DONE: ORA A ; Set Z/NZ for user RET ; and return ; * * * * * * * * ; ; Build 3-byte SCSI sector number and logical unit number ; ; NOTE: This routine assumes 16 sectors per track. ; ; It is safe to assume 16 sectors per track even though some hard ; disk controllers format more (Xebec=17, Adaptec=18, etc.) as the ; read and write commands use a block number, rather than a track ; and sector number to move to the correct block to read or write. ; ; * * * * * * * * BLD$SCSI$SCTR: XRA A ; Clear A & Carry ; A reg C HL reg pair LHLD CPMTRK ; -------- - 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---- MOV B,A ; LDA LOGUNIT ; ORA B ; Or in logical unit number STA HIGH$ADDR ; Save unit number & high byte MOV A,H ; STA MED$ADDR ; Save middle byte LDA HSTSEC ; Get sector number ORA L ; Or in low byte STA LOW$ADDR ; Save new low byte RET ; ; Select controller, and fall through to phase if selected ok. ; busbsy: equ 40h SELECT: XRA A OUT NCRICR ; Clear initiator command register OUT NCRTCR ; . and target command register CLEAR$ARBIT: XRA A OUT NCRMR ; . IN NCRRPI ; reset interrupts ENDIF IF HARD$DISK AND ARBITRATION ARBITRATE: LDA MY$ID ; Assert my ID (the initiator) OUT NCRODR ; . MVI A,1 ; start arbitration OUT NCRMR ; . IN$PROGRESS: IN NCRICR ; Wait for "arbitration in ANI 40H ; . progress" bit JNZ ARBITRATE$WON ; we have arbitration IN NCRBSR ANI 10H ; see if scsi reset has occured JZ IN$PROGRESS JMP CLEAR$ARBIT ARBITRATE$WON: LDA MY$ID MOV B,A IN NCRCSD ; See if we're the highest priority SUB B ; . remove my addr SUB B ; . compare my addr to bus data JM I$WIN ; We win if result < 0 JMP CLEAR$ARBIT ; . otherwise we lose -- start over I$WIN: IN NCRICR ; Check again for lost arbitration ANI 20H ; . (just in case) JNZ CLEAR$ARBIT ; We lost -- start over MVI A,08H ; Set assert BSY bit in ICR OUT NCRICR ; . IN NCRMR ; Reset arbitration bit ANI 0FEH ; . OUT NCRMR ; . IN NCRICR ; OR in SEL to ICR ORI 04H OUT NCRICR ENDIF IF HARD$DISK LDA MY$ID ; Select target: get our ID, MOV B,A ; . LDA TARGET ; . or in target ID ORA B ; . OUT NCRODR ; . and send to NCR chip IN NCRICR ORI 01H ; Assert data bus OUT NCRICR ; . MVI A,05H ; Release BSY, keep SEL OUT NCRICR ; . and assert data bus LXI B,6000H ; 250 ms loop (1M cycles) STIM: IN NCRCSBS ; Wait for BSY ANI BUSBSY ; . JNZ SELECT$OK ; Got him! DCR C JNZ STIM ; inner loop: 41*256 = 10496 cycles DCR B JNZ STIM ; outer loop: 10510*96 = 1M cycles XRA A ; Select timeout -- clear bus OUT NCRODR DCR A ; set timeout to 'a' STA STATUS JMP ALL$DONE ; and clear the registers SELECT$OK: XRA A ; Set good status ALL$DONE: MOV B,A ; save status MVI A,01H ; Release SEL OUT NCRICR ; . XRA A ; Release data bus OUT NCRICR ; . MOV A,B ; get status back ORA A ; Set status RNZ DCR A ; clear scsi status to timeout, 0ffh STA STATUS ; Save status for PWIDIR routine ; SCSI.011 ; * * * * * ; * --------\ NOTE: we fall through if we successfully ; * --------/ selected the controller!! ; * * * * * MVI A,00000110B ; Set DMA mode and Monitor Busy OUT NCRMR ; . SCSI$RDY: ; Wait for either a 5380 "Interrupt" or a REQ from Target. ; The REQ is needed since it may have come too soon after ; selection to register an Interrupt. IN NCRBSR ; Check for "Interrupt" ANI 00010000B ; . DB JRNZ,SCSI$INT-$-1 IN NCRCSBS ; Check for REQ ANI NCRREQ ; . ; Wait for Interrupt or REQ DB JRZ,255-($-SCSI$RDY) JMP PHASE ; Process phase vector SCSI$INT: ; Determine cause of 5380 "Interrupt". Either phase ; changed, busy dropped, or bus was reset. If bits 2 and 3 ; of the NCRBSR are not 0's when the Interrupt flag (bit 4) ; is set, then it is either a loss of BUSY or an SCSI RESET. XRA A OUT NCRICR ; Release data bus IN NCRBSR ; Read 5380 Bus and Stat Reg ANI 00001100B ; Keep interesting bits DB JRNZ,SCSI$EXIT-$-1 ; Reset or Busy Loss: Exit ; 00 --> Process phase vector PHASE: ; DMA mode and Monitor Busy must be cleared prior to clearing ; of the 5380 Interrupt Flag. Then mode register is restored. ; Otherwise the interrupt flag may not clear and the DMA Mode ; may not be useable. XRA A ; Clear 5380 Mode Register OUT NCRMR ; . IN NCRRPI ; Reset interrupts MVI A,00000110B ; Set DMA mode and Monitor Busy OUT NCRMR ; . IN NCRCSBS ; Update phase... ANI 00011100B ; Mask all but phase bits, clear carry bit RAR ; Rotate over for target MOV E,A ; . (Save for use with jump table) RAR ; . OUT NCRTCR ; Set phase MVI D,0 ; E is already set (3 ins ago) LXI H,PHASE$TABLE ; Get phase jump table base DAD D ; Add offset for this phase MOV A,M ; Get phase pointer into HL INX H ; . MOV H,M ; . MOV L,A ; Pointer is now together MVI D,01000000B ; DMA request mask(used by RSCSI and WSCSI) PCHL ; Go to it! PHASE$TABLE: DW PHASE0 DW PHASE1 DW PHASE2 DW PHASE3 DW PHASE4 DW PHASE5 DW PHASE6 DW PHASE7 PHASE0: ; Data out phase ... LXI H,SCSI$IO$COUNT ; Point to byte/block transfer mode MOV E,M ; Get transfer mode value LHLD DATPTR ; Use data pointer JMP WSCSI ; Execute SCSI write routine PHASE1: ; Data in phase ... LXI H,SCSI$IO$COUNT ; Point to byte/block transfer mode MOV E,M ; Get transfer mode value LHLD DATPTR ; Use data pointer JMP RSCSI ; Execute SCSI read routine PHASE2: ; Command out phase ... LHLD CMDPTR ; Use command pointer MVI E,1 ; Set mode to byte transfer JMP WSCSI ; Execute SCSI write routine PHASE3: ; Status in phase ... LXI H,STATUS ; Use status pointer MVI E,1 ; Set mode to byte transfer JMP RSCSI ; Execute SCSI read routine PHASE7: ; Message in phase ... LXI H,MESSAGE ; Use message pointer MVI E,1 ; Set mode to byte transfer JMP RSCSI ; Execute SCSI read routine ; Currently unused phases PHASE4: PHASE5: PHASE6: SCSI$EXIT: XRA A ; Clean up 5380 and exit. OUT NCRTCR ; . OUT NCRMR ; . IN NCRRPI ; Reset interrupts RET ; . ; Generalized SCSI write routine WSCSI: MVI A,1 ; Assert data bus OUT NCRICR MVI C,NCRDACK ; Set up destination port address OUT NCRSDS ; Start DMA send ; Wait for DMA request, keeping an eye on phase. Note that the NCR ; will not issue an ACK, nor will it generate DMA requests once the ; phase changes, so it is best to treat DMA request checking as a ; higher priority than phase change checking. WSCSI1: IN NCRBSR MOV B,A ; Save status for use below ANA D ; Check for DMA request DB JRZ,WSCSI2-$-1 ; This is the heart of the pseudo-DMA transfer. On entry, HL points ; to the data buffer and E should be 0 for 256 byte block transfer, ; or 1 for byte-by-byte transfer. NOTE: Use block transfer only if ; you can be sure the controller can buffer 256 bytes of data and ; can transfer at 5.25 us per byte. Extra DACK's after the last REQ ; will do no harm. ; ; OTIR register use: H = memory pointer, C = I/O port, B = counter ; MOV B,E ; Set up loop count DW OTIR80 ; ED B3 = OTIR instruction op code JMP WSCSI1 ; Write more bytes until phase changes ; This code skipped when data is being transferred ... WSCSI2: MOV A,B ; Check 5380 "interrupt" flag ANI 00010000B ; . JZ WSCSI1 ; Wait for DMA request, JMP SCSI$INT ; or process "interrupt" ; Generalized SCSI read routine RSCSI: ; Initiator command reg is already initialized MVI C,NCRDACK ; Source port address OUT NCRSDIR ; Write to this port starts dma recieve ; Wait for DMA request, keeping an eye on phase. Note: we must do ; a check for DMA request before checking for a phase change, since ; a byte may be queued up waiting to be DACKed prior to the phase ; change. ; RSCSI1: IN NCRBSR MOV B,A ; Keep for phase change checking ANA D ; Mask for DMA request DB JRZ,RSCSI2-$-1 ; This is the heart of the pseudo-DMA transfer. On entry, HL points ; to the data buffer and E should be 0 for 256 byte block transfer, ; or 1 for byte-by-byte transfer. NOTE: Use block transfer only if ; you can be sure the controller can buffer 256 bytes of data and ; can transfer at 5.25 us per byte. Extra DACK's after the last REQ ; will do no harm. ; ; INIR register use: H = memory pointer, C = I/O port, B = counter ; MOV B,E ; Set up loop count DW INIR80 ; ED B2 = INIR instruction op code JMP RSCSI1 ; Read more bytes until phase changes ; Be sure and check phase if no DMA ; request, since NCR won't issue any ; unneeded DACKs ; This code skipped when data is being transferred ... RSCSI2: MOV A,B ; Check 5380 "interrupt" flag ANI 00010000B ; . JZ RSCSI1 ; Wait for DMA request, JMP SCSI$INT ; or process "interrupt" ENDIF HDCODE EQU $ ; End of hard disk code ; * * * * * * * * ; ; COLD BOOT ENTRY ; ; NOTE: The following code does not stay resident. It is ; overlayed for use by all bios system variables. ; ; * * * * * * * * BOOT: MVI A,41H ; Turn off EPROM, but leave drive 0 OUT CONT ; . selected (1770 turns it off) LDA IOBYT ; Setup initial IOBYTE value STA IOBYTE ; . LXI SP,80H ; Initialize DART, CTC, etc. CALL IOINIT ; . IF HARD$DISK ; Init HD parameters, if present IN 029h ; Get ID value ANI 07H ; Mask off extra bits PUSH PSW ; . (Put ID in inital message) ADI '0' ; . . STA SCSI$ID$FOUND ; . . POP PSW ; . . INR A ; Bump to make 1 - 8 MOV B,A ; Convert to SCSI address XRA A ; . (Clear receiving register) STC ; . (Carry = bit to rotate) NEXT$BIT: ; . RAL ; . (Rotate left through carry) DCR B ; . (Decrement counter) JNZ NEXT$BIT ; . (Not done -- repeat) STA MY$ID ; Save ID value ENDIF ; SCSI reset is done by SCSI boot EPROM, and so should not ; be repeated here unless boot is from floppy. In any ; case, only boards jumpered for SCSI ID # 7 will do reset. IF HARD$DISK AND SCSI$RESET CPI 1000$0000B ; Perform SCSI reset if this is ID # 7 DB JRNZ,NOT7-$-1 ; . MVI A,80H ; Set reset line high OUT NCRICR ; . HOLD$RESET: DCR A ; Hold reset high for at least 50us DB JRNZ,HOLD$RESET-$-1 and 255 OUT NCRICR ; Clear reset line IN NCRRPI ; . and interrupt line ENDIF IF HARD$DISK NOT7: XRA A ; Clear 5380 registers (SCSI init) OUT NCRICR ; . OUT NCRMR ; . OUT NCRTCR ; . OUT NCRSER ; . ENDIF ; End of HD initialization XRA A ; Patch out old CP/M message STA CPMMSG LXI H,LOGMSG ; Display signon message CALL PUTS LXI H,Z80MSG CALL PUTS LXI H,DRIVE$TDATA ; Init drive type data LXI D,DRIVE$TYPES LXI B,8 DW LDIR80 IF ZCPR3 ; Init ZCPR3 stuff if present LXI H,LAST$SG ; Set up HL and DE to clear memory LXI D,LAST$SG+1 ; from the end of boot to 0ffffh MVI M,0 MOV A,H CMA MOV B,A MOV A,L CMA MOV C,A DW LDIR80 ZBOOT: LXI B,3 ; Set up the ZCPR3 command line LXI H,CMDSET ; . pointers LXI D,Z3CL ; DW LDIR80 ; LXI B,10 ; Move the automatic command to LXI H,AUTOCMD ; . the ZCPR3 command line LXI D,Z3CL+3 ; DW LDIR80 ; LXI B,11 ; Move the initial path descriptor LXI H,PATH ; . to the proper location LXI D,EXPATH ; DW LDIR80 ; LXI H,Z3WHL ; Turn the wheel byte on MVI M,0FFH ; LXI H,ENV ; Move environment and TCAP to the LXI D,Z3ENV ; . proper location LXI B,ENVEND-ENV+1 ; . DW LDIR80 ; ENDIF ; ZCPR3 init XRA A ; STA CDISK ; Indicate disk 0 selected STA HSTACT ; Set host buffer inactive STA UNACNT ; Clear unalloc count STA HSTSID ; Assume side zero JMP GOCPM ; Initialize & jump to CP/M LOGMSG: DB CR,LF,LF DB MSIZE/10 +'0',MSIZE MOD 10 +'0' CPMMSG: DB 'k CP/M vers 2.2' Z80MSG: DB 'K Z80 Operating System' DB CR,LF,'AMPRO BIOS Version ',VERS/10+'0','.' DB VERS MOD 10+'0' IF INTERNAL ; Display internal revision # 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,'Copyright (C) 1983,84,85,86 ' DB 'AMPRO Computers, Inc.' IF HARD$DISK ; Display SCSI ID for this unit DB CR,LF,LF,'SCSI initiator ID = ' SCSI$ID$FOUND: DB 'x' ENDIF IF HARD$DISK AND ARBITRATION DB CR,LF,'(Arbitration Enabled)' ENDIF IF HARD$DISK AND (NOT ARBITRATION) DB CR,LF,'(Arbitration Disabled)' ENDIF IF CLOCK DB CR,LF,'(Clock Enabled)' ENDIF IF BUFFKBD DB CR,LF,'(Console Buffer Enabled)' ENDIF IF INTERNAL DB CR,LF,'WARNING!!! Unreleased BIOS.' ENDIF DB CR,LF,0 IF ZCPR3 ; Include ZCPR3 definitions? ENV: JMP 0 ; Leading JMP ENV1: ; ZCPR3 enviornment descriptor ... DB 'Z3ENV' ; . Environment ID DB 1 ; . Class 1 environment (external) DW EXPATH ; . External path (PATH) DB EXPATHS ; DW RCP ; . Resident command package (RCP) DB RCPS ; DW IOP ; . Input/output package (IOP) DB IOPS ; DW FCP ; . Flow command package (FCP) DB FCPS ; DW Z3NDIR ; . Named directories (NDR) DB Z3NDIRS ; DW Z3CL ; . Command line (CL) DB Z3CLS ; DW Z3ENV ; . Environment (ENV) DB Z3ENVS ; DW SHSTK ; . Shell stack (SH) DB SHSTKS ; DB SHSIZE ; DW Z3MSG ; . Message buffer (MSG) DW EXTFCB ; . External FCB (FCB) DW EXTSTK ; . External stack (STK) DB 0 ; . Quiet flag (1=quiet, 0=not quiet) DW Z3WHL ; . Wheel byte (WHL) DB 4 ; . Processor speed (Mhz) DB 'P'-'@' ; . Max disk letter DB 31 ; . Max user number DB 1 ; . 1=ok to accept DU:, 0=not ok DB 0 ; . CRT selection DB 0 ; . Printer selection DB 80 ; . CRT 0: Width DB 24 ; # of lines DB 22 ; # of text lines DB 132 ; . CRT 1: Width DB 24 ; # of lines DB 22 ; # of text lines DB 80 ; . PRT 0: Width DB 66 ; # of lines DB 58 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 96 ; . PRT 1: Width DB 66 ; # of lines DB 58 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 132 ; . PRT 2: Width DB 66 ; # of lines DB 58 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 132 ; . PRT 3: Width DB 88 ; # of lines DB 82 ; # of text lines DB 1 ; FF flag (1=can form feed) DB 'SH ' ; . Shell variable filename DB 'VAR' ; . Shell variable filetype DB ' ' ; . File 1 DB ' ' ; DB ' ' ; . File 2 DB ' ' ; DB ' ' ; . File 3 DB ' ' ; DB ' ' ; . File 4 DB ' ' ; DB 0 ; Public drive area (ZRDOS +) DB 0 ; Public user area (ZRDOS +) ;ENV 128 bytes long ENV2: ; Terminal capabilities data DB 'ADM-3A ' ; . Name of terminal (ADM 3A) DB ' ' ; . DB 'K'-'@' ; . Cursor up DB 'J'-'@' ; . Cursor down DB 'L'-'@' ; . Cursor right DB 'H'-'@' ; . Cursor left DB 00 ; . Clear screen delay DB 00 ; . Cursor motion delay DB 00 ; . Clear to EOL delay DB 1BH,'*',0 ; . (CL) Clear screen string DB 1BH,'=%+ %+ ',0 ; . (CM) Cursor motion string DB 1BH,'T',0 ; . (CE) Clear to EOL string DB 1BH,')',0 ; . (SO) Start hilite string DB 1BH,'(',0 ; . (SE) End hilite string DB 0 ; . (TI) Terminal init string DB 0 ; . (TE) Terminal de-init string DB 0 ; ENVEND: ; End of environment and TCAP descriptors CMDSET: DW Z3CL+4 ; Point to first chr in cmd line buf DB Z3CLS ; Command line buffer size PATH: ; Initial path description DB '$',0 ; . Current drive, user 0 DB '$',15 ; . Current drive, user 15 DB 1,'$' ; . Drive A:, current user DB 1,0 ; . Drive A:, user 0 DB 1,15 ; . Drive A:, user 15 DB 0 ; (end of path) ENDIF ; ZCPR3 data DRIVE$TDATA: ; Drive type data DB 01H,02H,04H,08H DB 02H,00H,00H,00H LAST$SG EQU $ ORG BOOT UNINIT EQU $ MUNACT: DS 1 ; UNALLOCATED COUNT VALUE SEKDSK: DS 1 ;SEEK DISK NUMBER SEKTRK: DS 2 ;SEEK TRACK NUMBER SEKSEC: DS 1 ;SEEK SECTOR NUMBER HSTDSK: DS 1 ;HOST DISK NUMBER HSTTRK: DS 2 ;HOST TRACK NUMBER HSTSEC: DS 1 ;HOST SECTOR NUMBER CPMDSK: DS 1 ;SINGLE DENSITY DSK PARM CPMTRK: DS 2 ; AND TRK CPMSEC: DS 1 ; AND SECTOR SEKHST: DS 1 ;SEEK SHR SECSHF HSTACT: DS 1 ;HOST ACTIVE FLAG HSTWRT: DS 1 ;HOST WRITTEN FLAG UNACNT: DS 1 ;UNALLOC REC CNT UNADSK: DS 1 ;LAST UNALLOC DISK UNATRK: DS 2 ;LAST UNALLOC TRACK UNASEC: DS 1 ;LAST UNALLOC SECTOR CPMSPT: DS 1 ; LOGICAL SECTORS PER TRACK SECMSK: DS 1 ; SECTOR MASK SECSHF: DS 1 ; SECTOR SHIFT INT$RETRIES: DS 1 ; Internal retry counter for RD & WR ERFLAG: DS 1 ;ERROR REPORTING RSFLAG: DS 1 ;READ SECTOR FLAG READOP: DS 1 ;1 IF READ OPERATION WRTYPE: DS 1 ;WRITE OPERATION TYPE DMAADR: DS 2 ;LAST DMA ADDRESS HSTBUF: DS 1024 ;HOST BUFFER STATUS: DS 1 MESSAGE: DS 1 CMDPTR DS 2 DATPTR DS 2 LOGDSK DS 1 PHYDRV DS 1 DPHDRV DS 1 PHYTAG DS 2 PHYCMD DS 1 TAGDRV DS 1 TAGPHY DS 1 TAGTYP DS 1 TAGCTL DS 1 SCSI$IO$COUNT DS 1 SCSI$STAT$DAT DS 4 TARGET DS 1 LOGUNIT DS 1 CURDPB DS 2 ; AMPRO BIOS-SPECIFIC STORAGE IDSAVE: DS 6 ;READ ADDRESS BUFFER AREA LTRACK: DS 5 ;LAST FLOPPY TRACK ACCESSED LDISK: DS 1 ;LAST DISK SELECTED TRIES: DS 1 ;NUMBER OF TIMES TO DO IT RWHOST: DS 1 ;LOCAL READ/WRITE FLAG HSTSID: DS 1 ;HOST DISK SIDE SELECT MASK TIMEOUT: DS 1 ;TIMEOUT LOOP COUNTER SECTOR: DS 1 ;TEMPORARY STORAGE CHGDSK: DS 1 ; Flag to tell if we changed drives FRWCMD: DS 1 ; FDC command FRWPTR: DS 2 ; FDC data pointer SAVE$CMDPTR: DS 2 ; SCSI command pointer save area SAVE$DATPTR: DS 2 ; SCSI data pointer save area ; SPECIAL E DISK PARAMETERS - FILLED IN WHEN E DISK IS ; FIRST SELECTED, AND USED IN DEBLOCKING. ESECADJ: DS 1 ;SECTOR NUMBER ADJUST FOR SIDE 1 IF BUFFKBD KEYBUF: DS BUFFSIZE ; allocate buffer space for 80 character KEYSTOP: EQU $ ; end of buffer address ENDIF DIRBUF: DS 128 ;DIRECTORY ACCESS BUFFER ; * * * * * * * * ; ; Floppy drive directory check vector storage ; ; The length of these vectors allows the use of up to 256 ; directory entries, all of which are checked by BDOS. ; ; Note that these are used for removable floppy media only. ; ; Do not change QPARM to increase the number of directory ; entries without adjusting the variable FD$CKS. ; ; * * * * * * * * FD$CKS EQU (255/4)+1 ; maximum of 256 directory entries CSVA: DS FD$CKS CSVB: DS FD$CKS CSVC: DS FD$CKS CSVD: DS FD$CKS CSVE: DS FD$CKS ; * * * * * * * * ; ; Floppy drive allocation vector storage ; ; This area is used by BDOS to map all CP/M blocks for the ; target disk drive, and is maintained to indicate which blocks ; on the disk are in use. ; ; The length of the Floppy area is set to 50 Bytes, which is ; enough to handle a 96 TPI DS/DD disk as described in QPARM. ; ; Do not change QPARM to increase storage without adjusting ; the variable FD$ALV. ; ; * * * * * * * * FD$ALV EQU (394/8)+1 ; maximum of 395 disk blocks ALVA: DS FD$ALV ALVB: DS FD$ALV ALVC: DS FD$ALV ALVD: DS FD$ALV ALVE: DS FD$ALV FDATAEND EQU $ HD$VECTORS: EQU $ ; HD check & allocation vectors ; * * * * * * * * ; ; Hard disk directory check vector storage ; ; No Check storage is required - just an address ; ; * * * * * * * * CSVF: DS 0 CSVG: DS 0 CSVH: DS 0 CSVI: DS 0 CSVJ: DS 0 CSVK: DS 0 CSVL: DS 0 CSVM: DS 0 CSVN: DS 0 CSVO: DS 0 CSVP: DS 0 ; * * * * * * * * ; ; Hard disk allocation vector storage ; ; * * * * * * * * HD$ALV EQU (1279/8)+1 ; Maximum 1280 disk blocks ALVF: DS 0 ALVG: DS 0 ALVH: DS 0 ALVI: DS 0 ALVJ: DS 0 ALVK: DS 0 ALVL: DS 0 ALVM: DS 0 ALVN: DS 0 ALVO: DS 0 ALVP: DS 0 HD$CURRENT EQU $ ; Current Bios Buffer Area ptr ENDDATA EQU $ ; Mark the last avail. byte IF ZCPR3 ; RESERVE: EQU 0FD00H ; (0FD00H if ZCPR3) ENDIF ; IF NOT ZCPR3 ; RESERVE: EQU 00000H ; (00000H if no ZCPR3) ENDIF ; FDCSIZE: EQU (MAINEND-BIOS) + (LAST$SG-HDCODE) FDDSIZE: EQU FDATAEND-LAST$SG HDCSIZE: EQU HDCODE-MAINEND HDDSIZE: EQU ENDDATA-FDATAEND ; * * * * * * * * ; ; Show the available free space and the number of sectors needed ; to hold a SYSGEN image of CP/M with this BIOS: ; ; * * * * * * * * FREEMEM: EQU RESERVE-ENDDATA ; Free memory left SGSIZE: EQU (LAST$SG-CCP+127)/128 ; Sysgen size ; Number of sectors -- Must be 50H or less! END BIOS