aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev/floppy.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/iodev/floppy.cc')
-rw-r--r--tools/ioemu/iodev/floppy.cc1633
1 files changed, 1633 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/floppy.cc b/tools/ioemu/iodev/floppy.cc
new file mode 100644
index 0000000000..e4090d57b9
--- /dev/null
+++ b/tools/ioemu/iodev/floppy.cc
@@ -0,0 +1,1633 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: floppy.cc,v 1.69 2003/12/18 20:04:49 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+//
+// Floppy Disk Controller Docs:
+// Intel 82077A Data sheet
+// ftp://void-core.2y.net/pub/docs/fdc/82077AA_FloppyControllerDatasheet.pdf
+// Intel 82078 Data sheet
+// ftp://download.intel.com/design/periphrl/datashts/29047403.PDF
+// Other FDC references
+// http://debs.future.easyspace.com/Programming/Hardware/FDC/floppy.html
+// And a port list:
+// http://mudlist.eorbit.net/~adam/pickey/ports.html
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+
+extern "C" {
+#include <errno.h>
+}
+
+#ifdef __linux__
+extern "C" {
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+}
+#endif
+#include "bochs.h"
+// windows.h included by bochs.h
+#ifdef WIN32
+extern "C" {
+#include <winioctl.h>
+}
+#endif
+#define LOG_THIS theFloppyController->
+
+bx_floppy_ctrl_c *theFloppyController;
+
+/* for 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
+
+#define FROM_FLOPPY 10
+#define TO_FLOPPY 11
+
+#define FLOPPY_DMA_CHAN 2
+
+typedef struct {
+ unsigned id;
+ Bit8u trk;
+ Bit8u hd;
+ Bit8u spt;
+ unsigned sectors;
+} floppy_type_t;
+
+static floppy_type_t floppy_type[8] = {
+ {BX_FLOPPY_160K, 40, 1, 8, 320},
+ {BX_FLOPPY_180K, 40, 1, 9, 360},
+ {BX_FLOPPY_320K, 40, 2, 8, 640},
+ {BX_FLOPPY_360K, 40, 2, 9, 720},
+ {BX_FLOPPY_720K, 80, 2, 9, 1440},
+ {BX_FLOPPY_1_2, 80, 2, 15, 2400},
+ {BX_FLOPPY_1_44, 80, 2, 18, 2880},
+ {BX_FLOPPY_2_88, 80, 2, 36, 5760}
+};
+
+
+ int
+libfloppy_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theFloppyController = new bx_floppy_ctrl_c ();
+ bx_devices.pluginFloppyDevice = theFloppyController;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theFloppyController, BX_PLUGIN_FLOPPY);
+ return(0); // Success
+}
+
+ void
+libfloppy_LTX_plugin_fini(void)
+{
+}
+
+
+bx_floppy_ctrl_c::bx_floppy_ctrl_c(void)
+{
+ put("FDD");
+ settype(FDLOG);
+ s.floppy_timer_index = BX_NULL_TIMER_HANDLE;
+}
+
+bx_floppy_ctrl_c::~bx_floppy_ctrl_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_floppy_ctrl_c::init(void)
+{
+ Bit8u i;
+
+ BX_DEBUG(("Init $Id: floppy.cc,v 1.69 2003/12/18 20:04:49 vruppert Exp $"));
+ DEV_dma_register_8bit_channel(2, dma_read, dma_write, "Floppy Drive");
+ DEV_register_irq(6, "Floppy Drive");
+ for (unsigned addr=0x03F2; addr<=0x03F7; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "Floppy Drive", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "Floppy Drive", 1);
+ }
+
+
+ DEV_cmos_set_reg(0x10, 0x00); /* start out with: no drive 0, no drive 1 */
+
+ BX_FD_THIS s.num_supported_floppies = 0;
+
+ for (i=0; i<4; i++) {
+ BX_FD_THIS s.device_type[i] = BX_FLOPPY_NONE;
+ BX_FD_THIS s.media[i].type = BX_FLOPPY_NONE;
+ }
+
+ //
+ // Floppy A setup
+ //
+ BX_FD_THIS s.media[0].sectors_per_track = 0;
+ BX_FD_THIS s.media[0].tracks = 0;
+ BX_FD_THIS s.media[0].heads = 0;
+ BX_FD_THIS s.media[0].sectors = 0;
+ BX_FD_THIS s.media[0].fd = -1;
+ BX_FD_THIS s.media_present[0] = 0;
+ BX_FD_THIS s.device_type[0] = bx_options.floppya.Odevtype->get ();
+
+ switch (BX_FD_THIS s.device_type[0]) {
+ case BX_FLOPPY_NONE:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x00);
+ break;
+ case BX_FLOPPY_360K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x10);
+ break;
+ case BX_FLOPPY_1_2:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x20);
+ break;
+ case BX_FLOPPY_720K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x30);
+ break;
+ case BX_FLOPPY_1_44:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x40);
+ break;
+ case BX_FLOPPY_2_88:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x50);
+ break;
+
+ // use CMOS reserved types
+ case BX_FLOPPY_160K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x60);
+ BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 6"));
+ break;
+ case BX_FLOPPY_180K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x70);
+ BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 7"));
+ break;
+ case BX_FLOPPY_320K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x80);
+ BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 8"));
+ break;
+
+ default:
+ BX_PANIC(("unknown floppya type"));
+ }
+ if (BX_FD_THIS s.device_type[0] != BX_FLOPPY_NONE)
+ BX_FD_THIS s.num_supported_floppies++;
+
+ if (bx_options.floppya.Otype->get () != BX_FLOPPY_NONE) {
+ if ( bx_options.floppya.Ostatus->get () == BX_INSERTED) {
+ if (evaluate_media(bx_options.floppya.Otype->get (), bx_options.floppya.Opath->getptr (),
+ & BX_FD_THIS s.media[0]))
+ BX_FD_THIS s.media_present[0] = 1;
+ else
+ bx_options.floppya.Ostatus->set(BX_EJECTED);
+#define MED (BX_FD_THIS s.media[0])
+ BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppya.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ }
+ }
+
+
+ //
+ // Floppy B setup
+ //
+ BX_FD_THIS s.media[1].sectors_per_track = 0;
+ BX_FD_THIS s.media[1].tracks = 0;
+ BX_FD_THIS s.media[1].heads = 0;
+ BX_FD_THIS s.media[1].sectors = 0;
+ BX_FD_THIS s.media[1].fd = -1;
+ BX_FD_THIS s.media_present[1] = 0;
+ BX_FD_THIS s.device_type[1] = bx_options.floppyb.Odevtype->get ();
+
+ switch (BX_FD_THIS s.device_type[1]) {
+ case BX_FLOPPY_NONE:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x00);
+ break;
+ case BX_FLOPPY_360K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x01);
+ break;
+ case BX_FLOPPY_1_2:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x02);
+ break;
+ case BX_FLOPPY_720K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x03);
+ break;
+ case BX_FLOPPY_1_44:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x04);
+ break;
+ case BX_FLOPPY_2_88:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x05);
+ break;
+
+ // use CMOS reserved types
+ case BX_FLOPPY_160K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x06);
+ BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 6"));
+ break;
+ case BX_FLOPPY_180K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x07);
+ BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 7"));
+ break;
+ case BX_FLOPPY_320K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x08);
+ BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 8"));
+ break;
+
+ default:
+ BX_PANIC(("unknown floppyb type"));
+ }
+ if (BX_FD_THIS s.device_type[1] != BX_FLOPPY_NONE)
+ BX_FD_THIS s.num_supported_floppies++;
+
+ if (bx_options.floppyb.Otype->get () != BX_FLOPPY_NONE) {
+ if ( bx_options.floppyb.Ostatus->get () == BX_INSERTED) {
+ if (evaluate_media(bx_options.floppyb.Otype->get (), bx_options.floppyb.Opath->getptr (),
+ & BX_FD_THIS s.media[1]))
+ BX_FD_THIS s.media_present[1] = 1;
+ else
+ bx_options.floppyb.Ostatus->set(BX_EJECTED);
+#define MED (BX_FD_THIS s.media[1])
+ BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppyb.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ }
+ }
+
+
+
+ /* CMOS Equipment Byte register */
+ if (BX_FD_THIS s.num_supported_floppies > 0) {
+ DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e) |
+ ((BX_FD_THIS s.num_supported_floppies-1) << 6) | 1);
+ }
+ else
+ DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e));
+
+
+ if (BX_FD_THIS s.floppy_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_FD_THIS s.floppy_timer_index =
+ bx_pc_system.register_timer( this, timer_handler,
+ bx_options.Ofloppy_command_delay->get (), 0,0, "floppy");
+ }
+
+ BX_DEBUG(("bx_options.Ofloppy_command_delay = %u",
+ (unsigned) bx_options.Ofloppy_command_delay->get ()));
+}
+
+
+
+ void
+bx_floppy_ctrl_c::reset(unsigned type)
+{
+ Bit32u i;
+
+ BX_FD_THIS s.pending_irq = 0;
+ BX_FD_THIS s.reset_sensei = 0; /* no reset result present */
+
+ BX_FD_THIS s.main_status_reg = 0;
+ BX_FD_THIS s.status_reg0 = 0;
+ BX_FD_THIS s.status_reg1 = 0;
+ BX_FD_THIS s.status_reg2 = 0;
+ BX_FD_THIS s.status_reg3 = 0;
+
+ // software reset (via DOR port 0x3f2 bit 2) does not change DOR
+ if (type == BX_RESET_HARDWARE) {
+ BX_FD_THIS s.DOR = 0x0c;
+ // motor off, drive 3..0
+ // DMA/INT enabled
+ // normal operation
+ // drive select 0
+
+ // DIR and CCR affected only by hard reset
+ for (i=0; i<4; i++) {
+ BX_FD_THIS s.DIR[i] |= 0x80; // disk changed
+ }
+ BX_FD_THIS s.data_rate = 0; /* 500 Kbps */
+ }
+
+ for (i=0; i<4; i++) {
+ BX_FD_THIS s.cylinder[i] = 0;
+ BX_FD_THIS s.head[i] = 0;
+ BX_FD_THIS s.sector[i] = 0;
+ }
+
+ DEV_pic_lower_irq(6);
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_idle_phase();
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_floppy_ctrl_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_FD_SMF
+ bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ /* reads from the floppy io ports */
+ Bit32u
+bx_floppy_ctrl_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_FD_SMF
+ Bit8u status, value;
+
+ if (bx_dbg.floppy)
+ BX_INFO(("read access to port %04x", (unsigned) address));
+
+ switch (address) {
+#if BX_DMA_FLOPPY_IO
+ case 0x3F2: // diskette controller digital output register
+ value = BX_FD_THIS s.DOR;
+ return(value);
+ break;
+
+ case 0x3F4: /* diskette controller main status register */
+ status = BX_FD_THIS s.main_status_reg;
+ return(status);
+ break;
+
+ case 0x3F5: /* diskette controller data */
+ if (BX_FD_THIS s.result_size == 0) {
+ BX_ERROR(("port 0x3f5: no results to read"));
+ BX_FD_THIS s.main_status_reg = 0;
+ return BX_FD_THIS s.result[0];
+ }
+
+ value = BX_FD_THIS s.result[BX_FD_THIS s.result_index++];
+ BX_FD_THIS s.main_status_reg &= 0xF0;
+ if (BX_FD_THIS s.result_index >= BX_FD_THIS s.result_size) {
+ if (!BX_FD_THIS s.reset_sensei) BX_FD_THIS s.pending_irq = 0;
+ DEV_pic_lower_irq(6);
+ enter_idle_phase();
+ }
+ return(value);
+ break;
+#endif // #if BX_DMA_FLOPPY_IO
+
+ case 0x3F3: // Tape Drive Register
+ // see http://www.smsc.com/main/datasheets/37c93x.pdf page 18 for more details
+
+ switch( BX_FD_THIS s.DOR & 0x03 )
+ {
+ case 0x00:
+ if( (BX_FD_THIS s.DOR & 0x10) == 0) break;
+ return(2);
+ case 0x01:
+ if( (BX_FD_THIS s.DOR & 0x20) == 0) break;
+ return(1);
+ }
+ return(3);
+
+ case 0x3F6: // Reserved for future floppy controllers
+ // This address shared with the hard drive controller
+ value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len);
+ return( value );
+ break;
+
+ case 0x3F7: // diskette controller digital input register
+ // This address shared with the hard drive controller:
+ // Bit 7 : floppy
+ // Bits 6..0: hard drive
+ value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len);
+ value &= 0x7f;
+ // add in diskette change line
+ value |= (BX_FD_THIS s.DIR[BX_FD_THIS s.DOR & 0x03] & 0x80);
+ return( value );
+ break;
+ default:
+ BX_ERROR(("io_read: unsupported address 0x%04x", (unsigned) address));
+ return(0);
+ break;
+ }
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_floppy_ctrl_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_FD_SMF
+ bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ /* writes to the floppy io ports */
+ void
+bx_floppy_ctrl_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_FD_SMF
+ Bit8u dma_and_interrupt_enable;
+ Bit8u normal_operation, prev_normal_operation;
+ Bit8u drive_select;
+ Bit8u motor_on_drive0, motor_on_drive1;
+
+ if (bx_dbg.floppy)
+ BX_INFO(("write access to port %04x, value=%02x",
+ (unsigned) address, (unsigned) value));
+
+ switch (address) {
+#if BX_DMA_FLOPPY_IO
+ case 0x3F2: /* diskette controller digital output register */
+ motor_on_drive1 = value & 0x20;
+ motor_on_drive0 = value & 0x10;
+ dma_and_interrupt_enable = value & 0x08;
+ if (!dma_and_interrupt_enable)
+ BX_DEBUG(("DMA and interrupt capabilities disabled"));
+ normal_operation = value & 0x04;
+ drive_select = value & 0x03;
+
+ prev_normal_operation = BX_FD_THIS s.DOR & 0x04;
+ BX_FD_THIS s.DOR = value;
+
+ if (prev_normal_operation==0 && normal_operation) {
+ // transition from RESET to NORMAL
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ }
+ else if (prev_normal_operation && normal_operation==0) {
+ // transition from NORMAL to RESET
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ BX_FD_THIS s.pending_command = 0xfe; // RESET pending
+
+ }
+ BX_DEBUG(("io_write: digital output register"));
+ BX_DEBUG((" motor on, drive1 = %d", motor_on_drive1 > 0));
+ BX_DEBUG((" motor on, drive0 = %d", motor_on_drive0 > 0));
+ BX_DEBUG((" dma_and_interrupt_enable=%02x",
+ (unsigned) dma_and_interrupt_enable));
+ BX_DEBUG((" normal_operation=%02x",
+ (unsigned) normal_operation));
+ BX_DEBUG((" drive_select=%02x",
+ (unsigned) drive_select));
+ if (BX_FD_THIS s.device_type[drive_select] == BX_FLOPPY_NONE) {
+ BX_DEBUG(("WARNING: not existing drive selected"));
+ }
+ break;
+
+ case 0x3f4: /* diskette controller data rate select register */
+ BX_ERROR(("io_write: data rate select register unsupported"));
+ break;
+
+ case 0x3F5: /* diskette controller data */
+ BX_DEBUG(("command = %02x", (unsigned) value));
+ if (BX_FD_THIS s.command_complete) {
+ if (BX_FD_THIS s.pending_command!=0)
+ BX_PANIC(("io: 3f5: receiving new comm, old one (%02x) pending",
+ (unsigned) BX_FD_THIS s.pending_command));
+ BX_FD_THIS s.command[0] = value;
+ BX_FD_THIS s.command_complete = 0;
+ BX_FD_THIS s.command_index = 1;
+ /* read/write command in progress */
+ BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_BUSY;
+ switch (value) {
+ case 0x03: /* specify */
+ BX_FD_THIS s.command_size = 3;
+ break;
+ case 0x04: // get status
+ BX_FD_THIS s.command_size = 2;
+ break;
+ case 0x07: /* recalibrate */
+ BX_FD_THIS s.command_size = 2;
+ break;
+ case 0x08: /* sense interrupt status */
+ BX_FD_THIS s.command_size = 1;
+ break;
+ case 0x0f: /* seek */
+ BX_FD_THIS s.command_size = 3;
+ break;
+ case 0x4a: /* read ID */
+ BX_FD_THIS s.command_size = 2;
+ break;
+ case 0x4d: /* format track */
+ BX_FD_THIS s.command_size = 6;
+ break;
+ case 0x45:
+ case 0xc5: /* write normal data */
+ BX_FD_THIS s.command_size = 9;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0xc6:
+ case 0xe6: /* read normal data */
+ BX_FD_THIS s.command_size = 9;
+ break;
+
+ case 0x13: // Configure command (Enhanced)
+ BX_FD_THIS s.command_size = 4;
+ break;
+
+ case 0x0e: // dump registers (Enhanced drives)
+ case 0x10: // Version command, standard controller returns 80h
+ case 0x18: // National Semiconductor version command; return 80h
+ // These commands are not implemented on the standard
+ // controller and return an error. They are available on
+ // the enhanced controller.
+ BX_DEBUG(("io_write: 0x3f5: unsupported floppy command 0x%02x",
+ (unsigned) value));
+ BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command
+ BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command
+ enter_result_phase();
+ break;
+
+ default:
+ BX_ERROR(("io_write: 0x3f5: invalid floppy command 0x%02x",
+ (unsigned) value));
+ BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command
+ BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command
+ enter_result_phase();
+ break;
+ }
+ }
+ else {
+ BX_FD_THIS s.command[BX_FD_THIS s.command_index++] =
+ value;
+ }
+ if (BX_FD_THIS s.command_index ==
+ BX_FD_THIS s.command_size) {
+ /* read/write command not in progress any more */
+ floppy_command();
+ BX_FD_THIS s.command_complete = 1;
+ }
+ BX_DEBUG(("io_write: diskette controller data"));
+ return;
+ break;
+#endif // #if BX_DMA_FLOPPY_IO
+
+ case 0x3F6: /* diskette controller (reserved) */
+ BX_DEBUG(("io_write: reserved register 0x3f6 unsupported"));
+ // this address shared with the hard drive controller
+ DEV_hd_write_handler(bx_devices.pluginHardDrive, address, value, io_len);
+ break;
+
+#if BX_DMA_FLOPPY_IO
+ case 0x3F7: /* diskette controller configuration control register */
+ BX_DEBUG(("io_write: config control register"));
+ BX_FD_THIS s.data_rate = value & 0x03;
+ switch (BX_FD_THIS s.data_rate) {
+ case 0: BX_DEBUG((" 500 Kbps")); break;
+ case 1: BX_DEBUG((" 300 Kbps")); break;
+ case 2: BX_DEBUG((" 250 Kbps")); break;
+ case 3: BX_DEBUG((" 1 Mbps")); break;
+ }
+ return;
+ break;
+
+ default:
+ BX_ERROR(("io_write ignored: 0x%04x = 0x%02x", (unsigned) address, (unsigned) value));
+ break;
+#endif // #if BX_DMA_FLOPPY_IO
+ }
+}
+
+
+
+ void
+bx_floppy_ctrl_c::floppy_command(void)
+{
+#if BX_PROVIDE_CPU_MEMORY==0
+ BX_PANIC(("floppy_command(): uses DMA: not supported for"
+ " external environment"));
+#else
+ unsigned i;
+ Bit8u step_rate_time;
+ Bit8u head_unload_time;
+ Bit8u head_load_time;
+ Bit8u motor_on;
+ Bit8u head, drive, cylinder, sector, eot;
+ Bit8u sector_size, data_length;
+ Bit32u logical_sector;
+
+
+ BX_DEBUG(("FLOPPY COMMAND: "));
+ for (i=0; i<BX_FD_THIS s.command_size; i++)
+ BX_DEBUG(("[%02x] ", (unsigned) BX_FD_THIS s.command[i]));
+
+#if 0
+ /* execute phase of command is in progress (non DMA mode) */
+ BX_FD_THIS s.main_status_reg |= 20;
+#endif
+
+ BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0];
+ switch (BX_FD_THIS s.pending_command) {
+ case 0x03: // specify
+ // execution: specified parameters are loaded
+ // result: no result bytes, no interrupt
+ step_rate_time = BX_FD_THIS s.command[1] >> 4;
+ head_unload_time = BX_FD_THIS s.command[1] & 0x0f;
+ head_load_time = BX_FD_THIS s.command[2] >> 1;
+ if (BX_FD_THIS s.command[2] & 0x01)
+ BX_ERROR(("non DMA mode selected"));
+ enter_idle_phase();
+ return;
+ break;
+
+ case 0x04: // get status
+ drive = (BX_FD_THIS s.command[1] & 0x03);
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ BX_FD_THIS s.status_reg3 = 0x28 | (BX_FD_THIS s.head[drive]<<2) | drive
+ | (BX_FD_THIS s.media[drive].write_protected ? 0x40 : 0x00);
+ if (BX_FD_THIS s.cylinder[drive] == 0) BX_FD_THIS s.status_reg3 |= 0x10;
+ enter_result_phase();
+ return;
+ break;
+
+ case 0x07: // recalibrate
+ drive = (BX_FD_THIS s.command[1] & 0x03);
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+ BX_DEBUG(("floppy_command(): recalibrate drive %u",
+ (unsigned) drive));
+ motor_on = ( (BX_FD_THIS s.DOR>>(drive+4))
+ & 0x01 );
+ if (motor_on == 0) {
+ BX_INFO(("floppy_command(): recal drive with motor off"));
+ }
+ if (drive==0)
+ BX_FD_THIS s.DOR |= 0x10; // turn on MOTA
+ else
+ BX_FD_THIS s.DOR |= 0x20; // turn on MOTB
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ /* command head to track 0
+ * controller set to non-busy
+ * error condition noted in Status reg 0's equipment check bit
+ * seek end bit set to 1 in Status reg 0 regardless of outcome
+ * The last two are taken care of in timer().
+ */
+ BX_FD_THIS s.cylinder[drive] = 0;
+ BX_FD_THIS s.main_status_reg = (1 << drive);
+ return;
+ break;
+
+ case 0x08: /* sense interrupt status */
+ /* execution:
+ * get status
+ * result:
+ * no interupt
+ * byte0 = status reg0
+ * byte1 = current cylinder number (0 to 79)
+ */
+ drive = BX_FD_THIS s.DOR & 0x03;
+ if (!BX_FD_THIS s.pending_irq) {
+ BX_FD_THIS s.status_reg0 = 0x80;
+ }
+ else {
+ if (BX_FD_THIS s.reset_sensei > 0) {
+ drive = 4 - BX_FD_THIS s.reset_sensei;
+ BX_FD_THIS s.status_reg0 &= 0xf8;
+ BX_FD_THIS s.status_reg0 |= (BX_FD_THIS s.head[drive] << 2) | drive;
+ BX_FD_THIS s.reset_sensei--;
+ }
+ }
+
+ BX_DEBUG(("sense interrupt status"));
+ enter_result_phase();
+ return;
+ break;
+
+ case 0x0f: /* seek */
+ /* command:
+ * byte0 = 0F
+ * byte1 = drive & head select
+ * byte2 = cylinder number
+ * execution:
+ * postion head over specified cylinder
+ * result:
+ * no result bytes, issues an interrupt
+ */
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.command[2];
+ /* ??? should also check cylinder validity */
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ /* data reg not ready, drive busy */
+ BX_FD_THIS s.main_status_reg = (1 << drive);
+ return;
+ break;
+
+ case 0x13: // Configure
+ BX_DEBUG(("configure (eis = 0x%02x)", BX_FD_THIS s.command[2] & 0x40 ));
+ BX_DEBUG(("configure (efifo = 0x%02x)", BX_FD_THIS s.command[2] & 0x20 ));
+ BX_DEBUG(("configure (no poll = 0x%02x)", BX_FD_THIS s.command[2] & 0x10 ));
+ BX_DEBUG(("configure (fifothr = 0x%02x)", BX_FD_THIS s.command[2] & 0x0f ));
+ BX_DEBUG(("configure (pretrk = 0x%02x)", BX_FD_THIS s.command[3] ));
+ enter_idle_phase();
+ return;
+ break;
+
+ case 0x4a: // read ID
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
+ if (motor_on == 0) {
+ BX_ERROR(("floppy_command(): 0x4a: motor not on"));
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ }
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_command(): read ID: bad drive #%d", drive));
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive]<<2) | drive;
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ break;
+
+ case 0x4d: // format track
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
+ if (motor_on == 0)
+ BX_PANIC(("floppy_command(): format track: motor not on"));
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ sector_size = BX_FD_THIS s.command[2];
+ BX_FD_THIS s.format_count = BX_FD_THIS s.command[3];
+ BX_FD_THIS s.format_fillbyte = BX_FD_THIS s.command[5];
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_command(): format track: bad drive #%d", drive));
+
+ if (sector_size != 0x02) { // 512 bytes
+ BX_PANIC(("format track: sector size %d not supported", 128<<sector_size));
+ }
+ if (BX_FD_THIS s.format_count != BX_FD_THIS s.media[drive].sectors_per_track) {
+ BX_PANIC(("format track: %d sectors/track requested (%d expected)",
+ BX_FD_THIS s.format_count, BX_FD_THIS s.media[drive].sectors_per_track));
+ }
+ if ( BX_FD_THIS s.media_present[drive] == 0 ) {
+ // media not in drive, return error
+ BX_INFO(("attempt to format track with media not present"));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x25; // 0010 0101
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+ if (BX_FD_THIS s.media[drive].write_protected) {
+ // media write-protected, return error
+ BX_INFO(("attempt to format track with media write-protected"));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+
+ /* 4 header bytes per sector are required */
+ BX_FD_THIS s.format_count <<= 2;
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
+
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ BX_DEBUG(("format track"));
+ return;
+ break;
+
+ case 0x46: // read normal data, MT=0, SK=0
+ case 0x66: // read normal data, MT=0, SK=1
+ case 0xc6: // read normal data, MT=1, SK=0
+ case 0xe6: // read normal data, MT=1, SK=1
+ case 0x45: // write normal data, MT=0
+ case 0xc5: // write normal data, MT=1
+ BX_FD_THIS s.multi_track = (BX_FD_THIS s.command[0] >> 7);
+ if ( (BX_FD_THIS s.DOR & 0x08) == 0 )
+ BX_PANIC(("read/write command with DMA and int disabled"));
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
+ if (motor_on == 0)
+ BX_PANIC(("floppy_command(): read/write: motor not on"));
+ head = BX_FD_THIS s.command[3] & 0x01;
+ cylinder = BX_FD_THIS s.command[2]; /* 0..79 depending */
+ sector = BX_FD_THIS s.command[4]; /* 1..36 depending */
+ eot = BX_FD_THIS s.command[6]; /* 1..36 depending */
+ sector_size = BX_FD_THIS s.command[5];
+ data_length = BX_FD_THIS s.command[8];
+ BX_DEBUG(("read/write normal data"));
+ BX_DEBUG(("BEFORE"));
+ BX_DEBUG((" drive = %u", (unsigned) drive));
+ BX_DEBUG((" head = %u", (unsigned) head));
+ BX_DEBUG((" cylinder = %u", (unsigned) cylinder));
+ BX_DEBUG((" sector = %u", (unsigned) sector));
+ BX_DEBUG((" eot = %u", (unsigned) eot));
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_command(): read/write: bad drive #%d", drive));
+
+ // check that head number in command[1] bit two matches the head
+ // reported in the head number field. Real floppy drives are
+ // picky about this, as reported in SF bug #439945, (Floppy drive
+ // read input error checking).
+ if (head != ((BX_FD_THIS s.command[1]>>2)&1)) {
+ BX_ERROR(("head number in command[1] doesn't match head field"));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x04; // 0000 0100
+ BX_FD_THIS s.status_reg2 = 0x00; // 0000 0000
+ enter_result_phase();
+ return;
+ }
+
+ if ( BX_FD_THIS s.media_present[drive] == 0 ) {
+ // media not in drive, return error
+
+ BX_INFO(("attempt to read/write sector %u,"
+ " sectors/track=%u with media not present",
+ (unsigned) sector,
+ (unsigned) BX_FD_THIS s.media[drive].sectors_per_track));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x25; // 0010 0101
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+
+ if (sector_size != 0x02) { // 512 bytes
+ BX_PANIC(("read/write command: sector size %d not supported", 128<<sector_size));
+ }
+ if ( cylinder >= BX_FD_THIS s.media[drive].tracks ) {
+ BX_PANIC(("io: norm r/w parms out of range: sec#%02xh cyl#%02xh eot#%02xh head#%02xh",
+ (unsigned) sector, (unsigned) cylinder, (unsigned) eot,
+ (unsigned) head));
+ return;
+ }
+
+ if (sector > BX_FD_THIS s.media[drive].sectors_per_track) {
+ // requested sector > last sector on track
+ BX_INFO(("attempt to read/write sector %u,"
+ " sectors/track=%u", (unsigned) sector,
+ (unsigned) BX_FD_THIS s.media[drive].sectors_per_track));
+ // set controller to where drive would have left off
+ // after it discovered the sector was past EOT
+ BX_FD_THIS s.cylinder[drive] = cylinder;
+ BX_FD_THIS s.head[drive] = head;
+ BX_FD_THIS s.sector[drive] = BX_FD_THIS s.media[drive].sectors_per_track;
+
+ // 0100 0HDD abnormal termination
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
+ // 1000 0101 end of cyl/NDAT/NID
+ BX_FD_THIS s.status_reg1 = 0x85;
+ // 0000 0000
+ BX_FD_THIS s.status_reg2 = 0x00;
+ enter_result_phase();
+ return;
+ }
+
+ if (cylinder != BX_FD_THIS s.cylinder[drive])
+ BX_DEBUG(("io: cylinder request != current cylinder"));
+
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (cylinder * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (head * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (sector - 1);
+
+ if (logical_sector >= BX_FD_THIS s.media[drive].sectors) {
+ BX_PANIC(("io: logical sector out of bounds"));
+ }
+
+ BX_FD_THIS s.cylinder[drive] = cylinder;
+ BX_FD_THIS s.sector[drive] = sector;
+ BX_FD_THIS s.head[drive] = head;
+
+ if ((BX_FD_THIS s.command[0] & 0x4f) == 0x46) { // read
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, FROM_FLOPPY);
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
+
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ }
+ else if ((BX_FD_THIS s.command[0] & 0x7f) == 0x45) { // write
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
+
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ }
+ else
+ BX_PANIC(("floppy_command(): unknown read/write command"));
+
+ return;
+ break;
+
+ default: // invalid or unsupported command; these are captured in write() above
+ BX_PANIC(("You should never get here! cmd = 0x%02x",
+ BX_FD_THIS s.command[0]));
+ }
+#endif
+}
+
+ void
+bx_floppy_ctrl_c::floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer,
+ Bit32u bytes, Bit8u direction)
+{
+ int ret;
+
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_xfer: bad drive #%d", drive));
+
+ if (bx_dbg.floppy) {
+ BX_INFO(("drive=%u", (unsigned) drive));
+ BX_INFO(("offset=%u", (unsigned) offset));
+ BX_INFO(("bytes=%u", (unsigned) bytes));
+ BX_INFO(("direction=%s", (direction==FROM_FLOPPY)? "from" : "to"));
+ }
+
+#if BX_WITH_MACOS
+ if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+#endif
+ {
+ ret = lseek(BX_FD_THIS s.media[drive].fd, offset, SEEK_SET);
+ if (ret < 0) {
+ BX_PANIC(("could not perform lseek() on floppy image file"));
+ }
+ }
+
+ if (direction == FROM_FLOPPY) {
+#if BX_WITH_MACOS
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ ret = fd_read((char *) buffer, offset, bytes);
+ else
+#endif
+ ret = ::read(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes);
+ if (ret < int(bytes)) {
+ /* ??? */
+ if (ret > 0) {
+ BX_INFO(("partial read() on floppy image returns %u/%u",
+ (unsigned) ret, (unsigned) bytes));
+ memset(buffer + ret, 0, bytes - ret);
+ }
+ else {
+ BX_INFO(("read() on floppy image returns 0"));
+ memset(buffer, 0, bytes);
+ }
+ }
+ }
+
+ else { // TO_FLOPPY
+ BX_ASSERT (!BX_FD_THIS s.media[drive].write_protected);
+#if BX_WITH_MACOS
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ ret = fd_write((char *) buffer, offset, bytes);
+ else
+#endif
+ ret = ::write(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes);
+ if (ret < int(bytes)) {
+ BX_PANIC(("could not perform write() on floppy image file"));
+ }
+ }
+}
+
+
+
+ void
+bx_floppy_ctrl_c::timer_handler(void *this_ptr)
+{
+
+ bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
+
+ class_ptr->timer();
+}
+
+ void
+bx_floppy_ctrl_c::timer()
+{
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+ switch ( BX_FD_THIS s.pending_command ) {
+ case 0x07: // recal
+ case 0x0f: // seek
+ BX_FD_THIS s.status_reg0 = 0x20 | (BX_FD_THIS s.head[drive]<<2) | drive;
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) {
+ BX_FD_THIS s.status_reg0 |= 0x50;
+ }
+ else if (BX_FD_THIS s.media_present[drive] == 0) {
+ BX_FD_THIS s.status_reg0 |= 0x40;
+ BX_FD_THIS s.status_reg1 = 0x25;
+ BX_FD_THIS s.status_reg2 = 0x31;
+ }
+
+ /* reset changeline */
+ if (drive > 1) return;
+ if (BX_FD_THIS s.media_present[drive])
+ BX_FD_THIS s.DIR[drive] &= ~0x80; // clear disk change line
+
+ enter_idle_phase();
+ raise_interrupt();
+ break;
+
+ case 0x4a: /* read ID */
+ enter_result_phase();
+ break;
+
+ case 0xfe: // (contrived) RESET
+ theFloppyController->reset(BX_RESET_SOFTWARE);
+ BX_FD_THIS s.pending_command = 0;
+ BX_FD_THIS s.status_reg0 = 0xc0;
+ raise_interrupt();
+ BX_FD_THIS s.reset_sensei = 4;
+ break;
+
+ case 0x00: // nothing pending?
+ break;
+
+ default:
+ BX_PANIC(("floppy:timer(): unknown case %02x",
+ (unsigned) BX_FD_THIS s.pending_command));
+ }
+ return;
+}
+
+ void
+bx_floppy_ctrl_c::dma_write(Bit8u *data_byte)
+{
+ // A DMA write is from I/O to Memory
+ // We need to return then next data byte from the floppy buffer
+ // to be transfered via the DMA to memory. (read block from floppy)
+
+
+ *data_byte = BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++];
+
+ if (BX_FD_THIS s.floppy_buffer_index >= 512) {
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+ increment_sector(); // increment to next sector before retrieving next one
+ BX_FD_THIS s.floppy_buffer_index = 0;
+ if (DEV_dma_get_tc()) { // Terminal Count line, done
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
+ BX_FD_THIS s.status_reg1 = 0;
+ BX_FD_THIS s.status_reg2 = 0;
+
+ if (bx_dbg.floppy) {
+ BX_INFO(("<<READ DONE>>"));
+ BX_INFO(("AFTER"));
+ BX_INFO((" drive = %u", (unsigned) drive));
+ BX_INFO((" head = %u", (unsigned) BX_FD_THIS s.head[drive]));
+ BX_INFO((" cylinder = %u", (unsigned) BX_FD_THIS s.cylinder[drive]));
+ BX_INFO((" sector = %u", (unsigned) BX_FD_THIS s.sector[drive]));
+ }
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_result_phase();
+ }
+ else { // more data to transfer
+ Bit32u logical_sector;
+
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads *
+ BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.head[drive] *
+ BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.sector[drive] - 1);
+
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, FROM_FLOPPY);
+ }
+ }
+}
+
+ void
+bx_floppy_ctrl_c::dma_read(Bit8u *data_byte)
+{
+ // A DMA read is from Memory to I/O
+ // We need to write the data_byte which was already transfered from memory
+ // via DMA to I/O (write block to floppy)
+
+ Bit8u drive;
+ Bit32u logical_sector;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+ if (BX_FD_THIS s.pending_command == 0x4d) { // format track in progress
+ --BX_FD_THIS s.format_count;
+ switch (3 - (BX_FD_THIS s.format_count & 0x03)) {
+ case 0:
+ BX_FD_THIS s.cylinder[drive] = *data_byte;
+ break;
+ case 1:
+ if (*data_byte != BX_FD_THIS s.head[drive])
+ BX_ERROR(("head number does not match head field"));
+ break;
+ case 2:
+ BX_FD_THIS s.sector[drive] = *data_byte;
+ break;
+ case 3:
+ if (*data_byte != 2) BX_ERROR(("dma_read: sector size %d not supported", 128<<(*data_byte)));
+ BX_DEBUG(("formatting cylinder %u head %u sector %u",
+ BX_FD_THIS s.cylinder[drive], BX_FD_THIS s.head[drive],
+ BX_FD_THIS s.sector[drive]));
+ for (unsigned i = 0; i < 512; i++) {
+ BX_FD_THIS s.floppy_buffer[i] = BX_FD_THIS s.format_fillbyte;
+ }
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.sector[drive] - 1);
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, TO_FLOPPY);
+ break;
+ }
+ if ((BX_FD_THIS s.format_count == 0) || (DEV_dma_get_tc())) {
+ BX_FD_THIS s.format_count = 0;
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_result_phase();
+ }
+ return;
+ }
+
+ BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++] = *data_byte;
+
+ if (BX_FD_THIS s.floppy_buffer_index >= 512) {
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.sector[drive] - 1);
+ if ( BX_FD_THIS s.media[drive].write_protected ) {
+ // write protected error
+ BX_INFO(("tried to write disk %u, which is write-protected", drive));
+ // ST0: IC1,0=01 (abnormal termination: started execution but failed)
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
+ // ST1: DataError=1, NDAT=1, NotWritable=1, NID=1
+ BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111
+ // ST2: CRCE=1, SERR=1, BCYL=1, NDAM=1.
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, TO_FLOPPY);
+ increment_sector(); // increment to next sector after writing current one
+ BX_FD_THIS s.floppy_buffer_index = 0;
+ if (DEV_dma_get_tc()) { // Terminal Count line, done
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
+ BX_FD_THIS s.status_reg1 = 0;
+ BX_FD_THIS s.status_reg2 = 0;
+
+ if (bx_dbg.floppy) {
+ BX_INFO(("<<WRITE DONE>>"));
+ BX_INFO(("AFTER"));
+ BX_INFO((" drive = %u", (unsigned) drive));
+ BX_INFO((" head = %u", (unsigned) BX_FD_THIS s.head[drive]));
+ BX_INFO((" cylinder = %u", (unsigned) BX_FD_THIS s.cylinder[drive]));
+ BX_INFO((" sector = %u", (unsigned) BX_FD_THIS s.sector[drive]));
+ }
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_result_phase();
+ }
+ else { // more data to transfer
+ } // else
+ } // if BX_FD_THIS s.floppy_buffer_index >= 512
+}
+
+ void
+bx_floppy_ctrl_c::raise_interrupt(void)
+{
+ DEV_pic_raise_irq(6);
+ BX_FD_THIS s.pending_irq = 1;
+ BX_FD_THIS s.reset_sensei = 0;
+}
+
+
+ void
+bx_floppy_ctrl_c::increment_sector(void)
+{
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+
+ // values after completion of data xfer
+ // ??? calculation depends on base_count being multiple of 512
+ BX_FD_THIS s.sector[drive] ++;
+ if (BX_FD_THIS s.sector[drive] > BX_FD_THIS s.media[drive].sectors_per_track) {
+ BX_FD_THIS s.sector[drive] = 1;
+ if (BX_FD_THIS s.multi_track) {
+ BX_FD_THIS s.head[drive] ++;
+ if (BX_FD_THIS s.head[drive] > 1) {
+ BX_FD_THIS s.head[drive] = 0;
+ BX_FD_THIS s.cylinder[drive] ++;
+ }
+ }
+ else {
+ BX_FD_THIS s.cylinder[drive] ++;
+ }
+ if (BX_FD_THIS s.cylinder[drive] >= BX_FD_THIS s.media[drive].tracks) {
+ // Set to 1 past last possible cylinder value.
+ // I notice if I set it to tracks-1, prama linux won't boot.
+ BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.media[drive].tracks;
+ BX_INFO(("increment_sector: clamping cylinder to max"));
+ }
+ }
+}
+
+ unsigned
+bx_floppy_ctrl_c::set_media_status(unsigned drive, unsigned status)
+{
+ char *path;
+ unsigned type;
+
+ if (drive == 0)
+ type = bx_options.floppya.Otype->get ();
+ else
+ type = bx_options.floppyb.Otype->get ();
+
+ // if setting to the current value, nothing to do
+ if ((status == BX_FD_THIS s.media_present[drive]) &&
+ ((status == 0) || (type == BX_FD_THIS s.media[drive].type)))
+ return(status);
+
+ if (status == 0) {
+ // eject floppy
+ if (BX_FD_THIS s.media[drive].fd >= 0) {
+ close( BX_FD_THIS s.media[drive].fd );
+ BX_FD_THIS s.media[drive].fd = -1;
+ }
+ BX_FD_THIS s.media_present[drive] = 0;
+ if (drive == 0) {
+ bx_options.floppya.Ostatus->set(BX_EJECTED);
+ } else {
+ bx_options.floppyb.Ostatus->set(BX_EJECTED);
+ }
+ BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line
+ return(0);
+ }
+ else {
+ // insert floppy
+ if (drive == 0) {
+ path = bx_options.floppya.Opath->getptr ();
+ }
+ else {
+ path = bx_options.floppyb.Opath->getptr ();
+ }
+ if (!strcmp(path, "none"))
+ return(0);
+ if (evaluate_media(type, path, & BX_FD_THIS s.media[drive])) {
+ BX_FD_THIS s.media_present[drive] = 1;
+ if (drive == 0) {
+#define MED (BX_FD_THIS s.media[0])
+ BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppya.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ bx_options.floppya.Ostatus->set(BX_INSERTED);
+ } else {
+#define MED (BX_FD_THIS s.media[1])
+ BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppyb.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ bx_options.floppyb.Ostatus->set(BX_INSERTED);
+ }
+ BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line
+ return(1);
+ }
+ else {
+ BX_FD_THIS s.media_present[drive] = 0;
+ if (drive == 0) {
+ bx_options.floppya.Ostatus->set(BX_EJECTED);
+ } else {
+ bx_options.floppyb.Ostatus->set(BX_EJECTED);
+ }
+ return(0);
+ }
+ }
+}
+
+ unsigned
+bx_floppy_ctrl_c::get_media_status(unsigned drive)
+{
+ return( BX_FD_THIS s.media_present[drive] );
+}
+
+#ifdef O_BINARY
+#define BX_RDONLY O_RDONLY | O_BINARY
+#define BX_RDWR O_RDWR | O_BINARY
+#else
+#define BX_RDONLY O_RDONLY
+#define BX_RDWR O_RDWR
+#endif
+
+ bx_bool
+bx_floppy_ctrl_c::evaluate_media(unsigned type, char *path, floppy_t *media)
+{
+ struct stat stat_buf;
+ int i, ret;
+ int idx = -1;
+#ifdef __linux__
+ struct floppy_struct floppy_geom;
+#endif
+#ifdef WIN32
+ char sTemp[1024];
+ bx_bool raw_floppy = 0;
+ HANDLE hFile;
+ DWORD bytes;
+ DISK_GEOMETRY dg;
+ unsigned tracks, heads, spt;
+#endif
+
+ if (type == BX_FLOPPY_NONE)
+ return(0);
+
+ //If media file is already open, close it before reopening.
+ if(media->fd >=0) {
+ close(media->fd);
+ media->fd=-1;
+ }
+
+ // open media file (image file or device)
+ media->write_protected = 0;
+#ifdef macintosh
+ media->fd = 0;
+ if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+#endif
+#ifdef WIN32
+ if ( (isalpha(path[0])) && (path[1] == ':') && (strlen(path) == 2) ) {
+ raw_floppy = 1;
+ wsprintf(sTemp, "\\\\.\\%s", path);
+ hFile = CreateFile(sTemp, GENERIC_READ, FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ BX_ERROR(("Cannot open floppy drive"));
+ return(0);
+ } else {
+ if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg), &bytes, NULL)) {
+ BX_ERROR(("No media in floppy drive"));
+ CloseHandle(hFile);
+ return(0);
+ } else {
+ tracks = (unsigned)dg.Cylinders.QuadPart;
+ heads = (unsigned)dg.TracksPerCylinder;
+ spt = (unsigned)dg.SectorsPerTrack;
+ }
+ CloseHandle(hFile);
+ }
+ media->fd = open(sTemp, BX_RDWR);
+ } else {
+ media->fd = open(path, BX_RDWR);
+ }
+#else
+ media->fd = open(path, BX_RDWR);
+#endif
+
+ if (media->fd < 0) {
+ BX_INFO(( "tried to open '%s' read/write: %s",path,strerror(errno) ));
+ // try opening the file read-only
+ media->write_protected = 1;
+#ifdef macintosh
+ media->fd = 0;
+ if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+#endif
+#ifdef WIN32
+ if (raw_floppy == 1) {
+ media->fd = open(sTemp, BX_RDONLY);
+ } else {
+ media->fd = open(path, BX_RDONLY);
+ }
+#else
+ media->fd = open(path, BX_RDONLY);
+#endif
+ if (media->fd < 0) {
+ // failed to open read-only too
+ BX_INFO(( "tried to open '%s' read only: %s",path,strerror(errno) ));
+ media->type = type;
+ return(0);
+ }
+ }
+
+#if BX_WITH_MACOS
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ ret = fd_stat(&stat_buf);
+ else
+ ret = fstat(media->fd, &stat_buf);
+#elif defined(WIN32)
+ if (raw_floppy) {
+ memset (&stat_buf, 0, sizeof(stat_buf));
+ stat_buf.st_mode = S_IFCHR;
+ ret = 0;
+ } else {
+ ret = fstat(media->fd, &stat_buf);
+ }
+#else
+ // unix
+ ret = fstat(media->fd, &stat_buf);
+#endif
+ if (ret) {
+ BX_PANIC(("fstat floppy 0 drive image file returns error: %s", strerror(errno)));
+ return(0);
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (type == floppy_type[i].id) idx = i;
+ }
+ if (idx == -1 ) {
+ BX_PANIC(("evaluate_media: unknown media type"));
+ return(0);
+ }
+ if ( S_ISREG(stat_buf.st_mode) ) {
+ // regular file
+ switch (type) {
+ // use CMOS reserved types
+ case BX_FLOPPY_160K: // 160K 5.25"
+ case BX_FLOPPY_180K: // 180K 5.25"
+ case BX_FLOPPY_320K: // 320K 5.25"
+ // standard floppy types
+ case BX_FLOPPY_360K: // 360K 5.25"
+ case BX_FLOPPY_720K: // 720K 3.5"
+ case BX_FLOPPY_1_2: // 1.2M 5.25"
+ case BX_FLOPPY_2_88: // 2.88M 3.5"
+ media->type = type;
+ media->tracks = floppy_type[idx].trk;
+ media->heads = floppy_type[idx].hd;
+ media->sectors_per_track = floppy_type[idx].spt;
+ media->sectors = floppy_type[idx].sectors;
+ if (stat_buf.st_size > (media->sectors * 512)) {
+ BX_INFO(("evaluate_media: size of file '%s' (%lu) too large for selected type",
+ path, (unsigned long) stat_buf.st_size));
+ return(0);
+ }
+ break;
+ default: // 1.44M 3.5"
+ media->type = type;
+ if (stat_buf.st_size <= 1474560) {
+ media->tracks = floppy_type[idx].trk;
+ media->heads = floppy_type[idx].hd;
+ media->sectors_per_track = floppy_type[idx].spt;
+ }
+ else if (stat_buf.st_size == 1720320) {
+ media->sectors_per_track = 21;
+ media->tracks = 80;
+ media->heads = 2;
+ }
+ else if (stat_buf.st_size == 1763328) {
+ media->sectors_per_track = 21;
+ media->tracks = 82;
+ media->heads = 2;
+ }
+ else {
+ BX_INFO(("evaluate_media: file '%s' of unknown size %lu",
+ path, (unsigned long) stat_buf.st_size));
+ return(0);
+ }
+ media->sectors = media->heads * media->tracks * media->sectors_per_track;
+ }
+ return(1); // success
+ }
+
+ else if ( S_ISCHR(stat_buf.st_mode)
+#if BX_WITH_MACOS == 0
+#ifdef S_ISBLK
+ || S_ISBLK(stat_buf.st_mode)
+#endif
+#endif
+ ) {
+ // character or block device
+ // assume media is formatted to typical geometry for drive
+ media->type = type;
+#ifdef __linux__
+ if (ioctl(media->fd, FDGETPRM, &floppy_geom) < 0) {
+ BX_ERROR(("cannot determine media geometry"));
+ return(0);
+ }
+ media->tracks = floppy_geom.track;
+ media->heads = floppy_geom.head;
+ media->sectors_per_track = floppy_geom.sect;
+ media->sectors = floppy_geom.size;
+#elif defined(WIN32)
+ media->tracks = tracks;
+ media->heads = heads;
+ media->sectors_per_track = spt;
+ media->sectors = media->heads * media->tracks * media->sectors_per_track;
+#else
+ media->tracks = floppy_type[idx].trk;
+ media->heads = floppy_type[idx].hd;
+ media->sectors_per_track = floppy_type[idx].spt;
+ media->sectors = floppy_type[idx].sectors;
+#endif
+ return(1); // success
+ }
+ else {
+ // unknown file type
+ BX_INFO(("unknown mode type"));
+ return(0);
+ }
+}
+
+
+void
+bx_floppy_ctrl_c::enter_result_phase(void)
+{
+
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+
+ /* these are always the same */
+ BX_FD_THIS s.result_index = 0;
+ BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
+
+ /* invalid command */
+ if ((BX_FD_THIS s.status_reg0 & 0xc0) == 0x80) {
+ BX_FD_THIS s.result_size = 1;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
+ return;
+ }
+
+ switch (BX_FD_THIS s.pending_command) {
+ case 0x04: // get status
+ BX_FD_THIS s.result_size = 1;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg3;
+ break;
+ case 0x08: // sense interrupt
+ BX_FD_THIS s.result_size = 2;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
+ BX_FD_THIS s.result[1] = BX_FD_THIS s.cylinder[drive];
+ break;
+ case 0x4a: // read ID
+ case 0x4d: // format track
+ case 0x46: // read normal data
+ case 0x66:
+ case 0xc6:
+ case 0xe6:
+ case 0x45: // write normal data
+ case 0xc5:
+ BX_FD_THIS s.result_size = 7;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
+ BX_FD_THIS s.result[1] = BX_FD_THIS s.status_reg1;
+ BX_FD_THIS s.result[2] = BX_FD_THIS s.status_reg2;
+ BX_FD_THIS s.result[3] = BX_FD_THIS s.cylinder[drive];
+ BX_FD_THIS s.result[4] = BX_FD_THIS s.head[drive];
+ BX_FD_THIS s.result[5] = BX_FD_THIS s.sector[drive];
+ BX_FD_THIS s.result[6] = 2; /* sector size code */
+ raise_interrupt();
+ break;
+ }
+}
+
+void
+bx_floppy_ctrl_c::enter_idle_phase(void)
+{
+ BX_FD_THIS s.main_status_reg &= 0x0f; // leave drive status untouched
+ BX_FD_THIS s.main_status_reg |= FD_MS_MRQ; // data register ready
+
+ BX_FD_THIS s.command_complete = 1; /* waiting for new command */
+ BX_FD_THIS s.command_index = 0;
+ BX_FD_THIS s.command_size = 0;
+ BX_FD_THIS s.pending_command = 0;
+
+ BX_FD_THIS s.floppy_buffer_index = 0;
+}
+