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