Version:
~
[ 2.4 ] ~
[ 2.4.5 ] ~
** Warning: Cannot open xref database.
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: floppy.cc,v 1.120 2009/04/24 14:57:24 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
12 //
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
17 //
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
22 //
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 //
27 /////////////////////////////////////////////////////////////////////////
28
29 //
30 // Floppy Disk Controller Docs:
31 // Intel 82077A Data sheet
32 // ftp://void-core.2y.net/pub/docs/fdc/82077AA_FloppyControllerDatasheet.pdf
33 // Intel 82078 Data sheet
34 // ftp://download.intel.com/design/periphrl/datashts/29047403.PDF
35 // Other FDC references
36 // http://debs.future.easyspace.com/Programming/Hardware/FDC/floppy.html
37 // And a port list:
38 // http://mudlist.eorbit.net/~adam/pickey/ports.html
39 //
40
41 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
42 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
43 // is used to know when we are exporting symbols and when we are importing.
44 #define BX_PLUGGABLE
45
46
47 extern "C" {
48 #include <errno.h>
49 }
50
51 #ifdef __linux__
52 extern "C" {
53 #include <sys/ioctl.h>
54 #include <linux/fd.h>
55 }
56 #endif
57 #include "iodev.h"
58 #include "floppy.h"
59 // windows.h included by bochs.h
60 #ifdef WIN32
61 extern "C" {
62 #include <winioctl.h>
63 }
64 #endif
65 #define LOG_THIS theFloppyController->
66
67 bx_floppy_ctrl_c *theFloppyController;
68
69 /* for main status register */
70 #define FD_MS_MRQ 0x80
71 #define FD_MS_DIO 0x40
72 #define FD_MS_NDMA 0x20
73 #define FD_MS_BUSY 0x10
74 #define FD_MS_ACTD 0x08
75 #define FD_MS_ACTC 0x04
76 #define FD_MS_ACTB 0x02
77 #define FD_MS_ACTA 0x01
78
79 #define FROM_FLOPPY 10
80 #define TO_FLOPPY 11
81
82 #define FLOPPY_DMA_CHAN 2
83
84 #define FDRIVE_NONE 0x00
85 #define FDRIVE_525DD 0x01
86 #define FDRIVE_525HD 0x02
87 #define FDRIVE_350DD 0x04
88 #define FDRIVE_350HD 0x08
89 #define FDRIVE_350ED 0x10
90
91 typedef struct {
92 unsigned id;
93 Bit8u trk;
94 Bit8u hd;
95 Bit8u spt;
96 unsigned sectors;
97 Bit8u drive_mask;
98 } floppy_type_t;
99
100 static floppy_type_t floppy_type[8] = {
101 {BX_FLOPPY_160K, 40, 1, 8, 320, 0x03},
102 {BX_FLOPPY_180K, 40, 1, 9, 360, 0x03},
103 {BX_FLOPPY_320K, 40, 2, 8, 640, 0x03},
104 {BX_FLOPPY_360K, 40, 2, 9, 720, 0x03},
105 {BX_FLOPPY_720K, 80, 2, 9, 1440, 0x1f},
106 {BX_FLOPPY_1_2, 80, 2, 15, 2400, 0x02},
107 {BX_FLOPPY_1_44, 80, 2, 18, 2880, 0x18},
108 {BX_FLOPPY_2_88, 80, 2, 36, 5760, 0x10}
109 };
110
111 static Bit16u drate_in_k[4] = {
112 500, 300, 250, 1000
113 };
114
115
116 int libfloppy_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
117 {
118 theFloppyController = new bx_floppy_ctrl_c();
119 bx_devices.pluginFloppyDevice = theFloppyController;
120 BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theFloppyController, BX_PLUGIN_FLOPPY);
121 return(0); // Success
122 }
123
124 void libfloppy_LTX_plugin_fini(void)
125 {
126 delete theFloppyController;
127 }
128
129 bx_floppy_ctrl_c::bx_floppy_ctrl_c()
130 {
131 put("FDD");
132 s.floppy_timer_index = BX_NULL_TIMER_HANDLE;
133 }
134
135 bx_floppy_ctrl_c::~bx_floppy_ctrl_c()
136 {
137 BX_DEBUG(("Exit"));
138 }
139
140 void bx_floppy_ctrl_c::init(void)
141 {
142 Bit8u i, devtype, cmos_value;
143
144 BX_DEBUG(("Init $Id: floppy.cc,v 1.120 2009/04/24 14:57:24 vruppert Exp $"));
145 DEV_dma_register_8bit_channel(2, dma_read, dma_write, "Floppy Drive");
146 DEV_register_irq(6, "Floppy Drive");
147 for (unsigned addr=0x03F2; addr<=0x03F7; addr++) {
148 DEV_register_ioread_handler(this, read_handler, addr, "Floppy Drive", 1);
149 DEV_register_iowrite_handler(this, write_handler, addr, "Floppy Drive", 1);
150 }
151
152
153 cmos_value = 0x00; /* start out with: no drive 0, no drive 1 */
154
155 BX_FD_THIS s.num_supported_floppies = 0;
156
157 for (i=0; i<4; i++) {
158 BX_FD_THIS s.media[i].type = BX_FLOPPY_NONE;
159 BX_FD_THIS s.media[i].sectors_per_track = 0;
160 BX_FD_THIS s.media[i].tracks = 0;
161 BX_FD_THIS s.media[i].heads = 0;
162 BX_FD_THIS s.media[i].sectors = 0;
163 BX_FD_THIS s.media[i].fd = -1;
164 BX_FD_THIS s.media_present[i] = 0;
165 BX_FD_THIS s.device_type[i] = FDRIVE_NONE;
166 }
167
168 //
169 // Floppy A setup
170 //
171
172 devtype = SIM->get_param_enum(BXPN_FLOPPYA_DEVTYPE)->get();
173 cmos_value = (devtype << 4);
174 if (devtype != BX_FDD_NONE) {
175 BX_FD_THIS s.device_type[0] = 1 << (devtype - 1);
176 BX_FD_THIS s.num_supported_floppies++;
177 BX_FD_THIS s.statusbar_id[0] = bx_gui->register_statusitem(" A: ");
178 } else {
179 BX_FD_THIS s.statusbar_id[0] = -1;
180 }
181
182 if (SIM->get_param_enum(BXPN_FLOPPYA_TYPE)->get() != BX_FLOPPY_NONE) {
183 if (SIM->get_param_bool(BXPN_FLOPPYA_STATUS)->get()) {
184 if (evaluate_media(BX_FD_THIS s.device_type[0], SIM->get_param_enum(BXPN_FLOPPYA_TYPE)->get(),
185 SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), & BX_FD_THIS s.media[0])) {
186 BX_FD_THIS s.media_present[0] = 1;
187 #define MED (BX_FD_THIS s.media[0])
188 BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d",
189 SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(),
190 MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
191 #undef MED
192 } else {
193 SIM->get_param_bool(BXPN_FLOPPYA_STATUS)->set(0);
194 }
195 }
196 }
197
198 //
199 // Floppy B setup
200 //
201
202 devtype = SIM->get_param_enum(BXPN_FLOPPYB_DEVTYPE)->get();
203 cmos_value |= devtype;
204 if (devtype != BX_FDD_NONE) {
205 BX_FD_THIS s.device_type[1] = 1 << (devtype - 1);
206 BX_FD_THIS s.num_supported_floppies++;
207 BX_FD_THIS s.statusbar_id[1] = bx_gui->register_statusitem(" B: ");
208 } else {
209 BX_FD_THIS s.statusbar_id[1] = -1;
210 }
211
212 if (SIM->get_param_enum(BXPN_FLOPPYB_TYPE)->get() != BX_FLOPPY_NONE) {
213 if (SIM->get_param_bool(BXPN_FLOPPYB_STATUS)->get()) {
214 if (evaluate_media(BX_FD_THIS s.device_type[1], SIM->get_param_enum(BXPN_FLOPPYB_TYPE)->get(),
215 SIM->get_param_string(BXPN_FLOPPYB_PATH)->getptr(), & BX_FD_THIS s.media[1])) {
216 BX_FD_THIS s.media_present[1] = 1;
217 #define MED (BX_FD_THIS s.media[1])
218 BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d",
219 SIM->get_param_string(BXPN_FLOPPYB_PATH)->getptr(),
220 MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
221 #undef MED
222 } else {
223 SIM->get_param_bool(BXPN_FLOPPYB_STATUS)->set(0);
224 }
225 }
226 }
227
228 /* CMOS Floppy Type and Equipment Byte register */
229 DEV_cmos_set_reg(0x10, cmos_value);
230 if (BX_FD_THIS s.num_supported_floppies > 0) {
231 DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e) |
232 ((BX_FD_THIS s.num_supported_floppies-1) << 6) | 1);
233 } else {
234 DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e));
235 }
236
237 if (BX_FD_THIS s.floppy_timer_index == BX_NULL_TIMER_HANDLE) {
238 BX_FD_THIS s.floppy_timer_index =
239 bx_pc_system.register_timer(this, timer_handler, 250, 0, 0, "floppy");
240 }
241 /* phase out s.non_dma in favor of using FD_MS_NDMA, more like hardware */
242 BX_FD_THIS s.main_status_reg &= ~FD_MS_NDMA; // enable DMA from start
243 /* these registers are not cleared by reset */
244 BX_FD_THIS s.SRT = 0;
245 BX_FD_THIS s.HUT = 0;
246 BX_FD_THIS s.HLT = 0;
247 }
248
249 void bx_floppy_ctrl_c::reset(unsigned type)
250 {
251 Bit32u i;
252
253 BX_FD_THIS s.pending_irq = 0;
254 BX_FD_THIS s.reset_sensei = 0; /* no reset result present */
255
256 BX_FD_THIS s.main_status_reg = 0;
257 BX_FD_THIS s.status_reg0 = 0;
258 BX_FD_THIS s.status_reg1 = 0;
259 BX_FD_THIS s.status_reg2 = 0;
260 BX_FD_THIS s.status_reg3 = 0;
261
262 // software reset (via DOR port 0x3f2 bit 2) does not change DOR
263 if (type == BX_RESET_HARDWARE) {
264 BX_FD_THIS s.DOR = 0x0c;
265 // motor off, drive 3..0
266 // DMA/INT enabled
267 // normal operation
268 // drive select 0
269
270 // DIR and CCR affected only by hard reset
271 for (i=0; i<4; i++) {
272 BX_FD_THIS s.DIR[i] |= 0x80; // disk changed
273 }
274 BX_FD_THIS s.data_rate = 2; /* 250 Kbps */
275 BX_FD_THIS s.lock = 0;
276 } else {
277 BX_INFO(("controller reset in software"));
278 }
279 if (BX_FD_THIS s.lock == 0) {
280 BX_FD_THIS s.config = 0;
281 BX_FD_THIS s.pretrk = 0;
282 }
283 BX_FD_THIS s.perp_mode = 0;
284
285 for (i=0; i<4; i++) {
286 BX_FD_THIS s.cylinder[i] = 0;
287 BX_FD_THIS s.head[i] = 0;
288 BX_FD_THIS s.sector[i] = 0;
289 BX_FD_THIS s.eot[i] = 0;
290 }
291
292 DEV_pic_lower_irq(6);
293 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
294 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
295 }
296 enter_idle_phase();
297 }
298
299 void bx_floppy_ctrl_c::register_state(void)
300 {
301 unsigned i;
302 char name[8];
303 bx_list_c *drive;
304
305 bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "floppy", "Floppy State", 35);
306 new bx_shadow_num_c(list, "data_rate", &BX_FD_THIS s.data_rate);
307 bx_list_c *command = new bx_list_c(list, "command", 10);
308 for (i=0; i<10; i++) {
309 sprintf(name, "%d", i);
310 new bx_shadow_num_c(command, name, &BX_FD_THIS s.command[i], BASE_HEX);
311 }
312 new bx_shadow_num_c(list, "command_index", &BX_FD_THIS s.command_index);
313 new bx_shadow_num_c(list, "command_size", &BX_FD_THIS s.command_size);
314 new bx_shadow_bool_c(list, "command_complete", &BX_FD_THIS s.command_complete);
315 new bx_shadow_num_c(list, "pending_command", &BX_FD_THIS s.pending_command, BASE_HEX);
316 new bx_shadow_bool_c(list, "multi_track", &BX_FD_THIS s.multi_track);
317 new bx_shadow_bool_c(list, "pending_irq", &BX_FD_THIS s.pending_irq);
318 new bx_shadow_num_c(list, "reset_sensei", &BX_FD_THIS s.reset_sensei);
319 new bx_shadow_num_c(list, "format_count", &BX_FD_THIS s.format_count);
320 new bx_shadow_num_c(list, "format_fillbyte", &BX_FD_THIS s.format_fillbyte, BASE_HEX);
321 bx_list_c *result = new bx_list_c(list, "result", 10);
322 for (i=0; i<10; i++) {
323 sprintf(name, "%d", i);
324 new bx_shadow_num_c(result, name, &BX_FD_THIS s.result[i], BASE_HEX);
325 }
326 new bx_shadow_num_c(list, "result_index", &BX_FD_THIS s.result_index);
327 new bx_shadow_num_c(list, "result_size", &BX_FD_THIS s.result_size);
328 new bx_shadow_num_c(list, "DOR", &BX_FD_THIS s.DOR, BASE_HEX);
329 new bx_shadow_num_c(list, "TDR", &BX_FD_THIS s.TDR, BASE_HEX);
330 new bx_shadow_bool_c(list, "TC", &BX_FD_THIS s.TC);
331 new bx_shadow_num_c(list, "main_status_reg", &BX_FD_THIS s.main_status_reg, BASE_HEX);
332 new bx_shadow_num_c(list, "status_reg0", &BX_FD_THIS s.status_reg0, BASE_HEX);
333 new bx_shadow_num_c(list, "status_reg1", &BX_FD_THIS s.status_reg1, BASE_HEX);
334 new bx_shadow_num_c(list, "status_reg2", &BX_FD_THIS s.status_reg2, BASE_HEX);
335 new bx_shadow_num_c(list, "status_reg3", &BX_FD_THIS s.status_reg3, BASE_HEX);
336 new bx_shadow_num_c(list, "floppy_buffer_index", &BX_FD_THIS s.floppy_buffer_index);
337 new bx_shadow_bool_c(list, "lock", &BX_FD_THIS s.lock);
338 new bx_shadow_num_c(list, "SRT", &BX_FD_THIS s.SRT, BASE_HEX);
339 new bx_shadow_num_c(list, "HUT", &BX_FD_THIS s.HUT, BASE_HEX);
340 new bx_shadow_num_c(list, "HLT", &BX_FD_THIS s.HLT, BASE_HEX);
341 new bx_shadow_num_c(list, "config", &BX_FD_THIS s.config, BASE_HEX);
342 new bx_shadow_num_c(list, "pretrk", &BX_FD_THIS s.pretrk);
343 new bx_shadow_num_c(list, "perp_mode", &BX_FD_THIS s.perp_mode);
344 new bx_shadow_data_c(list, "buffer", BX_FD_THIS s.floppy_buffer, 512);
345 for (i=0; i<4; i++) {
346 sprintf(name, "drive%d", i);
347 drive = new bx_list_c(list, name, 6);
348 new bx_shadow_num_c(drive, "cylinder", &BX_FD_THIS s.cylinder[i]);
349 new bx_shadow_num_c(drive, "head", &BX_FD_THIS s.head[i]);
350 new bx_shadow_num_c(drive, "sector", &BX_FD_THIS s.sector[i]);
351 new bx_shadow_num_c(drive, "eot", &BX_FD_THIS s.eot[i]);
352 new bx_shadow_bool_c(drive, "media_present", &BX_FD_THIS s.media_present[i]);
353 new bx_shadow_num_c(drive, "DIR", &BX_FD_THIS s.DIR[i], BASE_HEX);
354 }
355 }
356
357 // static IO port read callback handler
358 // redirects to non-static class handler to avoid virtual functions
359
360 Bit32u bx_floppy_ctrl_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
361 {
362 #if !BX_USE_FD_SMF
363 bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
364
365 return class_ptr->read(address, io_len);
366 }
367
368 /* reads from the floppy io ports */
369 Bit32u bx_floppy_ctrl_c::read(Bit32u address, unsigned io_len)
370 {
371 #else
372 UNUSED(this_ptr);
373 #endif // !BX_USE_FD_SMF
374 Bit8u value = 0, drive;
375
376 Bit8u pending_command = BX_FD_THIS s.pending_command;
377 switch (address) {
378 #if BX_DMA_FLOPPY_IO
379 case 0x3F2: // diskette controller digital output register
380 value = BX_FD_THIS s.DOR;
381 break;
382
383 case 0x3F4: /* diskette controller main status register */
384 value = BX_FD_THIS s.main_status_reg;
385 break;
386
387 case 0x3F5: /* diskette controller data */
388 if ((BX_FD_THIS s.main_status_reg & FD_MS_NDMA) &&
389 ((BX_FD_THIS s.pending_command & 0x4f) == 0x46)) {
390 dma_write(&value);
391 lower_interrupt();
392 // don't enter idle phase until we've given CPU last data byte
393 if (BX_FD_THIS s.TC) enter_idle_phase();
394 } else if (BX_FD_THIS s.result_size == 0) {
395 BX_ERROR(("port 0x3f5: no results to read"));
396 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
397 value = BX_FD_THIS s.result[0];
398 } else {
399 value = BX_FD_THIS s.result[BX_FD_THIS s.result_index++];
400 BX_FD_THIS s.main_status_reg &= 0xF0;
401 BX_FD_THIS lower_interrupt();
402 if (BX_FD_THIS s.result_index >= BX_FD_THIS s.result_size) {
403 enter_idle_phase();
404 }
405 }
406 break;
407 #endif // #if BX_DMA_FLOPPY_IO
408
409 case 0x3F3: // Tape Drive Register
410 drive = BX_FD_THIS s.DOR & 0x03;
411 if (BX_FD_THIS s.media_present[drive]) {
412 switch (BX_FD_THIS s.media[drive].type) {
413 case BX_FLOPPY_160K:
414 case BX_FLOPPY_180K:
415 case BX_FLOPPY_320K:
416 case BX_FLOPPY_360K:
417 case BX_FLOPPY_1_2:
418 value = 0x00;
419 break;
420 case BX_FLOPPY_720K:
421 value = 0xc0;
422 break;
423 case BX_FLOPPY_1_44:
424 value = 0x80;
425 break;
426 case BX_FLOPPY_2_88:
427 value = 0x40;
428 break;
429 default: // BX_FLOPPY_NONE
430 value = 0x20;
431 break;
432 }
433 } else {
434 value = 0x20;
435 }
436 break;
437
438 case 0x3F6: // Reserved for future floppy controllers
439 // This address shared with the hard drive controller
440 value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len);
441 break;
442
443 case 0x3F7: // diskette controller digital input register
444 // This address shared with the hard drive controller:
445 // Bit 7 : floppy
446 // Bits 6..0: hard drive
447 value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len);
448 value &= 0x7f;
449 // add in diskette change line if motor is on
450 drive = BX_FD_THIS s.DOR & 0x03;
451 if (BX_FD_THIS s.DOR & (1<<(drive+4))) {
452 value |= (BX_FD_THIS s.DIR[drive] & 0x80);
453 }
454 break;
455
456 default:
457 BX_ERROR(("io_read: unsupported address 0x%04x", (unsigned) address));
458 return(0);
459 break;
460 }
461 BX_DEBUG(("read(): during command 0x%02x, port 0x%04x returns 0x%02x",
462 pending_command, address, value));
463 return (value);
464 }
465
466 // static IO port write callback handler
467 // redirects to non-static class handler to avoid virtual functions
468
469 void bx_floppy_ctrl_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
470 {
471 #if !BX_USE_FD_SMF
472 bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
473 class_ptr->write(address, value, io_len);
474 }
475
476 /* writes to the floppy io ports */
477 void bx_floppy_ctrl_c::write(Bit32u address, Bit32u value, unsigned io_len)
478 {
479 #else
480 UNUSED(this_ptr);
481 #endif // !BX_USE_FD_SMF
482 Bit8u dma_and_interrupt_enable;
483 Bit8u normal_operation, prev_normal_operation;
484 Bit8u drive_select;
485 Bit8u motor_on_drive0, motor_on_drive1;
486
487 BX_DEBUG(("write access to port 0x%04x, value=0x%02x", address, value));
488
489 switch (address) {
490 #if BX_DMA_FLOPPY_IO
491 case 0x3F2: /* diskette controller digital output register */
492 motor_on_drive0 = value & 0x10;
493 motor_on_drive1 = value & 0x20;
494 /* set status bar conditions for Floppy 0 and Floppy 1 */
495 if (BX_FD_THIS s.statusbar_id[0] >= 0) {
496 if (motor_on_drive0 != (BX_FD_THIS s.DOR & 0x10))
497 bx_gui->statusbar_setitem(BX_FD_THIS s.statusbar_id[0], motor_on_drive0);
498 }
499 if (BX_FD_THIS s.statusbar_id[1] >= 0) {
500 if (motor_on_drive1 != (BX_FD_THIS s.DOR & 0x20))
501 bx_gui->statusbar_setitem(BX_FD_THIS s.statusbar_id[1], motor_on_drive1);
502 }
503 dma_and_interrupt_enable = value & 0x08;
504 if (!dma_and_interrupt_enable)
505 BX_DEBUG(("DMA and interrupt capabilities disabled"));
506 normal_operation = value & 0x04;
507 drive_select = value & 0x03;
508
509 prev_normal_operation = BX_FD_THIS s.DOR & 0x04;
510 BX_FD_THIS s.DOR = value;
511
512 if (prev_normal_operation==0 && normal_operation) {
513 // transition from RESET to NORMAL
514 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, 250, 0);
515 } else if (prev_normal_operation && normal_operation==0) {
516 // transition from NORMAL to RESET
517 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
518 BX_FD_THIS s.pending_command = 0xfe; // RESET pending
519 }
520 BX_DEBUG(("io_write: digital output register"));
521 BX_DEBUG((" motor on, drive1 = %d", motor_on_drive1 > 0));
522 BX_DEBUG((" motor on, drive0 = %d", motor_on_drive0 > 0));
523 BX_DEBUG((" dma_and_interrupt_enable=%02x",
524 (unsigned) dma_and_interrupt_enable));
525 BX_DEBUG((" normal_operation=%02x",
526 (unsigned) normal_operation));
527 BX_DEBUG((" drive_select=%02x",
528 (unsigned) drive_select));
529 if (BX_FD_THIS s.device_type[drive_select] == FDRIVE_NONE) {
530 BX_DEBUG(("WARNING: non existing drive selected"));
531 }
532 break;
533
534 case 0x3f4: /* diskette controller data rate select register */
535 BX_FD_THIS s.data_rate = value & 0x03;
536 if (value & 0x80) {
537 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
538 BX_FD_THIS s.pending_command = 0xfe; // RESET pending
539 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, 250, 0);
540 }
541 if ((value & 0x7c) > 0) {
542 BX_ERROR(("write to data rate select register: unsupported bits set"));
543 }
544 break;
545
546 case 0x3F5: /* diskette controller data */
547 BX_DEBUG(("command = 0x%02x", (unsigned) value));
548 if ((BX_FD_THIS s.main_status_reg & FD_MS_NDMA) && ((BX_FD_THIS s.pending_command & 0x4f) == 0x45)) {
549 BX_FD_THIS dma_read((Bit8u *) &value);
550 BX_FD_THIS lower_interrupt();
551 break;
552 } else if (BX_FD_THIS s.command_complete) {
553 if (BX_FD_THIS s.pending_command != 0)
554 BX_PANIC(("write 0x03f5: receiving new command 0x%02x, old one (0x%02x) pending",
555 value, BX_FD_THIS s.pending_command));
556 BX_FD_THIS s.command[0] = value;
557 BX_FD_THIS s.command_complete = 0;
558 BX_FD_THIS s.command_index = 1;
559 /* read/write command in progress */
560 BX_FD_THIS s.main_status_reg &= ~FD_MS_DIO; // leave drive status untouched
561 BX_FD_THIS s.main_status_reg |= FD_MS_MRQ | FD_MS_BUSY;
562 switch (value) {
563 case 0x03: /* specify */
564 BX_FD_THIS s.command_size = 3;
565 break;
566 case 0x04: // get status
567 BX_FD_THIS s.command_size = 2;
568 break;
569 case 0x07: /* recalibrate */
570 BX_FD_THIS s.command_size = 2;
571 break;
572 case 0x08: /* sense interrupt status */
573 BX_FD_THIS s.command_size = 1;
574 break;
575 case 0x0f: /* seek */
576 BX_FD_THIS s.command_size = 3;
577 break;
578 case 0x4a: /* read ID */
579 BX_FD_THIS s.command_size = 2;
580 break;
581 case 0x4d: /* format track */
582 BX_FD_THIS s.command_size = 6;
583 break;
584 case 0x45:
585 case 0xc5: /* write normal data */
586 BX_FD_THIS s.command_size = 9;
587 break;
588 case 0x46:
589 case 0x66:
590 case 0xc6:
591 case 0xe6: /* read normal data */
592 BX_FD_THIS s.command_size = 9;
593 break;
594
595 case 0x0e: // dump registers (Enhanced drives)
596 case 0x10: // Version command, enhanced controller returns 0x90
597 case 0x14: // Unlock command (Enhanced)
598 case 0x94: // Lock command (Enhanced)
599 BX_FD_THIS s.command_size = 0;
600 BX_FD_THIS s.pending_command = value;
601 enter_result_phase();
602 break;
603 case 0x12: // Perpendicular mode (Enhanced)
604 BX_FD_THIS s.command_size = 2;
605 break;
606 case 0x13: // Configure command (Enhanced)
607 BX_FD_THIS s.command_size = 4;
608 break;
609
610 case 0x18: // National Semiconductor version command; return 80h
611 // These commands are not implemented on the standard
612 // controller and return an error. They are available on
613 // the enhanced controller.
614 BX_DEBUG(("io_write: 0x3f5: unsupported floppy command 0x%02x",
615 (unsigned) value));
616 BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command
617 BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command
618 enter_result_phase();
619 break;
620
621 default:
622 BX_ERROR(("io_write: 0x3f5: invalid floppy command 0x%02x",
623 (unsigned) value));
624 BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command
625 BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command
626 enter_result_phase();
627 break;
628 }
629 } else {
630 BX_FD_THIS s.command[BX_FD_THIS s.command_index++] =
631 value;
632 }
633 if (BX_FD_THIS s.command_index ==
634 BX_FD_THIS s.command_size) {
635 /* read/write command not in progress any more */
636 floppy_command();
637 BX_FD_THIS s.command_complete = 1;
638 }
639 BX_DEBUG(("io_write: diskette controller data"));
640 return;
641 break;
642 #endif // #if BX_DMA_FLOPPY_IO
643
644 case 0x3F6: /* diskette controller (reserved) */
645 BX_DEBUG(("io_write: reserved register 0x3f6 unsupported"));
646 // this address shared with the hard drive controller
647 DEV_hd_write_handler(bx_devices.pluginHardDrive, address, value, io_len);
648 break;
649
650 #if BX_DMA_FLOPPY_IO
651 case 0x3F7: /* diskette controller configuration control register */
652 if ((value & 0x03) != BX_FD_THIS s.data_rate)
653 BX_INFO(("io_write: config control register: 0x%02x", value));
654 BX_FD_THIS s.data_rate = value & 0x03;
655 switch (BX_FD_THIS s.data_rate) {
656 case 0: BX_DEBUG((" 500 Kbps")); break;
657 case 1: BX_DEBUG((" 300 Kbps")); break;
658 case 2: BX_DEBUG((" 250 Kbps")); break;
659 case 3: BX_DEBUG((" 1 Mbps")); break;
660 }
661 break;
662
663 default:
664 BX_ERROR(("io_write ignored: 0x%04x = 0x%02x", (unsigned) address, (unsigned) value));
665 break;
666 #endif // #if BX_DMA_FLOPPY_IO
667 }
668 }
669
670 void bx_floppy_ctrl_c::floppy_command(void)
671 {
672 #if BX_PROVIDE_CPU_MEMORY==0
673 BX_PANIC(("floppy_command(): uses DMA: not supported for"
674 " external environment"));
675 #else
676 unsigned i;
677 Bit8u motor_on;
678 Bit8u head, drive, cylinder, sector, eot;
679 Bit8u sector_size, data_length;
680 Bit32u logical_sector, sector_time, step_delay;
681
682 // Print command
683 char buf[9+(9*5)+1], *p = buf;
684 p += sprintf(p, "COMMAND: ");
685 for (i=0; i<BX_FD_THIS s.command_size; i++) {
686 p += sprintf(p, "[%02x] ", (unsigned) BX_FD_THIS s.command[i]);
687 }
688 BX_DEBUG((buf));
689
690 BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0];
691 switch (BX_FD_THIS s.pending_command) {
692 case 0x03: // specify
693 // execution: specified parameters are loaded
694 // result: no result bytes, no interrupt
695 BX_FD_THIS s.SRT = BX_FD_THIS s.command[1] >> 4;
696 BX_FD_THIS s.HUT = BX_FD_THIS s.command[1] & 0x0f;
697 BX_FD_THIS s.HLT = BX_FD_THIS s.command[2] >> 1;
698 BX_FD_THIS s.main_status_reg |= (BX_FD_THIS s.command[2] & 0x01) ? FD_MS_NDMA : 0;
699 if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA)
700 BX_ERROR(("non DMA mode not fully implemented yet"));
701 enter_idle_phase();
702 return;
703
704 case 0x04: // get status
705 drive = (BX_FD_THIS s.command[1] & 0x03);
706 BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
707 BX_FD_THIS s.status_reg3 = 0x28 | (BX_FD_THIS s.head[drive]<<2) | drive
708 | (BX_FD_THIS s.media[drive].write_protected ? 0x40 : 0x00);
709 if ((BX_FD_THIS s.device_type[drive] != FDRIVE_NONE) &&
710 (BX_FD_THIS s.cylinder[drive] == 0))
711 BX_FD_THIS s.status_reg3 |= 0x10;
712 enter_result_phase();
713 return;
714
715 case 0x07: // recalibrate
716 drive = (BX_FD_THIS s.command[1] & 0x03);
717 BX_FD_THIS s.DOR &= 0xfc;
718 BX_FD_THIS s.DOR |= drive;
719 BX_DEBUG(("floppy_command(): recalibrate drive %u",
720 (unsigned) drive));
721 step_delay = calculate_step_delay(drive, 0);
722 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, step_delay, 0);
723 /* command head to track 0
724 * controller set to non-busy
725 * error condition noted in Status reg 0's equipment check bit
726 * seek end bit set to 1 in Status reg 0 regardless of outcome
727 * The last two are taken care of in timer().
728 */
729 BX_FD_THIS s.cylinder[drive] = 0;
730 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
731 BX_FD_THIS s.main_status_reg |= (1 << drive);
732 return;
733
734 case 0x08: /* sense interrupt status */
735 /* execution:
736 * get status
737 * result:
738 * no interupt
739 * byte0 = status reg0
740 * byte1 = current cylinder number (0 to 79)
741 */
742 if (BX_FD_THIS s.reset_sensei > 0) {
743 drive = 4 - BX_FD_THIS s.reset_sensei;
744 BX_FD_THIS s.status_reg0 &= 0xf8;
745 BX_FD_THIS s.status_reg0 |= (BX_FD_THIS s.head[drive] << 2) | drive;
746 BX_FD_THIS s.reset_sensei--;
747 } else if (!BX_FD_THIS s.pending_irq) {
748 BX_FD_THIS s.status_reg0 = 0x80;
749 }
750 BX_DEBUG(("sense interrupt status"));
751 enter_result_phase();
752 return;
753
754 case 0x0f: /* seek */
755 /* command:
756 * byte0 = 0F
757 * byte1 = drive & head select
758 * byte2 = cylinder number
759 * execution:
760 * postion head over specified cylinder
761 * result:
762 * no result bytes, issues an interrupt
763 */
764 drive = BX_FD_THIS s.command[1] & 0x03;
765 BX_FD_THIS s.DOR &= 0xfc;
766 BX_FD_THIS s.DOR |= drive;
767
768 BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
769 step_delay = calculate_step_delay(drive, BX_FD_THIS s.command[2]);
770 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, step_delay, 0);
771 /* ??? should also check cylinder validity */
772 BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.command[2];
773 /* data reg not ready, drive not busy */
774 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
775 BX_FD_THIS s.main_status_reg |= (1 << drive);
776 return;
777
778 case 0x13: // Configure
779 BX_DEBUG(("configure (eis = 0x%02x)", BX_FD_THIS s.command[2] & 0x40));
780 BX_DEBUG(("configure (efifo = 0x%02x)", BX_FD_THIS s.command[2] & 0x20));
781 BX_DEBUG(("configure (no poll = 0x%02x)", BX_FD_THIS s.command[2] & 0x10));
782 BX_DEBUG(("configure (fifothr = 0x%02x)", BX_FD_THIS s.command[2] & 0x0f));
783 BX_DEBUG(("configure (pretrk = 0x%02x)", BX_FD_THIS s.command[3]));
784 BX_FD_THIS s.config = BX_FD_THIS s.command[2];
785 BX_FD_THIS s.pretrk = BX_FD_THIS s.command[3];
786 enter_idle_phase();
787 return;
788
789 case 0x4a: // read ID
790 drive = BX_FD_THIS s.command[1] & 0x03;
791 BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
792 BX_FD_THIS s.DOR &= 0xfc;
793 BX_FD_THIS s.DOR |= drive;
794
795 motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
796 if (motor_on == 0) {
797 BX_ERROR(("floppy_command(): read ID: motor not on"));
798 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
799 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
800 return; // Hang controller
801 }
802 if (BX_FD_THIS s.device_type[drive] == FDRIVE_NONE) {
803 BX_ERROR(("floppy_command(): read ID: bad drive #%d", drive));
804 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
805 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
806 return; // Hang controller
807 }
808 if (BX_FD_THIS s.media_present[drive] == 0) {
809 BX_INFO(("attempt to read sector ID with media not present"));
810 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
811 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
812 return; // Hang controller
813 }
814 BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive]<<2) | drive;
815 // time to read one sector at 300 rpm
816 sector_time = 200000 / BX_FD_THIS s.media[drive].sectors_per_track;
817 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index, sector_time, 0);
818 /* data reg not ready, controller busy */
819 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
820 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
821 return;
822
823 case 0x4d: // format track
824 drive = BX_FD_THIS s.command[1] & 0x03;
825 BX_FD_THIS s.DOR &= 0xfc;
826 BX_FD_THIS s.DOR |= drive;
827
828 motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
829 if (motor_on == 0)
830 BX_PANIC(("floppy_command(): format track: motor not on"));
831 BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
832 sector_size = BX_FD_THIS s.command[2];
833 BX_FD_THIS s.format_count = BX_FD_THIS s.command[3];
834 BX_FD_THIS s.format_fillbyte = BX_FD_THIS s.command[5];
835 if (BX_FD_THIS s.device_type[drive] == FDRIVE_NONE)
836 BX_PANIC(("floppy_command(): format track: bad drive #%d", drive));
837
838 if (sector_size != 0x02) { // 512 bytes
839 BX_PANIC(("format track: sector size %d not supported", 128<<sector_size));
840 }
841 if (BX_FD_THIS s.format_count != BX_FD_THIS s.media[drive].sectors_per_track) {
842 BX_PANIC(("format track: %d sectors/track requested (%d expected)",
843 BX_FD_THIS s.format_count, BX_FD_THIS s.media[drive].sectors_per_track));
844 }
845 if (BX_FD_THIS s.media_present[drive] == 0) {
846 BX_INFO(("attempt to format track with media not present"));
847 return; // Hang controller
848 }
849 if (BX_FD_THIS s.media[drive].write_protected) {
850 // media write-protected, return error
851 BX_INFO(("attempt to format track with media write-protected"));
852 BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
853 BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111
854 BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
855 enter_result_phase();
856 return;
857 }
858
859 /* 4 header bytes per sector are required */
860 BX_FD_THIS s.format_count <<= 2;
861
862 if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) {
863 BX_DEBUG(("non-DMA floppy format unimplemented"));
864 } else {
865 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
866 }
867 /* data reg not ready, controller busy */
868 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
869 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
870 BX_DEBUG(("format track"));
871 return;
872
873 case 0x46: // read normal data, MT=0, SK=0
874 case 0x66: // read normal data, MT=0, SK=1
875 case 0xc6: // read normal data, MT=1, SK=0
876 case 0xe6: // read normal data, MT=1, SK=1
877 case 0x45: // write normal data, MT=0
878 case 0xc5: // write normal data, MT=1
879 BX_FD_THIS s.multi_track = (BX_FD_THIS s.command[0] >> 7);
880 if ((BX_FD_THIS s.DOR & 0x08) == 0)
881 BX_PANIC(("read/write command with DMA and int disabled"));
882 drive = BX_FD_THIS s.command[1] & 0x03;
883 BX_FD_THIS s.DOR &= 0xfc;
884 BX_FD_THIS s.DOR |= drive;
885
886 motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
887 if (motor_on == 0)
888 BX_PANIC(("floppy_command(): read/write: motor not on"));
889 head = BX_FD_THIS s.command[3] & 0x01;
890 cylinder = BX_FD_THIS s.command[2]; /* 0..79 depending */
891 sector = BX_FD_THIS s.command[4]; /* 1..36 depending */
892 eot = BX_FD_THIS s.command[6]; /* 1..36 depending */
893 sector_size = BX_FD_THIS s.command[5];
894 data_length = BX_FD_THIS s.command[8];
895 BX_DEBUG(("read/write normal data"));
896 BX_DEBUG(("BEFORE"));
897 BX_DEBUG((" drive = %u", (unsigned) drive));
898 BX_DEBUG((" head = %u", (unsigned) head));
899 BX_DEBUG((" cylinder = %u", (unsigned) cylinder));
900 BX_DEBUG((" sector = %u", (unsigned) sector));
901 BX_DEBUG((" eot = %u", (unsigned) eot));
902 if (BX_FD_THIS s.device_type[drive] == FDRIVE_NONE)
903 BX_PANIC(("floppy_command(): read/write: bad drive #%d", drive));
904
905 // check that head number in command[1] bit two matches the head
906 // reported in the head number field. Real floppy drives are
907 // picky about this, as reported in SF bug #439945, (Floppy drive
908 // read input error checking).
909 if (head != ((BX_FD_THIS s.command[1]>>2)&1)) {
910 BX_ERROR(("head number in command[1] doesn't match head field"));
911 BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
912 BX_FD_THIS s.status_reg1 = 0x04; // 0000 0100
913 BX_FD_THIS s.status_reg2 = 0x00; // 0000 0000
914 enter_result_phase();
915 return;
916 }
917
918 if (BX_FD_THIS s.media_present[drive] == 0) {
919 BX_INFO(("attempt to read/write sector %u with media not present", (unsigned) sector));
920 return; // Hang controller
921 }
922
923 if (sector_size != 0x02) { // 512 bytes
924 BX_PANIC(("read/write command: sector size %d not supported", 128<<sector_size));
925 }
926
927 if (cylinder >= BX_FD_THIS s.media[drive].tracks) {
928 BX_PANIC(("io: norm r/w parms out of range: sec#%02xh cyl#%02xh eot#%02xh head#%02xh",
929 (unsigned) sector, (unsigned) cylinder, (unsigned) eot,
930 (unsigned) head));
931 return;
932 }
933
934 if (sector > BX_FD_THIS s.media[drive].sectors_per_track) {
935 BX_INFO(("attempt to read/write sector %u past last sector %u",
936 (unsigned) sector,
937 (unsigned) BX_FD_THIS s.media[drive].sectors_per_track));
938 BX_FD_THIS s.cylinder[drive] = cylinder;
939 BX_FD_THIS s.head[drive] = head;
940 BX_FD_THIS s.sector[drive] = sector;
941
942 BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
943 BX_FD_THIS s.status_reg1 = 0x04;
944 BX_FD_THIS s.status_reg2 = 0x00;
945 enter_result_phase();
946 return;
947 }
948
949 if (cylinder != BX_FD_THIS s.cylinder[drive]) {
950 BX_DEBUG(("io: cylinder request != current cylinder"));
951 reset_changeline();
952 }
953
954 logical_sector = (cylinder * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
955 (head * BX_FD_THIS s.media[drive].sectors_per_track) +
956 (sector - 1);
957
958 if (logical_sector >= BX_FD_THIS s.media[drive].sectors) {
959 BX_PANIC(("io: logical sector out of bounds"));
960 }
961 // This hack makes older versions of the Bochs BIOS work
962 if (eot == 0) {
963 eot = BX_FD_THIS s.media[drive].sectors_per_track;
964 }
965 BX_FD_THIS s.cylinder[drive] = cylinder;
966 BX_FD_THIS s.head[drive] = head;
967 BX_FD_THIS s.sector[drive] = sector;
968 BX_FD_THIS s.eot[drive] = eot;
969
970 if ((BX_FD_THIS s.command[0] & 0x4f) == 0x46) { // read
971 floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
972 512, FROM_FLOPPY);
973 /* controller busy; if DMA mode, data reg not ready */
974 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
975 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
976 if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) {
977 BX_FD_THIS s.main_status_reg |= (FD_MS_MRQ | FD_MS_DIO);
978 }
979 // time to read one sector at 300 rpm
980 sector_time = 200000 / BX_FD_THIS s.media[drive].sectors_per_track;
981 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index,
982 sector_time , 0);
983 } else if ((BX_FD_THIS s.command[0] & 0x7f) == 0x45) { // write
984 /* controller busy; if DMA mode, data reg not ready */
985 BX_FD_THIS s.main_status_reg &= FD_MS_NDMA;
986 BX_FD_THIS s.main_status_reg |= FD_MS_BUSY;
987 if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) {
988 BX_FD_THIS s.main_status_reg |= FD_MS_MRQ;
989 } else {
990 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
991 }
992 } else {
993 BX_PANIC(("floppy_command(): unknown read/write command"));
994 return;
995 }
996 break;
997
998 case 0x12: // Perpendicular mode
999 BX_FD_THIS s.perp_mode = BX_FD_THIS s.command[1];
1000 BX_INFO(("perpendicular mode: config=0x%02x", BX_FD_THIS s.perp_mode));
1001 enter_idle_phase();
1002 break;
1003
1004 default: // invalid or unsupported command; these are captured in write() above
1005 BX_PANIC(("You should never get here! cmd = 0x%02x",
1006 BX_FD_THIS s.command[0]));
1007 }
1008 #endif
1009 }
1010
1011 void bx_floppy_ctrl_c::floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer,
1012 Bit32u bytes, Bit8u direction)
1013 {
1014 int ret = 0;
1015
1016 if (BX_FD_THIS s.device_type[drive] == FDRIVE_NONE)
1017 BX_PANIC(("floppy_xfer: bad drive #%d", drive));
1018
1019 BX_DEBUG(("floppy_xfer: drive=%u, offset=%u, bytes=%u, direction=%s floppy",
1020 drive, offset, bytes, (direction==FROM_FLOPPY)? "from" : "to"));
1021
1022 #if BX_WITH_MACOS
1023 if (strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
1024 #endif
1025 {
1026 // don't need to seek the file if we are using Win95 type direct access
1027 if (!BX_FD_THIS s.media[drive].raw_floppy_win95) {
1028 ret = (int)lseek(BX_FD_THIS s.media[drive].fd, offset, SEEK_SET);
1029 if (ret < 0) {
1030 BX_PANIC(("could not perform lseek() to %d on floppy image file", offset));
1031 return;
1032 }
1033 }
1034 }
1035
1036 if (direction == FROM_FLOPPY) {
1037 #if BX_WITH_MACOS
1038 if (!strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
1039 ret = fd_read((char *) buffer, offset, bytes);
1040 else
1041 #endif
1042 #ifdef WIN32
1043 // if using Win95 direct access
1044 if (BX_FD_THIS s.media[drive].raw_floppy_win95) {
1045 DWORD ret_cnt = 0;
1046 DIOC_REGISTERS reg;
1047 HANDLE hFile = CreateFile("\\\\.\\vwin32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
1048 if (hFile == INVALID_HANDLE_VALUE)
1049 BX_PANIC(("Could not open floppy device under Win95 for read"));
1050 reg.reg_ECX = bytes >> 9; // / 512
1051 reg.reg_EDX = offset >> 9; // / 512
1052 reg.reg_EBX = (DWORD) buffer;
1053 reg.reg_EAX = BX_FD_THIS s.media[drive].raw_floppy_win95_drv;
1054 DeviceIoControl(hFile, VWIN32_DIOC_DOS_INT25,
1055 ®, sizeof(reg), ®, sizeof(reg), &ret_cnt, NULL);
1056 CloseHandle(hFile);
1057 // I don't know why this returns 28 instead of 512, but it works
1058 if (ret_cnt == 28)
1059 ret = 512;
1060 } else {
1061 #endif
1062 ret = ::read(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes);
1063 #ifdef WIN32
1064 }
1065 #endif
1066 if (ret < int(bytes)) {
1067 /* ??? */
1068 if (ret > 0) {
1069 BX_INFO(("partial read() on floppy image returns %u/%u",
1070 (unsigned) ret, (unsigned) bytes));
1071 memset(buffer + ret, 0, bytes - ret);
1072 }
1073 else {
1074 BX_INFO(("read() on floppy image returns 0"));
1075 memset(buffer, 0, bytes);
1076 }
1077 }
1078 }
1079
1080 else { // TO_FLOPPY
1081 BX_ASSERT (!BX_FD_THIS s.media[drive].write_protected);
1082 #if BX_WITH_MACOS
1083 if (!strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
1084 ret = fd_write((char *) buffer, offset, bytes);
1085 else
1086 #endif
1087 #ifdef WIN32
1088 // if using Win95 direct access
1089 if (BX_FD_THIS s.media[drive].raw_floppy_win95) {
1090 DWORD ret_cnt = 0;
1091 DIOC_REGISTERS reg;
1092 HANDLE hFile = CreateFile("\\\\.\\vwin32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
1093 if (hFile == INVALID_HANDLE_VALUE)
1094 BX_PANIC(("Could not open floppy device under Win95"));
1095 reg.reg_ECX = bytes >> 9; // / 512
1096 reg.reg_EDX = offset >> 9; // / 512
1097 reg.reg_EBX = (DWORD) buffer;
1098 reg.reg_EAX = BX_FD_THIS s.media[drive].raw_floppy_win95_drv;
1099 DeviceIoControl(hFile, VWIN32_DIOC_DOS_INT26,
1100 ®, sizeof(reg), ®, sizeof(reg), (LPDWORD) &ret_cnt, NULL);
1101 CloseHandle(hFile);
1102 // I don't know why this returns 28 instead of 512, but it works
1103 if (ret_cnt == 28)
1104 ret = 512;
1105 } else {
1106 #endif
1107 ret = ::write(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes);
1108 #ifdef WIN32
1109 }
1110 #endif
1111 if (ret < int(bytes)) {
1112 BX_PANIC(("could not perform write() on floppy image file"));
1113 }
1114 }
1115 }
1116
1117 void bx_floppy_ctrl_c::timer_handler(void *this_ptr)
1118 {
1119 bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
1120 class_ptr->timer();
1121 }
1122
1123 void bx_floppy_ctrl_c::timer()
1124 {
1125 Bit8u drive, motor_on;
1126
1127 drive = BX_FD_THIS s.DOR & 0x03;
1128 switch (BX_FD_THIS s.pending_command) {
1129 case 0x07: // recal
1130 BX_FD_THIS s.status_reg0 = 0x20 | drive;
1131 motor_on = ((BX_FD_THIS s.DOR>>(drive+4)) & 0x01);
1132 if ((BX_FD_THIS s.device_type[drive] == FDRIVE_NONE) || (motor_on == 0)) {
1133 BX_FD_THIS s.status_reg0 |= 0x50;
1134 }
1135 enter_idle_phase();
1136 BX_FD_THIS raise_interrupt();
1137 break;
1138
1139 case 0x0f: // seek
1140 BX_FD_THIS s.status_reg0 = 0x20 | (BX_FD_THIS s.head[drive]<<2) | drive;
1141 enter_idle_phase();
1142 BX_FD_THIS raise_interrupt();
1143 break;
1144
1145 case 0x4a: /* read ID */
1146 enter_result_phase();
1147 break;
1148
1149 case 0x45: /* write normal data */
1150 case 0xc5:
1151 if (BX_FD_THIS s.TC) { // Terminal Count line, done
1152 BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
1153 BX_FD_THIS s.status_reg1 = 0;
1154 BX_FD_THIS s.status_reg2 = 0;
1155
1156 BX_DEBUG(("<<WRITE DONE>>"));
1157 BX_DEBUG(("AFTER"));
1158 BX_DEBUG((" drive = %u", drive));
1159 BX_DEBUG((" head = %u", BX_FD_THIS s.head[drive]));
1160 BX_DEBUG((" cylinder = %u", BX_FD_THIS s.cylinder[drive]));
1161 BX_DEBUG((" sector = %u", BX_FD_THIS s.sector[drive]));
1162
1163 enter_result_phase();
1164 } else {
1165 // transfer next sector
1166 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
1167 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
1168 }
1169 }
1170 break;
1171
1172 case 0x46: /* read normal data */
1173 case 0x66:
1174 case 0xc6:
1175 case 0xe6:
1176 // transfer next sector
1177 if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) {
1178 BX_FD_THIS s.main_status_reg &= ~FD_MS_BUSY; // clear busy bit
1179 BX_FD_THIS s.main_status_reg |= FD_MS_MRQ | FD_MS_DIO; // data byte waiting
1180 } else {
1181 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
1182 }
1183 break;
1184
1185 case 0x4d: /* format track */
1186 if ((BX_FD_THIS s.format_count == 0) || BX_FD_THIS s.TC) {
1187 BX_FD_THIS s.format_count = 0;
1188 BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
1189 enter_result_phase();
1190 } else {
1191 // transfer next sector
1192 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
1193 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
1194 }
1195 }
1196 break;
1197
1198 case 0xfe: // (contrived) RESET
1199 theFloppyController->reset(BX_RESET_SOFTWARE);
1200 BX_FD_THIS s.pending_command = 0;
1201 BX_FD_THIS s.status_reg0 = 0xc0;
1202 BX_FD_THIS raise_interrupt();
1203 BX_FD_THIS s.reset_sensei = 4;
1204 break;
1205
1206 case 0x00: // nothing pending?
1207 break;
1208
1209 default:
1210 BX_PANIC(("floppy:timer(): unknown case %02x",
1211 (unsigned) BX_FD_THIS s.pending_command));
1212 }
1213 }
1214
1215 void bx_floppy_ctrl_c::dma_write(Bit8u *data_byte)
1216 {
1217 // A DMA write is from I/O to Memory
1218 // We need to return the next data byte from the floppy buffer
1219 // to be transfered via the DMA to memory. (read block from floppy)
1220
1221 Bit8u drive;
1222
1223 drive = BX_FD_THIS s.DOR & 0x03;
1224 *data_byte = BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++];
1225
1226 BX_FD_THIS s.TC = get_tc();
1227 if ((BX_FD_THIS s.floppy_buffer_index >= 512) || (BX_FD_THIS s.TC)) {
1228
1229 if (BX_FD_THIS s.floppy_buffer_index >= 512) {
1230 increment_sector(); // increment to next sector before retrieving next one
1231 BX_FD_THIS s.floppy_buffer_index = 0;
1232 }
1233 if (BX_FD_THIS s.TC) { // Terminal Count line, done
1234 BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
1235 BX_FD_THIS s.status_reg1 = 0;
1236 BX_FD_THIS s.status_reg2 = 0;
1237
1238 BX_DEBUG(("<<READ DONE>>"));
1239 BX_DEBUG(("AFTER"));
1240 BX_DEBUG((" drive = %u", drive));
1241 BX_DEBUG((" head = %u", BX_FD_THIS s.head[drive]));
1242 BX_DEBUG((" cylinder = %u", BX_FD_THIS s.cylinder[drive]));
1243 BX_DEBUG((" sector = %u", BX_FD_THIS s.sector[drive]));
1244
1245 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
1246 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
1247 }
1248 enter_result_phase();
1249 } else { // more data to transfer
1250 Bit32u logical_sector, sector_time;
1251
1252 // remember that not all floppies have two sides, multiply by s.head[drive]
1253 logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads *
1254 BX_FD_THIS s.media[drive].sectors_per_track) +
1255 (BX_FD_THIS s.head[drive] *
1256 BX_FD_THIS s.media[drive].sectors_per_track) +
1257 (BX_FD_THIS s.sector[drive] - 1);
1258
1259 floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
1260 512, FROM_FLOPPY);
1261 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
1262 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
1263 }
1264 // time to read one sector at 300 rpm
1265 sector_time = 200000 / BX_FD_THIS s.media[drive].sectors_per_track;
1266 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index,
1267 sector_time , 0);
1268 }
1269 }
1270 }
1271
1272 void bx_floppy_ctrl_c::dma_read(Bit8u *data_byte)
1273 {
1274 // A DMA read is from Memory to I/O
1275 // We need to write the data_byte which was already transfered from memory
1276 // via DMA to I/O (write block to floppy)
1277
1278 Bit8u drive;
1279 Bit32u logical_sector, sector_time;
1280
1281 drive = BX_FD_THIS s.DOR & 0x03;
1282 if (BX_FD_THIS s.pending_command == 0x4d) { // format track in progress
1283 BX_FD_THIS s.format_count--;
1284 switch (3 - (BX_FD_THIS s.format_count & 0x03)) {
1285 case 0:
1286 BX_FD_THIS s.cylinder[drive] = *data_byte;
1287 break;
1288 case 1:
1289 if (*data_byte != BX_FD_THIS s.head[drive])
1290 BX_ERROR(("head number does not match head field"));
1291 break;
1292 case 2:
1293 BX_FD_THIS s.sector[drive] = *data_byte;
1294 break;
1295 case 3:
1296 if (*data_byte != 2) BX_ERROR(("dma_read: sector size %d not supported", 128<<(*data_byte)));
1297 BX_DEBUG(("formatting cylinder %u head %u sector %u",
1298 BX_FD_THIS s.cylinder[drive], BX_FD_THIS s.head[drive],
1299 BX_FD_THIS s.sector[drive]));
1300 for (unsigned i = 0; i < 512; i++) {
1301 BX_FD_THIS s.floppy_buffer[i] = BX_FD_THIS s.format_fillbyte;
1302 }
1303 logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
1304 (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) +
1305 (BX_FD_THIS s.sector[drive] - 1);
1306 floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
1307 512, TO_FLOPPY);
1308 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
1309 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
1310 }
1311 // time to write one sector at 300 rpm
1312 sector_time = 200000 / BX_FD_THIS s.media[drive].sectors_per_track;
1313 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index,
1314 sector_time , 0);
1315 break;
1316 }
1317 } else { // write normal data
1318 BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++] = *data_byte;
1319
1320 BX_FD_THIS s.TC = get_tc();
1321 if ((BX_FD_THIS s.floppy_buffer_index >= 512) || (BX_FD_THIS s.TC)) {
1322 logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
1323 (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) +
1324 (BX_FD_THIS s.sector[drive] - 1);
1325 if (BX_FD_THIS s.media[drive].write_protected) {
1326 // write protected error
1327 BX_INFO(("tried to write disk %u, which is write-protected", drive));
1328 // ST0: IC1,0=01 (abnormal termination: started execution but failed)
1329 BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
1330 // ST1: DataError=1, NDAT=1, NotWritable=1, NID=1
1331 BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111
1332 // ST2: CRCE=1, SERR=1, BCYL=1, NDAM=1.
1333 BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
1334 enter_result_phase();
1335 return;
1336 }
1337 floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
1338 512, TO_FLOPPY);
1339 increment_sector(); // increment to next sector after writing current one
1340 BX_FD_THIS s.floppy_buffer_index = 0;
1341 if (!(BX_FD_THIS s.main_status_reg & FD_MS_NDMA)) {
1342 DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
1343 }
1344 // time to write one sector at 300 rpm
1345 sector_time = 200000 / BX_FD_THIS s.media[drive].sectors_per_track;
1346 bx_pc_system.activate_timer(BX_FD_THIS s.floppy_timer_index,
1347 sector_time , 0);
1348 // the following is a kludge; i (jc) don't know how to work with the timer
1349 if ((BX_FD_THIS s.main_status_reg & FD_MS_NDMA) && BX_FD_THIS s.TC) {
1350 enter_result_phase();
1351 }
1352 }
1353 }
1354 }
1355
1356 void bx_floppy_ctrl_c::raise_interrupt(void)
1357 {
1358 DEV_pic_raise_irq(6);
1359 BX_FD_THIS s.pending_irq = 1;
1360 BX_FD_THIS s.reset_sensei = 0;
1361 }
1362
1363 void bx_floppy_ctrl_c::lower_interrupt(void)
1364 {
1365 if (BX_FD_THIS s.pending_irq) {
1366 DEV_pic_lower_irq(6);
1367 BX_FD_THIS s.pending_irq = 0;
1368 }
1369 }
1370
1371 void bx_floppy_ctrl_c::increment_sector(void)
1372 {
1373 Bit8u drive;
1374
1375 drive = BX_FD_THIS s.DOR & 0x03;
1376
1377 // values after completion of data xfer
1378 // ??? calculation depends on base_count being multiple of 512
1379 BX_FD_THIS s.sector[drive] ++;
1380 if ((BX_FD_THIS s.sector[drive] > BX_FD_THIS s.eot[drive]) ||
1381 (BX_FD_THIS s.sector[drive] > BX_FD_THIS s.media[drive].sectors_per_track)) {
1382 BX_FD_THIS s.sector[drive] = 1;
1383 if (BX_FD_THIS s.multi_track) {
1384 BX_FD_THIS s.head[drive] ++;
1385 if (BX_FD_THIS s.head[drive] > 1) {
1386 BX_FD_THIS s.head[drive] = 0;
1387 BX_FD_THIS s.cylinder[drive] ++;
1388 reset_changeline();
1389 }
1390 } else {
1391 BX_FD_THIS s.cylinder[drive] ++;
1392 reset_changeline();
1393 }
1394 if (BX_FD_THIS s.cylinder[drive] >= BX_FD_THIS s.media[drive].tracks) {
1395 // Set to 1 past last possible cylinder value.
1396 // I notice if I set it to tracks-1, prama linux won't boot.
1397 BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.media[drive].tracks;
1398 BX_INFO(("increment_sector: clamping cylinder to max"));
1399 }
1400 }
1401 }
1402
1403 unsigned bx_floppy_ctrl_c::set_media_status(unsigned drive, unsigned status)
1404 {
1405 char *path;
1406 unsigned type;
1407
1408 if (drive == 0)
1409 type = SIM->get_param_enum(BXPN_FLOPPYA_TYPE)->get();
1410 else
1411 type = SIM->get_param_enum(BXPN_FLOPPYB_TYPE)->get();
1412
1413 // if setting to the current value, nothing to do
1414 if ((status == BX_FD_THIS s.media_present[drive]) &&
1415 ((status == 0) || (type == BX_FD_THIS s.media[drive].type)))
1416 return(status);
1417
1418 if (status == 0) {
1419 // eject floppy
1420 if (BX_FD_THIS s.media[drive].fd >= 0) {
1421 close(BX_FD_THIS s.media[drive].fd);
1422 BX_FD_THIS s.media[drive].fd = -1;
1423 }
1424 BX_FD_THIS s.media_present[drive] = 0;
1425 if (drive == 0) {
1426 SIM->get_param_bool(BXPN_FLOPPYA_STATUS)->set(0);
1427 } else {
1428 SIM->get_param_bool(BXPN_FLOPPYB_STATUS)->set(0);
1429 }
1430 BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line
1431 return(0);
1432 } else {
1433 // insert floppy
1434 if (drive == 0) {
1435 path = SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr();
1436 } else {
1437 path = SIM->get_param_string(BXPN_FLOPPYB_PATH)->getptr();
1438 }
1439 if (!strcmp(path, "none"))
1440 return(0);
1441 if (evaluate_media(BX_FD_THIS s.device_type[drive], type, path, & BX_FD_THIS s.media[drive])) {
1442 BX_FD_THIS s.media_present[drive] = 1;
1443 if (drive == 0) {
1444 #define MED (BX_FD_THIS s.media[0])
1445 BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(),
1446 MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
1447 #undef MED
1448 SIM->get_param_bool(BXPN_FLOPPYA_STATUS)->set(1);
1449 } else {
1450 #define MED (BX_FD_THIS s.media[1])
1451 BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", SIM->get_param_string(BXPN_FLOPPYB_PATH)->getptr(),
1452 MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
1453 #undef MED
1454 SIM->get_param_bool(BXPN_FLOPPYB_STATUS)->set(1);
1455 }
1456 return(1);
1457 } else {
1458 BX_FD_THIS s.media_present[drive] = 0;
1459 if (drive == 0) {
1460 SIM->get_param_bool(BXPN_FLOPPYA_STATUS)->set(0);
1461 SIM->get_param_enum(BXPN_FLOPPYA_TYPE)->set(BX_FLOPPY_NONE);
1462 } else {
1463 SIM->get_param_bool(BXPN_FLOPPYB_STATUS)->set(0);
1464 SIM->get_param_enum(BXPN_FLOPPYB_TYPE)->set(BX_FLOPPY_NONE);
1465 }
1466 return(0);
1467 }
1468 }
1469 }
1470
1471 unsigned bx_floppy_ctrl_c::get_media_status(unsigned drive)
1472 {
1473 return BX_FD_THIS s.media_present[drive];
1474 }
1475
1476 #ifdef O_BINARY
1477 #define BX_RDONLY O_RDONLY | O_BINARY
1478 #define BX_RDWR O_RDWR | O_BINARY
1479 #else
1480 #define BX_RDONLY O_RDONLY
1481 #define BX_RDWR O_RDWR
1482 #endif
1483
1484 bx_bool bx_floppy_ctrl_c::evaluate_media(Bit8u devtype, Bit8u type, char *path, floppy_t *media)
1485 {
1486 struct stat stat_buf;
1487 int i, ret;
1488 int type_idx = -1;
1489 #ifdef __linux__
1490 struct floppy_struct floppy_geom;
1491 #endif
1492 #ifdef WIN32
1493 char sTemp[1024];
1494 bx_bool raw_floppy = 0;
1495 HANDLE hFile;
1496 DWORD bytes;
1497 DISK_GEOMETRY dg;
1498 unsigned tracks = 0, heads = 0, spt = 0;
1499 #endif
1500
1501 //If media file is already open, close it before reopening.
1502 if(media->fd >=0) {
1503 close(media->fd);
1504 media->fd=-1;
1505 }
1506
1507 // check media type
1508 if (type == BX_FLOPPY_NONE) {
1509 return 0;
1510 }
1511 for (i = 0; i < 8; i++) {
1512 if (type == floppy_type[i].id) type_idx = i;
1513 }
1514 if (type_idx == -1) {
1515 BX_ERROR(("evaluate_media: unknown media type %d", type));
1516 return 0;
1517 }
1518 if ((floppy_type[type_idx].drive_mask & devtype) == 0) {
1519 BX_ERROR(("evaluate_media: media type %d not valid for this floppy drive", type));
1520 return 0;
1521 }
1522
1523 // open media file (image file or device)
1524 media->write_protected = 0;
1525 media->raw_floppy_win95 = 0;
1526 #ifdef macintosh
1527 media->fd = 0;
1528 if (strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
1529 #endif
1530 #ifdef WIN32
1531 if ((isalpha(path[0])) && (path[1] == ':') && (strlen(path) == 2)) {
1532 raw_floppy = 1;
1533 wsprintf(sTemp, "\\\\.\\%s", path);
1534 hFile = CreateFile(sTemp, GENERIC_READ, FILE_SHARE_WRITE, NULL,
1535 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1536 if (hFile == INVALID_HANDLE_VALUE) {
1537 // try to open it with Win95 style
1538 hFile = CreateFile("\\\\.\\vwin32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
1539 if (hFile == INVALID_HANDLE_VALUE) {
1540 BX_ERROR(("Cannot open floppy drive"));
1541 return(0);
1542 }
1543 media->raw_floppy_win95 = 1;
1544 media->raw_floppy_win95_drv = toupper(path[0]) - 'A';
1545 }
1546 // if using Win95 direct access, get params this way
1547 if (media->raw_floppy_win95) {
1548 DWORD ret_cnt = 0;
1549 DIOC_REGISTERS reg;
1550 BLOCK_DEV_PARAMS params;
1551 reg.reg_EAX = 0x440D;
1552 reg.reg_ECX = 0x0860;
1553 reg.reg_EDX = (DWORD) ¶ms;
1554 reg.reg_EBX = media->raw_floppy_win95_drv+1;
1555 //reg.reg_Flags = 0x0001; // assume error (carry flag is set)
1556 if (DeviceIoControl(hFile, VWIN32_DIOC_DOS_IOCTL ,
1557 ®, sizeof(reg), ®, sizeof(reg), &ret_cnt, NULL)) {
1558 tracks = params.cylinders;
1559 heads = params.num_heads;
1560 spt = params.sects_per_track;
1561 } else {
1562 CloseHandle(hFile);
1563 return(0);
1564 }
1565 } else {
1566 if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg), &bytes, NULL)) {
1567 BX_ERROR(("No media in floppy drive"));
1568 CloseHandle(hFile);
1569 return(0);
1570 } else {
1571 tracks = (unsigned)dg.Cylinders.QuadPart;
1572 heads = (unsigned)dg.TracksPerCylinder;
1573 spt = (unsigned)dg.SectorsPerTrack;
1574 }
1575 }
1576 CloseHandle(hFile);
1577 // if using Win95 direct access, don't open the file
1578 if (!media->raw_floppy_win95)
1579 media->fd = open(sTemp, BX_RDWR);
1580 } else {
1581 media->fd = open(path, BX_RDWR);
1582 }
1583 #else
1584 media->fd = open(path, BX_RDWR);
1585 #endif
1586
1587 // Don't open the handle if using Win95 style direct access
1588 if (!media->raw_floppy_win95) {
1589 if (media->fd < 0) {
1590 BX_INFO(("tried to open '%s' read/write: %s",path,strerror(errno)));
1591 // try opening the file read-only
1592 media->write_protected = 1;
1593 #ifdef macintosh
1594 media->fd = 0;
1595 if (strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
1596 #endif
1597 #ifdef WIN32
1598 if (raw_floppy == 1) {
1599 media->fd = open(sTemp, BX_RDONLY);
1600 } else {
1601 media->fd = open(path, BX_RDONLY);
1602 }
1603 #else
1604 media->fd = open(path, BX_RDONLY);
1605 #endif
1606 if (media->fd < 0) {
1607 // failed to open read-only too
1608 BX_INFO(("tried to open '%s' read only: %s",path,strerror(errno)));
1609 media->type = type;
1610 return(0);
1611 }
1612 }
1613 }
1614
1615 #if BX_WITH_MACOS
1616 if (!strcmp(SIM->get_param_string(BXPN_FLOPPYA_PATH)->getptr(), SuperDrive))
1617 ret = fd_stat(&stat_buf);
1618 else
1619 ret = fstat(media->fd, &stat_buf);
1620 #elif defined(WIN32)
1621 if (raw_floppy) {
1622 memset (&stat_buf, 0, sizeof(stat_buf));
1623 stat_buf.st_mode = S_IFCHR;
1624 ret = 0;
1625 } else {
1626 ret = fstat(media->fd, &stat_buf);
1627 }
1628 #else
1629 // unix
1630 ret = fstat(media->fd, &stat_buf);
1631 #endif
1632 if (ret) {
1633 BX_PANIC(("fstat floppy 0 drive image file returns error: %s", strerror(errno)));
1634 return(0);
1635 }
1636
1637 if (S_ISREG(stat_buf.st_mode)) {
1638 // regular file
1639 switch (type) {
1640 // use CMOS reserved types
1641 case BX_FLOPPY_160K: // 160K 5.25"
1642 case BX_FLOPPY_180K: // 180K 5.25"
1643 case BX_FLOPPY_320K: // 320K 5.25"
1644 // standard floppy types
1645 case BX_FLOPPY_360K: // 360K 5.25"
1646 case BX_FLOPPY_720K: // 720K 3.5"
1647 case BX_FLOPPY_1_2: // 1.2M 5.25"
1648 case BX_FLOPPY_2_88: // 2.88M 3.5"
1649 media->type = type;
1650 media->tracks = floppy_type[type_idx].trk;
1651 media->heads = floppy_type[type_idx].hd;
1652 media->sectors_per_track = floppy_type[type_idx].spt;
1653 media->sectors = floppy_type[type_idx].sectors;
1654 if (stat_buf.st_size > (media->sectors * 512)) {
1655 BX_ERROR(("evaluate_media: size of file '%s' (%lu) too large for selected type",
1656 path, (unsigned long) stat_buf.st_size));
1657 return 0;
1658 }
1659 break;
1660 default: // 1.44M 3.5"
1661 media->type = type;
1662 if (stat_buf.st_size <= 1474560) {
1663 media->tracks = floppy_type[type_idx].trk;
1664 media->heads = floppy_type[type_idx].hd;
1665 media->sectors_per_track = floppy_type[type_idx].spt;
1666 }
1667 else if (stat_buf.st_size == 1720320)
1668 {
1669 media->sectors_per_track = 21;
1670 media->tracks = 80;
1671 media->heads = 2;
1672 }
1673 else if (stat_buf.st_size == 1763328)
1674 {
1675 media->sectors_per_track = 21;
1676 media->tracks = 82;
1677 media->heads = 2;
1678 }
1679 else if (stat_buf.st_size == 1884160)
1680 {
1681 media->sectors_per_track = 23;
1682 media->tracks = 80;
1683 media->heads = 2;
1684 }
1685 else
1686 {
1687 BX_ERROR(("evaluate_media: file '%s' of unknown size %lu",
1688 path, (unsigned long) stat_buf.st_size));
1689 return 0;
1690 }
1691 media->sectors = media->heads * media->tracks * media->sectors_per_track;
1692 }
1693 return (media->sectors > 0); // success
1694 }
1695
1696 else if (S_ISCHR(stat_buf.st_mode)
1697 #if BX_WITH_MACOS == 0
1698 #ifdef S_ISBLK
1699 || S_ISBLK(stat_buf.st_mode)
1700 #endif
1701 #endif
1702 ) {
1703 // character or block device
1704 // assume media is formatted to typical geometry for drive
1705 media->type = type;
1706 #ifdef __linux__
1707 if (ioctl(media->fd, FDGETPRM, &floppy_geom) < 0) {
1708 BX_ERROR(("cannot determine media geometry, trying to use defaults"));
1709 media->tracks = floppy_type[type_idx].trk;
1710 media->heads = floppy_type[type_idx].hd;
1711 media->sectors_per_track = floppy_type[type_idx].spt;
1712 media->sectors = floppy_type[type_idx].sectors;
1713 return (media->sectors > 0);
1714 }
1715 media->tracks = floppy_geom.track;
1716 media->heads = floppy_geom.head;
1717 media->sectors_per_track = floppy_geom.sect;
1718 media->sectors = floppy_geom.size;
1719 #elif defined(WIN32)
1720 media->tracks = tracks;
1721 media->heads = heads;
1722 media->sectors_per_track = spt;
1723 media->sectors = media->heads * media->tracks * media->sectors_per_track;
1724 #else
1725 media->tracks = floppy_type[type_idx].trk;
1726 media->heads = floppy_type[type_idx].hd;
1727 media->sectors_per_track = floppy_type[type_idx].spt;
1728 media->sectors = floppy_type[type_idx].sectors;
1729 #endif
1730 return (media->sectors > 0); // success
1731 } else {
1732 // unknown file type
1733 BX_ERROR(("unknown mode type"));
1734 return 0;
1735 }
1736 }
1737
1738 void bx_floppy_ctrl_c::enter_result_phase(void)
1739 {
1740 Bit8u drive;
1741 unsigned i;
1742
1743 drive = BX_FD_THIS s.DOR & 0x03;
1744
1745 /* these are always the same */
1746 BX_FD_THIS s.result_index = 0;
1747 // not necessary to clear any status bits, we're about to set them all
1748 BX_FD_THIS s.main_status_reg |= FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
1749
1750 /* invalid command */
1751 if ((BX_FD_THIS s.status_reg0 & 0xc0) == 0x80) {
1752 BX_FD_THIS s.result_size = 1;
1753 BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
1754 return;
1755 }
1756
1757 switch (BX_FD_THIS s.pending_command) {
1758 case 0x04: // get status
1759 BX_FD_THIS s.result_size = 1;
1760 BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg3;
1761 break;
1762 case 0x08: // sense interrupt
1763 BX_FD_THIS s.result_size = 2;
1764 BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
1765 BX_FD_THIS s.result[1] = BX_FD_THIS s.cylinder[drive];
1766 break;
1767 case 0x0e: // dump registers
1768 BX_FD_THIS s.result_size = 10;
1769 for (i = 0; i < 4; i++) {
1770 BX_FD_THIS s.result[i] = BX_FD_THIS s.cylinder[i];
1771 }
1772 BX_FD_THIS s.result[4] = (BX_FD_THIS s.SRT << 4) | BX_FD_THIS s.HUT;
1773 BX_FD_THIS s.result[5] = (BX_FD_THIS s.HLT << 1) | ((BX_FD_THIS s.main_status_reg & FD_MS_NDMA) ? 1 : 0);
1774 BX_FD_THIS s.result[6] = BX_FD_THIS s.eot[drive];
1775 BX_FD_THIS s.result[7] = (BX_FD_THIS s.lock << 7) | (BX_FD_THIS s.perp_mode & 0x7f);
1776 BX_FD_THIS s.result[8] = BX_FD_THIS s.config;
1777 BX_FD_THIS s.result[9] = BX_FD_THIS s.pretrk;
1778 break;
1779 case 0x10: // version
1780 BX_FD_THIS s.result_size = 1;
1781 BX_FD_THIS s.result[0] = 0x90;
1782 break;
1783 case 0x14: // unlock
1784 case 0x94: // lock
1785 BX_FD_THIS s.lock = (BX_FD_THIS s.pending_command >> 7);
1786 BX_FD_THIS s.result_size = 1;
1787 BX_FD_THIS s.result[0] = (BX_FD_THIS s.lock << 4);
1788 break;
1789 case 0x4a: // read ID
1790 case 0x4d: // format track
1791 case 0x46: // read normal data
1792 case 0x66:
1793 case 0xc6:
1794 case 0xe6:
1795 case 0x45: // write normal data
1796 case 0xc5:
1797 BX_FD_THIS s.result_size = 7;
1798 BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
1799 BX_FD_THIS s.result[1] = BX_FD_THIS s.status_reg1;
1800 BX_FD_THIS s.result[2] = BX_FD_THIS s.status_reg2;
1801 BX_FD_THIS s.result[3] = BX_FD_THIS s.cylinder[drive];
1802 BX_FD_THIS s.result[4] = BX_FD_THIS s.head[drive];
1803 BX_FD_THIS s.result[5] = BX_FD_THIS s.sector[drive];
1804 BX_FD_THIS s.result[6] = 2; /* sector size code */
1805 BX_FD_THIS raise_interrupt();
1806 break;
1807 }
1808
1809 // Print command result (max. 10 bytes)
1810 char buf[8+(10*5)+1], *p = buf;
1811 p += sprintf(p, "RESULT: ");
1812 for (i=0; i<BX_FD_THIS s.result_size; i++) {
1813 p += sprintf(p, "[%02x] ", (unsigned) BX_FD_THIS s.result[i]);
1814 }
1815 BX_DEBUG((buf));
1816 }
1817
1818 void bx_floppy_ctrl_c::enter_idle_phase(void)
1819 {
1820 BX_FD_THIS s.main_status_reg &= (FD_MS_NDMA | 0x0f); // leave drive status untouched
1821 BX_FD_THIS s.main_status_reg |= FD_MS_MRQ; // data register ready
1822
1823 BX_FD_THIS s.command_complete = 1; /* waiting for new command */
1824 BX_FD_THIS s.command_index = 0;
1825 BX_FD_THIS s.command_size = 0;
1826 BX_FD_THIS s.pending_command = 0;
1827
1828 BX_FD_THIS s.floppy_buffer_index = 0;
1829 }
1830
1831 Bit32u bx_floppy_ctrl_c::calculate_step_delay(Bit8u drive, Bit8u new_cylinder)
1832 {
1833 Bit8u steps;
1834 Bit32u one_step_delay;
1835
1836 if (new_cylinder == BX_FD_THIS s.cylinder[drive]) {
1837 steps = 1;
1838 } else {
1839 steps = abs(new_cylinder - BX_FD_THIS s.cylinder[drive]);
1840 reset_changeline();
1841 }
1842 one_step_delay = ((BX_FD_THIS s.SRT ^ 0x0f) + 1) * 500000 / drate_in_k[BX_FD_THIS s.data_rate];
1843 return (steps * one_step_delay);
1844 }
1845
1846 void bx_floppy_ctrl_c::reset_changeline(void)
1847 {
1848 Bit8u drive = BX_FD_THIS s.DOR & 0x03;
1849 if (BX_FD_THIS s.media_present[drive])
1850 BX_FD_THIS s.DIR[drive] &= ~0x80;
1851 }
1852
1853 bx_bool bx_floppy_ctrl_c::get_tc(void)
1854 {
1855 Bit8u drive;
1856 bx_bool terminal_count;
1857 if (BX_FD_THIS s.main_status_reg & FD_MS_NDMA) {
1858 drive = BX_FD_THIS s.DOR & 0x03;
1859 /* figure out if we've sent all the data, in non-DMA mode...
1860 * the drive stays on the same cylinder for a read or write, so that's
1861 * not going to be an issue. EOT stands for the last sector to be I/Od.
1862 * it does all the head 0 sectors first, then the second if any.
1863 * now, regarding reaching the end of the sector:
1864 * == 512 would make it more precise, allowing one to spot bugs...
1865 * >= 512 makes it more robust, but allows for sloppy code...
1866 * pick your poison?
1867 * note: byte and head are 0-based; eot, sector, and heads are 1-based. */
1868 terminal_count = ((BX_FD_THIS s.floppy_buffer_index == 512) &&
1869 (BX_FD_THIS s.sector[drive] == BX_FD_THIS s.eot[drive]) &&
1870 (BX_FD_THIS s.head[drive] == (BX_FD_THIS s.media[drive].heads - 1)));
1871 } else {
1872 terminal_count = DEV_dma_get_tc();
1873 }
1874 return terminal_count;
1875 }
1876
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.