PAGE ,132 TITLE Installable DEVICE driver for LifeLine IDE controller for MS-DOS ; The Resident portion of the driver is the interface to MS-DOS and works primarily with ; logical sectors, on logical partitions(DOS drive letters). See note below on Physical Access. ; The Non-Resident portion will search the LifeLine IDE Controller's Primary J1 and Secondary ; J2 busses for valid devices and assign MS-DOS drive letters to valid Z-100 partitions. ; See notes below for valid devices and partition types. Power modes Sleep, Standby, Idle ; and Active are not implemented by this driver. It is assumed the devices are always in ; Active Mode. ; It is also assumed that no Software Reset of the IDE bus will be issued after ; the driver is installed. ; Multiple sector transfers are implemented and require additional initialization ; that is only performed by the Non-Resident portion of the driver. .LFCOND .TFCOND ; Use the /X switch to cause FALSE conds not to be listed INCLUDE PARMS.ASM INCLUDE VER.ASM INCLUDE DEFCONFG.ASM ; System configuration INCLUDE DEFBIOS.ASM ; BIOS definitions INCLUDE DEFFMT.ASM ; Disk format header INCLUDE DEFDEV.ASM ; BPB definitions INCLUDE DEFMS.ASM ; MS-DOS Function Definitions INCLUDE DEFMTR.ASM ; MTR ROM data definition INCLUDE DEFDOSI.ASM ; MS-DOS Interrupts INCLUDE DEFSBC.ASM ; PCMBR Definitions USEV20 EQU FALSE ; Must be declared before IDEMACRO if TRUE CLICKonERROR EQU TRUE ; Click the Keyboard on RWV errors INCLUDE DEF_IDE.ASM ; LifeLine IDE Port Definitions INCLUDE IDEMACRO.ASM ; LifeLine IDE V20 instructions DUMMY SEGMENT STACK ;PREVENT LINK ERR. MSG. DUMMY ENDS LLIDEHD segment para public 'code' LLIDEdsk proc far assume CS:LLIDEHD, ES:LLIDEHD, DS:LLIDEHD BEGIN: ; This is the start of the Resident portion of the device driver. DD -1 ; No device driver after this DW DVHA_CTL+DVHA_OCR+DVHA_IOCTL+DVHA_32B+DVHA_NFAT ; IOCTL,open/close/remove,gioctl support DW offset DEV_STRATEGY ; address of strategy routine DW offset DEV_INTERRUPT ; address of interrupt routine TOTALnumPARTS label BYTE DB ? ; Total # of PARTITIONS available for access DB 'LLIDEHD' ; Our Identifer ; These Variables must not change location as external programs depend on this knowledge TOTALnumDEVICES DB ? ; Total # of Physical DEVICES detected at installation ; RWVtable - Used for setting BP necessary for External Far Routine access to RWVphyDEVICE DW PIO_READ_SECS DW PIO_WRITE_SECS DW PIO_VERIFY_SECS ; RWVphyDEVICE - External Access to Read, Write, or Verify Physical Device ; We need this external far routine for INT 13h physical access ; Called with: ; AX:DX = Starting LBA Sector # on Device (Note REVERSE order) ; BX = Count of Physical sectors to Read/Write/Verify ; DS:SI = Ptr to Physical DSK Table (Obtained from PHYdeviceTABLE) DS must be LLIDEHD CS ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ( Obtained from RWVtable) ; RETURNS: ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for untransfered sectors ; NC for Success, with AL = STATUS register ; BX = 0 ; ES:DI adjusted for next transfer, ES and DI have been altered ; USES: CX,DX CALL RWVphyDEVICE ; Read or Write Physical Device RETF PHYdeviceTABLE LABEL NEAR ; PDSK_TABLE'S for Physical Devices DW 4*PDSK_SIZE DUP(0) ; Filled in by INIT, pointed to by IDSK_PDSK_PTR ; Below here nobody cares about the location CUR_LBA LABEL WORD ; Overlapping Current Sector, Cylinder, and Head CUR_SECTOR DB ? CUR_CYLINDER DW ? CUR_HEAD DB ? REQPACK label DWORD RH_OFS DW ? ; request header address RH_SEG DW ? BIOSdskBUF DD ? ;PTR to BIOS DSKBUF CMDTAB DW INITIALIZATION ; Fun 0 - DW MEDIA_CHECK ; Fun 1 - DW GET_BPB ; Fun 2 - DW IOCTL_INPUT ; Fun 3 - DW READ_SECS ; Fun 4 - DW ND_INPUT ; Fun 5 - Char Dev only DW INPUT_STATUS ; Fun 6 - Char Dev only DW INPUT_FLUSH ; Fun 7 - Char Dev only DW WRITE_SECS ; Fun 8 - DW WRITE_VERIFY ; Fun 9 - DW OUTPUT_STATUS ; Fun 10 - Char Dev only DW OUTPUT_FLUSH ; Fun 11 - Char Dev only DW IOCTL_OUT ; Fun 12 - DW OPEN ; Fun 13 - DW CLOSE ; Fun 14 - DW REMOVABLE ; Fun 15 - DW OUTPUT_BUSY ; Fun 16 - Char Dev only DW UNKN_ERR ; Fun 17 - Reserved DW UNKN_ERR ; Fun 18 - Reserved DW GIOCTL ; Fun 19 - Generic IOCTL ; DW UNKN_ERR ; Equ 20 - Reserved ; DW UNKN_ERR ; Equ 21 - Reserved ; DW UNKN_ERR ; Equ 23 - Reserved ; DW GETlogicalDEVICE ; Equ 24 - Get logical device ; DW SETlogicalDEVICE ; Equ 25 - Set logical device ; DW GIOCTL_CHECK ; Fun 26 - Generic IOCTL Check(DOS5+) IDE_GIO_40jmp label word DW IDE_GIO_SDP ;40 Set Device Parameters DW IDE_GIO_WRITE ;41 Write Device DW IDE_GIO_FMT ;42 Write/Verify track DW GENFAIL_ERR DW GENFAIL_ERR DW GENFAIL_ERR DW IDE_GIO_SVOL ;46 Set Volume Label DW IDE_GIO_SACC ;47 Set Access Flag ; DW ;48 Set Media Lock State ; DW ;49 Eject Media IDE_GIO_60jmp label word DW IDE_GIO_GDP ;60 Get Device Parameters DW IDE_GIO_READ ;61 Read Device DW IDE_GIO_VER ;62 Read/Verify track DW GENFAIL_ERR DW GENFAIL_ERR DW GENFAIL_ERR DW IDE_GIO_GVOL ;66 Get Volume Label DW IDE_GIO_GACC ;67 Get Access Flag ; DW ;68 Sense Media Type(DOS5+) ; The Strategy Routine DEV_STRATEGY: MOV CS:RH_SEG, ES ; Save address of request header MOV CS:RH_OFS, BX RETF ; The Interrupt Routine DEV_INTERRUPT: CALL BIOSX_SETSTACK ; Set BIOS_STACK PUSHF CLD PUSH DS PUSH ES PUSH DX PUSH AX PUSH BX PUSH CX LDS BX,CS:REQPACK ; Get address of request header PUSH BP MOV AL,SRH_CMD[BX] ; Get command PUSH DI CMP AL,19 ; Command valid? PUSH SI JA UNKN_ERR CBW SHL AX,1 XCHG DI,AX ; Index into jump table JMP word ptr CS:CMDTAB[DI] ; Jump to proper routine WRITE_VERIFY: ; (FUNCTION 9) XOR BP,BP JMP short WRITE_VERcont WRITE_SECS: ; (FUNCTION 8) MOV BP,offset PIO_WRITE_SECS WRITE_VERcont: MOV AL,SRHS_EWRF ; WRITE Fault Error if CY JMP short WRITEcont READ_SECS: ; (FUNCTION 4) MOV BP,offset PIO_READ_SECS MOV AL,SRHS_ERDF ; READ Fault Error if CY WRITEcont: MOV CS:RWerrCODE,AL ; Set Error Code in case of Errors MOV DX,CRW_START[BX] ; DX = starting sector number XOR SI,SI ; Set High word of double word sector CMP DX,-1 ; Huge request? JNZ IDE_BDTNH MOV SI,word ptr CRW_HUGESS[BX+2] ; Yes, get Dword sector MOV DX,word ptr CRW_HUGESS[BX] IDE_BDTNH: LES DI,CRW_TADDR[BX] ; ES:DI = trans address MOV CX,CRW_CNT[BX] ; CX = count CALL GET_DRIVE_PTR_DS ; DS:SI = Drive table, ret AX=orig SI JZ DISABLED ; Non-valid PARTITION, go show Unknown media TEST byte ptr IDSK_DEVATTR1[SI],IDSK_ACFLG ; Access Disabled? JNZ DISABLED ; Yes, go show Unknown media OR BP,BP ; See if it is a Write/Verify request JNZ NOTwrtVERIFY PUSH AX PUSH DX ; Yes, Write with Verify PUSH CX ; Save registers for Verify PUSH SI MOV BP,offset PIO_WRITE_SECS CALL RWVlogPART ; Write Logical Partition, CX Sectors in AX:DX to ES:DI POP SI POP CX ; Restore registers for Verify POP DX POP AX JC RWVerror ; Write error, exit MOV byte ptr RWerrCODE,SRHS_ECRC ; CRC error if Verify fails MOV BP,offset PIO_VERIFY_SECS NOTwrtVERIFY: CALL RWVlogPART ; Read, Write, or Verify Logical Partition CX Sectors in AX:DX JNC EXIT_DONE ; No errors, we are done RWVerror: MOV CL,0 ; Get Sector Size Shift Count org $-1 ; Might be 0 Shift ERRshftCNT DB ? SHR BX,CL ; Fix Count of remaining sectors in BX MOV AL,0 ORG $-1 ; Get correct error code RWerrCODE DB ? IDE_NSW:LDS SI,REQPACK ; Get back ptr to Request Packet SUB CRW_CNT[SI],BX ; Compute number of sectors Processed JMP short EXIT_ERR ; With read or write fault error in AL DISABLED: MOV AL,SRHS_EUKM ; Show Unknown media MOV BX,CX ; Get total requested to JMP short IDE_NSW ; show No Sectors Processed UNKN_ERR: MOV AL,SRHS_EUKC ; Get Unknown Command Error JMP short EXIT_ERR IOCTL_INPUT: ; (FUNCTION 3) ND_INPUT: ; (FUNCTION 5) INPUT_STATUS: ; (FUNCTION 6) INPUT_FLUSH: ; (FUNCTION 7) OUTPUT_STATUS: ; (FUNCTION 10) OUTPUT_FLUSH: ; (FUNCTION 11) IOCTL_OUT: ; (FUNCTION 12) OPEN: ; (FUNCTION 13) CLOSE: ; (FUNCTION 14) OUTPUT_BUSY: ; (FUNCTION 16) EXIT_DONE: MOV AH,SRHS_DON ; Get Done Bit EXIT: POP SI LDS BX,CS:REQPACK ; Get address of request header POP DI MOV word ptr SRH_STAT[BX],AX ; Set our return Status POP BP POP CX POP BX POP AX POP DX POP ES POP DS POPF CALL BIOSX_RESTACK ; Reset ORIG STACK RETF REMOVABLE: ; (FUNCTION 15) EXIT_BUSY: MOV AH,SRHS_BUI ; Get Busy Bit to show not removable JMP short EXIT MEDIA_CHECK: ; (FUNCTION 1) CALL GET_BPB_PTR_CHK ; Will not return if not Assigned and Enabled TEST AL,IDSK_FDC ; Force Disk Change bit set? MOV AL,CMCS_NOC ; Assume media is unchanged JZ MC_NOT_FDC MOV AL,CMCS_DKN ; Don't know if media changed MC_NOT_FDC: MOV byte ptr CMC_STAT[BX],AL ; Return changed status JMP short EXIT_DONE GET_BPB: ; (FUNCTION 2) CALL GET_BPB_PTR_CHK ; Will not return if not Assigned and Enabled MOV word ptr CBPB_BADDR[BX],SI ; Set in pointer to BPB info MOV word ptr CBPB_BADDR[BX+2],CS MOV byte ptr CBPB_MBYTE[BX], 0F8h ; Set Media ID byte for DOS AND byte ptr CS:IDSK_DEVATTR1[SI-IDSK_SECSZ],NOT IDSK_FDC ; Clear Force Disk Change bit JMP short EXIT_DONE POP_UNK_MEDIA_ERR: POP AX ; Take call off stack UNK_MEDIA_ERR: MOV AL,SRHS_EUKM ; Show Unknown media EXIT_ERR: MOV AH,SRHS_ERR or SRHS_DON ; Mark Error and Done JMP short EXIT ; GIOCTL - Get Device Parameters (FUNCTION 19 sub 60) IDE_GIO_GDP: MOV CX,IDSK_VOLID ; Get length of Parameter Block MOVE_DONE: REP MOVSB ; Perform final move, Volume Info or Parameter Block JMP short EXIT_DONE ; GIOCTL - Get Access Flag (FUNCTION 19 sub 67) IDE_GIO_GACC: MOV AL,0 ; Assume Disabled TEST byte ptr IDSK_DEVATTR1[SI],IDSK_ACFLG ;Access Disabled? JNZ GACC_ENAB ; Yes, Go set 0 INC AX ; Set to 1 for Enabled GACC_ENAB: INC DI ; PCFORMAT requires 0 or 1 STOSB ; Zero for Disabled, 1 for Enabled JMP short EXIT_DONE ; GIOCTL - Set Access Flag (FUNCTION 19 sub 47) IDE_GIO_SACC: AND byte ptr IDSK_DEVATTR1[SI],(not IDSK_ACFLG) ; Assume Enable Access CMP byte ptr ES:[DI+1],0 ; Is it 0 for Disable? JNZ EXIT_DONE OR byte ptr IDSK_DEVATTR1[SI],IDSK_ACFLG ; Yes, Disable Access JMP short EXIT_DONE ; GIOCTL - Set Device Parameters (FUNCTION 19 sub 40) IDE_GIO_SDP: PUSH ES PUSH DS XCHG SI,DI ; Ours to ES:DI, New one to DS:SI POP ES POP DS CALL CHKnewBPB ; Check for change in physical attributes JZ IDE_GIO_GDP ; No change, go set it GENFAIL_ERR: MOV AL,SRHS_EGNF ; Show General failure for now JMP short EXIT_ERR ; GIOCTL - Get Volume Label (FUNCTION 19 sub 66) IDE_GIO_GVOL: ADD SI,IDSK_VOLID INC DI ; Point to Serial #, Vol Label, FAT ID INC DI ; in the return parameter buffer IDE_SVOL_CONT: MOV CX,4+11+8 ; Total size for all three pieces of info JMP short MOVE_DONE ; GIOCTL - Generic IOCTL FUNCTION 19 GIOCTL: ; CMP byte ptr CGIO_MAJOR[BX],GIC_DSK ; Block device? ; JNZ UNKN_ERR MOV AL,byte ptr CGIO_MINOR[BX] LES DI,CGIO_BADDR[BX] ; Get parameter block addr in ES:DI MOV SI,offset IDE_GIO_60jmp TEST AL,20H JNZ IDE_GIO_60 MOV SI,offset IDE_GIO_40jmp IDE_GIO_60: AND AL,9FH CMP AL,7 ; Valid Function? JA GENFAIL_ERR CBW SHL AX,1 ADD SI,AX ; Save ptr to proper function CALL GET_DRIVE_PTR_DS ; Get pointer to drive in DS:SI JZ UNK_MEDIA_ERR ; Non-valid PARTITION XCHG BX,AX ; Orig SI returned in AX JMP word ptr [BX] ; Transfer to proper function GET_BPB_PTR_CHK: CALL GET_BPB_PTR ; Get ptr to BPB in DS:SI, check for Assigned Partition JZ POP_UNK_MEDIA_ERR ; Do not return if not Assigned MOV AL,CS:IDSK_DEVATTR1[SI-IDSK_SECSZ] TEST AL,IDSK_ACFLG ; Access Disabled ? JNZ POP_UNK_MEDIA_ERR ; Do not return if Disabled RETN ; Return AL = IDSK_DEVATTR1 if Assigned and Enabled ; GIOCTL - Set Volume Label (FUNCTION 19 sub 46) ; We will Read Logical Sector 0 (The boot loader) ; save the new Volume Label, and then Write it back. ; Finally we will save the new Volume information to our IDSK_TABLE. IDE_GIO_SVOL: PUSH SI ; Save ptr to partition table PUSH ES ; Save parameter block addr PUSH DI CALL READlogPART0 ; To BIOS DSKBUF LEA DI,PCFMT_VID[DI-512+FMT_OFFSET] ;Point to Serial # in DSKBUF ES:DI POP SI POP DS ; Get parameter block addr in DS:SI INC SI ; Point to Serial # Vol ID INC SI MOV CX,4+11+8 REP MOVSB ; Move in the new label PUSH CS POP DS ; Get back ptr to partition table POP SI ; in DS:SI PUSH SI MOV BP,offset PIO_WRITE_SECS CALL RWlogPART0 ; Write DSKBUF back to boot loader LEA SI,PCFMT_VID[DI-512+FMT_OFFSET] ;Point to Serial # in DSKBUF DS:SI POP DI PUSH ES PUSH DS ADD DI,IDSK_VOLID ; Ptr to Serial # in IDSK_TABLE ES:DI POP ES POP DS JMP short IDE_SVOL_CONT ; Move the new label to our Drive Table ; Physical Access Routines to our Drives/Devices ; These routines will allow PCFORMAT "physical" access to our drives. ; In order to accommodate very large devices we will break up the physical devices ; into separate logical drives. Each "Extended" partition will be viewed as a ; separate physical drive by these routines. ; IDSK_EXTBASE is the offset to the real beginning of the device. These routines ; actually have access from IDSK_EXTBASE to the end of the physical device, as no ; check on the cylinder request is performed. ; IDSK_CYLINDERS is the total logical cylinders from IDSK_EXTBASE to the end of the ; physical device not the logical drive so the actual device size may be calculated. ; So a Cylinder 0, Head 0, Sector 0, request (CHS) is viewed to be IDSK_EXTBASE. ; Furthermore Logical Sector 0 on any partition is IDSK_HIDSEC from IDSK_EXTBASE and ; there can be up to 4 partitions on each logical drive. ; If something other than 512 Bytes per Sector is used, programs like PCFORMAT may ; have a problem with buffer overflows. We will force only 512 byte reads/writes to ; both logical and physical Sector 0 if only one logical sector is requested to try ; and not overun their buffers. ; This is all the information that is relevant in the Master Boot Record ; (physical sector 0) or the Boot Loader (logical sector 0). ; This "Physical" access is calculated from the SPT and # of Heads in the BPB and ; is not necessarily the same CHS translation used by Int 13h to gain its "Physical" ; access to the devices. Neither of these CHS translations has any particular ; relationship to the actual Physical Geometry of the device stored in the PDSK ; physical drive table. ; GIOCTL - Read Device (FUNCTION 19 sub 61) IDE_GIO_READ: MOV BP,offset PIO_READ_SECS MOV AL,SRHS_ERDF ; Read Fault Error if CY JMP short IDE_GIO_RW ; GIOCTL - Write Device (FUNCTION 19 sub 41) IDE_GIO_WRITE: MOV BP,offset PIO_WRITE_SECS MOV AL,SRHS_EWRF ; WRITE Fault Error if CY IDE_GIO_RW: MOV BX,ES:[DI+5] ; Get starting sector request CALL CalcLogicalSector ; Return in AX:DX MOV CX,ES:[DI+7] ; Get sector count LES DI,ES:[DI+9] ; Get transfer address MOV BX,AX ; Check if Sector 0 on this disk OR BX,DX ; Create Sector 0 flag JZ IDE_GIO_RW_MBR ; Yes, it is the Master Boot requested CMP DX,IDSK_HIDSEC[SI] JNZ IDE_GIO_RW_MBR ; Not the Boot Loader request CMP AX,IDSK_HIDSEC[SI+2] JNZ IDE_GIO_RW_MBR XOR BX,BX ; Reset to show it is Sector 0 requested IDE_GIO_RW_MBR: CALL RWVlogDRIVE ; READ, WRITE, Sectors on Logical Drive JNC JMPDONE GIO_RW_ERR: MOV AL,0 ORG $-1 ; Get Read or Write Fault in AL GIO_RW_ERRC DB ? JMP EXIT_ERR ; Go to Exit Error With ; GIOCTL - Format/Verify track (FUNCTION 19 sub 42) IDE_GIO_FMT: TEST byte ptr ES:[DI],1 ; Check if format is ok ? JZ IDE_GIO_VER ; No, go verify XOR AX,AX STOSB ; Show format is ok JMPDONE:JMP EXIT_DONE ; GIOCTL - Verify track (FUNCTION 19 sub 62) IDE_GIO_VER: ; Patched to JMPDONE if User selected Fast Format MOV BP,offset PIO_VERIFY_SECS MOV AL,SRHS_ERDF ; Read Fault Error if CY XOR BX,BX ; Starting Sector 0 on track CALL CalcLogicalSector ; Return in AX:DX MOV CX,1 ; Assume 1 track TEST byte ptr ES:[DI],10B ; Perform multiple tracks ? JZ VERIFYnxtTRK MOV CX,ES:[DI+5] ; # of tracks Requested as loop counter VERIFYnxtTRK: PUSH CX PUSH AX PUSH DX MOV CX,IDSK_SPT[SI] ; VERIFY 1 track at a time PUSH SI CALL RWVlogDRIVE ; VERIFY Sectors on Logical Drive POP SI POP DX POP AX POP CX JC GIO_RW_ERR ; Yes, abort ADD DX,IDSK_SPT[SI] ; Setup for next track ADC AX,0 LOOP VERIFYnxtTRK ; Keep going until done JMP short JMPDONE LLIDEdsk endp ; Support Subroutines for Resident Driver ; CalcLogicalSector - Convert IOCTL Physical read/write requests to Logical Sector ; Called With: ; AL = Error code if an error occurs ; BX = Starting Sector # of request (Zero based) ; DS:SI = Ptr to Partition DSK Table ; ES:DI = Ptr to GIOCTL phy request structure ; Returns: ; AX:DX = Starting Logical Sector # on Logical Drive (Note REVERSE order) ; Uses: CX CalcLogicalSector: MOV GIO_RW_ERRC,AL ; Save error code MOV AX,IDSK_SPT[SI] ; Get logical sectors/head MUL word ptr ES:[DI+1] ; Mult by Head request XCHG CX,AX ; Save in CX MOV AX,IDSK_SECLCYL[SI] ; Get logical sectors/cylinder MUL word ptr ES:[DI+3] ; Mult by cylinder request ADD AX,CX ; Add sectors from Head request ADC DX,0 ADD AX,BX ; Add starting sector request ADC DX,0 XCHG DX,AX ; Fix order for call RETN ; CHKnewBPB - See if new BPB request is valid ; Called with: ; DS:SI pointing to passed Parameter block ; ES:DI pointing to our drive table info ; Returns: ; NZ for invalid request. Change in physical characteristics. ; ZR for no change, Also validates the BPB and sets force disk change CHKnewBPB: CMP byte ptr IDSK_EBS[SI],29h ; New one have Valid extended boot signature? JNZ NZRET MOV AX,IDSK_SECSZ[SI] CMP AX,CS:IDSK_SECSZ[DI] ; Do not allow changes in physical characteristics JNZ NZRET MOV AX,IDSK_SPT[SI] ; Sector Size, Sectors Per Track, or # of Heads MUL word ptr IDSK_HEADS[SI] CMP AX,CS:IDSK_SECLCYL[DI] JNZ NZRET ; Set Force Disk Change MOV byte ptr IDSK_DEVATTR1[SI],IDSK_FDC+IDSK_VBPB+IDSK_ASGN NZRET: RETN ; Ret ZR for GOOD, NZ for BAD ; GET_DRIVE_PTR_DS - Get PTR to IDSK_TABLE for partition ; Called with: ; DS:BX = Device Driver Packet GET_DRIVE_PTR_DS: ;Get pointer to drive in DS:SI PUSH CS CALL GET_BPB_PTR ;Must be called with DS:BX as packet POP DS LEA SI,[SI-IDSK_SECSZ] ;Make into Drive Table ptr, retain ZR flag RETN ;Not Zero Flag for Assigned Partition GET_BPB_PTR: ;Must be called with DS:BX as packet MOV AL,SRH_UNIT[BX] ;Get unit CBW ;Drive might be 0FFh SHL AX,1 ; so ptr is 0FFFEh or -2 XCHG SI,AX MOV SI,CS:BPB_TABLE[SI] ;SI = pointer to BPB TEST byte ptr CS:IDSK_DEVATTR1[SI-IDSK_SECSZ],IDSK_ASGN ;Not Zero Flag for Assigned Partition RETN ;AX = entry value of SI ; READlogPART0 - Read Logical Sector 0 from Partition ; Called with: ; DS:SI = Ptr to DSK Partition Table READlogPART0: MOV BP,offset PIO_READ_SECS ; RWlogPART0 - Read or Write Logical Sector 0 from Partition ; Called with: ; DS:SI = Ptr to DSK Partition Table ; BP = PIO_WRITE_SECS or PIO_READ_SECS RWlogPART0: LES DI,BIOSdskBUF ; Use BIOS DSKBUF for data transfer ; RWlogPART0buf - Read or Write Logical Sector 0 from Partition ; Called with: ; ES:DI = Transfer Address for data ; DS:SI = Ptr to DSK Partition Table ; BP = PIO_WRITE_SECS or PIO_READ_SECS RWlogPART0buf: XOR DX,DX ; Select Logical Sector 0 XOR AX,AX MOV CX,1 ; 1 Sector ; RWVlogPART - Read Write Verify Logical Partition ; Called with: ; AX:DX = Starting Logical Sector # in Partition (Note REVERSE order) ; CX = Count of Logical Sectors ; DS:SI = Ptr to DSK Partition Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; RETURNS: ; SI = Ptr to Physical DSK Table ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for untransfered sectors ; NC for Success, with AL = STATUS register ; BX = 0 ; ES:DI adjusted for next transfer, ES and DI have been altered ; USES: CX,DX ; We will force only 512 byte reads/writes to Sector 0 if only one logical sector ; is requested to try and not overun unknown buffers. This is all the information ; that is relevant anyway. No checks are made for attempting to read/write past ; the end of the partitions/drives/devices. RWVlogPART: MOV BX,AX ; Calc IF sector 0 request OR BX,DX ; By creating BX as a flag ADD DX,IDSK_HIDSEC[SI] ; Make relative to start of Logical Drive ADC AX,IDSK_HIDSEC[SI+2] ; RWVlogDRIVE - Read Write Verify Logical Drive ; Called with: ; AX:DX = Starting Logical Sector # on Logical Drive (Note REVERSE order) ; BX = Sector 0 Flag ; CX = Count of Logical Sectors ; DS:SI = Ptr to DSK Partition Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; RETURNS: ; SI = Ptr to Physical DSK Table ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for untransfered sectors ; NC for Success, with AL = STATUS register ; BX = 0 ; ES:DI adjusted for next transfer, ES and DI have been altered ; USES: CX,DX RWVlogDRIVE: OR BX,BX ; See if starting sector 0 MOV BX,IDSK_SSSC[SI] ; Sector Size Shift Count to BX XCHG CX,BX ; Sector Size Shift to CX, Count to BX MOV ERRshftCNT,CL ; Save for possible Error Exit JCXZ SKIPSSADJ ; No adjustment necessary JNZ PERFADJ ; Not starting sector 0, Adj both CMP BX,1 ; Only 1 requested? JZ SSADJ ; Yes, skip count adj if 1, starting @ sec 0 PERFADJ:SHL BX,CL ; Adjust Count SSADJ: SHL DX,1 RCL AX,1 ; Adjust Starting sector LOOP SSADJ SKIPSSADJ: ; RWVphyDRIVE - Read Write Verify Physical Drive ; Called with: ; AX:DX = Starting LBA Sector # on Logical Drive (Note REVERSE order) ; BX = Count of Physical sectors ; DS:SI = Ptr to Partition DSK Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; RETURNS: ; SI = Ptr to Physical DSK Table ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for untransfered sectors ; NC for Success, with AL = STATUS register ; BX = 0 ; ES:DI adjusted for next transfer, ES and DI have been altered ; USES: CX,DX RWVphyDRIVE: ADD DX,IDSK_EXTBASE[SI] ; Adjust to start of Physical device ADC AX,IDSK_EXTBASE[SI+2] MOV SI,IDSK_PDSK_PTR[SI] ; Get Ptr to Physical DSK Table ; RWVphyDEVICE - Read Write Verify Physical Device ; Called with: ; AX:DX = Starting LBA Sector # on Device (Note REVERSE order) ; BX = Count of Physical sectors ; DS:SI = Ptr to Physical DSK Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; RETURNS: ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for untransfered sectors ; NC for Success, with AL = STATUS register ; BX = 0 ; ES:DI adjusted for next transfer, ES and DI have been altered ; USES: CX,DX RWVphyDEVICE: MOV TOTAL_COUNT,BX ; Save total to Read/Write/Verify MOV CUR_LBA,DX ; Assume LBA MOV CUR_LBA+2,AX MOV CX,PDSK_CTRLFLG[SI] TEST CH,PDSK_CF1_LBA ; Logical Block Addressing Supported? JNZ IT_IS_LBA XCHG DX,AX ; Sector to DX:AX DIV word ptr PDSK_SECPCYL[SI] ; Calc Cylinder # MOV CUR_CYLINDER,AX XCHG DX,AX ; Get remainder DIV byte ptr PDSK_PHYSPH[SI] ; Calc Head # MOV CUR_HEAD,AL MOV AL,AH INC AX ; Sectors are 1 based MOV CUR_SECTOR,AL ; Set correct sector # IT_IS_LBA: MOV AL,CL ; Get Buss Control Flag OUT IDE_AC,AL ; Select Primary or Seconday Bus+correct wait states MOV AL,CH ; Get Drive Control Flag AND AL,PDSK_CF1_LBA+PDSK_CF1_D01 ; Isolate device 0 or 1 with or without LBA bits MOV CUR_LBA_DEV,AL ; Save for setting correct head OUT IDE_DH,AL ; Select correct device with/without LBA TEST AL,CB_DH_LBA ; Logical Block Addressing Supported ? JZ RWVchsDEVICE ; No, do it in CHS mode ; RWVnxtLBA - Read Write Verify in LBA Mode ; Arrive with: ; BX = Count of Physical sectors ; DS:SI = Ptr to Physical DSK Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; CUR_LBA,TOTAL_COUNT,CUR_LBA_DEV initialized ; Correct Buss and DRIVE selected RWVnxtLBA: ; BX = Total Remaining Sectors CALL RWV_LBA_CHS ; Read/Write/Verify next block, ; will not return if errors or finished NUMsecsTRAN EQU 127 ADD word ptr CUR_LBA,NUMsecsTRAN ; Calc start of next block to transfer ADC word ptr CUR_LBA+2,0 JMP short RWVnxtLBA ; Go process next block ; RWVchsDEVICE - Read Write Verify in Cylinder, Head, Sector Mode ; Arrive with: ; BX = Count of Physical sectors ; DS:SI = Ptr to Physical DSK Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; CUR_HEAD,CUR_CYLINDER,CUR_SECTOR,TOTAL_COUNT,CUR_LBA_DEV initialized ; DRIVE selected and not busy RWVchsDEVICE: ; BX = Total Remaining Sectors CALL RWV_LBA_CHS ; Read/Write/Verify next block, ; will not return if errors or finished ADD byte ptr CUR_SECTOR,NUMsecsTRAN ; Calc next block to read/write RWVchkCHS: MOV AL,CUR_SECTOR ; See if we need to switch heads CMP AL,PDSK_PHYSPH[SI] JBE RWVchsDEVICE ; Will not switch heads, go do next block SUB AL,PDSK_PHYSPH[SI] ; Fix correct next sector to process MOV CUR_SECTOR,AL INC CUR_HEAD ; Next head MOV AL,CUR_HEAD CMP AL,PDSK_PHYHEADS[SI] JB RWVchkCHS ; Not next Cylinder, check next head again XOR AX,AX MOV CUR_HEAD,AL ; Start back at Head 0 INC CUR_CYLINDER ; Next Cylinder JMP short RWVchkCHS ; Go see if we need to bump head again ; RWV_LBA_CHS - Read Write Verify next block up to 127 sectors ; Called with: ; BX = Total Count of Physical sectors remaining to transfer ; DS:SI = Ptr to Physical DSK Table ; ES:DI = Transfer Address for data ; BP = PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; CUR_LBA or CUR_HEAD,CUR_CYLINDER,CUR_SECTOR, and TOTAL_COUNT,CUR_LBA_DEV initialized ; Correct Buss and DRIVE selected ; Wait for Not busy. ; Program the 4 IDE registers IDE_SN, IDE_CL, IDE_CH, IDE_DH ; Also adjust the block count of physical sectors to process to no more than 127 ; to assure no segment wrap will occur. Adjust ES:DI for no segment wrap. ; Calc # of words. Program IDE_SC for # of sectors to process. Wait for DRDY and ; Finally jump to PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS which will ; Return only if no errors occur and there are more sectors to process. ; Otherwise will POP this call off the stack and Return to the previous caller. RWV_LBA_CHS: IN AL,IDE_STAT ; Read Status Register TEST AL,CB_STAT_BSY ; Check for Busy, also clear any errors JNZ RWV_LBA_CHS ; Unconditional wait for NOT Busy comment ^ MOV AL,byte ptr CUR_LBA ; Or CUR_SECTOR OUT IDE_SN,AL OUT IDE_SN,AL MOV AL,byte ptr CUR_LBA+1 ; or LOW BYTE CYLINDER OUT IDE_CL,AL OUT IDE_CL,AL MOV AL,byte ptr CUR_LBA+2 ; Or HIGH BYTE CYLINDER OUT IDE_CH,AL OUT IDE_CH,AL MOV AL,byte ptr CUR_LBA+3 ; Or HEAD OR AL,0 ; Add correct device and LBA bit if called for org $-1 CUR_LBA_DEV DB ? OUT IDE_DH,AL OUT IDE_DH,AL ^ MOV AX,CUR_LBA ; Or CUR_SECTOR and LOW BYTE CYLINDER MOV DX,IDE_SN ; Point to Sector # register OUT DX,AX ; Program IDE_SN and IDE_CL regs INC DX INC DX ; Point to IDE_CH reg MOV AX,CUR_LBA+2 ; Or HIGH BYTE CYLINDER and HEAD OR AH,0 ; Add correct device and LBA bit if called for org $-1 CUR_LBA_DEV DB ? OUT DX,AX ; Program IDE_CH and IDE_DH regs MOV CL,4 MOV AX,DI ; Adjust ES:DI so no wrap will occur AND DI,0FH SHR AX,CL MOV CX,ES ADD AX,CX MOV ES,AX CMP BX,NUMsecsTRAN ; More than 1 segment remaining to transfer? JB RWVlast ; Not more than 1 seg remaining MOV BX,NUMsecsTRAN ; Max for 1 segment to ensure no wrap with ES:DI RWVlast:MOV AX,BX ; Get Sector Count for this block SUB TOTAL_COUNT,AX ; Keep track of how many sectors Remaining OUT IDE_SC,AL ; Set # of sectors to read/write/verify XCHG BL,BH ; Setup for data transfer by setting # of words in BX UWAIT4DRDY: IN AL,IDE_ASTAT ; Read Aux Status Register TEST AL,CB_STAT_RDY ; Check for drive ready to accept command (DRDY) JZ UWAIT4DRDY ; Unconditional wait for Drive Ready JMP BP ; PIO_WRITE_SECS or PIO_READ_SECS or PIO_VERIFY_SECS ; PIO_WRITE_SECS - ; Called with: ; BX = Count of words to write ; DS:SI = Ptr to Physical DSK Table ; ES:DI = Adjusted Transfer Address for data ; DRDY set and all IDE registers ready for the WRITE command PIO_WRITE_SECS: MOV AL,PDSK_MW_CMD[SI] ; CMD_WRITE_SECTORS or CMD_WRITE_MULTIPLE ; PIO DATA-OUT COMMAND PROTOCOL ; ENTRY: AL = Command to execute ; BX = # of words to transfer (IDE_SC * 256) ; DS:SI = Ptr to Physical DSK Table ; ES:DI transfer address ; All IDE registers are programmed and DRDY is set ; RETURNS: ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for unwritten sectors ; POPS call off stack ; NC for good with BX = Remaining TOTAL_COUNT and ZR flag indicator ; ES:DI adjusted for next transfer, ES and DI have been altered ; USES: CX,DX PIO_DATAOUT: OUT IDE_CMD,AL ; Start command PUSH ES ; Switch our registers for string instructions XCHG SI,DI ; CS:[DI] is now our PDSK table POP DS ; DS:SI is now source data ptr MOV DX,IDE_DATAOUT ; Get word write register value ; HPIOO0: Check_Status State: ; This state is entered when the host has written a PIO data-out command to the device; ; or when a DRQ data block has been written and nIEN is set to one; ; or when a DRQ data block has been written, nIEN is cleared zero, ; and INTRQ has been asserted. ; When in this state, the host shall read the device Status register. ; When entering this state from the HI4 state, ; the host shall wait 400 ns before reading the Status register. ; When entering this state from the HPIOO1 state, ; the host shall wait one PIO transfer cycle time before reading the Status register. ; The wait may be accomplished by reading the Alternate Status register ; and ignoring the result. ; We set our count of words to read to allow at least 6 clock tics @ 15 MHz (400ns) HPIOO0: ; Also >> one PIO transfer cycle (8 clocks) MOV CX,CS:PDSK_BLKWRDS[DI] ; Get # of words in a block transfer CMP BX,CX ; Remaining words less than full block? JA WRITEfullBLK MOV CX,BX ; Set CX to transfer only the remaining words WRITEfullBLK: SUB BX,CX ; Adjust Remaining words to transfer ; Transition HPIOO0:HPIOO0: When BSY is set to one and DRQ is cleared to zero, ; the host shall make a transition to the HPIOO0: Check_Status state. HPIOO0a: IN AL,IDE_ASTAT ; Read Aux Status TEST AL,CB_STAT_BSY ; Check for busy JNZ HPIOO0a ; Unconditional wait for NOT Busy ; Transition HPIOO0:HI0: When BSY is cleared to zero and DRQ is cleared to zero, ; then the device has completed the command and shall make a transition ; to the HI0: Host_Idle state. ; If an error is reported, the host shall perform appropriate error recovery. IN AL,IDE_STAT ; Read Status Register, 400ns after Not Busy ; Transition HPIOO0:HPIOO1: When BSY is cleared to zero and DRQ is set to one, ; the host shall make a transition to the HPIOO1: Transfer_Data state. TEST AL,CB_STAT_DRQ ; Check for DRQ JZ PIOO_ERROR ; Error if DRQ not set ; HPIOO1: Transfer_Data State: ; This state is entered when the BSY is cleared to zero, DRQ is set to one. ; When in this state, the host shall write the device Data register to transfer data. CLI V20OUT: REP_OUTSW ; Write words, different code for V20 and 8088 STI DONEMOVE: ; Transition HPIOO1:HPIOO1: When the host has written the device Data register and ; the DRQ data block transfer has not completed, ; then the host shall make a transition to the HPIOO1: Transfer_Data state. ; Transition HPIOO1:HPIOO0: When the host has written the device Data register, ; the DRQ data block has been transferred, and nIEN is set to one, ; then the host shall make a transition to the HPIOO0: Check_Status state. OR BX,BX ; Any remaining words? JNZ HPIOO0 ; Go wait for drive to say it is ready for more ; Transition HPIOO0:HI0: When BSY is cleared to zero and DRQ is cleared to zero, ; then the device has completed the command and ; shall make a transition to the HI0: Host_Idle state. ; If an error is reported, the host shall perform appropriate error recovery. ; We must wait for last write to finish before checking for errors ; When entering this state from the HPIOO1 state, ; the host shall wait one PIO transfer cycle time before reading the Status register. ; The wait may be accomplished by reading the Alternate Status register ; and ignoring the result. ; STI;OR BX,BX;JNZ; = 2+3+4 = 9 Clocks, 1 PIO cycle = 8 clocks WAIT4WR2FIN: IN AL,IDE_ASTAT ; Read Aux Status TEST AL,CB_STAT_BSY ; Check for busy JNZ WAIT4WR2FIN ; Unconditional wait for NOT Busy OR SP,SP ; Set NZ for ERR_CHECK jump PIOO_ERROR: PUSH CS ; Restore our registers XCHG SI,DI ; ES:DI next transfer address POP DS ; DS:SI ptr to Physical Device Table JNZ ERR_CHECK ; Go see if an error occured on the write JMP short PIO_ERROR ; DRQ was not set, we have an error, AL has Status ; PIO_READ_SECS - ; Called with: ; BX = Count of Words to READ ; DS:SI = Ptr to Physical DSK Table ; ES:DI = Transfer Address for data ; DRDY set and all IDE registers ready for the READ command PIO_READ_SECS: MOV AL,PDSK_MR_CMD[SI] ; CMD_READ_SECTORS or CMD_READ_MULTIPLE ; PIO DATA-IN COMMAND PROTOCOL ; ENTRY: AL = Command to execute ; BX = # of words to transfer (IDE_SC * 256) ; DS:SI = Ptr to Physical DSK Table ; ES:DI transfer address ; All IDE registers are programmed and DRDY is set ; RETURNS: ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for untransfered sectors ; POPS call off stack ; NC for good read with BX = TOTAL_COUNT and ZR flag indicator ; ES:DI adjusted for next read, ES and DI have been altered ; USES: CX,DX PIO_DATAIN: OUT IDE_CMD,AL ; Start read sector command MOV DX,IDE_DATAIN ; Get word read register value HPIOI1: ; Check_Status State: ; This state is entered when the host has written a PIO data-in command ; to the device and nIEN is set to one, or when INTRQ is asserted. ; When in this state, the host shall read the device Status register. ; When entering this state from the HI4 state, ; the host shall wait 400 ns before reading the Status register. ; When entering this state from the HPIOI2 state, ; the host shall wait one PIO transfer cycle time before reading the Status register. ; The wait may be accomplished by reading the Alternate Status register ; and ignoring the result. ; We set our count of words to read to allow at least 6 clock tics @ 15 MHz (400ns) MOV CX,PDSK_BLKWRDS[SI] ; Get # of words in a block transfer CMP BX,CX ; Remaining words less than full block? JA READfullBLK MOV CX,BX ; Set CX to transfer only the remaining words READfullBLK: ; Also >> 1 PIO cycle (8 clocks) SUB BX,CX ; Adjust Remaining words to transfer ; Transition HPIOI1:HPIOI1: When BSY is set to one, the host shall make a transition ; to the HPIOI1: Check_Status state. HPIOI1a: IN AL,IDE_ASTAT ; Read Aux Status TEST AL,CB_STAT_BSY ; Check for busy JNZ HPIOI1a ; Unconditional wait for NOT Busy ; Transition HPIOI1:HI0: When BSY is cleared to zero and DRQ is cleared to zero, ; then the device has completed the command with an error. ; The host shall perform appropriate error recovery and make a transition to the HI0: Host_Idle state. IN AL,IDE_STAT ; Read Status Register, > 400ns after Not Busy TEST AL,CB_STAT_DRQ ; as other bits may not be valid yet under old ATA specs. JZ PIO_ERROR ; If NO DRQ, error exit ; Transition HPIOI1:HPIOI2: When BSY is cleared to zero and DRQ is set to one, ; the host shall make a transition to the HPIOI2: Transfer_Data state. HPIOI2: ; Transfer_Data State: ; This state is entered when the BSY is cleared to zero, ; DRQ is set to one, and the DRQ data block transfer has not completed. ; When in this state, the host shall read the device Data register to transfer data. CLI V20IN: REP_INSW ; Read words, different code for V20 and 8088 STI ; Transition HPIOI2:HPIOI2: When the host has read the device status register ; and the DRQ data block transfer has not completed, ; then the host shall make a transition to the HPIOI2: Transfer_Data state. ; Transition HPIOI2:HPIOI1: When the host has read the device Data register ; and the DRQ data block has been transferred, ; all blocks for the command have not been transferred, and nIEN is set to one, ; then the host shall make a transition to the HPIOI1: Check_Status state. OR BX,BX ; Any Words left in this request? JNZ HPIOI1 ; Yes, go wait for not busy and DRQ ; Transition HPIOI2:HI0: When the host has read the device Data register ; and all blocks for the command have been transferred, ; then the host shall make a transition to the HI0: Host_Idle state. ; The host may read the Status register. ERR_CHECK: IN AL,IDE_STAT ; Read the Status Register TEST AL,CB_STAT_ERR+CB_STAT_DF+CB_STAT_DRQ ; Check for ERR, Dev Fault, or DRQ still set? JNZ PIO_ERROR MOV BX,0 ; Get Total remaining sectors org $-2 TOTAL_COUNT DW 0 OR BX,BX ; Return BX = total remaining sectors JZ POPRET ; None left to process NC exit RETN PIO_ERROR: XCHG CX,AX ; Save STATUS in CL IN AL,IDE_SC ; Get Remaining Sectors in block CBW ; Can not be more than 127 (7Fh) ADD AX,TOTAL_COUNT ; Calc Total Remaining XCHG BX,AX ; Return BX = Total Remaining Sectors IN AL,IDE_ERR ; Read actual ERROR MOV CH,AL ; ERROR to CH IN AL,IDE_STAT ; Clear possible error condition if CLICKonERROR MOV AL,ZKEYCLK ; Get Click command OUT ZKEYBRDC,AL ; Click the keyboard to show an Error occured endif XCHG CX,AX ; Return orig STATUS in AL, ERROR in AH STC ; CY for Error, BX = Total Remaining Sectors POPRET: XCHG CX,AX MOV AL,0 ; Get # of wait states for NVsRAM org $-1 ; Deselect J1 and J2, Select 0 wait states NVsRAMwaitSTATES DB CB_AC_0WAITS+CB_AC_EWPEEP+CB_AC_BUS0 ; for NVsRAM as Default OUT IDE_AC,AL ; Reset Bus XCHG CX,AX POP CX ; Take call off stack RETN ; Finished processing, or ERROR occured ; PIO_VERIFY_SECS - ; Called with: ; DRDY set and all IDE registers ready for the VERIFY command ; PIO_VERIFY_SECS: MOV AL,CMD_READ_VERIFY_SECTORS ; Get the VERIFY sector command ; NON-DATA COMMAND PROTOCOL ; Called with: ; DRDY set and all IDE registers ready for the NON DATA command ; RETURNS: ; CY for Error, with AL = STATUS and AH = ERROR registers ; BX = Remaining TOTAL_COUNT adjusted for unVerified sectors ; POPS call off stack ; NC for good read with BX = TOTAL_COUNT and ZR flag indicator ; USES: CX,DX ; We will simply issue the command, wait for Not Busy, then see if an error occured. NON_DATA: OUT IDE_CMD,AL ; Start VERIFY sector command ; HND1: Check_Status State: ; This state is entered when the host has written a non-data command to the device ; and the nIEN bit in the device has been set to one, or when INTRQ has been asserted. ; When in this state, the host shall read the device Status register. ; When entering this state from another state other than when an interrupt has occurred, ; the host shall wait 400 ns before reading the Status register. MOV DX,IDE_ASTAT ; 400ns Delay HND1: IN AL,DX ; Read Aux Status Register TEST AL,CB_STAT_BSY ; Check for Busy JNZ HND1 ; Unconditional wait for NOT Busy ; Transition HND1:HI0: When the status read indicates that BSY is cleared to zero, ; the host shall make a transition to the HI0: Host_Idle state. ; If status indicates that an error has occured, ; the host shall take appropriate error recovery action. JMP short ERR_CHECK ; These Drive data tables are dynamic in size. We adjust the amount of memory to ; reserve depending on how many actual partitions we assign. BPB_TABLE LABEL WORD DW 25 DUP (0) IDSK_TABLE LABEL NEAR DW IDSK_SIZE*25 DUP(0) ; End of Resident Driver Code. Everything past here is thrown away after INITIALIZATION. XJ1M0flag DB FALSE XJ1S1flag DB FALSE ; Command Line Exclude Drive Detection flags XJ2M0flag DB FALSE XJ2S1flag DB FALSE FJ1M0flag DB FALSE FJ1S1flag DB FALSE ; Found Drive with Detection flags FJ2M0flag DB FALSE FJ2S1flag DB FALSE MJ1M0flag DB 9 MJ1S1flag DB 9 ; Command Line Default Transfer Mode flags MJ2M0flag DB 9 MJ2S1flag DB 9 WJ1M0flag DB CB_AC_0WAITS+CB_AC_BUS1+CB_AC_EWPEEP+CB_AC_MASTER WJ1S1flag DB CB_AC_0WAITS+CB_AC_BUS1+CB_AC_EWPEEP ; Command Line Wait States + Connector WJ2M0flag DB CB_AC_0WAITS+CB_AC_BUS2+CB_AC_EWPEEP+CB_AC_MASTER WJ2S1flag DB CB_AC_0WAITS+CB_AC_BUS2+CB_AC_EWPEEP ; + Enable EPROM Write Protect IJ1M0flag DB FALSE IJ1S1flag DB FALSE ; Got Drive Identify Buffer flag IJ2M0flag DB FALSE IJ2S1flag DB FALSE ;DJ1M0flag DB CB_DH_DEV0 ;DJ1S1flag DB CB_DH_DEV1 ; Drive Select bits ;DJ2M0flag DB CB_DH_DEV0 ;DJ2S1flag DB CB_DH_DEV1 TOTALnumDRIVES DB 0 ; Current total # of Simulated Mini-disk drives CLminPARTS DB 1 ; Maximum # of Partitions to install from Command line RemainDOSdrvs DB 26 ; Remaining # of Partitions to assign CLUCWAITIME DW 0 ; # of 1/4 sec Unconditional waits FirstDOSdrv DB ? ; First DOS drive Letter to Use MTRpowerON DB FALSE ; MTR detected condition ;CUR_EXT_BASE DW 0,0 ; Current Extended Base Sector # RETRY_COUNT DB 3 ; Retry reads a maximum of 3 times BIOSsignONscrn DB (TRUE AND 0FFH) ; Assume BIOS sign-on screen exists WAITSTATES DB CB_AC_0WAITS,CB_AC_1WAITS,CB_AC_2WAITS,CB_AC_3WAITS,CB_AC_4WAITS if NOT USEV20 CLV20FLAG DB FALSE ; /V Flag to disable patching of V20 specific instructions endif SIGNonMSG DB 13,10,9,'LifeLine IDE Hard Disk Device Driver Version 4.00',13,10,'$' NOcontERR DB 13,10,7,'ERROR:No LifeLine IDE Controller Detected!$' NOdrvsERR DB 13,10,7,'ERROR:No LifeLine IDE Drives Detected!$' if USEV20 NOv20ERR DB 13,10,7,'ERROR:V20 not detected!$' endif BADverERR DB 13,10,7,'ERROR:LLIDEHD only works with BIOS, MTR and DOS Version 4 !$' NOinstMSG DB 13,10,10,'***Driver NOT Installed Due To Prior ERROR***$' DrvUnFMT DB 'Unknown Format Part ' ; Unformatted Partition Label DrvUnASGN DB 'Unassigned Partition' ; Unassigned Partition Label BIOS_DRV_MSG label NEAR DB 1Bh,'j',1Bh,'Y' ; Save Cursor Position DrvLOC DB ' ' ; Position Cursor for this drive NextDOSdrv DB 'A: ' ; Current Drive Letter DrvLAB DB 'VolumeLabel FAT16 ',1Bh ; Partition Label DrvFATPROB DB 'q' ; Reverse Video for big FAT problem DrvSIZE DB 'xxxx',1Bh,'q' ; Partition Size DrvSZKM DB 'MB LIDE' ; Kilo or Mega Byte Indicator DrvBUS DB 's ' ; Primary or Secondary Bus DrvNUM DB '0' ; Unit 0 or 1 (Master or Slave), DrvRCP DB 1Bh,'k$' ; Restore Cursor Position DEFAULT_BPB LABEL NEAR DW 512 ;BYTES PER SECTOR DB ? ;CLUSTER FACTOR DW 1 ;RESERVED DB 2 ;# OF FATS DW 512 ;DIR ENTRIES DW ? ;TOTAL SECS DB 0F8h ;MEDIA ID DW ? ;FAT SECS DW 63 ;SPT DW ? ;HEADS DEFBPB_HIDSEC DW 0,0 ;HIDDEN SECTORS DD 0 ;LARGE TOTAL SECTORS IF BPB_SECS=0 DB ? ;PHYSICAL DRIVE NO. DB ? ;LOGICAL DRIVE NO. DB 29H ;EXTENDED BOOT SIG DD 0 ;32 BIT VOLUME ID DB 'NO NAME ' ;11 BYTE ASCII VOLUME LABEL DB 'FAT1' FAT16FLG DB '6 ' ;8 BYTES TEMP_T1 DW 0,0 TEMP_C1 DW 0 TEMP_H1 DW 0 TEMP_T2 DW 0,0 TEMP_C2 DW 0 TEMP_H2 DW 0 TEMP_DD1 DW 0,0,0 TEMP_DD2 DW 0,0,0 CLjumpTABLE label WORD DW GET_NEXT_SWITCH ; /A Local Buffered Writes DW GET_NEXT_SWITCH ; /B MTR break flag DW GET_NEXT_SWITCH ; /C Force CHS mode DW GET_NEXT_SWITCH ; /D DW GET_NEXT_SWITCH ; /E DW FASTFORMAT ; /F Fast Format switch DW GET_NEXT_SWITCH ; /G DW GET_NEXT_SWITCH ; /H DW GETminPARTS ; /I Minimum Partitions switch DW GET_NEXT_SWITCH ; /J DW GET_NEXT_SWITCH ; /K DW GETmaxPARTS ; /L Maximum Partitions Limit switch DW GETxferMODE ; /M Transfer Mode Switch DW GET_NEXT_SWITCH ; /N Number of Sectors per block transfers DW GET_NEXT_SWITCH ; /O DW GET_NEXT_SWITCH ; /P Skip Initialize Device Parameters DW GET_NEXT_SWITCH ; /Q DW GET_NEXT_SWITCH ; /R Force ReWrite of 1st Sector in transfer DW GET_NEXT_SWITCH ; /S Force Single Sector Transfers DW GET_NEXT_SWITCH ; /T Additional testing DW GETuncondWAIT ; /U Unconditional Wait switch DW SETV20FLAG ; /V Disable V20 instructions DW GETwaitSTATES ; /W Wait States switch DW GETexclDRVS ; /X Exclude Device switch INITIALIZATION: ; Functions performed by INIT - ; Check BIOS, DOS, and MTR versions ; Check V20 condition/code ; Deturmine if card Exists ; Process command line switches ; Deturmine all drives on J1 and J2 connectors ; Deturmine Partition Starting Sectors and/or Default Drive parameters ; Deturmine and Program Read/Write Multiple capabilites and Buffer Size ; Initialize BIOS DSKBUF ptr ; Remap CHS so SPT < 128 if possible ; First get the information passed to us by SYSINIT LES SI,CIN_BADDR[BX] ; Get Command Line Parameter Address IN ES:SI MOV AL,CIN_DRVNUM[BX] ; Get First DOS drive letter to use MOV CS:FirstDOSdrv,AL ; Save locally ADD AL,'A' MOV CS:NextDOSdrv,AL ; Save for Message display ; Now, check out the BIOS Version MOV DX,offset CS:BADverERR ; Setup for bad version XOR AX,AX MOV DS,AX ASSUME DS:IPAGE_SEG MOV AL,DS:[560H] ; Get ver from second location MOV AH,DS:[563H] CMP AX,(BIOS_CVER shl 8) or (BIOS_CREL) ; Is it CORRECT BIOS ? JNZ BADmsgEXIT MOV AX,MTR_DS ; Point to MTR Data Segment MOV DS,AX ASSUME DS:MTR_D_SEG ; Set ptr to BIOS's DSKBUF ADD AX,40H ; 400H bytes past MTR data Segment MOV word ptr CS:BIOSdskBUF+2,AX ; is the start of Bios's DSKBUF ; Check MTR version # CMP byte ptr MTR_VER,43h ; Lowest ver with IDE support JB BADmsgEXIT ; Check DOS version # SCALL GETVER CMP AL,4 ; 2 and 3 will not work JB BADmsgEXIT ; Check if we have a power-up condition and set our flag TEST byte ptr MTR_EXTRAflag,PowerUpFLG JZ NOTpowerUP DEC byte ptr CS:MTRpowerON ; Set flag to TRUE NOTpowerUP: ; Next deturmine if LifeLine IDE CONTROLLER exists TEST byte ptr MTR_controllers,LifeLineIDE ; Did MTR find NVsRAM? JNZ LLIDEexists ; Yes, go process command line switches NOllideEXIT: MOV DX,offset CS:NOcontERR ; No Controller Detected Error BADmsgEXIT: PUSH CS ; Make sure we can access our messages POP DS ASSUME DS:LLIDEHD PUSH DX ; Save ERROR message MOV DX,offset SIGNonMSG SCALL OUTSTR ; Display our sign-on message POP DX ; Get back ERROR message ptr SCALL OUTSTR ; Display the error message MOV DX,offset NOinstMSG SCALL OUTSTR ; Tell user we did not install LDS BX,REQPACK ; Get address of request header XOR AX,AX MOV byte ptr CIN_UNITS[BX],AL ; Set No Drives MOV word ptr CIN_KADDR[BX],AX ; Set End Address offset to 0 MOV word ptr CIN_KADDR[BX+2],CS ; Set End Address Segment MOV word ptr CIN_BADDR[BX],AX ; Set BPB Address offset to 0 MOV word ptr CIN_BADDR[BX+2],CS ; Set BPB Address Segment ; MOV word ptr CIN_MSGFLG[BX],CS ; Set CONFIG.SYS error flag ver 5+ only MOV AL,SRHS_EDNR ; Set Drive Not Ready Error Code JMP EXIT_ERR ; Go to Exit Error LLIDEexists: PUSH CS POP DS ; Set DS locally ; Now process the command line parameter switches @ ES:SI ; /A - Activate Local Buffer Write Transfers ; /B - Break out to MTR hand prompt to debug code ; /C - Force CHS mode even if drive supports LBA mode ; /F - Use Fast Format by assuming no bad sectors exist(Skip Verifing) ; /Inn - nn = Force Minimum DOS drive letters assigned to Partitions. ; valid range is 1-25 decimal. The default if no /I switch is 1. ; If Minimum is not reached, Unassigned Partitions are used up to ; minimum or Drive Z whichever comes first. ; /Lnn - nn = Limit Maximum DOS drive letters assigned to Partitions. ; valid range is 1-25 decimal. The default if no /L switch is 25 ; assuming a Boot drive for DOS's max of 26. If Maximum or Drive Z ; is reached, detection stops. ; /Mwxyz - w,x,y,z = 0,1,2,3,4,5 PIO Flow Control Transfer Mode ; If no /M switch or > 5 use transfer mode selected by Software Reset ; IF mode 5, explicitly set Default Transfer Mode. Otherwise use modes ; 0-4 for each device individually. You must specify all 4 devices even ; if that device is being excluded from detection with the /X switch. ; w = Primary J1 connector Master Device 0 ; x = Primary J1 connector Slave Device 1 ; y = Secondary J2 connector Master Device 0 ; z = Secondary J2 connector Slave Device 1 ; (see note below for information regarding transfer modes) ; /Nnnn - nnn = Number of Sectors per block transfers(1-127) ; /P - Skip Initialize Device Parameters command at Start-up ; /R - Force ReWrite of 1st sector in write transfers ; /S - Force Single Sector Transfer mode even if drive ; supports Multiple Sector Transfers ; /T - Activate additional tests ; /Unnn - nnn = decimal # of 1/4 second waits ; Unconditional wait for device to spin-up to speed ; Only if MTR detected Power-On condition ; Should never need more than 31 seconds ATA-1 ; /V - Disable patching of V20 specific code instructions ; if a V20 is detected and LLIDEHD was compiled with USEV20 = FALSE ; /Wwxyz - w,x,y,z = 0,1,2,3,4 Wait States for LLIDE controller access ; If no /W switch is given all four devices will use 0 wait states ; 0-4 is specified for each device individually. You must specify all 4 devices ; even if that device is being excluded from detection with the /X switch. ; w = Primary J1 connector Master Device 0 ; x = Primary J1 connector Slave Device 1 ; y = Secondary J2 connector Master Device 0 ; z = Secondary J2 connector Slave Device 1 ; /Xn[nnn] - n = 0,1,2,3 Exclude Device from Partition Detection ; 0,1 = Master,Slave on Primary J1 connector ; 2,3 = Master,Slave on Secondary J2 connector GET_NEXT_SWITCH: CALL GET_NEXT_BYTE CMP AL,'/' ; Valid switch character? JNZ GET_NEXT_SWITCH CALL GET_NEXT_BYTE CMP AL,'a' ; Lower case character? JB NOCVTUP CMP AL,'z' JA NOCVTUP SUB AL,'a'-'A' ; Convert to Upper Case NOCVTUP:SUB AL,'A' JB GET_NEXT_SWITCH ; An invalid switch was given, skip CMP AL,23 ; Max switch is /X JA GET_NEXT_SWITCH CBW SHL AX,1 XCHG BX,AX JMP word ptr CLjumpTABLE[BX] GETmaxPARTS: ; Get MAX DOS drive letters to use MOV BX,offset RemainDOSdrvs GETminPARTScont: CALL GETdecVAL JZ GET_NEXT_SWITCH ; 0 is Invalid CMP AX,25 JA GET_NEXT_SWITCH ; As is > 25 MOV [BX],AL ; Set Min or Max Partitions to use JMP short GET_NEXT_SWITCH GETminPARTS: ; Get MIN # of Partitions to use MOV BX,offset CLminPARTS JMP GETminPARTScont GETexclDRVS: CALL CHKdecVAL ; Get and Check next byte JB GET_NEXT_SWITCH ; Not a Decimal Digit CMP AL,3 ; 0-3 are valid JA GETexclDRVS ; Skip invalid decimal #'s CBW XCHG BX,AX ; Make ptr to correct drive flag MOV byte ptr XJ1M0flag[BX],TRUE ; Set exclude flag to TRUE JMP GETexclDRVS ; Check for more drives GETxferMODE: MOV BX,offset MJ1M0flag ; Point to first device Mode flag GETnxtXFERmode: CALL CHKdecVAL ; Get and Check next byte JB GET_NEXT_SWITCH ; Not a Decimal Digit MOV [BX],AL ; Save mode for this device INC BX ; Point to next device JMP GETnxtXFERmode GETwaitSTATES: MOV DI,offset WJ1M0flag ; Point to first device wait state flag MOV BX,offset WAITSTATES ; And to our translation table GETnxtWAITstate: CALL CHKdecVAL ; Get and Check next byte JB GET_NEXT_SWITCH ; Not a Decimal Digit CMP AL,4 JA GET_NEXT_SWITCH ; 0-4 are valid XLAT ; Translate # value to the correct bits AND byte ptr [DI], NOT CB_AC_0WAITS ; Take out old bits OR [DI],AL ; Save wait states BITS for this device INC DI ; Point to next device JMP GETnxtWAITstate GETuncondWAIT: CALL GETdecVAL ; Get # of Unconditional 1/4 sec waits MOV CLUCWAITIME,AX JGET_NEXT_SWITCH: JMP short GET_NEXT_SWITCH FASTFORMAT: MOV word ptr IDE_GIO_VER,0FBEBh ; Fix jump to skip Verify on Format Request JMP short JGET_NEXT_SWITCH if NOT USEV20 SETV20FLAG: DEC byte ptr CLV20FLAG ; Set our V20 flag to TRUE JMP short JGET_NEXT_SWITCH endif GETdecVAL: XOR CX,CX ; Start with value of 0 GETnxtDECnum: CALL CHKdecVAL ; Get and Check next byte JB ENDgdv ; Not a Decimal Digit CBW XCHG CX,AX ; Get previous value MOV DX,10 MUL DX ; x 10 ADD CX,AX ; Add to next digit JMP GETnxtDECnum ENDgdv: XCHG CX,AX ; Return Decimal # in AX OR AX,AX ; See if it is 0 RETN ; Return ZR flag indicator CHKdecVAL: LODS byte ptr ES:[SI] ; Get next command line byte CMP AL,'9' JA BADdv SUB AL,'0' ; Is next byte a decimal value? JB BADdv RETN BADdv: DEC SI ; Point back to non-decimal value STC RETN GET_NEXT_BYTE: LODS byte ptr ES:[SI] ; Get next command line character CMP AL,0AH ; End of switches JZ DONEwithSWITCHES CMP AL,0DH ; Can be either CR or LF JZ DONEwithSWITCHES RETN ; ZR indicated EOL DONEwithSWITCHES: POP AX ; Take call off stack ; Next check the code for proper V20 instructions XOR AX,AX MOV DS,AX ; Point to Interrupt Segment ASSUME DS:IPAGE_SEG MOV DS,MTR_DS ; Point to MTR Data Segment ASSUME DS:MTR_D_SEG TEST byte ptr MTR_EXTRAflag,V20chip ; Did MTR find a V20 chip? PUSH CS POP DS ; Set DS locally ASSUME DS:LLIDEHD IF USEV20 ; We need to make sure that we do not have an 8088 JNZ YESV20 MOV DX,offset CS:NOv20ERR JMP BADmsgEXIT ; Tell user no V20 found, Abort YESV20: ELSE ; Check if we have a V20 and patch the 8088 code unless user disabled it JZ NOV20 ; No, skip patches ROR byte ptr CLV20FLAG,1 ; User disable patch? JC NOV20 ; yes, skip MOV CX,0C089H ; MOV AX,AX fastest 2 byte NOP MOV word ptr V20IN,6DF3h MOV word ptr V20IN+2,CX ; Patch 8088 code to V20 code MOV word ptr V20OUT,6FF3h ; so it will run faster MOV word ptr V20OUT+2,CX NOV20: ENDIF ; Fix total partitons that we have available to driver MOV AL,26 SUB AL,FirstDOSdrv ; First DOS drive Letter to Use CMP AL,RemainDOSdrvs ; Maximum # of Partitions to install JAE MAXdrvsOK MOV RemainDOSdrvs,AL ; We only have this many MAXdrvsOK: CMP AL,CLminPARTS ; Minimum # of Partitions to install JAE MINdrvsOK MOV CLminPARTS,AL ; Yes, adjust down MINdrvsOK: ; Next get and Save DOS's Maximum Sector Size allowed SCALL GLOL ; Get PTR to CVT, DOS "List of Lists" MOV AX,ES:[BX+10H] ; Get Max Sector Size MOV DOSmaxSSZ,AX ; Save for check ; Now see if we need an unconditional wait XOR BP,BP ; Assume we have done none XCHG BP,CLUCWAITIME ; # of 1/4 sec Unconditional waits ROR byte ptr MTRpowerON,1 ; Power up condition? JNC NOUNCONDWAIT OR BP,BP ; 0 Requested JZ NOUNCONDWAIT UNCONDWAITLOOP: MOV CX,250 ; Get 1/4 second loop counter UNCWLP: CALL TIMER1msDELAY ; 1ms wait LOOP UNCWLP DEC BP ; any more 1/4 second waits? JNZ UNCONDWAITLOOP NOUNCONDWAIT: ; Next we need to read the programmed # of wait states required to access the NVsRAM. ; This is stored in ZFMT_FLG in the NVsRAM's Boot Loader. ; We make the assumption that BIOS has set the correct # of wait states to access ; the NVsRAM via this code and that we need to set wait states for NVsRAM on exit. MOV DX,EEportS ; Get Sector and Read port MOV AL,0 ; Set AL to Read Sector 0 OUT DX,AL MOV CX,ZFMT_FLG + FMT_OFFSET + 1 READEEP:IN AX,DX ; Read the NVsRAM, and bump to the next byte LOOP READEEP AND AL,CB_AC_WAITmask+CB_AC_EWPEEP+CB_AC_BUS0 ; Iso correct bits for Wait States + Write Protect MOV NVsRAMwaitSTATES,AL ; Also Deselects J1 and J2 ; Now see what we can find on the IDE busses. We will issue a Software Reset and check for ; status register values other than 0 or 0FFh and the Busy bit after 8us on both J1 and J2. ; If we get busy we will wait up to 31 seconds for Not Busy, check for correct drive ; signatures and issue a "Recalibrate" command to all devices without waiting for Not Busy. ; Finally we will wait for up to 31 seconds for each device to respond to the Recalibrate ; command and then attempt to read the Identify Device Buffer for all valid Detected Devices. ; Valid devices are: ; 1) Non-Packet devices only, Packet devices (CD-ROM etc.) will require a separate driver. ; 2) Post ATA-1 devices only, it must support Identify Device command ; to discover Device Geometry. ; 3) Only 16-bit data transfers are supported. ; 8-bit data transfers might be supported later. ; 4) 28-bit LBA and CHS modes are supported, ; 48-bit LBA is not supported at this time. ; Although 48-bit LBA mode(ATA-7+) is NOT excluded by the hardware, ; we decided drives larger than 137 GB were really not to useful in the Z-100 environment. ; Especially considering that the largest valid FAT-16 partition is 2 GB. ; The first 137 GB of very large drives would still be usable via 28-bit LBA mode. ; Pre ATA-1 devices are NOT excluded by the hardware either, but rather than implement ; some sort of device configuration scheme, like the old CMOS definitions on the ; original PC's, XT's, and AT's, we are excluding these devices at this time. ; You might view it as a "Plug-and-Play" approach. MOV BX,offset XJ1M0flag CALL STARTsoftRESET ; Start Software Reset on J1 MOV BX,offset XJ2M0flag CALL STARTsoftRESET ; Start Software Reset on J2 ; MOV word ptr CLUCWAITIME,0 ; Reset Wait time, for Software Reset CALL DETECT_DEVICES ; See if we find anything on J2 connector MOV BX,offset XJ1M0flag CALL DETECT_DEVICES ; See if we find anything on J1 connector MOV AX,word ptr FJ1M0flag OR AX,word ptr FJ2M0flag ; Did we find something on either BUS? JZ FOUNDnothing PUSH CS MOV DI,offset IDBUFFER ; Location to store IDENTIFY_DEVICE info POP ES CALL READ_ID_BUFS ; Read the Identify Device Buffer for all J1 Devices MOV DI,offset IDBUFFER+1024 MOV BX,offset XJ2M0flag CALL READ_ID_BUFS ; Read the Identify Device Buffer for all J2 Devices MOV AX,word ptr IJ1M0flag OR AX,word ptr IJ2M0flag ; Did we Get ID buffer for any drives? JNZ FOUNDsomeDRVS FOUNDnothing: MOV DX,offset NOdrvsERR ; Tell user no drives found JMP BADmsgEXIT ; We recognize partitions defined in the "PC" Master Boot Record format to allow non- ; destructive swapping of devices between the Z-100 and a "PC" compatible machine. ; We will assign DOS drive letters to Partitions according to the following criteria: ; The first DOS drive letter we use is dictated to us by DOS, depending on how ; many previous drive letters have been used up by BIOS and previously installed ; device drivers. We search the detected devices on the IDE bus in the following ; order if not Disabled with the /X switch on the command line in CONFIG.SYS. ; Primary J1 Connector, Master Device 0 ; Primary J1 Connector, Slave Device 1 ; Secondary J2 Connector, Master Device 0 ; Secondary J2 Connector, Slave Device 1 ; At any point in the following process we will quit assigning partition drive ; letters if either we reach Drive Z: or we run out because of the limit imposed ; by the /L switch in CONFIG.SYS. We will only assign as many drive letters as ; valid partitions we find, if we do not reach the maximum imposed by the /L switch, ; we will reserve unassigned partitions up to that limit. ; We will search both Primary and Extended Partitions types 5 and 0F ; for valid partition types. We consider valid partition types to be: ; Type 1 - FAT12 - partition or logical drive (fewer than 32,680 sectors) ; Type 4 - FAT16 - partition or logical drive (32,680 - 65,535 sectors) ; Type 6 - BIGDOS - partition or logical drive (33 MB - 2 GB) ; Type 0E - BIGDOS - partition or logical drive using BIOS INT 13h extensions ; All other types will be ignored. This should allow WINDOWS FAT32, NTFS and ; LINEX types to exist and be unaffected by our reading and writing. ; 1) One partition for each device detected (up to 4). ; a) First valid "bootable" partition, or ; b) First valid partition ; 2) All remaining valid partitions on each detected device. ; Here is an example of how it works: ; Assume that /L5 switch was given and DOS tells us to start at Drive I: ; and that we find the following devices and partitions on the IDE buss: ; Primary J1 Connector, Master Device 0 ; 1 Bootable FAT32 ; 2 non-boot FAT16 ; 3 non-boot FAT12 ; 4 none ; Primary J1 Connector, Slave Device 1 ; No device detected ; Secondary J2 Connector, Master Device 0 ; 1 non-boot FAT16 ; 2 none ; 3 Bootable FAT16 ; 4 non-boot NTFS ; Secondary J2 Connector, Slave Device 1 ; 1 none ; 2 none ; 3 none ; 4 none ; We will end up with the following partition drive letters: ; I: 2 non-boot FAT16 on Primary J1 Connector, Master Device 0 ; J: 3 Bootable FAT16 on Secondary J2 Connector, Master Device 0 ; K: 3 non-boot FAT12 on Primary J1 Connector, Master Device 0 ; L: 1 non-boot FAT16 on Secondary J2 Connector, Master Device 0 ; M: Unassigned Partition ; Next setup the "Physical Drive Tables" and Assign a Partition ; for each detected Non-Packet device FOUNDsomeDRVS: MOV SI,offset IDSK_TABLE ; Init Next drive ptr MOV BX,offset IDBUFFER ; Location of stored IDENTIFY_DEVICE info MOV AH,PDSK_CF1_LBA ; Select drive 0 + Assume LBA MOV DI,offset FJ1M0flag CALL ASGN1stPART MOV BX,offset IDBUFFER+512 MOV AH,PDSK_CF1_D01+PDSK_CF1_LBA ; Select drive 1 + Assume LBA MOV DI,offset FJ1S1flag CALL ASGN1stPART MOV BX,offset IDBUFFER+1024 MOV AH,PDSK_CF1_LBA ; Select drive 0 + Assume LBA MOV DI,offset FJ2M0flag CALL ASGN1stPART MOV BX,offset IDBUFFER+1536 MOV AH,PDSK_CF1_D01+PDSK_CF1_LBA ; Select drive 1 + Assume LBA MOV DI,offset FJ2S1flag CALL ASGN1stPART ; Now finish Assigning the rest of the partitions if there are more MOV BX,offset IDBUFFER ; Location of stored IDENTIFY_DEVICE info MOV AL,IJ1M0flag ; Found device flag CALL ASSIGNallPARTS MOV BX,offset IDBUFFER+512 MOV AL,IJ1S1flag CALL ASSIGNallPARTS MOV BX,offset IDBUFFER+1024 MOV AL,IJ2M0flag CALL ASSIGNallPARTS MOV BX,offset IDBUFFER+1536 MOV AL,IJ2S1flag CALL ASSIGNallPARTS ; See how many "Unassigned Partitions" we need, if any MOV AL,CLminPARTS ; Get the Minimum # of Partitions requested CMP TOTALnumPARTS,AL ; Compare to total we assigned JAE NoUNASSIGNED MOV TOTALnumPARTS,AL ; Fix total to reflect Minimum requested XOR AX,AX MOV IDSK_DEVATTR1[SI],AL ; Unassign current partition MOV IDSK_SECS[SI],AX ; As it may have valid values MOV IDSK_TSEC[SI],AX MOV IDSK_TSEC[SI+2],AX ; Zero its size MOV IDSK_NFATS[SI],AL ; Make sure DOS knows it is unformatted NoUNASSIGNED: ; Check to see if the BIOS sign-on screen still exists CALL Chk4BIOSsignONscrn JZ DispPARTSasgnd ; Display our sign-on message, BIOS sign-on does NOT exist INC byte ptr BIOSsignONscrn ; Set to FALSE MOV word ptr DrvLOC,0A0Dh ; Set Location to a CR,LF for message display MOV byte ptr DrvRCP,'$' ; Set correct termination point, no restore cursor MOV DX,offset SIGNonMSG SCALL OUTSTR ; Display our sign-on message ; Now display the partitions we assigned (in the sign-on screen, if it exists) DispPARTSasgnd: PUSH CS ; Reset our ES POP ES MOV SI,offset IDSK_TABLE ; Get ptr to first Partition MOV AL,TOTALnumPARTS ; Get the total # of Partitions we assigned CBW XCHG CX,AX ; In CX as loop counter DispPARTloop: PUSH CX COMMENT ^ MOV AL,IDSK_PHYDRV[SI] CBW MOV BX,512 ; Create ptr to correct IDBUFFER MUL BX ADD AX,offset IDBUFFER XCHG BX,AX ^ CALL DispPARTITION ; Put each Partition in the sign-on screen POP CX TEST byte ptr IDSK_DEVATTR1[SI],IDSK_VBPB JNZ SKIP0FATS MOV IDSK_NFATS[SI],CH ; Make sure DOS knows it is unformatted SKIP0FATS: ADD SI,IDSK_SIZE ; Point to next Partition INC byte ptr NextDOSdrv ; Update the DOS Drive Letter for Messages LOOP DispPARTloop ; Build the BPB pointer array to pass back to DOS and move IDSK_TABLE up in memory MOV SI,offset IDSK_TABLE ; Source PTR MOV BX,offset BPB_TABLE MOV AL,TOTALnumPARTS CBW MOV DX,AX ; Loop Counter SHL AX,1 ; 1 Word for each ptr ADD AX,BX XCHG DI,AX ; Destination PTR FIXTABLELOOP: LEA AX,IDSK_SECSZ[DI] ; Get new ptr to BPB MOV [BX],AX ; Save in BPB_TABLE INC BX INC BX MOV CX,IDSK_SIZE REP MOVSB ; Move our IDSK_TABLE up in memory DEC DX JNZ FIXTABLELOOP ; Finally report back to DOS what we have done MOV AL,TOTALnumPARTS LDS BX,REQPACK ; Get address of request header MOV byte ptr CIN_UNITS[BX],AL ; Set NUM Drives MOV word ptr CIN_KADDR[BX],DI ; Set End Address offset MOV word ptr CIN_KADDR[BX+2],CS ; Set End Address Segment MOV word ptr CIN_BADDR[BX],offset CS:BPB_TABLE ; Set BPB Address offset MOV word ptr CIN_BADDR[BX+2],CS ; Set BPB Address Segment ; MOV word ptr CIN_MSGFLG[BX],CS ; Set CONFIG.SYS error flag ver 5+ only JMP EXIT_DONE ; Support Subroutines for INIT ; ASSIGNallPARTS - ; ; ENTRY: ; AL = Got ID buffer flag (-1 if TRUE) ; BX = Identify Info buffer ; SI = Next IDSK_TABLE to use ASSIGNallPARTS: INC AL ; Found device flag JNZ NONEtoASSIGN ; No, did not find this device CMP RemainDOSdrvs,AL ; Any left to assign? JZ NONEtoASSIGN ; No more DOS drvs available ROR byte ptr ID_UNFORMATTED[BX],1 ; Did we mark for physical access only? JC NONEtoASSIGN MOV AL,TOTALnumDRIVES MOV ID_PHYDEV[BX+1],AL ; Fix for current setting PUSH SI MOV SI,ID_1ST_IDSKptr[BX] ; Get previous part PTR CALL READ_MBR ; Reload the Active Master Boot Record in DI MOV BP,SI ; Save ptr to previous part MOV SI,ID_1ST_PART[BX] ; Get the one we already used MOV byte ptr PCMBR_TYPE[SI],0 ; Don't assign it again POP SI ; Restore next part drive table ptr MOV DI,offset CS:CURRENT_MBR+PCMBR ; Reload DI with Start of current active MBR ASGNnxtPART: CALL FINDnxtPART JNZ NONEtoASSIGN ; All finished CALL ASGNthisPART JNZ ASGNnxtPART ; We still have more DOS drive letters MOV AL,ID_PHYDEV[BX+1] ; Fix for current setting MOV TOTALnumDRIVES,AL NONEtoASSIGN: RETN ; Ran out, quit ; ASGN1stPART - Setup the the 1st drive table and Assign the 1st Partition ; Called with: ; BX = Identify Info buffer ; AH = Drive selection 0 or 1 + Assumed LBA support ; SI = Next IDSK_TABLE to use ; DI = +0 = Ptr to found device flag, ; +4 = PIO transfer mode to use for this device, ; +8 = Wait States + J1 or J2 selection ; +12 = Ptr to Got ID buffer flag ; Return: ASGN1stPART: ROR byte ptr [DI+12],1 ; Did we find this one? JNC NONEtoASSIGN CMP RemainDOSdrvs,1 ; How many are left to assign? JC NONEtoASSIGN ; No more DOS drvs available ; Create our Physical Device Ptr, and save in IDSK_TABLE PUSH AX MOV AL,PDSK_SIZE ; Get Size of each Physical Device table MUL byte ptr TOTALnumDEVICES ; Mult by current zero based relitive device # ADD AX,offset CS:PHYdeviceTABLE MOV IDSK_PDSK_PTR[SI],AX ; Save ptr to Physical Device table XCHG BP,AX ; also in BP POP AX ; Select Device and wait for Not Busy MOV AL,[DI+8] ; Get correct amount of wait states+connector J1 or J2 MOV DS:PDSK_CTRLFLG[BP],AX ; Setup our Bus and Device flag OUT IDE_AC,AL ; Select Primary or Seconday Bus MOV AL,AH AND AL,CB_DH_DEV1 ; Get Select device 0 or 1 bit OUT IDE_DH,AL ; Select correct device CALL TIMERwait250NBSY ; Wait up to 1/4 second for Not Busy ; Now issue SET FEATURES, Set PIO Transfer Mode, if user requested other than default ; Mode and mode is not supported, we need to set back to default mode. ; Otherwise the mode has already been set in the detection process. ; Timing note: ; PIO Mode 0 is the speed of the 8-bit "ISA" bus in ; the original 4.77MHz IBM PC and PC/XT(3.3MB/s). 600ns x2 ; PIO Mode 1 is the speed of the 16-bit "ISA" bus in ; the original 6MHz IBM PC/AT (5.2MB/s). 383ns x2 ; PIO Mode 2 is the speed of the 16-bit "ISA" bus in ; the 8MHz IBM PC/AT(8.3MB/s). 240ns x2 ; PIO Mode 3 is (11.1MB/s). 180ns x2 ATA-2 ; PIO Mode 4 is (16.7MB/s). 120ns x2 ATA-2 ; A few things about this table bear mention. ; First of all, the PIO modes are defined in terms of their cycle time, ; representing how many nanoseconds it takes for each transfer to occur. ; The maximum transfer rate is the reciprocal of the cycle time, ; doubled because the IDE/ATA interface is two bytes (16 bits) wide. ; This follows a non-data command protocol CheckPIOmode: MOV byte ptr MODEchangedFLG,FALSE MOV AL,[DI+4] ; Get PIO Transfer Mode specified by command line CMP AL,5 ; Set Default Transfer Mode or Skip? JAE SKIPpioMODE ; Already set CMP AL,3 ; Modes 0,1,2? JB CHKmode012 TEST word ptr ID_VALID54_64[BX],10B ; Word 64 supported? JZ SETdefPIOmode ; No, set Default CMP AL,3 JNZ CHKmode4 TEST word ptr ID_PIOmode34[BX],1 ; Mode 3 supported? JNZ SKIPpioMODE ; Already set JMP short SETdefPIOmode ; No, set Default CHKmode4: TEST word ptr ID_PIOmode34[BX],10B ; Mode 4 supported? JNZ SKIPpioMODE ; Already set JMP short SETdefPIOmode ; No, set Default CHKmode012: MOV CX,ID_PIOxferMODE[BX] CMP AL,CH ; Mode request LE max Mode supported? JBE SKIPpioMODE ; Already set SETdefPIOmode: DEC byte ptr MODEchangedFLG ; Set to TRUE MOV AL,5 ; Not supported, set back to default transfer mode CALL SETPIOMODE ; A value of 5 will set Default Transfer Mode SKIPpioMODE: ; Set Device Parameters to default from Identify info. ; Some newer drives may not support this. ; Also some old drives may report incorrect values for Heads and SPT. MOV byte ptr MODEchangedFLG,TRUE MOV DX,ID_SPT[BX] ; 6 Get sectors/track MOV AX,ID_HEADS[BX] ; 3 Get # of Heads DEC AX ; Set to # of Heads - 1 MOV DH,AL ; SPT in DL, HEADS in DH CALL INITIALIZE_DEVICE_PARAMETERS JNZ NoIDP ; Did not clear BUSY bit? TEST AL,CB_STAT_DF+CB_STAT_ERR JZ YesIDP NoIDP: TEST word ptr ID_CAPABIL[BX],200h ; Device does not support IDP command JNZ SkipIDP ; It's ok if it supports LBA YesIDP: OR byte ptr DS:PDSK_CTRLFLG1[BP],PDSK_CF1_IDP ; Set Init Dev Parms Support bit SkipIDP: ; Now Re-Read Identify info with default Parms, ; in correct transfer mode if we reset either MOV AL,0 ORG $-1 MODEchangedFLG DB ? SHR AL,1 JNC SkipIDD MOV DI,BX ; Set location for Identify info CALL IDENTIFY_DRIVE SkipIDD:MOV DI,BP ; Set ptr to Physical Device table ; Finish Setting up drive tables from NEW Identify info MOV byte ptr IDSK_SPFUN[SI],IDSK_SPF0+IDSK_SPF2 ; All secs same size, use current BPB MOV AL,TOTALnumDEVICES MOV AH,AL ; Save our Zero based simulated relitive drive # MOV ID_PHYDEV[BX],AX ; Save our Zero based relitive device # INC TOTALnumDEVICES ; Set next one to use INC TOTALnumDRIVES MOV AX,ID_CAPABIL[BX] ; Get Capabilities TEST AX,200H ; Drive support LBA? JNZ LBASUPPORT AND byte ptr PDSK_CTRLFLG1[DI],NOT PDSK_CF1_LBA ; Clear LBA support bit MOV AX,ID_SPT[BX] ; 6 Get # sectors/track MUL word ptr ID_HEADS[BX] ; 3 x # of Heads MUL word ptr ID_CYLS[BX] ; 1 x total Cylinders MOV ID_TotalLBAsecs[BX],AX MOV ID_TotalLBAsecs[BX+2],DX ; Save total # of physical sectors LBASUPPORT: MOV AX,ID_TotalLBAsecs[BX] MOV PDSK_LBA_SECS[DI],AX ; Save total # of physical sectors MOV AX,ID_TotalLBAsecs[BX+2] MOV PDSK_LBA_SECS[DI+2],AX MOV byte ptr IDSK_DEVTYPE[SI],IDSK_DEVT5 MOV word ptr IDSK_DEVATTR[SI],IDSK_DEVA_AFLG+IDSK_DEVA_NR+IDSK_DEVA_GTRK+IDSK_DEVA_FDC MOV byte ptr IDSK_MEDIATYPE[SI],IDSK_MEDT0 MOV AX,ID_SPT[BX] ; 6 Get # sectors/track MOV PDSK_PHYSPH[DI],AL MOV CX,ID_HEADS[BX] ; 3 Get # of Heads MOV PDSK_PHYHEADS[DI],CL MUL CX ; Calc Physical Secs/Cyl MOV PDSK_SECPCYL[DI],AX XOR AX,AX MOV IDSK_HIDSEC[SI],AX ; Make logical call same as physical for now MOV IDSK_HIDSEC[SI+2],AX MOV IDSK_EXTBASE[SI],AX ; Start at the beginning of the drive MOV IDSK_EXTBASE[SI+2],AX MOV ID_UNFORMATTED[BX],AL ; Init NOT unformatted for this device MOV ID_1ST_IDSKptr[BX],SI ; Save ptr to 1st disk table for this device CALL TIMERwait250NBSY ; Wait 1/4 second for Not Busy CheckMULTIPLE: MOV AX,ID_MULTIPLE[BX] ; Get multiple count XOR AH,AH CMP AL,1 ; 0 is not supported JBE NOTMULTIPLE ; no sense in using multiple if only 1 ; Use SET_MULTIPLE_MODE command to see if it is really supported MOV DX,AX ; Save Sector count in DX OUT IDE_SC,AL ; Set count in sector count reg CALL WAIT4DRDY250 ; Wait up to 1/4 second for drive ready JC NOTMULTIPLE SetMULTIPLE: MOV AL,CMD_SET_MULTIPLE_MODE OUT IDE_CMD,AL ; Try and set Multiple Sector IO CALL TIMERwait250NBSY ; Wait 1/4 second for Not Busy JNZ NOTMULTIPLE TEST AL,CB_STAT_DF+CB_STAT_ERR XCHG DX,AX ; Get Sector count back in AX MOV CL,CMD_WRITE_MULTIPLE MOV CH,CMD_READ_MULTIPLE ; Get Multiple Sector IO commands JZ YESMULTIPLE ; Returned NO error, use multiple NOTMULTIPLE: IN AL,IDE_STAT ; Clear possible error MOV AX,1 ; Set to 1 if not supported MOV CL,CMD_WRITE_SECTORS ; Get regular 1 sector IO commands MOV CH,CMD_READ_SECTORS YESMULTIPLE: XCHG AL,AH ; Convert Sector Count to Words MOV PDSK_BLKWRDS[DI],AX MOV PDSK_MR_CMD[DI],CH ; Multiple/Regular Read Command Code MOV PDSK_MW_CMD[DI],CL ; Multiple/Regular Write Command Code ; We should now be setup to use our Read routine to read the device CALL READ_MBR ; Read MBR and see if valid Signature PUSHF PUSH DI CALL SETUP_MBR_GEOMETRY ; Set Logical CYLINDERS, SPT and HEADS POP DI ; DI pointing to start of MBR info POPF JNZ INVALID_MBR MOV BP,SI ; Set previous partition PTR same CALL FIND1stBOOTABLE ; Find the first valid partition MOV ID_1ST_PART[BX],DI ; Save so we know which one we assigned JZ ASGNthisPART ; Found a good one ; We could not find any usable partitions of Types 1, 4, 6, or 0Eh (FAT12 or FAT16) INVALID_MBR: DEC TOTALnumDRIVES DEC byte ptr ID_UNFORMATTED[BX] ; Show Unformatted for this device RET ; Exit with Physical Device Table set ; ASGNthisPART - ; Called with: ; DX:AX = PCMBR_BSEC for Partition to Assign ; SI = Current IDSK_TABLE Ptr ; DI = Current MBR Entry Ptr ; BX = Identify Info buffer Ptr ; BP = Previous IDSK_TABLE Ptr ; Return: ; ZR = No more DOS drive letters for assignment ; SI = Next IDSK_TABLE ; BP = Previous IDSK_TABLE (entry SI) ASGNthisPART: PUSH DI ; Save MBR ptr CALL READ_BL ; Read Boot Loader @ Sector DX:AX JNC FINISH_FORMATTED ; All is ok, finish setting up table POP DI ; Invalid Boot Loader, show as Unformatted PUSH DI ; Get back ptr to MBR CALL SET_DEFAULT_BPB ; Setup drive table PUSH SI LEA DI,IDSK_SECSZ[SI] ; Get our IDSK_TABLE as destin MOV SI,offset CS:DEFAULT_BPB ; Get Default Table as Source JMP short FINISH_UNFORMATTED FINISH_FORMATTED: PUSH SI LEA DI,IDSK_SECSZ[SI] ; Get our IDSK_TABLE as destin MOV SI,offset CS:CURRENT_BL+FMT_OFFSET+PCFMT_SECS ; Get Current Boot Loader as Source FINISH_UNFORMATTED: MOV CX,IDSK_SSSC-IDSK_SECSZ REP MOVSB ; Move the info from the boot loader to our drive table POP SI ; Get back cur drive table MOV AX,ID_PHYDEV[BX] ; Get our relitive device and drive #'s MOV IDSK_PHYDRV[SI],AX ; And set them in OR byte ptr IDSK_DEVATTR1[SI],IDSK_ASGN ; Show it as assigned POP DI ; Get back ptr in MBR MOV BP,SI ; Set previous partition PTR ADD SI,IDSK_SIZE ; Point to next table INC TOTALnumPARTS ; Show we have one more Assigned SUB RemainDOSdrvs,1 ; Show 1 less available, create zero flag indicator RETN ; Read Extended Master Boot Loader Record READ_EMBR: MOV CX,IDSK_EXTBASE[SI] OR CX,IDSK_EXTBASE[SI+2] JNZ NOTMBR0 MOV ID_EMBR1[BX],AX MOV ID_EMBR1[BX+2],DX JMP short YESMBR0 NOTMBR0:ADD AX,ID_EMBR1[BX] ADC DX,ID_EMBR1[BX+2] YESMBR0:MOV IDSK_EXTBASE[SI],AX ; Adjust to start of Extended Partition MBR MOV IDSK_EXTBASE[SI+2],DX ; Read a Master Boot Loader READ_MBR: MOV RETRY_COUNT,3 ; Retry a maximum of 3 times RETRY_MBR: PUSH BP PUSH BX XOR AX,AX MOV DX,AX ; Sector 0 MOV BX,1 ; 1 to READ MOV BP,offset PIO_READ_SECS PUSH SI MOV DI,offset CS:CURRENT_MBR ; Transfer address to ES:DI CALL RWVphyDRIVE POP SI POP BX POP BP PUSH CS ; Reset our ES POP ES JC CHK_RETRY MOV DI,offset CS:CURRENT_MBR+PCMBR ; Return DI pointing to start of MBR info CMP word ptr DS:[DI+(4*SIZE PCMASTERBOOTRECORD)],0AA55H ; Do we have a valid "PC" Master Boot Record ? RETN ; Return Zero Flag indicator for correct signature CHK_RETRY: DEC RETRY_COUNT JNZ RETRY_MBR ;*** ??? ; Big probs if we can not read one of these sectors OR SP,SP ; Set NZ to show NOT MBR for now RETN ; Read a Boot Loader record READ_BL: MOV IDSK_HIDSEC[SI],AX ; Make relative to start of Logical Drive MOV IDSK_HIDSEC[SI+2],DX MOV AX,PCMBR_SIZE[DI] ; Get Size of this partition MOV IDSK_TSEC[SI],AX MOV AX,PCMBR_SIZE[DI+2] MOV IDSK_TSEC[SI+2],AX ; And save for checks later XOR AX,AX MOV IDSK_SECS[SI],AX MOV IDSK_SSSC[SI],AX ; No sector xlat'ing from MBR's to Boot Loader's MOV RETRY_COUNT,3 ; Retry a maximum of 3 times RETRY_BL: PUSH BP PUSH BX PUSH SI MOV BP,offset PIO_READ_SECS MOV DI,offset CS:CURRENT_BL ; Transfer address to ES:DI CALL RWlogPART0buf ; Read Logical Sector 0 from Partition POP SI POP BX POP BP PUSH CS ; Reset our ES POP ES JNC VALIDATE_BL ; If no errors, go check it out DEC RETRY_COUNT JNZ RETRY_BL ;*** ??? ; Big probs if we can not read one of these sectors BADVBPB:STC ; Set CY to show NOT Boot Loader RETN ; Check all the values to verify DOS will be happy with what we found ; Return CY if we find any problem and the partition will be set up as Unformatted VALIDATE_BL: ; DS:SI is our IDSK_TABLE, DS:DI is PCFMT TABLE from boot loader MOV DI,offset CS:CURRENT_BL+FMT_OFFSET ; Point back to the start CMP word ptr [DI+510-FMT_OFFSET],0AA55H ; Do we have a valid "PC" Boot Loader Record ? JNZ BADVBPB CMP byte ptr PCFMT_EBS[DI],029h ; Extended boot Signature? JNZ BADVBPB MOV AL,PCFMT_CLF[DI] ; Cluster factor must be mult of two CBW ; Do not allow 128, CBW will set AH=0FFh if AL=80h OR AX,AX ; 0 is Invalid JZ BADVBPB CM2LP: SHR AX,1 ; Check for Mult of 2 JNC CM2LP ; Find first bit that is set OR AX,AX ; Any other bits still set? JNZ BADVBPB ; Yes, abort on Invalid cluster factor CMP PCFMT_NRS[DI],AX ; Number of reserved sectors JZ BADVBPB ; Cannot be 0 CMP PCFMT_FATS[DI],AL ; Number of FATs JZ BADVBPB ; Cannot be 0 CMP PCFMT_SPF[DI],AX ; Number of Sectors per FAT JBBZR1: JZ BADVBPB ; Cannot be 0 MOV AX,PCFMT_SECS[DI] ; Get logical sector size CMP AX,5555H ; Compare to Max Size allowed by DOS ORG $-2 DOSmaxSSZ DW 1024 ; Filled in above JA BADVBPB ; Too big, abort XCHG AL,AH ; Trick to SHR AX,1 ; Divide logical by physical(512) JC BADVBPB ; Must be even MULT of 512 CMP AX,16 ; Largest allowed is 8192 JA BADVBPB MOV CX,AX JCXZ BADVBPB ; Can not be 0 MOV AX,-1 ; So 512 will be 0 bits to shift CALC_LSSS: INC AX ; Count shift factor SHR CL,1 JNC CALC_LSSS OR CX,CX ; Any bits still set? JNZ BADVBPB ; Yes, Invalid Multiple of 512, only 1 bit can be set MOV IDSK_SSSC[SI],AX ; Set Logical Block Shift Count, 0 for 512 MOV AX,PCFMT_HSL[DI] ; Get the # of Hidden Sectors from Boot Loader MOV DX,PCFMT_HSH[DI] CALL ADJ_SIZE ; Adjust # of Hidden Sectors CMP DX,IDSK_HIDSEC[SI+2] ; See if we are pointing to the same spot JNZ BADVBPB ; If not, Physical access will not work CMP AX,IDSK_HIDSEC[SI] JBBNZ: JNZ BADVBPB MOV AX,32 ; Get the size of a directory entry MUL PCFMT_DIRS[DI] ; Calc total size in bytes DIV PCFMT_SECS[DI] ; Calc # of directory sectors OR DX,DX JNZ JBBNZ ; Must be even # of sectors OR AX,AX JBBZR: JZ JBBZR1 ; Must have at least 1 MOV AX,PCFMT_SPT[DI] MUL word ptr PCFMT_HEAD[DI] ; Calc Logical Secs per Cyl OR DX,DX JNZ JBBNZ ; Invalid, too big OR AX,AX JZ JBBZR ; Neither SPT nor HEADS can be 0 MOV IDSK_SECLCYL[SI],AX ; Save Logical Secs/Cyl in our drive table MOV AX,PCFMT_NPS[DI] ; DX is already 0 OR AX,AX ; Get the Total Sectors from the correct spot JNZ NOTLRG MOV AX,word ptr PCFMT_TOTS[DI] ; Get from the large spot MOV DX,word ptr PCFMT_TOTS[DI+2] NOTLRG: CALL ADJ_SIZE ; Fix its size, if necessary CMP DX,IDSK_TSEC[SI+2] ; Cmp to saved toal from MBR's PCMBR_SIZE JNZ TSZNZ CMP AX,IDSK_TSEC[SI] ; Is total in boot loader LE total in MBR? TSZNZ: JA JBBNZ ; No, too big, abort CALL SET_REMAIN_LOGCYL ; Set IDSK_CYLINDERS CALL CALC_TOTMINUS ; Calc TOTAL Sectors - Root DIR - Reserved JC JBBNZ ; Total was not enough for (RES+DIR), abort PUSH DX PUSH AX ; Save TOTAL Sectors - Root DIR - Reserved MOV AL,PCFMT_FATS[DI] ; Get # of FAT's XOR AH,AH MUL PCFMT_SPF[DI] ; DX:AX is total sectors in all FATS XCHG CX,AX ; Save in DX:CX POP AX ; Get back TOTAL sectors - (RES+DIR) SUB AX,CX ; Sub total from all FAT's POP CX ; To create SBB CX,DX ; Total DATA sectors = CX:AX MOV DX,CX ; Put in DX:AX JC JBBNZ ; Total was not enough for (RES+DIR+FATS), abort PUSH AX PUSH DX ; Save total Logical DATA sectors CALL ADJ_SIZE ; Convert to Physical, if necessary CMP DX,3Fh ; Maximum Physical DATA sectors is 3FFF80h JNZ $+5 CMP AX,0FF80h ; Are we in range? POP DX POP AX JA JBBNZ ; No, we are GT Maximum Size allowed, abort PUSH BX ; Called with DX:AX = Total DATA sectors CALL CalcSECSperFAT POP BX ; Ret AX = # of sectors that should be in a FAT CMP AX,PCFMT_SPF[DI] ; Same as what we got from boot loader? JNZ JBBNZ ; Not what we have, abort ; Enable Access, Validate BPB, Set Force Disk Change, Return NC for valid Boot Loader OR byte ptr IDSK_DEVATTR1[SI],IDSK_VBPB+IDSK_FDC AND byte ptr IDSK_DEVATTR1[SI],NOT IDSK_ACFLG ; Also sets NC RETN ; NC for valid boot loader SET_REMAIN_LOGCYL: MOV AX,ID_TotalLBAsecs[BX] ; Get Total Sectors on device MOV DX,ID_TotalLBAsecs[BX+2] SUB AX,IDSK_EXTBASE[SI] ; Sub the Start of this Logical Drive SBB DX,IDSK_EXTBASE[SI+2] ; to calc total remaining sectors on device MOV CX,IDSK_SSSC[SI] ; Get Sector Size Adj in CX JCXZ SKPFIX FIXSZ: SHR DX,1 RCR AX,1 ; Calc total Remaining Logical sectors LOOP FIXSZ SKPFIX: MOV CX,IDSK_SECLCYL[SI] ; Get Secs Per Logical Cylinder CMP DX,CX ; Will div overflow? JB SIZEOK MOV AX,-1 ; Yes, Set to MAX JMP short SETCYL SIZEOK: DIV CX ; Div by Logical Sectors per Clyinder OR DX,DX ; Any remainder? JZ SETCYL INC AX ; Yes, set one more cylinder SETCYL: MOV IDSK_CYLINDERS[SI],AX ; Set correct # of Remaining Logical Cylinders RET ADJ_SIZE: MOV CX,IDSK_SSSC[SI] ; Get Sector Size Adj in CX as loop counter JCXZ SKIP_ADJ ADJ_LP: SHL AX,1 ; Convert Logical to Physical RCL DX,1 ; This can be Count or Location LOOP ADJ_LP SKIP_ADJ: RETN ; Find the NEXT valid partition from a MBR or EMBR ; Called with: ; DI = First MBR Entry Ptr to check ; SI = Current IDSK_TABLE Ptr ; BX = Identify Info buffer Ptr ; BP = Previous IDSK_TABLE Ptr ; RETURN: ; NZ if we could not find any more valid partitions ; ZR if we found one with a valid Type and Size ; DX:AX = PCMBR_BSEC for Partition to Assign ; DI = MBR Entry Ptr for found partition ; USES: ; CX FINDnxtPART: ; First setup drive table with prev partition's values PUSH DI PUSH SI MOV DI,SI ; New part is destin MOV SI,BP ; Prev part is source MOV CX,IDSK_SIZE REP MOVSB ; Get the table started POP SI ; Get back ptr to cur table POP DI FINDvalidTYPE: CALL CHECKvalidTYPE ; See if Type 1,4,6, or 0Eh JZ FOUNDnxtPART ; Exit with DX,AX,DI Valid, ZR is set. ADD DI,size PCMASTERBOOTRECORD CMP DI,offset CS:CURRENT_MBR+PCMBR+(size PCMASTERBOOTRECORD*4) JB FINDvalidTYPE ; Keep searching all 4 entries MOV DI,offset CS:CURRENT_MBR+PCMBR ; No more, see if we have some Extended Types FINDvalidEXTENDED: CALL CHECK4EXTENDED ; See if Type 5 or 0Fh JNZ NOTEXTENDED ; Not extended, go check next entry PUSH word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*1 PUSH word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*2 PUSH word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*3 PUSH word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*4 PUSH DI PUSH word ptr IDSK_EXTBASE[SI] PUSH word ptr IDSK_EXTBASE[SI+2] CALL READ_EMBR JNZ DONEwithEXTENDED ; Invalid signature 0AA55H CALL FINDvalidTYPE ; Search for a partition to assign JNZ DONEwithEXTENDED ; Did not find any valid types INC byte ptr ID_PHYDEV[BX+1] ; One more Relitive simulated drive ASGNnxtEXTENDED: CMP RemainDOSdrvs,1 ; Only 1 remaining DOS drive letter? JZ FOUNDlstPART ; Yes, QUIT CALL ASGNthisPART ; Found one, assign it from AX:DX and DI CALL FINDnxtPART ; Set up new table, see if more to assign JZ ASGNnxtEXTENDED ; Found one, go assign it DONEwithEXTENDED: POP word ptr IDSK_EXTBASE[SI+2] POP word ptr IDSK_EXTBASE[SI] ; Adjust to Previous MBR CALL READ_MBR ; Get back old one, and continue POP DI POP word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*4 POP word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*3 POP word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*2 POP word ptr DS:CURRENT_MBR+PCMBR+PCMBR_TYPE*1 NOTEXTENDED: ADD DI,size PCMASTERBOOTRECORD CMP DI,offset CS:CURRENT_MBR+PCMBR+(size PCMASTERBOOTRECORD*4) JB FINDvalidEXTENDED OR SP,SP ; Set NZ for no more found FOUNDnxtPART: RETN ; DX,AX,DI Valid if ZR is set. FOUNDlstPART: ADD SP,14 ; Fix Stack XOR CX,CX ; Set ZR for found one RETN ; DX,AX,DI Valid ; Check if the current entry is a valid Boot Loader Record Partition Type ; if correct Type and Size, return its Starting sector in DX:AX from PCMBR_BSEC CHECKvalidTYPE: MOV AL,PCMBR_TYPE[DI] CMP AL,1 ; FAT12 partition? JZ CHKpartSIZE CMP AL,4 ; FAT16 partition? JZ CHKpartSIZE CMP AL,6 ; BIGDOS FAT16 partition? JZ CHKpartSIZE CMP AL,0Eh ; BIGDOS FAT16 partition using BIOS INT 13h extensions? JNZ BADTYPE CHKpartSIZE: MOV DX,PCMBR_SIZE[DI+2] ; Partition length in sectors MOV AX,PCMBR_SIZE[DI] OR DX,DX JNZ PARTSIZEOK CMP AX,64 ; Min size for valid part JB BADTYPE PARTSIZEOK: XOR AX,AX ; Set ZR flag for OK MOV PCMBR_TYPE[DI],AL ; Don't use this one again MOV AX,PCMBR_BSEC[DI] MOV DX,PCMBR_BSEC[DI+2] RETN ; Ret ZR for good Type and Size ; Check if the current entry is a valid Extended Master Boot Loader Record Type ; if it is the correct Type and Size, return its Sector in DX:AX from PCMBR_BSEC ; Check if BASE + EMBR1 = Current EMBR + SIZE. CHECK4EXTENDED: MOV AL,PCMBR_TYPE[DI] CMP AL,5 ; Extended partition? JZ CHKpartSIZE CMP AL,0Fh ; Extended partition using BIOS INT 13h extensions? JZ CHKpartSIZE BADTYPE:OR SP,SP ; Set NZ for bad Type or Size RETN ; Find the FIRST valid partition from a MBR or EMBR ; Called with: ; SI = Current IDSK_TABLE Ptr ; BX = Identify Info buffer Ptr ; BP = Previous IDSK_TABLE Ptr ; RETURN: ; NZ if we could not find any more valid partitions ; ZR if we found one with a valid Type and Size ; DX:AX = PCMBR_BSEC for Partition to Assign ; DI = MBR Entry Ptr for found partition ; USES: ; CX FIND1stPART: CALL READ_MBR JNZ FIND1stInvalidMBR ; Invalid signature 0AA55H FIND1stBOOTABLE: CMP byte ptr PCMBR_AFLAG[DI],80H ; Bootable? JNZ NotBOOTABLE CALL CHECKvalidTYPE ; See if Type 1,4,6, or 0Eh JZ FOUND1stPART ; Exit with DX,AX,DI Valid, ZR is set. NotBOOTABLE: ADD DI,size PCMASTERBOOTRECORD CMP DI,offset CS:CURRENT_MBR+PCMBR+(size PCMASTERBOOTRECORD*4) JB FIND1stBOOTABLE ; Keep searching all 4 entries MOV DI,offset CS:CURRENT_MBR+PCMBR FIND1stValidTYPE: CALL CHECKvalidTYPE ; See if Type 1,4,6, or 0Eh JZ FOUND1stPART ; Exit with DX,AX,DI Valid, ZR is set. ADD DI,size PCMASTERBOOTRECORD CMP DI,offset CS:CURRENT_MBR+PCMBR+(size PCMASTERBOOTRECORD*4) JB FIND1stValidTYPE ; Keep searching all 4 entries MOV DI,offset CS:CURRENT_MBR+PCMBR FIND1stEXTENDED: CALL CHECK4EXTENDED ; See if Type 5 or 0Fh JNZ FIND1stNotEXTENDED PUSH DI ; Save pts in case of failure PUSH word ptr IDSK_EXTBASE[SI] PUSH word ptr IDSK_EXTBASE[SI+2] CALL READ_EMBR JNZ FIND1stInvalidEXTENDED ; Invalid signature 0AA55H CALL FIND1stBOOTABLE JNZ FIND1stInvalidEXTENDED POP CX POP CX ; Fix stack, keep ZR flag POP CX FOUND1stPART: RETN ; DX,AX,DI Valid ZR is set. FIND1stInvalidEXTENDED: ; Oops, did not find one, keep going POP word ptr IDSK_EXTBASE[SI+2] POP word ptr IDSK_EXTBASE[SI] ; Adjust to Previous MBR CALL READ_MBR ; Get back old one POP DI ; and continue where we left off FIND1stNotEXTENDED: ADD DI,size PCMASTERBOOTRECORD CMP DI,offset CS:CURRENT_MBR+PCMBR+(size PCMASTERBOOTRECORD*4) JB FIND1stEXTENDED ; Keep searching all 4 entries FIND1stInvalidMBR: OR SP,SP ; Set NZ for none found RETN ; Set up the partition, we did not accept the boot loader information, ; so we view it as Unformatted. ; We can not configure a partition GT 3FFF80h DATA sectors approx 2 GB, ; with 1 RESERVED, 32 Root DIRECTORY sectors (512 Entries @ 32 bytes each), ; and 2 Maximum size FATS (512 total sectors), so the following code will fail ; if we try with more than 4,194,721 (4001A1h) total sectors. SET_DEFAULT_BPB: MOV AX,IDSK_HIDSEC[SI] MOV DEFBPB_HIDSEC,AX ; Fix DEFAULT_BPB hidden secs MOV AX,IDSK_HIDSEC[SI+2] MOV DEFBPB_HIDSEC+2,AX XOR CX,CX MOV IDSK_SSSC[SI],CX ; No sector xlat'ing from MBR's to Boot Loader's MOV AX,PCMBR_SIZE[DI] MOV DX,PCMBR_SIZE[DI+2] ; Get Size of this partition CMP DX,40h ; Maximum Physical sectors is 4001A1h JNZ $+5 CMP AX,1A1h ; Are we in range? JBE SIZEisOK MOV AX,1A1h ; No, set it to the Maximum for a partition MOV DX,40h SIZEisOK: MOV DI,offset DEFAULT_BPB-PCFMT_SECS ; Make our DEFAULT_BPB look like it came from a Boot Loader MOV PCFMT_NPS[DI],AX MOV word ptr PCFMT_TOTS[DI],DX ; Assume Small MOV word ptr PCFMT_TOTS[DI+2],DX OR DX,DX ; Is it? JZ SETnumHEADS MOV word ptr PCFMT_TOTS[DI],AX ; No, set large spot MOV PCFMT_NPS[DI],CX ; Set to 0 for large method XCHG CX,AX ; Set AX = 0 for Head check below SETnumHEADS: XCHG CX,AX ; Get Size to check for MOV PCFMT_HEAD[DI],65 ; Start with 64 CHKnumHEADS: DEC word ptr PCFMT_HEAD[DI] ; Try one less HEAD MOV AX,PCFMT_SPT[DI] MUL PCFMT_HEAD[DI] ; Calc Logical Secs per Cyl CMP AX,CX ; CX=Total Sec's if small, or 0 for large JB CHKnumHEADS ; Sec Per Cyl cannot be less than total sectors MOV IDSK_SECLCYL[SI],AX ; Save Logical Sec's per Cylinder CALL SET_REMAIN_LOGCYL ; Set IDSK_CYLINDERS ; Now calculate the Minimun Cluster Factor for the size of the partition CALL CALC_TOTMINUS ; Calc total Sectors - Root Dir - Reserved, in DX:AX PUSH AX ; Save TOTAL Sectors - (res+dir) PUSH DX MOV PCFMT_CLF[DI],1 ; INIT cluster factor to 1 XOR CX,CX SUB AX,512 ; Less Max Sectors for 2 Full Fats SBB DX,CX ; is Min Total DATA sectors JC USE_CF_1 ; Is Cluster Factor 1 PUSH BX MOV BX,65534 ; CX:BX Max DATA secs for Cluster Factor 1 CHECK_NXT_CF: CMP DX,CX JNZ $+4 CMP AX,BX ; Total in range for this Cluster Factor? JBE DONE_CF_CALC ; yes, go use it to calculate Sectors Per FAT SHL PCFMT_CLF[DI],1 ; no, Try NEXT higher cluster factor SHL BX,1 RCL CX,1 ; Double check value JMP CHECK_NXT_CF DONE_CF_CALC: POP BX USE_CF_1: POP DX ; Get back total POP AX ; Less root dir + reserved ; At this point, calculate the allocation units based on size of partition ; Do it until we get consecutive identical values. PUSH BP ; Calc Number of sectors occupied by a FAT PUSH BX ; Save our ptrs PUSH SI CALCBADAGAIN: PUSH AX ; Save TOTAL Sectors - (res+dir) PUSH DX MOV word ptr PCFMT_SPF[DI],1 ; Init to MIN value, force two calls MOV CX,5 CALCFATAGAIN: PUSH CX CALL CalcSECSperFAT ; Calc FAT sectors, IN AX MOV BP,AX MOV CL,PCFMT_FATS[DI] ; Get # of fats XOR CH,CH MUL CX ; DX:AX is total sectors in all FATS POP CX MOV BX,AX ; Save in SI:BX MOV SI,DX POP DX ; Get back TOTAL sectors - (RES+DIR) POP AX PUSH AX ; Save them again PUSH DX SUB AX,BX ; Calc new total DATA sectors SBB DX,SI XCHG BP,PCFMT_SPF[DI] ; Get previous, set new SEC/FAT CMP BP,PCFMT_SPF[DI] LOOPNZ CALCFATAGAIN ; If not the same, try up to 5 times JCXZ BADFATSEC ADD SP,4 ; Take total off stack POP SI POP BX POP BP RETN BADFATSEC: SUB PCFMT_NPS[DI],1 ; Reduce total by 1 JNC ITISM ; It Was not 0, so it is small SUB word ptr PCFMT_TOTS[DI],1 ; So sub 1 from large total SBB word ptr PCFMT_TOTS[DI+2],CX ; CX=0 JNZ ITISL ; Skip if High word is still > 0 XCHG CX,word ptr PCFMT_TOTS[DI] ; Set 0, get CX for small total ITISL: MOV PCFMT_NPS[DI],CX ; Set Total or 0 back in small location ITISM: POP DX ; Get back TOTAL sectors - (RES+DIR) POP AX SUB AX,1 ; Reduce total by 1 SBB DX,0 JMP short CALCBADAGAIN ; CalcSECSperFAT ; Called with: ; DX:AX = Total DATA sectors ; DI = Boot Loader PCFMT_TABLE ; Return: ; AX = # of Sectors occupied by a single FAT ; Uses: BX,CX CalcSECSperFAT: MOV CL,PCFMT_CLF[DI] ; Get cluster factor CALL CALC12_16 XCHG CX,AX ; CX=FAT entries/sector, AX=total clusters XOR DX,DX MOV BX,CX ; Get FAT entries/sector DEC BX ; Calc # in 1st sector of FAT DEC BX SUB AX,BX ; Reduce total Sec's by # in 1st sector JC CF1 DIV CX ; AX = remaining sectors in FAT OR DX,DX ; Any remainder? JZ CALCFAT2 INC AX ; Yes, add one for remainder CALCFAT2: INC AX ; Add one for 1st sec in FAT RETN CF1: MOV AX,1 ; 1 Sector/FAT RETN CALC12_16: ; Return: CX= Total # of Clusters MOV byte ptr FAT16FLG,'6' ; Assume 16-bit FAT's XOR CH,CH ; AX= # of Clusters in each sector of a FAT CMP CX,DX ; Too big for divide ? JBE CTOOBIG DIV CX ; Divide by cluster factor MAXCLNR:CMP AX,4087 ; 16 bit FATS? XCHG CX,AX ; Save in CX MOV AX,PCFMT_SECS[DI] ; Bytes/Sector JAE MAXCL16 SHL AX,1 ; mult Bytes/Sector BY 2 MOV BX,3 ; div 3 XOR DX,DX DIV BX ; 12 bit FATS MOV byte ptr FAT16FLG,'2' ; Label 12-bit FAT's RETN MAXCL16:SHR AX,1 ; 16 bit FATS-Divide Bytes/Sector by 2 RETN CTOOBIG:MOV AX,65534 ; Max # of clusters(2 thru 65535) JMP MAXCLNR CALC_TOTMINUS: ; Calc TOTAL Sectors - Root Dir - Reserved MOV AX,32 ; AX = bytes/directory entry MUL PCFMT_DIRS[DI] DIV PCFMT_SECS[DI] ; Calc Root Dir Sectors XCHG CX,AX ; Save in CX XOR DX,DX MOV AX,PCFMT_NPS[DI] ; Get total sectors OR AX,AX JNZ NOTBIG MOV AX,word ptr PCFMT_TOTS[DI] MOV DX,word ptr PCFMT_TOTS[DI+2] NOTBIG: SUB AX,CX ; Less secs in root directory SBB DX,0 JB TOOSM SUB AX,PCFMT_NRS[DI] ; and reserved secs SBB DX,0 TOOSM: RETN ; Return in DX:AX + CY if too small ; See if Initial BIOS Sign on screen still exists, ZERO flag tells all Chk4BIOSsignONscrn: MOV AX,ZGRNSEG ; Get the Green video segment MOV ES,AX XOR DI,DI CALL CHK_SOL ; Is the BIOS signon screen still there? JNZ CBSOSX ; NO, exit MOV DI,100H CHK_SOL:IN AL,ZVIDEO XCHG BX,AX ; Save current video state MOV AL,78H OUT ZVIDEO,AL ; Enable access to green plane CMP byte ptr ES:[DI+80h],80h JNZ CHKSOLX MOV AX,-1 MOV CX,40 REPZ SCASW ; See if signon screen is still there JNZ CHKSOLX CMP byte ptr ES:[DI+7Fh],1 CHKSOLX:XCHG BX,AX OUT ZVIDEO,AL CBSOSX: RETN ; Zero flag tells all ; DispPARTITION - ; Called With: ; BX = IDBUFFER ptr ; SI = IDSK_TABLE ptr ; NextDOSdrv set to correct DOS drive letter DispPARTITION: ROR byte ptr BIOSsignONscrn,1 JNC SKIPlocation MOV AL,NextDOSdrv ; Get Drive Letter in message ADD AL,5-'A' MOV DL,AL MOV DH,1 CMP AL,18 ; Calc its location in Bios Sign-On Screen JB DISPLFT SUB DL,13 MOV DH,2AH DISPLFT:XCHG DX,AX ADD AX,' ' MOV word ptr DrvLOC,AX ; Save Location in Message SKIPlocation: MOV DI,IDSK_PDSK_PTR[SI] ; Get ptr to Physical device info MOV AL,'0' TEST byte ptr PDSK_CTRLFLG1[DI],PDSK_CF1_D01 ; Device 1 select bit? JZ ITISDEV0 MOV AL,'1' ; Yes, set to Slave Device 1 ITISDEV0: MOV DrvNUM,AL MOV AL,'p' ; Assume Primary J1 connector TEST byte ptr PDSK_CTRLFLG[DI],PDSK_CF_J2 ; J2 Connector select bit? JZ ITISJ1 MOV AL,'s' ; Yes, set to J2 Secondary ITISJ1: MOV DrvBUS,AL ; Now calculate drive size, we will display up to 9999KB or 4000MB for 4GB ; our largest possible part CALL GETtotalSECS ; In DX:AX CALL ADJ_SIZE ; Convert to Physical, if necessary MOV byte ptr DrvSZKM,'K' ; Assume Kilo Byte size SHR DX,1 RCR AX,1 ; Divide by 2 for Kilobytes OR DX,DX JNZ CVT2MEG CMP AX,9999 ; Is kilo bytes in range? JBE ITISKB CVT2MEG: MOV byte ptr DrvSZKM,'M' ; It is Mega Byte size MOV CX,977 ; Get Megabyte divisor for 1024 byte sector size DIV CX ; Calc # of Megabytes in Partition CMP DX,488 JB ITISKB INC AX ITISKB: MOV DI,offset DrvSIZE CALL STORdecNUM4 ; Store in Sign-On message PUSH SI MOV DI,offset DrvLAB TEST byte ptr IDSK_DEVATTR1[SI],IDSK_ASGN ; Non-valid Unassigned PARTITION? JZ BNonValidPART CALL CalcREVvid ; Set Rev Video if Big FAT problem TEST byte ptr IDSK_DEVATTR1[SI],IDSK_VBPB ; Unformatted PARTITION? JZ BUnformattedPART LEA SI,IDSK_VOLAB[SI] ; Put the Volume Label from the boot loader MOV CX,11 ; Into the Bios Sign-On screen message REP MOVSB MOV AL,' ' ; Space before FAT12 or FAT16 STOSB MOV CX,4 STORlabel: REP MOVSW DispInBIOS: POP SI MOV DX,offset BIOS_DRV_MSG ; Display Partition assignment ROR byte ptr BIOSsignONscrn,1 JC FULLmessage MOV DX,offset DrvLOC ; Skip the location setting, DrvLOC now has CR,LF FULLmessage: SCALL OUTSTR ; in the Bios Sign-On Screen RETN BUnformattedPART: MOV SI,offset DrvUnFMT ; Unformatted Partition Label CONTunass: MOV CX,10 ; 20 Characters long JMP STORlabel BNonValidPART: MOV word ptr IDSK_SECSZ[SI],512 ; Ensure DOS doesn't abort on Sec Size MOV SI,offset DrvUnASGN ; Unassigned Partition Label MOV AL,'?' MOV DrvBUS,AL ; Unknown buss and drive MOV DrvNUM,AL JMP CONTunass COMMENT ^ LEA SI,ID_MODEL[BX] ; Get Model ASCII string from ID buffer MOV CX,10 BNVPLP: LODSW XCHG AL,AH ; Reverse byte order for Display STOSW ; and Store in DrvLAB LOOP BNVPLP JMP DispInBIOS ^ GETtotalSECS: MOV AX,IDSK_SECS[SI] XOR DX,DX ; Get # of Sectors in Partition OR AX,AX JNZ SZnotLARGE MOV AX,IDSK_TSEC[SI] ; From the large spot MOV DX,IDSK_TSEC[SI+2] SZnotLARGE: RET CalcREVvid: MOV AX,32 ; AX = bytes/directory entry MUL word ptr IDSK_DIRENTS[SI] DIV word ptr IDSK_SECSZ[SI] ; Calc Root Dir Sectors XCHG CX,AX ; Save in CX CALL GETtotalSECS ; In DX:AX SUB AX,CX ; Less secs in root directory SBB DX,0 SUB AX,IDSK_RES[SI] ; and reserved secs SBB DX,0 PUSH DX PUSH AX ; Save TOTAL Sectors - Root DIR - Reserved MOV AX,IDSK_NFATS[SI] ; Get # of FAT's MUL word ptr IDSK_FATSECS[SI] ; DX:AX is total sectors in all FATS XCHG CX,AX ; Save in DX:CX POP AX ; Get back TOTAL sectors - (RES+DIR) SUB AX,CX ; Sub total from all FAT's POP CX ; To create SBB CX,DX ; Total DATA sectors = CX:AX MOV DX,CX ; Put in DX:AX MOV CL,IDSK_SPAU[SI] ; Get cluster factor XOR CH,CH JCXZ CRV_NO ; Do not div by 0 CMP DX,CX ; Will div overflow? JAE CRV_NO DIV CX ; Div Total DATA secs by Cluster Factor CMP AX,65518 MOV AL,'p' ; Show potiential Big FAT problem JA CRV_YES CRV_NO: MOV AL,'q' ; Not Reverse Video CRV_YES:MOV DrvFATPROB,AL ; Save Reverse State RETN STORdecNUM4: ; Put 4 digit decimal # in a message MOV CX,4 STORdecNUM: XCHG DX,AX ; Save value in DX MOV AL,' ' PUSH CX REP STOSB ; Blank out anything already there POP CX DEC DI MOV BP,10 ; Set divisor STD DTDB10: XCHG DX,AX ; Get back remaining value XOR DX,DX DIV BP ; Use word divide so xchg's will work XCHG DX,AX ; Get remainder ADD AL,'0' ; Convert to ascii STOSB ; Save it OR DX,DX ; Anything left to do? LOOPNZ DTDB10 ; Keep going CLD RETN ; SETUP_MBR_GEOMETRY - ; CALLED WITH: ; SI = Current IDSK_TABLE Ptr ; BX = Identify Info buffer Ptr ; NZ Set for bad READ_MBR ; RETURNS: ; Logical CYLINDERS, HEADS, and SPT set in the Physical Drive Table ; We will attempt to use the PCMBR to set the Logical Geometry used by Int 13h. ; If the PCMBR is invalid, we will set some default valid values. SETUP_MBR_GEOMETRY proc NEAR PUSH SI MOV DI,IDSK_PDSK_PTR[SI] ; Get Ptr to Physical drive table JNZ SMG_INVALID_MBR ; Bad Signature, go setup default PUSH BX CALL CHECK_MBR_GEOMETRY ; See if we can figure SPT and # of Heads POP BX MOV AX,PDSK_LOG_SPT[DI] ; Assume we got geometry ok JNC CALC_MAX_CYL ; We did, go set Maximum Cylinders SMG_INVALID_MBR: ; Setup default values, MBR is invalid MOV AX,PDSK_PHYSPH[DI] ; Get # Heads in AH, SPT in AL CMP AL,63 ; SPT in range for Int 13h? JA SET_MAX_SPT CMP word ptr ID_CYLS[BX],1024 ; Cylinders in range for Int 13h? JBE SET_SPT_HEADS SET_MAX_SPT: MOV AX,0FF3FH ; AH = 255 Heads, AL = 63 SPT SET_SPT_HEADS: MOV PDSK_LOG_SPT[DI],AX ; Log SPT and # of HEADS, 1 based CALC_MAX_CYL: MUL AH XCHG CX,AX ; Secs/Cyl to CX MOV AX,PDSK_LBA_SECS[DI] MOV DX,PDSK_LBA_SECS[DI+2] MOV SI,-1 ; Set Max total Cyls CMP DX,CX JAE SET_MAX_CYLS DIV CX XCHG SI,AX ; Set SI to total Cyls OR DX,DX ; Any remainder? JZ SET_MAX_CYLS INC SI ; 1 More SET_MAX_CYLS: MOV PDSK_LOG_CYLS[DI],SI ; Logical Total # of Cylinders 1 based POP SI RETN CHECK_MBR_GEOMETRY: MOV BX,offset CURRENT_MBR+PCMBR+(4*SIZE PCMASTERBOOTRECORD) SET_MAX_CYL_PTR: MOV BP,BX ; Save ptr to MAX CYL vaild part CHK_NEXT_PTR: ;Sort 4 entries by max ending cylinder CALL GET_PREV_PCMBR_PTR ; Did we run out yet ? JB GET_MAX_CYL_PTR CMP byte ptr PCMBR_TYPE[BX],0 ; Valid partition ? JNZ CHK_MAX_CYL_PTR ; Yes, see about cylinder RECHKNEXTMBR: CALL SWAPPCMBRENTRY JZ CHK_NEXT_PTR JMP RECHKNEXTMBR CHK_MAX_CYL_PTR: ; At end yet ? CMP BX,offset CURRENT_MBR+PCMBR+(3*SIZE PCMASTERBOOTRECORD) JZ SET_MAX_CYL_PTR ; Yes, set last valid Partition LEA SI,[BX+SIZE PCMASTERBOOTRECORD] CMP PCMBR_TYPE[SI],0 ; Next one unused ? JZ SET_MAX_CYL_PTR ; Yes, set this one last CALL CALC_ENDING_CYL XCHG DX,AX ; Save this one in DX XCHG BX,SI CALL CALC_ENDING_CYL ; Get Cyl for next one XCHG BX,SI CMP DX,AX ; This one > next one JB CHK_NEXT_PTR ; No, try NEXT JNZ SWAP_MAX_CYL ; Yes, go swap MOV DX,word ptr PCMBR_EHEAD[BX] ; Get ENDING head/Sector MOV AX,word ptr PCMBR_EHEAD[SI] AND AX,0011111111111111B AND DX,0011111111111111B XCHG AL,AH ; Fix for compare XCHG DL,DH CMP DX,AX ; This one > next one JBE CHK_NEXT_PTR ; No, try NEXT SWAP_MAX_CYL: CALL SWAPPCMBRENTRY JMP CHK_MAX_CYL_PTR GET_MAX_CYL_PTR: XOR DX,DX MOV BX,BP ; Get ptr to max ending cyl CMP PCMBR_TYPE[BX],DL ; Unused ? JZ BAD_PCMBR ; Yes, set it up as physical CALL CALC_ENDING_CYL ; Get Max ending cylinder JNZ USE_ENDING_CYL ; We found a Valid # for Ending CYL CALL CALC_STARTING_CYL ; Valid cylinder ? JNZ USE_STARITNG_CYL SUB BP,SIZE PCMASTERBOOTRECORD ; Try previous one CMP BP,offset CURRENT_MBR+PCMBR JAE GET_MAX_CYL_PTR BAD_PCMBR: STC RETN USE_STARITNG_CYL: MOV TEMP_C2,AX INC AX ; One base it MOV PDSK_LOG_CYLS[DI],AX ; Set Temp Cylinders, in case Head 0 only MOV AL,PCMBR_SHEAD[BX] ; Get STARTING head AND AX,11111111B MOV TEMP_H2,AX ; Save it CALL CALC_TSTART JMP short SET_T2 SETUPNOHEAD: CALL FIX4NOHEAD ; Return AL = # of Heads JC BAD_PCMBR JMP SETUPHEAD0 USE_ENDING_CYL: MOV TEMP_C2,AX INC AX ; One base it MOV PDSK_LOG_CYLS[DI],AX ; Set Temp Cylinders, in case Head 0 only MOV AL,PCMBR_EHEAD[BX] ; Get ENDING head AND AX,11111111B MOV TEMP_H2,AX ; Save ending head CALL CALC_TEND ADD BX,SIZE PCMASTERBOOTRECORD SET_T2: MOV TEMP_T2,AX ; Save T2 total MOV TEMP_T2+2,DX FIND_PREV_PART: ; Now search for non zero head CALL GET_PREV_PCMBR_PTR ; Did we run out yet ? JNC CHECK_ENDING_HEAD ; No, go check this one XOR AX,AX ; Yes, found no head CMP TEMP_H2,AX ; Did we find a head before ? JZ SETUPNOHEAD ; No, do it the other way MOV BX,BP ; Get ptr, and see if starting CALL CALC_STARTING_CYL ; has a cylinder JNZ FOUND_START_CYL ; Yes, use it CHECK4VALIDCYL: CALL GET_PREV_PCMBR_PTR ; Did we run out yet ? JB SETUPNOHEAD ; Yep, do other way CALL CALC_STARTING_CYL ; Valid cylinder ? JNZ FOUND_START_CYL ; Yes, use it CALL CALC_ENDING_CYL ; Valid cylinder ? JZ CHECK4VALIDCYL ; No, go check previous FOUND_ENDING_CYL: MOV AL,PCMBR_EHEAD[BX] ; Get ENDING head AND AX,11111111B MOV TEMP_H1,AX ; Save it CALL CALC_TEND JMP SHORT USE_ENDING FOUND_START_CYL: MOV AL,PCMBR_SHEAD[BX] ; Get STARTING head AND AX,11111111B MOV TEMP_H1,AX ; Save it CALL CALC_TSTART JMP SHORT USE_STARTING CHECK_ENDING_HEAD: MOV AL,PCMBR_EHEAD[BX] ; Get ENDING head AND AX,11111111B JNZ FOUND_ENDING_HEAD CHECK_START_HEAD: MOV AL,PCMBR_SHEAD[BX] ; Starting head AND AX,11111111B JZ FIND_PREV_PART ; Must find non-zero head MOV TEMP_H1,AX CALL CALC_TSTART ; Calc total for starting location CMP DX,TEMP_T2+2 JNZ USE_STARTING CMP AX,TEMP_T2 JZ FIND_PREV_PART ; Cannot be the same as before USE_STARTING: MOV TEMP_T1,AX ; Save T1 total MOV TEMP_T1+2,DX CALL CALC_STARTING_CYL JMP SHORT SET_C1 FOUND_ENDING_HEAD: MOV TEMP_H1,AX CALL CALC_TEND ; Calc total for ending location CMP DX,TEMP_T2+2 JNZ USE_ENDING CMP AX,TEMP_T2 JZ CHECK_START_HEAD ; Cannot be the same as before USE_ENDING: MOV TEMP_T1,AX ; Save T1 total MOV TEMP_T1+2,DX CALL CALC_ENDING_CYL ; Here we calculate SPT = (C2)*(T1)-(C1)*(T2) / (H1)*(C2)-(C1)*(T2) SET_C1: MOV TEMP_C1,AX MUL TEMP_H2 MOV TEMP_DD2,AX MOV TEMP_DD2+2,DX MOV AX,TEMP_C2 MUL TEMP_H1 SUB AX,TEMP_DD2 SBB DX,TEMP_DD2+2 TEST DX,8000H ; Negative # ? JZ DEN_POSITIVE NEG AX NOT DX DEN_POSITIVE: XCHG SI,AX ; Save Denominator in BP:SI MOV BP,DX MOV AX,TEMP_T1 MUL TEMP_C2 MOV TEMP_DD1,AX MOV TEMP_DD1+2,DX MOV TEMP_DD1+4,0 MOV AX,TEMP_T1+2 MUL TEMP_C2 ADD TEMP_DD1+2,AX ADC TEMP_DD1+4,DX MOV AX,TEMP_T2 MUL TEMP_C1 MOV TEMP_DD2,AX MOV TEMP_DD2+2,DX MOV TEMP_DD2+4,0 MOV AX,TEMP_T2+2 MUL TEMP_C1 ADD TEMP_DD2+2,AX ADC TEMP_DD2+4,DX MOV AX,TEMP_DD1 MOV DX,TEMP_DD1+2 MOV CX,TEMP_DD1+4 SUB AX,TEMP_DD2 SBB DX,TEMP_DD2+2 SBB CX,TEMP_DD2+4 TEST CX,8000H JZ NUM_POSITIVE NEG AX NOT DX NOT CX NUM_POSITIVE: PUSH DI ; Denominator is BP:SI XCHG AX,DI ; Numerator now CX:DX:DI XOR AX,AX ; Init SPT CALC_SPT: INC AX ; Count how many times we sub BP:SI SUB DI,SI SBB DX,BP ; Divide TWORD/DWORD by using SBB CX,0 ; subtraction JC BAD_PCMBR_DI ; Must be even, or error JNZ CALC_SPT OR DX,DX JNZ CALC_SPT OR DI,DI ; Done yet ? JNZ CALC_SPT POP DI ; Yes, MOV PDSK_LOG_SPT[DI],AL ; Set Sectors/Track ; Now calc # of HEADS = (T2)-(H2)*(SPT) / (C2)*(SPT) MOV BX,AX ; Save SPT for below MUL TEMP_C2 XCHG SI,AX ; Save DEN in BP:SI MOV BP,DX MOV AX,TEMP_H2 MUL BX ; * SPT MOV BX,TEMP_T2 MOV CX,TEMP_T2+2 SUB BX,AX SBB CX,DX ; Numerator is CX:BX XOR AX,AX CALC_HEADS: INC AX ; Calc # of heads SUB BX,SI ; Divide by using subtraction SBB CX,BP ; for DWORD/DWORD JC BAD_PCMBR2 JNZ CALC_HEADS OR BX,BX JNZ CALC_HEADS SETUPHEAD0: ; Entry if we used the other method MOV PDSK_LOG_HEADS[DI],AL ; Set Heads CLC RETN BAD_PCMBR2: DEC AX ; Assume mistake in MBR JMP SETUPHEAD0 ; is correctable since SPT seems valid BAD_PCMBR_DI: POP DI STC ; Return CY for bad RETN ; We can arrive here in two conditions: ; 1. A single partition that has a possible ending head value, ; with starting cylinder value 0. ; 2. Single/Multiple partitions that all have head values of 0. FIX4NOHEAD: XOR SI,SI ; Init SPT to 0 MOV CX,4 ; Check all 4 parts MOV BX,offset CURRENT_MBR+PCMBR SETUPNOHEADLP: CMP PCMBR_TYPE[BX],CH ; Valid partition ? JZ NGTEC MOV BP,BX ; Save ptr to last part MOV AX,PCMBR_SCYL[BX] ; Starting Cylinder/Sector CALL CHK_MAX_SEC MOV AX,PCMBR_ECYL[BX] ; Ending Cylinder/Sector CALL CHK_MAX_SEC NGTEC: ADD BX,SIZE PCMASTERBOOTRECORD LOOP SETUPNOHEADLP ; Keep looking for greatest sec # MOV BX,BP ; Set ptr to last valid part CMP SI,2 JA FSECN1 MOV SI,2 ; Do not use SPT of 1 FSECN1: CMP BX,offset CURRENT_MBR+PCMBR ; 1st or 2nd condition ? JNZ SUNOH_COND2 TEST PCMBR_EHEAD[BX],11111111B ; Ending Head 0 ? JNZ SUNOH_COND1 SUNOH_COND2: ; Here with all heads 0 CALL CALC_SEC_CYL ; in CX JNZ F4NHBAD ; Error or odd COND2_NEXT_SPT: MOV AX,CX XOR DX,DX DIV SI ; SPCyl / SPT INC SI OR DX,DX JNZ COND2_NEXT_SPT ; Not even CMP AX,255 ; Heads in range ? JA COND2_NEXT_SPT DEC SI ; Fix SPT FOUNDVALIDSPT: XCHG SI,AX ; Save # of Heads MOV PDSK_LOG_SPT[DI],AL ; Set Sectors/Track XCHG SI,AX ; Return # of Heads in AL CLC RETN F4NHBAD:STC ; Return CY for bad RETN CALC_SEC_CYL: CALL CALC_TEND ; Base+Size-Sector MOV CX,PDSK_LOG_CYLS[DI] ; Get Cylinders DEC CX ; Zero base # of cyls CMP CX,DX ; Number of sectors/cyl > 1 word ? JBE F4NHBAD DIV CX XCHG CX,AX ; Save sec's/cylinder OR DX,DX ; Even secs/cyl ? / clear carry RETN ; Return ZF for even ; Here with ending head > 0 , only one part ; SI max SECTOR found SUNOH_COND1: MOV MINSPT,SI CALL CALC_SEC_CYL ; Return MIN SPC in CX, # of CYLS in AX JB F4NHBAD ; Error MOV MAXSPC,CX ; Save MAX SPC PUSH AX ; Save # of cyls CALL CALC_TEND POP SI ; # of cyls to SI PUSH AX ; Save Full track sec count PUSH DX INC SI ; Add for partial cyl used DIV SI ; TOT / CYLS DEC SI ; Back to full cyls used MOV CL,PCMBR_EHEAD[BX] AND CX,11111111B XCHG BX,AX ; Save Min SPC POP DX POP AX ; Get back full track sec count RETEST: PUSH BX MOV TEMP_SPC,BX PUSH AX PUSH DX XCHG BX,AX MUL SI ; Mul SPC by full cyls used POP BP PUSH BP SUB BX,AX ; Sub total used by full cyls SBB BP,DX XCHG BX,AX MOV DX,BP DIV CX ; Divide remaining by heads used OR DX,DX JNZ TEST_SPCX CMP AX,63 ; SPT > max ? JA TEST_SPCX CMP AX,0 ; or < min ? org $-2 MINSPT DW ? JB TEST_SPCX XCHG BP,AX ; Save SPT in BP MOV AX,0 org $-2 TEMP_SPC DW ? XOR DX,DX DIV BP ; SPC / SPT OR DX,DX ; Even # of Heads ? JNZ TEST_SPCX CMP AX,CX ; Heads less than current head ? JB TEST_SPCX CMP AX,255 ; Heads > max ? JA TEST_SPCX ADD SP,6 MOV SI,BP ; Set SPT JMP FOUNDVALIDSPT TEST_SPCX: POP DX POP AX POP BX INC BX ; Try next SPCyl CMP BX,1234h org $-2 MAXSPC DW ? JBE RETEST JMP F4NHBAD ; Ran out must be bad entry CHK_MAX_SEC: AND AX,111111B ; Isolate sector CMP AX,SI ; Greater than previous ? JB NGTSC XCHG SI,AX ; Save greatest Sector found NGTSC: RETN COMMENT ^ ; Call with DI ptr to Phy Drv Tab, BX ptr to PCMBR ; Return DX:AX logical sector of Starting Location. CNV_PHY_2_LOG: PUSH CX MOV AL,PDSK_LOG_SPT[DI] ; Get Sectors/Track(1 based) CBW PUSH AX MOV DL,PDSK_LOG_HEADS[DI] ; Get # Heads(1 based) XOR DH,DH MUL DX ; Calc Secs/Cyl XCHG CX,AX ; Save in CX POP DX MOV AL,PCMBR_SHEAD[BX] ; Starting head(0 based) AND AX,11111111B MUL DX ; Calc Sectors for starting head PUSH AX ; Save it PUSH CX ; and secs/cyl CALL CALC_STARTING_CYL MOV CX,PCMBR_SCYL[BX] ; Starting Cylinder/Sector AND CX,111111B ; Isolate sector DEC CX POP DX ; Get Secs/cyl MUL DX ; Calc Secs for starting cyl ADD AX,CX ; Add to starting sec ADC DX,0 POP CX ; Get # of Secs for starting head ADD AX,CX ADC DX,0 ; Add to total POP CX RETN ^ SWAPPCMBRENTRY: MOV CX,SIZE PCMASTERBOOTRECORD CMP BX,offset CURRENT_MBR+PCMBR+(3*SIZE PCMASTERBOOTRECORD) JZ SWPCMBRENTX CMP PCMBR_TYPE[BX+SIZE PCMASTERBOOTRECORD],CH JZ SWPCMBRENTX SWPCMBRENTLP: MOV AL,[BX] XCHG [BX+SIZE PCMASTERBOOTRECORD],AL MOV [BX],AL INC BX LOOP SWPCMBRENTLP SWPCMBRENTX: RETN GET_PREV_PCMBR_PTR: SUB BX,SIZE PCMASTERBOOTRECORD ; Point to previous one CMP BX,offset CURRENT_MBR+PCMBR ; Did we run out yet ? RETN CALC_TEND: MOV AX,PCMBR_BSEC[BX] ; Starting sector for part, MOV DX,PCMBR_BSEC[BX+2] ; Relative to beginning of disk ADD AX,PCMBR_SIZE[BX] ; + Partition length in sectors ADC DX,PCMBR_SIZE[BX+2] MOV CX,PCMBR_ECYL[BX] ; Ending Cylinder/Sector CALC_TSEC: AND CX,111111B ; Isolate sector to CX SUB AX,CX SBB DX,0 RETN CALC_TSTART: XOR DX,DX MOV AX,PCMBR_BSEC[BX] ; Starting sector for part, ADD AX,1 ; One base ADC DX,PCMBR_BSEC[BX+2] ; Relative to beginning of disk MOV CX,PCMBR_SCYL[BX] ; Starting Cylinder/Sector JMP CALC_TSEC CALC_ENDING_CYL: ; MOV CH,PCMBR_EHEAD[BX] ; Ending HEAD MOV AX,PCMBR_ECYL[BX] ; Ending Cylinder/Sector CALC_CYL: ; MOV CL,4 ; Get Top 2 bits from head ; SHR CH,CL ; to use as bits 10 and 11 for cyl ; AND CH,1100B AND AX,1111111111000000B XCHG AL,AH ROL AH,1 ; Fix Cyl Bits 8 and 9 ROL AH,1 ; OR AH,CH ; Add in bits 10 and 11 OR AX,AX JZ CALC_CYL_EXIT CMP AX,3FFH ; Ret NZ for valid Cylinder # CALC_CYL_EXIT: RETN CALC_STARTING_CYL: ; MOV CH,PCMBR_SHEAD[BX] ; Starting HEAD MOV AX,PCMBR_SCYL[BX] ; Starting Cylinder/Sector JMP CALC_CYL SETUP_MBR_GEOMETRY ENDP ; **** This is the start of the low level IDE command routines *** ; STARTsoftRESET ; Called With: ; BX = +0 = Ptr to Exclude device flag for device 0 ; +1 = Ptr to Exclude device flag for device 1 ; +4 = Ptr to Found device flag for device 0 ; +5 = Ptr to Found device flag for device 1 ; +8 = PIO transfer mode to use for device 0 ; +9 = PIO transfer mode to use for device 1 ; +12 = Wait States + J1 or J2 selection for device 0 ; +13 = Wait States + J1 or J2 selection for device 1 ; +16 = Ptr to Got Identify Device Buffer flag for device 0 ; +17 = Ptr to Got Identify Device Buffer flag for device 1 STARTsoftRESET proc NEAR CMP word ptr [BX],TRUE ; Excluding both Drives? JZ SSRX ; First select the correct bus Primary J1 or Secondary J2 MOV AL,[BX+12] ; Get Buss + Wait States Master Device 0 OUT IDE_AC,AL ; Select Primary or Seconday Bus IN AL,IDE_STAT ; Clear any possible error condition ; Now check the registers for all values the same, which indicates nothing is there MOV AL,CB_DH_DEV0 ; Select Master Device 0 CALL TEST_REGISTERS JZ NoMASTER ; Master Device 0 was not all the same MOV byte ptr [BX+4],TRUE ; Set May have Detected Master Flag NoMASTER: MOV AL,[BX+13] ; Get Buss + Wait States Slave Device 1 OUT IDE_AC,AL ; Select Primary or Seconday Bus IN AL,IDE_STAT ; Clear any possible error condition MOV AL,CB_DH_DEV1 ; Select Slave Device 1 CALL TEST_REGISTERS JZ NoSLAVE MOV byte ptr [BX+5],TRUE ; Set May have Detected Slave Flag NoSLAVE:CMP word ptr [BX+4],FALSE ; See if both Master and Slave were all the same, JZ SSRX ; Yes, assume nothing is on this buss. MOV AL,[BX+12] ; Get Buss + Wait States Master Device 0 OUT IDE_AC,AL ; Select Primary or Seconday Bus IN AL,IDE_STAT ; Clear any possible error condition ; Next we will issue "Software Reset" to the IDE bus, no unconditional waits will be ; used in case no controller or drives exist, both devices will respond if they exist. ; After this command Device 0 is always selected. ISSUEsoftRESET: MOV AL,CB_DC_SRST+CB_DC_NIEN OUT IDE_DC,AL ; Set reset bit, Disable interrupts ; The drive controllers are now required to set the busy bit within 400ns ; The newest specs require the host to leave the SRST bit on for a min of 5us ; we will wait 2 timer tics, 8us 8000ns, see if the Master device set the Busy bit, ; and turn off the SRST bit. MOV AH,2 ; 2 timer tic delay CALL TIMER_DELAY IN AL,IDE_ASTAT ; Read Aux Status Register TEST AL,CB_STAT_BSY ; Check for Busy bit JNZ YesMASTER ; Busy bit was correctly set, use previous setting MOV byte ptr [BX+4],FALSE ; Show we did not find Master Device 0 YesMASTER: MOV AL,CB_DC_NIEN ; Clear SRST reset bit, keep interrupts Disabled OUT IDE_DC,AL COMMENT ^ ; Now see if the Slave devices Status Register is something other than 0 or 0FFh MOV AL,[BX+13] ; Get Buss + Wait States Slave Device 1 OUT IDE_AC,AL ; Select Primary or Seconday Bus MOV AL,CB_DH_DEV1 ; Select Slave Device 1 OUT IDE_DH,AL IN AL,IDE_ASTAT ; Read Aux Status Register, Short Delay IN AL,IDE_STAT ; Read Status Register INC AX CMP AL,1 ; 0FFh and 0 are no good JA SSRX ; Not 0 or 0FFh, use previous setting MOV byte ptr [BX+5],FALSE ; Show we did not find Slave Device 1 ^ SSRX: RETN TEST_REGISTERS: OUT IDE_DH,AL ; Select Master or Slave IN AL,IDE_STAT ; Clear any possible error condition MOV AL,0FFH ; Set Cyl Low register to 0FFh CALL REGISTER_TEST ; See if nothing there JZ REGZERO ; Nothing, exit XOR AL,AL ; See if we still get something with 0 REGISTER_TEST: OUT IDE_CL,AL ; Put value on an empty connector MOV DX,IDE_BASE_ADDR MOV AH,AL ; Save the value we wrote 0 or FF IN AL,IDE_ASTAT CMP AL,AH ; Aux Status can not be 0 or FF JZ REGZERO MOV AH,AL ; Save Aux Status value in AH MOV CX,8 ; Check Against the First 8 Registers CHKregLP: IN AL,DX ; Get next register value INC DX CMP AL,AH ; Same as First register? LOOPZ CHKregLP ; Yes, keep checking REGZERO:RETN ; Return ZR for all the same STARTsoftRESET ENDP ; DETECT_DEVICES - Check Signature and Issue Recalibrate ; Called With: ; BX = +0 = Ptr to Exclude device flag for device 0 ; +1 = Ptr to Exclude device flag for device 1 ; +4 = Ptr to Found device flag for device 0 ; +5 = Ptr to Found device flag for device 1 ; +8 = PIO transfer mode to use for device 0 ; +9 = PIO transfer mode to use for device 1 ; +12 = Wait States + J1 or J2 selection for device 0 ; +13 = Wait States + J1 or J2 selection for device 1 ; +16 = Ptr to Got Identify Device Buffer flag for device 0 ; +17 = Ptr to Got Identify Device Buffer flag for device 1 DETECT_DEVICES proc NEAR ROR byte ptr [BX+4],1 ; Do we think we found Master Device 0? JC FoundMASTER ; We might be in a "Slave Only" condition, see what to do CheckSLAVEonly: ROR byte ptr [BX+5],1 ; Do we think we found Slave Device 1? JNC NoSLAVEdev MOV byte ptr [BX+5],FALSE ; Assume Slave does not exist, excluded, or Packet Device ROR byte ptr [BX+1],1 ; Excluding Slave Device 1? JC NoSLAVEdev MOV AL,[BX+13] ; Get Buss + Wait States Slave Device 1 OUT IDE_AC,AL ; Select Primary or Seconday Bus ; We will now follow the ATA specifications on how to determine if ; Slave Only Device 1 is functioning CALL TIMER1msDELAY ; At least 2ms total wait before checking status CALL TIMER1msDELAY CALL SET31_5 ; Wait up to at least 5 seconds, 31 from reset REchkSLAVE: MOV AL,CB_DH_DEV1 ; Select Slave Device 1 OUT IDE_DH,AL MOV DX,IDE_SN ; Point to Sector Number and Cyl Low Registers, + delay IN AL,IDE_STAT TEST AL,CB_STAT_BSY ; See if device is still busy JNZ SLAVEisBUSY IN AX,DX ; Read Current value XCHG DI,AX ; Save in DI MOV AX,0202h ; Now check if we can modify the sector registers OUT DX,AX ; this does not absolutly assure device exists IN AL,IDE_STAT ; Waste some time for things to settle on the bus IN AX,DX ; Read back what we wrote CMP AX,0202h ; Success with valid values? XCHG DI,AX ; Get back original value OUT DX,AX ; Restore for Signature test below JZ FoundSLAVE ; Device appears ready, go check for errors and Signature SLAVEisBUSY: CALL TIMER1msDELAY ; Not ready yet, wait another ms LOOP REchkSLAVE ; Keep waiting up to 31 seconds NoSLAVEdev: RET ; Did not find Valid Slave Device ; Now select the correct bus Primary J1 or Secondary J2 FoundMASTER: MOV byte ptr [BX+4],FALSE ; Assume Master does not exist, ; excluded, or Packet Device MOV AL,[BX+12] ; Get Buss + Wait States Master Drive 0 OUT IDE_AC,AL ; Select Primary or Seconday Bus ; MOV AL,CB_DH_DEV0 ; Select Device 0 ; OUT IDE_DH,AL ; "Software Reset" has been issued. Busy has been detected. Reset has been cleared. ; The Master device is always selected following a Software Reset. ; The drive controllers are now required to clear the busy bit within 31 seconds ; of RESET if a power-up condition exists, otherwise we will wait at least 5 seconds ; The newest specs require the host to wait 2ms before checking for Not busy CALL TIMER1msDELAY ; 2ms total wait before checking status CALL SET31_5 ; Wait up to at least 5 seconds, 31 from reset CALL TIMERwaitNBSY JNZ CheckSLAVEonly ; Master did not clear busy bit, check Slave only ; At this point we should be able to issue commands that do not require DRDY to be set. ; EXECUTE_DEVICE_DIAGNOSTIC, INITIALIZE_DEVICE_PARAMETERS, and IDENTIFY_PACKET_DEVICE ; Now deturmine weather Device 0 and 1 exists and passed Diagnostics ROR byte ptr [BX],1 ; Excluding Master Device 0? IN AL,IDE_ERR ; Read Error Register PUSH AX ; Save for Device 1 check JC CHECKDEV1 ; Only check Device 1 AND AL,NOT CB_ER_DEV1 ; Remove Device 1 failure flag CMP AL,CB_ER_PASS ; Device 0 passed Diag? JNZ CHECKDEV1 ; No, skip ; Device 0 did not fail Diag, Check its Signature CALL CHECKdevSIG ; Check Signature JC CHECKDEV1 MOV byte ptr [BX+4],TRUE ; Set our Device 0 exists flag CHECKDEV1: ROR byte ptr [BX+5],1 ; Do we think we found Slave Device 1? CMC JC SkipSlave MOV byte ptr [BX+5],FALSE ; Assume Slave does not exist or Packet Device MOV AL,[BX+13] ; Select correct # of wait states for Device 1 OUT IDE_AC,AL ; And Primary or Seconday Bus MOV AL,CB_DH_DEV1 ; Select Device 1 OUT IDE_DH,AL ; Select Device ROR byte ptr [BX+1],1 ; Excluding Slave Device 1? SkipSlave: POP AX ; Get back Master's Error Register value JC CHECKDEV0 ; Yes excluding Slave, Only check Device 0 TEST AL,CB_ER_DEV1 ; Did Device 1 Report failed Diag to Device 0? JNZ CHECKDEV0 ; yes, skip checking its Signature ; Device 1 did not fail Diag, Check its error register, Check Signature, Issue Recalibrate FoundSLAVE: IN AL,IDE_ERR ; Read Slave's Error Register CMP AL,CB_ER_PASS ; Device 1 passed Diag? JNZ CHECKDEV0 ; no, skip to Master check CALL CHECKdevSIG ; Check Signature JC CHECKDEV0 ; Bad sig, must be Packet device ; CALL START_RECAL ; Start the Recalibrate command ; JC CHECKDEV0 ; Skip, Drive did NOT show DRDY and/or Not Busy MOV byte ptr [BX+5],TRUE ; Set our Device 1 exists flag ; Now see if we should issue Recalibrate for dev 0 CHECKDEV0: comment ^ ROR byte ptr [BX+4],1 ; Test our Device 0 exists flag JNC DETECT_EXIT ; failed diag, invalid signature, or we are skipping Master MOV AL,[BX+12] ; Select correct # of wait states for Device 0 OUT IDE_AC,AL ; And Primary or Seconday Bus MOV AL,CB_DH_DEV0 ; Select Device 0 OUT IDE_DH,AL CALL START_RECAL JNC DETECT_EXIT ; Drive did show Not Busy and DRDY MOV byte ptr [BX+4],FALSE ; Reset Device 0 Exists flag to FALSE ^ DETECT_EXIT: RETN comment ^ ; We do this in part because some legacy drives require it, but we are also going ; to need to read the Master Boot Record that exists at Physical Sector Zero. START_RECAL: CALL TIMERwait250NBSY ; Wait 1/4 second for Not Busy JNZ STCRET ; Drive did not clear busy bit MOV DX,IDE_FR XOR AX,AX OUT DX,AX ; Feature and Sector count Registers 0 INC DX INC DX INC AX ; Sector # 1, Cyl low 0 OUT DX,AX IN AL,IDE_DH ; Get current device AND AX,CB_DH_DEV1 ; Isolate it and set AH=0 XCHG AL,AH ; Device+Head 0 to AH, Cyl high 0 to AL INC DX INC DX OUT DX,AX ; Cyl high 0, Head 0 + drive ; At this point all NON-PACKET device must show DRDY, drive ready bit in the status register. ; We can not be absolutely certain a device actually exists until we issue a command and get ; the proper response. Also it seems some legacy devices might require a command before ; Identify Device command. CALL WAIT4DRDY250 ; Wait up to 1/4 second for drive ready JC STCRET MOV AL,CMD_RECALIBRATE OUT IDE_CMD,AL ; Issue Recalibrate command RET ; Ret NC ^ ; Test Signature to deturmine Packet or Normal device CHECKdevSIG: CALL TIMERwait250NBSY ; Wait 1/4 second for Not Busy JNZ STCRET ; Drive did not clear busy bit ; We got NOT busy, Check for correct Signature MOV DX,IDE_SC IN AX,DX ; Read Sector Count and Number CMP AX,101H JNZ STCRET ; All devices return 1 for sector count and number INC DX INC DX ; Point to Cylinder Registers IN AX,DX ; Read Cylinder high and low OR AX,AX ; Non-Packet Devices return 0 for Cylinder JNZ STCRET IN AL,IDE_STAT ; If the status register is 0 OR AL,AL ; the device doesn't exist JNZ CMCLCX ; This does not absolutly assure device exists STCRET: STC ; Carry indicates none/bad drive CMCLCX: RETN DETECT_DEVICES ENDP ; READ_ID_BUFS ; Called With: ; DI = IDBUFFER Location to store IDENTIFY_DEVICE info ; BX = +0 = Ptr to Exclude device flag for device 0 ; +1 = Ptr to Exclude device flag for device 1 ; +4 = Ptr to Found device flag for device 0 ; +5 = Ptr to Found device flag for device 1 ; +8 = PIO transfer mode to use for device 0 ; +9 = PIO transfer mode to use for device 1 ; +12 = Wait States + J1 or J2 selection for device 0 ; +13 = Wait States + J1 or J2 selection for device 1 ; +16 = Ptr to Got Identify Device Buffer flag for device 0 ; +17 = Ptr to Got Identify Device Buffer flag for device 1 READ_ID_BUFS proc NEAR ROR byte ptr [BX+4],1 ; Did we find Device 0 and not skipping it? JNC TESTDEV1 MOV AL,[BX+12] OUT IDE_AC,AL ; Select the correct bus Primary J1 or Secondary J2 MOV AL,CB_DH_DEV0 ; Select Device 0 CALL ReadIDENTIFYinfo JC TESTDEV1 ; Could not get device to respond MOV byte ptr [BX+16],TRUE ; Set our Got Device 0 BUFFER flag TESTDEV1: ADD DI,512 ; Point to the next spot in buffer ROR byte ptr [BX+5],1 ; Did we find Device 1 and not skipping it? JNC RIBX MOV AL,[BX+13] OUT IDE_AC,AL ; Select the correct bus Primary J1 or Secondary J2 MOV AL,CB_DH_DEV1 ; Select Device 1 INC BX ; So we can get correct Command Line Transfer Mode CALL ReadIDENTIFYinfo DEC BX JC RIBX ; Could not get device to respond MOV byte ptr [BX+17],TRUE ; Set our Got Device 1 BUFFER flag RIBX: RET ReadIDENTIFYinfo: OUT IDE_DH,AL ; Select correct Device 0 or 1 ; "Recalibrate" has been issued, we need to wait for legacy drives to Restore to Cylinder 0 ; CALL WAIT31_5NBSY ; Wait up to at least 5 seconds, 31 from reset ; JNZ STCRET ; Did not get Not Busy condition ; Now issue SET FEATURES, Set PIO Transfer Mode, if user requested other than default Mode ; This follows a non-data command protocol CALL TIMERwait250NBSY ; Wait up to 1/4 second for Not Busy JNZ STCRET MOV AL,[BX+8] ; Set to 0-4 if user wants other than default mode CALL SETPIOMODE ; A value of 5 will set Default Transfer Mode JNZ STCRET ; Drive ERROR, Did not get Not Busy or DRDY condition TEST AL,CB_STAT_DF+CB_STAT_ERR JNZ STCRET ; Drive set error bit, ERROR ; CALL IDENTIFY_DRIVE ; Try and read IDENTIFY info Buffer ; RET ; Success, exit NC READ_ID_BUFS ENDP IDENTIFY_DRIVE proc NEAR ; Return CY for error ; Issue the IDENTIFY_DEVICE command, PIO data-in protocol CALL WAIT4DRDY250 ; Wait up to 1/4 second for drive ready JC STCRET MOV AL,CMD_IDENTIFY_DEVICE OUT IDE_CMD,AL ; Issue identify command MOV DX,IDE_DATAIN CALL SET31_5 ; Wait up to at least 5 seconds, 31 from reset CALL SKIP1STDELAY ; Wait for Not Busy JNZ STCRET TEST AL,CB_STAT_DRQ ; Drive ready to transfer data? JZ STCRET ; No DRQ, exit error CLI PUSH DI ; Setup for 16-bit data transfer MOV CX,256 ; of 512 byte IDENTIFY_DEVICE info REP_INSW STI POP DI ; More than 400ns delay IN AL,IDE_STAT ; before reading Status Register TEST AL,CB_STAT_ERR+CB_STAT_DF+CB_STAT_DRQ ; Check for ERR, Dev Fault, or DRQ still set? JNZ STCRET IDPX: RETN ; No, return NC IDENTIFY_DRIVE endp ; Issue Initialize device Parameters, required by some ATA-1 devices DRDY not required ; This command is never supposed to return an error in ATA-1 ; Mandatory Support for devices not implementing the PACKET Command feature set if ; a CHS translation is supported. ; Not mandatory for devices not implementing the PACKET command feature set if the device ; capacity is greater then 8 Gbytes and only LBA translation is supported. ; Called with DL = # of Sectors/Track and DH = (Total # of Heads -1) to program INITIALIZE_DEVICE_PARAMETERS proc NEAR ; Return ZERO flag indicator CALL TIMERwait250NBSY ; Wait up to 1/4 second for Not Busy JNZ IDPX MOV AL,DL ; Get logical sectors/track OUT IDE_SC,AL ; Set in Sector Count Register IN AL,IDE_DH ; Get current Device/Head reg value AND AL,CB_DH_DEV1 ; Isolate correct drive OR AL,DH ; Set # of Heads OUT IDE_DH,AL ; Set in Dev/Head Register CALL TIMERwait250NBSY ; Wait up to 1/4 second for Not Busy JNZ IDPX MOV AL,CMD_INITIALIZE_DEVICE_PARAMETERS OUT IDE_CMD,AL JMP short TIMERwait250NBSY ; Will return ZERO flag indicator and AL=status INITIALIZE_DEVICE_PARAMETERS endp comment ^ WAIT2MIN4DRDY: ; Will return immediately if Drive Ready MOV CX,480 ; 1/4 Sec * 4 * 60 * 2 Minutes W2M4DL: PUSH CX CALL WAIT4DRDY250 ; Check for Drive Ready POP CX LOOPZ W2M4DL ; Wait up to 2 Minutes for Drive Ready RETN ; Will return ZR and CY for NOT Ready ^ WAIT4DRDY250: ; Will return immediately if Drive Ready IN AL,IDE_STAT ; Clear possible error condition MOV CX,250 ; 250ms delay (1/4 sec) Loop counter JMP short SK1ST WAIT4L: CALL TIMER1msDELAY ; 1ms wait IN AL,IDE_ASTAT SK1ST: TEST AL,CB_STAT_RDY ; Check for drive ready to accept command LOOPZ WAIT4L JNZ WDRDYX ; Carry is clear STC ; Timed out, Drive Not Ready, Set CY WDRDYX: RETN SETZRFLAG: XOR AL,AL RET SETNZFLAG: OR SP,SP RETN SETPIOMODE: CMP AL,5 ; Valid Mode? JA SETZRFLAG ; No, exit ALL OK JNZ SETnewMODE ; Set Flow Control Mode? MOV AL,PIO_DEFAULT_MODE ; 5 is set PIO Default Transfer Mode JMP short SETdefaultMODE ; otherwise use default mode 0,1, or 2 SETnewMODE: ; Modes 0-4 are Valid OR AL,PIO_FLOW_MODE ; PIO Flow Control Transfer Mode x 00001 nnn SETdefaultMODE: OUT IDE_SC,AL ; Set mode in sector count reg MOV AL,FR_STM ; Set transfer mode(value in Sector Count register) ;SETFEATURES: OUT IDE_FR,AL ; Set Feature Register to correct value CALL WAIT4DRDY250 ; Wait up to 1/4 second for drive ready JC SETNZFLAG MOV AL,CMD_SET_FEATURES ; Execute Command Set Features OUT IDE_CMD,AL TIMERwait250NBSY: ; Will return immediately if busy clear MOV CX,250 ; 250ms delay (1/4 sec) Loop counter JMP short SKIP1STDELAY ; Skip first delay TIMERwaitNBSY: ; CX set for # of ms to wait for busy to clear CALL TIMER1msDELAY ; 1ms wait SKIP1STDELAY: ; CX set, will return immediately if busy clear IN AL,IDE_ASTAT TEST AL,CB_STAT_BSY LOOPNZ TIMERwaitNBSY ; Keep waiting for ~busy IN AL,IDE_STAT ; Read status > 400ns after Not Busy TWNBSY: RETN ; ZERO FLAG tells all SET31_5: MOV CX,1000*31 ; 31 second loop counter SUB CX,CLUCWAITIME ; Adjusted for how long we have already waited CMP CX,1000*5 JAE SET31_5X MOV CX,1000*5 ; Make it at least 5 seconds SET31_5X: RETN comment ^ SELJ1M: CALL SELJ1 MOV AL,CB_DH_DEV0 OUT IDE_DH,AL RETN SELJ1S: CALL SELJ1 MOV AL,CB_DH_DEV1 OUT IDE_DH,AL RETN SELJ2M: CALL SELJ2 MOV AL,CB_DH_DEV0 OUT IDE_DH,AL RETN SELJ2S: CALL SELJ2 MOV AL,CB_DH_DEV1 OUT IDE_DH,AL RETN SELJ1: CALL WAIT4HZ MOV AL,CB_AC_0WAITS+CB_AC_EWPEEP+CB_AC_BUS1 OUT IDE_AC,AL RETN SELJ2: CALL WAIT4HZ MOV AL,CB_AC_0WAITS+CB_AC_EWPEEP+CB_AC_BUS2 OUT IDE_AC,AL RETN WAIT4HZ: MOV AL,CB_AC_0WAITS+CB_AC_EWPEEP+CB_AC_BUS0 OUT IDE_AC,AL PUSH DX MOV DX,IDE_DATAIN W4HZ: IN AX,DX CMP AX,0FF7FH JNZ W4HZ POP DX RETN ^ ;-----------------------------------------------------------------------; ; ; ; TIMER DELAY ROUTINE - So code can be CPU speed independent ; ; Entry - AH = # of timer ticks to wait ; ; (each is 1/250,000th of a second, 4us) ; ; Uses AL ; ;-----------------------------------------------------------------------; TIM_CNT_2 equ 0E6h ;Timer 2 control port TIM_CW equ 0E7h ;Timer control port TIMER1msDELAY: INC word ptr CLUCWAITIME ; Keep track of how long we have waited since power-up MOV AH,250 ;Delay 1ms TIMER_DELAY PROC NEAR CLI MOV AL,0B0H OUT TIM_CW,AL ;SELECT ON TIMER 2 MOV AL,0FDH OUT ZTIMERS,AL ;CLEAR Timer interrupt status port OFF MOV AL,AH ;SET IT TO # of ticks requested OUT TIM_CNT_2,AL MOV AL,80H ;But make it negative OUT TIM_CNT_2,AL WAIT_A_SEC: MOV AL,80H OUT TIM_CW,AL ;READ TIMER 2 "ON THE FLY" IN AL,TIM_CNT_2 IN AL,TIM_CNT_2 SHL AL,1 ;IS MOST SIG STILL NEG? JC WAIT_A_SEC MOV AL,0B0H ;SHUT OFF TIMER 2 OUT TIM_CW,AL STI RETN TIMER_DELAY ENDP IDBUFFER = $ + 0 ; 2K Buffer for holding up to 4 drives IDENTIFY_DEVICE info CURRENT_MBR = IDBUFFER+2048 ; Current Master Boot Record CURRENT_BL = CURRENT_MBR+512 ; Current Boot Loader Record LLIDEHD ends end BEGIN