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