Floppy-Disk-Treiber - Überarbeitung und Ausbau


  • Mod

    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


  • Mod

    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.


  • Mod

    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


  • Mod

    Leider existiert beim Sektoren schreiben noch ein Problem mit echter Hardware:
    http://www.c-plusplus.net/forum/viewtopic-var-p-is-1844268.html#1844268

    Vielleicht 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, &sector);
    	// 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


  • Mod

    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;
        }
    }
    

  • Mod

    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.


  • Mod

    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.


  • Mod

    Trackwises Schreiben geht inzwischen (Rev. 68), ist natürlich viel schneller.


  • Mod

    Ich habe diesen Kommentar abgesetzt:
    http://wiki.osdev.org/ISA_DMA#Floppy_Disk_DMA_Programming

    Nachdem 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;
    }
    

  • Mod

    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.


Anmelden zum Antworten