DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/hardware/floppy.cpp
00001 /*
00002  * Floppy controller emulation for DOSBox-X
00003  * (C) 2012 Jonathan Campbell
00004 
00005  * [insert open source license here]
00006  */
00007 
00008 #if defined(_MSC_VER)
00009 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00010 #endif
00011 
00012 #include <math.h>
00013 #include <assert.h>
00014 #include "dosbox.h"
00015 #include "inout.h"
00016 #include "pic.h"
00017 #include "mem.h"
00018 #include "cpu.h"
00019 #include "dma.h"
00020 #include "ide.h"
00021 #include "mixer.h"
00022 #include "timer.h"
00023 #include "setup.h"
00024 #include "control.h"
00025 #include "callback.h"
00026 #include "bios_disk.h"
00027 
00028 #ifdef _MSC_VER
00029 # define MIN(a,b) ((a) < (b) ? (a) : (b))
00030 #else
00031 # define MIN(a,b) std::min(a,b)
00032 #endif
00033 
00034 #define MAX_FLOPPY_CONTROLLERS 1
00035 
00036 static unsigned char init_floppy = 0;
00037 
00038 class FloppyController;
00039 
00040 class FloppyDevice {
00041 public:
00042         FloppyController *controller;
00043 public:
00044         unsigned char current_track;
00045         bool select,motor;
00046         bool track0;
00047 public:
00048         int             int13_disk;
00049 public:
00050         FloppyDevice(FloppyController *c);
00051         void set_select(bool enable);   /* set selection on/off */
00052         void set_motor(bool enable);    /* set motor on/off */
00053         void motor_step(int dir);
00054         imageDisk *getImage();
00055         virtual ~FloppyDevice();
00056         double floppy_image_motor_position();
00057 };
00058 
00059 class FloppyController:public Module_base{
00060 public:
00061         int IRQ;
00062         int DMA;
00063         unsigned short base_io;
00064         unsigned char interface_index;
00065         IO_ReadHandleObject ReadHandler[8];
00066         IO_WriteHandleObject WriteHandler[8];
00067         uint8_t digital_output_register;
00068         bool int13fakev86io;            /* on certain INT 13h calls in virtual 8086 mode, trigger fake CPU I/O traps */
00069         bool instant_mode;              /* make floppy operations instantaneous if true */
00070         bool data_register_ready;       /* 0x3F4 bit 7 */
00071         bool data_read_expected;        /* 0x3F4 bit 6 (DIO) if set CPU is expected to read from the controller */
00072         bool non_dma_mode;              /* 0x3F4 bit 5 (NDMA) */
00073         bool busy_status;               /* 0x3F4 bit 4 (BUSY). By "busy" the floppy controller is in the middle of an instruction */
00074         bool positioning[4];            /* 0x3F4 bit 0-3 floppy A...D in positioning mode */
00075         bool irq_pending;
00076         bool register_pnp;
00077 /* FDC internal registers */
00078         uint8_t ST[4];                  /* ST0..ST3 */
00079         uint8_t current_cylinder[4];
00080 /* buffers */
00081         uint8_t in_cmd[16];
00082         uint8_t in_cmd_len;
00083         uint8_t in_cmd_pos;
00084         uint8_t out_res[16];
00085         uint8_t out_res_len;
00086         uint8_t out_res_pos;
00087         unsigned int motor_steps;
00088         int motor_dir;
00089         float fdc_motor_step_delay;
00090         bool in_cmd_state;
00091         bool out_res_state;
00092 public:
00093     void register_isapnp();
00094         bool dma_irq_enabled();
00095         int drive_selected();
00096         void reset_cmd();
00097         void reset_res();
00098         void reset_io();
00099         void update_ST3();
00100         uint8_t fdc_data_read();
00101         void fdc_data_write(uint8_t b);
00102         void prepare_res_phase(uint8_t len);
00103         void on_dor_change(unsigned char b);
00104         void invalid_command_code();
00105         void on_fdc_in_command();
00106         void on_reset();
00107 public:
00108         DmaChannel* dma;
00109         FloppyDevice* device[4];        /* Floppy devices */
00110 public:
00111         FloppyController(Section* configuration,unsigned char index);
00112         void install_io_port();
00113         void raise_irq();
00114         void lower_irq();
00115         ~FloppyController();
00116 };
00117 
00118 static FloppyController* floppycontroller[MAX_FLOPPY_CONTROLLERS]={NULL};
00119 
00120 bool FDC_AssignINT13Disk(unsigned char drv) {
00121         if (drv >= 2) return false;
00122         FloppyController *fdc = floppycontroller[0];
00123         if (fdc == NULL) return false;
00124         FloppyDevice *dev = fdc->device[drv];
00125 
00126         if (dev != NULL) {
00127                 /* sorry. move it aside */
00128                 delete dev;
00129                 fdc->device[drv] = NULL;
00130         }
00131 
00132         dev = fdc->device[drv] = new FloppyDevice(fdc);
00133         if (dev == NULL) return false;
00134         dev->int13_disk = drv;
00135         dev->set_select(fdc->drive_selected() == drv);
00136 
00137         LOG_MSG("FDC: Primary controller, drive %u assigned to INT 13h drive %u",drv,drv);
00138         return true;
00139 }
00140 
00141 bool FDC_UnassignINT13Disk(unsigned char drv) {
00142         if (drv >= 2) return false;
00143         FloppyController *fdc = floppycontroller[0];
00144         if (fdc == NULL) return false;
00145         FloppyDevice *dev = fdc->device[drv];
00146 
00147         if (dev != NULL) {
00148                 /* sorry. move it aside */
00149                 delete dev;
00150                 fdc->device[drv] = NULL;
00151         }
00152 
00153         LOG_MSG("FDC: Primary controller, drive %u unassigned from INT 13h drive %u",drv,drv);
00154         return true;
00155 }
00156 
00157 static void fdc_baseio_w(Bitu port,Bitu val,Bitu iolen);
00158 static Bitu fdc_baseio_r(Bitu port,Bitu iolen);
00159 
00160 void FDC_MotorStep(Bitu idx/*which IDE controller*/) {
00161         FloppyController *fdc;
00162         FloppyDevice *dev;
00163         int devidx;
00164 
00165         if (idx >= MAX_FLOPPY_CONTROLLERS) return;
00166         fdc = floppycontroller[idx];
00167         if (fdc == NULL) return;
00168 
00169         devidx = fdc->drive_selected()&3;
00170         dev = fdc->device[devidx];
00171 
00172 #if 0
00173         LOG_MSG("FDC: motor step. if=%u dev=%u rem=%u dir=%d current=%u\n",
00174                 idx,devidx,fdc->motor_steps,fdc->motor_dir,fdc->current_cylinder[devidx]);
00175 #endif
00176 
00177         if (dev != NULL && dev->track0 && fdc->motor_dir < 0) {
00178                 LOG_MSG("FDC: motor step abort. floppy drive signalling track0\n");
00179                 fdc->motor_steps = 0;
00180                 fdc->current_cylinder[devidx] = 0;
00181         }
00182 
00183         if (fdc->motor_steps > 0) {
00184                 fdc->motor_steps--;
00185                 fdc->current_cylinder[devidx] += fdc->motor_dir;
00186                 if (fdc->current_cylinder[devidx] <= 0) {
00187                         fdc->current_cylinder[devidx] = 0;
00188                         fdc->motor_steps = 0;
00189                 }
00190 /* NTS: fdc->current_cylinder[] is unsigned char, will never exceed 255 */
00191 /*              else if (fdc->current_cylinder[devidx] > 255) {
00192                         fdc->current_cylinder[devidx] = 255;
00193                         fdc->motor_steps = 0;
00194                 } */
00195 
00196                 if (dev != NULL)
00197                         dev->motor_step(fdc->motor_dir);
00198         }
00199 
00200         fdc->update_ST3();
00201         if (fdc->motor_steps != 0) {
00202                 /* step again */
00203                 PIC_AddEvent(FDC_MotorStep,fdc->fdc_motor_step_delay,idx);
00204         }
00205         else {
00206                 /* done stepping */
00207                 fdc->data_register_ready = 1;
00208                 fdc->busy_status = 0;
00209                 fdc->ST[0] &= 0x1F;
00210                 if (fdc->current_cylinder[devidx] == 0) fdc->ST[0] |= 0x20;
00211                 /* fire IRQ */
00212                 fdc->raise_irq();
00213                 /* no result phase */
00214                 fdc->reset_io();
00215 
00216                 /* real FDC's don't have this insight, but for DOSBox-X debugging... */
00217                 if (dev != NULL && dev->current_track != fdc->current_cylinder[devidx])
00218                         LOG_MSG("FDC: warning, after motor step FDC and drive are out of sync (fdc=%u drive=%u). OS or App needs to recalibrate\n",
00219                                 fdc->current_cylinder[devidx],dev->current_track);
00220 
00221 //              LOG_MSG("FDC: motor step finished. current=%u\n",fdc->current_cylinder);
00222         }
00223 }
00224 
00225 double FloppyDevice::floppy_image_motor_position() {
00226         const unsigned int motor_rpm = 300;
00227 
00228         if (!motor) return 0.0;
00229         return fmod((PIC_FullIndex() * motor_rpm/*rotations/minute*/) / 1000/*convert to seconds from milliseconds*/ / 60/*rotations/min to rotations/sec*/,1.0);
00230 }
00231 
00232 imageDisk *FloppyDevice::getImage() {
00233         if (int13_disk >= 0)
00234                 return GetINT13FloppyDrive((unsigned char)int13_disk);
00235 
00236         return NULL;
00237 }
00238 
00239 void FloppyController::on_reset() {
00240         /* TODO: cancel DOSBox events corresponding to read/seek/etc */
00241         PIC_RemoveSpecificEvents(FDC_MotorStep,interface_index);
00242         motor_dir = 0;
00243         motor_steps = 0;
00244         for (size_t i=0;i < 4;i++) current_cylinder[i] = 0;
00245         busy_status = 0;
00246         ST[0] &= 0x3F;
00247         reset_io();
00248         lower_irq();
00249 }
00250 
00251 FloppyDevice::~FloppyDevice() {
00252 }
00253 
00254 FloppyDevice::FloppyDevice(FloppyController *c) {
00255     (void)c;//UNUSED
00256         motor = select = false;
00257         current_track = 0;
00258         int13_disk = -1;
00259         track0 = false;
00260 }
00261 
00262 void FloppyDevice::set_select(bool enable) {
00263         select = enable;
00264 }
00265 
00266 void FloppyDevice::set_motor(bool enable) {
00267         motor = enable;
00268 }
00269 
00270 void FloppyDevice::motor_step(int dir) {
00271         current_track += dir;
00272 //      if (current_track < 0) current_track = 0;
00273         if (current_track > 84) current_track = 84;
00274         track0 = (current_track == 0);
00275 }
00276 
00277 int FloppyController::drive_selected() {
00278         return (digital_output_register & 3);
00279 }
00280 
00281 bool FloppyController::dma_irq_enabled() {
00282         return (digital_output_register & 0x08); /* bit 3 of DOR controls DMA/IRQ enable */
00283 }
00284 
00285 static void FDC_Destroy(Section* sec) {
00286     (void)sec;//UNUSED
00287         for (unsigned int i=0;i < MAX_FLOPPY_CONTROLLERS;i++) {
00288                 if (floppycontroller[i] != NULL) {
00289                         delete floppycontroller[i];
00290                         floppycontroller[i] = NULL;
00291                 }
00292         }
00293 
00294         init_floppy = 0;
00295 }
00296 
00297 static void FDC_Init(Section* sec,unsigned char fdc_interface) {
00298         Section_prop *section=static_cast<Section_prop *>(sec);
00299         FloppyController *fdc;
00300 
00301         assert(fdc_interface < MAX_FLOPPY_CONTROLLERS);
00302 
00303         if (!section->Get_bool("enable"))
00304                 return;
00305 
00306         if (!init_floppy) {
00307                 AddExitFunction(AddExitFunctionFuncPair(FDC_Destroy));
00308                 init_floppy = 1;
00309         }
00310 
00311         LOG(LOG_MISC,LOG_DEBUG)("Initializing floppy controller interface %u",fdc_interface);
00312 
00313     if (!IS_PC98_ARCH) {
00314         fdc = floppycontroller[fdc_interface] = new FloppyController(sec,fdc_interface);
00315         fdc->install_io_port();
00316 
00317                 PIC_SetIRQMask((unsigned int)(fdc->IRQ), false);
00318         }
00319 }
00320 
00321 void FDC_OnReset(Section *sec) {
00322     (void)sec;//UNUSED
00323         FDC_Init(control->GetSection("fdc, primary"),0);
00324 }
00325 
00326 void FDC_Primary_Init() {
00327         LOG(LOG_MISC,LOG_DEBUG)("Initializing floppy controller emulation");
00328 
00329         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(FDC_OnReset));
00330 }
00331 
00332 void FloppyController::update_ST3() {
00333         FloppyDevice *dev = device[drive_selected()&3];
00334 
00335         /* FIXME: Should assemble bits from FloppyDevice objects and their boolean "signal" variables */
00336         ST[3] =
00337                 0x20/*RDY*/ +
00338                 0x08/*DOUBLE-SIDED SIGNAL*/+
00339                 (drive_selected()&3);/*DRIVE SELECT*/
00340 
00341         if (dev != NULL)
00342                 ST[3] |= (dev->track0 ? 0x10 : 0)/*TRACK 0 signal from device*/;
00343 }
00344 
00345 void FloppyController::reset_io() {
00346         reset_cmd();
00347         reset_res();
00348         busy_status=0;
00349         data_read_expected=0;
00350 }
00351 
00352 void FloppyController::reset_cmd() {
00353         in_cmd_len=0;
00354         in_cmd_pos=0;
00355         in_cmd_state=false;
00356 }
00357 
00358 void FloppyController::reset_res() {
00359         out_res_len=0;
00360         out_res_pos=0;
00361         out_res_state=false;
00362 }
00363 
00364 void FloppyController::register_isapnp() {
00365         if (register_pnp && base_io > 0) {
00366                 unsigned char tmp[256];
00367                 unsigned int i;
00368 
00369                 const unsigned char h1[9] = {
00370                         ISAPNP_SYSDEV_HEADER(
00371                                 ISAPNP_ID('P','N','P',0x0,0x7,0x0,0x0), /* PNP0700 Generic floppy controller */
00372                                 ISAPNP_TYPE(0x01,0x02,0x00),            /* type: Mass Storage Device / Floppy / Generic */
00373                                 0x0001 | 0x0002)
00374                 };
00375 
00376                 i = 0;
00377                 memcpy(tmp+i,h1,9); i += 9;                     /* can't disable, can't configure */
00378                 /*----------allocated--------*/
00379                 tmp[i+0] = (8 << 3) | 7;                        /* IO resource */
00380                 tmp[i+1] = 0x01;                                /* 16-bit decode */
00381                 host_writew(tmp+i+2,base_io);                   /* min */
00382                 host_writew(tmp+i+4,base_io);                   /* max */
00383                 tmp[i+6] = 0x01;                                /* align */
00384                 tmp[i+7] = 0x06;                                /* length (FIXME: Emit as 7 unless we know IDE interface will not conflict) */
00385                 i += 7+1;
00386 
00387                 tmp[i+0] = (8 << 3) | 7;                        /* IO resource (FIXME: Don't emit this if we know IDE interface will take port 0x3F7) */
00388                 tmp[i+1] = 0x01;                                /* 16-bit decode */
00389                 host_writew(tmp+i+2,base_io+7);                 /* min */
00390                 host_writew(tmp+i+4,base_io+7);                 /* max */
00391                 tmp[i+6] = 0x01;                                /* align */
00392                 tmp[i+7] = 0x01;                                /* length */
00393                 i += 7+1;
00394 
00395                 if (IRQ > 0) {
00396                         tmp[i+0] = (4 << 3) | 3;                /* IRQ resource */
00397                         host_writew(tmp+i+1,1 << IRQ);
00398                         tmp[i+3] = 0x09;                        /* HTE=1 LTL=1 */
00399                         i += 3+1;
00400                 }
00401 
00402                 if (DMA >= 0) {
00403                         tmp[i+0] = (5 << 3) | 2;                /* IRQ resource */
00404                         tmp[i+1] = 1 << DMA;
00405                         tmp[i+2] = 0x00;                        /* 8-bit */
00406                         i += 2+1;
00407                 }
00408 
00409                 tmp[i+0] = 0x79;                                /* END TAG */
00410                 tmp[i+1] = 0x00;
00411                 i += 2;
00412                 /*-------------possible-----------*/
00413                 tmp[i+0] = 0x79;                                /* END TAG */
00414                 tmp[i+1] = 0x00;
00415                 i += 2;
00416                 /*-------------compatible---------*/
00417                 tmp[i+0] = 0x79;                                /* END TAG */
00418                 tmp[i+1] = 0x00;
00419                 i += 2;
00420 
00421                 if (!ISAPNP_RegisterSysDev(tmp,i))
00422                         LOG_MSG("ISAPNP register failed\n");
00423         }
00424 }
00425 
00426 FloppyController::FloppyController(Section* configuration,unsigned char index):Module_base(configuration){
00427         Section_prop * section=static_cast<Section_prop *>(configuration);
00428         int i;
00429 
00430         fdc_motor_step_delay = 400.0f / 80; /* FIXME: Based on 400ms seek time from track 0 to 80 */
00431         interface_index = index;
00432         data_register_ready = 1;
00433         data_read_expected = 0;
00434         non_dma_mode = 0;
00435         busy_status = 0;
00436         for (i=0;i < 4;i++) positioning[i] = false;
00437         for (i=0;i < 4;i++) ST[i] = 0x00;
00438         reset_io();
00439 
00440         digital_output_register = 0;
00441         device[0] = NULL;
00442         device[1] = NULL;
00443         device[2] = NULL;
00444         device[3] = NULL;
00445         base_io = 0;
00446         IRQ = -1;
00447         DMA = -1;
00448 
00449         update_ST3();
00450 
00451         int13fakev86io = section->Get_bool("int13fakev86io");
00452         instant_mode = section->Get_bool("instant mode");
00453         register_pnp = section->Get_bool("pnp");
00454 
00455         i = section->Get_int("irq");
00456         if (i > 0 && i <= 15) IRQ = i;
00457 
00458         i = section->Get_int("dma");
00459         if (i >= 0 && i <= 15) DMA = i;
00460 
00461         i = section->Get_hex("io");
00462         if (i >= 0x100 && i <= 0x3FF) base_io = i & ~7;
00463 
00464         if (IRQ < 0) IRQ = 6;
00465         if (DMA < 0) DMA = 2;
00466 
00467         dma = GetDMAChannel(DMA);
00468 
00469         if (base_io == 0) {
00470                 if (index == 0) base_io = 0x3F0;
00471         }
00472         else if (base_io == 1) {
00473                 if (index == 0) base_io = 0x370;
00474         }
00475 }
00476 
00477 void FloppyController::install_io_port(){
00478         unsigned int i;
00479 
00480         if (base_io != 0) {
00481                 LOG_MSG("FDC installing to io=%03xh IRQ=%d DMA=%d\n",base_io,IRQ,DMA);
00482                 for (i=0;i < 8;i++) {
00483                         if (i != 6) { /* does not use port 0x3F6 */
00484                                 WriteHandler[i].Install(base_io+i,fdc_baseio_w,IO_MA);
00485                                 ReadHandler[i].Install(base_io+i,fdc_baseio_r,IO_MA);
00486                         }
00487                 }
00488         }
00489 }
00490 
00491 FloppyController::~FloppyController() {
00492         unsigned int i;
00493 
00494         for (i=0;i < 4;i++) {
00495                 if (device[i] != NULL) {
00496                         delete device[i];
00497                         device[i] = NULL;
00498                 }
00499         }
00500 }
00501 
00502 FloppyController *match_fdc_controller(Bitu port) {
00503         unsigned int i;
00504 
00505         for (i=0;i < MAX_FLOPPY_CONTROLLERS;i++) {
00506                 FloppyController *fdc = floppycontroller[i];
00507                 if (fdc == NULL) continue;
00508                 if (fdc->base_io != 0U && fdc->base_io == (port&0xFFF8U)) return fdc;
00509         }
00510 
00511         return NULL;
00512 }
00513 
00514 /* when DOR port is written */
00515 void FloppyController::on_dor_change(unsigned char b) {
00516         unsigned char chg = b ^ digital_output_register;
00517         unsigned int i;
00518 
00519         /* !RESET line */
00520         if (chg & 0x04) {
00521                 if (!(b&0x04)) { /* if bit 2 == 0 s/w is resetting the controller */
00522                         LOG_MSG("FDC: Reset\n");
00523                         on_reset();
00524                 }
00525                 else {
00526                         LOG_MSG("FDC: Reset complete\n");
00527                         /* resetting the FDC on real hardware apparently fires another IRQ */
00528                         raise_irq();
00529                 }
00530         }
00531 
00532         /* drive select */
00533         if (chg & 0x03) {
00534                 int o,n;
00535 
00536                 o = drive_selected();
00537                 n = b & 3;
00538                 if (o >= 0) {
00539                         LOG_MSG("FDC: Drive select from %c to %c\n",o+'A',n+'A');
00540                         if (device[o] != NULL) device[o]->set_select(false);
00541                         if (device[n] != NULL) device[n]->set_select(true);
00542                         update_ST3();
00543                 }
00544         }
00545 
00546         /* DMA/IRQ enable */
00547         if (chg & 0x08 && IRQ >= 0) {
00548                 if ((b&0x08) && irq_pending) PIC_ActivateIRQ((unsigned int)IRQ);
00549                 else PIC_DeActivateIRQ((unsigned int)IRQ);
00550         }
00551 
00552         /* drive motors */
00553         if (chg & 0xF0) {
00554                 LOG_MSG("FDC: Motor control {A,B,C,D} = {%u,%u,%u,%u}\n",
00555                         (b>>7)&1,(b>>6)&1,(b>>5)&1,(b>>4)&1);
00556 
00557                 for (i=0;i < 4;i++) {
00558                         if (device[i] != NULL) device[i]->set_motor((b&(0x10<<i))?true:false);
00559                 }
00560         }
00561 
00562         digital_output_register = b;
00563 }
00564 
00565 /* IDE code needs to know if port 3F7 will be taken by FDC emulation */
00566 bool fdc_takes_port_3F7() {
00567         return (match_fdc_controller(0x3F7) != NULL);
00568 }
00569 
00570 void FloppyController::prepare_res_phase(uint8_t len) {
00571         data_read_expected = 1;
00572         out_res_pos = 0;
00573         out_res_len = len;
00574         out_res_state = true;
00575 }
00576 
00577 void FloppyController::invalid_command_code() {
00578         reset_res();
00579         prepare_res_phase(1);
00580         out_res[0] = ST[0] = 0x80;
00581 }
00582 
00583 uint8_t FloppyController::fdc_data_read() {
00584         if (busy_status) {
00585                 if (out_res_state) {
00586                         if (out_res_pos < out_res_len) {
00587                                 uint8_t b = out_res[out_res_pos++];
00588                                 if (out_res_pos >= out_res_len) reset_io();
00589                                 return b;
00590                         }
00591                         else {
00592                                 reset_io();
00593                         }
00594                 }
00595                 else {
00596                         reset_io();
00597                 }
00598         }
00599 
00600         return 0xFF;
00601 }
00602 
00603 void FloppyController::on_fdc_in_command() {
00604         imageDisk *image=NULL;
00605         FloppyDevice *dev;
00606         int devidx;
00607 
00608         in_cmd_state = false;
00609         devidx = drive_selected();
00610         dev = device[devidx];
00611         if (dev != NULL) image = dev->getImage();
00612 
00613         switch (in_cmd[0]&0x1F) {
00614                 case 0x0A:
00615                         break;
00616                 default:
00617                         LOG_MSG("FDC: Command len=%u %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
00618                                 in_cmd_len,
00619                                 in_cmd[0],in_cmd[1],in_cmd[2],in_cmd[3],in_cmd[4],
00620                                 in_cmd[5],in_cmd[6],in_cmd[7],in_cmd[8],in_cmd[9]);
00621                         break;
00622         }
00623 
00624         switch (in_cmd[0]&0x1F) {
00625                 case 0x03: /* Specify */
00626                         /*     |   7    6    5    4    3    2    1    0
00627                          * ----+------------------------------------------
00628                          *   0 |   0    0    0    0    0    0    1    1
00629                          *   1 |   <---- SRT ----->    <----- HUT ---->
00630                          *   2 |   <-------------- HLT ---------->   ND
00631                          * -----------------------------------------------
00632                          *   3     total
00633                          */
00634                         reset_res(); // TODO: Do something with this
00635                         reset_io();
00636                         break;
00637                 case 0x04: /* Check Drive Status */
00638                         /*     |   7    6    5    4    3    2    1    0
00639                          * ----+------------------------------------------
00640                          *   0 |               Register ST3
00641                          * -----------------------------------------------
00642                          *   1     total
00643                          */
00644                         reset_res();
00645                         prepare_res_phase(1);
00646                         out_res[0] = ST[3];
00647                         ST[0] = 0x00 | devidx;
00648                         break;
00649                 case 0x05: /* Write data (0101) */
00650                 case 0x09: /* Write deleted data (1001) */
00651                         /*     |   7    6    5    4    3    2    1    0
00652                          * ----+------------------------------------------
00653                          *   0 |               Register ST0
00654                          *   1 |               Register ST1
00655                          *   2 |               Register ST2
00656                          *   3 |             Logical cylinder
00657                          *   4 |               Logical head
00658                          *   5 |              Logical sector
00659                          *   6 |           Logical sector size
00660                          * -----------------------------------------------
00661                          *   7     total
00662                          */
00663                         /* must have a device present. must have an image. device motor and select must be enabled.
00664                          * current physical cylinder position must be within range of the image. request must have MFM bit set. */
00665                         dma = GetDMAChannel(DMA);
00666                         if (dev != NULL && dma != NULL && dev->motor && dev->select && image != NULL && (in_cmd[0]&0x40)/*MFM=1*/ &&
00667                                 current_cylinder[devidx] < image->cylinders && (in_cmd[1]&4U?1U:0U) <= image->heads &&
00668                                 (in_cmd[1]&4U?1U:0U) == in_cmd[3] && in_cmd[2] == current_cylinder[devidx] &&
00669                                 in_cmd[5] == 2U/*512 bytes/sector*/ && in_cmd[4] > 0U && in_cmd[4] <= image->sectors) {
00670                                 unsigned char sector[512];
00671                                 bool fail = false;
00672 
00673                                 /* TODO: delay related to how long it takes for the disk to rotate around to the sector requested */
00674                                 reset_res();
00675                                 ST[0] = 0x00 | devidx;
00676 
00677                                 while (!fail && !dma->tcount/*terminal count*/) {
00678                                         /* if we're reading past the track, fail */
00679                                         if (in_cmd[4] > image->sectors) {
00680                                                 fail = true;
00681                                                 break;
00682                                         }
00683 
00684                                         /* DMA transfer */
00685                                         dma->Register_Callback(0);
00686                                         if (dma->Read(512,sector) != 512) break;
00687 
00688                                         /* write sector */
00689                                         Bit8u err = image->Write_Sector(in_cmd[3]/*head*/,in_cmd[2]/*cylinder*/,in_cmd[4]/*sector*/,sector);
00690                                         if (err != 0x00) {
00691                                                 fail = true;
00692                                                 break;
00693                                         }
00694 
00695                                         /* if we're at the last sector of the track according to program, then stop */
00696                                         if (in_cmd[4] == in_cmd[6]) break;
00697 
00698                     /* next sector (TODO "multi-track" mode) */
00699                     if (in_cmd[4] == image->sectors)
00700                         in_cmd[4] = 1;
00701                     else
00702                         in_cmd[4]++;
00703                                 }
00704 
00705                                 if (fail) {
00706                                         ST[0] = (ST[0] & 0x3F) | 0x80;
00707                                         ST[1] = (1 << 0)/*missing address mark*/ | (1 << 2)/*no data*/;
00708                                         ST[2] = (1 << 0)/*missing data address mark*/;
00709                                         prepare_res_phase(1);
00710                                         out_res[0] = ST[0];
00711                                 }
00712                                 else {
00713                                         prepare_res_phase(7);
00714                                         out_res[0] = ST[0];
00715                                         out_res[1] = ST[1];
00716                                         out_res[2] = ST[2];
00717                                         out_res[3] = in_cmd[2];
00718                                         out_res[4] = in_cmd[3];
00719                                         out_res[5] = in_cmd[4];         /* the sector passing under the head at this time */
00720                                         out_res[6] = 2;                 /* 128 << 2 == 512 bytes/sector */
00721                                 }
00722                         }
00723                         else {
00724                                 /* TODO: real floppy controllers will pause for up to 1/2 a second before erroring out */
00725                                 reset_res();
00726                                 ST[0] = (ST[0] & 0x3F) | 0x80;
00727                                 ST[1] = (1 << 0)/*missing address mark*/ | (1 << 2)/*no data*/;
00728                                 ST[2] = (1 << 0)/*missing data address mark*/;
00729                                 prepare_res_phase(1);
00730                                 out_res[0] = ST[0];
00731                         }
00732                         raise_irq();
00733                         break;
00734                 case 0x06: /* Read data (0110) */
00735                         /*     |   7    6    5    4    3    2    1    0
00736                          * ----+------------------------------------------
00737                          *   0 |               Register ST0
00738                          *   1 |               Register ST1
00739                          *   2 |               Register ST2
00740                          *   3 |             Logical cylinder
00741                          *   4 |               Logical head
00742                          *   5 |              Logical sector
00743                          *   6 |           Logical sector size
00744                          * -----------------------------------------------
00745                          *   7     total
00746                          */
00747                         /* must have a device present. must have an image. device motor and select must be enabled.
00748                          * current physical cylinder position must be within range of the image. request must have MFM bit set. */
00749                         dma = GetDMAChannel(DMA);
00750                         if (dev != NULL && dma != NULL && dev->motor && dev->select && image != NULL && (in_cmd[0]&0x40)/*MFM=1*/ &&
00751                                 current_cylinder[devidx] < image->cylinders && (in_cmd[1]&4U?1U:0U) <= image->heads &&
00752                                 (in_cmd[1]&4U?1U:0U) == in_cmd[3] && in_cmd[2] == current_cylinder[devidx] &&
00753                                 in_cmd[5] == 2U/*512 bytes/sector*/ && in_cmd[4] > 0U && in_cmd[4] <= image->sectors) {
00754                                 unsigned char sector[512];
00755                                 bool fail = false;
00756 
00757                                 /* TODO: delay related to how long it takes for the disk to rotate around to the sector requested */
00758                                 reset_res();
00759                                 ST[0] = 0x00 | devidx;
00760 
00761                                 while (!fail && !dma->tcount/*terminal count*/) {
00762                                         /* if we're reading past the track, fail */
00763                                         if (in_cmd[4] > image->sectors) {
00764                                                 fail = true;
00765                                                 break;
00766                                         }
00767 
00768                                         /* read sector */
00769                                         Bit8u err = image->Read_Sector(in_cmd[3]/*head*/,in_cmd[2]/*cylinder*/,in_cmd[4]/*sector*/,sector);
00770                                         if (err != 0x00) {
00771                                                 fail = true;
00772                                                 break;
00773                                         }
00774 
00775                                         /* DMA transfer */
00776                                         dma->Register_Callback(0);
00777                                         if (dma->Write(512,sector) != 512) break;
00778 
00779                                         /* if we're at the last sector of the track according to program, then stop */
00780                                         if (in_cmd[4] == in_cmd[6]) break;
00781 
00782                     /* next sector (TODO "multi-track" mode) */
00783                     if (in_cmd[4] == image->sectors)
00784                         in_cmd[4] = 1;
00785                     else
00786                         in_cmd[4]++;
00787                                 }
00788 
00789                                 if (fail) {
00790                                         ST[0] = (ST[0] & 0x3F) | 0x80;
00791                                         ST[1] = (1 << 0)/*missing address mark*/ | (1 << 2)/*no data*/;
00792                                         ST[2] = (1 << 0)/*missing data address mark*/;
00793                                         prepare_res_phase(1);
00794                                         out_res[0] = ST[0];
00795                                 }
00796                                 else {
00797                                         prepare_res_phase(7);
00798                                         out_res[0] = ST[0];
00799                                         out_res[1] = ST[1];
00800                                         out_res[2] = ST[2];
00801                                         out_res[3] = in_cmd[2];
00802                                         out_res[4] = in_cmd[3];
00803                                         out_res[5] = in_cmd[4];         /* the sector passing under the head at this time */
00804                                         out_res[6] = 2;                 /* 128 << 2 == 512 bytes/sector */
00805                                 }
00806                         }
00807                         else {
00808                                 /* TODO: real floppy controllers will pause for up to 1/2 a second before erroring out */
00809                                 reset_res();
00810                                 ST[0] = (ST[0] & 0x3F) | 0x80;
00811                                 ST[1] = (1 << 0)/*missing address mark*/ | (1 << 2)/*no data*/;
00812                                 ST[2] = (1 << 0)/*missing data address mark*/;
00813                                 prepare_res_phase(1);
00814                                 out_res[0] = ST[0];
00815                         }
00816                         raise_irq();
00817                         break;
00818                 case 0x07: /* Calibrate drive */
00819                         ST[0] = 0x20 | devidx;
00820                         if (instant_mode) {
00821                                 /* move head to track 0 */
00822                                 current_cylinder[devidx] = 0;
00823                                 /* fire IRQ */
00824                                 raise_irq();
00825                                 /* no result phase */
00826                                 reset_io();
00827                         }
00828                         else {
00829                                 /* delay due to stepping the head to the desired cylinder */
00830                                 motor_steps = current_cylinder[devidx]; /* always to track 0 */
00831                                 if (motor_steps > 79) motor_steps = 79; /* calibrate is said to max out at 79 */
00832                                 motor_dir = -1; /* always step backwards */
00833 
00834                                 /* the command takes time to move the head */
00835                                 data_register_ready = 0;
00836                                 busy_status = 1;
00837 
00838                                 /* and make it happen */
00839                                 PIC_AddEvent(FDC_MotorStep,(motor_steps > 0 ? fdc_motor_step_delay : 0.1)/*ms*/,interface_index);
00840 
00841                                 /* return now */
00842                                 return;
00843                         }
00844                         break;
00845                 case 0x08: /* Check Interrupt Status */
00846                         /*     |   7    6    5    4    3    2    1    0
00847                          * ----+------------------------------------------
00848                          *   0 |               Register ST0
00849                          *   1 |              Current Cylinder
00850                          * -----------------------------------------------
00851                          *   2     total
00852                          */
00853                         if (irq_pending) {
00854                                 lower_irq(); /* doesn't cause IRQ, clears IRQ */
00855                                 reset_res();
00856                                 prepare_res_phase(2);
00857                                 out_res[0] = ST[0];
00858                                 out_res[1] = current_cylinder[devidx];
00859                         }
00860                         else {
00861                                 /* if no pending IRQ, signal error.
00862                                  * this is considered standard floppy controller behavior.
00863                                  * this also fixes an issue where Windows 3.1 QIC tape backup software like Norton Backup (Norton Desktop 3.0)
00864                                  * will "hang" Windows 3.1 polling the FDC in an endless loop if we don't return this error to say that no
00865                                  * more IRQs are pending. */
00866                                 reset_res();
00867                                 ST[0] = (ST[0] & 0x3F) | 0x80;
00868                                 out_res[0] = ST[0];
00869                                 prepare_res_phase(1);
00870                         }
00871                         break;
00872                 case 0x0A: /* Read ID */
00873                         /*     |   7    6    5    4    3    2    1    0
00874                          * ----+------------------------------------------
00875                          *   0 |               Register ST0
00876                          *   1 |               Register ST1
00877                          *   2 |               Register ST2
00878                          *   3 |             Logical cylinder
00879                          *   4 |               Logical head
00880                          *   5 |              Logical sector
00881                          *   6 |           Logical sector size
00882                          * -----------------------------------------------
00883                          *   7     total
00884                          */
00885                         /* must have a device present. must have an image. device motor and select must be enabled.
00886                          * current physical cylinder position must be within range of the image. request must have MFM bit set. */
00887                         if (dev != NULL && dev->motor && dev->select && image != NULL && (in_cmd[0]&0x40)/*MFM=1*/ &&
00888                                 current_cylinder[devidx] < image->cylinders && (in_cmd[1]&4U?1U:0U) <= image->heads) {
00889                                 int ns = (int)floor(dev->floppy_image_motor_position() * image->sectors);
00890                                 /* TODO: minor delay to emulate time for one sector to pass under the head */
00891                                 reset_res();
00892                                 out_res[0] = ST[0];
00893                                 out_res[1] = ST[1];
00894                                 out_res[2] = ST[2];
00895                                 out_res[3] = current_cylinder[devidx];
00896                                 out_res[4] = (in_cmd[1]&4?1:0);
00897                                 out_res[5] = ns + 1;            /* the sector passing under the head at this time */
00898                                 out_res[6] = 2;                 /* 128 << 2 == 512 bytes/sector */
00899                                 prepare_res_phase(7);
00900                         }
00901                         else {
00902                                 /* TODO: real floppy controllers will pause for up to 1/2 a second before erroring out */
00903                                 reset_res();
00904                                 ST[0] = (ST[0] & 0x3F) | 0x80;
00905                                 ST[1] = (1 << 0)/*missing address mark*/ | (1 << 2)/*no data*/;
00906                                 ST[2] = (1 << 0)/*missing data address mark*/;
00907                                 prepare_res_phase(1);
00908                                 out_res[0] = ST[0];
00909                         }
00910                         raise_irq();
00911                         break;
00912                 case 0x0C: /* Read deleted data (1100) */
00913                         /*     |   7    6    5    4    3    2    1    0
00914                          * ----+------------------------------------------
00915                          *   0 |               Register ST0
00916                          *   1 |               Register ST1
00917                          *   2 |               Register ST2
00918                          *   3 |             Logical cylinder
00919                          *   4 |               Logical head
00920                          *   5 |              Logical sector
00921                          *   6 |           Logical sector size
00922                          * -----------------------------------------------
00923                          *   7     total
00924                          */
00925                         /* the raw images used by DOSBox cannot represent "deleted sectors", so always fail */
00926                         reset_res();
00927                         ST[0] = (ST[0] & 0x3F) | 0x80;
00928                         ST[1] = (1 << 0)/*missing address mark*/ | (1 << 2)/*no data*/;
00929                         ST[2] = (1 << 0)/*missing data address mark*/;
00930                         prepare_res_phase(1);
00931                         break;
00932                 case 0x0E: /* Dump registers (response) */
00933                         /*     |   7    6    5    4    3    2    1    0
00934                          * ----+------------------------------------------
00935                          *   0 |                PCN-Drive 0
00936                          *   1 |                PCN-Drive 1
00937                          *   2 |                PCN-Drive 2
00938                          *   3 |                PCN-Drive 3
00939                          *   4 |   <----- SRT ---->    <----- HUT ---->
00940                          *   5 |   <------------ HLT ------------>   ND
00941                          *   6 |   <------------- SC/EOT ------------->
00942                          *   7 |  LOCK  0   D3   D2   D1   D0   GAP WGATE
00943                          *   8 |   0   EIS EFIFO POLL  <--- FIFOTHR -->
00944                          *   9 |                   PRETRK
00945                          * -----------------------------------------------
00946                          *  10     total
00947                          */
00948                         reset_res();
00949                         prepare_res_phase(10);
00950                         out_res[0] = current_cylinder[0];
00951                         out_res[1] = current_cylinder[1];
00952                         out_res[2] = current_cylinder[2];
00953                         out_res[3] = current_cylinder[3];
00954                         out_res[4] = 0x88;                      // FIXME
00955                         out_res[5] = 0x40;                      // FIXME
00956                         out_res[6] = 18;                        // FIXME
00957                         out_res[7] = 0;                         // FIXME
00958                         out_res[8] = 0x78;                      // FIXME
00959                         out_res[9] = 0x00;                      // FIXME
00960                         break;
00961                 case 0x0F: /* Seek Head */
00962                         ST[0] = 0x00 | drive_selected();
00963                         if (instant_mode) {
00964                                 /* move head to whatever track was wanted */
00965                                 current_cylinder[devidx] = in_cmd[2]; /* from 3rd byte of command */
00966                                 /* fire IRQ */
00967                                 raise_irq();
00968                                 /* no result phase */
00969                                 reset_io();
00970                         }
00971                         else {
00972                                 /* delay due to stepping the head to the desired cylinder */
00973                                 motor_steps = (unsigned int)abs((int)in_cmd[2] - (int)current_cylinder[devidx]);
00974                                 motor_dir = in_cmd[2] > current_cylinder[devidx] ? 1 : -1;
00975 
00976                                 /* the command takes time to move the head */
00977                                 data_register_ready = 0;
00978                                 busy_status = 1;
00979 
00980                                 /* and make it happen */
00981                                 PIC_AddEvent(FDC_MotorStep,(motor_steps > 0 ? fdc_motor_step_delay : 0.1)/*ms*/,interface_index);
00982 
00983                                 /* return now */
00984                                 return;
00985                         }
00986                         break;
00987                 case 0x13: /* Configure */
00988                         /*     |   7    6    5    4    3    2    1    0
00989                          * ----+------------------------------------------
00990                          *   0 |   0    0    0    1    0    0    1    1
00991                          *   1 |   0    0    0    0    0    0    0    0
00992                          *   2 |   0   EIS EFIFO POLL  <--- FIFOTHR -->
00993                          *   3 |                 PRETRK
00994                          * -----------------------------------------------
00995                          *   4     total
00996                          */
00997                         reset_res(); // TODO: Do something with this
00998                         reset_io();
00999                         break;
01000                 default:
01001                         LOG_MSG("FDC: Unknown command %02xh (somehow passed first check)\n",in_cmd[0]);
01002                         reset_io();
01003                         break;
01004         };
01005 
01006         switch (in_cmd[0]&0x1F) {
01007                 case 0x0A:
01008                         break;
01009                 default:
01010                         LOG_MSG("FDC: Response len=%u %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
01011                                 out_res_len,
01012                                 out_res[0],out_res[1],out_res[2],out_res[3],out_res[4],
01013                                 out_res[5],out_res[6],out_res[7],out_res[8],out_res[9]);
01014                         break;
01015         }
01016 }
01017 
01018 void FloppyController::fdc_data_write(uint8_t b) {
01019         if (!busy_status) {
01020                 /* we're starting another command */
01021                 reset_io();
01022                 in_cmd[0] = b;
01023                 in_cmd_len = 1;
01024                 in_cmd_pos = 1;
01025                 busy_status = true;
01026                 in_cmd_state = true;
01027 
01028                 /* right away.. how long is the command going to be? */
01029                 switch (in_cmd[0]&0x1F) {
01030                         case 0x03: /* Specify */
01031                                 /*     |   7    6    5    4    3    2    1    0
01032                                  * ----+------------------------------------------
01033                                  *   0 |   0    0    0    0    0    0    1    1
01034                                  *   1 |   <---- SRT ----->    <----- HUT ---->
01035                                  *   2 |   <-------------- HLT ---------->   ND
01036                                  * -----------------------------------------------
01037                                  *   3     total
01038                                  */
01039                                 in_cmd_len = 3;
01040                                 break;
01041                         case 0x04: /* Check Drive Status */
01042                                 /*     |   7    6    5    4    3    2    1    0
01043                                  * ----+------------------------------------------
01044                                  *   0 |   0    0    0    0    0    1    0    0
01045                                  *   1 |   x    x    x    x    x   HD  DR1  DR0
01046                                  * -----------------------------------------------
01047                                  *   2     total
01048                                  */
01049                                 in_cmd_len = 2;
01050                                 break;
01051                         case 0x05: /* Write data (0101) */
01052                         case 0x09: /* Write deleted data (1001) */
01053                                 /*     |   7    6    5    4    3    2    1    0
01054                                  * ----+------------------------------------------
01055                                  *   0 |  MT  MFM    0    0    0    1    0    1   
01056                                  *   1 |   0    0    0    0    0   HD  DR1  DR0
01057                                  *   2 |             Logical cylinder
01058                                  *   3 |               Logical head
01059                                  *   4 |          Logical sector (start)
01060                                  *   5 |           Logical sector size
01061                                  *   6 |               End of track               
01062                                  *   7 |               Gap 3 length
01063                                  *   8 |            Special sector size
01064                                  * -----------------------------------------------
01065                                  *   9     total
01066                                  */
01067                                 in_cmd_len = 9;
01068                                 break;
01069                         case 0x06: /* Read data (0110) */
01070                                 /*     |   7    6    5    4    3    2    1    0
01071                                  * ----+------------------------------------------
01072                                  *   0 |  MT  MFM   SK    0    0    1    1    0   
01073                                  *   1 |   0    0    0    0    0   HD  DR1  DR0
01074                                  *   2 |             Logical cylinder
01075                                  *   3 |               Logical head
01076                                  *   4 |          Logical sector (start)
01077                                  *   5 |           Logical sector size
01078                                  *   6 |               End of track               
01079                                  *   7 |               Gap 3 length
01080                                  *   8 |            Special sector size
01081                                  * -----------------------------------------------
01082                                  *   9     total
01083                                  */
01084                                 in_cmd_len = 9;
01085                                 break;
01086                         case 0x07: /* Calibrate drive (move head to track 0) */
01087                                 /*     |   7    6    5    4    3    2    1    0
01088                                  * ----+------------------------------------------
01089                                  *   0 |   0    0    0    0    0    1    1    1
01090                                  *   1 |   x    x    x    x    x    0  DR1  DR0
01091                                  * -----------------------------------------------
01092                                  *   2     total
01093                                  */
01094                                 in_cmd_len = 2;
01095                                 break;
01096                         case 0x08: /* Check Interrupt Status */
01097                                 /*     |   7    6    5    4    3    2    1    0
01098                                  * ----+------------------------------------------
01099                                  *   0 |   0    0    0    0    1    0    0    0
01100                                  * -----------------------------------------------
01101                                  *   1     total
01102                                  */
01103                                 break;
01104                         case 0x0A: /* Read ID */
01105                                 /*     |   7    6    5    4    3    2    1    0
01106                                  * ----+------------------------------------------
01107                                  *   0 |   0  MFM    0    0    1    0    1    0
01108                                  *   1 |   0    0    0    0    0   HD  DR1  DR0
01109                                  * -----------------------------------------------
01110                                  *   2     total
01111                                  */
01112                                 in_cmd_len = 2;
01113                                 break;
01114                         case 0x0C: /* Read deleted data (1100) */
01115                                 /*     |   7    6    5    4    3    2    1    0
01116                                  * ----+------------------------------------------
01117                                  *   0 |  MT  MFM   SK    0    0    1    1    0   
01118                                  *   1 |   0    0    0    0    0   HD  DR1  DR0
01119                                  *   2 |             Logical cylinder
01120                                  *   3 |               Logical head
01121                                  *   4 |          Logical sector (start)
01122                                  *   5 |           Logical sector size
01123                                  *   6 |               End of track               
01124                                  *   7 |               Gap 3 length
01125                                  *   8 |            Special sector size
01126                                  * -----------------------------------------------
01127                                  *   9     total
01128                                  */
01129                                 in_cmd_len = 9;
01130                                 break;
01131                         case 0x0E: /* Dump registers */
01132                                 /*     |   7    6    5    4    3    2    1    0
01133                                  * ----+------------------------------------------
01134                                  *   0 |   0    0    0    0    1    1    1    0
01135                                  * -----------------------------------------------
01136                                  *   1     total
01137                                  */
01138                                 in_cmd_len = 1;
01139                                 break;
01140                         case 0x0F: /* Seek Head */
01141                                 /*     |   7    6    5    4    3    2    1    0
01142                                  * ----+------------------------------------------
01143                                  *   0 |   0    0    0    0    1    1    1    1
01144                                  *   1 |   x    x    x    x    x   HD  DR1  DR0
01145                                  *   2 |                Cylinder
01146                                  * -----------------------------------------------
01147                                  *   3     total
01148                                  */
01149                                 if (in_cmd[0]&0x80) { /* Reject Seek Relative (1xfh) most FDC's I've tested don't support it */
01150                                         LOG_MSG("FDC: Seek Relative not supported\n");
01151                                         /* give Invalid Command Code 0x80 as response */
01152                                         invalid_command_code();
01153                                         reset_cmd();
01154                                 }
01155                                 else {
01156                                         in_cmd_len = 3;
01157                                 }
01158                                 break;
01159                         case 0x13: /* Configure */
01160                                 /*     |   7    6    5    4    3    2    1    0
01161                                  * ----+------------------------------------------
01162                                  *   0 |   0    0    0    1    0    0    1    1
01163                                  *   1 |   0    0    0    0    0    0    0    0
01164                                  *   2 |   0   EIS EFIFO POLL  <--- FIFOTHR -->
01165                                  *   3 |                 PRETRK
01166                                  * -----------------------------------------------
01167                                  *   4     total
01168                                  */
01169                                 in_cmd_len = 4;
01170                                 break;
01171                         default:
01172                                 LOG_MSG("FDC: Unknown command (first byte %02xh)\n",in_cmd[0]);
01173                                 /* give Invalid Command Code 0x80 as response */
01174                                 invalid_command_code();
01175                                 reset_cmd();
01176                                 break;
01177                 };
01178 
01179                 if (in_cmd_state && in_cmd_pos >= in_cmd_len)
01180                         on_fdc_in_command();
01181         }
01182         else if (in_cmd_state) {
01183                 if (in_cmd_pos < in_cmd_len)
01184                         in_cmd[in_cmd_pos++] = b;
01185 
01186                 if (in_cmd_pos >= in_cmd_len)
01187                         on_fdc_in_command();
01188         }
01189         else {
01190                 LOG_MSG("FDC: Unknown state!\n");
01191                 on_reset();
01192         }
01193 }
01194 
01195 static void fdc_baseio_w(Bitu port,Bitu val,Bitu iolen) {
01196         FloppyController *fdc = match_fdc_controller(port);
01197         if (fdc == NULL) {
01198                 LOG_MSG("WARNING: port read from I/O port not registered to FDC, yet callback triggered\n");
01199                 return;
01200         }
01201 
01202 //      LOG_MSG("FDC: Write port 0x%03x data 0x%02x irq_at_time=%u\n",port,val,fdc->irq_pending);
01203 
01204         if (iolen > 1) {
01205                 LOG_MSG("WARNING: FDC unusual port write %03xh val=%02xh len=%u, port I/O should be 8-bit\n",(int)port,(int)val,(int)iolen);
01206         }
01207 
01208         switch (port&7) {
01209                 case 2: /* digital output port */
01210                         fdc->on_dor_change(val&0xFF);
01211                         break;
01212                 case 5: /* data */
01213                         if (!fdc->data_register_ready) {
01214                                 LOG_MSG("WARNING: FDC data write when data port not ready\n");
01215                         }
01216                         else if (fdc->data_read_expected) {
01217                                 LOG_MSG("WARNING: FDC data write when data port ready but expecting I/O read\n");
01218                         }
01219                         else {
01220                                 fdc->fdc_data_write(val&0xFF);
01221                         }
01222                         break;
01223                 default:
01224                         LOG_MSG("DEBUG: FDC write port %03xh val %02xh len=%u\n",(int)port,(int)val,(int)iolen);
01225                         break;
01226         };
01227 }
01228 
01229 static Bitu fdc_baseio_r(Bitu port,Bitu iolen) {
01230         FloppyController *fdc = match_fdc_controller(port);
01231         unsigned char b;
01232 
01233         if (fdc == NULL) {
01234                 LOG_MSG("WARNING: port read from I/O port not registered to FDC, yet callback triggered\n");
01235                 return ~(0UL);
01236         }
01237 
01238 //      LOG_MSG("FDC: Read port 0x%03x irq_at_time=%u\n",port,fdc->irq_pending);
01239 
01240         if (iolen > 1) {
01241                 LOG_MSG("WARNING: FDC unusual port read %03xh len=%u, port I/O should be 8-bit\n",(int)port,(int)iolen);
01242         }
01243 
01244         switch (port&7) {
01245                 case 4: /* main status */
01246                         b =     (fdc->data_register_ready ? 0x80 : 0x00) +
01247                                 (fdc->data_read_expected ? 0x40 : 0x00) +
01248                                 (fdc->non_dma_mode ? 0x20 : 0x00) +
01249                                 (fdc->busy_status ? 0x10 : 0x00) +
01250                                 (fdc->positioning[3] ? 0x08 : 0x00) +
01251                                 (fdc->positioning[2] ? 0x04 : 0x00) +
01252                                 (fdc->positioning[1] ? 0x02 : 0x00) +
01253                                 (fdc->positioning[0] ? 0x01 : 0x00);
01254 //                      LOG_MSG("FDC: read status 0x%02x\n",b);
01255                         return b;
01256                 case 5: /* data */
01257                         if (!fdc->data_register_ready) {
01258                                 LOG_MSG("WARNING: FDC data read when data port not ready\n");
01259                                 return ~(0UL);
01260                         }
01261                         else if (!fdc->data_read_expected) {
01262                                 LOG_MSG("WARNING: FDC data read when data port ready but expecting I/O write\n");
01263                                 return ~(0UL);
01264                         }
01265 
01266                         b = fdc->fdc_data_read();
01267 //                      LOG_MSG("FDC: read data 0x%02x\n",b);
01268                         return b;
01269                 default:
01270                         LOG_MSG("DEBUG: FDC read port %03xh len=%u\n",(int)port,(int)iolen);
01271                         break;
01272         };
01273 
01274         return ~(0UL);
01275 }
01276 
01277 void FloppyController::raise_irq() {
01278         irq_pending = true;
01279         if (dma_irq_enabled() && IRQ >= 0) PIC_ActivateIRQ((unsigned int)IRQ);
01280 }
01281 
01282 void FloppyController::lower_irq() {
01283         irq_pending = false;
01284         if (IRQ >= 0) PIC_DeActivateIRQ((unsigned int)IRQ);
01285 }
01286 
01287 void BIOS_Post_register_FDC() {
01288         for (size_t i=0;i < MAX_FLOPPY_CONTROLLERS;i++) {
01289         if (floppycontroller[i] != NULL)
01290             floppycontroller[i]->register_isapnp();
01291     }
01292 }
01293