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