[SeaBIOS] [PATCHv2 3/3] floppy: Implement cylinder seeking when accessing a different cylinder.
Kevin O'Connor
kevin at koconnor.net
Thu Dec 12 18:06:55 CET 2013
It appears that on real hardware, floppies require a seek command to
move the floppy head between cylinders. So, track what cylinder is
active and issue a seek command whenever a new cylinder is requested.
This also breaks up the floppy_cmd() code so that the low-level
command functions can pass the desired cylinder directly to the
seeking code.
Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
---
src/hw/floppy.c | 86 ++++++++++++++++++++++++++++++++-------------------------
1 file changed, 48 insertions(+), 38 deletions(-)
diff --git a/src/hw/floppy.c b/src/hw/floppy.c
index d01f354..b848203 100644
--- a/src/hw/floppy.c
+++ b/src/hw/floppy.c
@@ -222,6 +222,7 @@ floppy_wait_irq(void)
// Floppy commands
#define FCF_WAITIRQ 0x10000
#define FC_CHECKIRQ (0x08 | (0<<8) | (2<<12))
+#define FC_SEEK (0x0f | (2<<8) | (0<<12) | FCF_WAITIRQ)
#define FC_RECALIBRATE (0x07 | (1<<8) | (0<<12) | FCF_WAITIRQ)
#define FC_READID (0x4a | (1<<8) | (7<<12) | FCF_WAITIRQ)
#define FC_READ (0xe6 | (8<<8) | (7<<12) | FCF_WAITIRQ)
@@ -342,15 +343,9 @@ floppy_drive_pio(u8 floppyid, int command, u8 *param)
/****************************************************************
- * Floppy media sense
+ * Floppy media sense and seeking
****************************************************************/
-static inline void
-set_diskette_current_cyl(u8 floppyid, u8 cyl)
-{
- SET_BDA(floppy_track[floppyid], cyl);
-}
-
static int
floppy_drive_recal(u8 floppyid)
{
@@ -364,7 +359,7 @@ floppy_drive_recal(u8 floppyid)
u8 frs = GET_BDA(floppy_recalibration_status);
SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
- set_diskette_current_cyl(floppyid, 0);
+ SET_BDA(floppy_track[floppyid], 0);
return DISK_RET_SUCCESS;
}
@@ -429,22 +424,37 @@ floppy_media_sense(struct drive_s *drive_gf)
return DISK_RET_SUCCESS;
}
+// Prepare a floppy for a data transfer.
static int
-check_recal_drive(struct drive_s *drive_gf)
+floppy_prep(struct drive_s *drive_gf, u8 cylinder)
{
u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id);
- if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
- && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
- // Media is known.
- return DISK_RET_SUCCESS;
+ if (!(GET_BDA(floppy_recalibration_status) & (1<<floppyid)) ||
+ !(GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED)) {
+ // Recalibrate drive.
+ int ret = floppy_drive_recal(floppyid);
+ if (ret)
+ return ret;
- // Recalibrate drive.
- int ret = floppy_drive_recal(floppyid);
- if (ret)
- return ret;
+ // Sense media.
+ ret = floppy_media_sense(drive_gf);
+ if (ret)
+ return ret;
+ }
+
+ // Seek to cylinder if needed.
+ u8 lastcyl = GET_BDA(floppy_track[floppyid]);
+ if (cylinder != lastcyl) {
+ u8 param[2];
+ param[0] = floppyid;
+ param[1] = cylinder;
+ int ret = floppy_drive_pio(floppyid, FC_SEEK, param);
+ if (ret)
+ return ret;
+ SET_BDA(floppy_track[floppyid], cylinder);
+ }
- // Sense media.
- return floppy_media_sense(drive_gf);
+ return DISK_RET_SUCCESS;
}
@@ -454,20 +464,17 @@ check_recal_drive(struct drive_s *drive_gf)
// Perform a floppy transfer command (setup DMA and issue PIO).
static int
-floppy_cmd(struct disk_op_s *op, int blocksize, int command, u8 *param)
+floppy_dma_cmd(struct disk_op_s *op, int count, int command, u8 *param)
{
- int ret = check_recal_drive(op->drive_gf);
- if (ret)
- return ret;
-
// Setup DMA controller
int isWrite = command != FC_READ;
- ret = dma_floppy((u32)op->buf_fl, op->count * blocksize, isWrite);
+ int ret = dma_floppy((u32)op->buf_fl, count, isWrite);
if (ret)
return DISK_RET_EBOUNDARY;
// Invoke floppy controller
- ret = floppy_drive_pio(param[0] & 1, command, param);
+ u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
+ ret = floppy_drive_pio(floppyid, command, param);
if (ret)
return ret;
@@ -485,9 +492,6 @@ floppy_cmd(struct disk_op_s *op, int blocksize, int command, u8 *param)
return DISK_RET_ECONTROLLER;
}
- u8 track = (command != FC_FORMAT ? param[3] : 0);
- set_diskette_current_cyl(param[0] & 1, track);
-
return DISK_RET_SUCCESS;
}
@@ -534,6 +538,9 @@ static int
floppy_read(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
+ int res = floppy_prep(op->drive_gf, chs.cylinder);
+ if (res)
+ goto fail;
// send read-normal-data command to controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
@@ -546,7 +553,7 @@ floppy_read(struct disk_op_s *op)
param[5] = chs.sector + op->count - 1; // last sector to read on track
param[6] = FLOPPY_GAPLEN;
param[7] = FLOPPY_DATALEN;
- int res = floppy_cmd(op, DISK_SECTOR_SIZE, FC_READ, param);
+ res = floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_READ, param);
if (res)
goto fail;
return DISK_RET_SUCCESS;
@@ -560,6 +567,9 @@ static int
floppy_write(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
+ int res = floppy_prep(op->drive_gf, chs.cylinder);
+ if (res)
+ goto fail;
// send write-normal-data command to controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
@@ -572,7 +582,7 @@ floppy_write(struct disk_op_s *op)
param[5] = chs.sector + op->count - 1; // last sector to write on track
param[6] = FLOPPY_GAPLEN;
param[7] = FLOPPY_DATALEN;
- int res = floppy_cmd(op, DISK_SECTOR_SIZE, FC_WRITE, param);
+ res = floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_WRITE, param);
if (res)
goto fail;
return DISK_RET_SUCCESS;
@@ -585,15 +595,12 @@ fail:
static int
floppy_verify(struct disk_op_s *op)
{
- int res = check_recal_drive(op->drive_gf);
+ struct chs_s chs = lba2chs(op);
+ int res = floppy_prep(op->drive_gf, chs.cylinder);
if (res)
goto fail;
- struct chs_s chs = lba2chs(op);
-
- // ??? should track be new val from return_status[3] ?
- u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
- set_diskette_current_cyl(floppyid, chs.cylinder);
+ // This command isn't implemented - just return success.
return DISK_RET_SUCCESS;
fail:
op->count = 0; // no sectors read
@@ -605,6 +612,9 @@ static int
floppy_format(struct disk_op_s *op)
{
struct chs_s chs = lba2chs(op);
+ int res = floppy_prep(op->drive_gf, chs.cylinder);
+ if (res)
+ return res;
// send format-track command to controller
u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
@@ -614,7 +624,7 @@ floppy_format(struct disk_op_s *op)
param[2] = op->count; // number of sectors per track
param[3] = FLOPPY_FORMAT_GAPLEN;
param[4] = FLOPPY_FILLBYTE;
- return floppy_cmd(op, 4, FC_FORMAT, param);
+ return floppy_dma_cmd(op, op->count * 4, FC_FORMAT, param);
}
int
--
1.8.3.1
More information about the SeaBIOS
mailing list