/****************************************************************************** * Module: Floppy.c * * Revision: 1.00 * * Date: 6/22/2000 * * Author: Goran Devic * ******************************************************************************* Module Description: This module contains the code for virtual floppy device. ******************************************************************************* * Changes: * * DATE DESCRIPTION OF CHANGES AUTHOR * * -------- --------------------------------------------------- ----------- * * 6/22/2000 Original Goran Devic * * -------- --------------------------------------------------- ----------- * ******************************************************************************* * Include Files * ******************************************************************************/ #include <strmini.h> // Include driver level C functions #include <ntddk.h> // Include ddk function header file #include "Vm.h" // Include root header file /****************************************************************************** * Local Defines, Variables and Macros * ******************************************************************************/ #define FLOPPY_CONTROLLER_BASE 0x3F0 typedef struct { BYTE DOR; BYTE Status; // Main Status Register (3F4/374) BYTE DIR; // Digital Input Register (3F7/377) } TController; typedef struct { BYTE Command[10]; // Command data array BYTE Command_Index; // Current data to write BYTE Command_Size; // Total expected command size BYTE Result[10]; // Result data array BYTE Result_Index; // Current data to read BYTE Result_Size; // Total size of the result DWORD track; // Current track DWORD head; // Current head DWORD sector; // Current sector DWORD Command0; // Used with transfer: pending command DWORD max_sector; // Used with transfer: final sector DWORD logical_sector; // Used with transfer: current logical } TFloppy; TController control; TFloppy floppy[2]; // Main status register #define FD_MS_MRQ 0x80 #define FD_MS_DIO 0x40 #define FD_MS_NDMA 0x20 #define FD_MS_BUSY 0x10 #define FD_MS_ACTD 0x08 #define FD_MS_ACTC 0x04 #define FD_MS_ACTB 0x02 #define FD_MS_ACTA 0x01 /****************************************************************************** * Functions * ******************************************************************************/ static int Floppy_Initialize(void); static DWORD Floppy_fnIN(DWORD port, DWORD io_len); static void Floppy_fnOUT(DWORD port, DWORD data, DWORD io_len); static void Command_Complete(TFloppy *p); static void Floppy_Command( TFloppy *p ); static void Floppy_Transfer(VMREQUEST *pVmReq, DWORD pArg, BYTE *pBuffer); static void Command_Reset(); /****************************************************************************** * void Floppy_Register(TVDD *pVdd) * ******************************************************************************* * Registration function for the virtual floppy device. * * Where: * pVdd is the address of the virtual device desctiptor to fill in ******************************************************************************/ void Floppy_Register(TVDD *pVdd) { pVdd->Initialize = Floppy_Initialize; pVdd->Name = "Floppy"; } /****************************************************************************** * int Floppy_Initialize() * ******************************************************************************* * Initializes virtual floppy controller device interface as a primary * controller. * * Returns: * 0 on success * nonzero on fail ******************************************************************************/ static int Floppy_Initialize() { TRACE(("Floppy_Initialize()")); memset(floppy, 0, sizeof(floppy)); // Register primary floppy controller RegisterIOPort(0x3F2, 0x3F7, Floppy_fnIN, Floppy_fnOUT ); // Initialize floppy controller control.DOR = 0x0C; // Enable DMA and controller control.DIR = 0x80; // Disk changed // Reset floppy device Command_Reset(); // Call soft reset function return( 0 ); } //***************************************************************************** // CALLBACK FUNCTIONS //***************************************************************************** static DWORD Floppy_fnIN(DWORD port, DWORD io_len) { DWORD value = 0xFF; TFloppy *p; TRACE(("Floppy_fnIN(DWORD port=%04X, DWORD io_len=%X)", port, io_len)); ASSERT(io_len==1); // Select the disk that receives attention p = &floppy[ control.DOR & 1 ]; switch( port - FLOPPY_CONTROLLER_BASE ) { case 2: // Digital Output Register value = control.DOR; break; case 4: // Main Status Register value = control.Status; break; case 5: // Data Register value = p->Result[p->Result_Index++]; if( p->Result_Index >= p->Result_Size ) { p->Result_Index = p->Result_Size = 0; control.Status = FD_MS_MRQ; } break; case 7: // Digital Input Register // This port is shared with the hard drive controller // Only bit 7 of DIR is used value = (value & 0x7F) | (control.DIR & 0x80); break; default: WARNING(("Invalid port read floppy+%d", port)); } return( value ); } static void Floppy_fnOUT(DWORD port, DWORD value, DWORD io_len) { TFloppy *p; TRACE(("Floppy_fnOUT(DWORD port=%04X, DWORD data=%X, DWORD io_len=%X)", port, value, io_len)); ASSERT(io_len==1); // Select the disk that receives attention p = &floppy[ control.DOR & 1 ]; switch( port - FLOPPY_CONTROLLER_BASE ) { case 2: // Digital Output Register // 7:4 MOTD:MOTA Motor on select // 3 DMA enable -> 1 // 2 ~RESET -> 1 // 1:0 DR1:DR0 Drive select control.DOR = (BYTE) value; ASSERT((control.DOR & 3)== 0); // I want to see when he's going to // access second floppy........ if( (control.DOR & 4)==0 ) // Execute soft reset if reset is held down Command_Reset(); break; case 4: // Write: Data Rate Select Register break; case 5: // Data Register p->Command[p->Command_Index++] = (BYTE) value; if( p->Command_Size==0 ) // Ready to accept a new command { switch( value ) { case 0x03: p->Command_Size = 3; break; // Specify case 0x04: p->Command_Size = 2; break; // Get Status case 0x07: p->Command_Size = 2; break; // Recalibrate case 0x0F: p->Command_Size = 3; break; // Seek case 0x4A: p->Command_Size = 2; break; // Read ID case 0xC5: p->Command_Size = 9; break; // Write normal data case 0xE6: p->Command_Size = 9; break; // Read normal data case 0x13: p->Command_Size = 3; break; // Configure (enhanced) case 0x0E: p->Command_Size = 3; break; // Configure (enhanced) default: ERROR(("Unsupported floppy command %02X", value)); } control.Status = FD_MS_MRQ | FD_MS_BUSY; } else if( p->Command_Index >= p->Command_Size ) { // This value completes the command Floppy_Command( p ); p->Command_Index = p->Command_Size = 0; } break; case 6: // Used with hdd controller // This port is shared with the hard drive controller break; case 7: // Configuration Control Register break; default: WARNING(("Invalid port write floppy+%d", port)); } } // This function should be called with a delay to simulate time it takes // for floppy to execute a command. However, it works even if it is not // called that way, but directly.. we'll see how long it will work...... static void Command_Complete(TFloppy *p) { switch( p->Command0 ) { case 0x07: // Delayed command: Recalibrate p->track = 0; case 0x0F: // Delayed command: Seek control.Status = FD_MS_MRQ; control.DIR = 0x00; VmSIMGenerateInterrupt(6); break; case 0x4A: // Delayed command: Read sector ID case 0xC5: // Delayed command: Write normal data case 0xE6: // Delayed command: Read normal data control.Status = FD_MS_MRQ | FD_MS_DIO; VmSIMGenerateInterrupt(6); break; case 0xFE: // Delayed command: Reset Command_Reset(); break; default: ERROR(("Invalid complition command %02X", p->Command0)); } } // This function parses a command and executes steps needed for it static void Floppy_Command( TFloppy *p ) { static VMREQUEST VmReq; DWORD track, head, sector; p->Result_Size = 0; p->Result_Index = 0; p->Command0 = p->Command[0]; switch( p->Command[0] ) { case 0x03: // Command: Specify case 0x13: // Command: Configure drive parameters control.Status = FD_MS_MRQ; break; case 0x04: // Command: Get Status p->Result[0] = 0x00; p->Result_Size = 1; control.Status = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY; break; case 0x07: // Command: Recalibrate control.Status = FD_MS_DIO | FD_MS_BUSY; Command_Complete( p ); break; case 0x08: // Command: Sense Interrupt Status p->Result[0] = 0x20 | (control.DOR & 3); p->Result[1] = (BYTE) p->track; p->Result_Size = 2; control.Status = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY; break; case 0x0F: // Command: Seek p->head = (p->Command[1] >> 2 ) & 1; p->track = p->Command[2]; control.Status = FD_MS_DIO | FD_MS_BUSY; Command_Complete( p ); break; case 0x4A: // Command: Read sector ID p->Result[0] = 0; p->Result[1] = 0; p->Result[2] = 0; p->Result[3] = (BYTE) p->track; p->Result[4] = (BYTE) p->head; p->Result[5] = (BYTE) p->sector; p->Result[6] = 2; control.Status = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY; Command_Complete( p ); break; case 0xE6: // Command: Read normal data case 0xC5: // Command: Write normal data p->Result_Size = 7; control.Status = FD_MS_MRQ | FD_MS_BUSY; track = p->Command[2]; // 0..79 head = p->Command[3] & 1; // 0..1 sector = p->Command[4]; // 1..36 p->max_sector = p->Command[6]; if( p->max_sector==0 || p->max_sector>MAX_FLOPPY_SECTORS ) p->max_sector = MAX_FLOPPY_SECTORS; p->logical_sector = (track * MAX_FLOPPY_HEADS * MAX_FLOPPY_SECTORS) + (head * MAX_FLOPPY_SECTORS) + (sector - 1); // Position the head over the starting coordinates p->track = track; p->head = head; p->sector = sector; // READ: // We request all sectors at once - app allocates a buffer // that is large enough, fills it in and sends it down to us so the // read completion routine can copy it to VM memory. // // WRITE: // First, we send a request for an empty buffer in which we // write our sectors from the write completion routine and send back. VmReq.head.type = p->Command[0]==0xE6? ReqVMReadDisk : ReqVMWriteDisk; VmReq.head.flags = Dma_GetAddress(2); VmReq.head.bufferlen = 0; VmReq.head.pComplete = Floppy_Transfer; VmReq.head.completeArg = (DWORD) p; VmReq.head.drive = (p==&floppy[0])? 0 : 1; VmReq.head.offset = p->logical_sector * 512; VmReq.head.sectors = p->max_sector - p->sector + 1; // Do not transfer more than the DMA has been set up to transfer if( Dma_GetOutstanding(2) < (VmReq.head.sectors * 512) ) VmReq.head.sectors = Dma_GetOutstanding(2) / 512; SendRequestToGui( &VmReq ); break; default: ERROR(("Invalid floppy command of %02X", p->Command[0])); } } // Completion function for floppy read operation - a buffer containing // the total number of sectors is returned from the app, filled in with // the sector data. // AND // Completion function for floppy write operation - an empty buffer // is returned from the app in which we deposit our sectors to // be written when this function returns it to the app. static void Floppy_Transfer(VMREQUEST *pVmReq, DWORD pArg, BYTE *pBuffer) { DWORD dwVmPhys; TFloppy *p = (TFloppy *) pArg; ASSERT(pBuffer); while( pVmReq->head.sectors && !Dma_IsTC(2) && (p->sector <= p->max_sector)) { dwVmPhys = Dma_GetAddress(2); if( p->Command0 == 0xE6 ) // READ disk { RtlMoveMemory((void *) (Mem.MemVM.linear + dwVmPhys), pBuffer, 512), MetaInvalidateVmPhysAddress( Dma_GetAddress(2) ); } else // WRITE to disk RtlMoveMemory(pBuffer, (void *)(Mem.MemVM.linear + dwVmPhys), 512); Dma_Roll(2, 512); // Increment logical and physical sector p->logical_sector++; if( p->sector++ > MAX_FLOPPY_SECTORS ) { p->sector = 1; if( p->head++ >= MAX_FLOPPY_HEADS ) { p->head = 0; if( p->track >= MAX_FLOPPY_TRACKS ) p->track = MAX_FLOPPY_TRACKS, p->logical_sector--; } } pBuffer += 512; pVmReq->head.sectors--; } // Finish data transfer, signal end-of-transfer p->Result[0] = ((BYTE)p->head << 2) | 0x20; // ST0 p->Result[1] = 0; // ST1 p->Result[2] = 0; // ST2 p->Result[3] = (BYTE) p->track; p->Result[4] = (BYTE) p->head; p->Result[5] = (BYTE) p->sector; p->Result[6] = 2; // Sector size Command_Complete( p ); } // Soft reset of the floppy controller static void Command_Reset() { int i; for( i=0; i<2; i++ ) { floppy[i].Command_Size = 0; floppy[i].Command_Index = 0; floppy[i].Result_Index = 0; floppy[i].Result_Size = 0; floppy[i].track = 0; floppy[i].head = 0; floppy[i].sector = 1; } }