Floppy-Disk-Treiber - Überarbeitung und Ausbau
-
Der zur Zeit in PrettyOS vorhandene Floppy-Treiber soll optimiert und bezüglich Laden/Speichern von Files weiter ausgebaut werden, damit die eigentliche "Datenbearbeitung" beginnen kann.
Hierzu sammele ich hier einige alternative Vorbilder mit umfangreichem Floppy-Treibermodul:
http://forum.osdev.org/viewtopic.php?f=1&t=21036
http://www.dynacube.net/source/index.html (files: floppy.h/c und fat12.h/c) insgesamt sehr interessantes indisches OS-Vorbild!
Wer mich hierbei unterstützen möchte, soll sich bitte melden.
-
Die Erstellung der Serial Number erfolgte unter DOS beim Formatieren wie folgt:
http://www.nondot.org/sabre/os/files/Disk/FloppySerialNumbers.txtThe first part of the serial number is equal to the sum of the time
(seconds and hundredths of a second) and the date (month and day); The
second part of the serial number is equal to the sum of the time (hours
and minutes) and date (year), where all numbers are in hex. For
example, if the diskette is formatted at 8:32:43.65 on 7/21/1991, the
first part of the serial number will be 2B41+0715, or 3256; the second
part of the serial number will be 0820+07C7, or 0FE7.
-
Das offizielle Datenblatt von Intel zum 82077AA (kompatibel zu 8272A disk controller) findet man hier:
http://www.nondot.org/sabre/os/files/Disk/82077AA_FloppyControllerDatasheet.pdfInteressant hieraus der Abschnitt "8.0 PROGRAMMING GUIDELINES" mit den Flussdiagrammen, z.B.:
Figure 8-5 Read/Write Operation: 500 ms Warteschleife für "Motor on" (zum Hochdrehen)
Figure 8-6 Formatting: 500 ms Warteschleife für "Motor on" (zum Hochdrehen)Anmerkung: Ich werde die Wartezeiten entsprechend einstellen.
-
> Anmerkung: Ich werde die Wartezeiten entsprechend einstellen.
Machst du eine aktive Schleife oder eine Art Timer? Btw sind 500 ms echt ganz schön lang..
-
Btw sind 500 ms echt ganz schön lang..
Das ist eine "tote" Warteschleife (im Modul timer.c) mit sleepMilliseconds(500). Vorher hatten wir 20 ms, dann haben wir auf 1000 ms erhöht, was merkwürdigerweise keine Veränderung im Verhalten brachte.
Bei osdev.org steht 300 ms für 3,5 Zoll- und 500 ms für 5,25 Zoll-Laufwerke.
Für den downturn soll man sogar 1000 ms warten, habe ich gelesen.
-
Der Floppy-Treiber mit fdir läuft in qemu und VirtualBox, aber nicht in Bochs und VirtualPC. Auf Hardware gibt es ebenfalls Probleme.
-
Problem deutlich entschärft, lag im Timer. Ab Version 31 geht fdir nun mit Bochs und realer Hardware (2 von 3 bei mir).
VirtualPC macht noch Mucken. Das Problem wurde bei www.brokenthorn beschrieben, die Lösung habe ich jedoch nicht verstanden bisher. Vielleicht kann mir da jemand auf die Sprünge helfen?
-
Es gibt bei mancher Hardware ein problem beim zweiten 'fdir'. Da fängt sich der Rechner irgendwo. Durch Debuggen Fehlerort gefunden:
ich habe den Fehlerort gefunden, der einige Hardware beim zweiten fdir ausbremst:
flpydsk_send_command (cyl); putch('c'); flpydsk_wait_irq(); putch('d');
das 'd' kommt nicht
also hängt er beim zweiten mal bei der Funktion: flpydsk_wait_irq();Diese Funktion hat fricky schon als verdächtig erkannt:
{ while ( _FloppyDiskIRQ == false) // wait for irq to fire ; /// <--- freeze!!! /// _FloppyDiskIRQ = false; }
Nun brauchen wir nur noch eine Lösung, aber das findet sich.
Hier steht was: http://wiki.osdev.org/Floppy_Disk_Controller#When_waiting_for_IRQ6.2C_I_get_stuck.21
-
Revision 32 funktioniert bereits sehr gut, nur noch geringe Probleme. Nun kann man an das Thema Laden/Speichern/Löschen/Formatieren(FAT12) auf Diskette gehen. Dies hat aber nichts mehr mit dem eigentlichen Gerätetreiber zu schaffen, sondern ist ein Thema des Filesystems, hier FAT12.
EDIT: das wird nun in fat12.h/c bearbeitet.
-
Vorschlag aus dem IRC:
<XanClic>Ach, btw, ehenkes: Habt ihr mal über sowas nachgedacht? http://lowlevel.brainsware.org/forum/index.php?topic=2389.msg27064#msg27064
<ehenkes>das mit dem zurückschalten auf RM, von floppy laden/schreiben, dann zurück nach PM könnte eine Menge sparen.
<XanClic>VM8086 wäre hübscher, aber so ist es gemeint, jo
<ehenkes>ich kenne mich damit nur noch nicht aus
<Tobiking>Das klingt aber irgendwie unschön
<XanClic>Aber solange es keinen USB-Treiber gibt, ist das allemal besser
<XanClic>...Als gar kein Treiber
<Tobiking>Achso für USB wäre es natürlich eine schnelle lösung
<ehenkes>klappt das auch mit Floppy an USB?
<XanClic>Jo, klappt mit allem, was das BIOS unterstützt
<Tobiking>Klar das ist wie im Bootloader
<ehenkes>Interessante Idee
<ehenkes>aber Sektoren Lesen/Schreiben können wir ja schon
<ehenkes>allerdings nicht über USB
<XanClic>Jo, wie es im Thread steht - als Zweittreiber eben
<ehenkes>das ist quasi VM86?
<XanClic>Jo
-
Leider existiert beim Sektoren schreiben noch ein Problem mit echter Hardware:
http://www.c-plusplus.net/forum/viewtopic-var-p-is-1844268.html#1844268Vielleicht kennt sich da jemand aus und kann helfen?
Mit folgender Prozedur versuche ich das Problem auf echter Hardware (qemu mit echter Floppy funktioniert!) einzugrenzen:
int32_t flpydsk_write_sector(int32_t sectorLBA) { if (_CurrentDrive >= 4) return -1; // convert LBA sector to CHS int32_t head=0, track=0, sector=1; flpydsk_lba_to_chs(sectorLBA, &head, &track, §or); // turn motor on and seek to track printformat("before turning motor on\n"); flpydsk_control_motor(true); printformat("before seeking track\n"); if(flpydsk_seek (track, head)) return -2; printformat("before writing sector and turning motor off\n"); // write sector and turn motor off flpydsk_transfer_sector(head, track, sector, 1); flpydsk_control_motor(false); return 0; }
Ergebnis:
before turning motor on
before seeking track
error write_sector. left: 1
before turning motor on
before seeking track
error write_sector. left: 0
timeout
E_Disk ...Damit ist klar, dass das Problem hier liegt:
if(flpydsk_seek (track, head)) return -2;
Nächster Schritt: Analyse von flpydsk_seek (track, head):
// seek to given track/cylinder int32_t flpydsk_seek( uint32_t cyl, uint32_t head ) { uint32_t st0, cyl0; if (_CurrentDrive >= 4) return -1; int32_t i; for(i=0; i<10; ++i) { // send the command flpydsk_send_command (FDC_CMD_SEEK); flpydsk_send_command ( (head) << 2 | _CurrentDrive); flpydsk_send_command (cyl); printformat("i=%d ", i); flpydsk_wait_irq(); flpydsk_check_int(&st0,&cyl0); if ( cyl0 == cyl) // found the cylinder? { printformat("cylinder found\n"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return 0; } } printformat("cylinder not found\n"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return -1; }
Resultat:
i=0 cylinder found
cyl0: 0 cyl: 0
i=0 ... i=9 cylinder not found
cyl0: 128 cyl: 0
error write_sector ...Da stimmt etwas nicht mit dem Verlassen der for-Schleife.
Folgende Version mit richtigem 'break' führt zumindest zum Formatiervorgang auf realer Hardware:// seek to given track/cylinder // TODO: does not work perfectly with write_sector on real hardware int32_t flpydsk_seek( uint32_t cyl, uint32_t head ) { int32_t retVal; uint32_t st0, cyl0; if (_CurrentDrive >= 4) return -1; int32_t i; for(i=0; i<5; ++i) { // send the command flpydsk_send_command (FDC_CMD_SEEK); flpydsk_send_command ( (head) << 2 | _CurrentDrive); flpydsk_send_command (cyl); printformat("i=%d ", i); flpydsk_wait_irq(); flpydsk_check_int(&st0,&cyl0); if(cyl0 == cyl) // found the cylinder? { retVal = 0; break; } } if(retVal==0) { printformat("cylinder found\n"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return 0; } else { printformat("cylinder not found\n"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return -1; } }
Noch nicht perfekt, aber immerhin ein Einstieg! ==> Rev. 62
-
Rev. 63: Problem besteht noch:
// seek to given track/cylinder // TODO: does not work perfectly with write_sector on real hardware int32_t flpydsk_seek( uint32_t cyl, uint32_t head ) { int32_t retVal; uint32_t st0, cyl0, i; if (_CurrentDrive >= 4) { return -2; } for(i=0; i<10; ++i) { // send the command flpydsk_send_command (FDC_CMD_SEEK); flpydsk_send_command ( (head) << 2 | _CurrentDrive); flpydsk_send_command (cyl); printformat("i=%d ", i); flpydsk_wait_irq(); flpydsk_check_int(&st0,&cyl0); if(cyl0 == cyl) // found the cylinder? { retVal = 0; break; } else { retVal = -1; } } if(retVal==0) { printformat("cyl. found\t"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return 0; } else { printformat("cyl. not found\t"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return -1; } }
-
Nach dem Einbau eines "calibrate" (setzt auf cyl. 0) klappt der seek-Vorgang:
// seek to given track/cylinder int32_t flpydsk_seek( uint32_t cyl, uint32_t head ) { int32_t retVal; uint32_t st0, cyl0, i; if (_CurrentDrive >= 4) { return -2; } /// TEST flpydsk_calibrate(_CurrentDrive); // calibrate the disk ==> cyl. 0 flpydsk_control_motor(true); /// TEST for(i=0; i<10; ++i) { // send the command flpydsk_send_command (FDC_CMD_SEEK); flpydsk_send_command ( (head) << 2 | _CurrentDrive); flpydsk_send_command (cyl); printformat("i=%d ", i); flpydsk_wait_irq(); flpydsk_check_int(&st0,&cyl0); if(cyl0 == cyl) // found the cylinder? { retVal = 0; break; } else { retVal = -1; } } if(retVal==0) { printformat("cyl. found\t"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return 0; } else { printformat("cyl. not found\t"); printformat("cyl0: %d cyl: %d \n",cyl0,cyl); return -1; } }
Optimierungspotential:
- Motor on/off
- Sektoren vs. Track schreiben
- Root Dir sieht merkwürdig aus bei fdir (im Windows Explorer oder nach dir a: in der Konsole kommen keine seltsamen Zeichen). Auf einem PC führt das Format zu einem für Windows unlesbaren Format, auf einem anderen ist alles ok.
- Quickformat vs. richtiges wipe out im Datenbereich?Bei qemu ist noch alles ok.
-
Motor on/off ab Rev. 66 sparsamer eingesetzt. Nun muss der Aufbau mit dem Schreiben in einzelne Sektoren ersetzt werden druch ein trackweises Schreiben, das ja bereits läuft, aber noch nicht genutzt wird.
-
Trackwises Schreiben geht inzwischen (Rev. 68), ist natürlich viel schneller.
-
Ich habe diesen Kommentar abgesetzt:
http://wiki.osdev.org/ISA_DMA#Floppy_Disk_DMA_ProgrammingNachdem wir (MrX, Cuervo, Erhard Henkes) doch signifikante Probleme mit dem Autoinit-Bit der DMA hatten, verzichten wir komplett darauf und initialisieren direkt vor der Vorbereitung auf Lesen/Schreiben:
// initialize DMA void flpydsk_initialize_dma() { outportb(0x0a, 0x06); // mask dma channel 2 outportb(0xd8, 0xFF); // reset master flip-flop outportb(0x04, 0x00); // DMA buffer address 0x1000 outportb(0x04, 0x10); outportb(0xd8, 0xFF); // reset master flip-flop outportb(0x05, 0xFF); // count to 0x23FF (number of bytes in a 3.5" floppy disk track: 18*512) outportb(0x05, 0x23); outportb(0x81, 0x00); // external page register = 0 outportb(0x0a, 0x02); // unmask dma channel 2 } /// autoinit ( 2^4 = 16 = 0x10 ) creates problems with MS Virtual PC and on real hardware! /// hence, it is not used here, but reinitialization is used before read/write // prepare the DMA for read transfer void flpydsk_dma_read() { outportb(0x0a, 0x06); // mask dma channel 2 outportb(0x0b, 0x46); // single transfer, address increment, read, channel 2 // without autoinit outportb(0x0a, 0x02); // unmask dma channel 2 } // prepare the DMA for write transfer void flpydsk_dma_write() { outportb(0x0a, 0x06); // mask dma channel 2 outportb(0x0b, 0x4A); // single transfer, address increment, write, channel 2 // without autoinit outportb(0x0a, 0x02); // unmask dma channel 2 }
int32_t flpydsk_transfer_sector(uint8_t head, uint8_t track, uint8_t sector, uint8_t operation) { uint32_t st0, cyl; flpydsk_initialize_dma(); if(operation == 0) // read a sector { flpydsk_dma_read(); flpydsk_send_command( FDC_CMD_READ_SECT | FDC_CMD_EXT_MULTITRACK | FDC_CMD_EXT_SKIP | FDC_CMD_EXT_DENSITY); } if(operation == 1) // write a sector { flpydsk_dma_write(); flpydsk_send_command( FDC_CMD_WRITE_SECT | FDC_CMD_EXT_MULTITRACK | FDC_CMD_EXT_DENSITY ); } flpydsk_send_command( head << 2 | _CurrentDrive ); flpydsk_send_command( track); flpydsk_send_command( head); flpydsk_send_command( sector); flpydsk_send_command( FLPYDSK_SECTOR_DTL_512 ); flpydsk_send_command( FLPY_SECTORS_PER_TRACK ); flpydsk_send_command( FLPYDSK_GAP3_LENGTH_3_5 ); flpydsk_send_command( 0xFF ); flpydsk_wait_irq(); int32_t j,retVal; for(j=0; j<7; ++j) { int32_t val = flpydsk_read_data(); // read status info: ST0 ST1 ST2 C H S Size(2: 512 Byte) if((j==6) && (val==2)) // value 2 means 512 Byte { retVal = 0; } else { retVal = -1; } } flpydsk_check_int(&st0,&cyl); // inform FDC that we handled interrupt return retVal; }
-
www.brokenthorn.com hatte im Kapitel 20 (Floppy Disk Treiber) noch das Autoinit-Bit gesetzt, ist nun im Kapitel 21 (DMA Treiber) offensichtlich ebenfalls auf diese deutlich zuverlässigere Technik eingeschwenkt. Dort war die Motivation die Simulation mittels MS Virtual PC.