; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * * ; * Ampro Computers, Inc. Bios version 3 * ; * * ; * Copyright (C) 1983,1984,1985 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. * ; * * ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; There have been many changes in the version 3 bios from the version ; 1 and version 2 bios. The major enhancements are listed below: ; ; Floppy-related enhancements: ; ; o Automatic 48tpi.com support in a 96tpi (includes R/W for ; Ampro formats, R/W for E-disk formats if bit 5 of BIOS+074H ; is set, boot of DS 48tpi Ampro format in a 96tpi drive). ; WARNING: writing to a 48tpi disk in a 96tpi drive is NOT ; RECOMMENDED and may cause read errors later. ; ; o If there is not floppy diskette and/or drive present on the ; first select after a warm or cold boot, the system will wait, ; beeping every 5 seconds until the problem is resolved (turn ; on the drive, put a disk in, etc.) or until a Ctrl-C is hit ; at the keyboard (which will abort the routine with a bdos ; error). ; ; 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: Many 1770/1772 FDC chips cannot support this rate. ; ; o Max drive letter eliminated (left as an assembly option). ; ; o 1 second software-timed motor on delay. ; ; ; Hard-disk related enhancements: ; ; o Hard disk support is SCSI generic and installed by HSYSINIT ; as a run-time option. ; ; o Bus (SCSI) arbitration added as assembly-time option. Default ; is no arbitration. ; ; o Each hard disk partition has its own DPB, which allows ; flexible partitioning of any disk drive, up to 88Mb. ; ; o The direct SCSI call has been changed to return better status. ; DE points to status data on error, rather than original user ; data. ; ; o A SCSI burst/byte option has been added to allow controllers to ; operate at optimum rates. ; ; ; Other enhancements: ; ; o Initial Z3 termcap entry set up for the ADM-3A terminal. ; ; * * * * * * * * ; * * * * * * * * ; ; Revision history: ; ; Ver Date Who Description ; --- ----- --- ------------------------------------------ ; 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 33 ; Current version THIS$MONTH EQU 8 ; Today's month THIS$DAY EQU 21 ; day THIS$YEAR EQU 85 ; year INT$REV EQU 99 ; Internal revision number ; System memory size 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 ; TRUE and FALSE NO EQU 0 FALSE EQU 0 YES EQU NOT FALSE TRUE EQU NOT FALSE ; Operating characteristics ZCPR3 EQU YES ; enable ZCPR3? HARD$DISK EQU YES ; enable hard disk routines? MAX$DRIVE EQU NO ; enable max drive checking? ARBITRATION EQU NO ; use arbitrated select? INTERNAL EQU NO ; internal (unreleased) bios? ; Z-80 opcode equates (reversed so we can use a DW to enter them) 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) ; Bit SET/RESET/TEST Z-80 opcode equates (use DB to enter) ; Example: SET 7,D would be DB BIT,BSET+B7+ZD BIT EQU 0CBH ; Bit prefix BTST EQU 040H ; Bit test BRES EQU 080H ; Bit reset BSET EQU 0C0H ; Bit set B0 EQU 000H ; Bit 0 B1 EQU 008H ; Bit 1 B2 EQU 010H ; Bit 2 B3 EQU 018H ; Bit 3 B4 EQU 020H ; Bit 4 B5 EQU 028H ; Bit 5 B6 EQU 030H ; Bit 6 B7 EQU 038H ; Bit 7 ZB EQU 000H ; B Reg ZC EQU 001H ; C Reg ZD EQU 002H ; D Reg ZE EQU 003H ; E Reg ZH EQU 004H ; H Reg ZL EQU 005H ; L Reg ZM EQU 006H ; M Reg ZA EQU 007H ; A Reg ; Jump relative opcode equates (use DB to enter) ; Example: JR AGAIN would be DB JR,AGAIN-$-1 JR EQU 018H ; JR addr JRNZ EQU 020H ; JR NZ,addr JRZ EQU 028H ; JR Z,addr JRNC EQU 030H ; JR NC,addr JRC EQU 038H ; JR C,addr ; IX and IY prefixes (use DB to enter) IX EQU 0DDH ; IX prefix IY EQU 0FDH ; IY prefix ; 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 NSECTS EQU (BIOS-CCP)/128 ; Warm boot sector count ; 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 ; BIOS+040H thru BIOS+06EH 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: DB 47H,13,47H,208,3,3,3,3 ; CTC0,CTC1,CTC2,CTC3 SIOAVAL: DB 4,46H,5,0EAH,3,0C1H,0,0,0,0 ; DART/SIO channel A SIOBVAL: DB 4,86H,5,06AH,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: DW 0,0,0,0,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 ; * * * * * * * * ; ; Current BIOS version ; ; This value is returned in the A register on a call to GETTBL ; (at BIOS+030H) and is the BIOS version number, in decimal. ; For example, BIOS version 3, revision 0 would be returned in ; the A register as 1EH (30 decimal). ; ; * * * * * * * * ORG BIOS+06EH VER: DB VERS ; VERSION OF THIS BIOS. ; * * * * * * * * ; ; Double step write enable flag ; ; This byte is used to determine if a write to a floppy will be ; allowed if the drive is set to "double step" mode (48tpi disk ; in a 96tpi drive). If this byte is set equal to 0DEH (Double ; step Enable), writes on "double step" drives will be allowed. ; If this byte is not equal to 0DEH, then a write will return a ; "write protected" result ("bad sector" on many implementations ; of CP/M) and the write will not occur. ; ; * * * * * * * * ORG BIOS+06FH DS$WRITE$OK: DB 0DEH ; Allow writes on double-step ; * * * * * * * * ; ; 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 (for now) DB 03H ; Drive ?: DD, Unit 3 (for now) ; * * * * * * * * ; ; SCSI ID (used in SCSI arbitration) ; ; * * * * * * * * ORG BIOS+076H MY$ID: DB 0 ; Indicate no ID at present DB 0 ; (Spare) ; * * * * * * * * ; ; Misc pointers ; ; * * * * * * * * ORG BIOS+078H DW 0 ; Pointer 0 DW 0 ; Pointer 1 DW 0 ; Pointer 2 DW 0 ; Pointer 3 ; * * * * * * * * ; ; 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 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 ; . DCR B ; Decrement count JNZ PATMV ; More bytes to do? 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 DW HD$BYTE$BLOCK ; Location of SCSI byte/block flag 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 MOV H,B MOV L,C SHLD DMAADR 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 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 ; . . 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 JZ SELERR ; . PUSH PSW ; 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 LDA PHYCMD ; Get command back INR A ; Select command? JNZ PDRVR1 ; No - don't update disk address LDA LOGDSK ; Yes - Set SEKDSK = LOGDSK STA SEKDSK ; . PDRVR1: POP PSW ; 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 ; * * * * * * * * ; ; 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 CONOUT ; . 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 ; . JZ WBERR ; 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 JNZ WBERR ; 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: DCR B ; Decrement loop counter JNZ WBLOOP ; ; NOTE: WBOOT falls through to GOCPM ; * * * * * * * * ; ; GOCPM Setup CP/M vectors and jump to CP/M ; ; * * * * * * * * GOCPM: 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 not, 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) JC SELSS ; Carry = SS, No carry = DS 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 JZ SET$DSTEP ; . DCR A ; Track=2 means normal step JZ SELEND ; . so just finish the select 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 B,A ; 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 ; . 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 ; . PUSH H ; save it (need to return it in HL) LXI D,10 ; Add offset to DPB 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 POP H ; 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 ; * * * * * * * * ; ; SELERR Indicate select error to the BDOS ; ; * * * * * * * * SELERR: LXI H,0 ; Zero indicates select failed RET ;--------------------------------------------------------------- ; ; READ CP/M SECTOR FROM THE CURRENTLY SELECTED DISK ; ; ENTRY VIA THE BIOS JUMP TABLE ; ;-------------------------------------------------------------- READ: CALL GETTYPE ANI 3 ; SIZE JZ READHST ; IF 128 BYTES XRA A STA UNACNT MVI 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: CALL GETTYPE ANI 3 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 ;CHECK FOR UNALLOC ; 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. LDA MUNACT STA UNACNT LDA SEKDSK ;DISK TO SEEK STA UNADSK ;UNADSK = SEKDSK LHLD SEKTRK SHLD UNATRK ;UNATRK = SECTRK LDA SEKSEC STA UNASEC ;UNASEC = SEKSEC CHKUNA: ;CHECK FOR WRITE TO UNALLOCATED SECTOR LDA UNACNT ;ANY UNALLOC REMAIN? ORA A JZ ALLOC ;SKIP IF NOT ; MORE UNALLOCATED RECORDS REMAIN DCR A ;UNACNT = UNACNT-1 STA UNACNT LDA SEKDSK ;SAME DISK? LXI H,UNADSK CMP M ;SEKDSK = UNADSK? JNZ ALLOC ;SKIP IF NOT ; DISKS ARE THE SAME LXI H,UNATRK CALL TRKCMP JNZ ALLOC ;SKIP IF NOT ; TRACKS ARE THE SAME LDA SEKSEC ;SAME SECTOR? LXI H,UNASEC CMP M ;SEKSEC = UNASEC? JNZ ALLOC ;SKIP IF NOT ; MATCH, MOVE TO NEXT SECTOR FOR FUTURE REF INR M ;UNASEC = UNASEC+1 MOV A,M ;END OF TRACK? ; WATCH FOR E-DISK - IF SO, USE DIFFERENT SPT VARIABLE PUSH B ;SAVE BC IN CASE WE NEEDED IT PUSH PSW LDA PHYDRV ; Test for E-disk CPI 04H ; . LDA CPMSPT ; Use normal SPT if not e-disk JZ EMATCH ; . LDA EPARM ; Else use E-disk SPT EMATCH: MOV B,A POP PSW CMP B ;COUNT CP/M SECTORS POP B ;RESTORE BC REGISTER JC NOOVF ;SKIP IF NO OVERFLOW ; OVERFLOW TO NEXT TRACK MVI M,0 ;UNASEC = 0 LHLD UNATRK INX H SHLD UNATRK NOOVF: ;MATCH FOUND, MARK AS UNNECESSARY READ XRA A ;0 TO ACCUMULATOR STA RSFLAG ;RSFLAG = 0 JMP RWOPER ;TO PERFORM THE WRITE ALLOC: ;NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ XRA A ;0 TO ACCUM STA UNACNT ;UNACNT = 0 INR A ;1 TO ACCUM STA RSFLAG ;RSFLAG = 1 RWOPER: ;ENTER HERE TO PERFORM THE READ/WRITE XRA A ;ZERO TO ACCUM STA ERFLAG ;NO ERRORS (YET) PUSH B ;SAVE IN CASE LDA SECSHF MOV B,A LDA SEKSEC ;COMPUTE HOST SECTOR SLOOP: ORA A ;CARRY = 0 RAR ;SHIFT RIGHT DCR B JNZ SLOOP POP B ;RESTORE 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? JZ FILHST ;FILL HOST IF NOT ; HOST BUFFER ACTIVE, SAME AS SEEK BUFFER? LDA SEKDSK LXI H,HSTDSK ;SAME DISK? CMP M ;SEKDSK = HSTDSK? JNZ NOMATCH ; No. ; SAME DISK, SAME TRACK? LXI H,HSTTRK CALL TRKCMP JNZ NOMATCH ; SAME DISK, SAME TRACK, SAME BUFFER? LDA SEKHST LXI H,HSTSEC ;SEKHST = HSTSEC? CMP M JZ MATCH ;SKIP IF 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: ; MAY HAVE TO FILL THE HOST BUFFER LDA SEKDSK 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 MATCH: ; 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. PUSH B ;SAVE REG LDA SECMSK MOV B,A LDA SEKSEC ;MASK BUFFER NUMBER ANA B ;LEAST SIGNIF BITS POP B ;RESTORE BC CONTENTS MOV L,A ;READY TO SHIFT MVI H,0 ;DOUBLE COUNT DAD H DAD H DAD H DAD H DAD H DAD H DAD H ; HL HAS RELATIVE HOST BUFFER ADDRESS LXI D,HSTBUF DAD D ;HL = HOST ADDRESS XCHG ;NOW IN DE LHLD DMAADR ;GET/PUT CP/M DATA MVI C,128 ;LENGTH OF MOVE LDA READOP ;WHICH WAY? ORA A JNZ RWMOVE ;SKIP IF READ ; WRITE OPERATION, MARK AND SWITCH DIRECTION MVI A,1 STA HSTWRT ;HSTWRT = 1 XCHG ;SOURCE/DEST SWAP RWMOVE: ; C INITIALLY 128, DE IS SOURCE, HL IS DEST LDAX D ;SOURCE CHARACTER INX D MOV M,A ;TO DEST INX H DCR C ;LOOP 128 TIMES JNZ RWMOVE ; 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 TRKCMP: XCHG LXI H,SEKTRK LDAX D ; LOW BYTE COMPARE CMP M ; SAME? RNZ ; RETURN IF NOT INX D ; NEXT.. INX H ; ..BYTES LDAX D ; HIGH ORDER COMPARE CMP M ; SETS FLAGS RET ; ZERO FLAG IF EQUAL 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 ; ;-------------------------------------------------------------------- ; ; 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 JNZ RWFAIL ;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 D ; Get current media type CALL GETTYPE ; . POP D ; . 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 JNZ RWFAIL ; Try again if failed RET ; Otherwise return RDS: MVI A,FREADS ; Indicate read sector CALL RDATA ; Read sector JNZ RWFAIL ; 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 JNZ 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 JNC WRDONE ; Exit if no busy bit RAR ; Test DRQ bit JNC 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 JNZ 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 JNC RDDONE ; Exit if no busy bit RAR ; Test DRQ bit JNC 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 JNZ 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,19 ; Wait 66.5 us for cmd to set up OC1: DCR A ; . JNZ 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 H ; Save buffer pointer PUSH PSW ; . and command IN STAT ; Check motor on bit RAL ; . JC MOTOROK ; Motor already on ... 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 ; . MOTOROK: POP PSW ; Get cmd and buffer ptr back POP H ; . 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 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 RZ ; . both disk & drive present) MVI C,BELL ; Beep if no disk CALL CONOUT ; . CALL CONST ; Check console status CNZ CONIN ; 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? JNZ NEXT$BLIP ; No, try again UABORT: XRA A ; Return with error DCR A ; . 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 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 JZ GOTSDS 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 JZ GSEXIT ;SIDE ZERO, IGNORE 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: ;GET THE DMA ADDRESS CALL GETTYPE 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 # JNC GETTRK1 ; Not DS -- leave track alone RAR ; Divide track by 2 to get side JNC GETTRK1 ; 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 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 LDA IOBYTE ANI 11B JZ TTYIN JMP CRTIN CONOUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C LDA IOBYTE ANI 11B JZ TTYOUT JMP CRTOUT READER: ;READ CHARACTER INTO REGISTER A FROM READER DEVICE JMP TTYIN ;NO CHOICES SUPPORTED PUNCH: ;PUNCH CHARACTER FROM REGISTER C JMP TTYOUT ;NO CHOICES SUPPORTED LISTST: ;RETURN LIST STATUS, 0FFH IF READY, ELSE 00H LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11000000B ;KEEP LST BITS ONLY JNZ LSTST1 ;TTY? JMP TTYOST ;CHECK TTY OUTPUT STATUS LSTST1: ANI 01000000B ;CRT? JZ LPTST ;NO OTHER CHOICE BUT LPT JMP CRTOST ;CHECK CRT OUTPUT STATUS LIST: ;LIST CHARACTER OUTPUT FROM REGISTER C CALL LISTST ;WAIT TILL READY TO SEND JZ LIST LDA IOBYTE ;CHECK DEVICE ASSIGNMENT ANI 11000000B ;KEEP LST BITS ONLY JZ TTYOUT CPI 01000000B JZ CRTOUT JMP LPTOUT ;NO FOURTH 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 JZ CRTRDY ;IF 0, NO H/S 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: CALL CRTOST ;OK TO SEND? JZ CRTOUT ;NOPE, WAIT MOV A,C ;CHARACTER TO REGISTER A OUT SIODPA RET CRTIN: CALL CRTIST ;READY? JZ CRTIN ;NOPE, WAIT IN SIODPA ;ELSE FETCH IT ANI 7FH ;STRIP PARITY BIT RET LPTOUT: ;PRINTER CHARACTER OUTPUT FROM REGISTER C CALL LPTST ;PRINTER READY? JZ LPTOUT ;NOPE, 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 JZ TTYRDY ;IF 0, 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: CALL TTYOST ;OK TO SEND? JZ TTYOUT ;NOPE, WAIT MOV A,C ;CHARACTER TO REGISTER A OUT SIODPB RET TTYIN: IN SIOCPB ANI RDA ;CHAR READY? JZ TTYIN ;NOPE, WAIT IN SIODPB ;ELSE FETCH IT ANI 7FH ;STRIP PARITY BIT RET MAINEND EQU $ ; End of main body of code IF HARD$DISK ; ORG ($+127)/128*128 ; ORG HD stuff on sector boundry ; * * * * * * * * ; ; 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. ; ; * * * * * * * * ; # of --mask-- disk # of rsv'd chk trk ; sect bs bm em size d ent block siz ofs x HD$DPB$BASE: EQU $ 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 DRVR3: ; * * * * * * * * ; ; Hard disk driver (Driver #3) ; ; FF Select Disk ; 00 Write Disk ; 01 Read Disk ; ; * * * * * * * * PUSH PSW ; Save command 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 POP PSW ; Restore command CPI 0FFH ; Select command? JNZ SCSI$IO ; If not, use common R/W JMP SELEND ; Otherwise finish select code ; * * * * * * * * ; ; 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) ; * * * * * * * * ; ; 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 CPI 0 JZ SCSI$WR ; ; Read from the hard disk described by (TARGET) and (LOGUNIT) ; SCSI$RD: MVI A,SCSI$RD$CMD ; Set read command JMP SCSI$DO$RW ; ; Write to the hard disk described by (TARGET) and (LOGUNIT) ; SCSI$WR: MVI A,SCSI$WR$CMD ; Set write 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,SCSI$IO$COUNT ; And the # of bytes to transfer MOV M,C ; . at one time (each block) CALL SELECT ; Perform the SCSI operation LDA STATUS ; Get the return status CPI 0FFH ; Timeout? JZ SCSI$DONE ; Yes, go save timeout status ANI 2 ; Check for SCSI error status JZ SCSI$DONE ; No error -- finish up PUSH PSW ; Save previous command ending status LXI H,SCSI$STAT$CMD ; Set up data and command pointers LXI D,SCSI$STAT$DAT ; . for request sense command LDA LOGUNIT ; Get logical unit number INX H ; Stuff in proper location in MOV M,A ; . SCSI command DCX H ; . LDA TARGET ; Reclaim target address CALL SCSI ; Execute request sense command POP PSW ; Get previous command status back SCSI$DONE: STA ERFLAG ; Save ending status 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: ENDIF IF HARD$DISK AND ARBITRATION xra a out ncricr ; Clear initiator command register out ncrmr ; . and mode register out ncrtcr ; . and target command register clear$arbit: in ncrmr ; Reset arbitration bit ani 0feh ; . out ncrmr ; . arbitrate: lda my$id ; Assert my ID (the initiator) out ncrodr ; . in ncrmr ; Set arbitration bit ori 01h ; . out ncrmr ; . in$progress: in ncricr ; Wait for "arbitration in ani 40h ; . progress" bit jz in$progress nop ; Arbritration delay nop in ncricr ; Check for lost arbitration ani 20h ; . jnz clear$arbit ; We lost -- start over 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,0ch ; Assert SEL and BSY out ncricr ; . nop ; Wait 1 bus clear delay 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 mvi a,0dh ; Assert data bus out ncricr ; . (along with SEL & BSY) in ncrmr ; Reset arbitration bit ani 0feh ; . out ncrmr ; . mvi a,05h ; Release BSY, keep SEL out ncricr ; . and assert data bus nop 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 mvi c,20 ; 200 us loop last$chance: in ncrcsbs ; Wait for BSY ani busbsy ; . jnz select$ok ; Just in time! mvi a,0ffh ; Select failed -- set error status jmp all$done ; and clear the registers select$ok: xra a ; Set good status ENDIF IF HARD$DISK AND (NOT ARBITRATION) xra a out ncrtcr ; Clear target cmd register lda target out ncrodr ; Set target ID mvi a,01h out ncricr ; Enable data bus mvi a,05h out ncricr ; Enable select, also push d ; Save data pointer lxi d,1000h ; Set time out counter wait$bsy$loop: dcx d ; We only wait so long ... mov a,d ; . ora e ; . jz wait$timeout ; Too late -- return timeout status in ncrcsbs ; Get current SCSI bus status ani ncrbsy ; Check for BUSY status jz wait$bsy$loop ; Not BUSY yet -- try again pop d ; Restore data pointer xra a jmp all$done ; Selected ok -- clean up wait$timeout: pop d ; Restore data pointer dcr a ; Was zero, now make 0FFH sta status ; Save in current status ENDIF IF HARD$DISK ; continue hard disk code all$done: sta status ; Save status for PWIDIR routine push psw mvi a,01h ; Release SEL out ncricr ; . xra a ; Release data bus out ncricr ; . pop psw ; Get status back ora a ; Set status rnz ; * * * * * ; * --------\ NOTE: we fall through to PHASE if we ; * --------/ successfully selected the controller!! ; * * * * * ; ; Next SCSI bus phase operator ; PHASE: XRA A OUT NCRMR ; Clear mode register OUT NCRICR ; . and initiator command register ; Wait for REQ active, watch for BSY loss. ; The new phase is not valid until REQ occurs. PH1: IN NCRCSBS ; Get current bus status MOV B,A ; . (Save in B) ANI NCRBSY ; Make sure we still have BSY RZ ; If BSY drops, get out. MOV A,B ; . (Get status back from B) ANI NCRREQ ; REQ active? JZ PH1 ; No -- wait for REQ active MOV A,B ; Update phase ANI 00011100B ; Mask all but phase bits RAR ; Rotate over for target MOV E,A ; . (Save for use with jump table) RAR ; . ANI 0FH ; Mask off carry, just in case OUT NCRTCR ; Set phase MOV A,B ; Get status back from B again MVI D,0 ; E is already set (6 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 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: RET ; Generalized SCSI write routine WSCSI: MVI A,1 ; Assert data bus OUT NCRICR MVI A,00000010B ; Set DMA mode OUT NCRMR MVI D,01000000B ; DMA request mask (for use below) 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: MOV A,E ; Are we in byte mode (CMD XFER)? ORA A ; . JNZ WSCI11 ; Yes, HL = cmd string pointer SHLD DATPTR ; Save (new) data ptr for multi-sector WSCI11: IN NCRBSR MOV C,A ; Save status for use below ANA D ; Check for DMA request JZ WSCSI2 ; This is the heart of the pseudo-DMA transfer. On entry, HL points ; to the data buffer and B 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 MVI C,NCRDACK ; Set up destination port address DB 0EDH ; ED B3 = OTIR instruction op code DB 0B3H JMP WSCSI1 ; Write more bytes until phase changes ; This code skipped when data is being transferred ... WSCSI2: MOV A,C ; Check phase ANI 00001000B ; . JNZ WSCSI1 ; . JMP PHASE ; Exit when phase changes ; Generalized SCSI read routine RSCSI: ; INITIATOR COMMAND REG IS ALREADY INITIALIZED, BY PHASE MVI A,00000010B ; Set DMA mode OUT NCRMR MVI D,01000000B ; DMA request mask (for use below) OUT NCRSDIR ; Start DMA initiator receive ; 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: SHLD DATPTR ; Save (new) data ptr for multi-sector IN NCRBSR MOV C,A ; Keep for phase change checking ANA D ; Mask for DMA request JZ RSCSI2 ; This is the heart of the pseudo-DMA transfer. On entry, HL points ; to the data buffer and B 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 MVI C,NCRDACK ; Set up source port address DB 0EDH ; ED B2 = INIR instruction op code DB 0B2H 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,C ; Check phase ANI 00001000B ; . JNZ RSCSI1 ; . JMP PHASE ; Exit when phase changes 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 XRA A ; Set SCSI byte/block mode to block STA HD$BYTE$BLOCK ; . 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 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 NOT7: XRA A ; Clear 5380 registers (SCSI init) OUT NCRICR ; . OUT NCRMR ; . OUT NCRTCR ; . OUT NCRSER ; . ENDIF ; End of HD initialization LXI H,LOGMSG ; Display signon message CALL PUTS 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','k CP/M vers 2.2' DB CR,LF,'AMPRO BIOS VERS ',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,1984,1985 ' 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 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 ' ' ; DS 128-($-ENV) ; Make 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 LAST$SG EQU $ ORG BOOT UNINIT EQU $ 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 MUNACT: DS 1 ; UNALLOCATED COUNT VALUE 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 HD$BYTE$BLOCK DS 1 ; 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 ; 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 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 HD$CURRENT EQU $ ; Current Bios Buffer Area ptr ; * * * * * * * * ; ; 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 ; ; * * * * * * * * IF HARD$DISK HD$ALV EQU (1279/8)+1 ; Maximum of 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 ENDIF IF NOT HARD$DISK ALVF: DS 0 ; If this is a floppy only system, ALVG: DS 0 ; define the hard disk vectors (to ALVH: DS 0 ; prevent assembly errors). ALVI: DS 0 ALVJ: DS 0 ALVK: DS 0 ALVL: DS 0 ALVM: DS 0 ALVN: DS 0 ALVO: DS 0 ALVP: DS 0 ENDIF 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