I'm trying to transfer data using the (ISA)DMA instead of PIO mode, but when I try to read it I'm only getting garbage.
First I tried with and blank floppy image. Not blank really, is a FAT12 image, so it haves the typical FAT12 BPB.
But the data that I read doesn't match with the data that I obtain if I hexdump (hd) the image.
I also tried another disk image (but with data) and I obtain the same data. That means that I'm reading only garbage data.
Here's my ugly code:
fpc.c
Code: Select all
/* Floppy disk controller. hextakatt 2019 */
/* cylinder = track, head = platter, sector = cluster */
#include <stdint.h>
#include <stdbool.h>
#include <kernel/kernel.h>
#include <kernel/cpu/irq.h>
#include <kernel/timer.h>
#include <drivers/cmos/cmos.h>
#include <kernel/terminal.h>
/* for 1.44MB floppies */
#define FloppySectorsPerTrack 18
#define MaxFunctionAttempts 10
enum FloppyPorts
{
StatusRegA = 0x3F0,
StatusRegB = 0x3F1,
FdcDOR = 0x3F2,
FdcMSR = 0x3F4,
FdcFIFO = 0x3F5,
FdcCCR = 0x3F7
};
enum FloppyDOR
{
FdcDORReset = 0x4,
FdcDMA = 0x8,
FdcMotorA = 0x10,
FdcMotorB = 0x20,
FdcMotorC = 0x40,
FdcMotorD = 0x80,
};
enum FloppyCommands
{
FdcReadTrack = 0x2, // generates IRQ6
FdcSpecify = 0x3, // * set drive parameters
FdcSenseStat = 0x4,
FdcWriteData = 0x5, // * write to the disk
FdcReadData = 0x6, // * read from the disk
FdcRecalibrate = 0x7, // * seek to cylinder 0
FdcSenseInt = 0x8, // * ack IRQ6, get status of last command
FdcWriteDeletedData = 0x9,
FdcReadID = 0xA, // generates IRQ6
FdcReadDeletedData = 0xC,
FdcFormatTrack = 0xD, // *
FdcDump = 0xE,
FdcSeek = 0xF, // * seek both heads to cylinder X
FdcVersion = 0x10, // * used during initialization, once
FdcScanEq = 0x11,
FdcPerpendicularM = 0x12, // * used during initialization, once, maybe
FdcConf = 0x13, // * set controller parameters
FdcLock = 0x14, // * protect controller params from a reset
FdcVerify = 0x16,
FdcScanLoEq = 0x19, // SCAN_LOW_OR_EQUAL
FdcScanHiEq = 0x1D, // SCAN_HIGH_OR_EQUAL
};
struct __system_stack* r;
size_t current_drive = 0;
/* Is any motor spining? */
bool __motor_spin = false;
volatile bool floppy_irq = false;
const uint8_t DMA_BUFFER = 0x1000;
void lba_chs(uint32_t lba, uint16_t* cyl, uint16_t* head, uint16_t* sector)
{
*cyl = lba / (2 * FloppySectorsPerTrack);
*head = ((lba % (2 * FloppySectorsPerTrack)) / FloppySectorsPerTrack);
*sector = ((lba % (2 * FloppySectorsPerTrack)) % FloppySectorsPerTrack + 1);
}
void floppy_dma_init(void)
{
outb(0x0A, 0x06);
outb(0x0C, 0xFF);
outb(0x04, 0);
outb(0x04, 0x10);
outb(0x0C, 0xFF);
outb(0x05, 0xFF);
outb(0x05, 0x23);
outb(0x81, 0);
outb(0x0A, 0x02);
}
void floppy_dma_write(void)
{
outb(0x0A, 0x06);
outb(0x0B, 0x5A);
outb(0x0A, 0x02);
}
void floppy_dma_read(void)
{
outb(0x0A, 0x06);
outb(0x0B, 0x56);
outb(0x0A, 0x02);
}
uint8_t floppy_status(void)
{
return inb(FdcMSR);
}
void floppy_ccr(uint8_t value)
{
outb(FdcCCR, value);
}
inline void floppy_wait_irq(void)
{
/* Poll until we're ready for send I/O data */
//while ((floppy_status() & 0x80) == 0);
while (!floppy_irq);
}
/* Send commands to FIFO port */
void floppy_send_command(uint8_t value)
{
for (int i = 0; i < 255; ++i) {
if ((floppy_status() & 0x80) == 0)
outb(FdcFIFO, value);
}
}
/* The same, we poll, but we read data instead of send */
uint8_t floppy_read_command(void)
{
for (int i = 0; i < 255; ++i) {
if ((floppy_status() & 0x80) == 0)
return inb(FdcFIFO);
}
}
void floppy_sense(uint32_t* cyl, uint32_t* st0)
{
*cyl = floppy_read_command();
*st0 = floppy_read_command();
}
/* Write data to the digital output register */
void floppy_dor(uint8_t value)
{
outb(FdcDOR, value);
}
/*
* Toggle the actual floppy motor
* Sorry for using goto... But I think that this is an apropiate use of it ;)
*/
void floppy_motor(bool motorstat)
{
uint8_t motor;
if (current_drive > 4)
goto err;
switch (current_drive) {
case 0:
motor = FdcMotorA;
break;
case 1:
motor = FdcMotorB;
break;
case 2:
motor = FdcMotorC;
break;
case 3:
motor = FdcMotorD;
break;
default:
goto err;
break;
}
if (motorstat) {
/* Turning on the selected motor */
floppy_dor(current_drive | FdcDORReset | FdcDMA | motor);
__motor_spin = true;
} else {
floppy_dor(FdcDORReset);
__motor_spin = false;
}
/* Wait until the floppy motor gets enough speed, because software runs faster than hardware. (like in this case) */
usleep(50);
return;
err:
kputs("Invalid drive number (%i)!\n", current_drive);
return;
}
/* Detect if floppy disk door is open. (true = open, false = closed) */
bool floppy_detect_swap(void)
{
floppy_motor(true);
if (inb(floppy_ccr) & 0x80) {
floppy_motor(false);
return true;
}
floppy_motor(false);
return false;
}
/* Seek to an given cylinder and head */
int floppy_seek(uint32_t cylinder, uint32_t head)
{
uint32_t st0, cyl;
for (int i = 0; i < MaxFunctionAttempts; ++i) {
floppy_send_command(FdcSeek);
floppy_send_command((head << 2) | current_drive);
floppy_send_command(cylinder);
floppy_wait_irq();
floppy_sense(&st0, &cyl);
if (cyl == cylinder) {
kputs("\nSeek finished\n");
return 0;
}
}
return 1;
}
/* Reads data in an certain LBA position */
uint8_t* floppy_read(uint32_t lba)
{
if (floppy_detect_swap())
return 0;
uint32_t st0, cyl;
uint8_t* cylinder, head, sector;
lba_chs(lba, &cylinder, &head, §or);
/* Prepare DMA IC to data read */
floppy_dma_read();
while (floppy_read_command() & 0x8);
floppy_motor(true);
floppy_seek(cylinder, head);
/* Send all the requested data to the FDC */
floppy_send_command(FdcReadData | 0x80 | 0x40 | 0x6);
floppy_send_command((head << 2) | current_drive);
floppy_send_command(cylinder);
floppy_send_command(head);
floppy_send_command(sector);
floppy_send_command(0x2);
floppy_send_command(FloppySectorsPerTrack);
floppy_send_command(0x1B);
floppy_send_command(0xFF);
floppy_motor(false);
floppy_wait_irq();
floppy_send_command(FdcSenseInt);
return (uint8_t*)DMA_BUFFER;
}
uint8_t floppy_type(void)
{
uint8_t c = read_cmos(0x10);
return c >> 4;
}
void floppy_recalibrate(void)
{
uint32_t cyl, st0;
floppy_motor(true);
for (int i = 0; i < MaxFunctionAttempts; ++i) {
floppy_send_command(FdcRecalibrate);
floppy_send_command(current_drive);
floppy_wait_irq();
floppy_sense(&cyl, &st0);
if (cyl == 0) {
floppy_motor(false);
return;
} else if ((floppy_read_command() & 0x20) == 0) {
continue;
}
}
floppy_motor(false);
kputs("\nError while trying to calibrate\n");
}
void floppy_mech_data(uint32_t step, uint32_t load, uint32_t unload)
{
floppy_send_command(((step & 0xF) << 4) | (unload & 0xF));
floppy_send_command((load << 1) | 1);
}
/* The BIOS usually leaves the FDC in an undefined state */
void floppy_reset(void)
{
floppy_send_command(FdcVersion);
/* instead of having 0x90 I get 0x00... */
uint8_t aaa = floppy_read_command();
if (aaa != 0x90)
kputs("what???->%x", aaa);
floppy_dor(0);
floppy_dor(0x4 | 0x8);
floppy_wait_irq();
/* Send SENSE to all four drives */
for (int i = 0; i < 4; ++i)
floppy_send_command(FdcSenseInt);
floppy_send_command(FdcSpecify);
floppy_mech_data(0x3, 0x10, 0xF0);
/* Recalibrate our disk */
floppy_recalibrate();
}
void floppy_handler(struct __system_stack* r)
{
floppy_irq = true;
}
void floppy_install(void)
{
uint8_t a = floppy_type();
if (a == 4) {
irq_install_handler(6, floppy_handler);
floppy_reset();
floppy_dma_init();
} else {
kputs("Current floppy type not supported. (%i)\n", a);
}
}