1 /* ide.c: Routines for handling HDF hard disk files
2 Copyright (c) 2003-2004 Garry Lancaster,
3 2004,2015 Philip Kendall
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19 Author contact information:
20
21 E-mail: Philip Kendall <philip-fuse@shadowmagic.org.uk>
22
23 */
24
25 #include "config.h"
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "internals.h"
32
33 typedef enum libspectrum_ide_command {
34
35 LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_RETRY = 0x20,
36 LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_NORETRY = 0x21,
37 LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_RETRY = 0x30,
38 LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_NORETRY = 0x31,
39 LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATA = 0xec,
40 LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATAPI = 0xa1,
41 LIBSPECTRUM_IDE_COMMAND_INITIALIZE_DEVICE_PARAMETERS = 0x91,
42
43 } libspectrum_ide_command;
44
45 typedef enum libspectrum_ide_phase {
46
47 LIBSPECTRUM_IDE_PHASE_READY,
48 LIBSPECTRUM_IDE_PHASE_PIO_OUT,
49 LIBSPECTRUM_IDE_PHASE_PIO_IN,
50
51 } libspectrum_ide_phase;
52
53 typedef enum libspectrum_ide_statusreg {
54
55 LIBSPECTRUM_IDE_STATUS_ERR = 0x01,
56 LIBSPECTRUM_IDE_STATUS_DRQ = 0x08,
57 LIBSPECTRUM_IDE_STATUS_DRDY = 0x40,
58 LIBSPECTRUM_IDE_STATUS_BSY = 0x80,
59
60 } libspectrum_ide_statusreg;
61
62 typedef enum libspectrum_ide_headreg {
63
64 LIBSPECTRUM_IDE_HEAD_HEAD = 0x0f,
65 LIBSPECTRUM_IDE_HEAD_DEV = 0x10,
66 LIBSPECTRUM_IDE_HEAD_LBA = 0x40,
67
68 } libspectrum_ide_headreg;
69
70 typedef enum libspectrum_ide_errorreg {
71
72 LIBSPECTRUM_IDE_ERROR_OK = 0x00,
73 LIBSPECTRUM_IDE_ERROR_ABRT = 0x04,
74 LIBSPECTRUM_IDE_ERROR_IDNF = 0x10,
75 LIBSPECTRUM_IDE_ERROR_UNC = 0x40,
76
77 } libspectrum_ide_errorcode;
78
79 typedef enum libspectrum_ide_identityfield {
80
81 LIBSPECTRUM_IDE_IDENTITY_NUM_CYLINDERS = 1,
82 LIBSPECTRUM_IDE_IDENTITY_NUM_HEADS = 3,
83 LIBSPECTRUM_IDE_IDENTITY_NUM_SECTORS = 6,
84 LIBSPECTRUM_IDE_IDENTITY_CAPABILITIES = 49,
85 LIBSPECTRUM_IDE_IDENTITY_FIELD_VALIDITY = 53,
86 LIBSPECTRUM_IDE_IDENTITY_CURRENT_CYLINDERS = 54,
87 LIBSPECTRUM_IDE_IDENTITY_CURRENT_HEADS = 55,
88 LIBSPECTRUM_IDE_IDENTITY_CURRENT_SECTORS = 56,
89 LIBSPECTRUM_IDE_IDENTITY_CURRENT_CAPACITY_LOW = 57,
90 LIBSPECTRUM_IDE_IDENTITY_CURRENT_CAPACITY_HI = 58,
91 LIBSPECTRUM_IDE_IDENTITY_TOTAL_SECTORS_LOW = 60,
92 LIBSPECTRUM_IDE_IDENTITY_TOTAL_SECTORS_HI = 61,
93
94 } libspectrum_ide_identityfield;
95
96 /* Operations on identity fields.
97 For reasons best known to Ramsoft, these (together with the disk
98 data itself) are stored in Intel little-endian format rather than
99 the way the ATA spec describes.
100 */
101 #define GET_WORD( arr, index ) \
102 ( ( (arr)[ ( (index) << 1 ) + 1 ] << 8 ) | ( (arr)[ (index) << 1 ] ) )
103 #define SET_WORD( arr, index, val ) \
104 (arr)[ ( (index) << 1 ) + 1 ] = (val) >> 8; \
105 (arr)[ (index) << 1 ] = (val) & 0xff;
106
107 struct libspectrum_ide_channel {
108
109 /* Interface bus width */
110 libspectrum_ide_databus databus;
111
112 /* 2 drives per channel */
113 libspectrum_ide_drive drive[2];
114
115 /* Last selection written to head register */
116 libspectrum_ide_unit selected;
117
118 /* Register values */
119 libspectrum_byte feature;
120 libspectrum_byte sector_count;
121 libspectrum_byte sector;
122 libspectrum_byte cylinder_low;
123 libspectrum_byte cylinder_high;
124 libspectrum_byte head;
125 libspectrum_byte data2;
126
127 /* Channel status */
128 libspectrum_ide_phase phase;
129 int datacounter;
130
131 /* Sector buffer */
132 libspectrum_byte buffer[512];
133 int sector_number;
134
135 /* One write cache for each drive */
136 GHashTable *cache[2];
137
138 };
139
140 /* Private function prototypes */
141 static gboolean write_to_disk( gpointer key, gpointer value,
142 gpointer user_data );
143 static gboolean clear_cache( gpointer key, gpointer value,
144 gpointer user_data GCC_UNUSED );
145 static int read_hdf( libspectrum_ide_channel *chn );
146 static int write_hdf( libspectrum_ide_channel *chn );
147 static libspectrum_byte read_data( libspectrum_ide_channel *chn );
148 static void write_data( libspectrum_ide_channel *chn,
149 libspectrum_byte data );
150 static libspectrum_error seek( libspectrum_ide_channel *chn );
151 static void identifydevice( libspectrum_ide_channel *chn );
152 static void readsector( libspectrum_ide_channel *chn );
153 static void writesector( libspectrum_ide_channel *chn );
154 static void init_device_params( libspectrum_ide_channel *chn );
155 static void execute_command( libspectrum_ide_channel *chn,
156 libspectrum_byte data );
157
158
159 /* Initialise a libspectrum_ide_channel structure */
160 libspectrum_ide_channel*
libspectrum_ide_alloc(libspectrum_ide_databus databus)161 libspectrum_ide_alloc( libspectrum_ide_databus databus )
162 {
163 libspectrum_ide_channel *channel;
164
165 channel = libspectrum_new( libspectrum_ide_channel, 1 );
166
167 channel->databus = databus;
168 channel->drive[ LIBSPECTRUM_IDE_MASTER ].disk = NULL;
169 channel->drive[ LIBSPECTRUM_IDE_SLAVE ].disk = NULL;
170
171 channel->cache[ LIBSPECTRUM_IDE_MASTER ] =
172 g_hash_table_new( g_int_hash, g_int_equal );
173 channel->cache[ LIBSPECTRUM_IDE_SLAVE ] =
174 g_hash_table_new( g_int_hash, g_int_equal );
175
176 return channel;
177 }
178
179 /* Free all memory used by a libspectrum_ide_channel structure */
180 libspectrum_error
libspectrum_ide_free(libspectrum_ide_channel * chn)181 libspectrum_ide_free( libspectrum_ide_channel *chn )
182 {
183 /* Make sure all HDF files are ejected first */
184 libspectrum_ide_eject( chn, LIBSPECTRUM_IDE_MASTER );
185 libspectrum_ide_eject( chn, LIBSPECTRUM_IDE_SLAVE );
186
187 g_hash_table_destroy( chn->cache[ LIBSPECTRUM_IDE_MASTER ] );
188 g_hash_table_destroy( chn->cache[ LIBSPECTRUM_IDE_SLAVE ] );
189
190 /* Free the channel structure */
191 libspectrum_free( chn );
192
193 return LIBSPECTRUM_ERROR_NONE;
194 }
195
196 libspectrum_error
libspectrum_ide_insert_into_drive(libspectrum_ide_drive * drv,const char * filename)197 libspectrum_ide_insert_into_drive( libspectrum_ide_drive *drv,
198 const char *filename )
199 {
200 FILE *f;
201 size_t l;
202
203 /* Open the file */
204 f = fopen( filename, "rb+" );
205 if( !f ) {
206 libspectrum_print_error(
207 LIBSPECTRUM_ERROR_UNKNOWN,
208 "libspectrum_ide_insert: unable to open file '%s': %s", filename,
209 strerror( errno )
210 );
211 return LIBSPECTRUM_ERROR_UNKNOWN;
212 }
213
214 /* Read the HDF header */
215 l = fread( &drv->hdf, 1, sizeof( libspectrum_hdf_header ), f );
216 if ( l != sizeof( libspectrum_hdf_header ) ) {
217 fclose( f );
218 libspectrum_print_error(
219 LIBSPECTRUM_ERROR_UNKNOWN,
220 "libspectrum_ide_insert: unable to read HDF header from '%s'", filename
221 );
222 return LIBSPECTRUM_ERROR_UNKNOWN;
223 }
224
225 /* Verify the validity of the header */
226 if( memcmp( &drv->hdf.signature, "RS-IDE", 6 ) ||
227 drv->hdf.id != 0x1a ) {
228 fclose( f );
229 libspectrum_print_error(
230 LIBSPECTRUM_ERROR_CORRUPT,
231 "libspectrum_ide_insert: '%s' is not a valid HDF file", filename
232 );
233 return LIBSPECTRUM_ERROR_CORRUPT;
234 }
235
236 /* Extract details from the header */
237 drv->disk = f;
238 drv->data_offset =
239 ( drv->hdf.datastart_hi << 8 ) | ( drv->hdf.datastart_low );
240 drv->sector_size = ( drv->hdf.flags & 0x01 ) ? 256 : 512;
241
242 /* Extract drive geometry from the drive identity command */
243 drv->cylinders = GET_WORD(
244 drv->hdf.drive_identity, LIBSPECTRUM_IDE_IDENTITY_NUM_CYLINDERS );
245 drv->heads = GET_WORD(
246 drv->hdf.drive_identity, LIBSPECTRUM_IDE_IDENTITY_NUM_HEADS );
247 drv->sectors = GET_WORD(
248 drv->hdf.drive_identity, LIBSPECTRUM_IDE_IDENTITY_NUM_SECTORS );
249
250 return LIBSPECTRUM_ERROR_NONE;
251 }
252
253 /* Insert a hard disk into a drive */
254 libspectrum_error
libspectrum_ide_insert(libspectrum_ide_channel * chn,libspectrum_ide_unit unit,const char * filename)255 libspectrum_ide_insert( libspectrum_ide_channel *chn,
256 libspectrum_ide_unit unit,
257 const char *filename )
258 {
259 libspectrum_ide_drive *drv = &chn->drive[unit];
260
261 libspectrum_ide_eject( chn, unit );
262 if ( !filename ) return LIBSPECTRUM_ERROR_NONE;
263
264 return libspectrum_ide_insert_into_drive( drv, filename );
265 }
266
267 static gboolean
write_to_disk(gpointer key,gpointer value,gpointer user_data)268 write_to_disk( gpointer key, gpointer value, gpointer user_data )
269 {
270 guint sector_number = *(guint*)key;
271 libspectrum_byte *buffer = value;
272 libspectrum_ide_drive *drv = user_data;
273
274 long sector_position;
275
276 sector_position = drv->data_offset + ( drv->sector_size * sector_number );
277
278 if( fseek( drv->disk, sector_position, SEEK_SET ) )
279 return FALSE;
280
281 if( fwrite( buffer, 1, drv->sector_size, drv->disk ) != drv->sector_size )
282 return FALSE;
283
284 libspectrum_free( key ); libspectrum_free( value );
285
286 return TRUE; /* TRUE => remove key/value pair from hash */
287 }
288
289 void
libspectrum_ide_commit_drive(libspectrum_ide_drive * drv,GHashTable * cache)290 libspectrum_ide_commit_drive( libspectrum_ide_drive *drv, GHashTable *cache )
291 {
292 if( !drv->disk ) return;
293
294 g_hash_table_foreach_remove( cache, write_to_disk, drv );
295 }
296
297 /* Commit any pending writes to disk */
298 libspectrum_error
libspectrum_ide_commit(libspectrum_ide_channel * chn,libspectrum_ide_unit unit)299 libspectrum_ide_commit( libspectrum_ide_channel *chn,
300 libspectrum_ide_unit unit )
301 {
302 libspectrum_ide_commit_drive( &chn->drive[ unit ], chn->cache[ unit ] );
303
304 return LIBSPECTRUM_ERROR_NONE;
305 }
306
307 static gboolean
clear_cache(gpointer key,gpointer value,gpointer user_data GCC_UNUSED)308 clear_cache( gpointer key, gpointer value, gpointer user_data GCC_UNUSED )
309 {
310 libspectrum_free( key );
311 libspectrum_free( value );
312 return TRUE;
313 }
314
315 /* Is there any dirty data for this disk? */
316 int
libspectrum_ide_dirty(libspectrum_ide_channel * chn,libspectrum_ide_unit unit)317 libspectrum_ide_dirty( libspectrum_ide_channel *chn,
318 libspectrum_ide_unit unit )
319 {
320 return g_hash_table_size( chn->cache[ unit ] ) != 0;
321 }
322
323 /* Eject a hard disk from a drive and free its cache */
324 libspectrum_error
libspectrum_ide_eject_from_drive(libspectrum_ide_drive * drv,GHashTable * cache)325 libspectrum_ide_eject_from_drive( libspectrum_ide_drive *drv,
326 GHashTable *cache )
327 {
328 if( !drv->disk ) return LIBSPECTRUM_ERROR_NONE;
329
330 fclose( drv->disk );
331 drv->disk = NULL;
332
333 g_hash_table_foreach_remove( cache, clear_cache, NULL );
334
335 return LIBSPECTRUM_ERROR_NONE;
336 }
337
338 /* Eject a hard disk from a channel / unit combination */
339 libspectrum_error
libspectrum_ide_eject(libspectrum_ide_channel * chn,libspectrum_ide_unit unit)340 libspectrum_ide_eject( libspectrum_ide_channel *chn,
341 libspectrum_ide_unit unit )
342 {
343 return libspectrum_ide_eject_from_drive( &chn->drive[ unit ],
344 chn->cache[ unit ] );
345 }
346
347 /* Reset an IDE channel */
348 libspectrum_error
libspectrum_ide_reset(libspectrum_ide_channel * chn)349 libspectrum_ide_reset( libspectrum_ide_channel *chn )
350 {
351 /* Reset channel status */
352 chn->selected = LIBSPECTRUM_IDE_MASTER;
353 chn->phase = LIBSPECTRUM_IDE_PHASE_READY;
354
355 if( chn->drive[LIBSPECTRUM_IDE_MASTER].disk ||
356 chn->drive[LIBSPECTRUM_IDE_SLAVE].disk ) {
357
358 /* ATA reset signature for non-PACKET-supporting drives */
359 chn->sector_count = 0x01;
360 chn->sector = 0x01;
361 chn->cylinder_low = 0x00;
362 chn->cylinder_high = 0x00;
363 chn->head = 0x00;
364
365 /* Diagnostics passed */
366 if (chn->drive[LIBSPECTRUM_IDE_MASTER].disk)
367 chn->drive[LIBSPECTRUM_IDE_MASTER].error = 0x01;
368 else
369 chn->drive[LIBSPECTRUM_IDE_MASTER].error = 0xff;
370
371 if (chn->drive[LIBSPECTRUM_IDE_SLAVE].disk)
372 chn->drive[LIBSPECTRUM_IDE_SLAVE].error = 0x01;
373 else
374 chn->drive[LIBSPECTRUM_IDE_SLAVE].error = 0xff;
375
376
377 /* Device ready, no PACKET support */
378 if (chn->drive[LIBSPECTRUM_IDE_MASTER].disk)
379 chn->drive[LIBSPECTRUM_IDE_MASTER].status = 0x40;
380 else
381 chn->drive[LIBSPECTRUM_IDE_MASTER].status = 0xff;
382
383 if (chn->drive[LIBSPECTRUM_IDE_SLAVE].disk)
384 chn->drive[LIBSPECTRUM_IDE_SLAVE].status = 0x40;
385 else
386 chn->drive[LIBSPECTRUM_IDE_SLAVE].status = 0xff;
387
388 /* Feature is write-only */
389 chn->feature = 0xff;
390
391 } else {
392
393 /* If no drive is present, set all registers to 0xff */
394 chn->sector_count = 0xff;
395 chn->sector = 0xff;
396 chn->cylinder_low = 0xff;
397 chn->cylinder_high = 0xff;
398 chn->head = 0xff;
399 chn->drive[LIBSPECTRUM_IDE_MASTER].error = 0xff;
400 chn->drive[LIBSPECTRUM_IDE_SLAVE].error = 0xff;
401 chn->drive[LIBSPECTRUM_IDE_MASTER].status = 0xff;
402 chn->drive[LIBSPECTRUM_IDE_SLAVE].status = 0xff;
403 chn->feature = 0xff;
404
405 }
406
407 return LIBSPECTRUM_ERROR_NONE;
408 }
409
410 int
libspectrum_ide_read_sector_from_hdf(libspectrum_ide_drive * drv,GHashTable * cache,libspectrum_dword sector_number,libspectrum_byte * dest)411 libspectrum_ide_read_sector_from_hdf( libspectrum_ide_drive *drv,
412 GHashTable *cache, libspectrum_dword sector_number, libspectrum_byte *dest )
413 {
414 libspectrum_byte *buffer, packed_buf[512];
415
416 /* First look in the write cache */
417 buffer = g_hash_table_lookup( cache, §or_number );
418
419 /* If it's not in the write cache, read from the disk image */
420 if( !buffer ) {
421
422 long sector_position;
423
424 sector_position =
425 drv->data_offset + ( drv->sector_size * sector_number );
426
427 /* Seek to the correct file position */
428 if( fseek( drv->disk, sector_position, SEEK_SET ) ) {
429 libspectrum_print_error(
430 LIBSPECTRUM_ERROR_WARNING,
431 "Couldn't seek in HDF file\n" );
432 return 1;
433 }
434
435 /* Read the packed data into a temporary buffer */
436 if ( fread( packed_buf, 1, drv->sector_size, drv->disk ) !=
437 drv->sector_size ) {
438 libspectrum_print_error(
439 LIBSPECTRUM_ERROR_WARNING,
440 "Couldn't read from HDF file\n" );
441 return 1;
442 }
443
444 buffer = packed_buf;
445 }
446
447 /* Unpack or copy the data into the sector buffer */
448 if( drv->sector_size == 256 ) {
449
450 int i;
451
452 for( i = 0; i < 256; i++ ) {
453 dest[ i*2 ] = buffer[ i ];
454 dest[ i*2 + 1 ] = 0xff;
455 }
456
457 } else {
458 memcpy( dest, buffer, 512 );
459 }
460
461 return 0;
462 }
463
464 /* Read a sector from the HDF file */
465 static int
read_hdf(libspectrum_ide_channel * chn)466 read_hdf( libspectrum_ide_channel *chn )
467 {
468 return libspectrum_ide_read_sector_from_hdf(
469 &chn->drive[ chn->selected ], chn->cache[ chn->selected ],
470 chn->sector_number, chn->buffer );
471 }
472
473 void
libspectrum_ide_write_sector_to_hdf(libspectrum_ide_drive * drv,GHashTable * cache,libspectrum_dword sector_number,libspectrum_byte * src)474 libspectrum_ide_write_sector_to_hdf( libspectrum_ide_drive *drv, GHashTable *cache,
475 libspectrum_dword sector_number, libspectrum_byte *src )
476 {
477 libspectrum_byte *buffer;
478
479 buffer = g_hash_table_lookup( cache, §or_number );
480
481 /* Add this sector to the write cache if it's not already present */
482 if( !buffer ) {
483
484 gint *key;
485
486 key = libspectrum_new( gint, 1 );
487 buffer = libspectrum_new( libspectrum_byte, drv->sector_size );
488
489 *key = sector_number;
490 g_hash_table_insert( cache, key, buffer );
491
492 }
493
494 /* Pack or copy the data into the write cache */
495 if ( drv->sector_size == 256 ) {
496 int i;
497 for( i = 0; i < 256; i++ ) buffer[i] = src[ i * 2 ];
498 } else {
499 memcpy( buffer, src, 512 );
500 }
501 }
502
503 /* Write a sector to the HDF file */
504 static int
write_hdf(libspectrum_ide_channel * chn)505 write_hdf( libspectrum_ide_channel *chn )
506 {
507 libspectrum_ide_write_sector_to_hdf( &chn->drive[ chn->selected ], chn->cache[ chn->selected ], chn->sector_number, chn->buffer );
508 return 0;
509 }
510
511 /* Read the data register */
512 static libspectrum_byte
read_data(libspectrum_ide_channel * chn)513 read_data( libspectrum_ide_channel *chn )
514 {
515 libspectrum_byte data;
516 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
517
518 /* Meaningful data is only returned in PIO input phase */
519 if( chn->phase != LIBSPECTRUM_IDE_PHASE_PIO_IN ) return 0xff;
520
521 switch( chn->databus ) {
522
523 case LIBSPECTRUM_IDE_DATA8:
524 /* 8-bit interfaces just read the low byte */
525 data = chn->buffer[ chn->datacounter ];
526 chn->datacounter += 2;
527 break;
528
529 case LIBSPECTRUM_IDE_DATA16:
530 data = chn->buffer[ chn->datacounter++ ];
531 break;
532
533 case LIBSPECTRUM_IDE_DATA16_BYTESWAP:
534 data = chn->buffer[ chn->datacounter ^ 1 ];
535 chn->datacounter++;
536 break;
537
538 case LIBSPECTRUM_IDE_DATA16_DATA2:
539 /* 16-bit interfaces using a secondary data register for the high byte */
540 data = chn->buffer[ chn->datacounter++ ];
541 chn->data2 = chn->buffer[ chn->datacounter++ ];
542 break;
543
544 default:
545 data = 0xff; break;
546
547 }
548
549 /* Check for end of phase */
550 if( chn->datacounter >= 512 ) {
551 if( chn->sector_count ) {
552 /* more sectors to read */
553 readsector( chn );
554 } else {
555 /* all sectors done */
556 chn->phase = LIBSPECTRUM_IDE_PHASE_READY;
557 drv->status &= ~LIBSPECTRUM_IDE_STATUS_DRQ;
558 }
559 }
560
561 return data;
562 }
563
564 /* Read the IDE interface */
565 libspectrum_byte
libspectrum_ide_read(libspectrum_ide_channel * chn,libspectrum_ide_register reg)566 libspectrum_ide_read( libspectrum_ide_channel *chn,
567 libspectrum_ide_register reg )
568 {
569 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
570
571 switch( reg ) {
572
573 case LIBSPECTRUM_IDE_REGISTER_DATA: return read_data( chn );
574 case LIBSPECTRUM_IDE_REGISTER_DATA2: return chn->data2;
575 case LIBSPECTRUM_IDE_REGISTER_ERROR_FEATURE: return drv->error;
576 case LIBSPECTRUM_IDE_REGISTER_SECTOR_COUNT: return chn->sector_count;
577 case LIBSPECTRUM_IDE_REGISTER_SECTOR: return chn->sector;
578 case LIBSPECTRUM_IDE_REGISTER_CYLINDER_LOW: return chn->cylinder_low;
579 case LIBSPECTRUM_IDE_REGISTER_CYLINDER_HIGH: return chn->cylinder_high;
580 case LIBSPECTRUM_IDE_REGISTER_HEAD_DRIVE: return chn->head;
581 case LIBSPECTRUM_IDE_REGISTER_COMMAND_STATUS:
582 if (!drv->disk) return 0x00;
583 else return drv->status;
584 break;
585
586 }
587
588 return 0xff;
589 }
590
591
592 /* Write the data register */
593 static void
write_data(libspectrum_ide_channel * chn,libspectrum_byte data)594 write_data( libspectrum_ide_channel *chn, libspectrum_byte data )
595 {
596 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
597
598 /* Data register can only be written in PIO output phase */
599 if( chn->phase != LIBSPECTRUM_IDE_PHASE_PIO_OUT ) return;
600
601 switch( chn->databus ) {
602
603 case LIBSPECTRUM_IDE_DATA8:
604 /* 8-bit interfaces just write the low byte */
605 chn->buffer[ chn->datacounter ] = data;
606 chn->datacounter += 2;
607 break;
608
609 case LIBSPECTRUM_IDE_DATA16:
610 chn->buffer[ chn->datacounter++ ] = data;
611 break;
612
613 case LIBSPECTRUM_IDE_DATA16_BYTESWAP:
614 chn->buffer[ chn->datacounter ^ 1 ] = data;
615 chn->datacounter++;
616 break;
617
618 case LIBSPECTRUM_IDE_DATA16_DATA2:
619 /* 16-bit interfaces using a secondary data register for the high byte */
620 chn->buffer[ chn->datacounter++ ] = data;
621 chn->buffer[ chn->datacounter++ ] = chn->data2;
622 break;
623 }
624
625 /* Check for end of phase */
626 if( chn->datacounter >= 512 ) {
627
628 /* Write data to disk */
629 if ( write_hdf( chn ) ) {
630 drv->status |= LIBSPECTRUM_IDE_STATUS_ERR;
631 drv->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_UNC;
632 }
633
634 if( chn->sector_count ) {
635 /* more sectors to write */
636 writesector( chn );
637 } else {
638 /* all sectors done */
639 chn->phase = LIBSPECTRUM_IDE_PHASE_READY;
640 drv->status &= ~LIBSPECTRUM_IDE_STATUS_DRQ;
641 }
642 }
643
644 }
645
646 /* Seek to the addressed sector */
647 static libspectrum_error
seek(libspectrum_ide_channel * chn)648 seek( libspectrum_ide_channel *chn )
649 {
650 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
651 int sectornumber;
652 int next_head;
653
654 /* Calculate sector number, depending upon LBA/CHS mode. */
655 if( chn->head & LIBSPECTRUM_IDE_HEAD_LBA ) {
656
657 sectornumber = ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD << 24 ) +
658 ( chn->cylinder_high << 16 ) +
659 ( chn->cylinder_low << 8 ) +
660 ( chn->sector );
661
662 } else {
663
664 int cylinder = ( chn->cylinder_high << 8 ) | ( chn->cylinder_low );
665 int head = ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD );
666 int sector = ( chn->sector - 1 );
667
668 if( cylinder >= drv->cylinders || head >= drv->heads ||
669 sector < 0 || sector >= drv->sectors ) {
670 sectornumber = -1;
671 } else {
672 sectornumber =
673 ( ( ( cylinder * drv->heads ) + head ) * drv->sectors ) + sector;
674 }
675
676 }
677
678 /* Seek to the correct position */
679 if( sectornumber < 0 ||
680 sectornumber >= ( drv->cylinders * drv->heads * drv->sectors ) ) {
681 drv->status |= LIBSPECTRUM_IDE_STATUS_ERR;
682 drv->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_IDNF;
683 return LIBSPECTRUM_ERROR_UNKNOWN;
684 }
685
686 chn->sector_number = sectornumber;
687
688 /* advance registers to next sector, for multiple sector accesses */
689 chn->sector_count--;
690 if (!chn->sector_count) return LIBSPECTRUM_ERROR_NONE;
691
692 if( chn->head & LIBSPECTRUM_IDE_HEAD_LBA ) {
693
694 /* increment using LBA scheme */
695 chn->sector = ( chn->sector + 1 ) & 0xff;
696 if( !chn->sector ) {
697 chn->cylinder_low = ( chn->cylinder_low + 1 ) & 0xff;
698 if( !chn->cylinder_low ) {
699 chn->cylinder_high = ( chn->cylinder_high + 1 ) & 0xff;
700 if( !chn->cylinder_high ) {
701 next_head = ( ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD ) + 1 ) &
702 LIBSPECTRUM_IDE_HEAD_HEAD;
703 chn->head = ( chn->head & ~LIBSPECTRUM_IDE_HEAD_HEAD ) | next_head;
704 }
705 }
706 }
707
708 } else {
709
710 /* increment using CHS scheme */
711 chn->sector = ( chn->sector % drv->sectors ) + 1;
712 /* NB sector number is 1-based */
713 if( chn->sector == 1 ) {
714 next_head = ( ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD ) + 1 )
715 % drv->heads;
716 chn->head = ( chn->head & ~LIBSPECTRUM_IDE_HEAD_HEAD ) | next_head;
717 if( !next_head ) {
718 chn->cylinder_low = ( chn->cylinder_low + 1 ) & 0xff;
719 if( !chn->cylinder_low ) {
720 chn->cylinder_high++;
721 }
722 }
723 }
724
725 }
726
727 return LIBSPECTRUM_ERROR_NONE;
728 }
729
730 /* Execute the IDENTIFY DEVICE command */
731 static void
identifydevice(libspectrum_ide_channel * chn)732 identifydevice( libspectrum_ide_channel *chn )
733 {
734 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
735 unsigned long sector_count = drv->cylinders * drv->heads * drv->sectors;
736
737 /* Clear sector buffer and copy in HDF identity information */
738 memset( &chn->buffer[0], 0, 512 );
739 memcpy( &chn->buffer[0], &drv->hdf.drive_identity[0], 0x6a );
740
741 /* Fill in fields that lie beyond the end of the HDF header */
742 /* Field validity */
743 /* TODO: handle drives that exceed the limits of the CHS scheme
744 (possibly determining their actual size from HDF file size);
745 in this case, words 54-58 will be flagged as invalid */
746 /* 0x01 = 'words 54-58 are valid' */
747 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_FIELD_VALIDITY, 0x01 );
748 /* Number of current logical cylinders */
749 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_CURRENT_CYLINDERS,
750 drv->cylinders );
751 /* Number of current logical heads */
752 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_CURRENT_HEADS,
753 drv->heads );
754 /* Number of current logical sectors per logical track */
755 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_CURRENT_SECTORS,
756 drv->sectors );
757 /* Current capacity in sectors */
758 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_CURRENT_CAPACITY_LOW,
759 ( sector_count & 0x0000ffff ) );
760 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_CURRENT_CAPACITY_HI,
761 ( sector_count & 0xffff0000 ) >> 16 );
762
763 /* Total number of user addressable sectors;
764 only defined if LBA supported */
765 if( GET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_CAPABILITIES ) &
766 0x0200 ) {
767 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_TOTAL_SECTORS_LOW,
768 ( sector_count & 0x0000ffff ) );
769 SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_TOTAL_SECTORS_HI,
770 ( sector_count & 0xffff0000 ) >> 16 );
771 }
772
773 /* prevent read_data from trying to read from disk after identity block
774 is completely read in */
775 chn->sector_count = 0;
776
777 /* Initiate the PIO input phase */
778 chn->phase = LIBSPECTRUM_IDE_PHASE_PIO_IN;
779 drv->status |= LIBSPECTRUM_IDE_STATUS_DRQ;
780 chn->datacounter = 0;
781 }
782
783 /* Execute the READ SECTOR command */
784 static void
readsector(libspectrum_ide_channel * chn)785 readsector( libspectrum_ide_channel *chn )
786 {
787 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
788
789 if( seek( chn ) ) return;
790
791 /* Read data from disk */
792 if( read_hdf( chn ) ) {
793
794 drv->status |= LIBSPECTRUM_IDE_STATUS_ERR;
795 drv->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_UNC;
796
797 } else {
798
799 /* Initiate the PIO input phase */
800 chn->phase = LIBSPECTRUM_IDE_PHASE_PIO_IN;
801 drv->status |= LIBSPECTRUM_IDE_STATUS_DRQ;
802 chn->datacounter = 0;
803
804 }
805 }
806
807 /* Execute the WRITE SECTOR command */
808 static void
writesector(libspectrum_ide_channel * chn)809 writesector( libspectrum_ide_channel *chn )
810 {
811 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
812
813 if( seek( chn ) ) return;
814
815 /* Initiate the PIO output phase */
816 chn->phase = LIBSPECTRUM_IDE_PHASE_PIO_OUT;
817 drv->status |= LIBSPECTRUM_IDE_STATUS_DRQ;
818 chn->datacounter = 0;
819 }
820
821 /* Execute the INITIALIZE DEVICE PARAMETERS command */
822 static void
init_device_params(libspectrum_ide_channel * chn)823 init_device_params( libspectrum_ide_channel *chn )
824 {
825 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
826 int size;
827
828 if ( !chn->sector_count ) {
829 drv->status |= LIBSPECTRUM_IDE_STATUS_ERR;
830 drv->error = LIBSPECTRUM_IDE_ERROR_ABRT;
831 return;
832 }
833
834 size = drv->heads * drv->sectors * drv->cylinders;
835
836 if ( size > 16514064 ) size = 16514064;
837
838 drv->heads = ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD ) + 1;
839 drv->sectors = chn->sector_count;
840 drv->cylinders = size / (drv->heads * drv->sectors);
841
842 /* maybe this would be better moved to identify device */
843 if ( drv->cylinders > 65535 ) drv->cylinders = 65535;
844
845 chn->phase = LIBSPECTRUM_IDE_PHASE_READY;
846
847 drv->error = LIBSPECTRUM_IDE_ERROR_OK;
848 drv->status &= ~( LIBSPECTRUM_IDE_STATUS_ERR | LIBSPECTRUM_IDE_STATUS_BSY |
849 LIBSPECTRUM_IDE_STATUS_DRQ );
850 drv->status |= LIBSPECTRUM_IDE_STATUS_DRDY;
851 }
852
853 /* Execute a command */
854 static void
execute_command(libspectrum_ide_channel * chn,libspectrum_byte data)855 execute_command( libspectrum_ide_channel *chn, libspectrum_byte data )
856 {
857 libspectrum_ide_drive *drv = &chn->drive[ chn->selected ];
858
859 if( !drv->disk ) return;
860 chn->phase = LIBSPECTRUM_IDE_PHASE_READY;
861
862 /* Clear error conditions */
863 drv->error = LIBSPECTRUM_IDE_ERROR_OK;
864 drv->status &= ~(LIBSPECTRUM_IDE_STATUS_ERR | LIBSPECTRUM_IDE_STATUS_BSY);
865 drv->status |= LIBSPECTRUM_IDE_STATUS_DRDY;
866
867 /* Perform command */
868 switch( data ) {
869
870 case LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_RETRY: readsector( chn ); break;
871 case LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_NORETRY: readsector( chn ); break;
872 case LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_RETRY: writesector( chn ); break;
873 case LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_NORETRY: writesector( chn ); break;
874 case LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATA: identifydevice( chn ); break;
875 case LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATAPI: identifydevice( chn ); break;
876 case LIBSPECTRUM_IDE_COMMAND_INITIALIZE_DEVICE_PARAMETERS:
877 init_device_params( chn ); break;
878
879 /* Unknown/unsupported commands */
880 default:
881 drv->status |= LIBSPECTRUM_IDE_STATUS_ERR;
882 drv->error = LIBSPECTRUM_IDE_ERROR_ABRT;
883 }
884 }
885
886
887 /* Write the IDE interface */
888 void
libspectrum_ide_write(libspectrum_ide_channel * chn,libspectrum_ide_register reg,libspectrum_byte data)889 libspectrum_ide_write( libspectrum_ide_channel *chn,
890 libspectrum_ide_register reg, libspectrum_byte data )
891 {
892 switch( reg ) {
893 case LIBSPECTRUM_IDE_REGISTER_DATA: write_data( chn, data ); break;
894 case LIBSPECTRUM_IDE_REGISTER_DATA2: chn->data2 = data; break;
895 case LIBSPECTRUM_IDE_REGISTER_ERROR_FEATURE: chn->feature = data; break;
896 case LIBSPECTRUM_IDE_REGISTER_SECTOR_COUNT: chn->sector_count = data; break;
897 case LIBSPECTRUM_IDE_REGISTER_SECTOR: chn->sector = data; break;
898 case LIBSPECTRUM_IDE_REGISTER_CYLINDER_LOW: chn->cylinder_low = data; break;
899 case LIBSPECTRUM_IDE_REGISTER_CYLINDER_HIGH:
900 chn->cylinder_high = data; break;
901
902 case LIBSPECTRUM_IDE_REGISTER_HEAD_DRIVE:
903 chn->head = data;
904 chn->selected = chn->head & LIBSPECTRUM_IDE_HEAD_DEV ?
905 LIBSPECTRUM_IDE_SLAVE : LIBSPECTRUM_IDE_MASTER;
906 break;
907
908 case LIBSPECTRUM_IDE_REGISTER_COMMAND_STATUS:
909 execute_command( chn, data ); break;
910 }
911 }
912