blob: 109ba5b65d16efdf88d45b2fa284d14f807d271e [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to access floppy drives.
2//
Kevin O'Connorc892b132009-08-11 21:59:37 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05004// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
8#include "types.h" // u8
9#include "disk.h" // DISK_RET_SUCCESS
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040010#include "config.h" // CONFIG_FLOPPY
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'Connor92f95b02008-12-29 20:42:40 -050023struct floppy_ext_dbt_s diskette_param_table2 VAR16_32 = {
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'Connor30853762009-01-17 18:49:20 -050042// Since no provisions are made for multiple drive types, most
43// values in this table are ignored. I set parameters for 1.44M
44// floppy here
45struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7) = {
46 .specify1 = 0xAF,
47 .specify2 = 0x02, // head load time 0000001, DMA used
48 .shutoff_ticks = 0x25,
49 .bps_code = 0x02,
50 .sectors = 18,
51 .interblock_len = 0x1B,
52 .data_len = 0xFF,
53 .gap_len = 0x6C,
54 .fill_byte = 0xF6,
55 .settle_time = 0x0F,
56 .startup_time = 0x08,
57};
58
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040059struct floppyinfo_s {
60 struct chs_s chs;
61 u8 config_data;
62 u8 media_state;
63};
64
65struct floppyinfo_s FloppyInfo[] VAR16_32 = {
66 // Unknown
67 { {0, 0, 0}, 0x00, 0x00},
68 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
69 { {2, 40, 9}, 0x00, 0x25},
70 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
71 { {2, 80, 15}, 0x00, 0x25},
72 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
73 { {2, 80, 9}, 0x00, 0x17},
74 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
75 { {2, 80, 18}, 0x00, 0x17},
76 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
77 { {2, 80, 36}, 0xCC, 0xD7},
78 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
79 { {1, 40, 8}, 0x00, 0x27},
80 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
81 { {1, 40, 9}, 0x00, 0x27},
82 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
83 { {2, 40, 8}, 0x00, 0x27},
84};
85
86static void
87addFloppy(int floppyid, int ftype)
88{
89 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
90 dprintf(1, "Bad floppy type %d\n", ftype);
91 return;
92 }
93
94 int driveid = Drives.drivecount;
95 if (driveid >= ARRAY_SIZE(Drives.drives))
96 return;
97 Drives.drivecount++;
98 memset(&Drives.drives[driveid], 0, sizeof(Drives.drives[0]));
99 Drives.drives[driveid].cntl_id = floppyid;
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400100 Drives.drives[driveid].floppy_type = ftype;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400101
102 memcpy(&Drives.drives[driveid].lchs, &FloppyInfo[ftype].chs
103 , sizeof(FloppyInfo[ftype].chs));
104
105 map_floppy_drive(driveid);
106}
107
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400108void
Kevin O'Connorc892b132009-08-11 21:59:37 -0400109floppy_setup()
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400110{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400111 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400112 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400113 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400114
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500115 if (CONFIG_COREBOOT) {
116 // XXX - disable floppies on coreboot for now.
117 } else {
118 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400119 if (type & 0xf0)
120 addFloppy(0, type >> 4);
121 if (type & 0x0f)
122 addFloppy(1, type & 0x0f);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400123 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400124
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400125 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400126
Kevin O'Connord21c0892008-11-26 17:02:43 -0500127 enable_hwirq(6, entry_0e);
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400128}
129
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500130
131/****************************************************************
132 * Low-level floppy IO
133 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500134
135static void
136floppy_reset_controller()
137{
138 // Reset controller
139 u8 val8 = inb(PORT_FD_DOR);
140 outb(val8 & ~0x04, PORT_FD_DOR);
141 outb(val8 | 0x04, PORT_FD_DOR);
142
143 // Wait for controller to come out of reset
144 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
145 ;
146}
147
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500148static int
149wait_floppy_irq()
150{
151 irq_enable();
152 u8 v;
153 for (;;) {
154 if (!GET_BDA(floppy_motor_counter)) {
155 irq_disable();
156 return -1;
157 }
158 v = GET_BDA(floppy_recalibration_status);
159 if (v & FRS_TIMEOUT)
160 break;
161 cpu_relax();
162 }
163 irq_disable();
164
165 v &= ~FRS_TIMEOUT;
166 SET_BDA(floppy_recalibration_status, v);
167 return 0;
168}
169
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500170static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400171floppy_prepare_controller(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500172{
173 CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
174
175 // turn on motor of selected drive, DMA & int enabled, normal operation
176 u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
177 u8 dor = 0x10;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400178 if (floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500179 dor = 0x20;
180 dor |= 0x0c;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400181 dor |= floppyid;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500182 outb(dor, PORT_FD_DOR);
183
184 // reset the disk motor timeout value of INT 08
185 SET_BDA(floppy_motor_counter, BX_FLOPPY_ON_CNT);
186
187 // wait for drive readiness
188 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
189 ;
190
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500191 if (!prev_reset)
192 wait_floppy_irq();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500193}
194
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400195static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500196floppy_pio(u8 *cmd, u8 cmdlen)
197{
198 floppy_prepare_controller(cmd[1] & 1);
199
200 // send command to controller
201 u8 i;
202 for (i=0; i<cmdlen; i++)
203 outb(cmd[i], PORT_FD_DATA);
204
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500205 int ret = wait_floppy_irq();
206 if (ret) {
207 floppy_reset_controller();
208 return -1;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400209 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500210
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500211 return 0;
212}
213
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400214static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500215floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
216{
217 // es:bx = pointer to where to place information from diskette
Kevin O'Connor35ae7262009-01-19 15:44:44 -0500218 u32 addr = (u32)MAKE_FLATPTR(regs->es, regs->bx);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500219
220 // check for 64K boundary overrun
Kevin O'Connore10d3452008-07-09 22:45:12 -0400221 u32 last_addr = addr + count;
222 if ((addr >> 16) != (last_addr >> 16)) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400223 disk_ret(regs, DISK_RET_EBOUNDARY);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400224 return -1;
225 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500226
227 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
228 if (cmd[0] == 0xe6)
229 // read
230 mode_register = 0x46;
231
Kevin O'Connor44c631d2008-03-02 11:24:36 -0500232 //DEBUGF("floppy dma c2\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500233 outb(0x06, PORT_DMA1_MASK_REG);
234 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connore10d3452008-07-09 22:45:12 -0400235 outb(addr, PORT_DMA_ADDR_2);
236 outb(addr>>8, PORT_DMA_ADDR_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500237 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
238 outb(count, PORT_DMA_CNT_2);
239 outb(count>>8, PORT_DMA_CNT_2);
240
241 // port 0b: DMA-1 Mode Register
242 // transfer type=write, channel 2
243 outb(mode_register, PORT_DMA1_MODE_REG);
244
245 // port 81: DMA-1 Page Register, channel 2
Kevin O'Connore10d3452008-07-09 22:45:12 -0400246 outb(addr>>16, PORT_DMA_PAGE_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500247
248 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
249
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400250 int ret = floppy_pio(cmd, cmdlen);
251 if (ret) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400252 disk_ret(regs, DISK_RET_ETIMEOUT);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400253 return -1;
254 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500255
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500256 // check port 3f4 for accessibility to status bytes
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500257 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400258 disk_ret(regs, DISK_RET_ECONTROLLER);
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500259 return -1;
260 }
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500261
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500262 // read 7 return status bytes from controller
263 u8 i;
264 for (i=0; i<7; i++) {
265 u8 v = inb(PORT_FD_DATA);
266 cmd[i] = v;
267 SET_BDA(floppy_return_status[i], v);
268 }
269
270 return 0;
271}
272
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500273
274/****************************************************************
275 * Floppy media sense
276 ****************************************************************/
277
278static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400279set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500280{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400281 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500282}
283
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500284static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400285floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500286{
287 // send Recalibrate command (2 bytes) to controller
288 u8 data[12];
289 data[0] = 0x07; // 07: Recalibrate
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400290 data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500291 floppy_pio(data, 2);
292
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400293 SETBITS_BDA(floppy_recalibration_status, 1<<floppyid);
294 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500295}
296
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500297static int
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400298floppy_media_sense(u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500299{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500300 // for now cheat and get drive type from CMOS,
301 // assume media is same as drive type
302
303 // ** config_data **
304 // Bitfields for diskette media control:
305 // Bit(s) Description (Table M0028)
306 // 7-6 last data rate set by controller
307 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
308 // 5-4 last diskette drive step rate selected
309 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
310 // 3-2 {data rate at start of operation}
311 // 1-0 reserved
312
313 // ** media_state **
314 // Bitfields for diskette drive media state:
315 // Bit(s) Description (Table M0030)
316 // 7-6 data rate
317 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
318 // 5 double stepping required (e.g. 360kB in 1.2MB)
319 // 4 media type established
320 // 3 drive capable of supporting 4MB media
321 // 2-0 on exit from BIOS, contains
322 // 000 trying 360kB in 360kB
323 // 001 trying 360kB in 1.2MB
324 // 010 trying 1.2MB in 1.2MB
325 // 011 360kB in 360kB established
326 // 100 360kB in 1.2MB established
327 // 101 1.2MB in 1.2MB established
328 // 110 reserved
329 // 111 all other formats/drives
330
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400331 u8 ftype = GET_GLOBAL(Drives.drives[driveid].floppy_type);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400332 SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data));
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400333 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400334 SET_BDA(floppy_media_state[floppyid]
335 , GET_GLOBAL(FloppyInfo[ftype].media_state));
336 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500337}
338
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400339static int
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400340check_recal_drive(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500341{
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400342 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400343 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
344 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500345 // Media is known.
346 return 0;
347
348 // Recalibrate drive.
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400349 floppy_drive_recal(floppyid);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500350
351 // Sense media.
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400352 int ret = floppy_media_sense(driveid);
Kevin O'Connord626e552009-02-27 21:05:59 -0500353 if (ret) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400354 disk_ret(regs, DISK_RET_EMEDIA);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400355 return -1;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500356 }
357 return 0;
358}
359
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500360
361/****************************************************************
362 * Floppy int13 handlers
363 ****************************************************************/
364
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500365// diskette controller reset
366static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400367floppy_1300(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500368{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400369 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
370 set_diskette_current_cyl(floppyid, 0); // current cylinder
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400371 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500372}
373
374// Read Diskette Sectors
375static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400376floppy_1302(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500377{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400378 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400379 if (check_recal_drive(regs, driveid))
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400380 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500381
382 u8 num_sectors = regs->al;
383 u8 track = regs->ch;
384 u8 sector = regs->cl;
385 u8 head = regs->dh;
386
387 if (head > 1 || sector == 0 || num_sectors == 0
388 || track > 79 || num_sectors > 72) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400389 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400390 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500391 }
392
393 // send read-normal-data command (9 bytes) to controller
394 u8 data[12];
395 data[0] = 0xe6; // e6: read normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400396 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500397 data[2] = track;
398 data[3] = head;
399 data[4] = sector;
400 data[5] = 2; // 512 byte sector size
401 data[6] = sector + num_sectors - 1; // last sector to read on track
402 data[7] = 0; // Gap length
403 data[8] = 0xff; // Gap length
404
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400405 int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
406 if (ret)
407 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500408
409 if (data[0] & 0xc0) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400410 disk_ret(regs, DISK_RET_ECONTROLLER);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400411 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500412 }
413
414 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400415 set_diskette_current_cyl(floppyid, track);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500416 // AL = number of sectors read (same value as passed)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400417 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400418 return;
419fail:
420 regs->al = 0; // no sectors read
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500421}
422
423// Write Diskette Sectors
424static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400425floppy_1303(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500426{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400427 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400428 if (check_recal_drive(regs, driveid))
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400429 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500430
431 u8 num_sectors = regs->al;
432 u8 track = regs->ch;
433 u8 sector = regs->cl;
434 u8 head = regs->dh;
435
436 if (head > 1 || sector == 0 || num_sectors == 0
437 || track > 79 || num_sectors > 72) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400438 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400439 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500440 }
441
442 // send write-normal-data command (9 bytes) to controller
443 u8 data[12];
444 data[0] = 0xc5; // c5: write normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400445 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500446 data[2] = track;
447 data[3] = head;
448 data[4] = sector;
449 data[5] = 2; // 512 byte sector size
450 data[6] = sector + num_sectors - 1; // last sector to write on track
451 data[7] = 0; // Gap length
452 data[8] = 0xff; // Gap length
453
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400454 int ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
455 if (ret)
456 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500457
458 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500459 if (data[1] & 0x02)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400460 disk_ret(regs, DISK_RET_EWRITEPROTECT);
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500461 else
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400462 disk_ret(regs, DISK_RET_ECONTROLLER);
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500463 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500464 }
465
466 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400467 set_diskette_current_cyl(floppyid, track);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500468 // AL = number of sectors read (same value as passed)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400469 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400470 return;
471fail:
472 regs->al = 0; // no sectors read
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500473}
474
475// Verify Diskette Sectors
476static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400477floppy_1304(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500478{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400479 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400480 if (check_recal_drive(regs, driveid))
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400481 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500482
483 u8 num_sectors = regs->al;
484 u8 track = regs->ch;
485 u8 sector = regs->cl;
486 u8 head = regs->dh;
487
488 if (head > 1 || sector == 0 || num_sectors == 0
489 || track > 79 || num_sectors > 72) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400490 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400491 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500492 }
493
494 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400495 set_diskette_current_cyl(floppyid, track);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500496 // AL = number of sectors verified (same value as passed)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400497 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400498 return;
499fail:
500 regs->al = 0; // no sectors read
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500501}
502
503// format diskette track
504static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400505floppy_1305(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500506{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400507 u8 floppyid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
Kevin O'Connora05223c2008-06-28 12:15:57 -0400508 dprintf(3, "floppy f05\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500509
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400510 if (check_recal_drive(regs, driveid))
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500511 return;
512
513 u8 num_sectors = regs->al;
514 u8 head = regs->dh;
515
516 if (head > 1 || num_sectors == 0 || num_sectors > 18) {
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400517 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500518 return;
519 }
520
521 // send format-track command (6 bytes) to controller
522 u8 data[12];
523 data[0] = 0x4d; // 4d: format track
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400524 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500525 data[2] = 2; // 512 byte sector size
526 data[3] = num_sectors; // number of sectors per track
527 data[4] = 0; // Gap length
528 data[5] = 0xf6; // Fill byte
529
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400530 int ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
531 if (ret)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500532 return;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500533
534 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500535 if (data[1] & 0x02)
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400536 disk_ret(regs, DISK_RET_EWRITEPROTECT);
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500537 else
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400538 disk_ret(regs, DISK_RET_ECONTROLLER);
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500539 return;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500540 }
541
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400542 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400543 disk_ret(regs, DISK_RET_SUCCESS);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500544}
545
546static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400547floppy_13XX(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500548{
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400549 disk_ret(regs, DISK_RET_EPARAM);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500550}
551
552void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400553floppy_13(struct bregs *regs, u8 driveid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500554{
Kevin O'Connorb792b3c2008-02-29 00:20:32 -0500555 switch (regs->ah) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400556 case 0x00: floppy_1300(regs, driveid); break;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400557 case 0x02: floppy_1302(regs, driveid); break;
558 case 0x03: floppy_1303(regs, driveid); break;
559 case 0x04: floppy_1304(regs, driveid); break;
560 case 0x05: floppy_1305(regs, driveid); break;
Kevin O'Connor48410fd2009-08-16 14:13:36 -0400561
562 // These functions are the same as for hard disks
563 case 0x01:
564 case 0x08:
565 case 0x15:
566 case 0x16:
567 disk_13(regs, driveid);
568 break;
569
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400570 default: floppy_13XX(regs, driveid); break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500571 }
572}
573
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500574
575/****************************************************************
576 * HW irqs
577 ****************************************************************/
578
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500579// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500580void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400581handle_0e()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500582{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400583 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400584 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400585 goto done;
586
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500587 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
588 outb(0x08, PORT_FD_DATA); // sense interrupt status
589 while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
590 ;
591 do {
592 inb(PORT_FD_DATA);
593 } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
594 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500595 // diskette interrupt has occurred
596 SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
Kevin O'Connor40967022008-07-21 22:23:05 -0400597
598done:
599 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500600}
601
602// Called from int08 handler.
603void
604floppy_tick()
605{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400606 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400607 return;
608
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500609 // time to turn off drive(s)?
610 u8 fcount = GET_BDA(floppy_motor_counter);
611 if (fcount) {
612 fcount--;
613 SET_BDA(floppy_motor_counter, fcount);
614 if (fcount == 0)
615 // turn motor(s) off
616 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
617 }
618}