1 /*
2 Hatari - floppy_ipf.c
3
4 This file is distributed under the GNU General Public License, version 2 or at
5 your option any later version. Read the file gpl.txt for details.
6
7 IPF disk image support.
8
9 IPF files are handled using the capsimage library, which emulates the FDC
10 at low level and allows to read complex protections.
11
12 RAW stream files made with KryoFlux board or CT RAW dumped with an Amiga are also handled
13 by the capsimage library.
14 */
15 const char floppy_ipf_fileid[] = "Hatari floppy_ipf.c : " __DATE__ " " __TIME__;
16
17 #include "main.h"
18 #include "file.h"
19 #include "floppy.h"
20 #include "floppy_ipf.h"
21 #include "fdc.h"
22 #include "log.h"
23 #include "memorySnapShot.h"
24 #include "screen.h"
25 #include "video.h"
26 #include "m68000.h"
27 #include "cycles.h"
28
29 #include <string.h>
30
31 #ifdef HAVE_CAPSIMAGE
32 #if CAPSIMAGE_VERSION == 5
33 #include <caps5/CapsLibAll.h>
34 #else
35 #include <caps/fdc.h>
36 #define CAPS_LIB_RELEASE 4
37 #define CAPS_LIB_REVISION 2
38 #endif
39 /* Macro to check release and revision */
40 #define CAPS_LIB_REL_REV ( CAPS_LIB_RELEASE * 100 + CAPS_LIB_REVISION )
41 #endif
42
43
44 /* To handle RAW stream images with one file per track/side */
45 #define IPF_MAX_TRACK_RAW_STREAM_IMAGE 84 /* track number can be 0 .. 83 */
46 #define IPF_MAX_SIDE_RAW_STREAM_IMAGE 2 /* side number can be 0 or 1 */
47
48 struct
49 {
50 int TrackSize;
51 Uint8 *TrackData;
52 } IPF_RawStreamImage[ MAX_FLOPPYDRIVES ][ IPF_MAX_TRACK_RAW_STREAM_IMAGE ][ IPF_MAX_SIDE_RAW_STREAM_IMAGE ];
53
54
55
56 typedef struct
57 {
58 #ifdef HAVE_CAPSIMAGE
59 Uint32 CapsLibRelease;
60 Uint32 CapsLibRevision;
61
62 struct CapsFdc Fdc; /* Fdc state */
63 struct CapsDrive Drive[ MAX_FLOPPYDRIVES ]; /* Physical drives */
64 CapsLong CapsImage[ MAX_FLOPPYDRIVES ]; /* Image Id or -1 if drive empty */
65 CapsLong CapsImageType[ MAX_FLOPPYDRIVES ]; /* ImageType or -1 if not known */
66
67 int Rev_Track[ MAX_FLOPPYDRIVES ]; /* Needed to handle CAPSSetRevolution for type II/III commands */
68 int Rev_Side[ MAX_FLOPPYDRIVES ];
69
70 bool DriveEnabled[ MAX_FLOPPYDRIVES ];/* Is drive ON or OFF */
71 bool DoubleSided[ MAX_FLOPPYDRIVES ];/* Is drive double sided or not */
72 #endif
73
74 Sint64 FdcClock; /* Current value of CyclesGlobalClockCounter */
75 } IPF_STRUCT;
76
77
78 static IPF_STRUCT IPF_State; /* All variables related to the IPF support */
79
80
81 static bool IPF_Eject_RawStreamImage ( int Drive );
82 #ifdef HAVE_CAPSIMAGE
83 static char *IPF_FilenameFindTrackSide (char *FileName);
84 static bool IPF_Insert_RawStreamImage ( int Drive );
85
86 static void IPF_CallBack_Trk ( struct CapsFdc *pc , CapsULong State );
87 static void IPF_CallBack_Irq ( struct CapsFdc *pc , CapsULong State );
88 static void IPF_CallBack_Drq ( struct CapsFdc *pc , CapsULong State );
89 static void IPF_Drive_Update_Enable_Side ( void );
90 static void IPF_FDC_LogCommand ( Uint8 Command );
91 #endif
92
93
94
95
96 /*-----------------------------------------------------------------------*/
97 /**
98 * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
99 * We must take care of whether Hatari was compiled with IPF support of not
100 * when saving/restoring snapshots to avoid incompatibilies.
101 */
IPF_MemorySnapShot_Capture(bool bSave)102 void IPF_MemorySnapShot_Capture(bool bSave)
103 {
104 int StructSize;
105 int Drive;
106 int Track , Side;
107 int TrackSize;
108 Uint8 *p;
109
110
111 if ( bSave ) /* Saving snapshot */
112 {
113 StructSize = sizeof ( IPF_State ); /* 0 if HAVE_CAPSIMAGE is not defined */
114 MemorySnapShot_Store(&StructSize, sizeof(StructSize));
115 if ( StructSize > 0 )
116 {
117 MemorySnapShot_Store(&IPF_State, sizeof(IPF_State));
118
119 /* Save the content of IPF_RawStreamImage[] */
120 for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
121 for ( Track=0 ; Track<IPF_MAX_TRACK_RAW_STREAM_IMAGE ; Track++ )
122 for ( Side=0 ; Side<IPF_MAX_SIDE_RAW_STREAM_IMAGE ; Side++ )
123 {
124 TrackSize = IPF_RawStreamImage[ Drive ][ Track ][Side].TrackSize;
125 // fprintf ( stderr , "IPF : save raw stream drive=%d track=%d side=%d : %d\n" , Drive , Track , Side , TrackSize );
126 MemorySnapShot_Store(&TrackSize, sizeof(TrackSize));
127 if ( TrackSize > 0 )
128 MemorySnapShot_Store(IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData, TrackSize);
129 }
130 }
131 }
132
133 else /* Restoring snapshot */
134 {
135 MemorySnapShot_Store(&StructSize, sizeof(StructSize));
136 if ( ( StructSize == 0 ) && ( sizeof ( IPF_State ) > 0 ) )
137 {
138 Log_AlertDlg(LOG_ERROR, "Hatari built with IPF floppy support, but no IPF data in memory snapshot -> skip");
139 return; /* Continue restoring the rest of the memory snapshot */
140 }
141 else if ( ( StructSize > 0 ) && ( sizeof ( IPF_State ) == 0 ) )
142 {
143 Log_AlertDlg(LOG_ERROR, "Memory snapshot with IPF floppy data, but Hatari built without IPF support -> skip");
144 MemorySnapShot_Skip( StructSize ); /* Ignore the IPF data */
145 return; /* Continue restoring the rest of the memory snapshot */
146 }
147 else if ( ( StructSize > 0 ) && ( StructSize != sizeof ( IPF_State ) ) )
148 {
149 Log_AlertDlg(LOG_ERROR, "Memory snapshot IPF floppy data incompatible with this Hatari version -> skip");
150 MemorySnapShot_Skip( StructSize ); /* Ignore the IPF data */
151 return; /* Continue restoring the rest of the memory snapshot */
152 }
153
154 if ( StructSize > 0 )
155 {
156 MemorySnapShot_Store(&IPF_State, sizeof(IPF_State));
157
158 #ifdef HAVE_CAPSIMAGE
159 /* For IPF structures, we need to update some pointers in Fdc/Drive/CapsImage */
160 /* drive : PUBYTE trackbuf, PUDWORD timebuf */
161 /* fdc : PCAPSDRIVE driveprc, PCAPSDRIVE drive, CAPSFDCHOOK callback functions */
162 IPF_State.Fdc.drive = IPF_State.Drive; /* Connect drives array to the FDC */
163 if ( IPF_State.Fdc.driveprc != NULL ) /* Recompute active drive's pointer */
164 IPF_State.Fdc.driveprc = IPF_State.Fdc.drive + IPF_State.Fdc.driveact;
165
166 CAPSFdcInvalidateTrack ( &IPF_State.Fdc , 0 ); /* Invalidate buffered track data for drive 0 */
167 CAPSFdcInvalidateTrack ( &IPF_State.Fdc , 1 ); /* Invalidate buffered track data for drive 1 */
168
169 /* Set callback functions */
170 IPF_State.Fdc.cbirq = IPF_CallBack_Irq;
171 IPF_State.Fdc.cbdrq = IPF_CallBack_Drq;
172 IPF_State.Fdc.cbtrk = IPF_CallBack_Trk;
173 #endif
174
175 /* Call IPF_Insert to recompute IPF_State.CapsImage[ Drive ] */
176 for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
177 if ( EmulationDrives[Drive].ImageType == FLOPPY_IMAGE_TYPE_IPF )
178 if ( IPF_Insert ( Drive , EmulationDrives[Drive].pBuffer , EmulationDrives[Drive].nImageBytes ) == false )
179 {
180 Log_AlertDlg(LOG_ERROR, "Error restoring IPF image %s in drive %d" ,
181 EmulationDrives[Drive].sFileName , Drive );
182 return;
183 }
184
185 /* Restore the content of IPF_RawStreamImage[] */
186 /* NOTE : IPF_Insert above might already have read the raw tracks from disk, */
187 /* so we free all those tracks and read them again from the snapshot instead */
188 /* (not very efficient, but it's a rare case anyway) */
189 for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
190 {
191 IPF_Eject_RawStreamImage ( Drive );
192 for ( Track=0 ; Track<IPF_MAX_TRACK_RAW_STREAM_IMAGE ; Track++ )
193 for ( Side=0 ; Side<IPF_MAX_SIDE_RAW_STREAM_IMAGE ; Side++ )
194 {
195 MemorySnapShot_Store(&TrackSize, sizeof(TrackSize));
196 // fprintf ( stderr , "IPF : restore raw stream drive=%d track=%d side=%d : %d\n" , Drive , Track , Side , TrackSize );
197 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackSize = TrackSize;
198 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData = NULL;
199 if ( TrackSize > 0 )
200 {
201 p = malloc ( TrackSize );
202 if ( p == NULL )
203 {
204 Log_AlertDlg(LOG_ERROR, "Error restoring IPF raw track drive %d track %d side %d size %d" ,
205 Drive, Track, Side , TrackSize );
206 return;
207 }
208 MemorySnapShot_Store(p, TrackSize);
209 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData = p;
210 }
211 }
212 }
213 Log_Printf ( LOG_DEBUG , "ipf load ok\n" );
214 }
215 }
216 }
217
218
219
220
221 /*-----------------------------------------------------------------------*/
222 /**
223 * Does filename end with a .IPF or .RAW or .CTR extension ? If so, return true.
224 * .RAW and .CTR support requires caps lib >= 5.1
225 */
IPF_FileNameIsIPF(const char * pszFileName,bool bAllowGZ)226 bool IPF_FileNameIsIPF(const char *pszFileName, bool bAllowGZ)
227 {
228 return ( File_DoesFileExtensionMatch(pszFileName,".ipf" )
229 || ( bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".ipf.gz") )
230 #if CAPS_LIB_REL_REV >= 501
231 || File_DoesFileExtensionMatch(pszFileName,".raw" )
232 || ( bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".raw.gz") )
233 || File_DoesFileExtensionMatch(pszFileName,".ctr" )
234 || ( bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".ctr.gz") )
235 #endif
236 );
237 }
238
239
240 /*
241 * Return a pointer to the part "tt.s.raw" at the end of the filename
242 * (there can be an extra suffix to ignore if the file is compressed).
243 * If we found a string where "tt" and "s" are digits, then we return
244 * a pointer to this string.
245 * If not found, we return NULL
246 */
247 #ifdef HAVE_CAPSIMAGE
IPF_FilenameFindTrackSide(char * FileName)248 static char *IPF_FilenameFindTrackSide (char *FileName)
249 {
250 char ext[] = ".raw";
251 int len;
252 char *p;
253
254 len = strlen ( FileName );
255 len -= strlen ( ext );
256
257 while ( len >= 4 ) /* need at least 4 chars for "tt.s" */
258 {
259 if ( strncasecmp ( ext , FileName + len , strlen ( ext ) ) == 0 )
260 {
261 p = FileName + len - 4;
262 if ( isdigit( p[0] ) && isdigit( p[1] )
263 && ( p[2] == '.' ) && isdigit( p[3] ) )
264 return p;
265 }
266
267 len--;
268 }
269
270 return NULL;
271 }
272 #endif
273
274
275 /*-----------------------------------------------------------------------*/
276 /**
277 * Load .IPF file into memory, set number of bytes loaded and return a pointer
278 * to the buffer.
279 */
IPF_ReadDisk(int Drive,const char * pszFileName,long * pImageSize,int * pImageType)280 Uint8 *IPF_ReadDisk(int Drive, const char *pszFileName, long *pImageSize, int *pImageType)
281 {
282 #ifndef HAVE_CAPSIMAGE
283 Log_AlertDlg(LOG_ERROR, "Hatari built without IPF support -> can't handle floppy image");
284 return NULL;
285
286 #else
287 Uint8 *pIPFFile;
288
289 *pImageSize = 0;
290
291 /* Just load directly a buffer, and set ImageSize accordingly */
292 pIPFFile = File_Read(pszFileName, pImageSize, NULL);
293 if (!pIPFFile)
294 {
295 *pImageSize = 0;
296 return NULL;
297 }
298
299 *pImageType = FLOPPY_IMAGE_TYPE_IPF;
300 return pIPFFile;
301 #endif
302 }
303
304
305 /*-----------------------------------------------------------------------*/
306 /**
307 * Save .IPF file from memory buffer. Returns true is all OK.
308 */
IPF_WriteDisk(int Drive,const char * pszFileName,Uint8 * pBuffer,int ImageSize)309 bool IPF_WriteDisk(int Drive, const char *pszFileName, Uint8 *pBuffer, int ImageSize)
310 {
311 /* saving is not supported for IPF files */
312 return false;
313 }
314
315
316
317
318 /*
319 * Init the FDC and the drives used to handle IPF images
320 */
IPF_Init(void)321 bool IPF_Init ( void )
322 {
323 #ifndef HAVE_CAPSIMAGE
324 return true;
325
326 #else
327 int i;
328 struct CapsVersionInfo caps_vi;
329
330 Log_Printf ( LOG_DEBUG , "IPF : IPF_Init\n" );
331
332 if ( CAPSInit() != imgeOk )
333 {
334 Log_Printf ( LOG_ERROR , "IPF : Could not initialize the capsimage library\n" );
335 return false;
336 }
337
338 if ( CAPSGetVersionInfo ( &caps_vi , 0 ) != imgeOk )
339 {
340 Log_Printf ( LOG_ERROR , "IPF : CAPSVersionInfo failed\n" );
341 return false;
342 }
343 Log_Printf ( LOG_INFO , "IPF : capsimage library version release=%d revision=%d\n" , (int)caps_vi.release , (int)caps_vi.revision );
344 IPF_State.CapsLibRelease = caps_vi.release;
345 IPF_State.CapsLibRevision = caps_vi.revision;
346
347 /* Default values for each physical drive */
348 memset ( IPF_State.Drive , 0 , sizeof ( IPF_State.Drive ) );
349 for ( i=0 ; i < MAX_FLOPPYDRIVES ; i++ )
350 {
351 IPF_State.Drive[ i ].type = sizeof ( struct CapsDrive );
352 IPF_State.Drive[ i ].rpm = CAPSDRIVE_35DD_RPM;
353 IPF_State.Drive[ i ].maxtrack = CAPSDRIVE_35DD_HST;
354
355 IPF_State.Rev_Track[ i ] = -1;
356 IPF_State.Rev_Side[ i ] = -1;
357
358 IPF_State.DriveEnabled[ i ] = true;
359 IPF_State.DoubleSided[ i ] = true;
360
361 IPF_State.CapsImageType[ i ] = -1;
362 }
363
364 /* Init FDC with 2 physical drives */
365 memset ( &IPF_State.Fdc , 0 , sizeof ( IPF_State.Fdc ) );
366 IPF_State.Fdc.type = sizeof( struct CapsFdc );
367 IPF_State.Fdc.model = cfdcmWD1772;
368 IPF_State.Fdc.drive = IPF_State.Drive;
369 IPF_State.Fdc.drivecnt = MAX_FLOPPYDRIVES;
370
371 if ( CAPSFdcInit ( &IPF_State.Fdc ) != imgeOk)
372 {
373 Log_Printf ( LOG_ERROR , "IPF : CAPSFdcInit failed\n" );
374 return false;
375 }
376
377 /* 2 drives by default */
378 IPF_State.Fdc.drivemax = MAX_FLOPPYDRIVES;
379 /* Update drives' state in case we have some drives ON or OFF in config or parameters */
380 IPF_Drive_Update_Enable_Side ();
381
382 /* FDC clock */
383 IPF_State.Fdc.clockfrq = 8000000;
384
385 /* Set callback functions */
386 IPF_State.Fdc.cbirq = IPF_CallBack_Irq;
387 IPF_State.Fdc.cbdrq = IPF_CallBack_Drq;
388 IPF_State.Fdc.cbtrk = IPF_CallBack_Trk;
389
390 CAPSFdcReset ( &IPF_State.Fdc );
391
392 return true;
393 #endif
394 }
395
396
397
398
399 /*
400 * Exit
401 */
IPF_Exit(void)402 void IPF_Exit ( void )
403 {
404 #ifndef HAVE_CAPSIMAGE
405 #else
406 CAPSExit();
407 #endif
408 }
409
410
411
412
413 /*
414 * Init the ressources to handle the IPF image inserted into a drive (0=A: 1=B:)
415 */
IPF_Insert(int Drive,Uint8 * pImageBuffer,long ImageSize)416 bool IPF_Insert ( int Drive , Uint8 *pImageBuffer , long ImageSize )
417 {
418 #ifndef HAVE_CAPSIMAGE
419 return false;
420
421 #else
422 CapsLong ImageId;
423 CapsLong ImageType;
424 const char *ImageTypeStr;
425 bool Type_OK;
426
427
428 ImageId = CAPSAddImage();
429 if ( ImageId < 0 )
430 {
431 Log_Printf ( LOG_ERROR , "IPF : error CAPSAddImage\n" );
432 return false;
433 }
434
435 #if CAPS_LIB_REL_REV >= 501
436 ImageType = CAPSGetImageTypeMemory ( pImageBuffer , ImageSize );
437 if ( ImageType == citError )
438 {
439 Log_Printf ( LOG_ERROR , "IPF : error CAPSGetImageTypeMemory\n" );
440 CAPSRemImage ( ImageId ) ;
441 return false;
442 }
443 else if ( ImageType == citUnknown )
444 {
445 Log_Printf ( LOG_ERROR , "IPF : unknown image type\n" );
446 CAPSRemImage ( ImageId ) ;
447 return false;
448 }
449
450 Type_OK = true;
451 switch ( ImageType ) {
452 case citIPF: ImageTypeStr = "IPF"; break;
453 case citCTRaw: ImageTypeStr = "CT RAW"; break;
454 case citKFStream: ImageTypeStr = "KF STREAM" ; break;
455 case citDraft: ImageTypeStr = "DRAFT" ; break;
456 default : ImageTypeStr = "NOT SUPPORTED\n";
457 Type_OK = false;
458 }
459 Log_Printf ( LOG_INFO , "IPF : IPF_Insert drive=%d buf=%p size=%ld imageid=%d type=%s\n" , Drive , pImageBuffer , ImageSize , ImageId , ImageTypeStr );
460
461 if ( !Type_OK )
462 {
463 CAPSRemImage ( ImageId ) ;
464 return false;
465 }
466
467
468 /* Special case for RAW stream image, we load all the tracks now */
469 if ( ImageType == citKFStream )
470 {
471 if ( IPF_Insert_RawStreamImage ( Drive ) == false )
472 {
473 Log_Printf ( LOG_ERROR , "IPF : can't load raw stream files\n" );
474 CAPSRemImage ( ImageId ) ;
475 return false;
476 }
477 }
478
479 #else
480 ImageType = -1;
481 #endif
482
483 if ( CAPSLockImageMemory ( ImageId , pImageBuffer , (CapsULong)ImageSize , DI_LOCK_MEMREF ) == imgeOk )
484 {
485 struct CapsImageInfo cii;
486 int i;
487
488 /* Print some debug infos */
489 if ( CAPSGetImageInfo ( &cii , ImageId ) == imgeOk )
490 {
491 printf("Type: %d\n", (int)cii.type);
492 printf("Release: %d\n", (int)cii.release);
493 printf("Revision: %d\n", (int)cii.revision);
494 printf("Min Cylinder: %d\n", (int)cii.mincylinder);
495 printf("Max Cylinder: %d\n", (int)cii.maxcylinder);
496 printf("Min Head: %d\n", (int)cii.minhead);
497 printf("Max Head: %d\n", (int)cii.maxhead);
498 printf("Creation Date: %04d/%02d/%02d %02d:%02d:%02d.%03d\n", (int)cii.crdt.year, (int)cii.crdt.month, (int)cii.crdt.day, (int)cii.crdt.hour, (int)cii.crdt.min, (int)cii.crdt.sec, (int)cii.crdt.tick);
499 printf("Platforms:");
500 for (i = 0; i < CAPS_MAXPLATFORM; i++)
501 if (cii.platform[i] != ciipNA)
502 printf ( " %s" , CAPSGetPlatformName(cii.platform[i]) );
503 printf("\n");
504
505 /* Some IPF disks are not correctly supported yet : display a warning */
506 if ( (int)cii.release == 3222 ) /* Sundog */
507 Log_AlertDlg ( LOG_INFO , "'Sundog' is not correctly supported yet, it requires write access." );
508 else if ( (int)cii.release == 3058 ) /* Lethal Xcess */
509 Log_AlertDlg ( LOG_INFO , "'Lethal Xcess' is not correctly supported yet, protection will fail" );
510 }
511 }
512 else
513 {
514 CAPSRemImage ( ImageId ) ;
515 return false;
516 }
517
518 if ( CAPSLoadImage ( ImageId , DI_LOCK_DENALT | DI_LOCK_DENVAR | DI_LOCK_UPDATEFD ) != imgeOk )
519 {
520 Log_Printf ( LOG_ERROR , "IPF : error CAPSLoadImage\n" );
521 CAPSUnlockImage ( ImageId );
522 CAPSRemImage ( ImageId ) ;
523 return false;
524 }
525
526
527 IPF_State.CapsImage[ Drive ] = ImageId;
528 IPF_State.CapsImageType[ Drive ] = ImageType;
529
530 IPF_State.Drive[ Drive ].diskattr |= CAPSDRIVE_DA_IN; /* Disk inserted, keep the value for "write protect" */
531
532 CAPSFdcInvalidateTrack ( &IPF_State.Fdc , Drive ); /* Invalidate previous buffered track data for drive, if any */
533
534 IPF_State.Rev_Track[ Drive ] = -1; /* Invalidate previous track/side to handle revolution's count */
535 IPF_State.Rev_Side[ Drive ] = -1;
536
537 return true;
538 #endif
539 }
540
541
542 /*
543 * Load all the raw stream files for all tracks/sides of a dump.
544 * We use the filename of the raw file in drive 'Drive' as a template
545 * where we replace track and side will all the possible values.
546 */
547 #ifdef HAVE_CAPSIMAGE
IPF_Insert_RawStreamImage(int Drive)548 static bool IPF_Insert_RawStreamImage ( int Drive )
549 {
550 int Track , Side;
551 char TrackFileName[ FILENAME_MAX ];
552 char *TrackSide_pointer;
553 char TrackSide_buf[ 4 + 1 ]; /* "tt.s" + \0 */
554 int TrackCount;
555 int TrackCount_0 , TrackCount_1;
556 Uint8 *p;
557 long Size;
558
559
560 return true; /* This function is not used for now, always return true */
561 /* Ensure the previous tracks are removed from memory */
562 IPF_Eject_RawStreamImage ( Drive );
563
564
565 /* Get the path+filename of the raw file that was inserted in 'Drive' */
566 /* then parse it to find the part with track/side */
567 strcpy ( TrackFileName , ConfigureParams.DiskImage.szDiskFileName[Drive] );
568
569 TrackSide_pointer = IPF_FilenameFindTrackSide ( TrackFileName );
570 if ( TrackSide_pointer == NULL )
571 {
572 Log_Printf ( LOG_ERROR , "IPF : error parsing track/side in raw filename\n" );
573 return false;
574 }
575
576 /* We try to load all the tracks for all the sides */
577 /* We ignore errors, as some tracks/side can really be missing from the image dump */
578 TrackCount = 0;
579 TrackCount_0 = 0;
580 TrackCount_1 = 0;
581 for ( Track=0 ; Track<IPF_MAX_TRACK_RAW_STREAM_IMAGE ; Track++ )
582 {
583 for ( Side=0 ; Side<IPF_MAX_SIDE_RAW_STREAM_IMAGE ; Side++ )
584 {
585 sprintf ( TrackSide_buf , "%02d.%d" , Track , Side );
586 memcpy ( TrackSide_pointer , TrackSide_buf , 4 );
587 Log_Printf ( LOG_INFO , "IPF : insert raw stream drive=%d track=%d side=%d %s\n" , Drive , Track , Side , TrackFileName );
588
589 p = File_Read ( TrackFileName , &Size , NULL);
590 if ( p )
591 {
592 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData = p;
593 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackSize = Size;
594 TrackCount++;
595 if ( Side==0 ) TrackCount_0++;
596 else TrackCount_1++;
597 }
598 else
599 {
600 Log_Printf ( LOG_INFO , "IPF : insert raw stream drive=%d track=%d side=%d %s -> not found\n" , Drive , Track , Side , TrackFileName );
601 /* File not loaded : either this track really doesn't exist or there was a system error */
602 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData = NULL;
603 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackSize = 0;
604 }
605 }
606 }
607
608
609 /* If we didn't load any track, there's a problem, we stop here */
610 if ( TrackCount == 0 )
611 {
612 Log_Printf ( LOG_WARN , "IPF : error, no raw track file could be loaded for %s\n" , ConfigureParams.DiskImage.szDiskFileName[Drive] );
613 /* Free all the tracks that were loaded so far */
614 IPF_Eject_RawStreamImage ( Drive );
615 return false;
616 }
617
618 Log_Printf ( LOG_INFO , "IPF : insert raw stream drive=%d, loaded %d tracks for side 0 and %d tracks for side 1\n", Drive, TrackCount_0, TrackCount_1 );
619
620 return true;
621 }
622 #endif
623
624
625
626 /*
627 * When ejecting a disk, free the ressources associated with an IPF image
628 */
IPF_Eject(int Drive)629 bool IPF_Eject ( int Drive )
630 {
631 #ifndef HAVE_CAPSIMAGE
632 return false;
633
634 #else
635 Log_Printf ( LOG_DEBUG , "IPF : IPF_Eject drive=%d imageid=%d\n" , Drive , IPF_State.CapsImage[ Drive ] );
636
637 CAPSFdcInvalidateTrack ( &IPF_State.Fdc , Drive ); /* Invalidate previous buffered track data for drive, if any */
638
639 if ( CAPSUnlockImage ( IPF_State.CapsImage[ Drive ] ) < 0 )
640 {
641 Log_Printf ( LOG_ERROR , "IPF : error CAPSUnlockImage\n" );
642 return false;
643 }
644
645 if ( CAPSRemImage ( IPF_State.CapsImage[ Drive ] ) < 0 )
646 {
647 Log_Printf ( LOG_ERROR , "IPF : error CAPSRemImage\n" );
648 return false;
649 }
650
651 /* Special case for RAW stream image, we must free all the tracks */
652 if ( IPF_State.CapsImageType[ Drive ] == citKFStream )
653 IPF_Eject_RawStreamImage ( Drive );
654
655 IPF_State.CapsImage[ Drive ] = -1;
656 IPF_State.CapsImageType[ Drive ] = -1;
657
658 IPF_State.Drive[ Drive ].diskattr &= ~CAPSDRIVE_DA_IN;
659
660 return true;
661 #endif
662 }
663
664
665 /*
666 * When ejecting a RAW stream image we must free all the individual tracks
667 */
IPF_Eject_RawStreamImage(int Drive)668 static bool IPF_Eject_RawStreamImage ( int Drive )
669 {
670 #ifndef HAVE_CAPSIMAGE
671 return true;
672
673 #else
674 int Track , Side;
675
676 return true; /* This function is not used for now, always return true */
677 for ( Track=0 ; Track<IPF_MAX_TRACK_RAW_STREAM_IMAGE ; Track++ )
678 for ( Side=0 ; Side<IPF_MAX_SIDE_RAW_STREAM_IMAGE ; Side++ )
679 {
680 if ( IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData != NULL )
681 {
682 Log_Printf ( LOG_DEBUG , "IPF : eject raw stream drive=%d track=%d side=%d\n" , Drive , Track , Side );
683 free ( IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData );
684 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackData = NULL;
685 IPF_RawStreamImage[ Drive ][ Track ][Side].TrackSize = 0;
686 }
687 }
688
689 return true;
690 #endif
691 }
692
693
694
695
696 /*
697 * Reset FDC state when a reset signal was received
698 */
IPF_Reset(void)699 void IPF_Reset ( void )
700 {
701 #ifdef HAVE_CAPSIMAGE
702 CAPSFdcReset ( &IPF_State.Fdc );
703
704 IPF_State.FdcClock = CyclesGlobalClockCounter;
705 #endif
706 }
707
708
709
710
711 /*
712 * Callback function used when track is changed.
713 * We need to update the track data by calling CAPSLockTrack
714 */
715 #ifdef HAVE_CAPSIMAGE
IPF_CallBack_Trk(struct CapsFdc * pc,CapsULong State)716 static void IPF_CallBack_Trk ( struct CapsFdc *pc , CapsULong State )
717 {
718 int Drive = State; /* State is the drive number in that case */
719 struct CapsDrive *pd = pc->drive+Drive; /* Current drive where the track change occurred */
720 struct CapsTrackInfoT1 cti;
721
722 cti.type=1;
723 if ( CAPSLockTrack ( &cti , IPF_State.CapsImage[ Drive ] , pd->buftrack , pd->bufside ,
724 DI_LOCK_DENALT|DI_LOCK_DENVAR|DI_LOCK_UPDATEFD|DI_LOCK_TYPE ) != imgeOk )
725 return;
726
727 LOG_TRACE(TRACE_FDC, "fdc ipf callback trk drive=%d buftrack=%d bufside=%d VBL=%d HBL=%d\n" , Drive ,
728 (int)pd->buftrack , (int)pd->bufside , nVBLs , nHBL );
729
730 pd->ttype = cti.type;
731 pd->trackbuf = cti.trackbuf;
732 pd->timebuf = cti.timebuf;
733 pd->tracklen = cti.tracklen;
734 pd->overlap = cti.overlap;
735 }
736 #endif
737
738
739
740
741 /*
742 * Callback function used when the FDC change the IRQ signal
743 */
744 #ifdef HAVE_CAPSIMAGE
IPF_CallBack_Irq(struct CapsFdc * pc,CapsULong State)745 static void IPF_CallBack_Irq ( struct CapsFdc *pc , CapsULong State )
746 {
747 LOG_TRACE(TRACE_FDC, "fdc ipf callback irq state=0x%x VBL=%d HBL=%d\n" , (int)State , nVBLs , nHBL );
748
749 if ( State )
750 FDC_SetIRQ ( FDC_IRQ_SOURCE_OTHER ); /* IRQ bit was set */
751 else
752 FDC_ClearIRQ (); /* IRQ bit was reset */
753 }
754 #endif
755
756
757
758
759 /*
760 * Callback function used when the FDC change the DRQ signal
761 * -> copy the byte to/from the DMA's FIFO if it's a read or a write to the disk
762 */
763 #ifdef HAVE_CAPSIMAGE
IPF_CallBack_Drq(struct CapsFdc * pc,CapsULong State)764 static void IPF_CallBack_Drq ( struct CapsFdc *pc , CapsULong State )
765 {
766 Uint8 Byte;
767
768 if ( State == 0 )
769 return; /* DRQ bit was reset, do nothing */
770
771 if ( FDC_DMA_GetModeControl_R_WR () != 0 ) /* DMA write mode */
772 {
773 Byte = FDC_DMA_FIFO_Pull (); /* Get a byte from the DMA FIFO */
774 CAPSFdcWrite ( &IPF_State.Fdc , 3 , Byte ); /* Write to FDC's reg 3 */
775
776 LOG_TRACE(TRACE_FDC, "fdc ipf callback drq state=0x%x write byte 0x%02x VBL=%d HBL=%d\n" , (int)State , Byte , nVBLs , nHBL );
777 }
778
779 else /* DMA read mode */
780 {
781 Byte = CAPSFdcRead ( &IPF_State.Fdc , 3 ); /* Read from FDC's reg 3 */
782 FDC_DMA_FIFO_Push ( Byte ); /* Add byte to the DMA FIFO */
783
784 LOG_TRACE(TRACE_FDC, "fdc ipf callback drq state=0x%x read byte 0x%02x VBL=%d HBL=%d\n" , (int)State , Byte , nVBLs , nHBL );
785 }
786 }
787 #endif
788
789
790
791 /*
792 * This function is used to enable/disable a drive when
793 * using the UI or command line parameters
794 *
795 * NOTE : for now, IPF only supports changing drive 1, drive 0
796 * is always ON.
797 */
IPF_Drive_Set_Enable(int Drive,bool value)798 void IPF_Drive_Set_Enable ( int Drive , bool value )
799 {
800 #ifndef HAVE_CAPSIMAGE
801 return;
802
803 #else
804 IPF_State.DriveEnabled[ Drive ] = value; /* Store the new state */
805
806 IPF_Drive_Update_Enable_Side (); /* Update IPF's internal state */
807 #endif
808 }
809
810
811 /*
812 * This function is used to configure a drive as single sided
813 * or double sided when using the UI or command line parameters
814 */
IPF_Drive_Set_DoubleSided(int Drive,bool value)815 void IPF_Drive_Set_DoubleSided ( int Drive , bool value )
816 {
817 #ifndef HAVE_CAPSIMAGE
818 return;
819
820 #else
821 IPF_State.DoubleSided[ Drive ] = value; /* Store the new state */
822
823 IPF_Drive_Update_Enable_Side (); /* Update IPF's internal state */
824 #endif
825 }
826
827
828 /*
829 * Update IPF's internal state depending on which drives are ON or OFF
830 * and if the drive is single or double sided (for capslib >= 5.1)
831 */
832 #ifdef HAVE_CAPSIMAGE
IPF_Drive_Update_Enable_Side(void)833 static void IPF_Drive_Update_Enable_Side ( void )
834 {
835 #if CAPS_LIB_REL_REV >= 501
836 int i;
837 #endif
838
839 if ( IPF_State.DriveEnabled[ 1 ] )
840 IPF_State.Fdc.drivemax = MAX_FLOPPYDRIVES; /* Should be 2 */
841 else
842 IPF_State.Fdc.drivemax = MAX_FLOPPYDRIVES - 1; /* Should be 1 */
843
844 #if CAPS_LIB_REL_REV >= 501
845 for ( i=0 ; i < MAX_FLOPPYDRIVES ; i++ )
846 {
847 if ( IPF_State.DoubleSided[ i ] )
848 IPF_State.Drive[ i ].diskattr &= ~CAPSDRIVE_DA_SS; /* Double sided */
849 else
850 IPF_State.Drive[ i ].diskattr |= CAPSDRIVE_DA_SS; /* Single sided */
851 }
852 #endif
853 }
854 #endif
855
856
857 /*
858 * Set the drive and the side to be used for the next FDC commands
859 * io_porta_old is the previous value, io_porta_new is the new value
860 * to take into account.
861 * We report a side change only when a drive is selected.
862 */
IPF_SetDriveSide(Uint8 io_porta_old,Uint8 io_porta_new)863 void IPF_SetDriveSide ( Uint8 io_porta_old , Uint8 io_porta_new )
864 {
865 #ifndef HAVE_CAPSIMAGE
866 return;
867
868 #else
869 int Side;
870
871 LOG_TRACE(TRACE_FDC, "fdc ipf change drive/side io_porta_old=0x%x io_porta_new=0x%x VBL=%d HBL=%d\n" , io_porta_old , io_porta_new , nVBLs , nHBL );
872
873 Side = ( (~io_porta_new) & 0x01 ); /* Side 0 or 1 */
874
875 IPF_State.Fdc.drivenew = -1; /* By default, don't select any drive */
876
877 /* Check drive 1 first */
878 if ( ( io_porta_new & 0x04 ) == 0 )
879 {
880 IPF_State.Drive[ 1 ].newside = Side;
881 IPF_State.Fdc.drivenew = 1; /* Select drive 1 */
882 }
883
884 /* If both drive 0 and drive 1 are enabled, we keep only drive 0 as newdrive */
885 if ( ( io_porta_new & 0x02 ) == 0 )
886 {
887 IPF_State.Drive[ 0 ].newside = Side;
888 IPF_State.Fdc.drivenew = 0; /* Select drive 0 (and un-select drive 1 if set above) */
889 }
890
891 IPF_Emulate(); /* Update emulation's state up to this point, then set new drive/side */
892 #endif
893 }
894
895
896
897
898 /*
899 * Write a byte into one of the FDC registers
900 * 0=command 1=track 2=sector 3=data
901 */
IPF_FDC_WriteReg(Uint8 Reg,Uint8 Byte)902 void IPF_FDC_WriteReg ( Uint8 Reg , Uint8 Byte )
903 {
904 #ifndef HAVE_CAPSIMAGE
905 return; /* This should not be reached (an IPF image can't be inserted without capsimage) */
906
907 #else
908 if ( Reg == 0 ) /* more detailed logs for command register */
909 IPF_FDC_LogCommand ( Byte );
910 else
911 LOG_TRACE(TRACE_FDC, "fdc ipf write reg=%d data=0x%x VBL=%d HBL=%d\n" , Reg , Byte , nVBLs , nHBL );
912
913 #if CAPS_LIB_REL_REV >= 501
914 /* In the case of CTR images, we must reset the revolution counter */
915 /* when a command access data on disk and track/side changed since last access */
916 if ( Reg == 0 )
917 {
918 int Type;
919 int Drive;
920
921 Type = FDC_GetCmdType ( Byte );
922 if ( ( Type == 2 ) || ( Type == 3 ) )
923 {
924 Drive = IPF_State.Fdc.driveact;
925 if ( ( Drive >= 0 )
926 && ( ( IPF_State.Drive[ Drive ].side != IPF_State.Rev_Side[ Drive ] ) || ( IPF_State.Drive[ Drive ].track != IPF_State.Rev_Track[ Drive ] ) ) )
927 {
928 IPF_State.Rev_Side[ Drive ] = IPF_State.Drive[ Drive ].side;
929 IPF_State.Rev_Track[ Drive ] = IPF_State.Drive[ Drive ].track;
930 CAPSSetRevolution ( IPF_State.CapsImage[ Drive ] , 0 );
931 }
932 }
933 }
934 #endif
935
936 IPF_Emulate(); /* Update emulation's state up to this point */
937
938 CAPSFdcWrite ( &IPF_State.Fdc , Reg , Byte );
939 #endif
940 }
941
942
943
944
945 /*
946 * Read the content of one of the FDC registers
947 * 0=status 1=track 2=sector 3=data
948 */
IPF_FDC_ReadReg(Uint8 Reg)949 Uint8 IPF_FDC_ReadReg ( Uint8 Reg )
950 {
951 #ifndef HAVE_CAPSIMAGE
952 return 0; /* This should not be reached (an IPF image can't be inserted without capsimage) */
953 #else
954 Uint8 Byte;
955
956 IPF_Emulate(); /* Update emulation's state up to this point */
957
958 Byte = CAPSFdcRead ( &IPF_State.Fdc , Reg );
959 LOG_TRACE(TRACE_FDC, "fdc ipf read reg=%d data=0x%x VBL=%d HBL=%d\n" , Reg , Byte , nVBLs , nHBL );
960
961 return Byte;
962 #endif
963 }
964
965
966
967
968 /*
969 * Return the content of some registers to display them in the statusbar
970 * We should not call IPF_Emulate() or similar, reading should not change emulation's state
971 */
IPF_FDC_StatusBar(Uint8 * pCommand,Uint8 * pHead,Uint8 * pTrack,Uint8 * pSector,Uint8 * pSide)972 void IPF_FDC_StatusBar ( Uint8 *pCommand , Uint8 *pHead , Uint8 *pTrack , Uint8 *pSector , Uint8 *pSide )
973 {
974 #ifndef HAVE_CAPSIMAGE
975 return; /* This should not be reached (an IPF image can't be inserted without capsimage) */
976 #else
977 int Drive;
978
979 Drive = IPF_State.Fdc.driveact;
980 if ( Drive < 0 ) /* If no drive enabled, use drive O for Head/Side */
981 Drive = 0;
982
983 /* We read directly in the structures, to be sure we don't change emulation's state */
984 *pCommand = IPF_State.Fdc.r_command;
985 *pHead = IPF_State.Drive[ Drive ].track;
986 *pTrack = IPF_State.Fdc.r_track;
987 *pSector = IPF_State.Fdc.r_sector;
988 *pSide = IPF_State.Drive[ Drive ].side;
989 #endif
990 }
991
992
993
994 #ifdef HAVE_CAPSIMAGE
IPF_FDC_LogCommand(Uint8 Command)995 static void IPF_FDC_LogCommand ( Uint8 Command )
996 {
997 Uint8 Head , Track , Sector , Side , DataReg;
998 int Drive;
999 int FrameCycles, HblCounterVideo, LineCycles;
1000 char buf[ 200 ];
1001
1002
1003 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1004
1005 Drive = IPF_State.Fdc.driveact;
1006 if ( Drive < 0 ) /* If no drive enabled, use drive O for Head/Side */
1007 Drive = 0;
1008
1009 /* We read directly in the structures, to be sure we don't change emulation's state */
1010 Head = IPF_State.Drive[ Drive ].track;
1011 Track = IPF_State.Fdc.r_track;
1012 Sector = IPF_State.Fdc.r_sector;
1013 DataReg = IPF_State.Fdc.r_data;
1014 Side = IPF_State.Drive[ Drive ].side;
1015
1016 if ( ( Command & 0xf0 ) == 0x00 ) /* Restore */
1017 sprintf ( buf , "type I restore spinup=%s verify=%s steprate=%d drive=%d tr=0x%x head_track=0x%x" ,
1018 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1019 ( Command & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
1020 FDC_StepRate_ms[ Command & 0x03 ] , Drive , Track , Head );
1021
1022 else if ( ( Command & 0xf0 ) == 0x10 ) /* Seek */
1023 sprintf ( buf , "type I seek dest_track=0x%x spinup=%s verify=%s steprate=%d drive=%d tr=0x%x head_track=0x%x" ,
1024 DataReg , ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1025 ( Command & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
1026 FDC_StepRate_ms[ Command & 0x03 ] , Drive , Track , Head );
1027
1028 else if ( ( Command & 0xe0 ) == 0x20 ) /* Step */
1029 sprintf ( buf , "type I step %d spinup=%s verify=%s steprate_ms=%d drive=%d tr=0x%x head_track=0x%x",
1030 ( IPF_State.Fdc.lineout & CAPSFDC_LO_DIRC ) ? 1 : -1 ,
1031 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1032 ( Command & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
1033 FDC_StepRate_ms[ Command & 0x03 ] , Drive , Track , Head );
1034
1035 else if ( ( Command & 0xe0 ) == 0x40 ) /* Step In */
1036 sprintf ( buf , "type I step in spinup=%s verify=%s steprate=%d drive=%d tr=0x%x head_track=0x%x" ,
1037 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1038 ( Command & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
1039 FDC_StepRate_ms[ Command & 0x03 ] , Drive , Track , Head );
1040
1041 else if ( ( Command & 0xe0 ) == 0x60 ) /* Step Out */
1042 sprintf ( buf , "type I step out spinup=%s verify=%s steprate=%d drive=%d tr=0x%x head_track=0x%x" ,
1043 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1044 ( Command & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
1045 FDC_StepRate_ms[ Command & 0x03 ] , Drive , Track , Head );
1046
1047 else if ( ( Command & 0xe0 ) == 0x80 ) /* Read Sector */
1048 sprintf ( buf , "type II read sector sector=0x%x multi=%s spinup=%s settle=%s tr=0x%x head_track=0x%x"
1049 " side=%d drive=%d dmasector=%d addr=0x%x",
1050 Sector, ( Command & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" ,
1051 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1052 ( Command & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1053 Track , Head , Side , Drive , FDC_DMA_GetSectorCount() , FDC_GetDMAAddress() );
1054
1055 else if ( ( Command & 0xe0 ) == 0xa0 ) /* Write Sector */
1056 sprintf ( buf , "type II write sector sector=0x%x multi=%s spinup=%s settle=%s tr=0x%x head_track=0x%x"
1057 " side=%d drive=%d dmasector=%d addr=0x%x",
1058 Sector, ( Command & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" ,
1059 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1060 ( Command & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1061 Track , Head , Side , Drive , FDC_DMA_GetSectorCount() , FDC_GetDMAAddress() );
1062
1063 else if ( ( Command & 0xf0 ) == 0xc0 ) /* Read Address */
1064 sprintf ( buf , "type III read address spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x" ,
1065 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1066 ( Command & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1067 Track , Head , Side , Drive , FDC_GetDMAAddress() );
1068
1069 else if ( ( Command & 0xf0 ) == 0xe0 ) /* Read Track */
1070 sprintf ( buf , "type III read track spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x" ,
1071 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1072 ( Command & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1073 Track , Head , Side , Drive , FDC_GetDMAAddress() );
1074
1075 else if ( ( Command & 0xf0 ) == 0xf0 ) /* Write Track */
1076 sprintf ( buf , "type III write track spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x" ,
1077 ( Command & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1078 ( Command & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1079 Track , Head , Side , Drive , FDC_GetDMAAddress() );
1080
1081 else /* Force Int */
1082 sprintf ( buf , "type IV force int 0x%x irq=%d index=%d" ,
1083 Command , ( Command & 0x8 ) >> 3 , ( Command & 0x4 ) >> 2 );
1084
1085
1086 LOG_TRACE(TRACE_FDC, "fdc ipf %s VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1087 buf , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1088 }
1089 #endif
1090
1091
1092
1093 /*
1094 * Run the FDC emulation during NbCycles cycles (relative to the 8MHz FDC's clock)
1095 */
IPF_Emulate(void)1096 void IPF_Emulate ( void )
1097 {
1098 #ifndef HAVE_CAPSIMAGE
1099 return;
1100
1101 #else
1102 int NbCycles;
1103 int Drive;
1104
1105 NbCycles = CyclesGlobalClockCounter - IPF_State.FdcClock; /* Number of cycles since last emulation */
1106 if ( NbCycles < 0 )
1107 NbCycles = 0; /* We should call CAPSFdcEmulate even when NbCycles=0 */
1108
1109 // LOG_TRACE(TRACE_FDC, "fdc ipf emulate cycles=%d VBL=%d HBL=%d clock=%lld\n" , NbCycles , nVBLs , nHBL , CyclesGlobalClockCounter );
1110
1111 /* Update Write Protect status for each drive */
1112 for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
1113 if ( Floppy_IsWriteProtected ( Drive ) )
1114 IPF_State.Drive[ Drive ].diskattr |= CAPSDRIVE_DA_WP; /* Disk write protected */
1115 else
1116 IPF_State.Drive[ Drive ].diskattr &= ~CAPSDRIVE_DA_WP; /* Disk is not write protected */
1117
1118
1119 CAPSFdcEmulate ( &IPF_State.Fdc , NbCycles ); /* Process at max NbCycles */
1120 IPF_State.FdcClock += IPF_State.Fdc.clockact; /* clockact can be < NbCycle in some cases */
1121
1122 /* Update UI's LEDs depending on Status Register */
1123 FDC_Drive_Set_BusyLed ( (IPF_State.Fdc.r_st0 & ~IPF_State.Fdc.r_stm) | (IPF_State.Fdc.r_st1 & IPF_State.Fdc.r_stm) );
1124 #endif
1125 }
1126
1127
1128
1129