; LAST UPDATED ON: 02 DECEMBER 83 -- Ver 2.2i (Release is N) ; REASON FOR UPDATE: Fixed bug with 4 M-DRIVE/Hs llo ; 10 OCT 83, Ver 2.2M Changed DISK3 for new firmware. Change M-DRIVE/H ; to be consistent with other CompuPro OS's llo ; 10 JUN 83, Ver 2.2i Add DISK3 driver as per initial specs. aep ; 25 MAY 83, Ver 2.2h Add format structure to sector xlate table. Move I/O ; initialization to the boot loader to achieve invariance ; against CBIOS size variations. aep ; 03 MAY 83, Ver 2.2g Continued debug and streamline of DISK2 driver with ; addition for multiple drives. BIOS split to permit ; boot from floppy of hard disk drivers in addition to ; full I/O compliment (loaded by file in "STARTUP.SUB" ; sequence.) aep ; 24 MAR 83, Ver 2.2f Update M-Drive/H with message, restructure hard disk ; driver for faster algorythm including relocation of up ; to one track's worth of "bad media" sectors. aep ; 02 JAN 83, Ver 2.2e General debug of M-DRIVE/H. aep ; 16 NOV 82, Ver 2.2e Add M-DRIVE/H driver. aep ; 10 OCT 82, Ver 2.2d Add DISK2 controller software to match existing ; but without the crazy sector relocation algorythm. aep ; 15 JUL 82, Ver 2.2L Modify to accomodate use of boot switch again, ; eliminate bit banger. aep ; 16 MAY 82, Ver 2.2LXmMN Restructure code (massive overhaul) to permit ; interrupt handler overlay. aep ; 25 MAR 82, Ver 2.2LXM Add mini-drives, M-BOOT, fix SPECIFY, ; full I/O byte implimentation. aep ; ; PROGRAM NAME: HMX1BIOS.ASM -- Customized BIOS for CP/M-80 version 2.2, ; developed for use with CompuPro Systems Components. ; ; ========================== Copyright 1983, CompuPro Corporation. ; || || ; || HMX1BIOS.ASM || ; || || ; ========================== ; ; This product is a copyright program product of CompuPro and is ; supplied for use with the CompuPro Computer Systems. ; ; CONSTANTS: VERS EQU 22 ;CP/M version number CBIOSV EQU 'N' ;CBIOS revision level (2.2x) (CompuPro level) ; ; LIBRARY CONSTANTS: MACLIB COMPUPRO ;Disk and Serial/Parallel interface constants MACLIB ASCII ;Mnemonics for common ASCII, other special characters MACLIB ACTIVE ;Flags directing construction for the various ;CompuPro products to "customize" the BIOS MACLIB CPMDISK ;CP/M disk defaults, CBIOS offsets, BDOS functions MACLIB BOOTSCPM ;CP/M cold/warm boot routines for each of the ;possible controller types ; ; Footnote #1. PAGE ; PROGRAM: ; ; The next statement produces a harmless error message if MAC is used instead. ASEG ;Used Digital Research RMAC assembler and ORG BIOS ;LINK linker to assemble this code ; JMP CBOOT ;+00h Cold boot LOADVEC EQU BIOS+1 ;Base vector address of interrupt or other code loaded ;by CP/M to extend the CBIOS beyond what the boot track ;can hold -- value stored here at end of cold boot. JMP WBOOT ;+03h Warm boot CIS: JMP CONST ;+06h Console status (input) CI: JMP CONIN ;+09h Console input CO: JMP CONOUT ;+0Ch Console output JMP LISTOUT ;+0Fh List output JMP PUNCH ;+12h Punch output JMP READER ;+15h Reader input JMP HOME ;+18h Set track to zero JMP SELDSK ;+1Bh Select disk unit JMP SETTRK ;+1Eh Set track JMP SETSEC ;+21h Set sector JMP SETDMA ;+24h Set Disk Memory Address JMP READ ;+27h Read from disk JMP WRITE ;+2Ah Write onto disk JMP LISTST ;+2Dh List status (output) JMP SECTRN ;+30h Translate sector number JMP SETNUM ;+33h Set number of sectors to R/W (dangerous) JMP SETXAD ;+36h Set extended address (unblocked data only) ; ; Footnote #2. ; ;**************************************************************** ;* DISK LOGICAL TO PHYSICAL SECTOR TRANSLATION TABLES * ;**************************************************************** if FLOPPY8 ; GPL1, GPL2, Physical sectors/track X8TABLE:DW XLT8S0 ;Single 128 DB 07h, 1Bh, 26, 0 ;Format information "header" DW XLT8D1 ;Double 256 DB 0Eh, 36h, 26, 0 DW XLT8D2 ;Double 512 DB 1Bh, 54h, 15, 0 DW XLT8D3 ;Double 1024 DB 35h, 74h, 08, 0 ;--------------------------------------- XLT8S0: DB 0,6,12,18,24,4,10,16,22,2,8,14,20 DB 1,7,13,19,25,5,11,17,23,3,9,15,21 ;--------------------------------------- XLT8D1: DB 0, 1,18,19,36,37, 2, 3,20,21,38,39 DB 4, 5,22,23,40,41, 6, 7,24,25,42,43 DB 8, 9,26,27,44,45,10,11,28,29,46,47 DB 12,13,30,31,48,49,14,15,32,33,50,51 DB 16,17,34,35 ;--------------------------------------- XLT8D2: DB 0, 1, 2, 3,16,17,18,19 DB 32,33,34,35,48,49,50,51 DB 4, 5, 6, 7,20,21,22,23 DB 36,37,38,39,52,53,54,55 DB 8, 9,10,11,24,25,26,27 DB 40,41,42,43,56,57,58,59 DB 12,13,14,15,28,29,30,31 DB 44,45,46,47 ;--------------------------------------- XLT8D3: DB 0, 1, 2, 3, 4, 5, 6, 7 DB 24,25,26,27,28,29,30,31 DB 48,49,50,51,52,53,54,55 DB 8, 9,10,11,12,13,14,15 DB 32,33,34,35,36,37,38,39 DB 56,57,58,59,60,61,62,63 DB 16,17,18,19,20,21,22,23 DB 40,41,42,43,44,45,46,47 endif ;======================================= if FLOPPY5 ; GPL1, GPL2, Physical sectors/track X5TABLE:DW XLT5S0 ;Single 128 DB 08h, 10h, 16, 0 ;Format information ; DB 07h, 09h, 18, 0 ;Optional replacement (tight) DW XLT5D1 ;Double 256 DB 10h, 19h, 16, 0 ; DB 07h, 09h, 18, 0 ;Optional replacement (tight) DW XLT5D2 ;Double 512 DB 2Ah, 50h, 08, 0 DW XLT5D3 ;Double 1024 ; DB 2Ah, 50h, 04, 0 ;Optional replacement (loose) DB 20h, 32h, 05, 0 ;--------------------------------------- XLT5S0: DB 0, 5,10,15, 4, 9,14, 3 ; 0, 5,10,15, 2, 7,12,17 DB 8,13, 2, 7,12, 1, 6,11 ; 4, 9,14, 1, 6,11,16, 3 DB 00,00 ; 8,13 ;--------------------------------------- XLT5D1: DB 0, 1,10,11,20,21,30,31 ; 0, 1,10,11,20,21,30,31 DB 8, 9,18,19,28,29 ; 4, 5,14,15,24,25,34,35 DB 6, 7,16,17,26,27 ; 8, 9,18,19,28,29 DB 4, 5,14,15,24,25 ; 2, 3,12,13,22,23,32,33 DB 2, 3,12,13,22,23,00,00,00,00 ; 6, 7,16,17,26,27 ;--------------------------------------- XLT5D2: DB 0, 1, 2, 3,16,17,18,19 DB 12,13,14,15,28,29,30,31 DB 8, 9,10,11,24,25,26,27 DB 4, 5, 6, 7,20,21,22,23 DB 00,00,00,00,00,00,00,00 ;Additional space for other allocations ;--------------------------------------- XLT5D3: DB 0, 1, 2, 3, 4, 5, 6, 7 DB 16,17,18,19,20,21,22,23 DB 32,33,34,35,36,37,38,39 DB 8, 9,10,11,12,13,14,15 DB 24,25,26,27,28,29,30,31 endif PAGE ;**************************************************************** ;* Macro for generating the Disk Parameter Blocks. * ;**************************************************************** ; ; Control Blocks (DPB's in CP/M-eze) for disk drives: ; ; MAKEDPB Name, Type, Heads, Sectors/Track, Records(128 bytes)/sector, ; Reserved cylinders, Ending cylinder number (starting at 1), ; Allocation block size (in bytes from 1024 to 16*1024) ; Directory entries, # of directory entries to be checked ; (and optional),0 will force 16K extents for CP/M 1.4 compatibility ; MAKEDPB MACRO ?NM,TY,HDS,SPT,SIR,?F,?L,BLS,NDIR,NCKS,K16 BSF SET 3 ;;Init block shift factor EXTMSK SET 0 ;;Init extent mask ACCU SET BLS/1024 REPT 6 ;;6 bit shift max if ACCU = 1 EXITM endif BSF SET BSF+1 ;;Bump block shift factor EXTMSK SET 1+(EXTMSK*2);;Shift in a bit from right ACCU SET ACCU/2 ;;Shift 1 bit right until in zero position ENDM SIB SET BLS/128 ;;Sectors in block DSMC SET ((((?L*HDS)-(?F))*SPT*SIR)+SIB-1)/SIB ;;Maximum data blocks if DSMC > 256 EXTMSK SET EXTMSK/2 ;;Shift out one for double byte allocation endif if not NUL K16 ;;optional [0] in last position EXTMSK SET K16 ;;forces 16K extent mask endif DIRBLK SET 8000h ;;Init allocation block for directory ADIR SET BLS/32 ;;32 bytes/dir entry ACCU SET NDIR ;;Set accumulator to number of dir entries REPT 15 ;;16 Bits of directory alloc max ACCU SET ACCU-(ADIR) ;;32 bytes per directory entry if ACCU <= 0 ;;Done when all accounted for EXITM endif ;;Subtract # of entries in one block DIRBLK SET (DIRBLK/2)+8000h;;Shift in one bit from the left ENDM if (TY > 40h) and (TY < 80h) DIRBLK SET 0FFFFh ;;Reserve all dir blocks for hard disks endif ;; ;;---- Disk Parameter Block Generated ---- ;; ;; Disk type definition blocks for each particular mode. ;; The produced format of these areas are as follows: ;; ;; 8 bit = disk type code (not standard CP/M) ;; 16 bit = Sectors per track (real start of DPB) ;; 8 bit = Block shift factor (block size in 128 byte segments) ;; 8 bit = Block Shift mask ;; 8 bit = Extent mask (a somewhat magical number best left alone) ;; 16 bit = Disk size in blocks (less 1 -- for expansion purposes) ;; 16 bit = Directory size. (less 1 in case size = 64K) ;; 16 bit = Allocation for directory. (vector to bitmap for this drive) ;; 16 bit = Check size. (# of 128 byte directory segments for checksums) ;; 16 bit = Offset to first track. (# of reserved cylinders to this drive) DB TY ;;Disk type identifier DPB&?NM DW SPT * SIR ;;Sectors/track DB BSF, SIB-1, EXTMSK ;;Block size factor, records/blk, mask DW DSMC-1, NDIR-1 ;;Disk size (blocks), Directory entries DB high DIRBLK, low DIRBLK ;;Directory allocation reserved blocks DW (NCKS+3)/4, ?F ;;Checksum vector size, reserved tracks ?NM&CSVZ EQU (NCKS+3)/4 ;;Checksum vector size ?NM&ALVZ EQU (DSMC+7)/8 ;;Max allocation vector size (bytes = blocks / 8 bits) ENDM ; DPB8TBL: ;8 inch floppy disk DPB table MAKEDPB F8S0, 00h, 1,26,1, 2,77, 1024, 64, 64 MAKEDPB F8S1, 01h, 2,26,1, 2*2,77, 1024, 128,128 MAKEDPB F8D2, 02h, 1,26,2, 2,77, 2048, 128,128, 0 ;16K extents MAKEDPB F8D3, 03h, 2,26,2, 2*2,77, 2048, 256,256 MAKEDPB F8D4, 04h, 1,15,4, 2,77, 2048, 128,128 MAKEDPB F8D5, 05h, 2,15,4, 2*2,77, 2048, 256,256 MAKEDPB F8D6, 06h, 1, 8,8, 2,77, 2048, 128,128 MAKEDPB F8D7, 07h, 2, 8,8, 2*2,77, 2048, 256,256 FD8CSVZ EQU F8D7CSVZ ;Max checksum vector size (4 dir entries / record) FD8ALVZ EQU F8D7ALVZ ;Max allocation vector size (bytes = blocks / 8 bits) DPBFD8 EQU DPBF8D7 ;DPB with largest size alloc, checksum vectors ; if FLOPPY5 DPB5TBL: ;5 1/4 inch floppy disk DPB table ; TYPE base = 20h, then 77 tracks ; TYPE base = 28h, then 40 tracks ; TYPE base = 30h, then 80 tracks MAKEDPB F5S0, BIAS5+0, 1,16,1, 2,TRK5, 1024, 64, 64 MAKEDPB F5S1, BIAS5+1, 2,16,1, 2*2,TRK5, 1024, 128,128 MAKEDPB F5D2, BIAS5+2, 1,16,2, 2,TRK5, 2048, 128,128, 0 ;16K extents MAKEDPB F5D3, BIAS5+3, 2,16,2, 2*2,TRK5, 2048, 256,256 MAKEDPB F5D4, BIAS5+4, 1, 8,4, 2,TRK5, 2048, 128,128 MAKEDPB F5D5, BIAS5+5, 2, 8,4, 2*2,TRK5, 2048, 256,256 MAKEDPB F5D6, BIAS5+6, 1, 5,8, 2,TRK5, 2048, 128,128 MAKEDPB F5D7, BIAS5+7, 2, 5,8, 2*2,TRK5, 2048, 256,256 FD5CSVZ EQU F5D7CSVZ ;Max checksum vector size (4 dir entries / record) FD5ALVZ EQU F5D7ALVZ ;Max allocation vector size (bytes = blocks / 8 bits) DPBFD5 EQU DPBF5D7 ;DPB with largest size alloc, checksum vectors endif ; if DISK2 ;Disk 2 hard disk controller active. if D2M10 ;Memorex (Fujitsu) 10 Mbyte drive MAKEDPB D2N1, 43h, 4,11,8, 4*1,123, 4096, 1024,0 ;First 5M MAKEDPB D2N2, 43h, 4,11,8, 4*123,243, 4096, 1024,0 ;Second 5M+ endif if D2M20 ;Memorex (Fujitsu) 20 Mbyte drive MAKEDPB D2N1, 43h, 8,11,8, 8*1, 94, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 8,11,8, 8*94,187, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 8,11,8, 8*187,243, 4096, 1024,0 ;Third 4M+ endif if D2F20B ;Fujitsu "BE" 20 Mbyte drive MAKEDPB D2N1, 43h, 4,22,8, 4*1, 94, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 4,22,8, 4*94,187, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 4,22,8, 4*187,243, 4096, 1024,0 ;Third 4M+ endif if D2M26 ;Shugart 26 Mbyte drive MAKEDPB D2N1, 43h, 8,16,8, 8*1, 57, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 8,16,8, 8*57,113, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 8,16,8, 8*113,169, 4096, 1024,0 ;Third 8M MAKEDPB D2N4, 43h, 8,16,8, 8*169,202, 4096, 1024,0 ;Fourth 2M+ endif if D2F40B ;Fujitsu "BE" 40 Mbyte drive MAKEDPB D2N1, 43h, 8,22,8, 8*1, 47, 4096, 1024,0 ;First 8M MAKEDPB D2N2, 43h, 8,22,8, 8*47, 93, 4096, 1024,0 ;Second 8M MAKEDPB D2N3, 43h, 8,22,8, 8*93,139, 4096, 1024,0 ;Third 8M MAKEDPB D2N4, 43h, 8,22,8, 8*139,185, 4096, 1024,0 ;Fourth 8M MAKEDPB D2N5, 43h, 8,22,8, 8*185,241, 4096, 1024,0 ;Fifth 8M endif endif ; if DISK3 ;Disk 3 hard disk controller active. if D3ST506 ;Seagate 5 Mbyte drive (ST506) MAKEDPB D3N1, 53h, 4,9,8, 1,153, 4096, 1024,0 ;First 5M endif if D3M20 ; 20 Mbyte drive MAKEDPB D3N1, 53h, 8,9,8, 1, 57, 4096, 1024,0 ;First 8M MAKEDPB D3N2, 53h, 8,9,8, 57,113, 4096, 1024,0 ;Second 8M MAKEDPB D3N3, 53h, 8,9,8, 113,153, 4096, 1024,0 ;Third 4M+ endif if D3Q540 ; 40 Mbyte Quantum drive MAKEDPB D3N1, 53h, 8,9,8, 2, 72, 4096, 1024,0 ;First 5M MAKEDPB D3N2, 53h, 8,9,8, 2+(8*75),180, 4096, 1024,0 ;Second 7.4M MAKEDPB D3N3, 53h, 8,9,8, 2+(8*180),285, 4096, 1024,0 ;Third 7.4M MAKEDPB D3N4, 53h, 8,9,8, 2+(8*285),390, 4096, 1024,0 ;Fourth 7.4M MAKEDPB D3N5, 53h, 8,9,8, 2+(8*390),492, 4096, 1024,0 ;Fourth 7.3M MAKEDPB D3N6, 53h, 8,9,8, 2+(8*492),509, 2048, 256,0 ;Sixth 1.2M endif endif ; ; Memory Drives: ; if XMDRIVE ;M-drive using 8088 extended addressing MAKEDPB MEM, MEMTYPE, 1,239,1, 0,32, 2048, 128,0, 0 ;16K ext endif ; if HMDRIVE ;M-DRIVE/H dynamic RAM disk drive HM$FTRK EQU 4 ;32 Sectors/Board = reserved parity area = tracks 0-3 HM$DIRS EQU ((HM$NUM+1)/2)*128 ;Directory entries to allocate if HM$NUM gt 4 MAKEDPB HMD, HMDTYPE, 1,8*HM$NUM,1, HM$FTRK,256, 2048, HM$DIRS,0 else MAKEDPB HMD, HMDTYPE, 1,8*HM$NUM,1, HM$FTRK,256, 2048, HM$DIRS,0 endif endif ; PAGE ;************************************************************************ ;* Macro for generating Control Headers for disk drives (DPH's): * ;************************************************************************ ; ; Set all letter drives to not in use status. IRPC ?X,ABCDEFGHIJKLMNOP DRV&?X SET 0 ALVZ&?X SET 0 CSVZ&?X SET 0 ENDM ; ; The produced format of these Disk Parameter Headers are as follows: ; 8 bits = Mask for drive selection (drive dependent and not std CP/M) ; 16 bits = -> translation table. (real start of DPH) ; 48 bits = Work area for CP/M. ; 16 bits = -> DIRBUF. ; 16 bits = -> Parameter block. ; 16 bits = -> check vector. ; 16 bits = -> allocation vector. ; ; To generate a DPH, use the format: ; MAKEDPH Drive letter, Physical drive selection mask, ; Initial Sector translation table for that drive, ; Initial DPB call letter name (see first entry of MAKEDPB). ; MAKEDPH MACRO ?DR,?MASK,?XLT,?DPBN ;; ?DR = disk name [A,B,C,...,P] ;; ?MASK = disk selection mask for port control ;; ?XLT = sector translation table address ;; ?DPBN = code name for largest corresponding DPB DRV&?DR SET $ DB ?MASK ;;Address of this drive letter's DPH DPH&?DR DW ?XLT ;;Sector translation table DW 0000h, 0000h, 0000h ;;Scratchpad for CP/M DW DIRBUF, DPB&?DPBN ;;Dir buffer, disk parameter block DW CSV&?DR, ALV&?DR ;;Checksum vector, Allocation vector start CSVZ&?DR SET ?DPBN&CSVZ ;;Set checksum vector reserve size ALVZ&?DR SET ?DPBN&ALVZ ;;Set allocation vector reserve size ENDM ; if not (DISK2 or DISK3) ;=1 ; Floppy disks are only permanent storage. if not (FLOPPY5 and BOOT5X) ;=2 MAKEDPH A, 00h, XLT8D3, FD8 ;Drive A: 8 inch floppy MAKEDPH B, 01h, XLT8D3, FD8 ;Drive B: 8 inch floppy if FPY8X4 ;If 4 drives are present ;=3 MAKEDPH C, 02h, XLT8D3, FD8 ;Drive C: 8 inch floppy MAKEDPH D, 03h, XLT8D3, FD8 ;Drive D: 8 inch floppy endif ;=3 ; if FLOPPY5 ;=3 MAKEDPH E, 00h, XLT5D3, FD5 ;Drive E: 5 inch floppy MAKEDPH F, 01h, XLT5D3, FD5 ;Drive F: 5 inch floppy if FPY5X4 ;If 4 drives are present ;=4 MAKEDPH G, 02h, XLT5D3, FD5 ;Drive G: 5 inch floppy MAKEDPH H, 03h, XLT5D3, FD5 ;Drive H: 5 inch floppy endif ;=4 endif ;=3 ; else ;Booting from 5 1/4 inch disks ;=2 ; MAKEDPH A, 00h, XLT5D3, FD5 ;Drive A: 5 inch floppy MAKEDPH B, 01h, XLT5D3, FD5 ;Drive B: 5 inch floppy if FPY5X4 ;If 4 drives are present ;=3 MAKEDPH C, 02h, XLT5D3, FD5 ;Drive C: 5 inch floppy MAKEDPH D, 03h, XLT5D3, FD5 ;Drive D: 5 inch floppy endif ;=3 ; if FLOPPY8 ;=3 MAKEDPH E, 00h, XLT8D3, FD8 ;Drive E: 8 inch floppy MAKEDPH F, 01h, XLT8D3, FD8 ;Drive F: 8 inch floppy if FPY8X4 ;If 4 drives are present ;=4 MAKEDPH G, 02h, XLT8D3, FD8 ;Drive G: 8 inch floppy MAKEDPH H, 03h, XLT8D3, FD8 ;Drive H: 8 inch floppy endif ;=4 endif ;=3 endif ;=2 ; else ;=1 ; Hard Disks exist, and should be used for drive A: if D2M10 ;If Memorex 10 megabyte drive ;=2 D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 8 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 8 inch hard disk, select #0, 2M+ if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'C' - 'A' ;Unit #1 base drive ID select MAKEDPH C, 21h, 00, D2N1 ;Drive C: 8 inch hard disk, select #1, 8M MAKEDPH D, 21h, 00, D2N2 ;Drive D: 8 inch hard disk, select #1, 2M+ endif ;=3 if DISK2Y ;If third drive present ;=3 D2UNIT2 EQU 'E' - 'A' ;Unit #2 base drive ID select MAKEDPH E, 42h, 00, D2N1 ;Drive E: 8 inch hard disk, select #2, 8M MAKEDPH F, 42h, 00, D2N2 ;Drive F: 8 inch hard disk, select #2, 2M+ endif ;=3 if DISK2Z ;If fourth drive present ;=3 D2UNIT3 EQU 'G' - 'A' ;Unit #3 base drive ID select MAKEDPH G, 83h, 00, D2N1 ;Drive G: 8 inch hard disk, select #3, 8M MAKEDPH H, 83h, 00, D2N2 ;Drive H: 8 inch hard disk, select #3, 2M+ endif ;=3 endif ;=2 ; if (D2M20 or D2F20B) ;If Memorex 20 megabyte drive ;=2 ; or Fujitsu "BE" 20 megabyte drive D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 8 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 8 inch hard disk, select #0, 8M MAKEDPH C, 10h, 00, D2N3 ;Drive C: 8 inch hard disk, select #0, 4M+ if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'D' - 'A' ;Unit #1 base drive ID select MAKEDPH D, 21h, 00, D2N1 ;Drive D: 8 inch hard disk, select #1, 8M MAKEDPH E, 21h, 00, D2N2 ;Drive E: 8 inch hard disk, select #1, 8M MAKEDPH F, 21h, 00, D2N3 ;Drive F: 8 inch hard disk, select #1, 4M+ endif ;=3 if DISK2Y ;If third drive present ;=3 D2UNIT2 EQU 'G' - 'A' ;Unit #2 base drive ID select MAKEDPH G, 42h, 00, D2N1 ;Drive G: 8 inch hard disk, select #2, 8M MAKEDPH H, 42h, 00, D2N2 ;Drive H: 8 inch hard disk, select #2, 8M MAKEDPH I, 42h, 00, D2N3 ;Drive I: 8 inch hard disk, select #2, 4M+ endif ;=3 if DISK2Z ;If fourth drive present ;=3 D2UNIT3 EQU 'J' - 'A' ;Unit #3 base drive ID select MAKEDPH J, 83h, 00, D2N1 ;Drive J: 8 inch hard disk, select #3, 8M MAKEDPH K, 83h, 00, D2N2 ;Drive K: 8 inch hard disk, select #3, 8M MAKEDPH L, 83h, 00, D2N3 ;Drive L: 8 inch hard disk, select #3, 4M+ endif ;=3 endif ;=2 ; if D2M26 ;If Shugart 26 megabyte drive ;=2 D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 14 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 14 inch hard disk, select #0, 8M MAKEDPH C, 10h, 00, D2N3 ;Drive C: 14 inch hard disk, select #0, 8M MAKEDPH D, 10h, 00, D2N4 ;Drive D: 14 inch hard disk, select #0, 2M+ if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'E' - 'A' ;Unit #1 base drive ID select MAKEDPH E, 21h, 00, D2N1 ;Drive E: 14 inch hard disk, select #1, 8M MAKEDPH F, 21h, 00, D2N2 ;Drive F: 14 inch hard disk, select #1, 8M MAKEDPH G, 21h, 00, D2N3 ;Drive G: 14 inch hard disk, select #1, 8M MAKEDPH H, 21h, 00, D2N4 ;Drive H: 14 inch hard disk, select #1, 2M+ endif ;=3 if DISK2Y ;If third drive present ;=3 D2UNIT2 EQU 'I' - 'A' ;Unit #2 base drive ID select MAKEDPH I, 42h, 00, D2N1 ;Drive I: 14 inch hard disk, select #2, 8M MAKEDPH J, 42h, 00, D2N2 ;Drive J: 14 inch hard disk, select #2, 8M MAKEDPH K, 42h, 00, D2N3 ;Drive K: 14 inch hard disk, select #2, 8M MAKEDPH L, 42h, 00, D2N4 ;Drive L: 14 inch hard disk, select #2, 2M+ endif ;=3 endif ;=2 ; if D2F40B ;If Fujitsu "BE" 40 megabyte drive ;=2 D2UNIT0 EQU 'A' - 'A' ;Unit #0 base drive ID select MAKEDPH A, 10h, 00, D2N1 ;Drive A: 8 inch hard disk, select #0, 8M MAKEDPH B, 10h, 00, D2N2 ;Drive B: 8 inch hard disk, select #0, 8M MAKEDPH C, 10h, 00, D2N3 ;Drive C: 8 inch hard disk, select #0, 8M MAKEDPH D, 10h, 00, D2N4 ;Drive D: 8 inch hard disk, select #0, 8M MAKEDPH E, 10h, 00, D2N5 ;Drive E: 8 inch hard disk, select #0, 8M if DISK2X ;If second drive present ;=3 D2UNIT1 EQU 'G' - 'A' ;Unit #1 base drive ID select MAKEDPH G, 21h, 00, D2N1 ;Drive G: 8 inch hard disk, select #1, 8M MAKEDPH H, 21h, 00, D2N2 ;Drive H: 8 inch hard disk, select #1, 8M MAKEDPH I, 21h, 00, D2N3 ;Drive I: 8 inch hard disk, select #1, 8M MAKEDPH J, 21h, 00, D2N4 ;Drive J: 8 inch hard disk, select #1, 8M MAKEDPH K, 21h, 00, D2N5 ;Drive K: 8 inch hard disk, select #1, 8M endif ;=3 endif ;=2 ; if DISK3 if D3ST506 MAKEDPH A, 00h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M if DISK3X MAKEDPH B, 01h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M endif endif if D3Q540 MAKEDPH A, 00h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M MAKEDPH B, 00h, 00, D3N2 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH C, 00h, 00, D3N3 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH D, 00h, 00, D3N4 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH E, 00h, 00, D3N5 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH F, 00h, 00, D3N6 ;Drive G: 5 inch hard disk, select #0, 1.2M if DISK3X MAKEDPH G, 01h, 00, D3N1 ;Drive G: 5 inch hard disk, select #0, 5M MAKEDPH H, 01h, 00, D3N2 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH I, 01h, 00, D3N3 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH J, 01h, 00, D3N4 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH K, 01h, 00, D3N5 ;Drive G: 5 inch hard disk, select #0, 7.5M MAKEDPH L, 01h, 00, D3N6 ;Drive G: 5 inch hard disk, select #0, 1.2M endif endif endif ; if FLOPPY8 ;=2 if ((DRVI eq 0) and (DRVJ eq 0)) ;=3 MAKEDPH I, 00h, XLT8D3, FD8 ;Drive I: 8 inch floppy MAKEDPH J, 01h, XLT8D3, FD8 ;Drive J: 8 inch floppy if FPY8X4 ;If 4 drives are present ;=4 MAKEDPH K, 02h, XLT8D3, FD8 ;Drive K: 8 inch floppy MAKEDPH L, 03h, XLT8D3, FD8 ;Drive L: 8 inch floppy endif ;=4 else ;=3 MAKEDPH N, 00h, XLT8D3, FD8 ;Drive N: 8 inch floppy MAKEDPH O, 01h, XLT8D3, FD8 ;Drive O: 8 inch floppy endif ;=3 endif ;=2 ; if FLOPPY5 ;=2 if ((DRVK eq 0) and (DRVL eq 0)) ;=3 MAKEDPH K, 00h, XLT5D3, FD5 ;Drive K: 5 1/4 inch floppy MAKEDPH L, 01h, XLT5D3, FD5 ;Drive L: 5 1/4 inch floppy else ;=3 if ((DRVN eq 0) and (DRVO eq 0)) ;=4 MAKEDPH N, 00h, XLT5D3, FD5 ;Drive N: 5 1/4 inch floppy MAKEDPH O, 01h, XLT5D3, FD5 ;Drive O: 5 1/4 inch floppy endif ;=4 endif ;=3 endif ;=2 endif ;=1 ; ; Memory drives. if HMDRIVE ;M-DRIVE/H present if XMDRIVE ;If extended memory drive, then can't use "M:" if (DRVH eq 0) ;If drive "H:" not already specified MAKEDPH H, 00h, 00, HMD ;Drive H: M-DRIVE/H memory drive else ;Use "N:" instead if so MAKEDPH N, 00h, 00, HMD ;Drive N: M-DRIVE/H memory drive endif else ;Use "M:" if this is the only memory drive MAKEDPH M, 00h, 00, HMD ;Drive M: M-DRIVE/H memory drive endif endif ; if XMDRIVE MAKEDPH M, 00h, 00, MEM ;Drive M: M-DRIVE using 8088 endif PAGE ;---------------------------------------- ; ; HOME ROUTINE: ; ; Return disk to home. This routine sets the track number to zero. ; The current host disk buffer is flushed to the disk, and made inactive. ; HOME: CALL FLUSH ;Flush host buffer if incomplete write CALL HSTOFF ;Clear host active flag LHLD SEKDSK ;Move desired drive, type back to active SHLD ACTDSK LDA SAVSEC ;Restore last desired sector to active status STA ACTSEC LDA SEKGPL ;Restore associated Gap Length for floppies STA ACTGPL LHLD DMAADR ;Move selected transfer address to SHLD BUFADR ;the internal buffer address pointer XRA A ;Clear the extended DMA byte (always bank 0) STA DMAADE STA BUFADE ;and extended buffer DMA address LXI B,0 ;Init to track 0 (theoretical position) ; ;---------------------------------------- ; ; SET TRACK ROUTINE: ; ; Set track number. The track number is saved for later use during ; a disk transfer operation. ; ;Entry: B,C = track number. ; SETTRK: MOV L,C! MOV H,B ;Put track number in "H,L" SHLD ACTTRK ;Save as active track number SHLD SEKTRK ;And as desired (blocking/deblocking) RET ; ;---------------------------------------- ; ; SECTOR TRANSLATION ROUTINE: ; ; Translate sector number from logical to physical. ; ;Entry: D,E = 0, then no translation required, ; else: D,E = translation table address and ; B,C = sector number to translate. ; ;Exit: H,L = translated sector number. ; SECTRN: MOV L,C! MOV H,B ;Offset (or record #) in H,L MOV A,E! ORA D ;See if translation required RZ ;Done if not DAD D ;Add base to offset MOV L,M! MVI H,0 ;Translation is always 1 byte for floppies RET ; ;---------------------------------------- ; ; SET SECTOR ROUTINE: ; ; Set the sector for later use in the disk transfer. No ; actual disk operations are perfomed. ; ;Entry: B,C = sector number (sometimes "B" contains an invalid number ; if less than 256 sectors used for selected drive). ; SETSEC: MOV A,C STA ACTSEC ;Sector to seek STA SAVSEC ;Special save area for blocking/deblocking RET ; ;---------------------------------------- ; ; SET NUMBER OF CONTINUOUS SECTORS TO USE: ; ; Set the number of sectors to be used during a single transfer. This ; is a potentially dangerous value to alter in that the READ and WRITE ; routines will currently only use this value correctly if the disk ; is marked as "non-blocked", or having 128 byte sectors. Unless you ; really have a need to alter this value, leave it at "1". ; ;Entry: C = number of sectors to use in the next disk transfer. ; SETNUM: MOV A,C STA NUMSEC RET ; ;---------------------------------------- ; ; SET DIRECT MEMORY ACCESS (Lower 2 bytes): ; ; Set Direct Memory Address (DMA) for subsequent disk read or ; write routines. This is the place the actual requested 128 ; byte record (CP/M 1.4 sector) goes. ; ;Entry: B,C = Disk memory address. ; SETDMA: MOV H,B! MOV L,C SHLD DMAADR ;Save as Direct Memory Access start byte SHLD BUFADR ;And as desired (blocking/deblocking) RET ; ;---------------------------------------- ; ; SET DIRECT MEMORY ACCESS (Upper byte of 24 bit address): ; ; Set extended bank address of DMA as above, currently inactive due ; to the potential havoc it wreaks if not used very, very carefully. ; ;Entry: C = extended bank address byte. ; SETXAD: MOV A,C ;Get extended address byte from "C" STA DMAADE ;Save in memory RET ; ;---------------------------------------- ; ; Create disk drive "existance" table to the actual drives presesnt. This ; makes a list of the relative DBH's for individual logical drives which occupy ; absolute letter identifications. Those logical drives with no associated ; physical counterpart have their pointers set to zero, indicating an invalid ; or non-existant drive. ; DSKTBL: IRPC ?D,ABCDEFGHIJKLMNOP DW DRV$&?D ENDM ; ; SELECT DISK DRIVE: ; ; Select the disk drive for subsequent disk transfers and return the ; appropriate DPB address. This routine diverges from the normal CP/M ; implementation of just saving the disk selection value until the ; transfer is performed. This divergence is required because floppy ; disks are a removable media and come in more than on format. This ; routine determines the correct format and initializes the DPH with ; the appropriate values in agreement with the format type. ; ;Entry: C = Disk selection value. ; D,E and 1 = 0, ==> Must determine disk type, ; else = 1, ==> Drive type has been determined. ; ;Exit: H,L = 0, If drive not selectable, ; H,L = DPH address if drive is selectable, and is initialized for the ; appropriate floppy disk format if D,E = 0, ; else the DPH pointed to contains data about the last disk accessed, ; (the following are not required by CP/M, but are by the FORMAT utility) ; D,E = DPB address if floppy disk was selected and initialized, ; B,C = Base address of sector translate, format info if floppy selected. ; SELDSK: LXI H,0 ;Indicate not found or drive not active MOV A,C! CPI 16! RNC ;Done if invalid drive specified ADI 'A' ;Add in ASCII bias to drive ID STA NRDYM2 ;Set drive letter into "not ready" message MOV B,H ;Clear upper byte of offset word LXI H,DSKTBL ;Base of disk DPH lookup table DAD B! DAD B ;Add offset times 2 MOV A,M! INX H ;Get low order byte of DPH pointer MOV H,M! MOV L,A ;Get word DPH pointer in "H,L" ORA H! JZ SELINV ;Check for valid drive, done if not ; MOV A,M ;Get disk selection mask STA ACTDSK ;Set into ACTDSK (make active disk) STA SEKDSK ;And desired (seek to) INX H ;Point to true DPH PUSH H ;Save DPH address LXI B,5*2 ;Add offset within DPH to its DPB pointer DAD B ;Result in "H,L" = DPH(DPB) MOV C,M! INX H ;Put start of DBP in "B,C" MOV B,M! DCX B ;Point to disk type for this DPH LDAX B ;Get it STA ACTTYP ;Save active disk type STA SEKTYP ;And desired POP H ;Restore DPH address CPI D2$TYPE ;See if fixed media (starting hard disk type) RNC ;Done if hard disk or memory drive ; LXI B,X8TABLE ;Show format parameter table base if FLOPPY5 CPI FD5TYPE ;See if 5 1/4 inch disk type JC SEL8XLT ;Use that format table if so LXI B,X5TABLE ;5 1/4 format parameter table base SEL8XLT:CALL FIX58 ;Patch controller ports endif MOV A,E! ANI 1 ;Mask "Force selected" bit, set status RNZ ;Done if drive previously selected ; PUSH B! PUSH H ;Save XLATE, DPH address again CALL TREAD ;Determine disk type POP D! POP B ;Restore DPH, XLATE address LXI H,0 ;Indicate drive not active RNZ ;If disk type determined, fix DPH to match ; FIX$DPH:PUSH B! PUSH D ;Save base of track info, Drive's DPH address LXI D,DPB8TBL+1 ;Get base of DPB's to get correct one if FLOPPY5 CPI FD5TYPE ;See if 5 1/4 inch disk type JC SEL8DPB ;Use that format table if so LXI D,DPB5TBL+1 ;Get, save base of DPB's to get correct one endif SEL8DPB:ANI 07h! MOV L,A ;Get disk type for DPB locate in "H,L" DAD H! DAD H ;times 4 DAD H! DAD H ;times 4 = 16 bytes/DPB XCHG! DAD D! XCHG ;Add base of DPB's to get correct one in "D,E" ANI 06h! MOV L,A! ADD A ;Remove sided bit (offset 2X), times 2 = 4X ADD L! MOV L,A ;Plus 2X = 6X, offset to table in "H,L" DAD B ;Add "Sector translation" base to offset POP B! PUSH B ;Recover, save again DPH base address MOV A,M! STAX B ;Set translation table address into DPH INX H! INX B ; as word value MOV A,M! STAX B ;At start of DPH INX H! MOV A,M ;Point to associated Gap 3 Length, get it STA SEKGPL ;Save for read/write operations STA ACTGPL LXI H,(5*2)-1 ;Offset from present DPH address to DPB pointer DAD B ;Move current pointer to DPH [DPB] pointer MOV M,E! INX H ;Set DPB address into DPH MOV M,D POP H! POP B ;Restore DPH start address, format table base RET SELINV: STA CDISK ;Put zero in A so drive A: is selected RET ; upon selection of nonexistent drive ; ; TREAD - Determine floppy disk type. ; ;Exit: Zbit set = no error and ; A = disk type (8n + (0-7)) if 8 inch. ; (8n + 20h + (0-7)) if 5 1/4 inch. ; n = 0-3 depending on the number of cylinders/drive. ; TREAD: CALL GTREADY ;Ask for drive status RNZ ;Abort if not ready LDA TEMPBF ;Get status byte ANI FD2SIDE ;Mask TS (two sided) bit RRC! RRC! RRC MOV B,A ;Save sided flag in "B" LXI H,SEKTYP ;Make into drive type with other info MOV A,M! ANI 38h ;Mask to select drive size bits ORA B! MOV M,A LXI H,RECAL ;Home to track 0 MVI B,LRECAL ;No status bytes for command CALL MOVETO ;Process command, (Always reports success) MVI A,2 ;Seek to track two (only done if recal worked) CALL DOSEEK! RNZ ;Abort if error (not reported if not tried) LXI H,DRID MVI M,FD$DRID ;Get disk ID (format info) TRD2: PUSH H LXI B,(DRIDL*256)+7 ;Command length + 7 bytes of status CALL EXEC ;Process command POP H JZ TRD3 ;Get proper drive type if successful MOV A,M! XRI FD$MFM ;Compliment MFM/FM bit MOV M,A! ANI FD$MFM JNZ TRD2 ;Loop for MFM if read not valid ORI 0FFh ;Abort if both FM and MFM tried and failed RET ; TRD3: LDA TEMPBF+6 ;Get number of bytes ADD A! MOV B,A ;times 2 in "B" LDA SEKTYP! ORA B ;Combine N with sided flag STA SEKTYP ;Save disk type CMP A ;Set zero flag (show no error) RET ; PAGE ;---------------------------------------- ; ; READ SECTOR ROUTINE: ; ; Read a CP/M 128 byte sector (also known as a "record"). ; ;Exit: A = 0, Z bit set for successful read operation. ; A = non-zero value if unsucessful read operation. ; READ: CALL CHKBKD ;Check for blocked drive MVI A,FD$RDAT ;Read from single density floppy JNC FINAL ;If non-blocked transfer XRA A ;Set flag to force a read CALL FILL ;Fill buffer with data POP H ;Host record address POP D ;User DMA PUSH PSW ;Save error status byte CALL MOVDTA ;Move 128 bytes POP PSW RZ ;Done if no error ; ; HSTOFF - Turn off host active to prevent write to host. ; Used if error on fill to write buffer and if HOME command issued. ; HSTOFF: XRA A ;Clear host active flag STA HSTACT INR A ;Indicate error condition RET ; ; Check Blocked Disk transfer. ; ;Exit: Carry bit set if blocked device. ; Carry bit clear if unblocked device (logical and physical ; sectors are both 128 bytes in length). ; CHKBKD: LDA SEKTYP ;Get type of device in use if (XMDRIVE or HMDRIVE) CPI MEMTYPE ;If M-drive RNC ;(not blocked) endif if (DISK2 or DISK3) CPI D2$TYPE ;If Hard Disk CMC! RC ;Always blocked if so endif ANI 06h ;See if 128 byte sector 8 or 5 inch RZ ;Not blocked device if so if FLOPPY5 LDA SEKTYP ;Get seek type again CPI FD5TYPE! CMC! RC ;Blocked if 5 1/4 inch, check track if 8" endif LDA SEKTRK ;See if track 0, single density possible ADI 0FFh! RC ;Blocked sectors if not track 0 XRA A ;Force type 0, reset blocked select bit STA ACTTYP ;Use as actual type in use, non-blocked set RET ; ;---------------------------------------- ; ; WRITE SECTOR ROUTINE: ; ; Write the selected 128 byte CP/M sector. ; ;Entry: C = 0, write to a previously allocated block. ; C = 1, write to the directory. ; C = 2, write to the first sector of unallocated data block. ; ;Exit: A = 0, Z bit set for successful write operation. ; A = non-zero value if unsucessful write operation. ; WRITE: CALL CHKBKD ;Check for blocked drive MVI A,FD$WRT ;Write to single density floppy JNC FINAL ;If non-blocked transfer XRA A ;Set for forced pre-read MOV B,C ;Save original write type in "B" DCR C ;Write type in "C" DCR C ;See if write to first of unallocated JNZ WRIT2 ;Forced read if not CMA ;No physical read from disk required WRIT2: PUSH B ;Save write type CALL FILL ;If "A"=0, then a physical read is required POP D ;Host record address POP H ;DMA address PUSH PSW ;Save error status CALL MOVDTA ;Move 128 bytes into host buffer POP PSW ;Recover error status and byte POP B ;Recover write type JNZ HSTOFF ;Abort if any errors occurred INR A ;Make status byte a "1" STA HSTWRT ;HSTWRT = 1 (active for next query) ANA B ;"B" = 1 if write to directory RZ ;Done if not ; ; FLUSH - Write out active host buffer onto disk. ; FLUSH: LXI H,HSTWRT ;See if host write flag is active MOV A,M! ORA A! RZ ;Done if host buffer already on disk DCR M ;Mark inactive for next query LHLD HSTDSK ;Move disk and type SHLD ACTDSK LHLD HSTTRK SHLD ACTTRK LHLD HSTSEC SHLD ACTSEC LXI H,HSTBUF ;Set DMA xfer from host buffer address SHLD BUFADR MVI A,FD$WRT+FD$MFM ;Write double density JMP FINAL ;From host buffer ; ; FILL - fill host buffer with approprite host sector. ; ;Entry: A = 0 means read physical sector required if not in buffer. ; A = 0FFh means read not required. ; ;Exit: On exit the stack will contain the following values: ; POP x = host record address. ; POP y = caller's buffer address. ; A = 0 and Z-bit set if no errors occurred. ; FILL: STA RDFLAG ;Save read flag LXI H,HSTBUF ;Set DMA xfer to host buffer address SHLD BUFADR LDA SEKTYP ;Get disk type if (DISK2 or DISK3) CPI D2$TYPE ;See if hard disk JNC FILL1 ;Use sector block size (log base 2) if so endif ;For both 8 inch and 5 1/4 inch floppies, RRC ;Put "N" field In the least significant bits FILL1: ANI 3 ;Strip to get log base 2 of sector size MOV B,A ;B = log base 2 (sector size) - 7 XCHG ;Host buffer base in D,E LXI H,128 ;128 byte records LDA SAVSEC ;Get logical sector (desired) FILL2: XCHG RRC ;Divide sector # by 2's to get physical JNC FILL3 ;If low bit not set DAD D ;Add bias to offset FILL3: XCHG DAD H ;Double significance of offset for next bit ANI 7Fh ;Mask to get physical sector DCR B ;Bump log count for bit shifts JNZ FILL2 ;If not all bits checked STA SEKSEC ;Physical sec # = desired / (sec size/128) LHLD DMAADR ;Get DMA address from user XTHL ;Set return parameters PUSH D ;Save host buffer pointer to 128 byte sector PUSH H ;Set return address of user LXI H,HSTACT ;Get current Host active flag MOV A,M! ORA A ;and it's status MVI M,1 ;which always becomes 1 (active) JZ FILL6 ;If host buffer was inactive, do a forced read LXI H,HSTSEC-1 ;Compare host values with desired LXI D,SEKSEC-1 MVI C,SEKTYP-SEKSEC+1+1 FILL4: INX H! INX D ;Bump pointers for comparison XRA A ;Clear error byte to zero value DCR C! RZ ;Bump comparison count, done if all bytes match LDAX D! CMP M ;See if current pair match JZ FILL4 ;Loop until all checked or mis-match CALL FLUSH ;Flush host buffer to disk if mis-match RNZ ;Abort if error FILL6: LHLD SEKDSK ;Move disk and type SHLD HSTDSK ;For new host physical read from disk SHLD ACTDSK LHLD SEKTRK SHLD HSTTRK SHLD ACTTRK LHLD SEKSEC SHLD HSTSEC SHLD ACTSEC LDA RDFLAG ;See if read required INR A! RZ ;Done if not ; RDMFM: MVI A,FD$RDAT+FD$MFM ;Read double density ; ; FINAL -- Perform final transfer processing, actual data ; moved from/to memory to/from storage device. ; ;Entry: A = Command, either READ or WRITE. ; ;Exit: A = 0, Z-bit set if no errors occured during transfer with ; a possibility of "MRTRY" retries at the transfer. ; A = non-zero, Z-bit reset if errors occurred on all "MRTRY" ; efforts at a transfer. ; ; --- Footnote #3. --- ; FINAL: LXI H,CIOPB ;Put the initial command in it's buffer MOV M,A! XCHG ;And save pointer in "D,E" LHLD ACTTRK ;Get track (head, cylinder) number in "H,L" LDA ACTSEC! MOV C,A ;Get active sector number in "C" LDA ACTTYP ;Get drive type in "A" if (XMDRIVE or HMDRIVE) CPI MEMTYPE ;See if either M-DRIVE JNC MEMFNL ;Special finish if so endif XCHG ;Track in "D,E" and pointer in "H,L" if (DISK2 or DISK3) CPI D2$TYPE ;See if hard disk type for drive JNC HARDFNL ;Use it's controls if so endif INX H ;Point to next buffer specification (drive) if FLOPPY5 MOV B,A ;Save type CALL FIX58 ;Set ports for 8 or 5 inch floppies MOV A,B endif ANI 07h ;Remove mini-floppy select RAR ;Get N (sector size), reset carry if 1 sided MOV B,A ;Save in "B" JNC FPYFNL ;Done if single sided XRA A ;Clear carry MOV A,E ;Convert to head and cylinder (CY becomes head) RAR ;(divide by 2) MOV E,A FPYFNL: MVI A,0 RAL ;Set head number bit from CY MOV D,A ;Move to head field RLC! RLC ;And head bit in drive # field MOV M,A ;Save temporary value of head LDA ACTDSK ;Get drive # ORA M ;Combine head with drive MOV M,A ;Set drive (0 0 0 0 0 HDS DS1 DS0) INX H! MOV M,E ;+2 = Set cylinder INX H! MOV M,D ;+3 = Set head INX H! MOV A,C! INR A ;+4 = Set sector (+1 for controller) MOV M,A ;Set beginning sector INX H! MOV A,B! MOV M,A ;+5 = Set N field INX H! LDA NUMSEC ;+6 = Ending sector, calculated by N*2 ADD C! MOV M,A ;Set EOT INX H! LDA ACTGPL ;+7 = Set Gap length MOV M,A ;Set GPL field INX H! MVI M,0FFh ;+8 = Set DTL to double density DCR B! JP FNL0 ;Use it if not single type (0) MVI M,80h ;Set DTL to 128 bytes FNL0: MVI E,MRTRY ;Set retry count in "E" FNL1: CALL GTREADY ;Ask for drive status RNZ ;Abort if still not ready DCR E! MOV A,E! RM ;Bump retry count, done if negative status LDA CIOPB+2 ;Get cylinder number CALL DOSEEK ;Seek to proper track JNZ FNL1 ;Retry if seek error LXI H,BUFADE ;Load blocking/deblocking buffer as DMA address MVI B,3 ;24 bits (3 bytes) FNL2: MOV A,M ;Get DMA address to controller DMAPT: OUT FD8PORT+FDMA DCX H! DCR B ;Data is backward in memory JNZ FNL2 ;Loop until all 3 bytes sent LXI H,CIOPB ;Point to Command Input/Output Parameter Block LXI B,(CIOPL SHL 8)+7 ;Set command length, get 7 status bytes CALL EXEC1 ;perform operation SUI 40h ;Remove "abnormal termination of command" bit ; -- This bit is set because at the end of the last sector read or written, ; there was no "Terminal Count" flag issued from an external DMA controller. ; On the DISK 1 this signal is grounded, and although the FDC indicates this ; error, if the "End of Track Encountered" error bit is also set and no others ; are, this represents a successful read or write by the DISK 1. JNZ FNL1 ;Try again if any other error LDA TEMPBF+1 ;Get second result status byte SUI FD$EOC ;Remove sector past end of cylinder bit RZ ;Done if no other errors ANI FD$NWRT ;See if write protect error JZ FNL1 ;Loop if not write error ; ; WRTERR - Get drive write protect status, ask to unprotect if necessary. ; WRTERR: CALL GTDSTS ;Get drive ready status MOV A,M ;Recover full status byte ANI FD$WRTP ;Select write protect status bit LXI H,WRTPMSG ;Write protected disk detected message CNZ PRINT ;Send to console if needs to be changed GTWRT1: CALL CIS ;See if character at console RNZ ;Abort if so CALL GTDSTS ;Get drive status JNZ GTWRT1 ;Loop if drive now not ready MOV A,M ;Recover full status byte ANI FD$WRTP ;Select write protect status bit JNZ GTWRT1 ;If drive still protected, check again JMP FNL1 ;Loop if not protected ; ; GTREADY - Wait until selected drive is ready or console driven abort ; (any char typed). ; ;Exit: Z-bit set if drive is ready, or was made ready after request. ; Z-bit reset if console abort -- drive not ready. ; GTREADY:MVI A,80h ;Turn on drives FDCMOT: OUT FD8PORT+FDON ;Serial port used as "motor on" control bit CALL GTDSTS ;Ask for drive status, ignore first reading CNZ GTDSTS ;Ask again if first reading didn't show ready RZ ;Done if ready LXI H,NRDYM1 ;Show not ready CALL PRINT GTRDY1: CALL GTDSTS ;Get drive status RZ ;Done if now ready CALL CIS ;See if character at console (abort) JZ GTRDY1 ;Loop until console abort or drive ready RET ; ; DOSEEK -- Seek to specified Track: ; ;Entry: A = Track number to seek to. ; DOSEEK: STA DSEKC+2 ;Save cylinder # LXI H,DSEKC ;Seek command pointer MVI B,DSEKL ;Seek command with no status bytes recieved ; ; MOVETO -- Move head according to command: ; ;Entry: H,L = address of command buffer. ; B = length of command buffer. ; ;Exit: Z-bit set if no error. ; MOVETO: CALL EXECP ;Set up seek command in controller SEEKI: CALL INTREQ ;Wait for interrupt service request MVI A,FD$RSTS ;Send Request for interrupt status command FDCD0: OUT FD8PORT+FDCD MVI C,2 ;2 status bytes recieved CALL GCMPS ;Get status SUI FDC$SKE! RZ ;Verify seek end, no other errors -- done if so LDA ACTDSK ;See if disk sampled matches XRA M! ANI 3 ;One in status byte JNZ SEEKI ;Loop until so INR A ;Show error RET ; ; GTDSTS - Get drive ready status of ACTDSK: ; ;Exit: Z-bit set if ready. ; GTDSTS: LXI H,DSTS ;Ask for drive status MVI B,DSTSL ;Command length and 1 status byte CALL EXECP ;Perform command INR C ;Get 1 status byte CALL GCMPS CMA ;Flip polarity of status byte ANI FD$RDY ;Mask ready bit RET ; ; EXEC, execute normal floppy disk controller command sequence: ; EXECP, execute floppy disk controller command sequence with no ; status check afterwards ("C" = 0): ; ;Entry: H,L = Pointer to first byte of command buffer, ; B = number of bytes to output to controller in sequence, ; C = number of bytes to input to status buffer. ; ;Exit: If C <> 0 then see GCMPS. ; H,L = 1 past last byte of command buffer if no status bytes. ; C = 0 on exit always. ; EXECP: MVI C,0 ;No status bytes for this execution EXEC: INX H ;Point to drive # byte LDA ACTDSK ;Set drive into command buffer MOV M,A ;at second byte DCX H ;Point to start of command sequence EXEC1: CALL GETRQM ;Wait for request for master service MOV A,M ;Get command byte FDCD1: OUT FD8PORT+FDCD ;Send to controller INX H! DCR B ;Point to next JNZ EXEC1 ;Loop if more bytes MOV A,C ;# of status bytes ORA A! RZ ;Done if no status bytes ; ; Command Phase completed, now wait for Execution Phase completion indication. CALL INTREQ ;Wait for interrupt service request ; ; Get result status byte(s) into TEMPBF buffer. ; ;Entry: C = number of Result Phase status bytes to read. ; ;Exit: H,L = TEMPBF = pointer to status bytes read in. ; A = first status byte anded with 0F8h with flags set accordingly. ; GCMPS: LXI H,TEMPBF ;Set status buffer address PUSH H ;Save status buffer start pointer GCMPS2: CALL GETRQM ;Wait for request for master service FDCD2: IN FD8PORT+FDCD ;Read result status byte MOV M,A! INX H ;In status buffer, point to next location DCR C ;Decrement status byte counter JNZ GCMPS2 ;Wait until all done POP H ;Point to first status byte MOV A,M ;Get first status byte ANI not (FD$HDS + FD$DRVS) ;Get completion status less drive, head bits RET ; ; Poll for RQM status bit, done when master requests service. GETRQM: FDCS1: IN FD8PORT+FDCS ;Get main status byte ORA A ;See if request for master service bit set JP GETRQM ;Loop if not ready ; ANI FD$DIO ;Mask data direction (DIO) bit RET ; ; Wait for Interrupt Service Request from FDC (8272). INTREQ: INTS1: IN FD8PORT+INTS ;Get interrupt status byte ORA A ;Sample interrupt service request bit JP INTREQ ;Wait for interrupt if not set RET ; ; Self modifying code to fix ports for 8 or 5 inch disk controller. ; if FLOPPY5 FIX58: CPI FD5TYPE ;See if 8 inch drive selected MVI A,FD8PORT JC FIXPORT FIX5: MVI A,FD5PORT ;Use 5 1/4 inch controller if not FIXPORT:STA FDCS1+1 ;Fix control/status ports to correspond INR A STA FDCD0+1 ;Data ports STA FDCD1+1 STA FDCD2+1 INR A STA DMAPT+1 ;DMA port as output STA INTS1+1 ;Status as input INR A STA FDCMOT+1 ;Motor on control port (bit banger unused) RET endif ; PAGE ;**************************************************************** ;* FINAL PROCESSING FOR HARD DISK DRIVES (DISK 2,3) * ;**************************************************************** ; ; Routines for driving the DISK2 and DISK3 controllers, and drives ; corresponding SELECTOR CHANNEL boards if DISK2 in conjunction ; with one to four hard disk drives. ; HARDFNL: if (DISK2 and DISK3) CPI D3$TYPE ;See if DISK3 driver JC D2$FNL ;Finish with DISK2 if not endif if DISK3 D3$FNL: LXI H,D3CIOPB+2 ;Point to DISK3 command parameter block LDA ACTDSK! MOV M,A ;Setup active disk as 0-3 selection of drive LDA CIOPB ;Get original command CMA! ANI 1 ;Correct for DISK3 read/write control bit INX H! MOV M,A ;Save Read/Write command type INX H! MOV M,C ;Save lower half of sector count INX H! XRA A! MOV M,A ;Upper half is zero INX H! MOV M,E ;Save track lower half INX H! MOV M,D ;and upper track half INX H! MVI M,1 ;Transfer 1 sector INX H! MOV M,A ;Upper half is zero XCHG! LHLD BUFADR ;Get local DMA address XCHG! INX H! MOV M,E ;Lower byte saved INX H! MOV M,D ;Middle byte saved LDA BUFADE ;Get upper byte INX H! MOV M,A ;Upper byte saved to complete command load ; Linkage remains fixed to loop back to start of D3CIOPB MVI A,D3$XFER ;Set up for data Input/Output transfer D3EXEC: LXI H,D3CIOPB ;Point to parameter block in use D3EXEC0:MOV M,A ;Put command in first byte of parameter block INX H! MVI M,0 ;Clear the status byte ; Begin command execution, wait for result status. MVI A,D3$ATTN ;Issue "attention" command to DISK3 to start OUT D3$PORT ;Disk controller operation D3EXEC1:MOV A,M! ORA A ;See if command execution has completed JZ D3EXEC1 ;End status check if byte now non-zero D3EXEC2:INR A! RZ ;See if result was 0FFh (valid completion) ADI '0'-1 ;Add ASCII bias for error type STA D3MSG2 PUSH B! PUSH D! PUSH H LXI H,D3MSG ;Show error message and type CALL PRINT CALL CI ;Wait for operator to respond POP H! POP D! POP B ORI 0FFh ;Show error condition on return RET ; endif ; PAGE ;******************************************************** ;* FINAL PROCESSING FOR DISK 2 HARD DISK DRIVES * ;******************************************************** ; if DISK2 D2$FNL: MOV A,E ;Get low order portion of track XCHG ;Put desired track number in "H,L" DAD H! DAD H ;Shift left 4 bits to put cylinder in "H" DAD H! DAD H ; if (D2M??) ;16 heads ; ANI 0000$1111b ;Select head value (0-15) ; endif if (D2M20 or D2F40B or D2M26) ;8 heads ANI 0000$0111b ;Select head value (0-7) DAD H ;Shift left (6 bits) again for proper cylinder in "H" endif if (D2M10 or D2F20B) ;4 heads ANI 0000$0011b ;Select head value (0-3) DAD H! DAD H ;Shift left again for proper cylinder in "H" endif MOV L,A ;Put head value in "L" XCHG ;Return both to "D,E" - command pointer "H,L" MOV A,M! ANI 1 ;Select Read/Write bit from floppy command MVI M,D2$READ ;Replace with hard disk read JZ D2FNLX ;if was a floppy read command MVI M,D2$WRT ;Use write command instead if not D2FNLX: MVI L,MRTRY ;Load max retry count D2FNLP: DCR L! MOV A,L! RM ;Bump retry count, done if neg (0FFh) status CALL D2SELECT ;(Re-)select appropriate drive, verify ready RNZ ;Abort if not PUSH H ;Save retry counter on stack CALL D2XFER ;Seek to proper cylinder, perform data transfer POP H ;Recover retry counter RZ ;Done if no errors ADD A ;See if timeout error (header not found) JP D2FNLP ;Loop in case of soft error (not timeout) ; ; D2RELOCATE -- Search relocation table for matching sector ID, load ; corresponding relocated sector values if found and try transfer ; again using alternate sector. ; D2RELOC:RRC ;Restore error byte ANI D2$OVR ;See if overrun was cause of timeout JNZ D2FNLP ;Treat as soft error if so LXI H,(RELTBL0 - 2) - (3 * D2$SCNT) ; Point to base of relocation table(s), pre-comp for 2 indexes PUSH D ;Save head, cylinder specification LXI D,(3 * D2$SCNT) ;Point to next disk unit's faulty sector table D2REL1: DAD D ;Add unit table size until correct one found DCR B ;Bump unit number count JP D2REL1 ;Loop until pointer at this unit's table POP D ;Recover head, cylinder specification ; ; Loop entry points -- if no match found, skip other tests for match of ; current head, sector and advance to next 3 byte entry in table. D2REL5: INX H ;Skip head storage byte D2REL4: INX H ;Skip sector storage byte D2REL3: MVI A,D2$SCNT-1 ;See if all available spare sectors checked INR B! CMP B ;Bump count of table entries, see if past max RC ;Irrecoverable error if no entries match MOV A,M! INX H! CMP D ;See if CYLINDER matches table JNZ D2REL5 ;Skip head, sector check if not MOV A,M! INX H! CMP E ;See if HEAD number matches table JNZ D2REL4 ;Skip sector check if not MOV A,M! INX H! CMP C ;See if SECTOR matches table JNZ D2REL3 ;See if scan next triplet if not ; ; Place new values in the registers to correspond with the relocated sector, ; and continue with the Disk 2 read/write operation using the "spare" storage. D2XRELS:MOV C,B ;Corresponding sector number counted in "B" LXI D,2 ;Relocated sectors at cylinder 0, head #2 JMP D2FNLX ;Continue with execution using new values ; ; D2SELECT -- Select appropriate DISK 2 hard disk drive. ; ;Entry: ACTDSK set to current active drive to get ready status. ; ;Exit: C,D,E unaltered, ; B = Lower nibble of drive select (0-3) from ACTDSK value, ; Zero bit set if drive is ready. ; D2SELECT:CALL D2READY ;See if drive is ready, ignore first if not CNZ D2READY ;Reset drive fault bit, again check drive ready RZ ;Return valid if so PUSH B! PUSH D! PUSH H ;Save desired positions LXI H,D2$MSG ;Show unit not ready CALL PRINT D2SEL1: CALL CIS ;See if character from console JNZ D2SEL2 ;Return with zero bit reset (error) if so CALL D2READY ;See if drive is ready JNZ D2SEL1 ;Keep trying until so or console abort D2SEL2: POP H! POP D! POP B ;Recover desired positions RET ; ; Routine to get the drive ready status of the active hard disk. D2READY:LDA ACTDSK ;Get unit select (upper and lower nibbles set) ANI 3! MOV B,A ;Select lower nibble in range 0-3, save in "B" ORI (D2$STRB + D2$RST) ;Add in drive strobe, fault clear bits OUT D2$CNTL ;Set drive select register ADI '0'-(D2$STRB+D2$RST);Add bias and save as drive ASCII ID STA D2$MSGX ;in error message LDA ACTDSK ;Get unit select (upper and lower nibbles set) ANI 1111$0000b ;Use only upper nibble single select OUT D2$DATA ;Set up DISK 2 with drive select IN D2$STAT ;Get status byte ANI (D2$ATTN + D2$NRDY) ;Strip out attention, drive ready bits XRI D2$ATTN ;Flip status of attention bit RET ;Return with drive ready status ; ; D2XFER - DISK 2 Data Transfer (to/from Hard Disk) Operation. ; ; -- Two part transfer (with seek to home position if cylinder unknown). ; First the command sequence is given to seek correct cylinder, and for drives ; having automatic settling time delays, the concurrent loading of the ; controller to execute the actual data transfer. For drives not having an ; automatic settling time delay, the No-op command is issued after a seek, ; which allows between 1 and 2 extra revolutions of the disk to allow the ; heads to settle. ; ;Entry: CIOPB = command, (read or write) ; D = Desired cylinder to seek, ; E = Desired head of cylinder, ; C = Desired sector of track, ; B = Actual disk unit in use. ; ;Exit: B,C,D,E unaltered, ; H,L = Pointer to current cylinder table storage, ; Zero bit set if no errors occurred. ; D2XFER: LXI H,HD2CYL ;Base address of last known cylinder positions PUSH B ;Get this drive's MOV C,B! MVI B,0 ;Offset for last cylinder position in "B,C" DAD B! POP B ;Add offset for desired drive unit number (0-3) MOV A,M ;Get last position for active unit CPI -1 ;Minus one indicates unknown -- seek home CZ D2HOME ;Execute sequence if "forced" home ;Current cylinder again (after home) in "A" SUB D ;Get the difference of desired and current CNZ D2SEEK ;If current not at desired cylinder, seek to it ; ; DISK 2 Transfer data to/from hard disk. PUSH H ;Save cylinder pointer MVI A,D2$STRB ;Add drive select command ORA B ;to unit desired OUT D2$CNTL ;Output to DISK 2 control register LDA ACTDSK ;Get active drive number ANI 1111$0000b ;Mask single select bits (high nibble) ORA E! OUT D2$DATA ;Select drive, head MVI A,D2$CYL ;Select cylinder number storage OUT D2$CNTL MOV A,D! OUT D2$DATA ;Get desired Cylinder number MVI A,D2$HEAD ;Select head number storage OUT D2$CNTL MOV A,E! OUT D2$DATA ;Get the desired Head number MVI A,D2$SEC ;Select sector storage OUT D2$CNTL MOV A,C! OUT D2$DATA ;Load desired sector ; ; Set up selector channel to correspond, with desired DMA value ready. IN SELCHAN ;Initialize selector channel to accept data LXI H,BUFADE ;Get extended DMA address byte MOV A,M! DCX H ;from storage, point to middle byte OUT SELCHAN ;Upper byte Loaded first MOV A,M! DCX H ;Load with remaining DMA mid, low bytes OUT SELCHAN MOV A,M ;For full 24 bit DMA transfer address OUT SELCHAN LDA CIOPB+0 ;Get hard disk Read/Write command CPI D2$READ ;See if read operation PUSH PSW ;Save Read/Write operation type MVI A,SELBYT+00h ;Upper bit not set if write operation JNZ D2XFER4 MVI A,SELBYT+80h ;Set up selector channel to correspond D2XFER4:OUT SELCHAN ;Last byte of 4 in sequence POP PSW! ORA B ;Get read/write command that begins transfer OUT D2$CNTL ;Send to controller to begin execution XTHL! XTHL ;Let the state machine start it's sequence ; Loop until command completed. D2XFER5:IN D2$STAT ;Get status of controller ORA A ;See if command still in process JM D2XFER5 ;Loop until transfer complete ANI (D2$TOUT + D2$CRC + D2$OVR + D2$NRDY + D2SEKD + D2$WRTF) ; Select Timeout, CRC error, Overrun, Not Ready, Seek Done, Write Fault* bits XRI D2$WRTF ;Flip status of write fault bit for final POP H ;Recover table pointer for cylinder updates PUSH PSW ;Save error status MVI A,D2$STRB ;Reset interrupt status bit to inactive state OUT D2$CNTL MVI A,0001$0000b ;Reselect Unit #0 to execute command OUT D2$DATA POP PSW! RZ ;Done if no errors from transfer operation MVI M,0FFh ;Show drive at unknown position, force home RET ;Return with error type set ; ; D2SEEK -- DISK 2 Execute seek to desired cylinder position. ; ;Entry: H,L = Pointer to current cylinder table storage, ; D = Desired cylinder to seek, ; E = Desired head of cylinder, ; C = Desired sector of track, ; B = Actual disk unit in use. ; ;Exit: B,C,D,E,H,L unaltered, return when desired cylinder has been reached. ; Storage for unit positioner [H,L] updated, indicating new location. ; D2SEEK: PUSH D ;Save head, cylinder numbers MVI E,D2$SOU ;Seek outward control byte JNC D2SEEKO ;If seek outward MVI E,D2$SIN ;Seek inward control byte CMA! INR A ;Two's compliment count to make positive D2SEEKO:MOV D,A ;Move number of tracks MOV A,B! ORA E ;Get selected drive, add in step direction OUT D2$CNTL ;Send to DISK2 XTHL! XTHL ;Allow the direction bit to settle D2STEP1:IN D2$DATA ;Activate a step if (D2M10 or D2M20 or D2F20B or D2F40B) ;All steps accumulated by drive DCR D ;Bump step counter JNZ D2STEP1 ;If not all steps sent POP D ;Recover desired cylinder MOV M,D ;Set current storage = desired else ;Steps are issued individually D2SEEK2:IN D2$STAT ;Get drive status ANI D2$SEKD ;See if seek complete JNZ D2SEEK2 ;Wait until so DCR D ;Bump step counter JNZ D2STEP1 ;Loop for another if not all steps sent if D2M26 ;Shugart drive requires computed settling time MVI A,D2$TIME ;Issue a NO-OP command to get settling delay ORA B ;Add in selected unit control bits OUT D2$CNTL ;Send to controller, use next instructions for ; command settling delay (no interrupt active) POP D ;Recover desired cylinder MOV M,D ;Set current storage = desired D2SKDLY:IN D2$STAT ;Get controller status byte ANI D2$TOUT ;Mask timeout bit (1-2 index pulse delay) JZ D2SKDLY ;Loop for settling time (approx.) till timeout else ;No extra delay necessary POP D ;Recover desired cylinder MOV M,D ;Set current storage = desired endif endif RET ; ; D2HOME -- DISK 2 Execute seek to Home position (cylinder #0). ; This routine is called when the actual position of a drive unit (as measured ; by it's cylinder number) is in doubt. It returns the positioner to the ; special "known" place at the outermost cylinder. ; ;Entry: H,L = Pointer to current cylinder table storage, ; B = Actual disk unit in use. ; ;Exit: B,C,D,E,H,L unaltered, return when cylinder 0 has been reached, ; A = 0, and storage for unit (H,L pointer) reset, indicating proper ; location of positioner. ; D2HOME: INR M ;Mark current with proper "home" value (zero) PUSH B! MOV C,M ;Save sector, use 256 count in "C" D2HOME0:IN D2$STAT ;Get drive status ANI D2$CYL0 ;See if cylinder 0 has been reached JZ D2HOME3 ;Done with seek operation if so MOV A,B ;Get active drive unit ORI (D2$SOU + D2$RST) ;Add in step out command, reset fault bit OUT D2$CNTL ;Send to DISK2 D2HOME1:IN D2$DATA ;Cause a step to be activated DCR C ;Bump step counter if (D2M10 or D2M20 or D2F20B or D2F40B) ; Fujitsu drives accept all pulses at once JNZ D2HOME1 ;Loop if not done with steps endif D2HOME2:IN D2$STAT ;Get drive status ANI D2$SEKD ;See if seek complete JNZ D2HOME2 ;Wait until so if D2M26 ;Shugart drives require separate pulses JMP D2HOME0 ;Loop if not done with steps endif D2HOME3:POP B ;Recover desired sector, active unit MOV A,M ;Get current cylinder again (after home) in "A" RET endif ; PAGE ;**************************************************************** ;* FINAL PROCESSING FOR BOTH TYPES OF MEMORY DRIVES * ;**************************************************************** ; ; First is M-DRIVE/H which may optionally utilize "parity byte" ; software error checking stored at its lowest value "tracks". ; if (XMDRIVE or HMDRIVE) MEMFNL:;** MOV A,C ;Get pseudo sector (4K bank) ;** MOV B,H ! MOV C,L ;Save requested cylinder *** DAD H! DAD H! DAD H ;Pseudo cylinder (128 byte block in 4K bank) DAD H! DAD H! DAD H ;Multiply by 128 to get offset in bank DAD H ;32 Pseudo cylinders max (32 * 128 = 4K) XCHG ;MDRIVE "track" address in "D,E" LHLD DMAADR ;Direct memory address in "H,L" if (XMDRIVE and HMDRIVE) ;If both MDRIVE's are active JZ MEMXFNL ;Use 8088 mapped memory if exactly "MEMTYPE" endif ; if HMDRIVE ;If M-DRIVE/H is active ; ----- For M-DRIVE/H ----- ;Entry: D,E = Translated "Track" in range of (4-511)*128, ; A = "Sector" in range of 0-"8n-1", where "n" is # of M-DRIVE/H boards. ;** B,C = Actual track ; ;** Rearranged so that "sector" info is actually the high order address byte - ; "D" becomes the low order track address byte, and since "D" may hold ; only a "1" or "0" in it's MSB, use it to identify the "high or low" ; sector being addressed by rotating that bit to the high order position. ; ; Lines commented out with a ";**" are a slightly more efficient way of keeping ; track of parity bytes. The line below a ";**" keeps parity the way other » CompuPrï BIOS'ó dï tï maintaiî compatibilitù across OS's. HMD$FNL:;** PUSH B ;Save "track" for parity PUSÈ PSW¡ OUÔ HM$CNTÌ ;Savå "sector¢ foò parity¬ higè "track¢ byte MOV A,D! OUT HM$CNTL ;Low order byte of "track" MOV A,E! OUT HM$CNTL ;Set "record" 1 or 0 ID in final byte setup ;** LXI B,5A80h ;Start parity ("B"), 128 Byte operation ("C") LXI B,8080H LDA CIOPB ;See if read operation requested CPI FD$RDAT JNZ HMD$WRT ;Do write if not HMD$RD: IN HM$DATA ;Get data byte from "drive" MOV M,A! INX H ;In memory, point to next location ADD B! MOV B,A ;Accumulate parity byte DCR C ;Until all 128 bytes done JNZ HMD$RD ;If done, point to stored disk parity byte POP PSW! OUT HM$CNTL ;Set High order byte of track ;** POP D ;** MOV A,E MOV A,D ;** RLC ;Move high bit into low order position OUT HM$CNTL ;Set Low order byte of track (single bit) ;** MOV A,D! ;** OUT HM$CNTL ;Use old byte of "track" to find single byte MOV A,E! OUT HM$CNTL ;Use old byte of "track" to find single byte IN HM$DATA! SUB B ;Get it and see if parity matches RET ;Return with error status ; ; Write operation to MDRIVE/H. HMD$WRT:MOV A,M! INX H ;Get byte from memory, point to