removable device support - io port API change
[qemu] / hw / fdc.c
1 /*
2  * QEMU Floppy disk emulator
3  * 
4  * Copyright (c) 2003 Jocelyn Mayer
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <inttypes.h>
28
29 #include "vl.h"
30
31 /********************************************************/
32 /* debug Floppy devices */
33 //#define DEBUG_FLOPPY
34
35 #ifdef DEBUG_FLOPPY
36 #define FLOPPY_DPRINTF(fmt, args...) \
37 do { printf("FLOPPY: " fmt , ##args); } while (0)
38 #else
39 #define FLOPPY_DPRINTF(fmt, args...)
40 #endif
41
42 #define FLOPPY_ERROR(fmt, args...) \
43 do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)
44
45 /********************************************************/
46 /* Floppy drive emulation                               */
47
48 /* Will always be a fixed parameter for us */
49 #define FD_SECTOR_LEN 512
50 #define FD_SECTOR_SC  2   /* Sector size code */
51
52 /* Floppy disk drive emulation */
53 typedef enum fdisk_type_t {
54     FDRIVE_DISK_288   = 0x01, /* 2.88 MB disk           */
55     FDRIVE_DISK_144   = 0x02, /* 1.44 MB disk           */
56     FDRIVE_DISK_720   = 0x03, /* 720 kB disk            */
57     FDRIVE_DISK_NONE  = 0x04, /* No disk                */
58 } fdisk_type_t;
59
60 typedef enum fdrive_type_t {
61     FDRIVE_DRV_144  = 0x00,   /* 1.44 MB 3"5 drive      */
62     FDRIVE_DRV_288  = 0x01,   /* 2.88 MB 3"5 drive      */
63     FDRIVE_DRV_120  = 0x02,   /* 1.2  MB 5"25 drive     */
64     FDRIVE_DRV_NONE = 0x03,   /* No drive connected     */
65 } fdrive_type_t;
66
67 typedef struct fdrive_t {
68     BlockDriverState *bs;
69     /* Drive status */
70     fdrive_type_t drive;
71     uint8_t motor;            /* on/off                 */
72     uint8_t perpendicular;    /* 2.88 MB access mode    */
73     uint8_t rv;               /* Revalidated            */
74     /* Position */
75     uint8_t head;
76     uint8_t track;
77     uint8_t sect;
78     /* Last operation status */
79     uint8_t dir;              /* Direction              */
80     uint8_t rw;               /* Read/write             */
81     /* Media */
82     fdisk_type_t disk;        /* Disk type              */
83     uint8_t last_sect;        /* Nb sector per track    */
84     uint8_t max_track;        /* Nb of tracks           */
85     uint8_t ro;               /* Is read-only           */
86 } fdrive_t;
87
88 static void fd_init (fdrive_t *drv, BlockDriverState *bs)
89 {
90     /* Drive */
91     drv->bs = bs;
92     if (bs)
93         drv->drive = FDRIVE_DRV_144;
94     else
95         drv->drive = FDRIVE_DRV_NONE;
96     drv->motor = 0;
97     drv->perpendicular = 0;
98     drv->rv = 0;
99     /* Disk */
100     drv->disk = FDRIVE_DISK_NONE;
101     drv->last_sect = 1;
102     drv->max_track = 0;
103 }
104
105 static int _fd_sector (uint8_t head, uint8_t track,
106                         uint8_t sect, uint8_t last_sect)
107 {
108     return (((track * 2) + head) * last_sect) + sect - 1;
109 }
110
111 /* Returns current position, in sectors, for given drive */
112 static int fd_sector (fdrive_t *drv)
113 {
114     return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect);
115 }
116
117 static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
118                     int enable_seek)
119 {
120     uint32_t sector;
121
122     if (track > drv->max_track) {
123         FLOPPY_ERROR("try to read %d %02x %02x (max=%d %02x %02x)\n",
124                      head, track, sect, 1, drv->max_track, drv->last_sect);
125         return 2;
126     }
127     if (sect > drv->last_sect) {
128         FLOPPY_ERROR("try to read %d %02x %02x (max=%d %02x %02x)\n",
129                      head, track, sect, 1, drv->max_track, drv->last_sect);
130         return 3;
131     }
132     sector = _fd_sector(head, track, sect, drv->last_sect);
133     if (sector != fd_sector(drv)) {
134 #if 0
135         if (!enable_seek) {
136             FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
137                          head, track, sect, 1, drv->max_track, drv->last_sect);
138             return 4;
139         }
140 #endif
141         drv->head = head;
142         drv->track = track;
143         drv->sect = sect;
144         return 1;
145     }
146
147     return 0;
148 }
149
150 /* Set drive back to track 0 */
151 static void fd_recalibrate (fdrive_t *drv)
152 {
153     FLOPPY_DPRINTF("recalibrate\n");
154     drv->head = 0;
155     drv->track = 0;
156     drv->sect = 1;
157     drv->dir = 1;
158     drv->rw = 0;
159 }
160
161 /* Revalidate a disk drive after a disk change */
162 static void fd_revalidate (fdrive_t *drv)
163 {
164     int64_t nb_sectors;
165
166     FLOPPY_DPRINTF("revalidate\n");
167     drv->rv = 0;
168     /* if no drive present, cannot do more */
169     if (!drv->bs)
170         return;
171         
172     if (bdrv_is_inserted(drv->bs)) {
173         bdrv_get_geometry(drv->bs, &nb_sectors);
174 #if 1
175         if (nb_sectors > 2880) 
176 #endif
177         {
178             /* Pretend we have a 2.88 MB disk */
179             drv->disk = FDRIVE_DISK_288;
180             drv->last_sect = 36;
181             drv->max_track = 80;
182 #if 1
183         } else if (nb_sectors > 1440) {
184             /* Pretend we have a 1.44 MB disk */
185             drv->disk = FDRIVE_DISK_144;
186             drv->last_sect = 18;
187             drv->max_track = 80;
188         } else {
189             /* Pretend we have a 720 kB disk */
190             drv->disk = FDRIVE_DISK_720;
191             drv->last_sect = 9;
192             drv->max_track = 80;
193 #endif
194         }
195         drv->ro = bdrv_is_read_only(drv->bs);
196     } else {
197         drv->disk = FDRIVE_DISK_NONE;
198         drv->last_sect = 1; /* Avoid eventual divide by 0 bugs */
199         drv->ro = 0;
200     }
201     drv->rv = 1;
202 }
203
204 static void fd_change_cb (void *opaque)
205 {
206     fdrive_t *drv = opaque;
207     fd_revalidate(drv);
208 }
209
210 /* Motor control */
211 static void fd_start (fdrive_t *drv)
212 {
213     drv->motor = 1;
214 }
215
216 static void fd_stop (fdrive_t *drv)
217 {
218     drv->motor = 0;
219 }
220
221 /* Re-initialise a drives (motor off, repositioned) */
222 static void fd_reset (fdrive_t *drv)
223 {
224     fd_stop(drv);
225     fd_recalibrate(drv);
226 }
227
228 /********************************************************/
229 /* Intel 82078 floppy disk controler emulation          */
230
231 static void fdctrl_reset (int do_irq);
232 static void fdctrl_reset_fifo (void);
233 static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size);
234 static void fdctrl_raise_irq (uint8_t status);
235
236 static uint32_t fdctrl_read_statusB (void *opaque, uint32_t reg);
237 static uint32_t fdctrl_read_dor (void *opaque, uint32_t reg);
238 static void fdctrl_write_dor (void *opaque, uint32_t reg, uint32_t value);
239 static uint32_t fdctrl_read_tape (void *opaque, uint32_t reg);
240 static void fdctrl_write_tape (void *opaque, uint32_t reg, uint32_t value);
241 static uint32_t fdctrl_read_main_status (void *opaque, uint32_t reg);
242 static void fdctrl_write_rate (void *opaque, uint32_t reg, uint32_t value);
243 static uint32_t fdctrl_read_data (void *opaque, uint32_t reg);
244 static void fdctrl_write_data (void *opaque, uint32_t reg, uint32_t value);
245 static uint32_t fdctrl_read_dir (void *opaque, uint32_t reg);
246
247 enum {
248     FD_CTRL_ACTIVE = 0x01,
249     FD_CTRL_RESET  = 0x02,
250     FD_CTRL_SLEEP  = 0x04,
251     FD_CTRL_BUSY   = 0x08,
252     FD_CTRL_INTR   = 0x10,
253 };
254
255 enum {
256     FD_DIR_WRITE   = 0,
257     FD_DIR_READ    = 1,
258     FD_DIR_SCANE   = 2,
259     FD_DIR_SCANL   = 3,
260     FD_DIR_SCANH   = 4,
261 };
262
263 enum {
264     FD_STATE_CMD    = 0x00,
265     FD_STATE_STATUS = 0x01,
266     FD_STATE_DATA   = 0x02,
267     FD_STATE_STATE  = 0x03,
268     FD_STATE_MULTI  = 0x10,
269     FD_STATE_SEEK   = 0x20,
270 };
271
272 #define FD_STATE(state) ((state) & FD_STATE_STATE)
273 #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
274 #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
275
276 typedef struct fdctrl_t {
277     /* Controler's identification */
278     uint8_t version;
279     /* HW */
280     int irq_lvl;
281     int dma_chann;
282     /* Controler state */
283     uint8_t state;
284     uint8_t dma_en;
285     uint8_t cur_drv;
286     uint8_t bootsel;
287     /* Command FIFO */
288     uint8_t fifo[FD_SECTOR_LEN];
289     uint32_t data_pos;
290     uint32_t data_len;
291     uint8_t data_state;
292     uint8_t data_dir;
293     uint8_t int_status;
294     /* States kept only to be returned back */
295     /* Timers state */
296     uint8_t timer0;
297     uint8_t timer1;
298     /* precompensation */
299     uint8_t precomp_trk;
300     uint8_t config;
301     uint8_t lock;
302     /* Power down config (also with status regB access mode */
303     uint8_t pwrd;
304     /* Floppy drives */
305     fdrive_t drives[2];
306 } fdctrl_t;
307
308 static fdctrl_t fdctrl;
309
310 void fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, uint32_t base,
311                   BlockDriverState **fds)
312 {
313 //    int io_mem;
314     int i;
315
316     FLOPPY_DPRINTF("init controler\n");
317     memset(&fdctrl, 0, sizeof(fdctrl));
318     fdctrl.version = 0x90; /* Intel 82078 controler */
319     fdctrl.irq_lvl = irq_lvl;
320     fdctrl.dma_chann = dma_chann;
321     fdctrl.config = 0x40; /* Implicit seek, polling & FIFO enabled */
322     if (fdctrl.dma_chann != -1) {
323         fdctrl.dma_en = 1;
324         DMA_register_channel(dma_chann, fdctrl_transfer_handler, &fdctrl);
325     } else {
326         fdctrl.dma_en = 0;
327     }
328     for (i = 0; i < MAX_FD; i++) {
329         fd_init(&fdctrl.drives[i], fds[i]);
330         if (fds[i])
331             bdrv_set_change_cb(fds[i], fd_change_cb, &fdctrl.drives[i]);
332     }
333     fdctrl_reset(0);
334     fdctrl.state = FD_CTRL_ACTIVE;
335     if (mem_mapped) {
336         FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
337 #if 0
338         io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
339         cpu_register_physical_memory(base, 0x08, io_mem);
340 #endif
341     } else {
342         register_ioport_read(base + 0x01, 1, 1, fdctrl_read_statusB, NULL);
343         register_ioport_read(base + 0x02, 1, 1, fdctrl_read_dor, NULL);
344         register_ioport_write(base + 0x02, 1, 1, fdctrl_write_dor, NULL);
345         register_ioport_read(base + 0x03, 1, 1, fdctrl_read_tape, NULL);
346         register_ioport_write(base + 0x03, 1, 1, fdctrl_write_tape, NULL);
347         register_ioport_read(base + 0x04, 1, 1, fdctrl_read_main_status, NULL);
348         register_ioport_write(base + 0x04, 1, 1, fdctrl_write_rate, NULL);
349         register_ioport_read(base + 0x05, 1, 1, fdctrl_read_data, NULL);
350         register_ioport_write(base + 0x05, 1, 1, fdctrl_write_data, NULL);
351         register_ioport_read(base + 0x07, 1, 1, fdctrl_read_dir, NULL);
352     }
353     fdctrl.bootsel = 0;
354     
355     for (i = 0; i < MAX_FD; i++) {
356         fd_revalidate(&fdctrl.drives[i]);
357     }
358 }
359
360 int fdctrl_get_drive_type(int drive_num)
361 {
362     return fdctrl.drives[drive_num].drive;
363 }
364
365 /* Change IRQ state */
366 static void fdctrl_reset_irq (void)
367 {
368     if (fdctrl.state & FD_CTRL_INTR) {
369         pic_set_irq(fdctrl.irq_lvl, 0);
370         fdctrl.state &= ~(FD_CTRL_INTR | FD_CTRL_SLEEP | FD_CTRL_BUSY);
371     }
372 }
373
374 static void fdctrl_raise_irq (uint8_t status)
375 {
376     if (~(fdctrl.state & FD_CTRL_INTR)) {
377         pic_set_irq(fdctrl.irq_lvl, 1);
378         fdctrl.state |= FD_CTRL_INTR;
379     }
380     FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status);
381     fdctrl.int_status = status;
382 }
383
384 /* Reset controler */
385 static void fdctrl_reset (int do_irq)
386 {
387     int i;
388
389     FLOPPY_DPRINTF("reset controler\n");
390     fdctrl_reset_irq();
391     /* Initialise controler */
392     fdctrl.cur_drv = 0;
393     /* FIFO state */
394     fdctrl.data_pos = 0;
395     fdctrl.data_len = 0;
396     fdctrl.data_state = FD_STATE_CMD;
397     fdctrl.data_dir = FD_DIR_WRITE;
398     for (i = 0; i < MAX_FD; i++)
399         fd_reset(&fdctrl.drives[i]);
400     fdctrl_reset_fifo();
401     if (do_irq)
402         fdctrl_raise_irq(0x20);
403 }
404
405 /* Status B register : 0x01 (read-only) */
406 static uint32_t fdctrl_read_statusB (void *opaque, uint32_t reg)
407 {
408     fdctrl_reset_irq();
409     FLOPPY_DPRINTF("status register: 0x00\n");
410
411     return 0;
412 }
413
414 /* Digital output register : 0x02 */
415 static uint32_t fdctrl_read_dor (void *opaque, uint32_t reg)
416 {
417     fdrive_t *cur_drv, *drv0, *drv1;
418     uint32_t retval = 0;
419
420     drv0 = &fdctrl.drives[fdctrl.bootsel];
421     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
422     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
423     /* Drive motors state indicators */
424     retval |= drv1->motor << 5;
425     retval |= drv0->motor << 4;
426     /* DMA enable */
427     retval |= fdctrl.dma_en << 3;
428     /* Reset indicator */
429     retval |= (fdctrl.state & FD_CTRL_RESET) == 0 ? 0x04 : 0;
430     /* Selected drive */
431     retval |= fdctrl.cur_drv;
432     FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
433
434     return retval;
435 }
436
437 static void fdctrl_write_dor (void *opaque, uint32_t reg, uint32_t value)
438 {
439     fdrive_t *drv0, *drv1;
440     
441     fdctrl_reset_irq();
442     drv0 = &fdctrl.drives[fdctrl.bootsel];
443     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
444     /* Reset mode */
445     if (fdctrl.state & FD_CTRL_RESET) {
446         if (!(value & 0x04)) {
447             FLOPPY_DPRINTF("Floppy controler in RESET state !\n");
448             return;
449         }
450     }
451     FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
452     /* Drive motors state indicators */
453     if (value & 0x20)
454         fd_start(drv1);
455     else
456         fd_stop(drv1);
457     if (value & 0x10)
458         fd_start(drv0);
459     else
460         fd_stop(drv0);
461     /* DMA enable */
462 #if 0
463     if (fdctrl.dma_chann != -1)
464         fdctrl.dma_en = 1 - ((value >> 3) & 1);
465 #endif
466     /* Reset */
467     if (!(value & 0x04)) {
468         if (!(fdctrl.state & FD_CTRL_RESET)) {
469             FLOPPY_DPRINTF("controler enter RESET state\n");
470             fdctrl.state |= FD_CTRL_RESET;
471             fdctrl_reset(1);
472         }
473     } else {
474         if (fdctrl.state & FD_CTRL_RESET) {
475             FLOPPY_DPRINTF("controler out of RESET state\n");
476             fdctrl.state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP);
477         }
478     }
479     /* Selected drive */
480     fdctrl.cur_drv = value & 1;
481 }
482
483 /* Tape drive register : 0x03 */
484 static uint32_t fdctrl_read_tape (void *opaque, uint32_t reg)
485 {
486     uint32_t retval = 0;
487
488     fdctrl_reset_irq();
489     /* Disk boot selection indicator */
490     retval |= fdctrl.bootsel << 2;
491     /* Tape indicators: never allowed */
492     FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
493
494     return retval;
495 }
496
497 static void fdctrl_write_tape (void *opaque, uint32_t reg, uint32_t value)
498 {
499     fdctrl_reset_irq();
500     /* Reset mode */
501     if (fdctrl.state & FD_CTRL_RESET) {
502         FLOPPY_DPRINTF("Floppy controler in RESET state !\n");
503         return;
504     }
505     FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
506     /* Disk boot selection indicator */
507     fdctrl.bootsel = (value >> 2) & 1;
508     /* Tape indicators: never allow */
509 }
510
511 /* Main status register : 0x04 (read) */
512 static uint32_t fdctrl_read_main_status (void *opaque, uint32_t reg)
513 {
514     uint32_t retval = 0;
515
516     fdctrl_reset_irq();
517     fdctrl.state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET);
518     if (!(fdctrl.state & FD_CTRL_BUSY)) {
519         /* Data transfer allowed */
520         retval |= 0x80;
521         /* Data transfer direction indicator */
522         if (fdctrl.data_dir == FD_DIR_READ)
523             retval |= 0x40;
524     }
525     /* Should handle 0x20 for SPECIFY command */
526     /* Command busy indicator */
527     if (FD_STATE(fdctrl.data_state) == FD_STATE_DATA ||
528         FD_STATE(fdctrl.data_state) == FD_STATE_STATUS)
529         retval |= 0x10;
530     FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
531
532     return retval;
533 }
534
535 /* Data select rate register : 0x04 (write) */
536 static void fdctrl_write_rate (void *opaque, uint32_t reg, uint32_t value)
537 {
538     fdctrl_reset_irq();
539     /* Reset mode */
540     if (fdctrl.state & FD_CTRL_RESET) {
541         if (reg != 0x2 || !(value & 0x04)) {
542             FLOPPY_DPRINTF("Floppy controler in RESET state !\n");
543             return;
544         }
545     }
546     FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
547     /* Reset: autoclear */
548     if (value & 0x80) {
549         fdctrl.state |= FD_CTRL_RESET;
550         fdctrl_reset(1);
551         fdctrl.state &= ~FD_CTRL_RESET;
552     }
553     if (value & 0x40) {
554         fdctrl.state |= FD_CTRL_SLEEP;
555         fdctrl_reset(1);
556     }
557 //        fdctrl.precomp = (value >> 2) & 0x07;
558 }
559
560 /* Digital input register : 0x07 (read-only) */
561 static uint32_t fdctrl_read_dir (void *opaque, uint32_t reg)
562 {
563     fdrive_t *drv0, *drv1;
564     uint32_t retval = 0;
565
566     fdctrl_reset_irq();
567     drv0 = &fdctrl.drives[fdctrl.bootsel];
568     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
569     if (drv0->rv || drv1->rv)
570         retval |= 0x80;
571     if (retval != 0)
572         FLOPPY_ERROR("Floppy digital input register: 0x%02x\n", retval);
573     drv0->rv = 0;
574     drv1->rv = 0;
575
576     return retval;
577 }
578
579 /* FIFO state control */
580 static void fdctrl_reset_fifo (void)
581 {
582     fdctrl.data_dir = FD_DIR_WRITE;
583     fdctrl.data_pos = 0;
584     fdctrl.data_state = FD_STATE_CMD;
585 }
586
587 /* Set FIFO status for the host to read */
588 static void fdctrl_set_fifo (int fifo_len, int do_irq)
589 {
590     fdctrl.data_dir = FD_DIR_READ;
591     fdctrl.data_len = fifo_len;
592     fdctrl.data_pos = 0;
593     fdctrl.data_state = FD_STATE_STATUS;
594     if (do_irq)
595         fdctrl_raise_irq(0x00);
596 }
597
598 /* Set an error: unimplemented/unknown command */
599 static void fdctrl_unimplemented (void)
600 {
601 #if 0
602     fdrive_t *cur_drv, *drv0, *drv1;
603
604     drv0 = &fdctrl.drives[fdctrl.bootsel];
605     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
606     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
607     fdctrl.fifo[0] = 0x60 | (cur_drv->head << 1) | fdctrl.cur_drv;
608     fdctrl.fifo[1] = 0x00;
609     fdctrl.fifo[2] = 0x00;
610     fdctrl_set_fifo(3, 1);
611 #else
612     fdctrl_reset_fifo();
613 #endif
614 }
615
616 /* Callback for transfer end (stop or abort) */
617 static void fdctrl_stop_transfer (uint8_t status0, uint8_t status1,
618                                   uint8_t status2)
619 {
620     fdrive_t *cur_drv, *drv0, *drv1;
621
622     drv0 = &fdctrl.drives[fdctrl.bootsel];
623     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
624     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
625     FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
626                    status0, status1, status2,
627                    status0 | (cur_drv->head << 1) | fdctrl.cur_drv);
628     fdctrl.fifo[0] = status0 | (cur_drv->head << 1) | fdctrl.cur_drv;
629     fdctrl.fifo[1] = status1;
630     fdctrl.fifo[2] = status2;
631     fdctrl.fifo[3] = cur_drv->track;
632     fdctrl.fifo[4] = cur_drv->head;
633     fdctrl.fifo[5] = cur_drv->sect;
634     fdctrl.fifo[6] = FD_SECTOR_SC;
635     fdctrl.data_dir = FD_DIR_READ;
636     if (fdctrl.state & FD_CTRL_BUSY)
637         DMA_release_DREQ(fdctrl.dma_chann);
638     fdctrl_set_fifo(7, 1);
639 }
640
641 /* Prepare a data transfer (either DMA or FIFO) */
642 static void fdctrl_start_transfer (int direction)
643 {
644     fdrive_t *cur_drv, *drv0, *drv1;
645     uint8_t kh, kt, ks;
646     int did_seek;
647
648     drv0 = &fdctrl.drives[fdctrl.bootsel];
649     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
650     fdctrl.cur_drv = fdctrl.fifo[1] & 1;
651     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
652     kt = fdctrl.fifo[2];
653     kh = fdctrl.fifo[3];
654     ks = fdctrl.fifo[4];
655     FLOPPY_DPRINTF("Start tranfert at %d %d %02x %02x (%d)\n",
656                    fdctrl.cur_drv, kh, kt, ks,
657                    _fd_sector(kh, kt, ks, cur_drv->last_sect));
658     did_seek = 0;
659     switch (fd_seek(cur_drv, kh, kt, ks, fdctrl.config & 0x40)) {
660     case 2:
661         /* sect too big */
662         fdctrl_stop_transfer(0x40, 0x00, 0x00);
663         fdctrl.fifo[3] = kt;
664         fdctrl.fifo[4] = kh;
665         fdctrl.fifo[5] = ks;
666         return;
667     case 3:
668         /* track too big */
669         fdctrl_stop_transfer(0x40, 0x80, 0x00);
670         fdctrl.fifo[3] = kt;
671         fdctrl.fifo[4] = kh;
672         fdctrl.fifo[5] = ks;
673         return;
674     case 4:
675         /* No seek enabled */
676         fdctrl_stop_transfer(0x40, 0x00, 0x00);
677         fdctrl.fifo[3] = kt;
678         fdctrl.fifo[4] = kh;
679         fdctrl.fifo[5] = ks;
680         return;
681     case 1:
682         did_seek = 1;
683         break;
684     default:
685         break;
686     }
687     /* Set the FIFO state */
688     fdctrl.data_dir = direction;
689     fdctrl.data_pos = 0;
690     fdctrl.data_state = FD_STATE_DATA; /* FIFO ready for data */
691     if (fdctrl.fifo[0] & 0x80)
692         fdctrl.data_state |= FD_STATE_MULTI;
693     if (did_seek)
694         fdctrl.data_state |= FD_STATE_SEEK;
695     if (fdctrl.dma_en) {
696         int dma_mode;
697         /* DMA transfer are enabled. Check if DMA channel is well programmed */
698         dma_mode = DMA_get_channel_mode(fdctrl.dma_chann);
699         dma_mode = (dma_mode >> 2) & 3;
700         FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d)\n", dma_mode, direction,
701                        (128 << fdctrl.fifo[5]) *
702                        (cur_drv->last_sect - ks + 1));
703         if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
704               direction == FD_DIR_SCANH) && dma_mode == 0) ||
705             (direction == FD_DIR_WRITE && dma_mode == 2) ||
706             (direction == FD_DIR_READ && dma_mode == 1)) {
707             /* No access is allowed until DMA transfer has completed */
708             fdctrl.state |= FD_CTRL_BUSY;
709             /* Now, we just have to wait for the DMA controler to
710              * recall us...
711              */
712             DMA_hold_DREQ(fdctrl.dma_chann);
713             DMA_schedule(fdctrl.dma_chann);
714             return;
715         }
716     }
717     FLOPPY_DPRINTF("start non-DMA transfer\n");
718     /* IO based transfer: calculate len */
719     if (fdctrl.fifo[5] == 00) {
720         fdctrl.data_len = fdctrl.fifo[8];
721     } else {
722         fdctrl.data_len = 128 << fdctrl.fifo[5];
723         fdctrl.data_len *= (cur_drv->last_sect - ks + 1);
724         if (fdctrl.fifo[0] & 0x80)
725             fdctrl.data_len *= 2;
726     }
727     fdctrl_raise_irq(0x00);
728
729     return;
730 }
731
732 /* Prepare a transfer of deleted data */
733 static void fdctrl_start_transfer_del (int direction)
734 {
735     /* We don't handle deleted data,
736      * so we don't return *ANYTHING*
737      */
738     fdctrl_stop_transfer(0x60, 0x00, 0x00);
739 }
740
741 /* handlers for DMA transfers */
742 /* XXX: the partial transfer logic seems to be broken */
743 static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
744 {
745     fdrive_t *cur_drv, *drv0, *drv1;
746     int len;
747     uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
748     uint8_t tmpbuf[FD_SECTOR_LEN];
749
750     fdctrl_reset_irq();
751     if (!(fdctrl.state & FD_CTRL_BUSY)) {
752         FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
753         return 0;
754     }
755     drv0 = &fdctrl.drives[fdctrl.bootsel];
756     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
757     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
758     if (fdctrl.data_dir == FD_DIR_SCANE || fdctrl.data_dir == FD_DIR_SCANL ||
759         fdctrl.data_dir == FD_DIR_SCANH)
760         status2 = 0x04;
761     for (fdctrl.data_len = size; fdctrl.data_pos < fdctrl.data_len;
762          fdctrl.data_pos += len) {
763         len = size - fdctrl.data_pos;
764         if (len > FD_SECTOR_LEN)
765             len = FD_SECTOR_LEN;
766         FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x "
767                        "(%d-0x%08x)\n", len, size, fdctrl.data_pos,
768                        fdctrl.data_len, fdctrl.cur_drv, cur_drv->head,
769                        cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
770                        fd_sector(cur_drv) * 512);
771         if (fdctrl.data_dir != FD_DIR_WRITE) {
772             /* READ & SCAN commands */
773             if (cur_drv->bs == NULL) {
774                 fdctrl_stop_transfer(0x40, 0x00, 0x00);
775                 goto transfer_error;
776             }
777             if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), tmpbuf, 1) < 0) {
778                 FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
779                                fd_sector(cur_drv));
780                 /* Sure, image size is too small... */
781                 memset(tmpbuf, 0, FD_SECTOR_LEN);
782             }
783             if (fdctrl.data_dir == FD_DIR_READ) {
784                 cpu_physical_memory_write(addr + fdctrl.data_pos,
785                                           tmpbuf, len);
786                 if (len < FD_SECTOR_LEN) {
787                     memcpy(&fdctrl.fifo[0], tmpbuf + len, FD_SECTOR_LEN - len);
788                     memset(&fdctrl.fifo[FD_SECTOR_LEN - len], 0, len);
789                 }
790             } else {
791                 int ret;
792                 /* XXX: what to do if not enough data ? */
793                 cpu_physical_memory_read(addr + fdctrl.data_pos, 
794                                          fdctrl.fifo, len); 
795                 if (len < FD_SECTOR_LEN) {
796                     memset(&fdctrl.fifo[len], 0, FD_SECTOR_LEN - len);
797                 }
798                 ret = memcmp(tmpbuf, fdctrl.fifo, FD_SECTOR_LEN);
799                 if (ret == 0) {
800                     status2 = 0x08;
801                     goto end_transfer;
802                 }
803                 if ((ret < 0 && fdctrl.data_dir == FD_DIR_SCANL) ||
804                     (ret > 0 && fdctrl.data_dir == FD_DIR_SCANH)) {
805                     status2 = 0x00;
806                     goto end_transfer;
807                 }
808             }
809         } else {
810             /* WRITE commands */
811             cpu_physical_memory_read(addr + fdctrl.data_pos, tmpbuf, len);
812             if (len < FD_SECTOR_LEN) {
813                 memset(tmpbuf + len, 0, FD_SECTOR_LEN - len);
814             }
815             if (cur_drv->bs == NULL ||
816                 bdrv_write(cur_drv->bs, fd_sector(cur_drv), tmpbuf, 1) < 0) {
817                 FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
818                 fdctrl_stop_transfer(0x60, 0x00, 0x00);
819                 goto transfer_error;
820             }
821         }
822         if (len == FD_SECTOR_LEN) {
823             /* Seek to next sector */
824             if (cur_drv->sect == cur_drv->last_sect) {
825                 if (cur_drv->head == 0) {
826                     cur_drv->head = 1;
827                 } else {
828                     cur_drv->track++;
829                     cur_drv->head = 0;
830                 }
831                 cur_drv->sect = 1;
832                 FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
833                                cur_drv->head, cur_drv->track, cur_drv->sect,
834                                fd_sector(cur_drv));
835                 if (cur_drv->head == 0) {
836                     FLOPPY_DPRINTF("end transfer\n");
837                     goto end_transfer;
838                 }
839                 if (!FD_MULTI_TRACK(fdctrl.data_state)) {
840                     /* Single track read */
841                     FLOPPY_DPRINTF("single track transfert: end transfer\n");
842 //                    status1 |= 0x80;
843                     goto end_transfer;
844                 }
845             } else {
846                 cur_drv->sect++;
847                 FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
848                                cur_drv->head, cur_drv->track, cur_drv->sect,
849                                fd_sector(cur_drv));
850             }
851         }
852     }
853 end_transfer:
854     if (fdctrl.data_dir == FD_DIR_SCANE ||
855         fdctrl.data_dir == FD_DIR_SCANL ||
856         fdctrl.data_dir == FD_DIR_SCANH)
857         status2 = 0x08;
858     if (FD_DID_SEEK(fdctrl.data_state))
859         status0 |= 0x20;
860     fdctrl_stop_transfer(status0, status1, status2);
861 transfer_error:
862
863     return fdctrl.data_pos;
864 }
865
866 /* Data register : 0x05 */
867 static uint32_t fdctrl_read_data (void *opaque, uint32_t reg)
868 {
869     fdrive_t *cur_drv, *drv0, *drv1;
870     uint32_t retval = 0;
871     int pos, len;
872
873     fdctrl_reset_irq();
874     drv0 = &fdctrl.drives[fdctrl.bootsel];
875     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
876     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
877     fdctrl.state &= ~FD_CTRL_SLEEP;
878     if (FD_STATE(fdctrl.data_state) == FD_STATE_CMD) {
879         FLOPPY_ERROR("can't read data in CMD state\n");
880         return 0;
881     }
882     pos = fdctrl.data_pos;
883     if (FD_STATE(fdctrl.data_state) == FD_STATE_DATA) {
884         pos %= FD_SECTOR_LEN;
885         if (pos == 0) {
886             len = fdctrl.data_len - fdctrl.data_pos;
887             if (len > FD_SECTOR_LEN)
888                 len = FD_SECTOR_LEN;
889             bdrv_read(cur_drv->bs, fd_sector(cur_drv),
890                       fdctrl.fifo, len);
891         }
892     }
893     retval = fdctrl.fifo[pos];
894     if (++fdctrl.data_pos == fdctrl.data_len) {
895         fdctrl.data_pos = 0;
896         /* Switch from transfert mode to status mode
897          * then from status mode to command mode
898          */
899         if (FD_STATE(fdctrl.data_state) == FD_STATE_DATA)
900             fdctrl_stop_transfer(0x20, 0x00, 0x00);
901         else
902             fdctrl_reset_fifo();
903     }
904     FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
905
906     return retval;
907 }
908
909 static void fdctrl_write_data (void *opaque, uint32_t reg, uint32_t value)
910 {
911     fdrive_t *cur_drv, *drv0, *drv1;
912
913     fdctrl_reset_irq();
914     drv0 = &fdctrl.drives[fdctrl.bootsel];
915     drv1 = &fdctrl.drives[1 - fdctrl.bootsel];
916     cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
917     /* Reset mode */
918     if (fdctrl.state & FD_CTRL_RESET) {
919         FLOPPY_DPRINTF("Floppy controler in RESET state !\n");
920         return;
921     }
922     fdctrl.state &= ~FD_CTRL_SLEEP;
923     if ((fdctrl.data_state & FD_STATE_STATE) == FD_STATE_STATUS) {
924         FLOPPY_ERROR("can't write data in status mode\n");
925         return;
926     }
927     /* Is it write command time ? */
928     if (FD_STATE(fdctrl.data_state) == FD_STATE_DATA) {
929         /* FIFO data write */
930         fdctrl.fifo[fdctrl.data_pos++] = value;
931         if (fdctrl.data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) ||
932             fdctrl.data_pos == fdctrl.data_len) {
933             bdrv_write(cur_drv->bs, fd_sector(cur_drv),
934                        fdctrl.fifo, FD_SECTOR_LEN);
935         }
936         /* Switch from transfert mode to status mode
937          * then from status mode to command mode
938          */
939         if (FD_STATE(fdctrl.data_state) == FD_STATE_DATA)
940             fdctrl_stop_transfer(0x20, 0x00, 0x00);
941         return;
942     }
943     if (fdctrl.data_pos == 0) {
944         /* Command */
945         switch (value & 0x5F) {
946         case 0x46:
947             /* READ variants */
948             FLOPPY_DPRINTF("READ command\n");
949             /* 8 parameters cmd */
950             fdctrl.data_len = 9;
951             goto enqueue;
952         case 0x4C:
953             /* READ_DELETED variants */
954             FLOPPY_DPRINTF("READ_DELETED command\n");
955             /* 8 parameters cmd */
956             fdctrl.data_len = 9;
957             goto enqueue;
958         case 0x50:
959             /* SCAN_EQUAL variants */
960             FLOPPY_DPRINTF("SCAN_EQUAL command\n");
961             /* 8 parameters cmd */
962             fdctrl.data_len = 9;
963             goto enqueue;
964         case 0x56:
965             /* VERIFY variants */
966             FLOPPY_DPRINTF("VERIFY command\n");
967             /* 8 parameters cmd */
968             fdctrl.data_len = 9;
969             goto enqueue;
970         case 0x59:
971             /* SCAN_LOW_OR_EQUAL variants */
972             FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n");
973             /* 8 parameters cmd */
974             fdctrl.data_len = 9;
975             goto enqueue;
976         case 0x5D:
977             /* SCAN_HIGH_OR_EQUAL variants */
978             FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n");
979             /* 8 parameters cmd */
980             fdctrl.data_len = 9;
981             goto enqueue;
982         default:
983             break;
984         }
985         switch (value & 0x7F) {
986         case 0x45:
987             /* WRITE variants */
988             FLOPPY_DPRINTF("WRITE command\n");
989             /* 8 parameters cmd */
990             fdctrl.data_len = 9;
991             goto enqueue;
992         case 0x49:
993             /* WRITE_DELETED variants */
994             FLOPPY_DPRINTF("WRITE_DELETED command\n");
995             /* 8 parameters cmd */
996             fdctrl.data_len = 9;
997             goto enqueue;
998         default:
999             break;
1000         }
1001         switch (value) {
1002         case 0x03:
1003             /* SPECIFY */
1004             FLOPPY_DPRINTF("SPECIFY command\n");
1005             /* 1 parameter cmd */
1006             fdctrl.data_len = 3;
1007             goto enqueue;
1008         case 0x04:
1009             /* SENSE_DRIVE_STATUS */
1010             FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n");
1011             /* 1 parameter cmd */
1012             fdctrl.data_len = 2;
1013             goto enqueue;
1014         case 0x07:
1015             /* RECALIBRATE */
1016             FLOPPY_DPRINTF("RECALIBRATE command\n");
1017             /* 1 parameter cmd */
1018             fdctrl.data_len = 2;
1019             goto enqueue;
1020         case 0x08:
1021             /* SENSE_INTERRUPT_STATUS */
1022             FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n",
1023                            fdctrl.int_status);
1024             /* No parameters cmd: returns status if no interrupt */
1025             fdctrl.fifo[0] =
1026                 fdctrl.int_status | (cur_drv->head << 2) | fdctrl.cur_drv;
1027             fdctrl.fifo[1] = cur_drv->track;
1028             fdctrl_set_fifo(2, 0);
1029             return;
1030         case 0x0E:
1031             /* DUMPREG */
1032             FLOPPY_DPRINTF("DUMPREG command\n");
1033             /* Drives position */
1034             fdctrl.fifo[0] = drv0->track;
1035             fdctrl.fifo[1] = drv1->track;
1036             fdctrl.fifo[2] = 0;
1037             fdctrl.fifo[3] = 0;
1038             /* timers */
1039             fdctrl.fifo[4] = fdctrl.timer0;
1040             fdctrl.fifo[5] = (fdctrl.timer1 << 1) | fdctrl.dma_en;
1041             fdctrl.fifo[6] = cur_drv->last_sect;
1042             fdctrl.fifo[7] = (fdctrl.lock << 7) |
1043                     (cur_drv->perpendicular << 2);
1044             fdctrl.fifo[8] = fdctrl.config;
1045             fdctrl.fifo[9] = fdctrl.precomp_trk;
1046             fdctrl_set_fifo(10, 0);
1047             return;
1048         case 0x0F:
1049             /* SEEK */
1050             FLOPPY_DPRINTF("SEEK command\n");
1051             /* 2 parameters cmd */
1052             fdctrl.data_len = 3;
1053             goto enqueue;
1054         case 0x10:
1055             /* VERSION */
1056             FLOPPY_DPRINTF("VERSION command\n");
1057             /* No parameters cmd */
1058             /* Controler's version */
1059             fdctrl.fifo[0] = fdctrl.version;
1060             fdctrl_set_fifo(1, 1);
1061             return;
1062         case 0x12:
1063             /* PERPENDICULAR_MODE */
1064             FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n");
1065             /* 1 parameter cmd */
1066             fdctrl.data_len = 2;
1067             goto enqueue;
1068         case 0x13:
1069             /* CONFIGURE */
1070             FLOPPY_DPRINTF("CONFIGURE command\n");
1071             /* 3 parameters cmd */
1072             fdctrl.data_len = 4;
1073             goto enqueue;
1074         case 0x14:
1075             /* UNLOCK */
1076             FLOPPY_DPRINTF("UNLOCK command\n");
1077             /* No parameters cmd */
1078             fdctrl.lock = 0;
1079             fdctrl.fifo[0] = 0;
1080             fdctrl_set_fifo(1, 0);
1081             return;
1082         case 0x17:
1083             /* POWERDOWN_MODE */
1084             FLOPPY_DPRINTF("POWERDOWN_MODE command\n");
1085             /* 2 parameters cmd */
1086             fdctrl.data_len = 3;
1087             goto enqueue;
1088         case 0x18:
1089             /* PART_ID */
1090             FLOPPY_DPRINTF("PART_ID command\n");
1091             /* No parameters cmd */
1092             fdctrl.fifo[0] = 0x41; /* Stepping 1 */
1093             fdctrl_set_fifo(1, 0);
1094             return;
1095         case 0x2C:
1096             /* SAVE */
1097             FLOPPY_DPRINTF("SAVE command\n");
1098             /* No parameters cmd */
1099             fdctrl.fifo[0] = 0;
1100             fdctrl.fifo[1] = 0;
1101             /* Drives position */
1102             fdctrl.fifo[2] = drv0->track;
1103             fdctrl.fifo[3] = drv1->track;
1104             fdctrl.fifo[4] = 0;
1105             fdctrl.fifo[5] = 0;
1106             /* timers */
1107             fdctrl.fifo[6] = fdctrl.timer0;
1108             fdctrl.fifo[7] = fdctrl.timer1;
1109             fdctrl.fifo[8] = cur_drv->last_sect;
1110             fdctrl.fifo[9] = (fdctrl.lock << 7) |
1111                     (cur_drv->perpendicular << 2);
1112             fdctrl.fifo[10] = fdctrl.config;
1113             fdctrl.fifo[11] = fdctrl.precomp_trk;
1114             fdctrl.fifo[12] = fdctrl.pwrd;
1115             fdctrl.fifo[13] = 0;
1116             fdctrl.fifo[14] = 0;
1117             fdctrl_set_fifo(15, 1);
1118             return;
1119         case 0x33:
1120             /* OPTION */
1121             FLOPPY_DPRINTF("OPTION command\n");
1122             /* 1 parameter cmd */
1123             fdctrl.data_len = 2;
1124             goto enqueue;
1125         case 0x42:
1126             /* READ_TRACK */
1127             FLOPPY_DPRINTF("READ_TRACK command\n");
1128             /* 8 parameters cmd */
1129             fdctrl.data_len = 9;
1130             goto enqueue;
1131         case 0x4A:
1132             /* READ_ID */
1133             FLOPPY_DPRINTF("READ_ID command\n");
1134             /* 1 parameter cmd */
1135             fdctrl.data_len = 2;
1136             goto enqueue;
1137         case 0x4C:
1138             /* RESTORE */
1139             FLOPPY_DPRINTF("RESTORE command\n");
1140             /* 17 parameters cmd */
1141             fdctrl.data_len = 18;
1142             goto enqueue;
1143         case 0x4D:
1144             /* FORMAT_TRACK */
1145             FLOPPY_DPRINTF("FORMAT_TRACK command\n");
1146             /* 5 parameters cmd */
1147             fdctrl.data_len = 9;
1148             goto enqueue;
1149         case 0x8E:
1150             /* DRIVE_SPECIFICATION_COMMAND */
1151             FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n");
1152             /* 5 parameters cmd */
1153             fdctrl.data_len = 6;
1154             goto enqueue;
1155         case 0x8F:
1156             /* RELATIVE_SEEK_OUT */
1157             FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n");
1158             /* 2 parameters cmd */
1159             fdctrl.data_len = 3;
1160             goto enqueue;
1161         case 0x94:
1162             /* LOCK */
1163             FLOPPY_DPRINTF("LOCK command\n");
1164             /* No parameters cmd */
1165             fdctrl.lock = 1;
1166             fdctrl.fifo[0] = 0x10;
1167             fdctrl_set_fifo(1, 1);
1168             return;
1169         case 0xCD:
1170             /* FORMAT_AND_WRITE */
1171             FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n");
1172             /* 10 parameters cmd */
1173             fdctrl.data_len = 11;
1174             goto enqueue;
1175         case 0xCF:
1176             /* RELATIVE_SEEK_IN */
1177             FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n");
1178             /* 2 parameters cmd */
1179             fdctrl.data_len = 3;
1180             goto enqueue;
1181         default:
1182             /* Unknown command */
1183             FLOPPY_ERROR("unknown command: 0x%02x\n", value);
1184             fdctrl_unimplemented();
1185             return;
1186         }
1187     }
1188 enqueue:
1189     fdctrl.fifo[fdctrl.data_pos] = value;
1190     if (++fdctrl.data_pos == fdctrl.data_len) {
1191         /* We now have all parameters
1192          * and will be able to treat the command
1193          */
1194         switch (fdctrl.fifo[0] & 0x1F) {
1195         case 0x06:
1196         {
1197             /* READ variants */
1198             FLOPPY_DPRINTF("treat READ command\n");
1199             fdctrl_start_transfer(FD_DIR_READ);
1200             return;
1201         }
1202         case 0x0C:
1203             /* READ_DELETED variants */
1204 //            FLOPPY_DPRINTF("treat READ_DELETED command\n");
1205             FLOPPY_ERROR("treat READ_DELETED command\n");
1206             fdctrl_start_transfer_del(1);
1207             return;
1208         case 0x16:
1209             /* VERIFY variants */
1210 //            FLOPPY_DPRINTF("treat VERIFY command\n");
1211             FLOPPY_ERROR("treat VERIFY command\n");
1212             fdctrl_stop_transfer(0x20, 0x00, 0x00);
1213             return;
1214         case 0x10:
1215             /* SCAN_EQUAL variants */
1216 //            FLOPPY_DPRINTF("treat SCAN_EQUAL command\n");
1217             FLOPPY_ERROR("treat SCAN_EQUAL command\n");
1218             fdctrl_start_transfer(FD_DIR_SCANE);
1219             return;
1220         case 0x19:
1221             /* SCAN_LOW_OR_EQUAL variants */
1222 //            FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n");
1223             FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n");
1224             fdctrl_start_transfer(FD_DIR_SCANL);
1225             return;
1226         case 0x1D:
1227             /* SCAN_HIGH_OR_EQUAL variants */
1228 //            FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n");
1229             FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n");
1230             fdctrl_start_transfer(FD_DIR_SCANH);
1231             return;
1232         default:
1233             break;
1234         }
1235         switch (fdctrl.fifo[0] & 0x3F) {
1236         case 0x05:
1237             /* WRITE variants */
1238             FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl.fifo[0]);
1239             fdctrl_start_transfer(FD_DIR_WRITE);
1240             return;
1241         case 0x09:
1242             /* WRITE_DELETED variants */
1243 //            FLOPPY_DPRINTF("treat WRITE_DELETED command\n");
1244             FLOPPY_ERROR("treat WRITE_DELETED command\n");
1245             fdctrl_start_transfer_del(FD_DIR_WRITE);
1246             return;
1247         default:
1248             break;
1249         }
1250         switch (fdctrl.fifo[0]) {
1251         case 0x03:
1252             /* SPECIFY */
1253             FLOPPY_DPRINTF("treat SPECIFY command\n");
1254             fdctrl.timer0 = (fdctrl.fifo[1] >> 4) & 0xF;
1255             fdctrl.timer1 = fdctrl.fifo[1] >> 1;
1256             /* No result back */
1257             fdctrl_reset_fifo();
1258             break;
1259         case 0x04:
1260             /* SENSE_DRIVE_STATUS */
1261             FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n");
1262             fdctrl.cur_drv = fdctrl.fifo[1] & 1;
1263             cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
1264             cur_drv->head = (fdctrl.fifo[1] >> 2) & 1;
1265             /* 1 Byte status back */
1266             fdctrl.fifo[0] = (cur_drv->ro << 6) |
1267                 (cur_drv->track == 0 ? 0x10 : 0x00) |
1268                 fdctrl.cur_drv;
1269             fdctrl_set_fifo(1, 0);
1270             break;
1271         case 0x07:
1272             /* RECALIBRATE */
1273             FLOPPY_DPRINTF("treat RECALIBRATE command\n");
1274             fdctrl.cur_drv = fdctrl.fifo[1] & 1;
1275             cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
1276             fd_recalibrate(cur_drv);
1277             fdctrl_reset_fifo();
1278             /* Raise Interrupt */
1279             fdctrl_raise_irq(0x20);
1280             break;
1281         case 0x0F:
1282             /* SEEK */
1283             FLOPPY_DPRINTF("treat SEEK command\n");
1284             fdctrl.cur_drv = fdctrl.fifo[1] & 1;
1285             cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
1286             if (fdctrl.fifo[2] <= cur_drv->track)
1287                 cur_drv->dir = 1;
1288             else
1289                 cur_drv->dir = 0;
1290             cur_drv->head = (fdctrl.fifo[1] >> 2) & 1;
1291             if (fdctrl.fifo[2] > cur_drv->max_track) {
1292                 fdctrl_raise_irq(0x60);
1293             } else {
1294                 cur_drv->track = fdctrl.fifo[2];
1295                 fdctrl_reset_fifo();
1296                 /* Raise Interrupt */
1297                 fdctrl_raise_irq(0x20);
1298             }
1299             break;
1300         case 0x12:
1301             /* PERPENDICULAR_MODE */
1302             FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n");
1303             if (fdctrl.fifo[1] & 0x80)
1304                 cur_drv->perpendicular = fdctrl.fifo[1] & 0x7;
1305             /* No result back */
1306             fdctrl_reset_fifo();
1307             break;
1308         case 0x13:
1309             /* CONFIGURE */
1310             FLOPPY_DPRINTF("treat CONFIGURE command\n");
1311             fdctrl.config = fdctrl.fifo[2];
1312             fdctrl.precomp_trk =  fdctrl.fifo[3];
1313             /* No result back */
1314             fdctrl_reset_fifo();
1315             break;
1316         case 0x17:
1317             /* POWERDOWN_MODE */
1318             FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n");
1319             fdctrl.pwrd = fdctrl.fifo[1];
1320             fdctrl.fifo[0] = fdctrl.fifo[1];
1321             fdctrl_set_fifo(1, 1);
1322             break;
1323         case 0x33:
1324             /* OPTION */
1325             FLOPPY_DPRINTF("treat OPTION command\n");
1326             /* No result back */
1327             fdctrl_reset_fifo();
1328             break;
1329         case 0x42:
1330             /* READ_TRACK */
1331 //            FLOPPY_DPRINTF("treat READ_TRACK command\n");
1332             FLOPPY_ERROR("treat READ_TRACK command\n");
1333             fdctrl_unimplemented();
1334             break;
1335         case 0x4A:
1336                 /* READ_ID */
1337 //            FLOPPY_DPRINTF("treat READ_ID command\n");
1338             FLOPPY_ERROR("treat READ_ID command\n");
1339             fdctrl_stop_transfer(0x00, 0x00, 0x00);
1340             break;
1341         case 0x4C:
1342             /* RESTORE */
1343             FLOPPY_DPRINTF("treat RESTORE command\n");
1344             /* Drives position */
1345             drv0->track = fdctrl.fifo[3];
1346             drv1->track = fdctrl.fifo[4];
1347             /* timers */
1348             fdctrl.timer0 = fdctrl.fifo[7];
1349             fdctrl.timer1 = fdctrl.fifo[8];
1350             cur_drv->last_sect = fdctrl.fifo[9];
1351             fdctrl.lock = fdctrl.fifo[10] >> 7;
1352             cur_drv->perpendicular = (fdctrl.fifo[10] >> 2) & 0xF;
1353             fdctrl.config = fdctrl.fifo[11];
1354             fdctrl.precomp_trk = fdctrl.fifo[12];
1355             fdctrl.pwrd = fdctrl.fifo[13];
1356             fdctrl_reset_fifo();
1357             break;
1358         case 0x4D:
1359             /* FORMAT_TRACK */
1360 //                FLOPPY_DPRINTF("treat FORMAT_TRACK command\n");
1361             FLOPPY_ERROR("treat FORMAT_TRACK command\n");
1362             fdctrl_unimplemented();
1363             break;
1364         case 0x8E:
1365             /* DRIVE_SPECIFICATION_COMMAND */
1366             FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n");
1367             if (fdctrl.fifo[fdctrl.data_pos - 1] & 0x80) {
1368                 /* Command parameters done */
1369                 if (fdctrl.fifo[fdctrl.data_pos - 1] & 0x40) {
1370                     fdctrl.fifo[0] = fdctrl.fifo[1];
1371                     fdctrl.fifo[2] = 0;
1372                     fdctrl.fifo[3] = 0;
1373                     fdctrl_set_fifo(4, 1);
1374                 } else {
1375                     fdctrl_reset_fifo();
1376                 }
1377             } else if (fdctrl.data_len > 7) {
1378                 /* ERROR */
1379                 fdctrl.fifo[0] = 0x80 |
1380                     (cur_drv->head << 2) | fdctrl.cur_drv;
1381                 fdctrl_set_fifo(1, 1);
1382             }
1383             break;
1384         case 0x8F:
1385             /* RELATIVE_SEEK_OUT */
1386             FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n");
1387             fdctrl.cur_drv = fdctrl.fifo[1] & 1;
1388             cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
1389             cur_drv->head = (fdctrl.fifo[1] >> 2) & 1;
1390             if (fdctrl.fifo[2] + cur_drv->track > cur_drv->max_track) {
1391                 /* ERROR */
1392                 fdctrl_raise_irq(0x70);
1393             } else {
1394                 cur_drv->track += fdctrl.fifo[2];
1395                 cur_drv->dir = 0;
1396                 fdctrl_reset_fifo();
1397                 fdctrl_raise_irq(0x20);
1398             }
1399             break;
1400         case 0xCD:
1401             /* FORMAT_AND_WRITE */
1402 //                FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n");
1403             FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n");
1404             fdctrl_unimplemented();
1405             break;
1406         case 0xCF:
1407                 /* RELATIVE_SEEK_IN */
1408             FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n");
1409             fdctrl.cur_drv = fdctrl.fifo[1] & 1;
1410             cur_drv = fdctrl.cur_drv == 0 ? drv0 : drv1;
1411             cur_drv->head = (fdctrl.fifo[1] >> 2) & 1;
1412             if (fdctrl.fifo[2] > cur_drv->track) {
1413                 /* ERROR */
1414                 fdctrl_raise_irq(0x60);
1415             } else {
1416                 fdctrl_reset_fifo();
1417                 cur_drv->track -= fdctrl.fifo[2];
1418                 cur_drv->dir = 1;
1419                 /* Raise Interrupt */
1420                 fdctrl_raise_irq(0x20);
1421             }
1422             break;
1423         }
1424     }
1425 }