blob: 6354bd8bd37e6e62381354884036b56170b7d7f1 [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to access floppy drives.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
6// This file may be distributed under the terms of the GNU GPLv3 license.
7
8#include "types.h" // u8
9#include "disk.h" // DISK_RET_SUCCESS
10#include "config.h" // CONFIG_FLOPPY_SUPPORT
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "biosvar.h" // SET_BDA
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050012#include "util.h" // irq_disable
13#include "cmos.h" // inb_cmos
Kevin O'Connord21c0892008-11-26 17:02:43 -050014#include "pic.h" // eoi_pic1
Kevin O'Connor9521e262008-07-04 13:04:29 -040015#include "bregs.h" // struct bregs
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050016
17#define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
18
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050019// New diskette parameter table adding 3 parameters from IBM
20// Since no provisions are made for multiple drive types, most
21// values in this table are ignored. I set parameters for 1.44M
22// floppy here
Kevin O'Connor4a754b32008-12-28 21:37:27 -050023struct floppy_ext_dbt_s diskette_param_table2 VAR16 = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050024 .dbt = {
25 .specify1 = 0xAF,
26 .specify2 = 0x02, // head load time 0000001, DMA used
27 .shutoff_ticks = 0x25,
28 .bps_code = 0x02,
29 .sectors = 18,
30 .interblock_len = 0x1B,
31 .data_len = 0xFF,
32 .gap_len = 0x6C,
33 .fill_byte = 0xF6,
34 .settle_time = 0x0F,
35 .startup_time = 0x08,
36 },
37 .max_track = 79, // maximum track
38 .data_rate = 0, // data transfer rate
39 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050040};
41
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040042void
43floppy_drive_setup()
44{
Kevin O'Connorf54c1502008-06-14 15:56:16 -040045 if (! CONFIG_FLOPPY_SUPPORT)
46 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -040047 dprintf(3, "init floppy drives\n");
Kevin O'Connor020c4762008-08-17 11:30:07 -040048 if (CONFIG_COREBOOT)
49 // XXX - disable floppies on coreboot for now.
50 outb_cmos(0, CMOS_FLOPPY_DRIVE_TYPE);
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040051 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
52 u8 out = 0;
Kevin O'Connor88c00ee2008-05-18 00:14:13 -040053 u8 num_floppies = 0;
54
55 if (type & 0xf0) {
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040056 out |= 0x07;
Kevin O'Connor88c00ee2008-05-18 00:14:13 -040057 num_floppies++;
58 }
59 if (type & 0x0f) {
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040060 out |= 0x70;
Kevin O'Connor88c00ee2008-05-18 00:14:13 -040061 num_floppies++;
62 }
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040063 SET_BDA(floppy_harddisk_info, out);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -040064
65 // Update equipment word bits for floppy
66 if (num_floppies == 1)
67 // 1 drive, ready for boot
68 SETBITS_BDA(equipment_list_flags, 0x01);
69 else if (num_floppies == 2)
70 // 2 drives, ready for boot
71 SETBITS_BDA(equipment_list_flags, 0x41);
72
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040073 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040074
Kevin O'Connord21c0892008-11-26 17:02:43 -050075 enable_hwirq(6, entry_0e);
Kevin O'Connor3bbcc142008-04-13 17:07:33 -040076}
77
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050078static inline void
79set_diskette_current_cyl(u8 drive, u8 cyl)
80{
81 if (drive)
82 SET_BDA(floppy_track1, cyl);
83 else
84 SET_BDA(floppy_track0, cyl);
85}
86
87static u16
88get_drive_type(u8 drive)
89{
90 // check CMOS to see if drive exists
91 u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
92 if (drive == 0)
93 drive_type >>= 4;
94 else
95 drive_type &= 0x0f;
96 return drive_type;
97}
98
99static u16
100floppy_media_known(u8 drive)
101{
102 if (!(GET_BDA(floppy_recalibration_status) & (1<<drive)))
103 return 0;
104 u8 v = GET_BDA(floppy_media_state[drive]);
105 if (!(v & FMS_MEDIA_DRIVE_ESTABLISHED))
106 return 0;
107 return 1;
108}
109
110static void
111floppy_reset_controller()
112{
113 // Reset controller
114 u8 val8 = inb(PORT_FD_DOR);
115 outb(val8 & ~0x04, PORT_FD_DOR);
116 outb(val8 | 0x04, PORT_FD_DOR);
117
118 // Wait for controller to come out of reset
119 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
120 ;
121}
122
123static void
124floppy_prepare_controller(u8 drive)
125{
126 CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
127
128 // turn on motor of selected drive, DMA & int enabled, normal operation
129 u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
130 u8 dor = 0x10;
131 if (drive)
132 dor = 0x20;
133 dor |= 0x0c;
134 dor |= drive;
135 outb(dor, PORT_FD_DOR);
136
137 // reset the disk motor timeout value of INT 08
138 SET_BDA(floppy_motor_counter, BX_FLOPPY_ON_CNT);
139
140 // wait for drive readiness
141 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
142 ;
143
144 if (prev_reset == 0) {
145 irq_enable();
146 u8 v;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400147 for (;;) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500148 v = GET_BDA(floppy_recalibration_status);
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400149 if (v & FRS_TIMEOUT)
150 break;
151 cpu_relax();
152 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500153 irq_disable();
154
155 v &= ~FRS_TIMEOUT;
156 SET_BDA(floppy_recalibration_status, v);
157 }
158}
159
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400160static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500161floppy_pio(u8 *cmd, u8 cmdlen)
162{
163 floppy_prepare_controller(cmd[1] & 1);
164
165 // send command to controller
166 u8 i;
167 for (i=0; i<cmdlen; i++)
168 outb(cmd[i], PORT_FD_DATA);
169
170 irq_enable();
171 u8 v;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400172 for (;;) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500173 if (!GET_BDA(floppy_motor_counter)) {
174 irq_disable();
175 floppy_reset_controller();
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400176 return -1;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500177 }
178 v = GET_BDA(floppy_recalibration_status);
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400179 if (v & FRS_TIMEOUT)
180 break;
181 cpu_relax();
182 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500183 irq_disable();
184
185 v &= ~FRS_TIMEOUT;
186 SET_BDA(floppy_recalibration_status, v);
187
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500188 return 0;
189}
190
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400191#define floppy_ret(regs, code) \
192 __floppy_ret(__func__, __LINE__, (regs), (code))
193
194void
195__floppy_ret(const char *fname, int lineno, struct bregs *regs, u8 code)
196{
197 SET_BDA(floppy_last_status, code);
198 if (code)
199 __set_code_fail(fname, lineno, regs, code);
200 else
201 set_code_success(regs);
202}
203
204static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500205floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
206{
207 // es:bx = pointer to where to place information from diskette
Kevin O'Connore10d3452008-07-09 22:45:12 -0400208 u32 addr = (u32)MAKE_FARPTR(regs->es, regs->bx);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500209
210 // check for 64K boundary overrun
Kevin O'Connore10d3452008-07-09 22:45:12 -0400211 u32 last_addr = addr + count;
212 if ((addr >> 16) != (last_addr >> 16)) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400213 floppy_ret(regs, DISK_RET_EBOUNDARY);
214 return -1;
215 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500216
217 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
218 if (cmd[0] == 0xe6)
219 // read
220 mode_register = 0x46;
221
Kevin O'Connor44c631d2008-03-02 11:24:36 -0500222 //DEBUGF("floppy dma c2\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500223 outb(0x06, PORT_DMA1_MASK_REG);
224 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connore10d3452008-07-09 22:45:12 -0400225 outb(addr, PORT_DMA_ADDR_2);
226 outb(addr>>8, PORT_DMA_ADDR_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500227 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
228 outb(count, PORT_DMA_CNT_2);
229 outb(count>>8, PORT_DMA_CNT_2);
230
231 // port 0b: DMA-1 Mode Register
232 // transfer type=write, channel 2
233 outb(mode_register, PORT_DMA1_MODE_REG);
234
235 // port 81: DMA-1 Page Register, channel 2
Kevin O'Connore10d3452008-07-09 22:45:12 -0400236 outb(addr>>16, PORT_DMA_PAGE_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500237
238 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
239
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400240 int ret = floppy_pio(cmd, cmdlen);
241 if (ret) {
242 floppy_ret(regs, DISK_RET_ETIMEOUT);
243 return -1;
244 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500245
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500246 // check port 3f4 for accessibility to status bytes
247 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
248 BX_PANIC("int13_diskette: ctrl not ready\n");
249
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500250 // read 7 return status bytes from controller
251 u8 i;
252 for (i=0; i<7; i++) {
253 u8 v = inb(PORT_FD_DATA);
254 cmd[i] = v;
255 SET_BDA(floppy_return_status[i], v);
256 }
257
258 return 0;
259}
260
261static void
262floppy_drive_recal(u8 drive)
263{
264 // send Recalibrate command (2 bytes) to controller
265 u8 data[12];
266 data[0] = 0x07; // 07: Recalibrate
267 data[1] = drive; // 0=drive0, 1=drive1
268 floppy_pio(data, 2);
269
270 SETBITS_BDA(floppy_recalibration_status, 1<<drive);
271 set_diskette_current_cyl(drive, 0);
272}
273
274static u16
275floppy_media_sense(u8 drive)
276{
277 u16 rv;
278 u8 config_data, media_state;
279
280 floppy_drive_recal(drive);
281
282 // for now cheat and get drive type from CMOS,
283 // assume media is same as drive type
284
285 // ** config_data **
286 // Bitfields for diskette media control:
287 // Bit(s) Description (Table M0028)
288 // 7-6 last data rate set by controller
289 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
290 // 5-4 last diskette drive step rate selected
291 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
292 // 3-2 {data rate at start of operation}
293 // 1-0 reserved
294
295 // ** media_state **
296 // Bitfields for diskette drive media state:
297 // Bit(s) Description (Table M0030)
298 // 7-6 data rate
299 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
300 // 5 double stepping required (e.g. 360kB in 1.2MB)
301 // 4 media type established
302 // 3 drive capable of supporting 4MB media
303 // 2-0 on exit from BIOS, contains
304 // 000 trying 360kB in 360kB
305 // 001 trying 360kB in 1.2MB
306 // 010 trying 1.2MB in 1.2MB
307 // 011 360kB in 360kB established
308 // 100 360kB in 1.2MB established
309 // 101 1.2MB in 1.2MB established
310 // 110 reserved
311 // 111 all other formats/drives
312
313 switch (get_drive_type(drive)) {
314 case 1:
315 // 360K 5.25" drive
316 config_data = 0x00; // 0000 0000
317 media_state = 0x25; // 0010 0101
318 rv = 1;
319 break;
320 case 2:
321 // 1.2 MB 5.25" drive
322 config_data = 0x00; // 0000 0000
323 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
324 rv = 1;
325 break;
326 case 3:
327 // 720K 3.5" drive
328 config_data = 0x00; // 0000 0000 ???
329 media_state = 0x17; // 0001 0111
330 rv = 1;
331 break;
332 case 4:
333 // 1.44 MB 3.5" drive
334 config_data = 0x00; // 0000 0000
335 media_state = 0x17; // 0001 0111
336 rv = 1;
337 break;
338 case 5:
339 // 2.88 MB 3.5" drive
340 config_data = 0xCC; // 1100 1100
341 media_state = 0xD7; // 1101 0111
342 rv = 1;
343 break;
344 //
345 // Extended floppy size uses special cmos setting
346 case 6:
347 // 160k 5.25" drive
348 config_data = 0x00; // 0000 0000
349 media_state = 0x27; // 0010 0111
350 rv = 1;
351 break;
352 case 7:
353 // 180k 5.25" drive
354 config_data = 0x00; // 0000 0000
355 media_state = 0x27; // 0010 0111
356 rv = 1;
357 break;
358 case 8:
359 // 320k 5.25" drive
360 config_data = 0x00; // 0000 0000
361 media_state = 0x27; // 0010 0111
362 rv = 1;
363 break;
364 default:
365 // not recognized
366 config_data = 0x00; // 0000 0000
367 media_state = 0x00; // 0000 0000
368 rv = 0;
369 }
370
371 SET_BDA(floppy_last_data_rate, config_data);
372 SET_BDA(floppy_media_state[drive], media_state);
373 return rv;
374}
375
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400376static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500377check_drive(struct bregs *regs, u8 drive)
378{
379 // see if drive exists
380 if (drive > 1 || !get_drive_type(drive)) {
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500381 // XXX - return type doesn't match
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400382 floppy_ret(regs, DISK_RET_ETIMEOUT);
383 return -1;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500384 }
385
386 // see if media in drive, and type is known
387 if (floppy_media_known(drive) == 0 && floppy_media_sense(drive) == 0) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400388 floppy_ret(regs, DISK_RET_EMEDIA);
389 return -1;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500390 }
391 return 0;
392}
393
394// diskette controller reset
395static void
396floppy_1300(struct bregs *regs, u8 drive)
397{
398 if (drive > 1) {
399 floppy_ret(regs, DISK_RET_EPARAM);
400 return;
401 }
402 if (!get_drive_type(drive)) {
403 floppy_ret(regs, DISK_RET_ETIMEOUT);
404 return;
405 }
406 set_diskette_current_cyl(drive, 0); // current cylinder
407 floppy_ret(regs, DISK_RET_SUCCESS);
408}
409
410// Read Diskette Status
411static void
412floppy_1301(struct bregs *regs, u8 drive)
413{
414 u8 v = GET_BDA(floppy_last_status);
415 regs->ah = v;
416 set_cf(regs, v);
417}
418
419// Read Diskette Sectors
420static void
421floppy_1302(struct bregs *regs, u8 drive)
422{
423 if (check_drive(regs, drive))
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400424 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500425
426 u8 num_sectors = regs->al;
427 u8 track = regs->ch;
428 u8 sector = regs->cl;
429 u8 head = regs->dh;
430
431 if (head > 1 || sector == 0 || num_sectors == 0
432 || track > 79 || num_sectors > 72) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400433 floppy_ret(regs, DISK_RET_EPARAM);
434 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500435 }
436
437 // send read-normal-data command (9 bytes) to controller
438 u8 data[12];
439 data[0] = 0xe6; // e6: read normal data
440 data[1] = (head << 2) | drive; // HD DR1 DR2
441 data[2] = track;
442 data[3] = head;
443 data[4] = sector;
444 data[5] = 2; // 512 byte sector size
445 data[6] = sector + num_sectors - 1; // last sector to read on track
446 data[7] = 0; // Gap length
447 data[8] = 0xff; // Gap length
448
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400449 int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
450 if (ret)
451 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500452
453 if (data[0] & 0xc0) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400454 floppy_ret(regs, DISK_RET_ECONTROLLER);
455 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500456 }
457
458 // ??? should track be new val from return_status[3] ?
459 set_diskette_current_cyl(drive, track);
460 // AL = number of sectors read (same value as passed)
461 floppy_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400462 return;
463fail:
464 regs->al = 0; // no sectors read
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500465}
466
467// Write Diskette Sectors
468static void
469floppy_1303(struct bregs *regs, u8 drive)
470{
471 if (check_drive(regs, drive))
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400472 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500473
474 u8 num_sectors = regs->al;
475 u8 track = regs->ch;
476 u8 sector = regs->cl;
477 u8 head = regs->dh;
478
479 if (head > 1 || sector == 0 || num_sectors == 0
480 || track > 79 || num_sectors > 72) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400481 floppy_ret(regs, DISK_RET_EPARAM);
482 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500483 }
484
485 // send write-normal-data command (9 bytes) to controller
486 u8 data[12];
487 data[0] = 0xc5; // c5: write normal data
488 data[1] = (head << 2) | drive; // HD DR1 DR2
489 data[2] = track;
490 data[3] = head;
491 data[4] = sector;
492 data[5] = 2; // 512 byte sector size
493 data[6] = sector + num_sectors - 1; // last sector to write on track
494 data[7] = 0; // Gap length
495 data[8] = 0xff; // Gap length
496
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400497 int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
498 if (ret)
499 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500500
501 if (data[0] & 0xc0) {
502 if (data[1] & 0x02) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400503 floppy_ret(regs, DISK_RET_EWRITEPROTECT);
504 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500505 }
506 BX_PANIC("int13_diskette_function: read error\n");
507 }
508
509 // ??? should track be new val from return_status[3] ?
510 set_diskette_current_cyl(drive, track);
511 // AL = number of sectors read (same value as passed)
512 floppy_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400513 return;
514fail:
515 regs->al = 0; // no sectors read
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500516}
517
518// Verify Diskette Sectors
519static void
520floppy_1304(struct bregs *regs, u8 drive)
521{
522 if (check_drive(regs, drive))
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400523 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500524
525 u8 num_sectors = regs->al;
526 u8 track = regs->ch;
527 u8 sector = regs->cl;
528 u8 head = regs->dh;
529
530 if (head > 1 || sector == 0 || num_sectors == 0
531 || track > 79 || num_sectors > 72) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400532 floppy_ret(regs, DISK_RET_EPARAM);
533 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500534 }
535
536 // ??? should track be new val from return_status[3] ?
537 set_diskette_current_cyl(drive, track);
538 // AL = number of sectors verified (same value as passed)
539 floppy_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400540 return;
541fail:
542 regs->al = 0; // no sectors read
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500543}
544
545// format diskette track
546static void
547floppy_1305(struct bregs *regs, u8 drive)
548{
Kevin O'Connora05223c2008-06-28 12:15:57 -0400549 dprintf(3, "floppy f05\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500550
551 if (check_drive(regs, drive))
552 return;
553
554 u8 num_sectors = regs->al;
555 u8 head = regs->dh;
556
557 if (head > 1 || num_sectors == 0 || num_sectors > 18) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400558 floppy_ret(regs, DISK_RET_EPARAM);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500559 return;
560 }
561
562 // send format-track command (6 bytes) to controller
563 u8 data[12];
564 data[0] = 0x4d; // 4d: format track
565 data[1] = (head << 2) | drive; // HD DR1 DR2
566 data[2] = 2; // 512 byte sector size
567 data[3] = num_sectors; // number of sectors per track
568 data[4] = 0; // Gap length
569 data[5] = 0xf6; // Fill byte
570
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400571 int ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
572 if (ret)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500573 return;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500574
575 if (data[0] & 0xc0) {
576 if (data[1] & 0x02) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400577 floppy_ret(regs, DISK_RET_EWRITEPROTECT);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500578 return;
579 }
580 BX_PANIC("int13_diskette_function: read error\n");
581 }
582
583 set_diskette_current_cyl(drive, 0);
584 floppy_ret(regs, 0);
585}
586
587// read diskette drive parameters
588static void
589floppy_1308(struct bregs *regs, u8 drive)
590{
Kevin O'Connora05223c2008-06-28 12:15:57 -0400591 dprintf(3, "floppy f08\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500592
593 u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
594 u8 num_floppies = 0;
595 if (drive_type & 0xf0)
596 num_floppies++;
597 if (drive_type & 0x0f)
598 num_floppies++;
599
600 if (drive > 1) {
601 regs->ax = 0;
602 regs->bx = 0;
603 regs->cx = 0;
604 regs->dx = 0;
605 regs->es = 0;
606 regs->di = 0;
607 regs->dl = num_floppies;
Kevin O'Connore10d3452008-07-09 22:45:12 -0400608 set_fail(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500609 return;
610 }
611
612 if (drive == 0)
613 drive_type >>= 4;
614 else
615 drive_type &= 0x0f;
616
617 regs->bh = 0;
618 regs->bl = drive_type;
619 regs->ah = 0;
620 regs->al = 0;
621 regs->dl = num_floppies;
622
623 switch (drive_type) {
624 case 0: // none
625 regs->cx = 0;
626 regs->dh = 0; // max head #
627 break;
628
629 case 1: // 360KB, 5.25"
630 regs->cx = 0x2709; // 40 tracks, 9 sectors
631 regs->dh = 1; // max head #
632 break;
633
634 case 2: // 1.2MB, 5.25"
635 regs->cx = 0x4f0f; // 80 tracks, 15 sectors
636 regs->dh = 1; // max head #
637 break;
638
639 case 3: // 720KB, 3.5"
640 regs->cx = 0x4f09; // 80 tracks, 9 sectors
641 regs->dh = 1; // max head #
642 break;
643
644 case 4: // 1.44MB, 3.5"
645 regs->cx = 0x4f12; // 80 tracks, 18 sectors
646 regs->dh = 1; // max head #
647 break;
648
649 case 5: // 2.88MB, 3.5"
650 regs->cx = 0x4f24; // 80 tracks, 36 sectors
651 regs->dh = 1; // max head #
652 break;
653
654 case 6: // 160k, 5.25"
655 regs->cx = 0x2708; // 40 tracks, 8 sectors
656 regs->dh = 0; // max head #
657 break;
658
659 case 7: // 180k, 5.25"
660 regs->cx = 0x2709; // 40 tracks, 9 sectors
661 regs->dh = 0; // max head #
662 break;
663
664 case 8: // 320k, 5.25"
665 regs->cx = 0x2708; // 40 tracks, 8 sectors
666 regs->dh = 1; // max head #
667 break;
668
669 default: // ?
670 BX_PANIC("floppy: int13: bad floppy type\n");
671 }
672
673 /* set es & di to point to 11 byte diskette param table in ROM */
674 regs->es = SEG_BIOS;
Kevin O'Connor117fc212008-04-13 18:17:02 -0400675 regs->di = (u32)&diskette_param_table2;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500676 /* disk status not changed upon success */
Kevin O'Connore10d3452008-07-09 22:45:12 -0400677 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500678}
679
680// read diskette drive type
681static void
682floppy_1315(struct bregs *regs, u8 drive)
683{
Kevin O'Connora05223c2008-06-28 12:15:57 -0400684 dprintf(6, "floppy f15\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500685 if (drive > 1) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400686 set_fail(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500687 regs->ah = 0; // only 2 drives supported
688 // set_diskette_ret_status here ???
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500689 return;
690 }
691 u8 drive_type = get_drive_type(drive);
692
693 regs->ah = (drive_type != 0);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400694 set_success(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500695}
696
697// get diskette change line status
698static void
699floppy_1316(struct bregs *regs, u8 drive)
700{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500701 if (drive > 1) {
702 floppy_ret(regs, DISK_RET_EPARAM);
703 return;
704 }
705 floppy_ret(regs, DISK_RET_ECHANGED);
706}
707
708static void
709floppy_13XX(struct bregs *regs, u8 drive)
710{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500711 floppy_ret(regs, DISK_RET_EPARAM);
712}
713
714void
715floppy_13(struct bregs *regs, u8 drive)
716{
Kevin O'Connorb792b3c2008-02-29 00:20:32 -0500717 if (! CONFIG_FLOPPY_SUPPORT) {
718 // Minimal stubs
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500719 switch (regs->ah) {
720 case 0x01: floppy_1301(regs, drive); break;
721 default: floppy_13XX(regs, drive); break;
722 }
Kevin O'Connorb792b3c2008-02-29 00:20:32 -0500723 return;
724 }
725 switch (regs->ah) {
726 case 0x00: floppy_1300(regs, drive); break;
727 case 0x01: floppy_1301(regs, drive); break;
728 case 0x02: floppy_1302(regs, drive); break;
729 case 0x03: floppy_1303(regs, drive); break;
730 case 0x04: floppy_1304(regs, drive); break;
731 case 0x05: floppy_1305(regs, drive); break;
732 case 0x08: floppy_1308(regs, drive); break;
733 case 0x15: floppy_1315(regs, drive); break;
734 case 0x16: floppy_1316(regs, drive); break;
735 default: floppy_13XX(regs, drive); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500736 }
737}
738
739// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500740void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400741handle_0e()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500742{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400743 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400744 if (! CONFIG_FLOPPY_SUPPORT)
745 goto done;
746
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500747 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
748 outb(0x08, PORT_FD_DATA); // sense interrupt status
749 while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
750 ;
751 do {
752 inb(PORT_FD_DATA);
753 } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
754 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500755 // diskette interrupt has occurred
756 SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
Kevin O'Connor40967022008-07-21 22:23:05 -0400757
758done:
759 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500760}
761
762// Called from int08 handler.
763void
764floppy_tick()
765{
Kevin O'Connor40967022008-07-21 22:23:05 -0400766 if (! CONFIG_FLOPPY_SUPPORT)
767 return;
768
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500769 // time to turn off drive(s)?
770 u8 fcount = GET_BDA(floppy_motor_counter);
771 if (fcount) {
772 fcount--;
773 SET_BDA(floppy_motor_counter, fcount);
774 if (fcount == 0)
775 // turn motor(s) off
776 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
777 }
778}