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