]>
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 | ||
9fea808a BS |
363 | enum { |
364 | FD_REG_0 = 0x00, | |
365 | FD_REG_STATUSB = 0x01, | |
366 | FD_REG_DOR = 0x02, | |
367 | FD_REG_TDR = 0x03, | |
368 | FD_REG_MSR = 0x04, | |
369 | FD_REG_DSR = 0x04, | |
370 | FD_REG_FIFO = 0x05, | |
371 | FD_REG_DIR = 0x07, | |
372 | }; | |
373 | ||
374 | enum { | |
375 | FD_CMD_SPECIFY = 0x03, | |
376 | FD_CMD_SENSE_DRIVE_STATUS = 0x04, | |
377 | FD_CMD_RECALIBRATE = 0x07, | |
378 | FD_CMD_SENSE_INTERRUPT_STATUS = 0x08, | |
379 | FD_CMD_DUMPREG = 0x0e, | |
380 | FD_CMD_SEEK = 0x0f, | |
381 | FD_CMD_VERSION = 0x10, | |
382 | FD_CMD_PERPENDICULAR_MODE = 0x12, | |
383 | FD_CMD_CONFIGURE = 0x13, | |
384 | FD_CMD_UNLOCK = 0x14, | |
385 | FD_CMD_POWERDOWN_MODE = 0x17, | |
386 | FD_CMD_PART_ID = 0x18, | |
387 | FD_CMD_SAVE = 0x2c, | |
388 | FD_CMD_OPTION = 0x33, | |
389 | FD_CMD_READ_TRACK = 0x42, | |
390 | FD_CMD_WRITE = 0x45, | |
391 | FD_CMD_READ = 0x46, | |
392 | FD_CMD_WRITE_DELETED = 0x49, | |
393 | FD_CMD_READ_ID = 0x4a, | |
394 | FD_CMD_READ_DELETED = 0x4c, | |
395 | FD_CMD_RESTORE = 0x4c, | |
396 | FD_CMD_FORMAT_TRACK = 0x4d, | |
397 | FD_CMD_SCAN_EQUAL = 0x50, | |
398 | FD_CMD_VERIFY = 0x56, | |
399 | FD_CMD_SCAN_LOW_OR_EQUAL = 0x59, | |
400 | FD_CMD_SCAN_HIGH_OR_EQUAL = 0x5d, | |
401 | FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e, | |
402 | FD_CMD_RELATIVE_SEEK_OUT = 0x8f, | |
403 | FD_CMD_LOCK = 0x94, | |
404 | FD_CMD_FORMAT_AND_WRITE = 0xcd, | |
405 | FD_CMD_RELATIVE_SEEK_IN = 0xcf, | |
406 | }; | |
407 | ||
408 | enum { | |
409 | FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */ | |
410 | FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */ | |
411 | FD_CONFIG_POLL = 0x10, /* Poll enabled */ | |
412 | FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */ | |
413 | FD_CONFIG_EIS = 0x40, /* No implied seeks */ | |
414 | }; | |
415 | ||
416 | enum { | |
417 | FD_SR0_EQPMT = 0x10, | |
418 | FD_SR0_SEEK = 0x20, | |
419 | FD_SR0_ABNTERM = 0x40, | |
420 | FD_SR0_INVCMD = 0x80, | |
421 | FD_SR0_RDYCHG = 0xc0, | |
422 | }; | |
423 | ||
424 | enum { | |
425 | FD_DOR_SELMASK = 0x01, | |
426 | FD_DOR_nRESET = 0x04, | |
427 | FD_DOR_DMAEN = 0x08, | |
428 | FD_DOR_MOTEN0 = 0x10, | |
429 | FD_DOR_MOTEN1 = 0x20, | |
430 | FD_DOR_MOTEN2 = 0x40, | |
431 | FD_DOR_MOTEN3 = 0x80, | |
432 | }; | |
433 | ||
434 | enum { | |
435 | FD_TDR_BOOTSEL = 0x0c, | |
436 | }; | |
437 | ||
438 | enum { | |
439 | FD_DSR_DRATEMASK= 0x03, | |
440 | FD_DSR_PWRDOWN = 0x40, | |
441 | FD_DSR_SWRESET = 0x80, | |
442 | }; | |
443 | ||
444 | enum { | |
445 | FD_MSR_DRV0BUSY = 0x01, | |
446 | FD_MSR_DRV1BUSY = 0x02, | |
447 | FD_MSR_DRV2BUSY = 0x04, | |
448 | FD_MSR_DRV3BUSY = 0x08, | |
449 | FD_MSR_CMDBUSY = 0x10, | |
450 | FD_MSR_NONDMA = 0x20, | |
451 | FD_MSR_DIO = 0x40, | |
452 | FD_MSR_RQM = 0x80, | |
453 | }; | |
454 | ||
455 | enum { | |
456 | FD_DIR_DSKCHG = 0x80, | |
457 | }; | |
458 | ||
8977f3c1 | 459 | #define FD_STATE(state) ((state) & FD_STATE_STATE) |
baca51fa FB |
460 | #define FD_SET_STATE(state, new_state) \ |
461 | do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0) | |
8977f3c1 FB |
462 | #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) |
463 | #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK) | |
baca51fa | 464 | #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) |
8977f3c1 | 465 | |
baca51fa FB |
466 | struct fdctrl_t { |
467 | fdctrl_t *fdctrl; | |
4b19ec0c | 468 | /* Controller's identification */ |
8977f3c1 FB |
469 | uint8_t version; |
470 | /* HW */ | |
d537cf6c | 471 | qemu_irq irq; |
8977f3c1 | 472 | int dma_chann; |
5dcb6b91 | 473 | target_phys_addr_t io_base; |
4b19ec0c | 474 | /* Controller state */ |
ed5fd2cc | 475 | QEMUTimer *result_timer; |
8977f3c1 FB |
476 | uint8_t state; |
477 | uint8_t dma_en; | |
478 | uint8_t cur_drv; | |
479 | uint8_t bootsel; | |
480 | /* Command FIFO */ | |
33f00271 | 481 | uint8_t *fifo; |
8977f3c1 FB |
482 | uint32_t data_pos; |
483 | uint32_t data_len; | |
484 | uint8_t data_state; | |
485 | uint8_t data_dir; | |
486 | uint8_t int_status; | |
890fa6be | 487 | uint8_t eot; /* last wanted sector */ |
8977f3c1 FB |
488 | /* States kept only to be returned back */ |
489 | /* Timers state */ | |
490 | uint8_t timer0; | |
491 | uint8_t timer1; | |
492 | /* precompensation */ | |
493 | uint8_t precomp_trk; | |
494 | uint8_t config; | |
495 | uint8_t lock; | |
496 | /* Power down config (also with status regB access mode */ | |
497 | uint8_t pwrd; | |
741402f9 | 498 | /* Sun4m quirks? */ |
a06e5a3c | 499 | int sun4m; |
8977f3c1 FB |
500 | /* Floppy drives */ |
501 | fdrive_t drives[2]; | |
baca51fa FB |
502 | }; |
503 | ||
504 | static uint32_t fdctrl_read (void *opaque, uint32_t reg) | |
505 | { | |
506 | fdctrl_t *fdctrl = opaque; | |
507 | uint32_t retval; | |
508 | ||
a541f297 | 509 | switch (reg & 0x07) { |
9fea808a | 510 | case FD_REG_0: |
a06e5a3c | 511 | if (fdctrl->sun4m) { |
741402f9 BS |
512 | // Identify to Linux as S82078B |
513 | retval = fdctrl_read_statusB(fdctrl); | |
514 | } else { | |
515 | retval = (uint32_t)(-1); | |
516 | } | |
4f431960 | 517 | break; |
9fea808a | 518 | case FD_REG_STATUSB: |
4f431960 JM |
519 | retval = fdctrl_read_statusB(fdctrl); |
520 | break; | |
9fea808a | 521 | case FD_REG_DOR: |
4f431960 JM |
522 | retval = fdctrl_read_dor(fdctrl); |
523 | break; | |
9fea808a | 524 | case FD_REG_TDR: |
baca51fa | 525 | retval = fdctrl_read_tape(fdctrl); |
4f431960 | 526 | break; |
9fea808a | 527 | case FD_REG_MSR: |
baca51fa | 528 | retval = fdctrl_read_main_status(fdctrl); |
4f431960 | 529 | break; |
9fea808a | 530 | case FD_REG_FIFO: |
baca51fa | 531 | retval = fdctrl_read_data(fdctrl); |
4f431960 | 532 | break; |
9fea808a | 533 | case FD_REG_DIR: |
baca51fa | 534 | retval = fdctrl_read_dir(fdctrl); |
4f431960 | 535 | break; |
a541f297 | 536 | default: |
4f431960 JM |
537 | retval = (uint32_t)(-1); |
538 | break; | |
a541f297 | 539 | } |
ed5fd2cc | 540 | FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); |
baca51fa FB |
541 | |
542 | return retval; | |
543 | } | |
544 | ||
545 | static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) | |
546 | { | |
547 | fdctrl_t *fdctrl = opaque; | |
548 | ||
ed5fd2cc FB |
549 | FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); |
550 | ||
a541f297 | 551 | switch (reg & 0x07) { |
9fea808a | 552 | case FD_REG_DOR: |
4f431960 JM |
553 | fdctrl_write_dor(fdctrl, value); |
554 | break; | |
9fea808a | 555 | case FD_REG_TDR: |
baca51fa | 556 | fdctrl_write_tape(fdctrl, value); |
4f431960 | 557 | break; |
9fea808a | 558 | case FD_REG_DSR: |
baca51fa | 559 | fdctrl_write_rate(fdctrl, value); |
4f431960 | 560 | break; |
9fea808a | 561 | case FD_REG_FIFO: |
baca51fa | 562 | fdctrl_write_data(fdctrl, value); |
4f431960 | 563 | break; |
a541f297 | 564 | default: |
4f431960 | 565 | break; |
a541f297 | 566 | } |
baca51fa FB |
567 | } |
568 | ||
62a46c61 FB |
569 | static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg) |
570 | { | |
5dcb6b91 | 571 | return fdctrl_read(opaque, (uint32_t)reg); |
62a46c61 FB |
572 | } |
573 | ||
5fafdf24 | 574 | static void fdctrl_write_mem (void *opaque, |
62a46c61 FB |
575 | target_phys_addr_t reg, uint32_t value) |
576 | { | |
5dcb6b91 | 577 | fdctrl_write(opaque, (uint32_t)reg, value); |
62a46c61 FB |
578 | } |
579 | ||
e80cfcfc | 580 | static CPUReadMemoryFunc *fdctrl_mem_read[3] = { |
62a46c61 FB |
581 | fdctrl_read_mem, |
582 | fdctrl_read_mem, | |
583 | fdctrl_read_mem, | |
e80cfcfc FB |
584 | }; |
585 | ||
586 | static CPUWriteMemoryFunc *fdctrl_mem_write[3] = { | |
62a46c61 FB |
587 | fdctrl_write_mem, |
588 | fdctrl_write_mem, | |
589 | fdctrl_write_mem, | |
e80cfcfc FB |
590 | }; |
591 | ||
7c560456 BS |
592 | static CPUReadMemoryFunc *fdctrl_mem_read_strict[3] = { |
593 | fdctrl_read_mem, | |
594 | NULL, | |
595 | NULL, | |
596 | }; | |
597 | ||
598 | static CPUWriteMemoryFunc *fdctrl_mem_write_strict[3] = { | |
599 | fdctrl_write_mem, | |
600 | NULL, | |
601 | NULL, | |
602 | }; | |
603 | ||
3ccacc4a BS |
604 | static void fd_save (QEMUFile *f, fdrive_t *fd) |
605 | { | |
606 | uint8_t tmp; | |
607 | ||
608 | tmp = fd->drflags; | |
609 | qemu_put_8s(f, &tmp); | |
610 | qemu_put_8s(f, &fd->head); | |
611 | qemu_put_8s(f, &fd->track); | |
612 | qemu_put_8s(f, &fd->sect); | |
613 | qemu_put_8s(f, &fd->dir); | |
614 | qemu_put_8s(f, &fd->rw); | |
615 | } | |
616 | ||
617 | static void fdc_save (QEMUFile *f, void *opaque) | |
618 | { | |
619 | fdctrl_t *s = opaque; | |
620 | ||
621 | qemu_put_8s(f, &s->state); | |
622 | qemu_put_8s(f, &s->dma_en); | |
623 | qemu_put_8s(f, &s->cur_drv); | |
624 | qemu_put_8s(f, &s->bootsel); | |
625 | qemu_put_buffer(f, s->fifo, FD_SECTOR_LEN); | |
626 | qemu_put_be32s(f, &s->data_pos); | |
627 | qemu_put_be32s(f, &s->data_len); | |
628 | qemu_put_8s(f, &s->data_state); | |
629 | qemu_put_8s(f, &s->data_dir); | |
630 | qemu_put_8s(f, &s->int_status); | |
631 | qemu_put_8s(f, &s->eot); | |
632 | qemu_put_8s(f, &s->timer0); | |
633 | qemu_put_8s(f, &s->timer1); | |
634 | qemu_put_8s(f, &s->precomp_trk); | |
635 | qemu_put_8s(f, &s->config); | |
636 | qemu_put_8s(f, &s->lock); | |
637 | qemu_put_8s(f, &s->pwrd); | |
638 | fd_save(f, &s->drives[0]); | |
639 | fd_save(f, &s->drives[1]); | |
640 | } | |
641 | ||
642 | static int fd_load (QEMUFile *f, fdrive_t *fd) | |
643 | { | |
644 | uint8_t tmp; | |
645 | ||
646 | qemu_get_8s(f, &tmp); | |
647 | fd->drflags = tmp; | |
648 | qemu_get_8s(f, &fd->head); | |
649 | qemu_get_8s(f, &fd->track); | |
650 | qemu_get_8s(f, &fd->sect); | |
651 | qemu_get_8s(f, &fd->dir); | |
652 | qemu_get_8s(f, &fd->rw); | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
657 | static int fdc_load (QEMUFile *f, void *opaque, int version_id) | |
658 | { | |
659 | fdctrl_t *s = opaque; | |
660 | int ret; | |
661 | ||
662 | if (version_id != 1) | |
663 | return -EINVAL; | |
664 | ||
665 | qemu_get_8s(f, &s->state); | |
666 | qemu_get_8s(f, &s->dma_en); | |
667 | qemu_get_8s(f, &s->cur_drv); | |
668 | qemu_get_8s(f, &s->bootsel); | |
669 | qemu_get_buffer(f, s->fifo, FD_SECTOR_LEN); | |
670 | qemu_get_be32s(f, &s->data_pos); | |
671 | qemu_get_be32s(f, &s->data_len); | |
672 | qemu_get_8s(f, &s->data_state); | |
673 | qemu_get_8s(f, &s->data_dir); | |
674 | qemu_get_8s(f, &s->int_status); | |
675 | qemu_get_8s(f, &s->eot); | |
676 | qemu_get_8s(f, &s->timer0); | |
677 | qemu_get_8s(f, &s->timer1); | |
678 | qemu_get_8s(f, &s->precomp_trk); | |
679 | qemu_get_8s(f, &s->config); | |
680 | qemu_get_8s(f, &s->lock); | |
681 | qemu_get_8s(f, &s->pwrd); | |
682 | ||
683 | ret = fd_load(f, &s->drives[0]); | |
684 | if (ret == 0) | |
685 | ret = fd_load(f, &s->drives[1]); | |
686 | ||
687 | return ret; | |
688 | } | |
689 | ||
690 | static void fdctrl_external_reset(void *opaque) | |
691 | { | |
692 | fdctrl_t *s = opaque; | |
693 | ||
694 | fdctrl_reset(s, 0); | |
695 | } | |
696 | ||
7c560456 BS |
697 | static fdctrl_t *fdctrl_init_common (qemu_irq irq, int dma_chann, |
698 | target_phys_addr_t io_base, | |
699 | BlockDriverState **fds) | |
8977f3c1 | 700 | { |
baca51fa | 701 | fdctrl_t *fdctrl; |
8977f3c1 FB |
702 | int i; |
703 | ||
4b19ec0c | 704 | FLOPPY_DPRINTF("init controller\n"); |
baca51fa FB |
705 | fdctrl = qemu_mallocz(sizeof(fdctrl_t)); |
706 | if (!fdctrl) | |
707 | return NULL; | |
33f00271 AZ |
708 | fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN); |
709 | if (fdctrl->fifo == NULL) { | |
710 | qemu_free(fdctrl); | |
711 | return NULL; | |
712 | } | |
5fafdf24 | 713 | fdctrl->result_timer = qemu_new_timer(vm_clock, |
ed5fd2cc FB |
714 | fdctrl_result_timer, fdctrl); |
715 | ||
4b19ec0c | 716 | fdctrl->version = 0x90; /* Intel 82078 controller */ |
d537cf6c | 717 | fdctrl->irq = irq; |
baca51fa FB |
718 | fdctrl->dma_chann = dma_chann; |
719 | fdctrl->io_base = io_base; | |
9fea808a | 720 | fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */ |
baca51fa FB |
721 | if (fdctrl->dma_chann != -1) { |
722 | fdctrl->dma_en = 1; | |
723 | DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl); | |
8977f3c1 | 724 | } else { |
baca51fa | 725 | fdctrl->dma_en = 0; |
8977f3c1 | 726 | } |
9fea808a | 727 | for (i = 0; i < MAX_FD; i++) { |
baca51fa | 728 | fd_init(&fdctrl->drives[i], fds[i]); |
caed8802 | 729 | } |
baca51fa FB |
730 | fdctrl_reset(fdctrl, 0); |
731 | fdctrl->state = FD_CTRL_ACTIVE; | |
7c560456 BS |
732 | register_savevm("fdc", io_base, 1, fdc_save, fdc_load, fdctrl); |
733 | qemu_register_reset(fdctrl_external_reset, fdctrl); | |
9fea808a | 734 | for (i = 0; i < MAX_FD; i++) { |
7c560456 BS |
735 | fd_revalidate(&fdctrl->drives[i]); |
736 | } | |
737 | ||
738 | return fdctrl; | |
739 | } | |
740 | ||
741 | fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped, | |
742 | target_phys_addr_t io_base, | |
743 | BlockDriverState **fds) | |
744 | { | |
745 | fdctrl_t *fdctrl; | |
746 | int io_mem; | |
747 | ||
748 | fdctrl = fdctrl_init_common(irq, dma_chann, io_base, fds); | |
749 | ||
750 | fdctrl->sun4m = 0; | |
8977f3c1 | 751 | if (mem_mapped) { |
4f431960 JM |
752 | io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, |
753 | fdctrl); | |
e80cfcfc | 754 | cpu_register_physical_memory(io_base, 0x08, io_mem); |
8977f3c1 | 755 | } else { |
5dcb6b91 BS |
756 | register_ioport_read((uint32_t)io_base + 0x01, 5, 1, &fdctrl_read, |
757 | fdctrl); | |
758 | register_ioport_read((uint32_t)io_base + 0x07, 1, 1, &fdctrl_read, | |
759 | fdctrl); | |
760 | register_ioport_write((uint32_t)io_base + 0x01, 5, 1, &fdctrl_write, | |
761 | fdctrl); | |
762 | register_ioport_write((uint32_t)io_base + 0x07, 1, 1, &fdctrl_write, | |
763 | fdctrl); | |
8977f3c1 | 764 | } |
a541f297 | 765 | |
baca51fa | 766 | return fdctrl; |
caed8802 | 767 | } |
8977f3c1 | 768 | |
2be17ebd BS |
769 | static void fdctrl_handle_tc(void *opaque, int irq, int level) |
770 | { | |
771 | //fdctrl_t *s = opaque; | |
772 | ||
773 | if (level) { | |
774 | // XXX | |
775 | FLOPPY_DPRINTF("TC pulsed\n"); | |
776 | } | |
777 | } | |
778 | ||
741402f9 | 779 | fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base, |
2be17ebd | 780 | BlockDriverState **fds, qemu_irq *fdc_tc) |
741402f9 BS |
781 | { |
782 | fdctrl_t *fdctrl; | |
7c560456 | 783 | int io_mem; |
741402f9 | 784 | |
7c560456 | 785 | fdctrl = fdctrl_init_common(irq, 0, io_base, fds); |
a06e5a3c | 786 | fdctrl->sun4m = 1; |
7c560456 BS |
787 | io_mem = cpu_register_io_memory(0, fdctrl_mem_read_strict, |
788 | fdctrl_mem_write_strict, | |
789 | fdctrl); | |
790 | cpu_register_physical_memory(io_base, 0x08, io_mem); | |
2be17ebd | 791 | *fdc_tc = *qemu_allocate_irqs(fdctrl_handle_tc, fdctrl, 1); |
741402f9 BS |
792 | |
793 | return fdctrl; | |
794 | } | |
795 | ||
baca51fa FB |
796 | /* XXX: may change if moved to bdrv */ |
797 | int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) | |
caed8802 | 798 | { |
baca51fa | 799 | return fdctrl->drives[drive_num].drive; |
8977f3c1 FB |
800 | } |
801 | ||
802 | /* Change IRQ state */ | |
baca51fa | 803 | static void fdctrl_reset_irq (fdctrl_t *fdctrl) |
8977f3c1 | 804 | { |
ed5fd2cc | 805 | FLOPPY_DPRINTF("Reset interrupt\n"); |
d537cf6c | 806 | qemu_set_irq(fdctrl->irq, 0); |
ed5fd2cc | 807 | fdctrl->state &= ~FD_CTRL_INTR; |
8977f3c1 FB |
808 | } |
809 | ||
baca51fa | 810 | static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) |
8977f3c1 | 811 | { |
6f7e9aec | 812 | // Sparc mutation |
a06e5a3c | 813 | if (fdctrl->sun4m && !fdctrl->dma_en) { |
4f431960 JM |
814 | fdctrl->state &= ~FD_CTRL_BUSY; |
815 | fdctrl->int_status = status; | |
816 | return; | |
6f7e9aec | 817 | } |
baca51fa | 818 | if (~(fdctrl->state & FD_CTRL_INTR)) { |
d537cf6c | 819 | qemu_set_irq(fdctrl->irq, 1); |
baca51fa | 820 | fdctrl->state |= FD_CTRL_INTR; |
8977f3c1 FB |
821 | } |
822 | FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); | |
baca51fa | 823 | fdctrl->int_status = status; |
8977f3c1 FB |
824 | } |
825 | ||
4b19ec0c | 826 | /* Reset controller */ |
baca51fa | 827 | static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) |
8977f3c1 FB |
828 | { |
829 | int i; | |
830 | ||
4b19ec0c | 831 | FLOPPY_DPRINTF("reset controller\n"); |
baca51fa | 832 | fdctrl_reset_irq(fdctrl); |
4b19ec0c | 833 | /* Initialise controller */ |
baca51fa | 834 | fdctrl->cur_drv = 0; |
8977f3c1 | 835 | /* FIFO state */ |
baca51fa FB |
836 | fdctrl->data_pos = 0; |
837 | fdctrl->data_len = 0; | |
838 | fdctrl->data_state = FD_STATE_CMD; | |
839 | fdctrl->data_dir = FD_DIR_WRITE; | |
8977f3c1 | 840 | for (i = 0; i < MAX_FD; i++) |
baca51fa FB |
841 | fd_reset(&fdctrl->drives[i]); |
842 | fdctrl_reset_fifo(fdctrl); | |
8977f3c1 | 843 | if (do_irq) |
9fea808a | 844 | fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG); |
baca51fa FB |
845 | } |
846 | ||
847 | static inline fdrive_t *drv0 (fdctrl_t *fdctrl) | |
848 | { | |
849 | return &fdctrl->drives[fdctrl->bootsel]; | |
850 | } | |
851 | ||
852 | static inline fdrive_t *drv1 (fdctrl_t *fdctrl) | |
853 | { | |
854 | return &fdctrl->drives[1 - fdctrl->bootsel]; | |
855 | } | |
856 | ||
857 | static fdrive_t *get_cur_drv (fdctrl_t *fdctrl) | |
858 | { | |
859 | return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl); | |
8977f3c1 FB |
860 | } |
861 | ||
862 | /* Status B register : 0x01 (read-only) */ | |
baca51fa | 863 | static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl) |
8977f3c1 | 864 | { |
8977f3c1 | 865 | FLOPPY_DPRINTF("status register: 0x00\n"); |
8977f3c1 FB |
866 | return 0; |
867 | } | |
868 | ||
869 | /* Digital output register : 0x02 */ | |
baca51fa | 870 | static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl) |
8977f3c1 | 871 | { |
8977f3c1 FB |
872 | uint32_t retval = 0; |
873 | ||
8977f3c1 | 874 | /* Drive motors state indicators */ |
baca51fa | 875 | if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON) |
9fea808a | 876 | retval |= FD_DOR_MOTEN0; |
baca51fa | 877 | if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON) |
9fea808a | 878 | retval |= FD_DOR_MOTEN1; |
8977f3c1 | 879 | /* DMA enable */ |
9fea808a BS |
880 | if (fdctrl->dma_en) |
881 | retval |= FD_DOR_DMAEN; | |
8977f3c1 | 882 | /* Reset indicator */ |
9fea808a BS |
883 | if (!(fdctrl->state & FD_CTRL_RESET)) |
884 | retval |= FD_DOR_nRESET; | |
8977f3c1 | 885 | /* Selected drive */ |
baca51fa | 886 | retval |= fdctrl->cur_drv; |
8977f3c1 FB |
887 | FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval); |
888 | ||
889 | return retval; | |
890 | } | |
891 | ||
baca51fa | 892 | static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) |
8977f3c1 | 893 | { |
8977f3c1 | 894 | /* Reset mode */ |
baca51fa | 895 | if (fdctrl->state & FD_CTRL_RESET) { |
9fea808a | 896 | if (!(value & FD_DOR_nRESET)) { |
4b19ec0c | 897 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
8977f3c1 FB |
898 | return; |
899 | } | |
900 | } | |
901 | FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); | |
902 | /* Drive motors state indicators */ | |
9fea808a | 903 | if (value & FD_DOR_MOTEN1) |
baca51fa | 904 | fd_start(drv1(fdctrl)); |
8977f3c1 | 905 | else |
baca51fa | 906 | fd_stop(drv1(fdctrl)); |
9fea808a | 907 | if (value & FD_DOR_MOTEN0) |
baca51fa | 908 | fd_start(drv0(fdctrl)); |
8977f3c1 | 909 | else |
baca51fa | 910 | fd_stop(drv0(fdctrl)); |
8977f3c1 FB |
911 | /* DMA enable */ |
912 | #if 0 | |
baca51fa | 913 | if (fdctrl->dma_chann != -1) |
9fea808a | 914 | fdctrl->dma_en = value & FD_DOR_DMAEN ? 1 : 0; |
8977f3c1 FB |
915 | #endif |
916 | /* Reset */ | |
9fea808a | 917 | if (!(value & FD_DOR_nRESET)) { |
baca51fa | 918 | if (!(fdctrl->state & FD_CTRL_RESET)) { |
4b19ec0c | 919 | FLOPPY_DPRINTF("controller enter RESET state\n"); |
baca51fa | 920 | fdctrl->state |= FD_CTRL_RESET; |
8977f3c1 FB |
921 | } |
922 | } else { | |
baca51fa | 923 | if (fdctrl->state & FD_CTRL_RESET) { |
4b19ec0c | 924 | FLOPPY_DPRINTF("controller out of RESET state\n"); |
fb6cf1d0 | 925 | fdctrl_reset(fdctrl, 1); |
baca51fa | 926 | fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP); |
8977f3c1 FB |
927 | } |
928 | } | |
929 | /* Selected drive */ | |
9fea808a | 930 | fdctrl->cur_drv = value & FD_DOR_SELMASK; |
8977f3c1 FB |
931 | } |
932 | ||
933 | /* Tape drive register : 0x03 */ | |
baca51fa | 934 | static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) |
8977f3c1 FB |
935 | { |
936 | uint32_t retval = 0; | |
937 | ||
8977f3c1 | 938 | /* Disk boot selection indicator */ |
baca51fa | 939 | retval |= fdctrl->bootsel << 2; |
8977f3c1 FB |
940 | /* Tape indicators: never allowed */ |
941 | FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval); | |
942 | ||
943 | return retval; | |
944 | } | |
945 | ||
baca51fa | 946 | static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value) |
8977f3c1 | 947 | { |
8977f3c1 | 948 | /* Reset mode */ |
baca51fa | 949 | if (fdctrl->state & FD_CTRL_RESET) { |
4b19ec0c | 950 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
8977f3c1 FB |
951 | return; |
952 | } | |
953 | FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); | |
954 | /* Disk boot selection indicator */ | |
9fea808a | 955 | fdctrl->bootsel = (value & FD_TDR_BOOTSEL) >> 2; |
8977f3c1 FB |
956 | /* Tape indicators: never allow */ |
957 | } | |
958 | ||
959 | /* Main status register : 0x04 (read) */ | |
baca51fa | 960 | static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) |
8977f3c1 FB |
961 | { |
962 | uint32_t retval = 0; | |
963 | ||
baca51fa FB |
964 | fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET); |
965 | if (!(fdctrl->state & FD_CTRL_BUSY)) { | |
8977f3c1 | 966 | /* Data transfer allowed */ |
9fea808a | 967 | retval |= FD_MSR_RQM; |
8977f3c1 | 968 | /* Data transfer direction indicator */ |
baca51fa | 969 | if (fdctrl->data_dir == FD_DIR_READ) |
9fea808a | 970 | retval |= FD_MSR_DIO; |
8977f3c1 | 971 | } |
9fea808a | 972 | /* Should handle FD_MSR_NONDMA for SPECIFY command */ |
8977f3c1 | 973 | /* Command busy indicator */ |
baca51fa FB |
974 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA || |
975 | FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) | |
9fea808a | 976 | retval |= FD_MSR_CMDBUSY; |
8977f3c1 FB |
977 | FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); |
978 | ||
979 | return retval; | |
980 | } | |
981 | ||
982 | /* Data select rate register : 0x04 (write) */ | |
baca51fa | 983 | static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) |
8977f3c1 | 984 | { |
8977f3c1 | 985 | /* Reset mode */ |
baca51fa | 986 | if (fdctrl->state & FD_CTRL_RESET) { |
4f431960 JM |
987 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
988 | return; | |
989 | } | |
8977f3c1 FB |
990 | FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); |
991 | /* Reset: autoclear */ | |
9fea808a | 992 | if (value & FD_DSR_SWRESET) { |
baca51fa FB |
993 | fdctrl->state |= FD_CTRL_RESET; |
994 | fdctrl_reset(fdctrl, 1); | |
995 | fdctrl->state &= ~FD_CTRL_RESET; | |
8977f3c1 | 996 | } |
9fea808a | 997 | if (value & FD_DSR_PWRDOWN) { |
baca51fa FB |
998 | fdctrl->state |= FD_CTRL_SLEEP; |
999 | fdctrl_reset(fdctrl, 1); | |
8977f3c1 | 1000 | } |
8977f3c1 FB |
1001 | } |
1002 | ||
ea185bbd FB |
1003 | static int fdctrl_media_changed(fdrive_t *drv) |
1004 | { | |
1005 | int ret; | |
4f431960 | 1006 | |
5fafdf24 | 1007 | if (!drv->bs) |
ea185bbd FB |
1008 | return 0; |
1009 | ret = bdrv_media_changed(drv->bs); | |
1010 | if (ret) { | |
1011 | fd_revalidate(drv); | |
1012 | } | |
1013 | return ret; | |
1014 | } | |
1015 | ||
8977f3c1 | 1016 | /* Digital input register : 0x07 (read-only) */ |
baca51fa | 1017 | static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) |
8977f3c1 | 1018 | { |
8977f3c1 FB |
1019 | uint32_t retval = 0; |
1020 | ||
ea185bbd | 1021 | if (fdctrl_media_changed(drv0(fdctrl)) || |
4f431960 | 1022 | fdctrl_media_changed(drv1(fdctrl))) |
9fea808a | 1023 | retval |= FD_DIR_DSKCHG; |
8977f3c1 | 1024 | if (retval != 0) |
baca51fa | 1025 | FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); |
8977f3c1 FB |
1026 | |
1027 | return retval; | |
1028 | } | |
1029 | ||
1030 | /* FIFO state control */ | |
baca51fa | 1031 | static void fdctrl_reset_fifo (fdctrl_t *fdctrl) |
8977f3c1 | 1032 | { |
baca51fa FB |
1033 | fdctrl->data_dir = FD_DIR_WRITE; |
1034 | fdctrl->data_pos = 0; | |
1035 | FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD); | |
8977f3c1 FB |
1036 | } |
1037 | ||
1038 | /* Set FIFO status for the host to read */ | |
baca51fa | 1039 | static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq) |
8977f3c1 | 1040 | { |
baca51fa FB |
1041 | fdctrl->data_dir = FD_DIR_READ; |
1042 | fdctrl->data_len = fifo_len; | |
1043 | fdctrl->data_pos = 0; | |
1044 | FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS); | |
8977f3c1 | 1045 | if (do_irq) |
baca51fa | 1046 | fdctrl_raise_irq(fdctrl, 0x00); |
8977f3c1 FB |
1047 | } |
1048 | ||
1049 | /* Set an error: unimplemented/unknown command */ | |
baca51fa | 1050 | static void fdctrl_unimplemented (fdctrl_t *fdctrl) |
8977f3c1 FB |
1051 | { |
1052 | #if 0 | |
baca51fa FB |
1053 | fdrive_t *cur_drv; |
1054 | ||
1055 | cur_drv = get_cur_drv(fdctrl); | |
9fea808a | 1056 | fdctrl->fifo[0] = FD_SR0_ABNTERM | FD_SR0_SEEK | (cur_drv->head << 2) | fdctrl->cur_drv; |
baca51fa FB |
1057 | fdctrl->fifo[1] = 0x00; |
1058 | fdctrl->fifo[2] = 0x00; | |
1059 | fdctrl_set_fifo(fdctrl, 3, 1); | |
8977f3c1 | 1060 | #else |
baca51fa | 1061 | // fdctrl_reset_fifo(fdctrl); |
9fea808a | 1062 | fdctrl->fifo[0] = FD_SR0_INVCMD; |
baca51fa | 1063 | fdctrl_set_fifo(fdctrl, 1, 0); |
8977f3c1 FB |
1064 | #endif |
1065 | } | |
1066 | ||
1067 | /* Callback for transfer end (stop or abort) */ | |
baca51fa | 1068 | static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, |
4f431960 | 1069 | uint8_t status1, uint8_t status2) |
8977f3c1 | 1070 | { |
baca51fa | 1071 | fdrive_t *cur_drv; |
8977f3c1 | 1072 | |
baca51fa | 1073 | cur_drv = get_cur_drv(fdctrl); |
8977f3c1 FB |
1074 | FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", |
1075 | status0, status1, status2, | |
890fa6be FB |
1076 | status0 | (cur_drv->head << 2) | fdctrl->cur_drv); |
1077 | fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv; | |
baca51fa FB |
1078 | fdctrl->fifo[1] = status1; |
1079 | fdctrl->fifo[2] = status2; | |
1080 | fdctrl->fifo[3] = cur_drv->track; | |
1081 | fdctrl->fifo[4] = cur_drv->head; | |
1082 | fdctrl->fifo[5] = cur_drv->sect; | |
1083 | fdctrl->fifo[6] = FD_SECTOR_SC; | |
1084 | fdctrl->data_dir = FD_DIR_READ; | |
ed5fd2cc | 1085 | if (fdctrl->state & FD_CTRL_BUSY) { |
baca51fa | 1086 | DMA_release_DREQ(fdctrl->dma_chann); |
ed5fd2cc FB |
1087 | fdctrl->state &= ~FD_CTRL_BUSY; |
1088 | } | |
baca51fa | 1089 | fdctrl_set_fifo(fdctrl, 7, 1); |
8977f3c1 FB |
1090 | } |
1091 | ||
1092 | /* Prepare a data transfer (either DMA or FIFO) */ | |
baca51fa | 1093 | static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) |
8977f3c1 | 1094 | { |
baca51fa | 1095 | fdrive_t *cur_drv; |
8977f3c1 FB |
1096 | uint8_t kh, kt, ks; |
1097 | int did_seek; | |
1098 | ||
9fea808a | 1099 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
baca51fa FB |
1100 | cur_drv = get_cur_drv(fdctrl); |
1101 | kt = fdctrl->fifo[2]; | |
1102 | kh = fdctrl->fifo[3]; | |
1103 | ks = fdctrl->fifo[4]; | |
4b19ec0c | 1104 | FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", |
baca51fa | 1105 | fdctrl->cur_drv, kh, kt, ks, |
8977f3c1 FB |
1106 | _fd_sector(kh, kt, ks, cur_drv->last_sect)); |
1107 | did_seek = 0; | |
baca51fa | 1108 | switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { |
8977f3c1 FB |
1109 | case 2: |
1110 | /* sect too big */ | |
9fea808a | 1111 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); |
baca51fa FB |
1112 | fdctrl->fifo[3] = kt; |
1113 | fdctrl->fifo[4] = kh; | |
1114 | fdctrl->fifo[5] = ks; | |
8977f3c1 FB |
1115 | return; |
1116 | case 3: | |
1117 | /* track too big */ | |
9fea808a | 1118 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x80, 0x00); |
baca51fa FB |
1119 | fdctrl->fifo[3] = kt; |
1120 | fdctrl->fifo[4] = kh; | |
1121 | fdctrl->fifo[5] = ks; | |
8977f3c1 FB |
1122 | return; |
1123 | case 4: | |
1124 | /* No seek enabled */ | |
9fea808a | 1125 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); |
baca51fa FB |
1126 | fdctrl->fifo[3] = kt; |
1127 | fdctrl->fifo[4] = kh; | |
1128 | fdctrl->fifo[5] = ks; | |
8977f3c1 FB |
1129 | return; |
1130 | case 1: | |
1131 | did_seek = 1; | |
1132 | break; | |
1133 | default: | |
1134 | break; | |
1135 | } | |
1136 | /* Set the FIFO state */ | |
baca51fa FB |
1137 | fdctrl->data_dir = direction; |
1138 | fdctrl->data_pos = 0; | |
1139 | FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */ | |
1140 | if (fdctrl->fifo[0] & 0x80) | |
1141 | fdctrl->data_state |= FD_STATE_MULTI; | |
1142 | else | |
1143 | fdctrl->data_state &= ~FD_STATE_MULTI; | |
8977f3c1 | 1144 | if (did_seek) |
baca51fa FB |
1145 | fdctrl->data_state |= FD_STATE_SEEK; |
1146 | else | |
1147 | fdctrl->data_state &= ~FD_STATE_SEEK; | |
1148 | if (fdctrl->fifo[5] == 00) { | |
1149 | fdctrl->data_len = fdctrl->fifo[8]; | |
1150 | } else { | |
4f431960 | 1151 | int tmp; |
3bcb80f1 | 1152 | fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); |
baca51fa FB |
1153 | tmp = (cur_drv->last_sect - ks + 1); |
1154 | if (fdctrl->fifo[0] & 0x80) | |
1155 | tmp += cur_drv->last_sect; | |
4f431960 | 1156 | fdctrl->data_len *= tmp; |
baca51fa | 1157 | } |
890fa6be | 1158 | fdctrl->eot = fdctrl->fifo[6]; |
baca51fa | 1159 | if (fdctrl->dma_en) { |
8977f3c1 FB |
1160 | int dma_mode; |
1161 | /* DMA transfer are enabled. Check if DMA channel is well programmed */ | |
baca51fa | 1162 | dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); |
8977f3c1 | 1163 | dma_mode = (dma_mode >> 2) & 3; |
baca51fa | 1164 | FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", |
4f431960 | 1165 | dma_mode, direction, |
baca51fa | 1166 | (128 << fdctrl->fifo[5]) * |
4f431960 | 1167 | (cur_drv->last_sect - ks + 1), fdctrl->data_len); |
8977f3c1 FB |
1168 | if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || |
1169 | direction == FD_DIR_SCANH) && dma_mode == 0) || | |
1170 | (direction == FD_DIR_WRITE && dma_mode == 2) || | |
1171 | (direction == FD_DIR_READ && dma_mode == 1)) { | |
1172 | /* No access is allowed until DMA transfer has completed */ | |
baca51fa | 1173 | fdctrl->state |= FD_CTRL_BUSY; |
4b19ec0c | 1174 | /* Now, we just have to wait for the DMA controller to |
8977f3c1 FB |
1175 | * recall us... |
1176 | */ | |
baca51fa FB |
1177 | DMA_hold_DREQ(fdctrl->dma_chann); |
1178 | DMA_schedule(fdctrl->dma_chann); | |
8977f3c1 | 1179 | return; |
baca51fa | 1180 | } else { |
4f431960 | 1181 | FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction); |
8977f3c1 FB |
1182 | } |
1183 | } | |
1184 | FLOPPY_DPRINTF("start non-DMA transfer\n"); | |
1185 | /* IO based transfer: calculate len */ | |
baca51fa | 1186 | fdctrl_raise_irq(fdctrl, 0x00); |
8977f3c1 FB |
1187 | |
1188 | return; | |
1189 | } | |
1190 | ||
1191 | /* Prepare a transfer of deleted data */ | |
baca51fa | 1192 | static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) |
8977f3c1 FB |
1193 | { |
1194 | /* We don't handle deleted data, | |
1195 | * so we don't return *ANYTHING* | |
1196 | */ | |
9fea808a | 1197 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); |
8977f3c1 FB |
1198 | } |
1199 | ||
1200 | /* handlers for DMA transfers */ | |
85571bc7 FB |
1201 | static int fdctrl_transfer_handler (void *opaque, int nchan, |
1202 | int dma_pos, int dma_len) | |
8977f3c1 | 1203 | { |
baca51fa FB |
1204 | fdctrl_t *fdctrl; |
1205 | fdrive_t *cur_drv; | |
1206 | int len, start_pos, rel_pos; | |
8977f3c1 FB |
1207 | uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; |
1208 | ||
baca51fa | 1209 | fdctrl = opaque; |
baca51fa | 1210 | if (!(fdctrl->state & FD_CTRL_BUSY)) { |
8977f3c1 FB |
1211 | FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); |
1212 | return 0; | |
1213 | } | |
baca51fa FB |
1214 | cur_drv = get_cur_drv(fdctrl); |
1215 | if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || | |
1216 | fdctrl->data_dir == FD_DIR_SCANH) | |
8977f3c1 | 1217 | status2 = 0x04; |
85571bc7 FB |
1218 | if (dma_len > fdctrl->data_len) |
1219 | dma_len = fdctrl->data_len; | |
890fa6be | 1220 | if (cur_drv->bs == NULL) { |
4f431960 | 1221 | if (fdctrl->data_dir == FD_DIR_WRITE) |
9fea808a | 1222 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); |
4f431960 | 1223 | else |
9fea808a | 1224 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); |
4f431960 | 1225 | len = 0; |
890fa6be FB |
1226 | goto transfer_error; |
1227 | } | |
baca51fa | 1228 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; |
85571bc7 FB |
1229 | for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { |
1230 | len = dma_len - fdctrl->data_pos; | |
baca51fa FB |
1231 | if (len + rel_pos > FD_SECTOR_LEN) |
1232 | len = FD_SECTOR_LEN - rel_pos; | |
6f7e9aec FB |
1233 | FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " |
1234 | "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, | |
baca51fa FB |
1235 | fdctrl->data_len, fdctrl->cur_drv, cur_drv->head, |
1236 | cur_drv->track, cur_drv->sect, fd_sector(cur_drv), | |
9fea808a | 1237 | fd_sector(cur_drv) * FD_SECTOR_LEN); |
baca51fa | 1238 | if (fdctrl->data_dir != FD_DIR_WRITE || |
4f431960 | 1239 | len < FD_SECTOR_LEN || rel_pos != 0) { |
baca51fa FB |
1240 | /* READ & SCAN commands and realign to a sector for WRITE */ |
1241 | if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), | |
4f431960 | 1242 | fdctrl->fifo, 1) < 0) { |
8977f3c1 FB |
1243 | FLOPPY_DPRINTF("Floppy: error getting sector %d\n", |
1244 | fd_sector(cur_drv)); | |
1245 | /* Sure, image size is too small... */ | |
baca51fa | 1246 | memset(fdctrl->fifo, 0, FD_SECTOR_LEN); |
8977f3c1 | 1247 | } |
890fa6be | 1248 | } |
4f431960 JM |
1249 | switch (fdctrl->data_dir) { |
1250 | case FD_DIR_READ: | |
1251 | /* READ commands */ | |
85571bc7 FB |
1252 | DMA_write_memory (nchan, fdctrl->fifo + rel_pos, |
1253 | fdctrl->data_pos, len); | |
4f431960 JM |
1254 | break; |
1255 | case FD_DIR_WRITE: | |
baca51fa | 1256 | /* WRITE commands */ |
85571bc7 FB |
1257 | DMA_read_memory (nchan, fdctrl->fifo + rel_pos, |
1258 | fdctrl->data_pos, len); | |
baca51fa | 1259 | if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), |
4f431960 | 1260 | fdctrl->fifo, 1) < 0) { |
baca51fa | 1261 | FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); |
9fea808a | 1262 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); |
baca51fa | 1263 | goto transfer_error; |
890fa6be | 1264 | } |
4f431960 JM |
1265 | break; |
1266 | default: | |
1267 | /* SCAN commands */ | |
baca51fa | 1268 | { |
4f431960 | 1269 | uint8_t tmpbuf[FD_SECTOR_LEN]; |
baca51fa | 1270 | int ret; |
85571bc7 | 1271 | DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); |
baca51fa | 1272 | ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); |
8977f3c1 FB |
1273 | if (ret == 0) { |
1274 | status2 = 0x08; | |
1275 | goto end_transfer; | |
1276 | } | |
baca51fa FB |
1277 | if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || |
1278 | (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { | |
8977f3c1 FB |
1279 | status2 = 0x00; |
1280 | goto end_transfer; | |
1281 | } | |
1282 | } | |
4f431960 | 1283 | break; |
8977f3c1 | 1284 | } |
4f431960 JM |
1285 | fdctrl->data_pos += len; |
1286 | rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; | |
baca51fa | 1287 | if (rel_pos == 0) { |
8977f3c1 | 1288 | /* Seek to next sector */ |
4f431960 JM |
1289 | FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n", |
1290 | cur_drv->head, cur_drv->track, cur_drv->sect, | |
1291 | fd_sector(cur_drv), | |
1292 | fdctrl->data_pos - len); | |
890fa6be FB |
1293 | /* XXX: cur_drv->sect >= cur_drv->last_sect should be an |
1294 | error in fact */ | |
1295 | if (cur_drv->sect >= cur_drv->last_sect || | |
1296 | cur_drv->sect == fdctrl->eot) { | |
4f431960 JM |
1297 | cur_drv->sect = 1; |
1298 | if (FD_MULTI_TRACK(fdctrl->data_state)) { | |
1299 | if (cur_drv->head == 0 && | |
1300 | (cur_drv->flags & FDISK_DBL_SIDES) != 0) { | |
890fa6be FB |
1301 | cur_drv->head = 1; |
1302 | } else { | |
1303 | cur_drv->head = 0; | |
4f431960 JM |
1304 | cur_drv->track++; |
1305 | if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) | |
1306 | break; | |
890fa6be FB |
1307 | } |
1308 | } else { | |
1309 | cur_drv->track++; | |
1310 | break; | |
8977f3c1 | 1311 | } |
4f431960 JM |
1312 | FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", |
1313 | cur_drv->head, cur_drv->track, | |
1314 | cur_drv->sect, fd_sector(cur_drv)); | |
890fa6be FB |
1315 | } else { |
1316 | cur_drv->sect++; | |
8977f3c1 FB |
1317 | } |
1318 | } | |
1319 | } | |
4f431960 | 1320 | end_transfer: |
baca51fa FB |
1321 | len = fdctrl->data_pos - start_pos; |
1322 | FLOPPY_DPRINTF("end transfer %d %d %d\n", | |
4f431960 | 1323 | fdctrl->data_pos, len, fdctrl->data_len); |
baca51fa FB |
1324 | if (fdctrl->data_dir == FD_DIR_SCANE || |
1325 | fdctrl->data_dir == FD_DIR_SCANL || | |
1326 | fdctrl->data_dir == FD_DIR_SCANH) | |
8977f3c1 | 1327 | status2 = 0x08; |
baca51fa | 1328 | if (FD_DID_SEEK(fdctrl->data_state)) |
9fea808a | 1329 | status0 |= FD_SR0_SEEK; |
baca51fa FB |
1330 | fdctrl->data_len -= len; |
1331 | // if (fdctrl->data_len == 0) | |
890fa6be | 1332 | fdctrl_stop_transfer(fdctrl, status0, status1, status2); |
4f431960 | 1333 | transfer_error: |
8977f3c1 | 1334 | |
baca51fa | 1335 | return len; |
8977f3c1 FB |
1336 | } |
1337 | ||
8977f3c1 | 1338 | /* Data register : 0x05 */ |
baca51fa | 1339 | static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) |
8977f3c1 | 1340 | { |
baca51fa | 1341 | fdrive_t *cur_drv; |
8977f3c1 FB |
1342 | uint32_t retval = 0; |
1343 | int pos, len; | |
1344 | ||
baca51fa FB |
1345 | cur_drv = get_cur_drv(fdctrl); |
1346 | fdctrl->state &= ~FD_CTRL_SLEEP; | |
1347 | if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) { | |
8977f3c1 FB |
1348 | FLOPPY_ERROR("can't read data in CMD state\n"); |
1349 | return 0; | |
1350 | } | |
baca51fa FB |
1351 | pos = fdctrl->data_pos; |
1352 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { | |
8977f3c1 FB |
1353 | pos %= FD_SECTOR_LEN; |
1354 | if (pos == 0) { | |
baca51fa | 1355 | len = fdctrl->data_len - fdctrl->data_pos; |
8977f3c1 FB |
1356 | if (len > FD_SECTOR_LEN) |
1357 | len = FD_SECTOR_LEN; | |
d6c1a327 | 1358 | bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1); |
8977f3c1 FB |
1359 | } |
1360 | } | |
baca51fa FB |
1361 | retval = fdctrl->fifo[pos]; |
1362 | if (++fdctrl->data_pos == fdctrl->data_len) { | |
1363 | fdctrl->data_pos = 0; | |
890fa6be | 1364 | /* Switch from transfer mode to status mode |
8977f3c1 FB |
1365 | * then from status mode to command mode |
1366 | */ | |
ed5fd2cc | 1367 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { |
9fea808a | 1368 | fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00); |
ed5fd2cc | 1369 | } else { |
baca51fa | 1370 | fdctrl_reset_fifo(fdctrl); |
ed5fd2cc FB |
1371 | fdctrl_reset_irq(fdctrl); |
1372 | } | |
8977f3c1 FB |
1373 | } |
1374 | FLOPPY_DPRINTF("data register: 0x%02x\n", retval); | |
1375 | ||
1376 | return retval; | |
1377 | } | |
1378 | ||
baca51fa | 1379 | static void fdctrl_format_sector (fdctrl_t *fdctrl) |
8977f3c1 | 1380 | { |
baca51fa FB |
1381 | fdrive_t *cur_drv; |
1382 | uint8_t kh, kt, ks; | |
1383 | int did_seek; | |
8977f3c1 | 1384 | |
9fea808a | 1385 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
baca51fa FB |
1386 | cur_drv = get_cur_drv(fdctrl); |
1387 | kt = fdctrl->fifo[6]; | |
1388 | kh = fdctrl->fifo[7]; | |
1389 | ks = fdctrl->fifo[8]; | |
1390 | FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", | |
1391 | fdctrl->cur_drv, kh, kt, ks, | |
1392 | _fd_sector(kh, kt, ks, cur_drv->last_sect)); | |
1393 | did_seek = 0; | |
9fea808a | 1394 | switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { |
baca51fa FB |
1395 | case 2: |
1396 | /* sect too big */ | |
9fea808a | 1397 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); |
baca51fa FB |
1398 | fdctrl->fifo[3] = kt; |
1399 | fdctrl->fifo[4] = kh; | |
1400 | fdctrl->fifo[5] = ks; | |
1401 | return; | |
1402 | case 3: | |
1403 | /* track too big */ | |
9fea808a | 1404 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x80, 0x00); |
baca51fa FB |
1405 | fdctrl->fifo[3] = kt; |
1406 | fdctrl->fifo[4] = kh; | |
1407 | fdctrl->fifo[5] = ks; | |
1408 | return; | |
1409 | case 4: | |
1410 | /* No seek enabled */ | |
9fea808a | 1411 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); |
baca51fa FB |
1412 | fdctrl->fifo[3] = kt; |
1413 | fdctrl->fifo[4] = kh; | |
1414 | fdctrl->fifo[5] = ks; | |
1415 | return; | |
1416 | case 1: | |
1417 | did_seek = 1; | |
1418 | fdctrl->data_state |= FD_STATE_SEEK; | |
1419 | break; | |
1420 | default: | |
1421 | break; | |
1422 | } | |
1423 | memset(fdctrl->fifo, 0, FD_SECTOR_LEN); | |
1424 | if (cur_drv->bs == NULL || | |
1425 | bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { | |
37a4c539 | 1426 | FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); |
9fea808a | 1427 | fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); |
baca51fa | 1428 | } else { |
4f431960 JM |
1429 | if (cur_drv->sect == cur_drv->last_sect) { |
1430 | fdctrl->data_state &= ~FD_STATE_FORMAT; | |
1431 | /* Last sector done */ | |
1432 | if (FD_DID_SEEK(fdctrl->data_state)) | |
9fea808a | 1433 | fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00); |
4f431960 JM |
1434 | else |
1435 | fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); | |
1436 | } else { | |
1437 | /* More to do */ | |
1438 | fdctrl->data_pos = 0; | |
1439 | fdctrl->data_len = 4; | |
1440 | } | |
baca51fa FB |
1441 | } |
1442 | } | |
1443 | ||
1444 | static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) | |
1445 | { | |
1446 | fdrive_t *cur_drv; | |
1447 | ||
baca51fa | 1448 | cur_drv = get_cur_drv(fdctrl); |
8977f3c1 | 1449 | /* Reset mode */ |
baca51fa | 1450 | if (fdctrl->state & FD_CTRL_RESET) { |
4b19ec0c | 1451 | FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); |
8977f3c1 FB |
1452 | return; |
1453 | } | |
baca51fa FB |
1454 | fdctrl->state &= ~FD_CTRL_SLEEP; |
1455 | if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) { | |
8977f3c1 FB |
1456 | FLOPPY_ERROR("can't write data in status mode\n"); |
1457 | return; | |
1458 | } | |
1459 | /* Is it write command time ? */ | |
baca51fa | 1460 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { |
8977f3c1 | 1461 | /* FIFO data write */ |
baca51fa FB |
1462 | fdctrl->fifo[fdctrl->data_pos++] = value; |
1463 | if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) || | |
1464 | fdctrl->data_pos == fdctrl->data_len) { | |
d6c1a327 | 1465 | bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1); |
8977f3c1 | 1466 | } |
890fa6be | 1467 | /* Switch from transfer mode to status mode |
8977f3c1 FB |
1468 | * then from status mode to command mode |
1469 | */ | |
baca51fa | 1470 | if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) |
9fea808a | 1471 | fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00); |
8977f3c1 FB |
1472 | return; |
1473 | } | |
baca51fa | 1474 | if (fdctrl->data_pos == 0) { |
8977f3c1 FB |
1475 | /* Command */ |
1476 | switch (value & 0x5F) { | |
9fea808a | 1477 | case FD_CMD_READ: |
8977f3c1 FB |
1478 | /* READ variants */ |
1479 | FLOPPY_DPRINTF("READ command\n"); | |
1480 | /* 8 parameters cmd */ | |
baca51fa | 1481 | fdctrl->data_len = 9; |
8977f3c1 | 1482 | goto enqueue; |
9fea808a | 1483 | case FD_CMD_READ_DELETED: |
8977f3c1 FB |
1484 | /* READ_DELETED variants */ |
1485 | FLOPPY_DPRINTF("READ_DELETED command\n"); | |
1486 | /* 8 parameters cmd */ | |
baca51fa | 1487 | fdctrl->data_len = 9; |
8977f3c1 | 1488 | goto enqueue; |
9fea808a | 1489 | case FD_CMD_SCAN_EQUAL: |
8977f3c1 FB |
1490 | /* SCAN_EQUAL variants */ |
1491 | FLOPPY_DPRINTF("SCAN_EQUAL command\n"); | |
1492 | /* 8 parameters cmd */ | |
baca51fa | 1493 | fdctrl->data_len = 9; |
8977f3c1 | 1494 | goto enqueue; |
9fea808a | 1495 | case FD_CMD_VERIFY: |
8977f3c1 FB |
1496 | /* VERIFY variants */ |
1497 | FLOPPY_DPRINTF("VERIFY command\n"); | |
1498 | /* 8 parameters cmd */ | |
baca51fa | 1499 | fdctrl->data_len = 9; |
8977f3c1 | 1500 | goto enqueue; |
9fea808a | 1501 | case FD_CMD_SCAN_LOW_OR_EQUAL: |
8977f3c1 FB |
1502 | /* SCAN_LOW_OR_EQUAL variants */ |
1503 | FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n"); | |
1504 | /* 8 parameters cmd */ | |
baca51fa | 1505 | fdctrl->data_len = 9; |
8977f3c1 | 1506 | goto enqueue; |
9fea808a | 1507 | case FD_CMD_SCAN_HIGH_OR_EQUAL: |
8977f3c1 FB |
1508 | /* SCAN_HIGH_OR_EQUAL variants */ |
1509 | FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n"); | |
1510 | /* 8 parameters cmd */ | |
baca51fa | 1511 | fdctrl->data_len = 9; |
8977f3c1 FB |
1512 | goto enqueue; |
1513 | default: | |
1514 | break; | |
1515 | } | |
1516 | switch (value & 0x7F) { | |
9fea808a | 1517 | case FD_CMD_WRITE: |
8977f3c1 FB |
1518 | /* WRITE variants */ |
1519 | FLOPPY_DPRINTF("WRITE command\n"); | |
1520 | /* 8 parameters cmd */ | |
baca51fa | 1521 | fdctrl->data_len = 9; |
8977f3c1 | 1522 | goto enqueue; |
9fea808a | 1523 | case FD_CMD_WRITE_DELETED: |
8977f3c1 FB |
1524 | /* WRITE_DELETED variants */ |
1525 | FLOPPY_DPRINTF("WRITE_DELETED command\n"); | |
1526 | /* 8 parameters cmd */ | |
baca51fa | 1527 | fdctrl->data_len = 9; |
8977f3c1 FB |
1528 | goto enqueue; |
1529 | default: | |
1530 | break; | |
1531 | } | |
1532 | switch (value) { | |
9fea808a | 1533 | case FD_CMD_SPECIFY: |
8977f3c1 FB |
1534 | /* SPECIFY */ |
1535 | FLOPPY_DPRINTF("SPECIFY command\n"); | |
1536 | /* 1 parameter cmd */ | |
baca51fa | 1537 | fdctrl->data_len = 3; |
8977f3c1 | 1538 | goto enqueue; |
9fea808a | 1539 | case FD_CMD_SENSE_DRIVE_STATUS: |
8977f3c1 FB |
1540 | /* SENSE_DRIVE_STATUS */ |
1541 | FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n"); | |
1542 | /* 1 parameter cmd */ | |
baca51fa | 1543 | fdctrl->data_len = 2; |
8977f3c1 | 1544 | goto enqueue; |
9fea808a | 1545 | case FD_CMD_RECALIBRATE: |
8977f3c1 FB |
1546 | /* RECALIBRATE */ |
1547 | FLOPPY_DPRINTF("RECALIBRATE command\n"); | |
1548 | /* 1 parameter cmd */ | |
baca51fa | 1549 | fdctrl->data_len = 2; |
8977f3c1 | 1550 | goto enqueue; |
9fea808a | 1551 | case FD_CMD_SENSE_INTERRUPT_STATUS: |
8977f3c1 FB |
1552 | /* SENSE_INTERRUPT_STATUS */ |
1553 | FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n", | |
baca51fa | 1554 | fdctrl->int_status); |
8977f3c1 | 1555 | /* No parameters cmd: returns status if no interrupt */ |
953569d2 | 1556 | #if 0 |
baca51fa FB |
1557 | fdctrl->fifo[0] = |
1558 | fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv; | |
953569d2 FB |
1559 | #else |
1560 | /* XXX: int_status handling is broken for read/write | |
1561 | commands, so we do this hack. It should be suppressed | |
1562 | ASAP */ | |
1563 | fdctrl->fifo[0] = | |
1564 | 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv; | |
1565 | #endif | |
baca51fa FB |
1566 | fdctrl->fifo[1] = cur_drv->track; |
1567 | fdctrl_set_fifo(fdctrl, 2, 0); | |
4f431960 | 1568 | fdctrl_reset_irq(fdctrl); |
9fea808a | 1569 | fdctrl->int_status = FD_SR0_RDYCHG; |
8977f3c1 | 1570 | return; |
9fea808a | 1571 | case FD_CMD_DUMPREG: |
8977f3c1 FB |
1572 | /* DUMPREG */ |
1573 | FLOPPY_DPRINTF("DUMPREG command\n"); | |
1574 | /* Drives position */ | |
baca51fa FB |
1575 | fdctrl->fifo[0] = drv0(fdctrl)->track; |
1576 | fdctrl->fifo[1] = drv1(fdctrl)->track; | |
1577 | fdctrl->fifo[2] = 0; | |
1578 | fdctrl->fifo[3] = 0; | |
8977f3c1 | 1579 | /* timers */ |
baca51fa FB |
1580 | fdctrl->fifo[4] = fdctrl->timer0; |
1581 | fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en; | |
1582 | fdctrl->fifo[6] = cur_drv->last_sect; | |
1583 | fdctrl->fifo[7] = (fdctrl->lock << 7) | | |
4f431960 | 1584 | (cur_drv->perpendicular << 2); |
baca51fa FB |
1585 | fdctrl->fifo[8] = fdctrl->config; |
1586 | fdctrl->fifo[9] = fdctrl->precomp_trk; | |
1587 | fdctrl_set_fifo(fdctrl, 10, 0); | |
8977f3c1 | 1588 | return; |
9fea808a | 1589 | case FD_CMD_SEEK: |
8977f3c1 FB |
1590 | /* SEEK */ |
1591 | FLOPPY_DPRINTF("SEEK command\n"); | |
1592 | /* 2 parameters cmd */ | |
baca51fa | 1593 | fdctrl->data_len = 3; |
8977f3c1 | 1594 | goto enqueue; |
9fea808a | 1595 | case FD_CMD_VERSION: |
8977f3c1 FB |
1596 | /* VERSION */ |
1597 | FLOPPY_DPRINTF("VERSION command\n"); | |
1598 | /* No parameters cmd */ | |
4b19ec0c | 1599 | /* Controller's version */ |
baca51fa FB |
1600 | fdctrl->fifo[0] = fdctrl->version; |
1601 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 | 1602 | return; |
9fea808a | 1603 | case FD_CMD_PERPENDICULAR_MODE: |
8977f3c1 FB |
1604 | /* PERPENDICULAR_MODE */ |
1605 | FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n"); | |
1606 | /* 1 parameter cmd */ | |
baca51fa | 1607 | fdctrl->data_len = 2; |
8977f3c1 | 1608 | goto enqueue; |
9fea808a | 1609 | case FD_CMD_CONFIGURE: |
8977f3c1 FB |
1610 | /* CONFIGURE */ |
1611 | FLOPPY_DPRINTF("CONFIGURE command\n"); | |
1612 | /* 3 parameters cmd */ | |
baca51fa | 1613 | fdctrl->data_len = 4; |
8977f3c1 | 1614 | goto enqueue; |
9fea808a | 1615 | case FD_CMD_UNLOCK: |
8977f3c1 FB |
1616 | /* UNLOCK */ |
1617 | FLOPPY_DPRINTF("UNLOCK command\n"); | |
1618 | /* No parameters cmd */ | |
baca51fa FB |
1619 | fdctrl->lock = 0; |
1620 | fdctrl->fifo[0] = 0; | |
1621 | fdctrl_set_fifo(fdctrl, 1, 0); | |
8977f3c1 | 1622 | return; |
9fea808a | 1623 | case FD_CMD_POWERDOWN_MODE: |
8977f3c1 FB |
1624 | /* POWERDOWN_MODE */ |
1625 | FLOPPY_DPRINTF("POWERDOWN_MODE command\n"); | |
1626 | /* 2 parameters cmd */ | |
baca51fa | 1627 | fdctrl->data_len = 3; |
8977f3c1 | 1628 | goto enqueue; |
9fea808a | 1629 | case FD_CMD_PART_ID: |
8977f3c1 FB |
1630 | /* PART_ID */ |
1631 | FLOPPY_DPRINTF("PART_ID command\n"); | |
1632 | /* No parameters cmd */ | |
baca51fa FB |
1633 | fdctrl->fifo[0] = 0x41; /* Stepping 1 */ |
1634 | fdctrl_set_fifo(fdctrl, 1, 0); | |
8977f3c1 | 1635 | return; |
9fea808a | 1636 | case FD_CMD_SAVE: |
8977f3c1 FB |
1637 | /* SAVE */ |
1638 | FLOPPY_DPRINTF("SAVE command\n"); | |
1639 | /* No parameters cmd */ | |
baca51fa FB |
1640 | fdctrl->fifo[0] = 0; |
1641 | fdctrl->fifo[1] = 0; | |
8977f3c1 | 1642 | /* Drives position */ |
baca51fa FB |
1643 | fdctrl->fifo[2] = drv0(fdctrl)->track; |
1644 | fdctrl->fifo[3] = drv1(fdctrl)->track; | |
1645 | fdctrl->fifo[4] = 0; | |
1646 | fdctrl->fifo[5] = 0; | |
8977f3c1 | 1647 | /* timers */ |
baca51fa FB |
1648 | fdctrl->fifo[6] = fdctrl->timer0; |
1649 | fdctrl->fifo[7] = fdctrl->timer1; | |
1650 | fdctrl->fifo[8] = cur_drv->last_sect; | |
1651 | fdctrl->fifo[9] = (fdctrl->lock << 7) | | |
4f431960 | 1652 | (cur_drv->perpendicular << 2); |
baca51fa FB |
1653 | fdctrl->fifo[10] = fdctrl->config; |
1654 | fdctrl->fifo[11] = fdctrl->precomp_trk; | |
1655 | fdctrl->fifo[12] = fdctrl->pwrd; | |
1656 | fdctrl->fifo[13] = 0; | |
1657 | fdctrl->fifo[14] = 0; | |
1658 | fdctrl_set_fifo(fdctrl, 15, 1); | |
8977f3c1 | 1659 | return; |
9fea808a | 1660 | case FD_CMD_OPTION: |
8977f3c1 FB |
1661 | /* OPTION */ |
1662 | FLOPPY_DPRINTF("OPTION command\n"); | |
1663 | /* 1 parameter cmd */ | |
baca51fa | 1664 | fdctrl->data_len = 2; |
8977f3c1 | 1665 | goto enqueue; |
9fea808a | 1666 | case FD_CMD_READ_TRACK: |
8977f3c1 FB |
1667 | /* READ_TRACK */ |
1668 | FLOPPY_DPRINTF("READ_TRACK command\n"); | |
1669 | /* 8 parameters cmd */ | |
baca51fa | 1670 | fdctrl->data_len = 9; |
8977f3c1 | 1671 | goto enqueue; |
9fea808a | 1672 | case FD_CMD_READ_ID: |
8977f3c1 FB |
1673 | /* READ_ID */ |
1674 | FLOPPY_DPRINTF("READ_ID command\n"); | |
1675 | /* 1 parameter cmd */ | |
baca51fa | 1676 | fdctrl->data_len = 2; |
8977f3c1 | 1677 | goto enqueue; |
9fea808a | 1678 | case FD_CMD_RESTORE: |
8977f3c1 FB |
1679 | /* RESTORE */ |
1680 | FLOPPY_DPRINTF("RESTORE command\n"); | |
1681 | /* 17 parameters cmd */ | |
baca51fa | 1682 | fdctrl->data_len = 18; |
8977f3c1 | 1683 | goto enqueue; |
9fea808a | 1684 | case FD_CMD_FORMAT_TRACK: |
8977f3c1 FB |
1685 | /* FORMAT_TRACK */ |
1686 | FLOPPY_DPRINTF("FORMAT_TRACK command\n"); | |
1687 | /* 5 parameters cmd */ | |
baca51fa | 1688 | fdctrl->data_len = 6; |
8977f3c1 | 1689 | goto enqueue; |
9fea808a | 1690 | case FD_CMD_DRIVE_SPECIFICATION_COMMAND: |
8977f3c1 FB |
1691 | /* DRIVE_SPECIFICATION_COMMAND */ |
1692 | FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n"); | |
1693 | /* 5 parameters cmd */ | |
baca51fa | 1694 | fdctrl->data_len = 6; |
8977f3c1 | 1695 | goto enqueue; |
9fea808a | 1696 | case FD_CMD_RELATIVE_SEEK_OUT: |
8977f3c1 FB |
1697 | /* RELATIVE_SEEK_OUT */ |
1698 | FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n"); | |
1699 | /* 2 parameters cmd */ | |
baca51fa | 1700 | fdctrl->data_len = 3; |
8977f3c1 | 1701 | goto enqueue; |
9fea808a | 1702 | case FD_CMD_LOCK: |
8977f3c1 FB |
1703 | /* LOCK */ |
1704 | FLOPPY_DPRINTF("LOCK command\n"); | |
1705 | /* No parameters cmd */ | |
baca51fa FB |
1706 | fdctrl->lock = 1; |
1707 | fdctrl->fifo[0] = 0x10; | |
1708 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 | 1709 | return; |
9fea808a | 1710 | case FD_CMD_FORMAT_AND_WRITE: |
8977f3c1 FB |
1711 | /* FORMAT_AND_WRITE */ |
1712 | FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n"); | |
1713 | /* 10 parameters cmd */ | |
baca51fa | 1714 | fdctrl->data_len = 11; |
8977f3c1 | 1715 | goto enqueue; |
9fea808a | 1716 | case FD_CMD_RELATIVE_SEEK_IN: |
8977f3c1 FB |
1717 | /* RELATIVE_SEEK_IN */ |
1718 | FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n"); | |
1719 | /* 2 parameters cmd */ | |
baca51fa | 1720 | fdctrl->data_len = 3; |
8977f3c1 FB |
1721 | goto enqueue; |
1722 | default: | |
1723 | /* Unknown command */ | |
1724 | FLOPPY_ERROR("unknown command: 0x%02x\n", value); | |
baca51fa | 1725 | fdctrl_unimplemented(fdctrl); |
8977f3c1 FB |
1726 | return; |
1727 | } | |
1728 | } | |
4f431960 | 1729 | enqueue: |
baca51fa FB |
1730 | FLOPPY_DPRINTF("%s: %02x\n", __func__, value); |
1731 | fdctrl->fifo[fdctrl->data_pos] = value; | |
1732 | if (++fdctrl->data_pos == fdctrl->data_len) { | |
8977f3c1 FB |
1733 | /* We now have all parameters |
1734 | * and will be able to treat the command | |
1735 | */ | |
4f431960 JM |
1736 | if (fdctrl->data_state & FD_STATE_FORMAT) { |
1737 | fdctrl_format_sector(fdctrl); | |
8977f3c1 FB |
1738 | return; |
1739 | } | |
4f431960 | 1740 | switch (fdctrl->fifo[0] & 0x1F) { |
9fea808a | 1741 | case FD_CMD_READ & 0x1F: |
4f431960 JM |
1742 | { |
1743 | /* READ variants */ | |
1744 | FLOPPY_DPRINTF("treat READ command\n"); | |
1745 | fdctrl_start_transfer(fdctrl, FD_DIR_READ); | |
1746 | return; | |
1747 | } | |
9fea808a | 1748 | case FD_CMD_READ_DELETED & 0x1F: |
8977f3c1 FB |
1749 | /* READ_DELETED variants */ |
1750 | // FLOPPY_DPRINTF("treat READ_DELETED command\n"); | |
1751 | FLOPPY_ERROR("treat READ_DELETED command\n"); | |
baca51fa | 1752 | fdctrl_start_transfer_del(fdctrl, FD_DIR_READ); |
8977f3c1 | 1753 | return; |
9fea808a | 1754 | case FD_CMD_VERIFY & 0x1F: |
8977f3c1 FB |
1755 | /* VERIFY variants */ |
1756 | // FLOPPY_DPRINTF("treat VERIFY command\n"); | |
1757 | FLOPPY_ERROR("treat VERIFY command\n"); | |
9fea808a | 1758 | fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00); |
8977f3c1 | 1759 | return; |
9fea808a | 1760 | case FD_CMD_SCAN_EQUAL & 0x1F: |
8977f3c1 FB |
1761 | /* SCAN_EQUAL variants */ |
1762 | // FLOPPY_DPRINTF("treat SCAN_EQUAL command\n"); | |
1763 | FLOPPY_ERROR("treat SCAN_EQUAL command\n"); | |
baca51fa | 1764 | fdctrl_start_transfer(fdctrl, FD_DIR_SCANE); |
8977f3c1 | 1765 | return; |
9fea808a | 1766 | case FD_CMD_SCAN_LOW_OR_EQUAL & 0x1F: |
8977f3c1 FB |
1767 | /* SCAN_LOW_OR_EQUAL variants */ |
1768 | // FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n"); | |
1769 | FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n"); | |
baca51fa | 1770 | fdctrl_start_transfer(fdctrl, FD_DIR_SCANL); |
8977f3c1 | 1771 | return; |
9fea808a | 1772 | case FD_CMD_SCAN_HIGH_OR_EQUAL & 0x1F: |
8977f3c1 FB |
1773 | /* SCAN_HIGH_OR_EQUAL variants */ |
1774 | // FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n"); | |
1775 | FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n"); | |
baca51fa | 1776 | fdctrl_start_transfer(fdctrl, FD_DIR_SCANH); |
8977f3c1 FB |
1777 | return; |
1778 | default: | |
1779 | break; | |
1780 | } | |
baca51fa | 1781 | switch (fdctrl->fifo[0] & 0x3F) { |
9fea808a | 1782 | case FD_CMD_WRITE & 0x3F: |
8977f3c1 | 1783 | /* WRITE variants */ |
baca51fa FB |
1784 | FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]); |
1785 | fdctrl_start_transfer(fdctrl, FD_DIR_WRITE); | |
8977f3c1 | 1786 | return; |
9fea808a | 1787 | case FD_CMD_WRITE_DELETED & 0x3F: |
8977f3c1 FB |
1788 | /* WRITE_DELETED variants */ |
1789 | // FLOPPY_DPRINTF("treat WRITE_DELETED command\n"); | |
1790 | FLOPPY_ERROR("treat WRITE_DELETED command\n"); | |
baca51fa | 1791 | fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE); |
8977f3c1 FB |
1792 | return; |
1793 | default: | |
1794 | break; | |
1795 | } | |
baca51fa | 1796 | switch (fdctrl->fifo[0]) { |
9fea808a | 1797 | case FD_CMD_SPECIFY: |
8977f3c1 FB |
1798 | /* SPECIFY */ |
1799 | FLOPPY_DPRINTF("treat SPECIFY command\n"); | |
baca51fa | 1800 | fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; |
e309de25 | 1801 | fdctrl->timer1 = fdctrl->fifo[2] >> 1; |
4f431960 | 1802 | fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ; |
8977f3c1 | 1803 | /* No result back */ |
baca51fa | 1804 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1805 | break; |
9fea808a | 1806 | case FD_CMD_SENSE_DRIVE_STATUS: |
8977f3c1 FB |
1807 | /* SENSE_DRIVE_STATUS */ |
1808 | FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n"); | |
9fea808a | 1809 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
4f431960 | 1810 | cur_drv = get_cur_drv(fdctrl); |
baca51fa | 1811 | cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; |
8977f3c1 | 1812 | /* 1 Byte status back */ |
baca51fa | 1813 | fdctrl->fifo[0] = (cur_drv->ro << 6) | |
8977f3c1 | 1814 | (cur_drv->track == 0 ? 0x10 : 0x00) | |
890fa6be FB |
1815 | (cur_drv->head << 2) | |
1816 | fdctrl->cur_drv | | |
1817 | 0x28; | |
baca51fa | 1818 | fdctrl_set_fifo(fdctrl, 1, 0); |
8977f3c1 | 1819 | break; |
9fea808a | 1820 | case FD_CMD_RECALIBRATE: |
8977f3c1 FB |
1821 | /* RECALIBRATE */ |
1822 | FLOPPY_DPRINTF("treat RECALIBRATE command\n"); | |
9fea808a | 1823 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
4f431960 | 1824 | cur_drv = get_cur_drv(fdctrl); |
8977f3c1 | 1825 | fd_recalibrate(cur_drv); |
4f431960 | 1826 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1827 | /* Raise Interrupt */ |
9fea808a | 1828 | fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); |
8977f3c1 | 1829 | break; |
9fea808a | 1830 | case FD_CMD_SEEK: |
8977f3c1 FB |
1831 | /* SEEK */ |
1832 | FLOPPY_DPRINTF("treat SEEK command\n"); | |
9fea808a | 1833 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
4f431960 JM |
1834 | cur_drv = get_cur_drv(fdctrl); |
1835 | fd_start(cur_drv); | |
baca51fa | 1836 | if (fdctrl->fifo[2] <= cur_drv->track) |
8977f3c1 FB |
1837 | cur_drv->dir = 1; |
1838 | else | |
1839 | cur_drv->dir = 0; | |
4f431960 | 1840 | fdctrl_reset_fifo(fdctrl); |
baca51fa | 1841 | if (fdctrl->fifo[2] > cur_drv->max_track) { |
9fea808a | 1842 | fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK); |
8977f3c1 | 1843 | } else { |
baca51fa | 1844 | cur_drv->track = fdctrl->fifo[2]; |
8977f3c1 | 1845 | /* Raise Interrupt */ |
9fea808a | 1846 | fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); |
8977f3c1 FB |
1847 | } |
1848 | break; | |
9fea808a | 1849 | case FD_CMD_PERPENDICULAR_MODE: |
8977f3c1 FB |
1850 | /* PERPENDICULAR_MODE */ |
1851 | FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n"); | |
baca51fa FB |
1852 | if (fdctrl->fifo[1] & 0x80) |
1853 | cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; | |
8977f3c1 | 1854 | /* No result back */ |
baca51fa | 1855 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1856 | break; |
9fea808a | 1857 | case FD_CMD_CONFIGURE: |
8977f3c1 FB |
1858 | /* CONFIGURE */ |
1859 | FLOPPY_DPRINTF("treat CONFIGURE command\n"); | |
baca51fa FB |
1860 | fdctrl->config = fdctrl->fifo[2]; |
1861 | fdctrl->precomp_trk = fdctrl->fifo[3]; | |
8977f3c1 | 1862 | /* No result back */ |
baca51fa | 1863 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1864 | break; |
9fea808a | 1865 | case FD_CMD_POWERDOWN_MODE: |
8977f3c1 FB |
1866 | /* POWERDOWN_MODE */ |
1867 | FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n"); | |
baca51fa FB |
1868 | fdctrl->pwrd = fdctrl->fifo[1]; |
1869 | fdctrl->fifo[0] = fdctrl->fifo[1]; | |
1870 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 | 1871 | break; |
9fea808a | 1872 | case FD_CMD_OPTION: |
8977f3c1 FB |
1873 | /* OPTION */ |
1874 | FLOPPY_DPRINTF("treat OPTION command\n"); | |
1875 | /* No result back */ | |
baca51fa | 1876 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1877 | break; |
9fea808a | 1878 | case FD_CMD_READ_TRACK: |
8977f3c1 | 1879 | /* READ_TRACK */ |
9fea808a | 1880 | FLOPPY_DPRINTF("treat READ_TRACK command\n"); |
8977f3c1 | 1881 | FLOPPY_ERROR("treat READ_TRACK command\n"); |
baca51fa | 1882 | fdctrl_start_transfer(fdctrl, FD_DIR_READ); |
8977f3c1 | 1883 | break; |
9fea808a | 1884 | case FD_CMD_READ_ID: |
4f431960 | 1885 | /* READ_ID */ |
baca51fa | 1886 | FLOPPY_DPRINTF("treat READ_ID command\n"); |
ed5fd2cc | 1887 | /* XXX: should set main status register to busy */ |
890fa6be | 1888 | cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; |
5fafdf24 | 1889 | qemu_mod_timer(fdctrl->result_timer, |
ed5fd2cc | 1890 | qemu_get_clock(vm_clock) + (ticks_per_sec / 50)); |
8977f3c1 | 1891 | break; |
9fea808a | 1892 | case FD_CMD_RESTORE: |
8977f3c1 FB |
1893 | /* RESTORE */ |
1894 | FLOPPY_DPRINTF("treat RESTORE command\n"); | |
1895 | /* Drives position */ | |
baca51fa FB |
1896 | drv0(fdctrl)->track = fdctrl->fifo[3]; |
1897 | drv1(fdctrl)->track = fdctrl->fifo[4]; | |
8977f3c1 | 1898 | /* timers */ |
baca51fa FB |
1899 | fdctrl->timer0 = fdctrl->fifo[7]; |
1900 | fdctrl->timer1 = fdctrl->fifo[8]; | |
1901 | cur_drv->last_sect = fdctrl->fifo[9]; | |
1902 | fdctrl->lock = fdctrl->fifo[10] >> 7; | |
1903 | cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF; | |
1904 | fdctrl->config = fdctrl->fifo[11]; | |
1905 | fdctrl->precomp_trk = fdctrl->fifo[12]; | |
1906 | fdctrl->pwrd = fdctrl->fifo[13]; | |
1907 | fdctrl_reset_fifo(fdctrl); | |
8977f3c1 | 1908 | break; |
9fea808a | 1909 | case FD_CMD_FORMAT_TRACK: |
8977f3c1 | 1910 | /* FORMAT_TRACK */ |
4f431960 | 1911 | FLOPPY_DPRINTF("treat FORMAT_TRACK command\n"); |
9fea808a | 1912 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
4f431960 JM |
1913 | cur_drv = get_cur_drv(fdctrl); |
1914 | fdctrl->data_state |= FD_STATE_FORMAT; | |
1915 | if (fdctrl->fifo[0] & 0x80) | |
1916 | fdctrl->data_state |= FD_STATE_MULTI; | |
1917 | else | |
1918 | fdctrl->data_state &= ~FD_STATE_MULTI; | |
1919 | fdctrl->data_state &= ~FD_STATE_SEEK; | |
1920 | cur_drv->bps = | |
1921 | fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; | |
baca51fa | 1922 | #if 0 |
4f431960 JM |
1923 | cur_drv->last_sect = |
1924 | cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : | |
1925 | fdctrl->fifo[3] / 2; | |
baca51fa | 1926 | #else |
4f431960 | 1927 | cur_drv->last_sect = fdctrl->fifo[3]; |
baca51fa | 1928 | #endif |
4f431960 JM |
1929 | /* TODO: implement format using DMA expected by the Bochs BIOS |
1930 | * and Linux fdformat (read 3 bytes per sector via DMA and fill | |
1931 | * the sector with the specified fill byte | |
1932 | */ | |
1933 | fdctrl->data_state &= ~FD_STATE_FORMAT; | |
1934 | fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); | |
8977f3c1 | 1935 | break; |
9fea808a | 1936 | case FD_CMD_DRIVE_SPECIFICATION_COMMAND: |
8977f3c1 FB |
1937 | /* DRIVE_SPECIFICATION_COMMAND */ |
1938 | FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n"); | |
baca51fa | 1939 | if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { |
8977f3c1 | 1940 | /* Command parameters done */ |
baca51fa FB |
1941 | if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { |
1942 | fdctrl->fifo[0] = fdctrl->fifo[1]; | |
1943 | fdctrl->fifo[2] = 0; | |
1944 | fdctrl->fifo[3] = 0; | |
1945 | fdctrl_set_fifo(fdctrl, 4, 1); | |
8977f3c1 | 1946 | } else { |
baca51fa | 1947 | fdctrl_reset_fifo(fdctrl); |
8977f3c1 | 1948 | } |
baca51fa | 1949 | } else if (fdctrl->data_len > 7) { |
8977f3c1 | 1950 | /* ERROR */ |
baca51fa FB |
1951 | fdctrl->fifo[0] = 0x80 | |
1952 | (cur_drv->head << 2) | fdctrl->cur_drv; | |
1953 | fdctrl_set_fifo(fdctrl, 1, 1); | |
8977f3c1 FB |
1954 | } |
1955 | break; | |
9fea808a | 1956 | case FD_CMD_RELATIVE_SEEK_OUT: |
8977f3c1 FB |
1957 | /* RELATIVE_SEEK_OUT */ |
1958 | FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n"); | |
9fea808a | 1959 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
4f431960 JM |
1960 | cur_drv = get_cur_drv(fdctrl); |
1961 | fd_start(cur_drv); | |
1962 | cur_drv->dir = 0; | |
baca51fa | 1963 | if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { |
4f431960 | 1964 | cur_drv->track = cur_drv->max_track - 1; |
baca51fa FB |
1965 | } else { |
1966 | cur_drv->track += fdctrl->fifo[2]; | |
8977f3c1 | 1967 | } |
4f431960 | 1968 | fdctrl_reset_fifo(fdctrl); |
9fea808a | 1969 | fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); |
8977f3c1 | 1970 | break; |
9fea808a | 1971 | case FD_CMD_FORMAT_AND_WRITE: |
8977f3c1 | 1972 | /* FORMAT_AND_WRITE */ |
9fea808a | 1973 | FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n"); |
8977f3c1 | 1974 | FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n"); |
baca51fa | 1975 | fdctrl_unimplemented(fdctrl); |
8977f3c1 | 1976 | break; |
9fea808a | 1977 | case FD_CMD_RELATIVE_SEEK_IN: |
4f431960 | 1978 | /* RELATIVE_SEEK_IN */ |
8977f3c1 | 1979 | FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n"); |
9fea808a | 1980 | fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; |
4f431960 JM |
1981 | cur_drv = get_cur_drv(fdctrl); |
1982 | fd_start(cur_drv); | |
1983 | cur_drv->dir = 1; | |
baca51fa | 1984 | if (fdctrl->fifo[2] > cur_drv->track) { |
4f431960 | 1985 | cur_drv->track = 0; |
baca51fa FB |
1986 | } else { |
1987 | cur_drv->track -= fdctrl->fifo[2]; | |
8977f3c1 | 1988 | } |
4f431960 JM |
1989 | fdctrl_reset_fifo(fdctrl); |
1990 | /* Raise Interrupt */ | |
9fea808a | 1991 | fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); |
8977f3c1 FB |
1992 | break; |
1993 | } | |
1994 | } | |
1995 | } | |
ed5fd2cc FB |
1996 | |
1997 | static void fdctrl_result_timer(void *opaque) | |
1998 | { | |
1999 | fdctrl_t *fdctrl = opaque; | |
b7ffa3b1 | 2000 | fdrive_t *cur_drv = get_cur_drv(fdctrl); |
4f431960 | 2001 | |
b7ffa3b1 TS |
2002 | /* Pretend we are spinning. |
2003 | * This is needed for Coherent, which uses READ ID to check for | |
2004 | * sector interleaving. | |
2005 | */ | |
2006 | if (cur_drv->last_sect != 0) { | |
2007 | cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; | |
2008 | } | |
ed5fd2cc FB |
2009 | fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); |
2010 | } |