Bochs/bochs/iodev/floppy.cc
Todd T.Fries bdb89cd364 merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.

In general this provides a generic interface for logging.

logfunctions:: is a class that is inherited by some classes, and also
.   allocated as a standalone global called 'genlog'.  All logging uses
.   one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
.   class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
.   respectively.
.
.   An example usage:
.     BX_INFO(("Hello, World!\n"));

iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance.  It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf().  At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.

More cleanup is coming, but this works for now.  If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.

Comments, bugs, flames, to me: todd@fries.net
2001-05-15 14:49:57 +00:00

1298 lines
41 KiB
C++

// Copyright (C) 2001 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
extern "C" {
#include <errno.h>
}
#include "bochs.h"
#define LOG_THIS bx_floppy.
bx_floppy_ctrl_c bx_floppy;
#if BX_USE_FD_SMF
#define this (&bx_floppy)
#endif
/* 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
bx_floppy_ctrl_c::bx_floppy_ctrl_c(void)
{
setprefix("[FDD ]");
settype(FDLOG);
BX_DEBUG(("Init.\n"));
}
bx_floppy_ctrl_c::~bx_floppy_ctrl_c(void)
{
// nothing for now
BX_DEBUG(("Exit.\n"));
}
void
bx_floppy_ctrl_c::init(bx_devices_c *d, bx_cmos_c *cmos)
{
BX_FD_THIS devices = d;
BX_FD_THIS devices->register_irq(6, "Floppy Drive");
for (unsigned addr=0x03F2; addr<=0x03F7; addr++) {
BX_FD_THIS devices->register_io_read_handler(this, read_handler,
addr, "Floppy Drive");
BX_FD_THIS devices->register_io_write_handler(this, write_handler,
addr, "Floppy Drive");
}
cmos->s.reg[0x10] = 0x00; /* start out with: no drive 0, no drive 1 */
BX_FD_THIS s.num_supported_floppies = 0;
//
// 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].type = BX_FLOPPY_NONE;
BX_FD_THIS s.media[0].fd = -1;
BX_FD_THIS s.media_present[0] = 0;
switch (bx_options.floppya.type) {
case BX_FLOPPY_NONE:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0x0f) | 0x00;
break;
case BX_FLOPPY_1_2:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0x0f) | 0x20;
break;
case BX_FLOPPY_720K:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0x0f) | 0x30;
break;
case BX_FLOPPY_1_44:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0x0f) | 0x40;
break;
case BX_FLOPPY_2_88:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0x0f) | 0x50;
break;
default:
BX_PANIC(( "unknown floppya type\n" ));
}
if (bx_options.floppya.type != BX_FLOPPY_NONE) {
BX_FD_THIS s.num_supported_floppies++;
if ( bx_options.floppya.initial_status == BX_INSERTED) {
if (evaluate_media(bx_options.floppya.type, bx_options.floppya.path,
& BX_FD_THIS s.media[0]))
BX_FD_THIS s.media_present[0] = 1;
}
}
//
// 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].type = BX_FLOPPY_NONE;
BX_FD_THIS s.media[1].fd = -1;
BX_FD_THIS s.media_present[1] = 0;
switch (bx_options.floppyb.type) {
case BX_FLOPPY_NONE:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0xf0) | 0x00;
break;
case BX_FLOPPY_1_2:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0xf0) | 0x02;
break;
case BX_FLOPPY_720K:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0xf0) | 0x03;
break;
case BX_FLOPPY_1_44:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0xf0) | 0x04;
break;
case BX_FLOPPY_2_88:
cmos->s.reg[0x10] = (cmos->s.reg[0x10] & 0xf0) | 0x05;
break;
default:
BX_PANIC(("unknown floppyb type\n"));
}
if (bx_options.floppyb.type != BX_FLOPPY_NONE) {
BX_FD_THIS s.num_supported_floppies++;
if ( bx_options.floppyb.initial_status == BX_INSERTED) {
if (evaluate_media(bx_options.floppyb.type, bx_options.floppyb.path,
& BX_FD_THIS s.media[1]))
BX_FD_THIS s.media_present[1] = 1;
}
}
/* CMOS Equipment Byte register */
if (BX_FD_THIS s.num_supported_floppies > 0)
cmos->s.reg[0x14] = (cmos->s.reg[0x14] & 0x3e) |
((BX_FD_THIS s.num_supported_floppies-1) << 6) |
1;
else
cmos->s.reg[0x14] = (cmos->s.reg[0x14] & 0x3e);
BX_FD_THIS s.floppy_timer_index =
bx_pc_system.register_timer( this, timer_handler,
bx_options.floppy_command_delay, 0,0);
BX_INFO(("bx_options.floppy_command_delay = %u\n",
(unsigned) bx_options.floppy_command_delay));
}
void
bx_floppy_ctrl_c::reset(unsigned source)
{
Bit32u i;
BX_FD_THIS s.data_rate = 0; /* 500 Kbps */
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.result_index = 0;
BX_FD_THIS s.result_size = 0;
/* data register ready, not in DMA mode */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ;
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 (source == BX_RESET_HARDWARE) {
BX_FD_THIS s.DOR = 0x0c;
// motor off, drive 3..0
// DMA/INT enabled
// normal operation
// drive select 0
// DIR affected only by hard reset
BX_FD_THIS s.DIR |= 0x80; // disk changed
}
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;
}
BX_FD_THIS s.floppy_buffer_index = 0;
}
// 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 (io_len > 1)
BX_PANIC(("io read from address %08x, len=%u\n",
(unsigned) address, (unsigned) io_len));
// ???
//if (bx_cpu.cs.selector.value != 0xf000) {
// BX_INFO(("BIOS: floppy: read access to port %04x\n", (unsigned) address);
// }
if (bx_dbg.floppy)
BX_INFO(("read access to port %04x\n", (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_PANIC(("diskette controller:port3f5: no results to read\n"));
}
value = BX_FD_THIS s.result[BX_FD_THIS s.result_index++];
if (BX_FD_THIS s.result_index >= BX_FD_THIS s.result_size) {
BX_FD_THIS s.result_size = 0;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ;
}
return(value);
break;
#endif // #if BX_DMA_FLOPPY_IO
case 0x3F6: // Reserved for future floppy controllers
// This address shared with the hard drive controller
value = BX_FD_THIS devices->hard_drive->read_handler(BX_FD_THIS devices->hard_drive, 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 = BX_FD_THIS devices->hard_drive->read_handler(BX_FD_THIS devices->hard_drive, address, io_len);
value &= 0x7f;
// add in diskette change line
value |= (BX_FD_THIS s.DIR & 0x80);
return( value );
break;
}
#if BX_DMA_FLOPPY_IO
BX_PANIC(("io_read: bailing\n"));
return(0);
#endif // #if BX_DMA_FLOPPY_IO
}
// 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 (io_len > 1)
BX_PANIC(("io write to address %08x, len=%u\n",
(unsigned) address, (unsigned) io_len));
// ???
//if (bx_cpu.cs.selector.value != 0xf000) {
// BX_INFO(("BIOS: floppy: write access to port %04x, value=%02x\n",
// (unsigned) address, (unsigned) value));
// }
if (bx_dbg.floppy)
BX_INFO(("write access to port %04x, value=%02x\n",
(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_INFO(("DMA and interrupt capabilities disabled\n"));
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
#if 0
BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
BX_FD_THIS s.pending_command = 0xfe; // RESET pending
bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
bx_options.floppy_command_delay, 0 );
#endif
}
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_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
bx_options.floppy_command_delay, 0 );
}
if (bx_dbg.floppy) {
BX_INFO(("io_write: digital output register\n"));
BX_INFO((" motor on, drive1 = %d\n", motor_on_drive1 > 0));
BX_INFO((" motor on, drive0 = %d\n", motor_on_drive0 > 0));
BX_INFO((" dma_and_interrupt_enable=%02x\n",
(unsigned) dma_and_interrupt_enable));
BX_INFO((" normal_operation=%02x\n",
(unsigned) normal_operation));
BX_INFO((" drive_select=%02x\n",
(unsigned) drive_select));
}
if (drive_select>1) {
BX_PANIC(("io_write: drive_select>1\n"));
}
break;
case 0x3f4: /* diskette controller data rate select register */
BX_PANIC(("io_write: data rate select register\n"));
break;
case 0x3F5: /* diskette controller data */
if (bx_dbg.floppy)
BX_INFO(("command = %02x\n", (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\n",
(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;
floppy_command();
BX_FD_THIS s.command_complete = 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 0xc5: /* write normal data */
BX_FD_THIS s.command_size = 9;
break;
case 0xe6: /* read normal data */
BX_FD_THIS s.command_size = 9;
break;
case 0x13: // Configure command (Enhanced)
BX_FD_THIS s.command_size = 3;
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_FD_THIS s.command_size = 1;
BX_FD_THIS s.result[0] = 0x80;
BX_FD_THIS s.result_size = 1;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.command_complete = 1;
break;
default:
BX_PANIC(("io write:3f5: unsupported case 0x%02x\n",
(unsigned) value));
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;
}
}
if (bx_dbg.floppy)
BX_INFO(("io_write: diskette controller data\n"));
return;
break;
#endif // #if BX_DMA_FLOPPY_IO
case 0x3F6: /* diskette controller (reserved) */
if (bx_dbg.floppy)
BX_INFO(("io_write: reserved register unsupported\n"));
// this address shared with the hard drive controller
BX_FD_THIS devices->hard_drive->write_handler(BX_FD_THIS devices->hard_drive, address, value, io_len);
break;
#if BX_DMA_FLOPPY_IO
case 0x3F7: /* diskette controller configuration control register */
if (bx_dbg.floppy)
BX_INFO(("io_write: config control register\n"));
BX_FD_THIS s.data_rate = value & 0x03;
if (bx_dbg.floppy)
switch (BX_FD_THIS s.data_rate) {
case 0: BX_INFO((" 500 Kbps\n")); break;
case 1: BX_INFO((" 300 Kbps\n")); break;
case 2: BX_INFO((" 250 Kbps\n")); break;
case 3: BX_INFO((" 1 Mbps\n")); break;
}
return;
break;
default:
BX_PANIC(("io_write: unknown port %04h\n", (unsigned) address));
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\n"));
#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;
if (bx_dbg.floppy) {
BX_INFO(("FLOPPY COMMAND: "));
for (i=0; i<BX_FD_THIS s.command_size; i++)
BX_INFO(("[%02x] ", (unsigned) BX_FD_THIS s.command[i]));
BX_INFO(("\n"));
}
#if 0
/* execute phase of command is in progress (non DMA mode) */
BX_FD_THIS s.main_status_reg |= 20;
#endif
switch (BX_FD_THIS s.command[0]) {
case 0x03: // specify
//BX_INFO(("floppy_command specify\n"));
// 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;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ;
return;
break;
case 0x04: // get status
BX_FD_THIS s.result[0] = 0x00;
BX_FD_THIS s.result_size = 1;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
return;
break;
case 0x07: // recalibrate
//BX_INFO(("floppy_command recalibrate\n"));
drive = (BX_FD_THIS s.command[1] & 0x03);
BX_FD_THIS s.DOR &= 0xfc;
BX_FD_THIS s.DOR |= drive;
if (bx_dbg.floppy)
BX_INFO(("floppy_command(): recalibrate drive %u\n",
(unsigned) drive));
if (drive > 1)
BX_PANIC(("floppy_command(): drive > 1\n"));
//motor_on = BX_FD_THIS s.DOR & 0xf0;
motor_on = ( (BX_FD_THIS s.DOR>>(drive+4))
& 0x01 );
if (motor_on == 0) {
BX_INFO(("floppy_command(): recal drive with motor off\n"));
}
if (drive==0)
BX_FD_THIS s.DOR |= 0x10; // turn on MOTA
else
BX_FD_THIS s.DOR |= 0x20; // turn on MOTB
BX_FD_THIS s.cylinder[drive] = 0;
bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
bx_options.floppy_command_delay, 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
*/
/* data reg not ready, controller busy */
BX_FD_THIS s.main_status_reg = FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.pending_command = 0x07; // recalibrate pending
return;
break;
case 0x08: /* sense interrupt status */
//BX_INFO(("floppy_command sense interrupt status\n"));
/* execution:
* get status
* result:
* no interupt
* byte0 = status reg0
* byte1 = current cylinder number (0 to 79)
*/
/*BX_FD_THIS s.status_reg0 = ;*/
drive = BX_FD_THIS s.DOR & 0x03;
BX_FD_THIS s.result[0] = 0x20 | drive;
BX_FD_THIS s.result[1] = BX_FD_THIS s.cylinder[drive];
BX_FD_THIS s.result_size = 2;
BX_FD_THIS s.result_index = 0;
/* read ready */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
if (bx_dbg.floppy)
BX_INFO(("sense interrupt status\n"));
return;
break;
case 0x0f: /* seek */
//BX_INFO(("floppy_command seek\n"));
/* 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];
if (drive > 1)
BX_PANIC(("floppy_command(): seek: drive>1\n"));
/* ??? should also check cylinder validity */
bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
bx_options.floppy_command_delay, 0 );
/* data reg not ready, controller busy */
BX_FD_THIS s.main_status_reg = FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.pending_command = 0x0f; /* seek pending */
return;
break;
case 0x13: // Configure
BX_INFO(("floppy io: configure (mode=%02xh, pretrack=%02xh)\n",
(unsigned)(BX_FD_THIS s.command[2]), (unsigned)(BX_FD_THIS s.command[3]) ));
BX_FD_THIS s.result_size = 0;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.pending_command = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
return;
break;
case 0x4a: // read ID
//BX_INFO(("floppy_command read ID\n")); // ???
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(): 4a: motor not on\n"));
if (drive > 1)
BX_PANIC(("floppy io: 4a: bad drive #\n"));
BX_FD_THIS s.result_size = 7;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.result[0] = 0; /* ??? */
BX_FD_THIS s.result[1] = 0;
BX_FD_THIS s.result[2] = 0;
BX_FD_THIS s.result[3] = BX_FD_THIS s.cylinder[drive];
BX_FD_THIS s.result[4] = 0; /* head */
BX_FD_THIS s.result[5] = 0; /* sector at completion */
BX_FD_THIS s.result[6] = 2;
bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
bx_options.floppy_command_delay, 0 );
/* data reg not ready, controller busy */
BX_FD_THIS s.main_status_reg = FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.pending_command = 0x4a; /* read ID pending */
return;
break;
case 0xe6: // read normal data
//BX_INFO(("floppy_command read normal data\n"));
case 0xc5: // write normal data
//BX_INFO(("floppy_command write normal data\n"));
if ( (BX_FD_THIS s.DOR & 0x08) == 0 )
BX_PANIC(("read/write command with DMA and int disabled\n"));
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\n"));
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];
if (bx_dbg.floppy) {
BX_INFO(("\n\nread/write normal data\n"));
BX_INFO(("BEFORE\n"));
BX_INFO((" drive = %u\n", (unsigned) drive));
BX_INFO((" head = %u\n", (unsigned) head));
BX_INFO((" cylinder = %u\n", (unsigned) cylinder));
BX_INFO((" sector = %u\n", (unsigned) sector));
BX_INFO((" eot = %u\n", (unsigned) eot));
}
if (drive > 1)
BX_PANIC(("floppy io: bad drive #\n"));
if (head > 1)
BX_PANIC(("floppy io: bad head #\n"));
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\n", (unsigned) sector,
(unsigned) BX_FD_THIS s.media[drive].sectors_per_track));
BX_FD_THIS s.result_size = 7;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.result[0] = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
BX_FD_THIS s.result[1] = 0x25; // 0010 0101
BX_FD_THIS s.result[2] = 0x31; // 0011 0001
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 = 512
BX_FD_THIS s.pending_command = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS devices->pic->trigger_irq(6);
return;
}
if (sector_size != 0x02) { // 512 bytes
BX_PANIC(("sector_size not 512\n"));
}
if ( cylinder >= BX_FD_THIS s.media[drive].tracks ) {
BX_INFO(("\nfloppy io: normal read/write: params out of range\n"));
BX_INFO(("*** sector # %02xh\n", (unsigned) sector));
BX_INFO(("*** cylinder #%02xh\n", (unsigned) cylinder));
BX_INFO(("*** eot #%02xh\n", (unsigned) eot));
BX_INFO(("*** head #%02xh\n", (unsigned) head));
BX_PANIC(("bailing\n"));
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\n", (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;
BX_FD_THIS s.result_size = 7;
BX_FD_THIS s.result_index = 0;
// 0100 0HDD abnormal termination
BX_FD_THIS s.result[0] = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
// 1000 0101 end of cyl/NDAT/NID
BX_FD_THIS s.result[1] = 0x86;
// 0000 0000
BX_FD_THIS s.result[2] = 0x00;
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 = 512
bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
bx_options.floppy_command_delay, 0 );
/* data reg not ready, controller busy */
BX_FD_THIS s.main_status_reg = FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0];
return;
}
#if 0
if (eot != BX_FD_THIS s.media[drive].sectors_per_track)
BX_INFO(("floppy io: bad eot #%02xh\n", (unsigned) eot);
#endif
if (cylinder != BX_FD_THIS s.cylinder[drive])
BX_INFO(("floppy io: cylinder request != current cylinder\n"));
logical_sector = (cylinder * 2 * 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(("floppy io: logical sector out of bounds\n"));
}
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] == 0xe6) { // read
floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
512, FROM_FLOPPY);
BX_FD_THIS s.floppy_buffer_index = 0;
bx_pc_system.set_DRQ(FLOPPY_DMA_CHAN, 1);
/* data reg not ready, controller busy */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0];
return;
}
else if (BX_FD_THIS s.command[0] == 0xc5) { // write
BX_FD_THIS s.floppy_buffer_index = 0;
bx_pc_system.set_DRQ(FLOPPY_DMA_CHAN, 1);
/* data reg not ready, controller busy */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0];
return;
}
else
BX_PANIC(("floppy_command(): unknown read/write command\n"));
return;
break;
default:
BX_PANIC(("floppy_command(): unknown function\n"));
}
BX_PANIC(("\nfloppy_command()\n"));
#endif
}
void
bx_floppy_ctrl_c::floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer,
Bit32u bytes, Bit8u direction)
{
int ret;
if (drive > 1)
BX_PANIC(("floppy_xfer: drive > 1\n"));
if (bx_dbg.floppy) {
BX_INFO(("drive=%u\n", (unsigned) drive));
BX_INFO(("offset=%u\n", (unsigned) offset));
BX_INFO(("bytes=%u\n", (unsigned) bytes));
BX_INFO(("direction=%s\n", (direction==FROM_FLOPPY)? "from" : "to"));
}
#ifdef macintosh
if (strcmp(bx_options.floppya.path, 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\n"));
}
}
if (direction == FROM_FLOPPY) {
#ifdef macintosh
if (!strcmp(bx_options.floppya.path, 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\n",
(unsigned) ret, (unsigned) bytes));
memset(buffer + ret, 0, bytes - ret);
}
else {
BX_INFO(("read() on floppy image returns 0\n"));
memset(buffer, 0, bytes);
}
}
}
else { // TO_FLOPPY
#ifdef macintosh
if (!strcmp(bx_options.floppya.path, 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\n"));
}
}
}
void
bx_floppy_ctrl_c::timer_handler(void *this_ptr)
{
#if defined(SIMX86)
printf("Floppy timer\n");
#endif
bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
class_ptr->timer();
}
void
bx_floppy_ctrl_c::timer()
{
switch ( BX_FD_THIS s.pending_command ) {
case 0x07: // recal
BX_FD_THIS s.pending_command = 0;
/* write ready, not busy */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ;
BX_FD_THIS devices->pic->trigger_irq(6);
goto reset_changeline;
break;
case 0x0f: // seek
BX_FD_THIS s.pending_command = 0;
/* write ready, not busy */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ;
BX_FD_THIS devices->pic->trigger_irq(6);
goto reset_changeline;
break;
case 0x4a: /* read ID */
case 0xc5: // write normal data
case 0xe6: // read normal data
BX_FD_THIS s.pending_command = 0;
/* read ready, busy */
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO;
BX_FD_THIS devices->pic->trigger_irq(6);
break;
case 0xfe: // (contrived) RESET
reset(BX_RESET_SOFTWARE);
BX_FD_THIS s.pending_command = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ;
BX_FD_THIS devices->pic->trigger_irq(6);
break;
default:
BX_PANIC(("floppy:timer(): unknown case %02x\n",
(unsigned) BX_FD_THIS s.pending_command));
}
return;
reset_changeline:
unsigned drive = BX_FD_THIS s.DOR & 0x3;
if (drive > 1) return;
if (BX_FD_THIS s.media_present[drive])
BX_FD_THIS s.DIR &= ~0x80; // clear disk change line
}
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 (bx_pc_system.TC) { // Terminal Count line, done
BX_FD_THIS s.pending_command = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.result_size = 7;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.result[0] = (BX_FD_THIS s.head[drive] << 2) | drive;
BX_FD_THIS s.result[1] = 0;
BX_FD_THIS s.result[2] = 0;
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;
if (bx_dbg.floppy) {
BX_INFO(("<<READ DONE>>\n"));
BX_INFO(("AFTER\n"));
BX_INFO((" drive = %u\n", (unsigned) drive));
BX_INFO((" head = %u\n", (unsigned) BX_FD_THIS s.head[drive]));
BX_INFO((" cylinder = %u\n", (unsigned) BX_FD_THIS s.cylinder[drive]));
BX_INFO((" sector = %u\n", (unsigned) BX_FD_THIS s.sector[drive]));
}
BX_FD_THIS devices->pic->trigger_irq(6);
bx_pc_system.set_DRQ(FLOPPY_DMA_CHAN, 0);
}
else { // more data to transfer
Bit32u logical_sector;
logical_sector = (BX_FD_THIS s.cylinder[drive] * 2 *
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;
BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++] = *data_byte;
if (BX_FD_THIS s.floppy_buffer_index >= 512) {
drive = BX_FD_THIS s.DOR & 0x03;
logical_sector = (BX_FD_THIS s.cylinder[drive] * 2 * 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);
increment_sector(); // increment to next sector after writing current one
BX_FD_THIS s.floppy_buffer_index = 0;
if (bx_pc_system.TC) { // Terminal Count line, done
BX_FD_THIS s.pending_command = 0;
BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
BX_FD_THIS s.result_size = 7;
BX_FD_THIS s.result_index = 0;
BX_FD_THIS s.result[0] = (BX_FD_THIS s.head[drive] << 2) | drive;
BX_FD_THIS s.result[1] = 0;
BX_FD_THIS s.result[2] = 0;
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;
if (bx_dbg.floppy) {
BX_INFO(("<<WRITE DONE>>\n"));
BX_INFO(("AFTER\n"));
BX_INFO((" drive = %u\n", (unsigned) drive));
BX_INFO((" head = %u\n", (unsigned) BX_FD_THIS s.head[drive]));
BX_INFO((" cylinder = %u\n", (unsigned) BX_FD_THIS s.cylinder[drive]));
BX_INFO((" sector = %u\n", (unsigned) BX_FD_THIS s.sector[drive]));
}
BX_FD_THIS devices->pic->trigger_irq(6);
bx_pc_system.set_DRQ(FLOPPY_DMA_CHAN, 0);
}
else { // more data to transfer
} // else
} // if BX_FD_THIS s.floppy_buffer_index >= 512
}
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] -= BX_FD_THIS s.media[drive].sectors_per_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] ++;
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\n"));
}
}
}
}
unsigned
bx_floppy_ctrl_c::set_media_status(unsigned drive, unsigned status)
{
char *path;
unsigned type;
// if setting to the current value, nothing to do
if (status == BX_FD_THIS s.media_present[drive])
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;
BX_FD_THIS s.DIR |= 0x80; // disk changed line
return(0);
}
else {
// insert floppy
if (drive == 0) {
path = bx_options.floppya.path;
type = bx_options.floppya.type;
}
else {
path = bx_options.floppyb.path;
type = bx_options.floppyb.type;
}
if (evaluate_media(type, path, & BX_FD_THIS s.media[drive])) {
BX_FD_THIS s.media_present[drive] = 1;
BX_FD_THIS s.DIR |= 0x80; // disk changed line
return(1);
}
else {
BX_FD_THIS s.media_present[drive] = 0;
return(0);
}
}
}
unsigned
bx_floppy_ctrl_c::get_media_status(unsigned drive)
{
return( BX_FD_THIS s.media_present[drive] );
}
Boolean
bx_floppy_ctrl_c::evaluate_media(unsigned type, char *path, floppy_t *media)
{
struct stat stat_buf;
int ret;
char sTemp[1024];
if (type == BX_FLOPPY_NONE)
return(0);
// open media file (image file or device)
#ifdef macintosh
media->fd = 0;
if (strcmp(bx_options.floppya.path, SuperDrive))
#endif
media->fd = open(path, O_RDWR
#ifdef O_BINARY
| O_BINARY
#endif
);
if (media->fd < 0) {
BX_INFO(( "floppy open of %s:\n",path,strerror(errno) ));
return(0);
}
#if BX_WITH_MACOS
if (!strcmp(bx_options.floppya.path, SuperDrive))
ret = fd_stat(&stat_buf);
else
ret = fstat(media->fd, &stat_buf);
#elif BX_WITH_WIN32
stat_buf.st_mode = S_IFCHR;
// maybe replace with code that sets ret to -1 if the disk is not available
ret = 0;
#else
// unix
ret = fstat(media->fd, &stat_buf);
#endif
if (ret) {
BX_PANIC(("fstat()'ing floppy 0 drive image file returns error!\n"));
return(0);
}
if ( S_ISREG(stat_buf.st_mode) ) {
// regular file
switch (type) {
case BX_FLOPPY_720K: // 720K 3.5"
media->type = BX_FLOPPY_720K;
media->sectors_per_track = 9;
media->tracks = 80;
media->heads = 2;
break;
case BX_FLOPPY_1_2: // 1.2M 5.25"
media->type = BX_FLOPPY_1_2;
media->sectors_per_track = 15;
media->tracks = 80;
media->heads = 2;
break;
case BX_FLOPPY_1_44: // 1.44M 3.5"
media->type = BX_FLOPPY_1_44;
if (stat_buf.st_size <= 1474560) {
media->sectors_per_track = 18;
media->tracks = 80;
media->heads = 2;
}
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\n",
path, (unsigned long) stat_buf.st_size));
return(0);
}
break;
case BX_FLOPPY_2_88: // 2.88M 3.5"
media->type = BX_FLOPPY_2_88;
media->sectors_per_track = 36;
media->tracks = 80;
media->heads = 2;
break;
default:
BX_PANIC(("evaluate_media: unknown media type\n"));
}
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
switch (type) {
case BX_FLOPPY_720K: // 720K 3.5"
media->type = BX_FLOPPY_720K;
media->sectors_per_track = 9;
media->tracks = 80;
media->heads = 2;
break;
case BX_FLOPPY_1_2: // 1.2M 5.25"
media->type = BX_FLOPPY_1_2;
media->sectors_per_track = 15;
media->tracks = 80;
media->heads = 2;
break;
case BX_FLOPPY_1_44: // 1.44M 3.5"
media->type = BX_FLOPPY_1_44;
media->sectors_per_track = 18;
media->tracks = 80;
media->heads = 2;
break;
case BX_FLOPPY_2_88: // 2.88M 3.5"
media->type = BX_FLOPPY_2_88;
media->sectors_per_track = 36;
media->tracks = 80;
media->heads = 2;
break;
default:
BX_PANIC(("evaluate_media: unknown media type\n"));
}
media->sectors = media->heads * media->tracks * media->sectors_per_track;
return(1); // success
}
else {
// unknown file type
BX_INFO(("unknown mode type\n"));
return(0);
}
}