'=========================================================================== ' Subject: DISPLAY ATA/ATAPI DEVICE INFO Date: 06-18-98 (00:19) ' Author: Alec Skelly Code: QB, PDS ' Origin: skellya@usa.net Packet: DISK.ABC '=========================================================================== '**************************************************************************** '*** PIO_IN by Alec Skelly 6-17-98 (QuickBasic 4.5) *** '*** This program displays ATA/ATAPI device information. Because it uses *** '*** the device's own protocol (PIO Data In) to talk directly through the *** '*** controller, it can detect devices that aren't configured in CMOS. *** '*** It will not detect devices under WIN95/98--reboot to MS-DOS mode. *** '*** A far more powerful version of this program, ATA_INFO, can be down- *** '*** loaded free from my web page: http://www.geocities.com/Athens/8254. *** '**************************************************************************** DECLARE SUB PIODataIn (port%, device%, Buffer AS ANY, opcode%, result%, nest%) DECLARE FUNCTION INP16% (port%) DECLARE SUB POKE16 (ptr%, word%) DECLARE SUB StringFix (t$) DEFINT A-Z '$INCLUDE: 'QB.BI' TYPE ATA GenConfig AS INTEGER 'For a complete description of these LogCyl AS INTEGER 'fields, obtain a copy of a recent ATA spec. Reserved1 AS INTEGER LogHead AS INTEGER Vendor1 AS INTEGER Vendor2 AS INTEGER LogSect AS INTEGER Vendor3 AS INTEGER Vendor4 AS INTEGER Vendor5 AS INTEGER Serial AS STRING * 20 'serial number Vendor6 AS INTEGER Vendor7 AS INTEGER ReadBytes AS INTEGER Revision AS STRING * 8 'firmware revision Model AS STRING * 40 'model name/number RWMulti AS INTEGER Reserved2 AS INTEGER Capabilities AS INTEGER Reserved3 AS INTEGER PIOMode AS INTEGER DMAMode AS INTEGER FieldsValid AS INTEGER CurCyl AS INTEGER CurHead AS INTEGER CurSect AS INTEGER CurTotSect AS LONG CurRWMulti AS INTEGER LBASectors AS LONG SingleDMA AS INTEGER MultiDMA AS INTEGER AdvPIOMode AS INTEGER MinDMAns AS INTEGER DMAns AS INTEGER MinPIOns AS INTEGER MinPIOCHRDYns AS INTEGER Reserved4 AS STRING * 38 UDMA AS INTEGER Reserved5 AS STRING * 334 END TYPE TYPE DataBuffer b AS STRING * 512 END TYPE DIM Buffer AS DataBuffer DIM ATADevice AS ATA DATA &H1F0, &H170, &H168, &H1E8 Disk = -1 FOR i = 1 TO 4 READ port FOR dev = 0 TO 1 Buffer.b = "" PIODataIn port, dev, Buffer, &HEC, result, 0 LSET ATADevice = Buffer IF result THEN Disk = Disk + 1 StringFix ATADevice.Model StringFix ATADevice.Revision StringFix ATADevice.Serial CLS COLOR 15 PRINT "Device"; Disk; "("; HEX$(port); ","; HEX$(dev); ")" PRINT "ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ" COLOR 7 PRINT "Type : "; IF result > 0 THEN a$ = "ATA" ELSE a$ = "ATAPI" PRINT a$ PRINT "Model : "; LTRIM$(RTRIM$(ATADevice.Model)) PRINT "Revision : "; LTRIM$(RTRIM$(ATADevice.Revision)) PRINT "Serial : "; LTRIM$(RTRIM$(ATADevice.Serial)) sz& = (ATADevice.LogCyl + 1&) * (ATADevice.LogHead + 1&) sz& = sz& * ATADevice.LogSect \ 2048& PRINT "Size :"; sz&; "MB" COLOR 15 PRINT "ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ" COLOR 7 PRINT PRINT "Press ESC to exit or any other key to continue..."; k$ = "" DO k$ = INKEY$ LOOP WHILE LEN(k$) = 0 LOCATE , 1: PRINT SPACE$(80); IF k$ = CHR$(27) THEN END END IF NEXT NEXT IF Disk = -1 THEN PRINT "No devices detected." END '**************************************************************************** '*** INP16 is like INP but reads a word instead of a byte *** '**************************************************************************** FUNCTION INP16 (port) d = 0: dptr = VARPTR(d) ASM$ = CHR$(&HBA) + MKI$(port) 'mov dx,port ASM$ = ASM$ + CHR$(&HED) 'in ax,dx ASM$ = ASM$ + CHR$(&HBB) + MKI$(dptr) 'mov bx,dptr ASM$ = ASM$ + CHR$(&H89) + CHR$(7) 'mov [bx],ax ASM$ = ASM$ + CHR$(&HCB) 'retf CALL ABSOLUTE(SADD(ASM$)) INP16 = d END FUNCTION '*************************************************************************** '*** PIODataIn is a limited, slightly out of spec implementation of the *** '*** ATA PIO Data In protocol. In this case, "out of spec" means "works *** '*** even with old or out of spec hard drives." '*** --------------------------------------------- '*** port is the controller's base address in hex. '*** device is the device number: 0 for master, 1 for slave. '*** Buffer is a TYPEd 512 byte string. '*** opcode is the ATA command number: ECh for IDENTIFY DEVICE '*** A1h for ATAPI IDENTIFY DEVICE '*** result is the number of bytes read (negative value indicates ATAPI) '*** nest is a counter to keep track of recursion--start with 0. '*** --------------------------------------------- '*************************************************************************** SUB PIODataIn (port, device, Buffer AS DataBuffer, opcode, result, nest) nest = nest + 1 dev = device * 16 'device #: 0=0, 1=16 ptr = VARPTR(Buffer) result = 0 tout& = 25000 'timeout loop count GOSUB WaitForNotBusy OUT port + 6, dev 'set device # GOSUB WaitForNotBusy IF INP(port + 7) = 0 THEN EXIT SUB 'device not present t& = 0 DO WHILE ((INP(port + 7) AND 64) = 0) AND (t& < tout& * 2) 'wait for DRDY=1 t& = t& + 1 LOOP OUT port + 7, opcode 'issue command FOR r = 0 TO 510 STEP 2 GOSUB WaitForNotBusy IF t& >= tout& THEN EXIT FOR 'timed out a = INP(port + 7) 'get status IF (a AND 1) THEN 'if ERR=1 IF (INP(port + 1) AND 4) AND nest < 2 THEN 'if ABRT=1 PIODataIn port, device, Buffer, &HA1, result, nest 'ATAPI Identify Dev. result = result * -1 END IF EXIT SUB END IF a = INP16(port) 'read word POKE16 ptr + r, a 'copy to buffer result = r + 2 NEXT EXIT SUB WaitForNotBusy: t& = 0 DO WHILE (INP(port + 7) AND 128) AND (t& < tout&) 'wait for BSY=0 t& = t& + 1 LOOP RETURN END SUB '**************************************************************************** '*** POKE16 is like POKE but writes a word instead of a byte *** '**************************************************************************** SUB POKE16 (ptr, word) ASM$ = CHR$(&HBA) + MKI$(word) 'mov dx,word ASM$ = ASM$ + CHR$(&HBB) + MKI$(ptr) 'mov bx,ptr ASM$ = ASM$ + CHR$(&H89) + CHR$(&H17) 'mov [bx],dx ASM$ = ASM$ + CHR$(&HCB) 'retf CALL ABSOLUTE(SADD(ASM$)) END SUB '**************************************************************************** '*** ATA spec. requires each pair of bytes in a string to be inverted. *** '*** StringFix returns them to readable form. *** '**************************************************************************** SUB StringFix (t$) DO WHILE a < LEN(t$) a = a + 2 a$ = a$ + MID$(t$, a, 1) a$ = a$ + MID$(t$, a - 1, 1) LOOP FOR i = 1 TO LEN(a$) IF MID$(a$, i, 1) = CHR$(0) THEN MID$(a$, i) = " " NEXT t$ = a$ END SUB