auf Festplattenkopf zugreifen
-
-
habt ihr mein zeug bekommen??? hab noch gar keine antwort/resonanz bekommen... *g*
cu todo
-
Mal ne DAU-Frage:
Was ist wenn das Programm ein Fehler hat und ihr den Kopf zu weit nach unten bewegt? Dann ist die Platte doch kaputt, oder irre ich mich da?
-
Man kann den Kopf nicht selbst steuern, deshalb kann das auch nicht passieren.
-
wäre dankbar wenn du es mir auch schicken könntest.
doyouknowit@gmx.demeines wissens nach optimieren einige HDD-Controller (die können wirklich sagen wo der Kopf hin soll) die Befehle in der "Warteschlange" hinsichtlich der Anordnung.
Bei derartigen Geräten müsste diese Funktion zunächst deaktiviert werden, um dann u.U. indirekt über "normale" Befehle den Kopf zu dirigieren. Zu beachten wäre des weiteren auf jeden Fall noch der Cache, und ... (sind nur so ein paar Dinge die mir spontan einfallen).mfg
-bg-
-
Also bevor du dich an die Köpfe machst musst du die register kennen also die standart register der Festplatte sind:
DatenRegister=( 1f0h <-- 16bit breit {(R/W
FehlerRegister=( 1f1h <-- 8bit breit {(R
Vorkompensation=( 1f1h <-- 8bit breit {(W
FeatureRegisteSektorenzahl=( 1f2h <-- 8bit breit {(R/W
Sektornummer=( 1f3h <-- 8bit breit {(R/W
ZylinderLSB=( 1f4h <-- 8bit breit {(R/W
ZylinderMSB=( 1f5h <-- 8bit breit {(R/W
Laufwerk/Kopf=( 1f6h <-- 8bit breit{(R/W
StatusRegister1f7h <-- 8bit breit {(R
BefehlRegister1f7h <-- 8bit breit {(W
StatusRegisterAlternativ=( 3f6h <-- 8bit breit {(R
Digitales AusgabeRegister3f6h <-- 8bit breit {(W
Laufwerkadresse =(3f7h <-- 8bit breit {( RAllen Festplatten haben diesen register sonst wären...
MFG
OS-Developer
Embended Developer
-
@todo: Bei der Nachfrage könntest du es ja doch mal hier reinschreiben oder mir bitte auch schicken!
Danke!
[url=mailto:drakos@bloodtide.de">drakos@bloodtide.de[/url]
-
Wie wärs wenn du es gleich hier postest?Dann hätten alle was davon,vll auch in die FAQ.
-
ok ok, ich geb mich geschlagen
viel spaß beim lesen??Nach dem ATA-Standard ist die kleinste adressierbare Einheit auf einer Festplatte 1 Sektor.
Das entspricht 512 Byte. Eine Festplatte ist auch noch unterteilt in Spuren und Köpfe.
In jeder Spur befindet sich eine bestimmte Anzahl von Sektoren. Die Kopfnummer gibt an, auf
welcher Platte gelesen werden soll, da eig. alle Festplatten aus mehreren (magn.) Scheiben
besteht, die jeweils oben und unten lesbar/beschreibbar sind. Wenn also eine Festplatte 8
solcher Scheiben besitzt, hat sie 16 Köpfe. Auf jeder Seite gibt es eine bestimmte Anzahl von
puren (in denen es wiederum wie schon gesagt eine bestimmte Anzahl von Sektoren gibt)
Die Gesammtzahl der Sektoren auf einer Festplatte könnte man also wie folgt ausrechnen:n_secs = n_secs_per_track * n_tracks_per_head * n_heads_per_drive
Um von Festplatten zu lesen unterscheidet man zwei Adressierungsarten: CHS und LBA
CHS steht für Cylinder/Head/Sector (Cylinder ist ein anderes Wort für Spur, der Rest dürfte
sich von selbst erklären) Bei dieser Adressierungsart gibt man beim Lesen und Schreiben
Spur, Kopf und Sektor an, was gewöhnlich über das BIOS und dessen Interrupt 0x13 erfolgt.
Hier nun ein kleines Beispielprogramm in Assembler zum Auslesen des Bootsektors einer
Festplatte:.Model Small
.Stack 100h
.Data
Buffer DB 512 Dup(0).Code
start:
; beim Lesen/Schreiben muss man natürlich die Adresse eines Puffers, wo die gelesenen
; Daten hinkopiert werden sollen, angeben. In diesem Fall haben wir einen Puffer im
; Datensegment, dessen Adresse in ES:BX angegeben wird.
Mov Ax,@Data
Mov Ds,Ax
Mov Es,Ax
Mov Bx,Offset Buffer; Funktion 02h = Sektor(en) lesen
Mov Ah,02h
; In AL muss die Anzahl der zu lesenden Sektoren stehen (hier 1)
Mov Al,1; In CH muss die Spurnummer stehen. Da 8 Bit in den meisten Fällen nicht ausreichen (das wären
; nur 256 Spuren), stehen in den Bits 6 und 7 von CL noch Bit 8 und 9 der Spurnummer
Mov Cx,0 ; CX = Spurnummer (Bootsektor in Spur 0)
ShR Cx,8 ; jetzt stehen in CL Bit 8 und 9 der Spurnummer
ShL Cl,6 ; Bit 8 und 9 d. Spurnummer müssen aber in Bit 6 und 7 von CL stehen
Mov Ch,0 ; CH = unt. 8 Bits der Spurnummer; Bits 0...5 mit der Sektornummer "füllen" (OR muss benutzt werden, da sonst der Teil der
; Spurnummer in Bits 6 und 7 gelöscht/verändert werden würde)
; (Bootsektor befindet sich in Sektor 1 (in Spur 0 und Seite 1)
Or Cl,1Mov Dh,1 ; Kopfnummer -> DH (Bootsektor ist immer auf Seite 1)
Mov Dl,80h ; Laufwerksnummer -> DL 1. Festplatte = 80h, 2. = 81h usw.
; (bei Diskettenlaufwerken geht es bei 0 los)
Int 13h ; und nun los! BIOS-Int 13h ist für die Laufwerksverwaltung da.; jetzt folgt Code zum Ausgeben des Puffers - nicht weiter kommentiert
Mov Si,Offset Buffer
Mov Ah,0Eh
Mov Bx,7
Mov Cx,512output_loop:
LodSB
Int 10h
Dec Cx
JNZ output_loopMov Ax,4C00h
Int 21h
End start
EndDiese Routine kann man ohne weiteres auch zu einer allgemeinen Read-Sector-Routine
umprogrammieren, indem man einfach die festen Werte durch Variablen (die über den Stack
übergeben werden) ersetzt. (den Puffer-Ausgabe-Code muss man dann natürlich auch entfernenWenn man sich aber die CHS-Adressierung genauer anschaut, wird man bald feststellen, dass es ein
Limit gibt, und zwar die Anzahl der Spuren/Zylinder. Da (nur) 10 Bits für die Spurnummer
zur Verfügung stehen, kann man max. 1024 Spuren lesen. Die max. Größe kann dann seitens
des BIOS nur 516 MB betragen! Und wer hat schon noch solche kleinen Platten? (außer mir
Einige BIOS-Hersteller haben sich dann so Sachen ausgedacht, wie z.B. das noch 4 weitere Bits
in Bits 4...7 von Dh stehen usw.
Da das aber absolut nicht einheitlich ist, sollte man diese Methode nicht verwenden.Aber es gibt ja noch eine andere Adressierungsmethode, mit der man diese Hürde ohne weiteres
überwinden kann: LBA. LBA steht für Logical Block Adressing und ist Bestandteil des
ATA-2-Standards, welchen alle EIDE (Enhanced IDE) beherrschen. Bei LBA wird eine absolute
(bei Null beginnende) Sektornummer (64 Bit) angegeben, die wie folgt ausgerechnet wird:LBA = (((cylinder * heads_per_track) + head) * sectors_per_track) + sector - 1
Man kann natürlich auch Spur/Zylinder, Kopf und Sektor aus einem LBA-Wert ausrechnen:
sector = (LBA mod sectors_per_track) + 1
head = (LBA / sectors_per_track) mod heads_per_cylinder
cylinder = (LBA / sectors_per_track) / heads_per_cylinder"mod" bedeutet Modulo und ist der Rest einer Division (in C/C++ der %-Operator)
LBA ist nicht automatisch auf allen PCs/Festplatten verfügbar. Man kann aber davon ausgehen, dass
alle PCs/Festplatten nach 1993 LBA beherrschen. Deshalb sollte man, bevor man LBA benutzt,
erst überprüfen, ob LBA verfügbar ist. Das geht über die unten erläuterte Funktion.Nun möchte ich noch einmal den BIOS-Interrupt 13h ansprechen, über den ja alle
Laufwerk-Operationen ablaufen.CHS-Adressierung
------------------Sektor(en) lesen
AH = 02h (Funktionsnummer).
AL = Anzahl der zu lesenden Sektoren
CH = Spurnummer Bits 0-7
CL = Spurnummer Bits 8-9 (Bits 6-7 von CL)
CL = Sektornummer (Bits 0-5 von CL, 1-63, Sektorennummern beginnen bei 1)
DL = Laufwerksnummer (80h-FFh für Festplatten)
DH = Kopfnummer
ES:BX = Segment:Offset des PuffersSektor(en) schreiben
AH = 03h (Funktionsnummer).
Der Rest ist genauso wie beim Lesen von Sektoren. (Der Puffer enthält die zu schreibenden
DatenLBA-Adressierung
-----------------Install-Check der Int13-Erweiterung (LBA)
AH = 41h
DL = drive number (same as with READ/WRITE SECTORS).
BX = 55AAhRückgabewerte:
BX = AA55h (Bytes getauscht) und CF=0, wenn LBA auf dem gewünschten Laufwerk verfügbar ist.
In CX werden auch noch andere Informationen zurückgegeben. Für genauere Details würde ich einen
Blick in Ralph Browns Interrupt-Liste empfehlen.Erweitertes Sektor(en)-lesen
AH = 42h
DL = Laufwerksnummer
DS:SI = Zeiger auf Disk-Request-Packet-StructureAufbau der Disk-Request-Packet-Structure
BYTE Packet-Länge (10h)
BYTE Reserviert (00h)
WORD Anzahl zu lesender Sektoren
WORD Puffer-Offset
WORD Puffer-Segment
QWORD 64-Bit Logical Block AddressErweitertes Sektor(en)-schreiben
AH = 43h
DL = Laufwerksnummer
DS:SI = Zeiger auf Disk-Request-Packet-Structure (ist genauso aufgebaut wie oben)Ein Beispiel über LBA-Programmierung kann ich bei Bedarf auch noch mailen...
cu todo
-
ich habs, was plymtacky wollte! die richtung in die es geht, hatte <os-developer> ja schon richtig vorgegeben. die adressen (also kopf usw.) muss man erst in die entsprechenden register schreiben. dann schickt man an das befehls-register den befehl 0x70 (SEEK) und er positioniert alles so, wie es in den adressmedienregistern steht.
ich hoffe, ihr seid mir nicht böse, wenn ich hier im ASM-Forum mit C-Code komme
Bsp in C:
// all hd-regs #define HD_DATA_REG 0x1F0 #define HD_ERROR_REG 0x1F1 /* read */ #define HD_FEATURE_REG 0x1F1 /* write */ #define HD_SEC_COUNT_REG 0x1F2 #define HD_SEC_NUM_REG 0x1F3 #define HD_CYL_NUM_LOW_REG 0x1F4 #define HD_CYL_NUM_HI_REG 0x1F5 #define HD_DRIVE_REG 0x1F6 #define HD_STATE_REG 0x1F7 /* read */ #define HD_COM_REG 0x1F7 /* write */ // flags in the state-reg #define HD_ERROR 1 #define HD_INDEX 2 #define HD_CORRECTED_DATA 4 #define HD_DATA_REQUEST 8 #define HD_DRIVE_SEEK_COMPLETE 16 #define HD_DRIVE_FAULT 32 #define HD_DRIVE_READY 64 #define HD_BUSY 128 // commands #define HD_READ_SEC_RETRY 0x20 #define HD_READ_SEC 0x21 #define HD_WRITE_SEC_RETRY 0x30 #define HD_WRITE_SEC 0x31 #define HD_SEEK 0x70 #define HD_READ_MULTIPLE 0xC4 #define HD_WRITE_MULTIPLE 0xC5 #define HD_READ_DMA_RETRY 0xC8 #define HD_READ_DMA 0xC9 #define HD_WRITE_DMA_RETRY 0xCA #define HD_WRITE_DMA 0xCB #define HD_STANDBY 0xE2 #define HD_SLEEP 0xE6 #define HD_IDENTIFY_DEVICE 0xEC void main (void) { outb (HD_SEC_COUNT_REG, 1); outb (HD_SEC_NUM_REG, 1); outb (HD_CYL_NUM_LOW_REG, 0); outb (HD_CYL_NUM_HI_REG, 0); outb (HD_DRIVE_REG, 128|32|(0<<4)|1); outb (HD_COM_REG, HD_SEEK); }
(outb ist eine methode zum "outen" eines bytes an den angegebenen port)
wenn man einen sektor lesen will, kann man das so machen:
//... hier her noch die defs von oben void insw (int port, void far * buffer, int words) { asm Push Di asm ClD asm Mov Dx,port asm Mov Cx,words asm Mov Di,Word Ptr [buffer] asm Mov Ax,Word Ptr [buffer+2] asm Mov Es,Ax insw_loop: asm In Ax,Dx asm StoSW asm Loop insw_loop asm Pop Di } void main (void) { int i; int retries = 10000; char buffer[512]; outb (HD_SEC_COUNT_REG, 1); outb (HD_SEC_NUM_REG, 1); outb (HD_CYL_NUM_LOW_REG, 0); outb (HD_CYL_NUM_HI_REG, 0); outb (HD_DRIVE_REG, 128|32|(0<<4)|1); outb (HD_COM_REG, HD_READ_SEC); do { i = (unsigned) inb(HD_STATE_REG); if (i & HD_BUSY) continue; if (i & HD_DATA_REQUEST) goto ok_to_read; } while (--retries > 0); // output error-msg return; ok_to_read: insw (HD_DATA_REG, buffer, 256); for (i = 0; i < 512; ++i) printf ("%c", *((char*)buffer+i)); }
(liest den boot-sector der ersten festplatte)
viel spaß damit
cu todo