]>
Commit | Line | Data |
---|---|---|
8977f3c1 | 1 | /* |
890fa6be | 2 | * QEMU Floppy disk emulator (Intel 82078) |
5fafdf24 | 3 | * |
3ccacc4a | 4 | * Copyright (c) 2003, 2007 Jocelyn Mayer |
5fafdf24 | 5 | * |
8977f3c1 FB |
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 | */ | |
e80cfcfc FB |
24 | /* |
25 | * The controller is used in Sun4m systems in a slightly different | |
26 | * way. There are changes in DOR register and DMA is not available. | |
27 | */ | |
87ecb68b PB |
28 | #include "hw.h" |
29 | #include "fdc.h" | |
30 | #include "block.h" | |
31 | #include "qemu-timer.h" | |
32 | #include "isa.h" | |
8977f3c1 FB |
33 | |
34 | /********************************************************/ | |
35 | /* debug Floppy devices */ | |
36 | //#define DEBUG_FLOPPY | |
37 | ||
38 | #ifdef DEBUG_FLOPPY | |
39 | #define FLOPPY_DPRINTF(fmt, args...) \ | |
40 | do { printf("FLOPPY: " fmt , ##args); } while (0) | |
41 | #else | |
42 | #define FLOPPY_DPRINTF(fmt, args...) | |
43 | #endif | |
44 | ||
45 | #define FLOPPY_ERROR(fmt, args...) \ | |
46 | do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0) | |
47 | ||
48 | /********************************************************/ | |
49 | /* Floppy drive emulation */ | |
50 | ||
51 | /* Will always be a fixed parameter for us */ | |
52 | #define FD_SECTOR_LEN 512 | |
53 | #define FD_SECTOR_SC 2 /* Sector size code */ | |
54 | ||
55 | /* Floppy disk drive emulation */ | |
56 | typedef enum fdisk_type_t { | |
57 | FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */ | |
58 | FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */ | |
59 | FDRIVE_DISK_720 = 0x03, /* 720 kB disk */ | |
baca51fa FB |
60 | FDRIVE_DISK_USER = 0x04, /* User defined geometry */ |
61 | FDRIVE_DISK_NONE = 0x05, /* No disk */ | |
8977f3c1 FB |
62 | } fdisk_type_t; |
63 | ||
64 | typedef enum fdrive_type_t { | |
65 | FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ | |
66 | FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ | |
67 | FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ | |
68 | FDRIVE_DRV_NONE = 0x03, /* No drive connected */ | |
69 | } fdrive_type_t; | |
70 | ||
baca51fa FB |
71 | typedef enum fdrive_flags_t { |
72 | FDRIVE_MOTOR_ON = 0x01, /* motor on/off */ | |
baca51fa FB |
73 | } fdrive_flags_t; |
74 | ||
75 | typedef enum fdisk_flags_t { | |
76 | FDISK_DBL_SIDES = 0x01, | |
77 | } fdisk_flags_t; | |
78 | ||
8977f3c1 FB |
79 | typedef struct fdrive_t { |
80 | BlockDriverState *bs; | |
81 | /* Drive status */ | |
82 | fdrive_type_t drive; | |
baca51fa | 83 | fdrive_flags_t drflags; |
8977f3c1 | 84 | uint8_t perpendicular; /* 2.88 MB access mode */ |
8977f3c1 FB |
85 | /* Position */ |
86 | uint8_t head; | |
87 | uint8_t track; | |
88 | uint8_t sect; | |
89 | /* Last operation status */ | |
90 | uint8_t dir; /* Direction */ | |
91 | uint8_t rw; /* Read/write */ | |
92 | /* Media */ | |
baca51fa | 93 | fdisk_flags_t flags; |
8977f3c1 FB |
94 | uint8_t last_sect; /* Nb sector per track */ |
95 | uint8_t max_track; /* Nb of tracks */ | |
baca51fa | 96 | uint16_t bps; /* Bytes per sector */ |
8977f3c1 FB |
97 | uint8_t ro; /* Is read-only */ |
98 | } fdrive_t; | |
99 | ||
caed8802 | 100 | static void fd_init (fdrive_t *drv, BlockDriverState *bs) |
8977f3c1 FB |
101 | { |
102 | /* Drive */ | |
caed8802 | 103 | drv->bs = bs; |
b939777c | 104 | drv->drive = FDRIVE_DRV_NONE; |
baca51fa | 105 | drv->drflags = 0; |
8977f3c1 | 106 | drv->perpendicular = 0; |
8977f3c1 | 107 | /* Disk */ |
baca51fa | 108 | drv->last_sect = 0; |
8977f3c1 FB |
109 | drv->max_track = 0; |
110 | } | |
111 | ||
112 | static int _fd_sector (uint8_t head, uint8_t track, | |
4f431960 | 113 | uint8_t sect, uint8_t last_sect) |
8977f3c1 FB |
114 | { |
115 | return (((track * 2) + head) * last_sect) + sect - 1; | |
116 | } | |
117 | ||
118 | /* Returns current position, in sectors, for given drive */ | |
119 | static int fd_sector (fdrive_t *drv) | |
120 | { | |
121 | return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect); | |
122 | } | |
123 | ||
124 | static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, | |
125 | int enable_seek) | |
126 | { | |
127 | uint32_t sector; | |
baca51fa FB |
128 | int ret; |
129 | ||
130 | if (track > drv->max_track || | |
4f431960 | 131 | (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { |
ed5fd2cc FB |
132 | FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", |
133 | head, track, sect, 1, | |
134 | (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, | |
135 | drv->max_track, drv->last_sect); | |
8977f3c1 FB |
136 | return 2; |
137 | } | |
138 | if (sect > drv->last_sect) { | |
ed5fd2cc FB |
139 | FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", |
140 | head, track, sect, 1, | |
141 | (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, | |
142 | drv->max_track, drv->last_sect); | |
8977f3c1 FB |
143 | return 3; |
144 | } | |
145 | sector = _fd_sector(head, track, sect, drv->last_sect); | |
baca51fa | 146 | ret = 0; |
8977f3c1 FB |
147 | if (sector != fd_sector(drv)) { |
148 | #if 0 | |
149 | if (!enable_seek) { | |
150 | FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n", | |
151 | head, track, sect, 1, drv->max_track, drv->last_sect); | |
152 | return 4; | |
153 | } | |
154 | #endif | |
155 | drv->head = head; | |
4f431960 JM |
156 | if (drv->track != track) |
157 | ret = 1; | |
8977f3c1 FB |
158 | drv->track = track; |
159 | drv->sect = sect; | |
8977f3c1 FB |
160 | } |
161 | ||
baca51fa | 162 | return ret; |
8977f3c1 FB |
163 | } |
164 | ||
165 | /* Set drive back to track 0 */ | |
166 | static void fd_recalibrate (fdrive_t *drv) | |
167 | { | |
168 | FLOPPY_DPRINTF("recalibrate\n"); | |
169 | drv->head = 0; | |
170 | drv->track = 0; | |
171 | drv->sect = 1; | |
172 | drv->dir = 1; | |
173 | drv->rw = 0; | |
174 | } | |
175 | ||
a541f297 FB |
176 | /* Recognize floppy formats */ |
177 | typedef struct fd_format_t { | |
178 | fdrive_type_t drive; | |
179 | fdisk_type_t disk; | |
180 | uint8_t last_sect; | |
181 | uint8_t max_track; | |
182 | uint8_t max_head; | |
60fe76f3 | 183 | const char *str; |
a541f297 FB |
184 | } fd_format_t; |
185 | ||
51a65271 | 186 | static const fd_format_t fd_formats[] = { |
a541f297 FB |
187 | /* First entry is default format */ |
188 | /* 1.44 MB 3"1/2 floppy disks */ | |
189 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", }, | |
190 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", }, | |
191 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", }, | |
192 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", }, | |
193 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", }, | |
194 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", }, | |
195 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", }, | |
196 | { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", }, | |
197 | /* 2.88 MB 3"1/2 floppy disks */ | |
198 | { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", }, | |
199 | { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", }, | |
200 | { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", }, | |
201 | { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", }, | |
202 | { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", }, | |
203 | /* 720 kB 3"1/2 floppy disks */ | |
204 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", }, | |
205 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", }, | |
206 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", }, | |
207 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", }, | |
208 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", }, | |
209 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", }, | |
210 | /* 1.2 MB 5"1/4 floppy disks */ | |
211 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", }, | |
212 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", }, | |
213 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", }, | |
214 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", }, | |
215 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", }, | |
216 | /* 720 kB 5"1/4 floppy disks */ | |
217 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", }, | |
218 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", }, | |
219 | /* 360 kB 5"1/4 floppy disks */ | |
220 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", }, | |
221 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", }, | |
222 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", }, | |
223 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", }, | |
5fafdf24 | 224 | /* 320 kB 5"1/4 floppy disks */ |
a541f297 FB |
225 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", }, |
226 | { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", }, | |
227 | /* 360 kB must match 5"1/4 better than 3"1/2... */ | |
228 | { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", }, | |
229 | /* end */ | |
230 | { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, }, | |
231 | }; | |
232 | ||
8977f3c1 | 233 | /* Revalidate a disk drive after a disk change */ |
caed8802 | 234 | static void fd_revalidate (fdrive_t *drv) |
8977f3c1 | 235 | { |
51a65271 | 236 | const fd_format_t *parse; |
96b8f136 | 237 | uint64_t nb_sectors, size; |
a541f297 | 238 | int i, first_match, match; |
baca51fa | 239 | int nb_heads, max_track, last_sect, ro; |
8977f3c1 FB |
240 | |
241 | FLOPPY_DPRINTF("revalidate\n"); | |
a541f297 | 242 | if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { |
4f431960 JM |
243 | ro = bdrv_is_read_only(drv->bs); |
244 | bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); | |
245 | if (nb_heads != 0 && max_track != 0 && last_sect != 0) { | |
246 | FLOPPY_DPRINTF("User defined disk (%d %d %d)", | |
ed5fd2cc | 247 | nb_heads - 1, max_track, last_sect); |
4f431960 JM |
248 | } else { |
249 | bdrv_get_geometry(drv->bs, &nb_sectors); | |
250 | match = -1; | |
251 | first_match = -1; | |
252 | for (i = 0;; i++) { | |
253 | parse = &fd_formats[i]; | |
254 | if (parse->drive == FDRIVE_DRV_NONE) | |
255 | break; | |
256 | if (drv->drive == parse->drive || | |
257 | drv->drive == FDRIVE_DRV_NONE) { | |
258 | size = (parse->max_head + 1) * parse->max_track * | |
259 | parse->last_sect; | |
260 | if (nb_sectors == size) { | |
261 | match = i; | |
262 | break; | |
263 | } | |
264 | if (first_match == -1) | |
265 | first_match = i; | |
266 | } | |
267 | } | |
268 | if (match == -1) { | |
269 | if (first_match == -1) | |
270 | match = 1; | |
271 | else | |
272 | match = first_match; | |
273 | parse = &fd_formats[match]; | |
274 | } | |
275 | nb_heads = parse->max_head + 1; | |
276 | max_track = parse->max_track; | |
277 | last_sect = parse->last_sect; | |
278 | drv->drive = parse->drive; | |
279 | FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str, | |
ed5fd2cc | 280 | nb_heads, max_track, last_sect, ro ? "ro" : "rw"); |
4f431960 JM |
281 | } |
282 | if (nb_heads == 1) { | |
283 | drv->flags &= ~FDISK_DBL_SIDES; | |
284 | } else { | |
285 | drv->flags |= FDISK_DBL_SIDES; | |
286 | } | |
287 | drv->max_track = max_track; | |
288 | drv->last_sect = last_sect; | |
289 | drv->ro = ro; | |
8977f3c1 | 290 | } else { |
4f431960 | 291 | FLOPPY_DPRINTF("No disk in drive\n"); |
baca51fa | 292 | drv->last_sect = 0; |
4f431960 JM |
293 | drv->max_track = 0; |
294 | drv->flags &= ~FDISK_DBL_SIDES; | |
8977f3c1 | 295 | } |
caed8802 FB |
296 | } |
297 | ||
8977f3c1 FB |
298 | /* Motor control */ |
299 | static void fd_start (fdrive_t *drv) | |
300 | { | |
baca51fa | 301 | drv->drflags |= FDRIVE_MOTOR_ON; |
8977f3c1 FB |
302 | } |
303 | ||
304 | static void fd_stop (fdrive_t *drv) | |
305 | { | |
baca51fa | 306 | drv->drflags &= ~FDRIVE_MOTOR_ON; |
8977f3c1 FB |
307 | } |
308 | ||
309 | /* Re-initialise a drives (motor off, repositioned) */ | |
310 | static void fd_reset (fdrive_t *drv) | |
311 | { | |
312 | fd_stop(drv); | |
313 | fd_recalibrate(drv); | |
314 | } | |
315 | ||
316 | /********************************************************/ | |
4b19ec0c | 317 | /* Intel 82078 floppy disk controller emulation */ |
8977f3c1 | 318 | |
baca51fa FB |
319 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); |
320 | static void fdctrl_reset_fifo (fdctrl_t *fdctrl); | |
85571bc7 FB |
321 | static int fdctrl_transfer_handler (void *opaque, int nchan, |
322 | int dma_pos, int dma_len); | |
baca51fa | 323 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); |
ed5fd2cc | 324 | static void fdctrl_result_timer(void *opaque); |
baca51fa FB |
325 | |
326 | static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl); | |
327 | static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl); | |
328 | static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value); | |
329 | static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl); | |
330 | static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value); | |
331 | static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl); | |
332 | static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value); | |
333 | static uint32_t fdctrl_read_data (fdctrl_t *fdctrl); | |
334 | static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value); | |
335 | static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl); | |
8977f3c1 FB |
336 | |
337 | enum { | |
ed5fd2cc | 338 | FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */ |
8977f3c1 | 339 | FD_CTRL_RESET = 0x02, |
ed5fd2cc FB |
340 | FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */ |
341 | FD_CTRL_BUSY = 0x08, /* dma transfer in progress */ | |
8977f3c1 FB |
342 | FD_CTRL_INTR = 0x10, |
343 | }; | |
344 | ||
345 | enum { | |
346 | FD_DIR_WRITE = 0, | |
347 | FD_DIR_READ = 1, | |
348 | FD_DIR_SCANE = 2, | |
349 | FD_DIR_SCANL = 3, | |
350 | FD_DIR_SCANH = 4, | |
351 | }; | |
352 | ||
353 | enum { | |
354 | FD_STATE_CMD = 0x00, | |
355 | FD_STATE_STATUS = 0x01, | |
356 | FD_STATE_DATA = 0x02, | |
357 | FD_STATE_STATE = 0x03, | |
358 | FD_STATE_MULTI = 0x10, | |
359 | FD_STATE_SEEK = 0x20, | |
baca51fa | 360 | FD_STATE_FORMAT = 0x40, |
8977f3c1 FB |
361 | }; |
362 | ||
363 | #define FD_STATE(state) ((state) & FD_STATE_STATE) | |
baca51fa FB |
364 | #define FD_SET_STATE(state, new_state) \ |
365 | do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0) | |
8977f3c1 FB |
366 | #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) |
367 | #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK) | |
baca51fa | 368 | #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) |
8977f3c1 | 369 | |
baca51fa FB |
370 | struct fdctrl_t { |
371 | fdctrl_t *fdctrl; | |
4b19ec0c | 372 | /* Controller's identification */ |
8977f3c1 FB |
373 | uint8_t version; |
374 | /* HW */ | |
d537cf6c | 375 | qemu_irq irq; |
8977f3c1 | 376 | int dma_chann; |
5dcb6b91 | 377 | target_phys_addr_t io_base; |
4b19ec0c | 378 | /* Controller state */ |
ed5fd2cc | 379 | QEMUTimer *result_timer; |
8977f3c1 FB |
380 | uint8_t state; |
381 | uint8_t dma_en; | |
382 | uint8_t cur_drv; | |
383 | uint8_t bootsel; | |
384 | /* Command FIFO */ | |
33f00271 | 385 | uint8_t *fifo; |
8977f3c1 FB |
386 | uint32_t data_pos; |
387 | uint32_t data_len; | |
388 | uint8_t data_state; | |
389 | uint8_t data_dir; | |
390 | uint8_t int_status; | |
890fa6be | 391 | uint8_t eot; /* last wanted sector */ |
8977f3c1 FB |
392 | /* States kept only to be returned back */ |
393 | /* Timers state */ | |
394 | uint8_t timer0; | |
395 | uint8_t timer1; | |
396 | /* precompensation */ | |
397 | uint8_t precomp_trk; | |
398 | uint8_t config; | |
399 | uint8_t lock; | |
400 | /* Power down config (also with status regB access mode */ | |
401 | uint8_t pwrd; | |
741402f9 | 402 | /* Sun4m quirks? */ |
a06e5a3c | 403 | int sun4m; |
8977f3c1 FB |
404 | /* Floppy drives */ |
405 | fdrive_t drives[2]; | |
baca51fa FB |
406 | }; |
407 | ||
408 | static uint32_t fdctrl_read (void *opaque, uint32_t reg) | |
409 | { | |
410 | fdctrl_t *fdctrl = opaque; | |
411 | uint32_t retval; | |
412 | ||
a541f297 | 413 | switch (reg & 0x07) { |
6f7e9aec | 414 | case 0x00: |
a06e5a3c | 415 | if (fdctrl->sun4m) { |
741402f9 BS |
416 | // Identify to Linux as S82078B |
417 | retval = fdctrl_read_statusB(fdctrl); | |
418 | } else { | |
419 | retval = (uint32_t)(-1); | |
420 | } | |
4f431960 | 421 | break; |
a541f297 | 422 | case 0x01: |
4f431960 JM |
423 | retval = fdctrl_read_statusB(fdctrl); |
424 | break; | |
a541f297 | 425 | case 0x02: |
4f431960 JM |
426 | retval = fdctrl_read_dor(fdctrl); |
427 | break; | |
a541f297 | 428 | case 0x03: |
baca51fa | 429 | retval = fdctrl_read_tape(fdctrl); |
4f431960 | 430 | break; |
a541f297 | 431 | case 0x04: |
baca51fa | 432 | retval = fdctrl_read_main_status(fdctrl); |
4f431960 | 433 | break; |
a541f297 | 434 | case 0x05: |
baca51fa | 435 | retval = fdctrl_read_data(fdctrl); |
4f431960 | 436 | break; |
a541f297 | 437 | case 0x07: |
baca51fa | 438 | retval = fdctrl_read_dir(fdctrl); |
4f431960 | 439 | break; |
a541f297 | 440 | default: |
4f431960 JM |
441 | retval = (uint32_t)(-1); |
442 | break; | |
a541f297 | 443 | } |
ed5fd2cc | 444 | FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); |
baca51fa FB |
445 | |
446 | return retval; | |
447 | } | |
448 | ||
449 | static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) | |
450 | { | |
451 | fdctrl_t *fdctrl = opaque; | |
452 | ||
ed5fd2cc FB |
453 | FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); |
454 | ||
a541f297 FB |
455 | switch (reg & 0x07) { |
456 | case 0x02: | |
4f431960 JM |
457 | fdctrl_write_dor(fdctrl, value); |
458 | break; | |
a541f297 | 459 | case 0x03: |
baca51fa | 460 | fdctrl_write_tape(fdctrl, value); |
4f431960 | 461 | break; |
a541f297 | 462 | case 0x04: |
baca51fa | 463 | fdctrl_write_rate(fdctrl, value); |
4f431960 | 464 | break; |
a541f297 | 465 | case 0x05: |
baca51fa | 466 | fdctrl_write_data(fdctrl, value); |
4f431960 | 467 | break; |
a541f297 | 468 | default: |
4f431960 | 469 | break; |
a541f297 | 470 | } |
baca51fa FB |
471 | } |
472 | ||
62a46c61 FB |
473 | static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg) |
474 | { | |
5dcb6b91 | 475 | return fdctrl_read(opaque, (uint32_t)reg); |
62a46c61 FB |
476 | } |
477 | ||
5fafdf24 | 478 | static void fdctrl_write_mem (void *opaque, |
62a46c61 FB |
479 | target_phys_addr_t reg, uint32_t value) |
480 | { | |
5dcb6b91 | 481 | fdctrl_write(opaque, (uint32_t)reg, value); |
62a46c61 FB |
482 | } |
483 | ||
e80cfcfc | 484 | static CPUReadMemoryFunc *fdctrl_mem_read[3] = { |
62a46c61 FB |
485 | fdctrl_read_mem, |
486 | fdctrl_read_mem, | |
487 | fdctrl_read_mem, | |
e80cfcfc FB |
488 | }; |
489 | ||
490 | static CPUWriteMemoryFunc *fdctrl_mem_write[3] = { | |
62a46c61 FB |
491 | fdctrl_write_mem, |
492 | fdctrl_write_mem, | |
493 | fdctrl_write_mem, | |
e80cfcfc FB |
494 | }; |
495 | ||
7c560456 BS |
496 | static CPUReadMemoryFunc *fdctrl_mem_read_strict[3] = { |
497 | fdctrl_read_mem, | |
498 | NULL, | |
499 | NULL, | |
500 | }; | |
501 | ||
502 | static CPUWriteMemoryFunc *fdctrl_mem_write_strict[3] = { | |
503 | fdctrl_write_mem, | |
504 | NULL, | |
505 | NULL, | |
506 | }; | |
507 | ||
3ccacc4a BS |
508 | static void fd_save (QEMUFile *f, fdrive_t *fd) |
509 | { | |
510 | uint8_t tmp; | |
511 | ||
512 | tmp = fd->drflags; | |
513 | qemu_put_8s(f, &tmp); | |
514 | qemu_put_8s(f, &fd->head); | |
515 | qemu_put_8s(f, &fd->track); | |
516 | qemu_put_8s(f, &fd->sect); | |
517 | qemu_put_8s(f, &fd->dir); | |
518 | qemu_put_8s(f, &fd->rw); | |
519 | } | |
520 | ||
521 | static void fdc_save (QEMUFile *f, void *opaque) | |
522 | { | |
523 | fdctrl_t *s = opaque; | |
524 | ||
525 | qemu_put_8s(f, &s->state); | |
526 | qemu_put_8s(f, &s->dma_en); | |
527 | qemu_put_8s(f, &s->cur_drv); | |
528 | qemu_put_8s(f, &s->bootsel); | |
529 | qemu_put_buffer(f, s->fifo, FD_SECTOR_LEN); | |
530 | qemu_put_be32s(f, &s->data_pos); | |
531 | qemu_put_be32s(f, &s->data_len); | |
532 | qemu_put_8s(f, &s->data_state); | |
533 | qemu_put_8s(f, &s->data_dir); | |
534 | qemu_put_8s(f, &s->int_status); | |
535 | qemu_put_8s(f, &s->eot); | |
536 | qemu_put_8s(f, &s->timer0); | |
537 | qemu_put_8s(f, &s->timer1); | |
538 | qemu_put_8s(f, &s->precomp_trk); | |
539 | qemu_put_8s(f, &s->config); | |
540 | qemu_put_8s(f, &s->lock); | |
541 | qemu_put_8s(f, &s->pwrd); | |
542 | fd_save(f, &s->drives[0]); | |
543 | fd_save(f, &s->drives[1]); | |
544 | } | |
545 | ||
546 | static int fd_load (QEMUFile *f, fdrive_t *fd) | |
547 | { | |
548 | uint8_t tmp; | |
549 | ||
550 | qemu_get_8s(f, &tmp); | |
551 | fd->drflags = tmp; | |
552 | qemu_get_8s(f, &fd->head); | |
553 | qemu_get_8s(f, &fd->track); | |
554 | qemu_get_8s(f, &fd->sect); | |
555 | qemu_get_8s(f, &fd->dir); | |
556 | qemu_get_8s(f, &fd->rw); | |
557 | ||
558 | return 0; | |
559 | } | |
560 | ||
561 | static int fdc_load (QEMUFile *f, void *opaque, int version_id) | |
562 | { | |
563 | fdctrl_t *s = opaque; | |
564 | int ret; | |
565 | ||
566 | if (version_id != 1) | |
567 | return -EINVAL; | |
568 | ||
569 | qemu_get_8s(f, &s->state); | |
570 | qemu_get_8s(f, &s->dma_en); | |
571 | qemu_get_8s(f, &s->cur_drv); | |
572 | qemu_get_8s(f, &s->bootsel); | |
573 | qemu_get_buffer(f, s->fifo, FD_SECTOR_LEN); | |
574 | qemu_get_be32s(f, &s->data_pos); | |
575 | qemu_get_be32s(f, &s->data_len); | |
576 | qemu_get_8s(f, &s->data_state); | |
577 | qemu_get_8s(f, &s->data_dir); | |
578 | qemu_get_8s(f, &s->int_status); | |
579 | qemu_get_8s(f, &s->eot); | |
580 | qemu_get_8s(f, &s->timer0); | |
581 | qemu_get_8s(f, &s->timer1); | |
582 | qemu_get_8s(f, &s->precomp_trk); | |
583 | qemu_get_8s(f, &s->config); | |
584 | qemu_get_8s(f, &s->lock); | |
585 | qemu_get_8s(f, &s->pwrd); | |
586 | ||
587 | ret = fd_load(f, &s->drives[0]); | |
588 | if (ret == 0) | |
589 | ret = fd_load(f, &s->drives[1]); | |
590 | ||
591 | return ret; | |
592 | } | |
593 | ||
594 | static void fdctrl_external_reset(void *opaque) | |
595 | { | |
596 | fdctrl_t *s = opaque; | |
597 | ||
598 | fdctrl_reset(s, 0); | |
599 | } | |
600 | ||
7c560456 BS |
601 | static fdctrl_t *fdctrl_init_common (qemu_irq irq, int dma_chann, |
602 | target_phys_addr_t io_base, | |
603 | BlockDriverState **fds) | |
8977f3c1 | 604 | { |
baca51fa | 605 | fdctrl_t *fdctrl; |
8977f3c1 FB |
606 | int i; |
607 | ||
4b19ec0c | 608 | FLOPPY_DPRINTF("init controller\n"); |
baca51fa FB |
609 | fdctrl = qemu_mallocz(sizeof(fdctrl_t)); |
610 | if (!fdctrl) | |
611 | return NULL; | |
33f00271 AZ |
612 | fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN); |
613 | if (fdctrl->fifo == NULL) { | |
614 | qemu_free(fdctrl); | |
615 | return NULL; | |
616 | } | |
5fafdf24 | 617 | fdctrl->result_timer = qemu_new_timer(vm_clock, |
ed5fd2cc FB |
618 | fdctrl_result_timer, fdctrl); |
619 | ||
4b19ec0c | 620 | fdctrl->version = 0x90; /* Intel 82078 controller */ |
d537cf6c | 621 | fdctrl->irq = irq; |
baca51fa FB |
622 | fdctrl->dma_chann = dma_chann; |
623 | fdctrl->io_base = io_base; | |
a541f297 | 624 | fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */ |
baca51fa FB |
625 | if (fdctrl->dma_chann != -1) { |
626 | fdctrl->dma_en = 1; | |
627 | DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl); | |
8977f3c1 | 628 | } else { |
baca51fa | 629 | fdctrl->dma_en = 0; |
8977f3c1 | 630 | } |
baca51fa FB |
631 | for (i = 0; i < 2; i++) { |
632 | fd_init(&fdctrl->drives[i], fds[i]); | |
caed8802 | 633 | } |
baca51fa FB |
634 | fdctrl_reset(fdctrl, 0); |
635 | fdctrl->state = FD_CTRL_ACTIVE; | |
7c560456 BS |
636 | register_savevm("fdc", io_base, 1, fdc_save, fdc_load, fdctrl); |
637 | qemu_register_reset(fdctrl_external_reset, fdctrl); | |
638 | for (i = 0; i < 2; i++) { | |
639 | fd_revalidate(&fdctrl->drives[i]); | |
640 | } | |
641 | ||
642 | return fdctrl; | |
643 | } | |
644 | ||
645 | fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped, | |
646 | target_phys_addr_t io_base, | |
647 | BlockDriverState **fds) | |
648 | { | |
649 | fdctrl_t *fdctrl; | |
650 | int io_mem; | |
651 | ||
652 | fdctrl = fdctrl_init_common(irq, dma_chann, io_base, fds); | |
653 | ||
654 | fdctrl->sun4m = 0; | |
8977f3c1 | 655 | if (mem_mapped) { |
4f431960 JM |
656 | io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, |
657 | fdctrl); | |
e80cfcfc | 658 | cpu_register_physical_memory(io_base, 0x08, io_mem); |
8977f3c1 | 659 | } else { |
5dcb6b91 BS |
660 | register_ioport_read((uint32_t)io_base + 0x01, 5, 1, &fdctrl_read, |
661 | fdctrl); | |
662 | register_ioport_read((uint32_t)io_base + 0x07, 1, 1, &fdctrl_read, | |
663 | fdctrl); | |
664 | register_ioport_write((uint32_t)io_base + 0x01, 5, 1, &fdctrl_write, | |
665 | fdctrl); | |
666 | register_ioport_write((uint32_t)io_base + 0x07, 1, 1, &fdctrl_write, | |
667 | fdctrl); | |
8977f3c1 | 668 | } |
a541f297 | 669 | |
baca51fa | 670 | return fdctrl; |
caed8802 | 671 | } |
8977f3c1 | 672 | |
741402f9 BS |
673 | fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base, |
674 | BlockDriverState **fds) | |
675 | { | |
676 | fdctrl_t *fdctrl; | |
7c560456 | 677 | int io_mem; |
741402f9 | 678 | |
7c560456 | 679 | fdctrl = fdctrl_init_common(irq, 0, io_base, fds); |
a06e5a3c | 680 | fdctrl->sun4m = 1; |
7c560456 BS |
681 | io_mem = cpu_register_io_memory(0, fdctrl_mem_read_strict, |
682 | fdctrl_mem_write_strict, | |
683 | fdctrl); | |
684 | cpu_register_physical_memory(io_base, 0x08, io_mem); | |
741402f9 BS |
685 | |
686 | return fdctrl; | |
687 | } | |
688 | ||
baca51fa FB |
689 | /* XXX: may change if moved to bdrv */ |
690 | int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) | |
caed8802 | 691 | { |
baca51fa | 692 | return fdctrl->drives[drive_num].drive; |
8977f3c1 FB |
693 | } |
694 | ||
695 | /* Change IRQ state */ | |
baca51fa | 696 | static void fdctrl_reset_irq (fdctrl_t *fdctrl) |
8977f3c1 | 697 | { |
ed5fd2cc | 698 | FLOPPY_DPRINTF("Reset interrupt\n"); |
d537cf6c | 699 | qemu_set_irq(fdctrl->irq, 0); |
ed5fd2cc | 700 | fdctrl->state &= ~FD_CTRL_INTR; |
8977f3c1 FB |
701 | } |
702 | ||
baca51fa | 703 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) |
8977f3c1 | 704 | { |
6f7e9aec | 705 | // Sparc mutation |
a06e5a3c | 706 | if (fdctrl->sun4m && !fdctrl->dma_en) { |
4f431960 JM |
707 | fdctrl->state &= ~FD_CTRL_BUSY; |
708 | fdctrl->int_status = status; | |
709 | return; | |
6f7e9aec | 710 | } |
baca51fa | 711 | if (~(fdctrl->state & FD_CTRL_INTR)) { |
d537cf6c | 712 | qemu_set_irq(fdctrl->irq, 1); |
baca51fa | 713 | fdctrl->state |= FD_CTRL_INTR; |
8977f3c1 FB |
714 | } |
715 | FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); | |
baca51fa | 716 | fdctrl->int_status = status; |
8977f3c1 FB |
717 | } |
718 | ||
4b19ec0c | 719 | /* Reset controller */ |
baca51fa | 720 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) |
8977f3c1 FB |
721 | { |
722 | int i; | |
723 | ||
4b19ec0c | 724 | FLOPPY_DPRINTF("reset controller\n"); |
baca51fa | 725 | fdctrl_reset_irq(fdctrl); |
4b19ec0c | 726 | /* Initialise controller */ |
baca51fa | 727 | fdctrl->cur_drv = 0; |
8977f3c1 | 728 | /* FIFO state */ |
baca51fa FB |
729 | fdctrl->data_pos = 0; |
730 | fdctrl->data_len = 0; | |
731 | fdctrl->data_state = FD_STATE_CMD; | |
732 | fdctrl->data_dir = FD_DIR_WRITE; | |
8977f3c1 | 733 | for (i = 0; i < MAX_FD; i++) |
baca51fa FB |
734 | fd_reset(&fdctrl->drives[i]); |
735 | fdctrl_reset_fifo(fdctrl); | |
8977f3c1 | 736 | if (do_irq) |
ed5fd2cc | 737 | fdctrl_raise_irq(fdctrl, 0xc0); |
baca51fa FB |
738 | } |
739 | ||
740 | static inline fdrive_t *drv0 (fdctrl_t *fdctrl) | |
741 | { | |
742 | return &fdctrl->drives[fdctrl->bootsel]; | |
743 | } | |
744 | ||
745 | static inline fdrive_t *drv1 (fdctrl_t *fdctrl) | |
746 | { | |
747 | return &fdctrl->drives[1 - fdctrl->bootsel]; | |
748 | } | |
749 | ||
750 | static fdrive_t *get_cur_drv (fdctrl_t *fdctrl) | |
751 | { | |
752 | return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl); | |
8977f3c1 FB |
753 | } |
754 | ||
755 | /* Status B register : 0x01 (read-only) */ | |
baca51fa | 756 | static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl) |
8977f3c1 | 757 | { |
8977f3c1 | 758 | FLOPPY_DPRINTF("status register: 0x00\n"); |
8977f3c1 FB |
759 | return 0; |
760 | } | |
761 | ||
762 | /* Digital output register : 0x02 */ | |
baca51fa | 763 | static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl) |
8977f3c1 | 764 | { |
8977f3c1 FB |
765 | uint32_t retval = 0; |
766 | ||
8977f3c1 | 767 | /* Drive motors state indicators */ |
baca51fa | 768 | if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON) |
4f431960 | 769 | retval |= 1 << 5; |
baca51fa | 770 | if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON) |
4f431960 | 771 | retval |= 1 << 4; |
8977f3c1 | 772 | /* DMA enable */ |
baca51fa | 773 | retval |= fdctrl->dma_en << 3; |
8977f3c1 | 774 | /* Reset indicator */ |
baca51fa | 775 | retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0; |
8977f3c1 | 776 | /* Selected drive */ |
baca51fa | 777 | retval |= fdctrl->cur_drv; |
8977f3c1 FB |
778 | FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval); |
779 | ||
780 | return retval; | |
781 | } | |
782 | ||
baca51fa | 783 | static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) |
8977f3c1 | 784 | { |
8977f3c1 | 785 | /* Reset mode */ |
baca51fa | 786 | if (fdctrl->state & FD_CTRL_RESET) { |
8977f3c1 | 787 | if (!(value & 0x04)) { |
4b19ec0c | 788 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
8977f3c1 FB |
789 | return; |
790 | } | |
791 | } | |
792 | FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); | |
793 | /* Drive motors state indicators */ | |
794 | if (value & 0x20) | |
baca51fa | 795 | fd_start(drv1(fdctrl)); |
8977f3c1 | 796 | else |
baca51fa | 797 | fd_stop(drv1(fdctrl)); |
8977f3c1 | 798 | if (value & 0x10) |
baca51fa | 799 | fd_start(drv0(fdctrl)); |
8977f3c1 | 800 | else |
baca51fa | 801 | fd_stop(drv0(fdctrl)); |
8977f3c1 FB |
802 | /* DMA enable */ |
803 | #if 0 | |
baca51fa FB |
804 | if (fdctrl->dma_chann != -1) |
805 | fdctrl->dma_en = 1 - ((value >> 3) & 1); | |
8977f3c1 FB |
806 | #endif |
807 | /* Reset */ | |
808 | if (!(value & 0x04)) { | |
baca51fa | 809 | if (!(fdctrl->state & FD_CTRL_RESET)) { |
4b19ec0c | 810 | FLOPPY_DPRINTF("controller enter RESET state\n"); |
baca51fa | 811 | fdctrl->state |= FD_CTRL_RESET; |
8977f3c1 FB |
812 | } |
813 | } else { | |
baca51fa | 814 | if (fdctrl->state & FD_CTRL_RESET) { |
4b19ec0c | 815 | FLOPPY_DPRINTF("controller out of RESET state\n"); |
fb6cf1d0 | 816 | fdctrl_reset(fdctrl, 1); |
baca51fa | 817 | fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP); |
8977f3c1 FB |
818 | } |
819 | } | |
820 | /* Selected drive */ | |
baca51fa | 821 | fdctrl->cur_drv = value & 1; |
8977f3c1 FB |
822 | } |
823 | ||
824 | /* Tape drive register : 0x03 */ | |
baca51fa | 825 | static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) |
8977f3c1 FB |
826 | { |
827 | uint32_t retval = 0; | |
828 | ||
8977f3c1 | 829 | /* Disk boot selection indicator */ |
baca51fa | 830 | retval |= fdctrl->bootsel << 2; |
8977f3c1 FB |
831 | /* Tape indicators: never allowed */ |
832 | FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval); | |
833 | ||
834 | return retval; | |
835 | } | |
836 | ||
baca51fa | 837 | static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value) |
8977f3c1 | 838 | { |
8977f3c1 | 839 | /* Reset mode */ |
baca51fa | 840 | if (fdctrl->state & FD_CTRL_RESET) { |
4b19ec0c | 841 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
8977f3c1 FB |
842 | return; |
843 | } | |
844 | FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); | |
845 | /* Disk boot selection indicator */ | |
baca51fa | 846 | fdctrl->bootsel = (value >> 2) & 1; |
8977f3c1 FB |
847 | /* Tape indicators: never allow */ |
848 | } | |
849 | ||
850 | /* Main status register : 0x04 (read) */ | |
baca51fa | 851 | static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) |
8977f3c1 FB |
852 | { |
853 | uint32_t retval = 0; | |
854 | ||
baca51fa FB |
855 | fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET); |
856 | if (!(fdctrl->state & FD_CTRL_BUSY)) { | |
8977f3c1 FB |
857 | /* Data transfer allowed */ |
858 | retval |= 0x80; | |
859 | /* Data transfer direction indicator */ | |
baca51fa | 860 | if (fdctrl->data_dir == FD_DIR_READ) |
8977f3c1 FB |
861 | retval |= 0x40; |
862 | } | |
863 | /* Should handle 0x20 for SPECIFY command */ | |
864 | /* Command busy indicator */ | |
baca51fa FB |
865 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA || |
866 | FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) | |
8977f3c1 FB |
867 | retval |= 0x10; |
868 | FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); | |
869 | ||
870 | return retval; | |
871 | } | |
872 | ||
873 | /* Data select rate register : 0x04 (write) */ | |
baca51fa | 874 | static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) |
8977f3c1 | 875 | { |
8977f3c1 | 876 | /* Reset mode */ |
baca51fa | 877 | if (fdctrl->state & FD_CTRL_RESET) { |
4f431960 JM |
878 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
879 | return; | |
880 | } | |
8977f3c1 FB |
881 | FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); |
882 | /* Reset: autoclear */ | |
883 | if (value & 0x80) { | |
baca51fa FB |
884 | fdctrl->state |= FD_CTRL_RESET; |
885 | fdctrl_reset(fdctrl, 1); | |
886 | fdctrl->state &= ~FD_CTRL_RESET; | |
8977f3c1 FB |
887 | } |
888 | if (value & 0x40) { | |
baca51fa FB |
889 | fdctrl->state |= FD_CTRL_SLEEP; |
890 | fdctrl_reset(fdctrl, 1); | |
8977f3c1 FB |
891 | } |
892 | // fdctrl.precomp = (value >> 2) & 0x07; | |
893 | } | |
894 | ||
ea185bbd FB |
895 | static int fdctrl_media_changed(fdrive_t *drv) |
896 | { | |
897 | int ret; | |
4f431960 | 898 | |
5fafdf24 | 899 | if (!drv->bs) |
ea185bbd FB |
900 | return 0; |
901 | ret = bdrv_media_changed(drv->bs); | |
902 | if (ret) { | |
903 | fd_revalidate(drv); | |
904 | } | |
905 | return ret; | |
906 | } | |
907 | ||
8977f3c1 | 908 | /* Digital input register : 0x07 (read-only) */ |
baca51fa | 909 | static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) |
8977f3c1 | 910 | { |
8977f3c1 FB |
911 | uint32_t retval = 0; |
912 | ||
ea185bbd | 913 | if (fdctrl_media_changed(drv0(fdctrl)) || |
4f431960 | 914 | fdctrl_media_changed(drv1(fdctrl))) |
8977f3c1 FB |
915 | retval |= 0x80; |
916 | if (retval != 0) | |
baca51fa | 917 | FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); |
8977f3c1 FB |
918 | |
919 | return retval; | |
920 | } | |
921 | ||
922 | /* FIFO state control */ | |
baca51fa | 923 | static void fdctrl_reset_fifo (fdctrl_t *fdctrl) |
8977f3c1 | 924 | { |
baca51fa FB |
925 | fdctrl->data_dir = FD_DIR_WRITE; |
926 | fdctrl->data_pos = 0; | |
927 | FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD); | |
8977f3c1 FB |
928 | } |
929 | ||
930 | /* Set FIFO status for the host to read */ | |
baca51fa | 931 | static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq) |
8977f3c1 | 932 | { |
baca51fa FB |
933 | fdctrl->data_dir = FD_DIR_READ; |
934 | fdctrl->data_len = fifo_len; | |
935 | fdctrl->data_pos = 0; | |
936 | FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS); | |
8977f3c1 | 937 | if (do_irq) |
baca51fa | 938 | fdctrl_raise_irq(fdctrl, 0x00); |
8977f3c1 FB |
939 | } |
940 | ||
941 | /* Set an error: unimplemented/unknown command */ | |
baca51fa | 942 | static void fdctrl_unimplemented (fdctrl_t *fdctrl) |
8977f3c1 FB |
943 | { |
944 | #if 0 | |
baca51fa FB |
945 | fdrive_t *cur_drv; |
946 | ||
947 | cur_drv = get_cur_drv(fdctrl); | |
890fa6be | 948 | fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv; |
baca51fa FB |
949 | fdctrl->fifo[1] = 0x00; |
950 | fdctrl->fifo[2] = 0x00; | |
951 | fdctrl_set_fifo(fdctrl, 3, 1); | |
8977f3c1 | 952 | #else |
baca51fa FB |
953 | // fdctrl_reset_fifo(fdctrl); |
954 | fdctrl->fifo[0] = 0x80; | |
955 | fdctrl_set_fifo(fdctrl, 1, 0); | |
8977f3c1 FB |
956 | #endif |
957 | } | |
958 | ||
959 | /* Callback for transfer end (stop or abort) */ | |
baca51fa | 960 | static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, |
4f431960 | 961 | uint8_t status1, uint8_t status2) |
8977f3c1 | 962 | { |
baca51fa | 963 | fdrive_t *cur_drv; |
8977f3c1 | 964 | |
baca51fa | 965 | cur_drv = get_cur_drv(fdctrl); |
8977f3c1 FB |
966 | FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", |
967 | status0, status1, status2, | |
890fa6be FB |
968 | status0 | (cur_drv->head << 2) | fdctrl->cur_drv); |
969 | fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv; | |
baca51fa FB |
970 | fdctrl->fifo[1] = status1; |
971 | fdctrl->fifo[2] = status2; | |
972 | fdctrl->fifo[3] = cur_drv->track; | |
973 | fdctrl->fifo[4] = cur_drv->head; | |
974 | fdctrl->fifo[5] = cur_drv->sect; | |
975 | fdctrl->fifo[6] = FD_SECTOR_SC; | |
976 | fdctrl->data_dir = FD_DIR_READ; | |
ed5fd2cc | 977 | if (fdctrl->state & FD_CTRL_BUSY) { |
baca51fa | 978 | DMA_release_DREQ(fdctrl->dma_chann); |
ed5fd2cc FB |
979 | fdctrl->state &= ~FD_CTRL_BUSY; |
980 | } | |
baca51fa | 981 | fdctrl_set_fifo(fdctrl, 7, 1); |
8977f3c1 FB |
982 | } |
983 | ||
984 | /* Prepare a data transfer (either DMA or FIFO) */ | |
baca51fa | 985 | static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) |
8977f3c1 | 986 | { |
baca51fa | 987 | fdrive_t *cur_drv; |
8977f3c1 FB |
988 | uint8_t kh, kt, ks; |
989 | int did_seek; | |
990 | ||
baca51fa FB |
991 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
992 | cur_drv = get_cur_drv(fdctrl); | |
993 | kt = fdctrl->fifo[2]; | |
994 | kh = fdctrl->fifo[3]; | |
995 | ks = fdctrl->fifo[4]; | |
4b19ec0c | 996 | FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", |
baca51fa | 997 | fdctrl->cur_drv, kh, kt, ks, |
8977f3c1 FB |
998 | _fd_sector(kh, kt, ks, cur_drv->last_sect)); |
999 | did_seek = 0; | |
baca51fa | 1000 | switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { |
8977f3c1 FB |
1001 | case 2: |
1002 | /* sect too big */ | |
baca51fa FB |
1003 | fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); |
1004 | fdctrl->fifo[3] = kt; | |
1005 | fdctrl->fifo[4] = kh; | |
1006 | fdctrl->fifo[5] = ks; | |
8977f3c1 FB |
1007 | return; |
1008 | case 3: | |
1009 | /* track too big */ | |
baca51fa FB |
1010 | fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); |
1011 | fdctrl->fifo[3] = kt; | |
1012 | fdctrl->fifo[4] = kh; | |
1013 | fdctrl->fifo[5] = ks; | |
8977f3c1 FB |
1014 | return; |
1015 | case 4: | |
1016 | /* No seek enabled */ | |
baca51fa FB |
1017 | fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); |
1018 | fdctrl->fifo[3] = kt; | |
1019 | fdctrl->fifo[4] = kh; | |
1020 | fdctrl->fifo[5] = ks; | |
8977f3c1 FB |
1021 | return; |
1022 | case 1: | |
1023 | did_seek = 1; | |
1024 | break; | |
1025 | default: | |
1026 | break; | |
1027 | } | |
1028 | /* Set the FIFO state */ | |
baca51fa FB |
1029 | fdctrl->data_dir = direction; |
1030 | fdctrl->data_pos = 0; | |
1031 | FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */ | |
1032 | if (fdctrl->fifo[0] & 0x80) | |
1033 | fdctrl->data_state |= FD_STATE_MULTI; | |
1034 | else | |
1035 | fdctrl->data_state &= ~FD_STATE_MULTI; | |
8977f3c1 | 1036 | if (did_seek) |
baca51fa FB |
1037 | fdctrl->data_state |= FD_STATE_SEEK; |
1038 | else | |
1039 | fdctrl->data_state &= ~FD_STATE_SEEK; | |
1040 | if (fdctrl->fifo[5] == 00) { | |
1041 | fdctrl->data_len = fdctrl->fifo[8]; | |
1042 | } else { | |
4f431960 | 1043 | int tmp; |
3bcb80f1 | 1044 | fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); |
baca51fa FB |
1045 | tmp = (cur_drv->last_sect - ks + 1); |
1046 | if (fdctrl->fifo[0] & 0x80) | |
1047 | tmp += cur_drv->last_sect; | |
4f431960 | 1048 | fdctrl->data_len *= tmp; |
baca51fa | 1049 | } |
890fa6be | 1050 | fdctrl->eot = fdctrl->fifo[6]; |
baca51fa | 1051 | if (fdctrl->dma_en) { |
8977f3c1 FB |
1052 | int dma_mode; |
1053 | /* DMA transfer are enabled. Check if DMA channel is well programmed */ | |
baca51fa | 1054 | dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); |
8977f3c1 | 1055 | dma_mode = (dma_mode >> 2) & 3; |
baca51fa | 1056 | FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", |
4f431960 | 1057 | dma_mode, direction, |
baca51fa | 1058 | (128 << fdctrl->fifo[5]) * |
4f431960 | 1059 | (cur_drv->last_sect - ks + 1), fdctrl->data_len); |
8977f3c1 FB |
1060 | if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || |
1061 | direction == FD_DIR_SCANH) && dma_mode == 0) || | |
1062 | (direction == FD_DIR_WRITE && dma_mode == 2) || | |
1063 | (direction == FD_DIR_READ && dma_mode == 1)) { | |
1064 | /* No access is allowed until DMA transfer has completed */ | |
baca51fa | 1065 | fdctrl->state |= FD_CTRL_BUSY; |
4b19ec0c | 1066 | /* Now, we just have to wait for the DMA controller to |
8977f3c1 FB |
1067 | * recall us... |
1068 | */ | |
baca51fa FB |
1069 | DMA_hold_DREQ(fdctrl->dma_chann); |
1070 | DMA_schedule(fdctrl->dma_chann); | |
8977f3c1 | 1071 | return; |
baca51fa | 1072 | } else { |
4f431960 | 1073 | FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction); |
8977f3c1 FB |
1074 | } |
1075 | } | |
1076 | FLOPPY_DPRINTF("start non-DMA transfer\n"); | |
1077 | /* IO based transfer: calculate len */ | |
baca51fa | 1078 | fdctrl_raise_irq(fdctrl, 0x00); |
8977f3c1 FB |
1079 | |
1080 | return; | |
1081 | } | |
1082 | ||
1083 | /* Prepare a transfer of deleted data */ | |
baca51fa | 1084 | static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) |
8977f3c1 FB |
1085 | { |
1086 | /* We don't handle deleted data, | |
1087 | * so we don't return *ANYTHING* | |
1088 | */ | |
baca51fa | 1089 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); |
8977f3c1 FB |
1090 | } |
1091 | ||
1092 | /* handlers for DMA transfers */ | |
85571bc7 FB |
1093 | static int fdctrl_transfer_handler (void *opaque, int nchan, |
1094 | int dma_pos, int dma_len) | |
8977f3c1 | 1095 | { |
baca51fa FB |
1096 | fdctrl_t *fdctrl; |
1097 | fdrive_t *cur_drv; | |
1098 | int len, start_pos, rel_pos; | |
8977f3c1 FB |
1099 | uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; |
1100 | ||
baca51fa | 1101 | fdctrl = opaque; |
baca51fa | 1102 | if (!(fdctrl->state & FD_CTRL_BUSY)) { |
8977f3c1 FB |
1103 | FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); |
1104 | return 0; | |
1105 | } | |
baca51fa FB |
1106 | cur_drv = get_cur_drv(fdctrl); |
1107 | if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || | |
1108 | fdctrl->data_dir == FD_DIR_SCANH) | |
8977f3c1 | 1109 | status2 = 0x04; |
85571bc7 FB |
1110 | if (dma_len > fdctrl->data_len) |
1111 | dma_len = fdctrl->data_len; | |
890fa6be | 1112 | if (cur_drv->bs == NULL) { |
4f431960 JM |
1113 | if (fdctrl->data_dir == FD_DIR_WRITE) |
1114 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); | |
1115 | else | |
1116 | fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); | |
1117 | len = 0; | |
890fa6be FB |
1118 | goto transfer_error; |
1119 | } | |
baca51fa | 1120 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; |
85571bc7 FB |
1121 | for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { |
1122 | len = dma_len - fdctrl->data_pos; | |
baca51fa FB |
1123 | if (len + rel_pos > FD_SECTOR_LEN) |
1124 | len = FD_SECTOR_LEN - rel_pos; | |
6f7e9aec FB |
1125 | FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " |
1126 | "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, | |
baca51fa FB |
1127 | fdctrl->data_len, fdctrl->cur_drv, cur_drv->head, |
1128 | cur_drv->track, cur_drv->sect, fd_sector(cur_drv), | |
6f7e9aec | 1129 | fd_sector(cur_drv) * 512); |
baca51fa | 1130 | if (fdctrl->data_dir != FD_DIR_WRITE || |
4f431960 | 1131 | len < FD_SECTOR_LEN || rel_pos != 0) { |
baca51fa FB |
1132 | /* READ & SCAN commands and realign to a sector for WRITE */ |
1133 | if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), | |
4f431960 | 1134 | fdctrl->fifo, 1) < 0) { |
8977f3c1 FB |
1135 | FLOPPY_DPRINTF("Floppy: error getting sector %d\n", |
1136 | fd_sector(cur_drv)); | |
1137 | /* Sure, image size is too small... */ | |
baca51fa | 1138 | memset(fdctrl->fifo, 0, FD_SECTOR_LEN); |
8977f3c1 | 1139 | } |
890fa6be | 1140 | } |
4f431960 JM |
1141 | switch (fdctrl->data_dir) { |
1142 | case FD_DIR_READ: | |
1143 | /* READ commands */ | |
85571bc7 FB |
1144 | DMA_write_memory (nchan, fdctrl->fifo + rel_pos, |
1145 | fdctrl->data_pos, len); | |
4f431960 JM |
1146 | break; |
1147 | case FD_DIR_WRITE: | |
baca51fa | 1148 | /* WRITE commands */ |
85571bc7 FB |
1149 | DMA_read_memory (nchan, fdctrl->fifo + rel_pos, |
1150 | fdctrl->data_pos, len); | |
baca51fa | 1151 | if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), |
4f431960 | 1152 | fdctrl->fifo, 1) < 0) { |
baca51fa FB |
1153 | FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); |
1154 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); | |
1155 | goto transfer_error; | |
890fa6be | 1156 | } |
4f431960 JM |
1157 | break; |
1158 | default: | |
1159 | /* SCAN commands */ | |
baca51fa | 1160 | { |
4f431960 | 1161 | uint8_t tmpbuf[FD_SECTOR_LEN]; |
baca51fa | 1162 | int ret; |
85571bc7 | 1163 | DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); |
baca51fa | 1164 | ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); |
8977f3c1 FB |
1165 | if (ret == 0) { |
1166 | status2 = 0x08; | |
1167 | goto end_transfer; | |
1168 | } | |
baca51fa FB |
1169 | if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || |
1170 | (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { | |
8977f3c1 FB |
1171 | status2 = 0x00; |
1172 | goto end_transfer; | |
1173 | } | |
1174 | } | |
4f431960 | 1175 | break; |
8977f3c1 | 1176 | } |
4f431960 JM |
1177 | fdctrl->data_pos += len; |
1178 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; | |
baca51fa | 1179 | if (rel_pos == 0) { |
8977f3c1 | 1180 | /* Seek to next sector */ |
4f431960 JM |
1181 | FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n", |
1182 | cur_drv->head, cur_drv->track, cur_drv->sect, | |
1183 | fd_sector(cur_drv), | |
1184 | fdctrl->data_pos - len); | |
890fa6be FB |
1185 | /* XXX: cur_drv->sect >= cur_drv->last_sect should be an |
1186 | error in fact */ | |
1187 | if (cur_drv->sect >= cur_drv->last_sect || | |
1188 | cur_drv->sect == fdctrl->eot) { | |
4f431960 JM |
1189 | cur_drv->sect = 1; |
1190 | if (FD_MULTI_TRACK(fdctrl->data_state)) { | |
1191 | if (cur_drv->head == 0 && | |
1192 | (cur_drv->flags & FDISK_DBL_SIDES) != 0) { | |
890fa6be FB |
1193 | cur_drv->head = 1; |
1194 | } else { | |
1195 | cur_drv->head = 0; | |
4f431960 JM |
1196 | cur_drv->track++; |
1197 | if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) | |
1198 | break; | |
890fa6be FB |
1199 | } |
1200 | } else { | |
1201 | cur_drv->track++; | |
1202 | break; | |
8977f3c1 | 1203 | } |
4f431960 JM |
1204 | FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", |
1205 | cur_drv->head, cur_drv->track, | |
1206 | cur_drv->sect, fd_sector(cur_drv)); | |
890fa6be FB |
1207 | } else { |
1208 | cur_drv->sect++; | |
8977f3c1 FB |
1209 | } |
1210 | } | |
1211 | } | |
4f431960 | 1212 | end_transfer: |
baca51fa FB |
1213 | len = fdctrl->data_pos - start_pos; |
1214 | FLOPPY_DPRINTF("end transfer %d %d %d\n", | |
4f431960 | 1215 | fdctrl->data_pos, len, fdctrl->data_len); |
baca51fa FB |
1216 | if (fdctrl->data_dir == FD_DIR_SCANE || |
1217 | fdctrl->data_dir == FD_DIR_SCANL || | |
1218 | fdctrl->data_dir == FD_DIR_SCANH) | |
8977f3c1 | 1219 | status2 = 0x08; |
baca51fa | 1220 | if (FD_DID_SEEK(fdctrl->data_state)) |
8977f3c1 | 1221 | status0 |= 0x20; |
baca51fa FB |
1222 | fdctrl->data_len -= len; |
1223 | // if (fdctrl->data_len == 0) | |
890fa6be | 1224 | fdctrl_stop_transfer(fdctrl, status0, status1, status2); |
4f431960 | 1225 | transfer_error: |
8977f3c1 | 1226 | |
baca51fa | 1227 | return len; |
8977f3c1 FB |
1228 | } |
1229 | ||
8977f3c1 | 1230 | /* Data register : 0x05 */ |
baca51fa | 1231 | static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) |
8977f3c1 | 1232 | { |
baca51fa | 1233 | fdrive_t *cur_drv; |
8977f3c1 FB |
1234 | uint32_t retval = 0; |
1235 | int pos, len; | |
1236 | ||
baca51fa FB |
1237 | cur_drv = get_cur_drv(fdctrl); |
1238 | fdctrl->state &= ~FD_CTRL_SLEEP; | |
1239 | if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) { | |
8977f3c1 FB |
1240 | FLOPPY_ERROR("can't read data in CMD state\n"); |
1241 | return 0; | |
1242 | } | |
baca51fa FB |
1243 | pos = fdctrl->data_pos; |
1244 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { | |
8977f3c1 FB |
1245 | pos %= FD_SECTOR_LEN; |
1246 | if (pos == 0) { | |
baca51fa | 1247 | len = fdctrl->data_len - fdctrl->data_pos; |
8977f3c1 FB |
1248 | if (len > FD_SECTOR_LEN) |
1249 | len = FD_SECTOR_LEN; | |
d6c1a327 | 1250 | bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1); |
8977f3c1 FB |
1251 | } |
1252 | } | |
baca51fa FB |
1253 | retval = fdctrl->fifo[pos]; |
1254 | if (++fdctrl->data_pos == fdctrl->data_len) { | |
1255 | fdctrl->data_pos = 0; | |
890fa6be | 1256 | /* Switch from transfer mode to status mode |
8977f3c1 FB |
1257 | * then from status mode to command mode |
1258 | */ | |
ed5fd2cc | 1259 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { |
baca51fa | 1260 | fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); |
ed5fd2cc | 1261 | } else { |
baca51fa | 1262 | fdctrl_reset_fifo(fdctrl); |
ed5fd2cc FB |
1263 | fdctrl_reset_irq(fdctrl); |
1264 | } | |
8977f3c1 FB |
1265 | } |
1266 | FLOPPY_DPRINTF("data register: 0x%02x\n", retval); | |
1267 | ||
1268 | return retval; | |
1269 | } | |
1270 | ||
baca51fa | 1271 | static void fdctrl_format_sector (fdctrl_t *fdctrl) |
8977f3c1 | 1272 | { |
baca51fa FB |
1273 | fdrive_t *cur_drv; |
1274 | uint8_t kh, kt, ks; | |
1275 | int did_seek; | |
8977f3c1 | 1276 | |
baca51fa FB |
1277 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
1278 | cur_drv = get_cur_drv(fdctrl); | |
1279 | kt = fdctrl->fifo[6]; | |
1280 | kh = fdctrl->fifo[7]; | |
1281 | ks = fdctrl->fifo[8]; | |
1282 | FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", | |
1283 | fdctrl->cur_drv, kh, kt, ks, | |
1284 | _fd_sector(kh, kt, ks, cur_drv->last_sect)); | |
1285 | did_seek = 0; | |
1286 | switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { | |
1287 | case 2: | |
1288 | /* sect too big */ | |
1289 | fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); | |
1290 | fdctrl->fifo[3] = kt; | |
1291 | fdctrl->fifo[4] = kh; | |
1292 | fdctrl->fifo[5] = ks; | |
1293 | return; | |
1294 | case 3: | |
1295 | /* track too big */ | |
1296 | fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); | |
1297 | fdctrl->fifo[3] = kt; | |
1298 | fdctrl->fifo[4] = kh; | |
1299 | fdctrl->fifo[5] = ks; | |
1300 | return; | |
1301 | case 4: | |
1302 | /* No seek enabled */ | |
1303 | fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); | |
1304 | fdctrl->fifo[3] = kt; | |
1305 | fdctrl->fifo[4] = kh; | |
1306 | fdctrl->fifo[5] = ks; | |
1307 | return; | |
1308 | case 1: | |
1309 | did_seek = 1; | |
1310 | fdctrl->data_state |= FD_STATE_SEEK; | |
1311 | break; | |
1312 | default: | |
1313 | break; | |
1314 | } | |
1315 | memset(fdctrl->fifo, 0, FD_SECTOR_LEN); | |
1316 | if (cur_drv->bs == NULL || | |
1317 | bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { | |
37a4c539 | 1318 | FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); |
baca51fa FB |
1319 | fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); |
1320 | } else { | |
4f431960 JM |
1321 | if (cur_drv->sect == cur_drv->last_sect) { |
1322 | fdctrl->data_state &= ~FD_STATE_FORMAT; | |
1323 | /* Last sector done */ | |
1324 | if (FD_DID_SEEK(fdctrl->data_state)) | |
1325 | fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); | |
1326 | else | |
1327 | fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); | |
1328 | } else { | |
1329 | /* More to do */ | |
1330 | fdctrl->data_pos = 0; | |
1331 | fdctrl->data_len = 4; | |
1332 | } | |
baca51fa FB |
1333 | } |
1334 | } | |
1335 | ||
1336 | static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) | |
1337 | { | |
1338 | fdrive_t *cur_drv; | |
1339 | ||
baca51fa | 1340 | cur_drv = get_cur_drv(fdctrl); |
8977f3c1 | 1341 | /* Reset mode */ |
baca51fa | 1342 | if (fdctrl->state & FD_CTRL_RESET) { |
4b19ec0c | 1343 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
8977f3c1 FB |
1344 | return; |
1345 | } | |
baca51fa FB |
1346 | fdctrl->state &= ~FD_CTRL_SLEEP; |
1347 | if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) { | |
8977f3c1 FB |
1348 | FLOPPY_ERROR("can't write data in status mode\n"); |
1349 | return; | |
1350 | } | |
1351 | /* Is it write command time ? */ | |
baca51fa | 1352 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { |
8977f3c1 | 1353 | /* FIFO data write */ |
baca51fa FB |
1354 | fdctrl->fifo[fdctrl->data_pos++] = value; |
1355 | if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) || | |
1356 | fdctrl->data_pos == fdctrl->data_len) { | |
d6c1a327 | 1357 | bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1); |
8977f3c1 | 1358 | } |
890fa6be | 1359 | /* Switch from transfer mode to status mode |
8977f3c1 FB |
1360 | * then from status mode to command mode |
1361 | */ | |
baca51fa FB |
1362 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) |
1363 | fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); | |
8977f3c1 FB |
1364 | return; |
1365 | } | |
baca51fa | 1366 | if (fdctrl->data_pos == 0) { |
8977f3c1 FB |
1367 | /* Command */ |
1368 | switch (value & 0x5F) { | |
1369 | case 0x46: | |
1370 | /* READ variants */ | |
1371 | FLOPPY_DPRINTF("READ command\n"); | |
1372 | /* 8 parameters cmd */ | |
baca51fa | 1373 | fdctrl->data_len = 9; |
8977f3c1 FB |
1374 | goto enqueue; |
1375 | case 0x4C: | |
1376 | /* READ_DELETED variants */ | |
1377 | FLOPPY_DPRINTF("READ_DELETED command\n"); | |
1378 | /* 8 parameters cmd */ | |
baca51fa | 1379 | fdctrl->data_len = 9; |
8977f3c1 FB |
1380 | goto enqueue; |
1381 | case 0x50: | |
1382 | /* SCAN_EQUAL variants */ | |
1383 | FLOPPY_DPRINTF("SCAN_EQUAL command\n"); | |
1384 | /* 8 parameters cmd */ | |
baca51fa | 1385 | fdctrl->data_len = 9; |
8977f3c1 FB |
1386 | goto enqueue; |
1387 | case 0x56: | |
1388 | /* VERIFY variants */ | |
1389 | FLOPPY_DPRINTF("VERIFY command\n"); | |
1390 | /* 8 parameters cmd */ | |
baca51fa | 1391 | fdctrl->data_len = 9; |
8977f3c1 FB |
1392 | goto enqueue; |
1393 | case 0x59: | |
1394 | /* SCAN_LOW_OR_EQUAL variants */ | |
1395 | FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n"); | |
1396 | /* 8 parameters cmd */ | |
baca51fa | 1397 | fdctrl->data_len = 9; |
8977f3c1 FB |
1398 | goto enqueue; |
1399 | case 0x5D: | |
1400 | /* SCAN_HIGH_OR_EQUAL variants */ | |
1401 | FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n"); | |
1402 | /* 8 parameters cmd */ | |
baca51fa | 1403 | fdctrl->data_len = 9; |
8977f3c1 FB |
1404 | goto enqueue; |
1405 | default: | |
1406 | break; | |
1407 | } | |
1408 | switch (value & 0x7F) { | |
1409 | case 0x45: | |
1410 | /* WRITE variants */ | |
1411 | FLOPPY_DPRINTF("WRITE command\n"); | |
1412 | /* 8 parameters cmd */ | |
baca51fa | 1413 | fdctrl->data_len = 9; |
8977f3c1 FB |
1414 | goto enqueue; |
1415 | case 0x49: | |
1416 | /* WRITE_DELETED variants */ | |
1417 | FLOPPY_DPRINTF("WRITE_DELETED command\n"); | |
1418 | /* 8 parameters cmd */ | |
baca51fa | 1419 | fdctrl->data_len = 9; |
8977f3c1 FB |
1420 | goto enqueue; |
1421 | default: | |
1422 | break; | |
1423 | } | |
1424 | switch (value) { | |
1425 | case 0x03: | |
1426 | /* SPECIFY */ | |
1427 | FLOPPY_DPRINTF("SPECIFY command\n"); | |
1428 | /* 1 parameter cmd */ | |
baca51fa | 1429 | fdctrl->data_len = 3; |
8977f3c1 FB |
1430 | goto enqueue; |
1431 | case 0x04: | |
1432 | /* SENSE_DRIVE_STATUS */ | |
1433 | FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n"); | |
1434 | /* 1 parameter cmd */ | |
baca51fa | 1435 | fdctrl->data_len = 2; |
8977f3c1 FB |
1436 | goto enqueue; |
1437 | case 0x07: | |
1438 | /* RECALIBRATE */ | |
1439 | FLOPPY_DPRINTF("RECALIBRATE command\n"); | |
1440 | /* 1 parameter cmd */ | |
baca51fa | 1441 | fdctrl->data_len = 2; |
8977f3c1 FB |
1442 | goto enqueue; |
1443 | case 0x08: | |
1444 | /* SENSE_INTERRUPT_STATUS */ | |
1445 | FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n", | |
baca51fa | 1446 | fdctrl->int_status); |
8977f3c1 | 1447 | /* No parameters cmd: returns status if no interrupt */ |
953569d2 | 1448 | #if 0 |
baca51fa FB |
1449 | fdctrl->fifo[0] = |
1450 | fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv; | |
953569d2 FB |
1451 | #else |
1452 | /* XXX: int_status handling is broken for read/write | |
1453 | commands, so we do this hack. It should be suppressed | |
1454 | ASAP */ | |
1455 | fdctrl->fifo[0] = | |
1456 | 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv; | |
1457 | #endif | |
baca51fa FB |
1458 | fdctrl->fifo[1] = cur_drv->track; |
1459 | fdctrl_set_fifo(fdctrl, 2, 0); | |
4f431960 JM |
1460 | fdctrl_reset_irq(fdctrl); |
1461 | fdctrl->int_status = 0xC0; | |
8977f3c1 FB |
1462 | return; |
1463 | case 0x0E: | |
1464 | /* DUMPREG */ | |
1465 | FLOPPY_DPRINTF("DUMPREG command\n"); | |
1466 | /* Drives position */ | |
baca51fa FB |
1467 | fdctrl->fifo[0] = drv0(fdctrl)->track; |
1468 | fdctrl->fifo[1] = drv1(fdctrl)->track; | |
1469 | fdctrl->fifo[2] = 0; | |
1470 | fdctrl->fifo[3] = 0; | |
8977f3c1 | 1471 | /* timers */ |
baca51fa FB |
1472 | fdctrl->fifo[4] = fdctrl->timer0; |
1473 | fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en; | |
1474 | fdctrl->fifo[6] = cur_drv->last_sect; | |
1475 | fdctrl->fifo[7] = (fdctrl->lock << 7) | | |
4f431960 | 1476 | (cur_drv->perpendicular << 2); |
baca51fa FB |
1477 | fdctrl->fifo[8] = fdctrl->config; |
1478 | fdctrl->fifo[9] = fdctrl->precomp_trk; | |
1479 | fdctrl_set_fifo(fdctrl, 10, 0); | |
8977f3c1 FB |
1480 | return; |
1481 | case 0x0F: | |
1482 | /* SEEK */ | |
1483 | FLOPPY_DPRINTF("SEEK command\n"); | |
1484 | /* 2 parameters cmd */ | |
baca51fa | 1485 | fdctrl->data_len = 3; |
8977f3c1 FB |
1486 | goto enqueue; |
1487 | case 0x10: | |
1488 | /* VERSION */ | |
1489 | FLOPPY_DPRINTF("VERSION command\n"); | |
1490 | /* No parameters cmd */ | |
4b19ec0c | 1491 | /* Controller's version */ |
baca51fa FB |
1492 | fdctrl->fifo[0] = fdctrl->version; |
1493 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 FB |
1494 | return; |
1495 | case 0x12: | |
1496 | /* PERPENDICULAR_MODE */ | |
1497 | FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n"); | |
1498 | /* 1 parameter cmd */ | |
baca51fa | 1499 | fdctrl->data_len = 2; |
8977f3c1 FB |
1500 | goto enqueue; |
1501 | case 0x13: | |
1502 | /* CONFIGURE */ | |
1503 | FLOPPY_DPRINTF("CONFIGURE command\n"); | |
1504 | /* 3 parameters cmd */ | |
baca51fa | 1505 | fdctrl->data_len = 4; |
8977f3c1 FB |
1506 | goto enqueue; |
1507 | case 0x14: | |
1508 | /* UNLOCK */ | |
1509 | FLOPPY_DPRINTF("UNLOCK command\n"); | |
1510 | /* No parameters cmd */ | |
baca51fa FB |
1511 | fdctrl->lock = 0; |
1512 | fdctrl->fifo[0] = 0; | |
1513 | fdctrl_set_fifo(fdctrl, 1, 0); | |
8977f3c1 FB |
1514 | return; |
1515 | case 0x17: | |
1516 | /* POWERDOWN_MODE */ | |
1517 | FLOPPY_DPRINTF("POWERDOWN_MODE command\n"); | |
1518 | /* 2 parameters cmd */ | |
baca51fa | 1519 | fdctrl->data_len = 3; |
8977f3c1 FB |
1520 | goto enqueue; |
1521 | case 0x18: | |
1522 | /* PART_ID */ | |
1523 | FLOPPY_DPRINTF("PART_ID command\n"); | |
1524 | /* No parameters cmd */ | |
baca51fa FB |
1525 | fdctrl->fifo[0] = 0x41; /* Stepping 1 */ |
1526 | fdctrl_set_fifo(fdctrl, 1, 0); | |
8977f3c1 FB |
1527 | return; |
1528 | case 0x2C: | |
1529 | /* SAVE */ | |
1530 | FLOPPY_DPRINTF("SAVE command\n"); | |
1531 | /* No parameters cmd */ | |
baca51fa FB |
1532 | fdctrl->fifo[0] = 0; |
1533 | fdctrl->fifo[1] = 0; | |
8977f3c1 | 1534 | /* Drives position */ |
baca51fa FB |
1535 | fdctrl->fifo[2] = drv0(fdctrl)->track; |
1536 | fdctrl->fifo[3] = drv1(fdctrl)->track; | |
1537 | fdctrl->fifo[4] = 0; | |
1538 | fdctrl->fifo[5] = 0; | |
8977f3c1 | 1539 | /* timers */ |
baca51fa FB |
1540 | fdctrl->fifo[6] = fdctrl->timer0; |
1541 | fdctrl->fifo[7] = fdctrl->timer1; | |
1542 | fdctrl->fifo[8] = cur_drv->last_sect; | |
1543 | fdctrl->fifo[9] = (fdctrl->lock << 7) | | |
4f431960 | 1544 | (cur_drv->perpendicular << 2); |
baca51fa FB |
1545 | fdctrl->fifo[10] = fdctrl->config; |
1546 | fdctrl->fifo[11] = fdctrl->precomp_trk; | |
1547 | fdctrl->fifo[12] = fdctrl->pwrd; | |
1548 | fdctrl->fifo[13] = 0; | |
1549 | fdctrl->fifo[14] = 0; | |
1550 | fdctrl_set_fifo(fdctrl, 15, 1); | |
8977f3c1 FB |
1551 | return; |
1552 | case 0x33: | |
1553 | /* OPTION */ | |
1554 | FLOPPY_DPRINTF("OPTION command\n"); | |
1555 | /* 1 parameter cmd */ | |
baca51fa | 1556 | fdctrl->data_len = 2; |
8977f3c1 FB |
1557 | goto enqueue; |
1558 | case 0x42: | |
1559 | /* READ_TRACK */ | |
1560 | FLOPPY_DPRINTF("READ_TRACK command\n"); | |
1561 | /* 8 parameters cmd */ | |
baca51fa | 1562 | fdctrl->data_len = 9; |
8977f3c1 FB |
1563 | goto enqueue; |
1564 | case 0x4A: | |
1565 | /* READ_ID */ | |
1566 | FLOPPY_DPRINTF("READ_ID command\n"); | |
1567 | /* 1 parameter cmd */ | |
baca51fa | 1568 | fdctrl->data_len = 2; |
8977f3c1 FB |
1569 | goto enqueue; |
1570 | case 0x4C: | |
1571 | /* RESTORE */ | |
1572 | FLOPPY_DPRINTF("RESTORE command\n"); | |
1573 | /* 17 parameters cmd */ | |
baca51fa | 1574 | fdctrl->data_len = 18; |
8977f3c1 FB |
1575 | goto enqueue; |
1576 | case 0x4D: | |
1577 | /* FORMAT_TRACK */ | |
1578 | FLOPPY_DPRINTF("FORMAT_TRACK command\n"); | |
1579 | /* 5 parameters cmd */ | |
baca51fa | 1580 | fdctrl->data_len = 6; |
8977f3c1 FB |
1581 | goto enqueue; |
1582 | case 0x8E: | |
1583 | /* DRIVE_SPECIFICATION_COMMAND */ | |
1584 | FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n"); | |
1585 | /* 5 parameters cmd */ | |
baca51fa | 1586 | fdctrl->data_len = 6; |
8977f3c1 FB |
1587 | goto enqueue; |
1588 | case 0x8F: | |
1589 | /* RELATIVE_SEEK_OUT */ | |
1590 | FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n"); | |
1591 | /* 2 parameters cmd */ | |
baca51fa | 1592 | fdctrl->data_len = 3; |
8977f3c1 FB |
1593 | goto enqueue; |
1594 | case 0x94: | |
1595 | /* LOCK */ | |
1596 | FLOPPY_DPRINTF("LOCK command\n"); | |
1597 | /* No parameters cmd */ | |
baca51fa FB |
1598 | fdctrl->lock = 1; |
1599 | fdctrl->fifo[0] = 0x10; | |
1600 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 FB |
1601 | return; |
1602 | case 0xCD: | |
1603 | /* FORMAT_AND_WRITE */ | |
1604 | FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n"); | |
1605 | /* 10 parameters cmd */ | |
baca51fa | 1606 | fdctrl->data_len = 11; |
8977f3c1 FB |
1607 | goto enqueue; |
1608 | case 0xCF: | |
1609 | /* RELATIVE_SEEK_IN */ | |
1610 | FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n"); | |
1611 | /* 2 parameters cmd */ | |
baca51fa | 1612 | fdctrl->data_len = 3; |
8977f3c1 FB |
1613 | goto enqueue; |
1614 | default: | |
1615 | /* Unknown command */ | |
1616 | FLOPPY_ERROR("unknown command: 0x%02x\n", value); | |
baca51fa | 1617 | fdctrl_unimplemented(fdctrl); |
8977f3c1 FB |
1618 | return; |
1619 | } | |
1620 | } | |
4f431960 | 1621 | enqueue: |
baca51fa FB |
1622 | FLOPPY_DPRINTF("%s: %02x\n", __func__, value); |
1623 | fdctrl->fifo[fdctrl->data_pos] = value; | |
1624 | if (++fdctrl->data_pos == fdctrl->data_len) { | |
8977f3c1 FB |
1625 | /* We now have all parameters |
1626 | * and will be able to treat the command | |
1627 | */ | |
4f431960 JM |
1628 | if (fdctrl->data_state & FD_STATE_FORMAT) { |
1629 | fdctrl_format_sector(fdctrl); | |
8977f3c1 FB |
1630 | return; |
1631 | } | |
4f431960 JM |
1632 | switch (fdctrl->fifo[0] & 0x1F) { |
1633 | case 0x06: | |
1634 | { | |
1635 | /* READ variants */ | |
1636 | FLOPPY_DPRINTF("treat READ command\n"); | |
1637 | fdctrl_start_transfer(fdctrl, FD_DIR_READ); | |
1638 | return; | |
1639 | } | |
8977f3c1 FB |
1640 | case 0x0C: |
1641 | /* READ_DELETED variants */ | |
1642 | // FLOPPY_DPRINTF("treat READ_DELETED command\n"); | |
1643 | FLOPPY_ERROR("treat READ_DELETED command\n"); | |
baca51fa | 1644 | fdctrl_start_transfer_del(fdctrl, FD_DIR_READ); |
8977f3c1 FB |
1645 | return; |
1646 | case 0x16: | |
1647 | /* VERIFY variants */ | |
1648 | // FLOPPY_DPRINTF("treat VERIFY command\n"); | |
1649 | FLOPPY_ERROR("treat VERIFY command\n"); | |
baca51fa | 1650 | fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); |
8977f3c1 FB |
1651 | return; |
1652 | case 0x10: | |
1653 | /* SCAN_EQUAL variants */ | |
1654 | // FLOPPY_DPRINTF("treat SCAN_EQUAL command\n"); | |
1655 | FLOPPY_ERROR("treat SCAN_EQUAL command\n"); | |
baca51fa | 1656 | fdctrl_start_transfer(fdctrl, FD_DIR_SCANE); |
8977f3c1 FB |
1657 | return; |
1658 | case 0x19: | |
1659 | /* SCAN_LOW_OR_EQUAL variants */ | |
1660 | // FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n"); | |
1661 | FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n"); | |
baca51fa | 1662 | fdctrl_start_transfer(fdctrl, FD_DIR_SCANL); |
8977f3c1 FB |
1663 | return; |
1664 | case 0x1D: | |
1665 | /* SCAN_HIGH_OR_EQUAL variants */ | |
1666 | // FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n"); | |
1667 | FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n"); | |
baca51fa | 1668 | fdctrl_start_transfer(fdctrl, FD_DIR_SCANH); |
8977f3c1 FB |
1669 | return; |
1670 | default: | |
1671 | break; | |
1672 | } | |
baca51fa | 1673 | switch (fdctrl->fifo[0] & 0x3F) { |
8977f3c1 FB |
1674 | case 0x05: |
1675 | /* WRITE variants */ | |
baca51fa FB |
1676 | FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]); |
1677 | fdctrl_start_transfer(fdctrl, FD_DIR_WRITE); | |
8977f3c1 FB |
1678 | return; |
1679 | case 0x09: | |
1680 | /* WRITE_DELETED variants */ | |
1681 | // FLOPPY_DPRINTF("treat WRITE_DELETED command\n"); | |
1682 | FLOPPY_ERROR("treat WRITE_DELETED command\n"); | |
baca51fa | 1683 | fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE); |
8977f3c1 FB |
1684 | return; |
1685 | default: | |
1686 | break; | |
1687 | } | |
baca51fa | 1688 | switch (fdctrl->fifo[0]) { |
8977f3c1 FB |
1689 | case 0x03: |
1690 | /* SPECIFY */ | |
1691 | FLOPPY_DPRINTF("treat SPECIFY command\n"); | |
baca51fa | 1692 | fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; |
e309de25 | 1693 | fdctrl->timer1 = fdctrl->fifo[2] >> 1; |
4f431960 | 1694 | fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ; |
8977f3c1 | 1695 | /* No result back */ |
baca51fa | 1696 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 FB |
1697 | break; |
1698 | case 0x04: | |
1699 | /* SENSE_DRIVE_STATUS */ | |
1700 | FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n"); | |
baca51fa | 1701 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
4f431960 | 1702 | cur_drv = get_cur_drv(fdctrl); |
baca51fa | 1703 | cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; |
8977f3c1 | 1704 | /* 1 Byte status back */ |
baca51fa | 1705 | fdctrl->fifo[0] = (cur_drv->ro << 6) | |
8977f3c1 | 1706 | (cur_drv->track == 0 ? 0x10 : 0x00) | |
890fa6be FB |
1707 | (cur_drv->head << 2) | |
1708 | fdctrl->cur_drv | | |
1709 | 0x28; | |
baca51fa | 1710 | fdctrl_set_fifo(fdctrl, 1, 0); |
8977f3c1 FB |
1711 | break; |
1712 | case 0x07: | |
1713 | /* RECALIBRATE */ | |
1714 | FLOPPY_DPRINTF("treat RECALIBRATE command\n"); | |
baca51fa | 1715 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
4f431960 | 1716 | cur_drv = get_cur_drv(fdctrl); |
8977f3c1 | 1717 | fd_recalibrate(cur_drv); |
4f431960 | 1718 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1719 | /* Raise Interrupt */ |
4f431960 | 1720 | fdctrl_raise_irq(fdctrl, 0x20); |
8977f3c1 FB |
1721 | break; |
1722 | case 0x0F: | |
1723 | /* SEEK */ | |
1724 | FLOPPY_DPRINTF("treat SEEK command\n"); | |
baca51fa | 1725 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
4f431960 JM |
1726 | cur_drv = get_cur_drv(fdctrl); |
1727 | fd_start(cur_drv); | |
baca51fa | 1728 | if (fdctrl->fifo[2] <= cur_drv->track) |
8977f3c1 FB |
1729 | cur_drv->dir = 1; |
1730 | else | |
1731 | cur_drv->dir = 0; | |
4f431960 | 1732 | fdctrl_reset_fifo(fdctrl); |
baca51fa FB |
1733 | if (fdctrl->fifo[2] > cur_drv->max_track) { |
1734 | fdctrl_raise_irq(fdctrl, 0x60); | |
8977f3c1 | 1735 | } else { |
baca51fa | 1736 | cur_drv->track = fdctrl->fifo[2]; |
8977f3c1 | 1737 | /* Raise Interrupt */ |
baca51fa | 1738 | fdctrl_raise_irq(fdctrl, 0x20); |
8977f3c1 FB |
1739 | } |
1740 | break; | |
1741 | case 0x12: | |
1742 | /* PERPENDICULAR_MODE */ | |
1743 | FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n"); | |
baca51fa FB |
1744 | if (fdctrl->fifo[1] & 0x80) |
1745 | cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; | |
8977f3c1 | 1746 | /* No result back */ |
baca51fa | 1747 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 FB |
1748 | break; |
1749 | case 0x13: | |
1750 | /* CONFIGURE */ | |
1751 | FLOPPY_DPRINTF("treat CONFIGURE command\n"); | |
baca51fa FB |
1752 | fdctrl->config = fdctrl->fifo[2]; |
1753 | fdctrl->precomp_trk = fdctrl->fifo[3]; | |
8977f3c1 | 1754 | /* No result back */ |
baca51fa | 1755 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 FB |
1756 | break; |
1757 | case 0x17: | |
1758 | /* POWERDOWN_MODE */ | |
1759 | FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n"); | |
baca51fa FB |
1760 | fdctrl->pwrd = fdctrl->fifo[1]; |
1761 | fdctrl->fifo[0] = fdctrl->fifo[1]; | |
1762 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 FB |
1763 | break; |
1764 | case 0x33: | |
1765 | /* OPTION */ | |
1766 | FLOPPY_DPRINTF("treat OPTION command\n"); | |
1767 | /* No result back */ | |
baca51fa | 1768 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 FB |
1769 | break; |
1770 | case 0x42: | |
1771 | /* READ_TRACK */ | |
1772 | // FLOPPY_DPRINTF("treat READ_TRACK command\n"); | |
1773 | FLOPPY_ERROR("treat READ_TRACK command\n"); | |
baca51fa | 1774 | fdctrl_start_transfer(fdctrl, FD_DIR_READ); |
8977f3c1 FB |
1775 | break; |
1776 | case 0x4A: | |
4f431960 | 1777 | /* READ_ID */ |
baca51fa | 1778 | FLOPPY_DPRINTF("treat READ_ID command\n"); |
ed5fd2cc | 1779 | /* XXX: should set main status register to busy */ |
890fa6be | 1780 | cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; |
5fafdf24 | 1781 | qemu_mod_timer(fdctrl->result_timer, |
ed5fd2cc | 1782 | qemu_get_clock(vm_clock) + (ticks_per_sec / 50)); |
8977f3c1 FB |
1783 | break; |
1784 | case 0x4C: | |
1785 | /* RESTORE */ | |
1786 | FLOPPY_DPRINTF("treat RESTORE command\n"); | |
1787 | /* Drives position */ | |
baca51fa FB |
1788 | drv0(fdctrl)->track = fdctrl->fifo[3]; |
1789 | drv1(fdctrl)->track = fdctrl->fifo[4]; | |
8977f3c1 | 1790 | /* timers */ |
baca51fa FB |
1791 | fdctrl->timer0 = fdctrl->fifo[7]; |
1792 | fdctrl->timer1 = fdctrl->fifo[8]; | |
1793 | cur_drv->last_sect = fdctrl->fifo[9]; | |
1794 | fdctrl->lock = fdctrl->fifo[10] >> 7; | |
1795 | cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF; | |
1796 | fdctrl->config = fdctrl->fifo[11]; | |
1797 | fdctrl->precomp_trk = fdctrl->fifo[12]; | |
1798 | fdctrl->pwrd = fdctrl->fifo[13]; | |
1799 | fdctrl_reset_fifo(fdctrl); | |
8977f3c1 FB |
1800 | break; |
1801 | case 0x4D: | |
1802 | /* FORMAT_TRACK */ | |
4f431960 JM |
1803 | FLOPPY_DPRINTF("treat FORMAT_TRACK command\n"); |
1804 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; | |
1805 | cur_drv = get_cur_drv(fdctrl); | |
1806 | fdctrl->data_state |= FD_STATE_FORMAT; | |
1807 | if (fdctrl->fifo[0] & 0x80) | |
1808 | fdctrl->data_state |= FD_STATE_MULTI; | |
1809 | else | |
1810 | fdctrl->data_state &= ~FD_STATE_MULTI; | |
1811 | fdctrl->data_state &= ~FD_STATE_SEEK; | |
1812 | cur_drv->bps = | |
1813 | fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; | |
baca51fa | 1814 | #if 0 |
4f431960 JM |
1815 | cur_drv->last_sect = |
1816 | cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : | |
1817 | fdctrl->fifo[3] / 2; | |
baca51fa | 1818 | #else |
4f431960 | 1819 | cur_drv->last_sect = fdctrl->fifo[3]; |
baca51fa | 1820 | #endif |
4f431960 JM |
1821 | /* TODO: implement format using DMA expected by the Bochs BIOS |
1822 | * and Linux fdformat (read 3 bytes per sector via DMA and fill | |
1823 | * the sector with the specified fill byte | |
1824 | */ | |
1825 | fdctrl->data_state &= ~FD_STATE_FORMAT; | |
1826 | fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); | |
8977f3c1 FB |
1827 | break; |
1828 | case 0x8E: | |
1829 | /* DRIVE_SPECIFICATION_COMMAND */ | |
1830 | FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n"); | |
baca51fa | 1831 | if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { |
8977f3c1 | 1832 | /* Command parameters done */ |
baca51fa FB |
1833 | if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { |
1834 | fdctrl->fifo[0] = fdctrl->fifo[1]; | |
1835 | fdctrl->fifo[2] = 0; | |
1836 | fdctrl->fifo[3] = 0; | |
1837 | fdctrl_set_fifo(fdctrl, 4, 1); | |
8977f3c1 | 1838 | } else { |
baca51fa | 1839 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1840 | } |
baca51fa | 1841 | } else if (fdctrl->data_len > 7) { |
8977f3c1 | 1842 | /* ERROR */ |
baca51fa FB |
1843 | fdctrl->fifo[0] = 0x80 | |
1844 | (cur_drv->head << 2) | fdctrl->cur_drv; | |
1845 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 FB |
1846 | } |
1847 | break; | |
1848 | case 0x8F: | |
1849 | /* RELATIVE_SEEK_OUT */ | |
1850 | FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n"); | |
baca51fa | 1851 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
4f431960 JM |
1852 | cur_drv = get_cur_drv(fdctrl); |
1853 | fd_start(cur_drv); | |
1854 | cur_drv->dir = 0; | |
baca51fa | 1855 | if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { |
4f431960 | 1856 | cur_drv->track = cur_drv->max_track - 1; |
baca51fa FB |
1857 | } else { |
1858 | cur_drv->track += fdctrl->fifo[2]; | |
8977f3c1 | 1859 | } |
4f431960 JM |
1860 | fdctrl_reset_fifo(fdctrl); |
1861 | fdctrl_raise_irq(fdctrl, 0x20); | |
8977f3c1 FB |
1862 | break; |
1863 | case 0xCD: | |
1864 | /* FORMAT_AND_WRITE */ | |
1865 | // FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n"); | |
1866 | FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n"); | |
baca51fa | 1867 | fdctrl_unimplemented(fdctrl); |
8977f3c1 FB |
1868 | break; |
1869 | case 0xCF: | |
4f431960 | 1870 | /* RELATIVE_SEEK_IN */ |
8977f3c1 | 1871 | FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n"); |
baca51fa | 1872 | fdctrl->cur_drv = fdctrl->fifo[1] & 1; |
4f431960 JM |
1873 | cur_drv = get_cur_drv(fdctrl); |
1874 | fd_start(cur_drv); | |
1875 | cur_drv->dir = 1; | |
baca51fa | 1876 | if (fdctrl->fifo[2] > cur_drv->track) { |
4f431960 | 1877 | cur_drv->track = 0; |
baca51fa FB |
1878 | } else { |
1879 | cur_drv->track -= fdctrl->fifo[2]; | |
8977f3c1 | 1880 | } |
4f431960 JM |
1881 | fdctrl_reset_fifo(fdctrl); |
1882 | /* Raise Interrupt */ | |
1883 | fdctrl_raise_irq(fdctrl, 0x20); | |
8977f3c1 FB |
1884 | break; |
1885 | } | |
1886 | } | |
1887 | } | |
ed5fd2cc FB |
1888 | |
1889 | static void fdctrl_result_timer(void *opaque) | |
1890 | { | |
1891 | fdctrl_t *fdctrl = opaque; | |
b7ffa3b1 | 1892 | fdrive_t *cur_drv = get_cur_drv(fdctrl); |
4f431960 | 1893 | |
b7ffa3b1 TS |
1894 | /* Pretend we are spinning. |
1895 | * This is needed for Coherent, which uses READ ID to check for | |
1896 | * sector interleaving. | |
1897 | */ | |
1898 | if (cur_drv->last_sect != 0) { | |
1899 | cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; | |
1900 | } | |
ed5fd2cc FB |
1901 | fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); |
1902 | } |