2021-09-14 15:48:57 +00:00
|
|
|
/*
|
|
|
|
* floppy.c
|
|
|
|
*
|
|
|
|
* Created on: Aug 20, 2011
|
|
|
|
* Author: Tiberiu
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <debugio.h>
|
|
|
|
#include <stdio.h>
|
2021-09-14 15:51:43 +00:00
|
|
|
#include <stdlib.h>
|
2021-09-14 15:48:57 +00:00
|
|
|
#include <time.h>
|
|
|
|
#include <storage.h>
|
2021-09-14 15:51:43 +00:00
|
|
|
#include <fileio.h>
|
2021-09-14 15:48:57 +00:00
|
|
|
#include "floppy.h"
|
|
|
|
#include "../dma/dma.h"
|
|
|
|
#include "../cmos/cmos.h"
|
|
|
|
|
|
|
|
FloppyType fdTypes[] = {
|
|
|
|
/* Sectors
|
|
|
|
* | Sectors per track
|
|
|
|
* | | Heads
|
|
|
|
* | | | Tracks
|
|
|
|
* | | | | Gap1
|
|
|
|
* | | | | | Data rate
|
|
|
|
* | | | | | | Spec1
|
|
|
|
* | | | | | | | SRT HLT HUT Motor Spinup time
|
|
|
|
* | | | | | | | | | | | Motor Spindown time
|
|
|
|
* | | | | | | | | | | | | Interrupt timeout
|
|
|
|
* | | | | | | | | | | | | | Disk type name string*/
|
|
|
|
{ 0, 0,0, 0,0x00,0x00,0x00,0x00,0x00,0x00, 0, 0, 0, "none"},
|
|
|
|
{ 720, 9,2,40,0x2A,0x01,0xDF,0x0C,0x04,0x00,1000,1000,3000, "5.25\" 360k"},
|
|
|
|
{ 2400,15,2,80,0x1B,0x00,0xDF,0x0A,0x08,0x00, 400,1000,3000, "5.25\" 1.2M"},
|
|
|
|
{ 1440, 9,2,80,0x2A,0x02,0xDF,0x0F,0x04,0x00,1000,1000,3000, "3.5\" 720k"},
|
|
|
|
{ 2880,18,2,80,0x1B,0x00,0xCF,0x0C,0x08,0x00, 400,1000,3000, "3.5\" 1.44M"},
|
|
|
|
//{ 2880,18,2,80,0x1B,0x00,0xCF,0x0C,0x08,0x00,2000,3000,3000, "3.5\" 1.44M"},
|
|
|
|
{ 5760,36,2,80,0x1B,0x03,0xAF,0x0A,0x0F,0x00, 400,1000,3000, "3.5\" 2.88M AMI BIOS"},
|
|
|
|
{ 5760,36,2,80,0x1B,0x03,0xAF,0x0A,0x0F,0x00, 400,1000,3000, "3.5\" 2.88M"},
|
|
|
|
};
|
|
|
|
|
|
|
|
int8 fd0, fd1;
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* IRQ handler etc *
|
|
|
|
**************************************/
|
|
|
|
volatile uint8 FloppyIrqFired;
|
|
|
|
void FloppyIrqHandler(_RegsStack32* UNUSED(r))
|
|
|
|
{
|
|
|
|
FloppyIrqFired = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloppyWaitIrq()
|
|
|
|
{
|
|
|
|
TimerStart(fdTypes[4].InterruptTimeout);
|
|
|
|
|
|
|
|
while (!FloppyIrqFired && !TimerIsDone());
|
|
|
|
|
|
|
|
if (!FloppyIrqFired) {
|
2021-09-14 15:51:43 +00:00
|
|
|
Error("Floppy", "Irq timeout [%ums] !\n", fdTypes[4].InterruptTimeout);
|
2021-09-14 15:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Installation *
|
|
|
|
**************************************/
|
|
|
|
void FloppyInitialize()
|
|
|
|
{
|
|
|
|
// Detect drives
|
|
|
|
uint8 fd = CmosRead(0x10);
|
|
|
|
fd0 = fd >> 4;
|
|
|
|
fd1 = fd & 0xf;
|
|
|
|
|
|
|
|
if (fd0 > 6) fd0 = 0;
|
|
|
|
if (fd1 > 6) fd1 = 0;
|
|
|
|
|
|
|
|
if (!fd0 && !fd1) {
|
2021-09-14 15:51:43 +00:00
|
|
|
Error("Floppy", "No supported floppy drives found.");
|
2021-09-14 15:48:57 +00:00
|
|
|
outportb(FloppyRegisterDigitalOutput, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Detected floppy drives: %#fd0=%#%s %#fd1=%#%s\n", ColorLightCyan, Color(ColorCyan, ColorWhite), fdTypes[fd0].Name, ColorLightCyan, Color(ColorCyan, ColorWhite), fdTypes[fd1].Name);
|
2021-09-14 15:48:57 +00:00
|
|
|
// Reset floppy controller
|
|
|
|
FloppyReset();
|
|
|
|
|
|
|
|
// Configure and lock
|
|
|
|
FloppyConfigure();
|
|
|
|
FloppySendCommand(FloppyCommandLock | 0x80);
|
|
|
|
FloppyReadData();
|
|
|
|
|
|
|
|
// Enable perpendicular mode for 3.5" ED floppies
|
|
|
|
if (fd0 > 4)
|
|
|
|
{
|
|
|
|
FloppySendCommand(FloppyCommandPerpendicularMode);
|
|
|
|
FloppySendCommand(1);
|
|
|
|
}
|
|
|
|
if (fd1 > 4)
|
|
|
|
{
|
|
|
|
FloppySendCommand(FloppyCommandPerpendicularMode);
|
|
|
|
FloppySendCommand(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize DMA
|
|
|
|
FloppyInitDma();
|
2021-09-14 15:51:43 +00:00
|
|
|
|
|
|
|
// Mount
|
|
|
|
if (fd0) VfsMount("fd0", Floppy0ReadRoutine, Floppy0WriteRoutine, Min (fdTypes[fd0].SectorsPerTrack * 512, 0x2400));
|
|
|
|
if (fd1) VfsMount("fd1", Floppy1ReadRoutine, Floppy1WriteRoutine, Min (fdTypes[fd1].SectorsPerTrack * 512, 0x2400));
|
|
|
|
|
2021-09-14 15:48:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FloppyInitDma()
|
|
|
|
{
|
|
|
|
DmaMaskChannel(2);
|
|
|
|
DmaResetFlipFlop(2);
|
|
|
|
DmaSetAddress(2, 0, 0x10);
|
|
|
|
DmaResetFlipFlop(2);
|
|
|
|
DmaSetCount(2, 0xff, 0x23);
|
|
|
|
DmaSetExternalPageRegisters(2,0);
|
|
|
|
DmaUnmaskChannel(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Controller reset *
|
|
|
|
**************************************/
|
|
|
|
void FloppyReset()
|
|
|
|
{
|
|
|
|
FloppyIrqFired = 0; int32 i = 0;
|
|
|
|
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Resetting...\n");
|
2021-09-14 15:48:57 +00:00
|
|
|
|
|
|
|
// Clear reset bit from DOR
|
|
|
|
outportb(FloppyRegisterDigitalOutput, 0);
|
|
|
|
for (i = 0; i < 1000; i++);
|
|
|
|
outportb(FloppyRegisterDigitalOutput, 4|8);
|
|
|
|
|
|
|
|
// Wait for IRQ6
|
|
|
|
FloppyWaitIrq(fd0);
|
|
|
|
|
|
|
|
// Recalibrate every drive
|
|
|
|
if (fd0)
|
|
|
|
{
|
|
|
|
FloppyMotor(0,1);
|
|
|
|
FloppySelectDrive(0);
|
|
|
|
FloppyRecalibrate(0);
|
|
|
|
FloppyMotor(0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd1)
|
|
|
|
{
|
|
|
|
FloppyMotor(1,1);
|
|
|
|
FloppySelectDrive(1);
|
|
|
|
FloppyRecalibrate(1);
|
|
|
|
FloppyMotor(1,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Configure floppy controller *
|
|
|
|
**************************************/
|
|
|
|
void FloppyConfigure()
|
|
|
|
{
|
|
|
|
FloppySendCommand(FloppyCommandConfigure);
|
|
|
|
FloppySendCommand(0);
|
|
|
|
FloppySendCommand(1<<6 | 7);
|
|
|
|
FloppySendCommand(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Base commands *
|
|
|
|
**************************************/
|
|
|
|
void FloppySendCommand (uint8 command)
|
|
|
|
{
|
|
|
|
int32 t;
|
|
|
|
for (t = 0; t < 5000 && ((inportb(FloppyRegisterMainStatus) & FloppyMsrRQM) == 0); t++) ;
|
|
|
|
|
|
|
|
outportb (FloppyRegisterFIFO, command);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8 FloppyReadData ()
|
|
|
|
{
|
|
|
|
int32 t;
|
|
|
|
for (t = 0; t < 5000 && ((inportb(FloppyRegisterMainStatus) & FloppyMsrRQM) == 0); t++) ;
|
|
|
|
|
|
|
|
return inportb (FloppyRegisterFIFO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Sense interrupt *
|
|
|
|
**************************************/
|
|
|
|
void FloppySenseInterrupt(uint8 *st0, uint8 *cyl)
|
|
|
|
{
|
|
|
|
FloppySendCommand(FloppyCommandSenseInterrupt);
|
|
|
|
*st0 = FloppyReadData();
|
|
|
|
*cyl = FloppyReadData();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Specify *
|
|
|
|
**************************************/
|
|
|
|
void FloppySpecify (uint8 fd)
|
|
|
|
{
|
|
|
|
FloppySendCommand(FloppyCommandSpecify);
|
|
|
|
FloppySendCommand((fdTypes[fd].SRT << 4) | fdTypes[fd].HUT);
|
|
|
|
FloppySendCommand(fdTypes[fd].HLT << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Motor on/off *
|
|
|
|
**************************************/
|
|
|
|
void FloppyMotor (uint8 fd_number, uint8 status)
|
|
|
|
{
|
|
|
|
if (fd_number >= 2) return;
|
|
|
|
|
|
|
|
uint8 fd = (fd_number == 0) ? fd0 : fd1;
|
|
|
|
uint8 temp = inportb(FloppyRegisterDigitalOutput);
|
|
|
|
|
|
|
|
// Turn motor on/off
|
|
|
|
if (status) temp |= 0x1<<(4+fd_number);
|
|
|
|
else temp &= ~(0x1<<(4+fd_number));
|
|
|
|
outportb(FloppyRegisterDigitalOutput, temp);
|
|
|
|
|
|
|
|
// Wait for spinup/spindown
|
|
|
|
if (status) TimerStart(fdTypes[fd].Spinup);
|
|
|
|
else TimerStart(fdTypes[fd].Spindown);
|
|
|
|
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Waiting for motor...\n");
|
2021-09-14 15:48:57 +00:00
|
|
|
while (!TimerIsDone());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* Select drive *
|
|
|
|
**************************************/
|
|
|
|
void FloppySelectDrive(uint8 number)
|
|
|
|
{
|
|
|
|
if (number >= 2) return;
|
|
|
|
uint8 fd = (number == 0) ? fd0 : fd1;
|
|
|
|
|
|
|
|
// Set CCR
|
|
|
|
outportb(FloppyRegisterConfigurationControl, fdTypes[fd].DataRate);
|
|
|
|
|
|
|
|
// Specify
|
|
|
|
FloppySpecify(fd);
|
|
|
|
|
|
|
|
// Select drive
|
|
|
|
uint8 dor = inportb(FloppyRegisterDigitalOutput);
|
|
|
|
dor = (dor & ~0xFF) | number;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* RECALIBRATE *
|
|
|
|
* motor must be on, drive selected *
|
|
|
|
**************************************/
|
|
|
|
void FloppyRecalibrate(uint8 fd_number)
|
|
|
|
{
|
|
|
|
if (fd_number >= 2) return;
|
|
|
|
|
|
|
|
uint8 st0, cyl, timeout = 10;
|
|
|
|
do {
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Recalibrating: attempt %u/10\n", 11-timeout);
|
2021-09-14 15:48:57 +00:00
|
|
|
FloppyIrqFired = 0;
|
|
|
|
FloppySendCommand(FloppyCommandRecalibrate);
|
|
|
|
FloppySendCommand(fd_number);
|
|
|
|
FloppyWaitIrq();
|
|
|
|
|
|
|
|
FloppySenseInterrupt(&st0, &cyl);
|
|
|
|
|
|
|
|
timeout--;
|
|
|
|
} while((st0 & 0x20) == 0 && timeout > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* SEEK *
|
|
|
|
* motor must be on, drive selected *
|
|
|
|
**************************************/
|
|
|
|
void FloppySeek(uint8 fd_number, uint8 cylinder, uint8 head)
|
|
|
|
{
|
|
|
|
if (fd_number >= 2) return;
|
|
|
|
|
|
|
|
uint8 st0, cyl, timeout = 10;
|
|
|
|
do {
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Seeking: attempt %u/10\n", 11-timeout);
|
2021-09-14 15:48:57 +00:00
|
|
|
FloppyIrqFired = 0;
|
|
|
|
FloppySendCommand(FloppyCommandSeek);
|
|
|
|
FloppySendCommand(head<<2 | fd_number);
|
|
|
|
FloppySendCommand(cylinder);
|
|
|
|
FloppyWaitIrq();
|
|
|
|
|
|
|
|
FloppySenseInterrupt(&st0, &cyl);
|
|
|
|
|
|
|
|
timeout--;
|
|
|
|
} while(cyl != cylinder && timeout > 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************
|
|
|
|
* READ/WRITE *
|
|
|
|
* motor must be on, drive selected *
|
|
|
|
**************************************/
|
|
|
|
void FloppyRW(uint8 isWrite, uint8 fd_number, uint8 head, uint8 cylinder, uint8 sector)
|
|
|
|
{
|
|
|
|
if (fd_number >= 2) return;
|
|
|
|
uint8 fd = (fd_number == 0) ? fd0 : fd1;
|
|
|
|
|
|
|
|
uint8 timeout = 10;
|
|
|
|
uint8 result[7], i, error;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
error = 0;
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Read/write operation: attempt %u/10\n", 11-timeout);
|
2021-09-14 15:48:57 +00:00
|
|
|
FloppyIrqFired = 0;
|
|
|
|
|
|
|
|
if (isWrite) FloppySendCommand(FloppyCommandWriteData | FloppyModeMultitrack | FloppyModeMagneticEncoding);
|
|
|
|
else FloppySendCommand(FloppyCommandReadData | FloppyModeMultitrack | FloppyModeMagneticEncoding);
|
|
|
|
|
|
|
|
FloppySendCommand(head<<2 | fd_number);
|
|
|
|
FloppySendCommand(cylinder);
|
|
|
|
FloppySendCommand(head);
|
|
|
|
FloppySendCommand(sector);
|
|
|
|
FloppySendCommand(2);
|
|
|
|
FloppySendCommand(fdTypes[fd].SectorsPerTrack);
|
|
|
|
FloppySendCommand(fdTypes[fd].Gap);
|
|
|
|
FloppySendCommand(0xff);
|
|
|
|
|
|
|
|
FloppyWaitIrq();
|
|
|
|
|
|
|
|
for (i = 0; i < 7; i++)
|
|
|
|
result[i] = FloppyReadData();
|
|
|
|
|
|
|
|
// Disk is write protected, don't try again
|
|
|
|
if (result[1] & 2)
|
|
|
|
{
|
2021-09-14 15:51:43 +00:00
|
|
|
Error("Floppy", "Error: disk is write protected!\n");
|
2021-09-14 15:48:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any other error - try again
|
|
|
|
if (result[0] & 0xC8) error = 1;
|
|
|
|
if (result[1] & 0xB5) error = 1;
|
|
|
|
if (result[2] & 0x77) error = 1;
|
|
|
|
if (result[6] & 0x02) error = 1;
|
|
|
|
|
|
|
|
timeout--;
|
|
|
|
} while (timeout > 0 && !error);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 FloppyRead(uint8 drive, uint32 lba)
|
|
|
|
{
|
|
|
|
if (drive >= 2) return 0;
|
|
|
|
uint8 fd = (drive == 0) ? fd0 : fd1;
|
|
|
|
|
|
|
|
// Convert LBA to CHS
|
|
|
|
uint32 cyl=0, head=0, sect=1;
|
|
|
|
ConvertLbaToChs(fdTypes[fd].SectorsPerTrack, lba, &cyl, &head, §);
|
2021-09-14 15:51:43 +00:00
|
|
|
Log("Floppy", "Converted LBA=%u to Cyl=%u Head=%u Sect=%u\n", lba, cyl, head, sect);
|
2021-09-14 15:48:57 +00:00
|
|
|
|
|
|
|
FloppyInitDma();
|
|
|
|
|
|
|
|
// Reset drive if necessary
|
|
|
|
if ((inportb(FloppyRegisterMainStatus) & 0xC0) != 0x80)
|
|
|
|
FloppyReset();
|
|
|
|
|
|
|
|
// Start motor, select drive
|
|
|
|
FloppyMotor(drive, 1);
|
|
|
|
FloppySelectDrive(drive);
|
|
|
|
|
|
|
|
// Seek to correct location
|
|
|
|
FloppySeek(drive, cyl, head);
|
|
|
|
|
|
|
|
// Start DMA read
|
|
|
|
DmaMaskChannel(2);
|
|
|
|
DmaSetMode(2, 0x46);
|
|
|
|
DmaUnmaskChannel(2);
|
|
|
|
|
|
|
|
FloppyRW(0, drive, head, cyl, sect);
|
|
|
|
|
|
|
|
FloppyMotor(drive, 0);
|
|
|
|
|
|
|
|
return 0x1000;
|
|
|
|
}
|
|
|
|
|
2021-09-14 15:51:43 +00:00
|
|
|
uint32 FloppyWrite(uint8 drive, uint32 lba)
|
|
|
|
{
|
|
|
|
if (drive >= 2) return 0;
|
|
|
|
uint8 fd = (drive == 0) ? fd0 : fd1;
|
|
|
|
|
|
|
|
// Convert LBA to CHS
|
|
|
|
uint32 cyl=0, head=0, sect=1;
|
|
|
|
ConvertLbaToChs(fdTypes[fd].SectorsPerTrack, lba, &cyl, &head, §);
|
|
|
|
Log("Floppy", "Converted LBA=%u to Cyl=%u Head=%u Sect=%u\n", lba, cyl, head, sect);
|
|
|
|
|
|
|
|
FloppyInitDma();
|
|
|
|
|
|
|
|
// Reset drive if necessary
|
|
|
|
if ((inportb(FloppyRegisterMainStatus) & 0xC0) != 0x80)
|
|
|
|
FloppyReset();
|
|
|
|
|
|
|
|
// Start motor, select drive
|
|
|
|
FloppyMotor(drive, 1);
|
|
|
|
FloppySelectDrive(drive);
|
|
|
|
|
|
|
|
// Seek to correct location
|
|
|
|
FloppySeek(drive, cyl, head);
|
|
|
|
|
|
|
|
// Start DMA write
|
|
|
|
DmaMaskChannel(2);
|
|
|
|
DmaSetMode(2, 0x4A);
|
|
|
|
DmaUnmaskChannel(2);
|
|
|
|
|
|
|
|
FloppyRW(0, drive, head, cyl, sect);
|
|
|
|
|
|
|
|
FloppyMotor(drive, 0);
|
|
|
|
|
|
|
|
return 0x1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** Theses are for the VFS ***/
|
|
|
|
uint32 Floppy0ReadRoutine (uint32 offset, void* buffer)
|
|
|
|
{
|
|
|
|
uint32 ret = FloppyRead(0, offset);
|
|
|
|
|
|
|
|
if (!ret) return NULL;
|
|
|
|
memcpy(buffer, (const void*) ret, 0x2400);
|
|
|
|
|
|
|
|
return (uint32)buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Floppy1ReadRoutine (uint32 offset, void* buffer)
|
|
|
|
{
|
|
|
|
uint32 ret = FloppyRead(1, offset);
|
|
|
|
|
|
|
|
if (!ret) return NULL;
|
|
|
|
memcpy(buffer, (const void*) ret, 0x2400);
|
|
|
|
|
|
|
|
return (uint32)buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Floppy0WriteRoutine (uint32 offset, void* buffer)
|
|
|
|
{
|
|
|
|
memcpy((void*)0x1000, buffer, 0x2400);
|
|
|
|
return FloppyWrite(0, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 Floppy1WriteRoutine (uint32 offset, void* buffer)
|
|
|
|
{
|
|
|
|
memcpy((void*)0x1000, buffer, 0x2400);
|
|
|
|
return FloppyWrite(1, offset);
|
|
|
|
}
|