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, &sector_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, &sector_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