|
|
1.1 root 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 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 "cycles.h"
27:
28: #ifdef HAVE_CAPSIMAGE
29: #if CAPSIMAGE_VERSION == 5
30: #include <caps5/CapsLibAll.h>
31: #else
32: #include <caps/fdc.h>
33: #define CAPS_LIB_RELEASE 4
34: #define CAPS_LIB_REVISION 2
35: #endif
36: /* Macro to check release and revision */
37: #define CAPS_LIB_REL_REV ( CAPS_LIB_RELEASE * 100 + CAPS_LIB_REVISION )
38: #endif
39:
40:
41: typedef struct
42: {
43: #ifdef HAVE_CAPSIMAGE
44: Uint32 CapsLibRelease;
45: Uint32 CapsLibRevision;
46:
47: struct CapsFdc Fdc; /* Fdc state */
48: struct CapsDrive Drive[ MAX_FLOPPYDRIVES ]; /* Physical drives */
49: CapsLong CapsImage[ MAX_FLOPPYDRIVES ]; /* For the IPF disk images */
50:
51: int Rev_Track[ MAX_FLOPPYDRIVES ]; /* Needed to handle CAPSSetRevolution for type II/III commands */
52: int Rev_Side[ MAX_FLOPPYDRIVES ];
53:
54: bool DriveEnabled[ MAX_FLOPPYDRIVES ];/* Is drive ON or OFF */
55: bool DoubleSided[ MAX_FLOPPYDRIVES ];/* Is drive double sided or not */
56:
57: Sint64 FdcClock; /* Current value of CyclesGlobalClockCounter */
58: #endif
59: } IPF_STRUCT;
60:
61:
62: static IPF_STRUCT IPF_State; /* All variables related to the IPF support */
63:
64:
65: #ifdef HAVE_CAPSIMAGE
66: static void IPF_CallBack_Trk ( struct CapsFdc *pc , CapsULong State );
67: static void IPF_CallBack_Irq ( struct CapsFdc *pc , CapsULong State );
68: static void IPF_CallBack_Drq ( struct CapsFdc *pc , CapsULong State );
69: static void IPF_Drive_Update_Enable_Side ( void );
70: #endif
71:
72:
73:
74:
75: /*-----------------------------------------------------------------------*/
76: /**
77: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
78: * We must take care of whether Hatari was compiled with IPF support of not
79: * when saving/restoring snapshots to avoid incompatibilies.
80: */
81: void IPF_MemorySnapShot_Capture(bool bSave)
82: {
83: int StructSize;
84: int Drive;
85:
86: if ( bSave ) /* Saving snapshot */
87: {
88: StructSize = sizeof ( IPF_State ); /* 0 if HAVE_CAPSIMAGE is not defined */
89: MemorySnapShot_Store(&StructSize, sizeof(StructSize));
90: fprintf ( stderr , "ipf save %d\n" , StructSize );
91: if ( StructSize > 0 )
92: MemorySnapShot_Store(&IPF_State, sizeof(IPF_State));
93: }
94:
95: else /* Restoring snapshot */
96: {
97: MemorySnapShot_Store(&StructSize, sizeof(StructSize));
98: fprintf ( stderr , "ipf load %d\n" , StructSize );
99: if ( ( StructSize == 0 ) && ( sizeof ( IPF_State ) > 0 ) )
100: {
101: Log_AlertDlg(LOG_ERROR, "This memory snapshot doesn't include IPF data but this version of Hatari was built with IPF support");
102: return; /* Continue restoring the rest of the memory snapshot */
103: }
104: else if ( ( StructSize > 0 ) && ( sizeof ( IPF_State ) == 0 ) )
105: {
106: Log_AlertDlg(LOG_ERROR, "This memory snapshot includes IPF data but this version of Hatari was not built with IPF support");
107: MemorySnapShot_Skip( StructSize ); /* Ignore the IPF data */
108: return; /* Continue restoring the rest of the memory snapshot */
109: }
110: else if ( ( StructSize > 0 ) && ( StructSize != sizeof ( IPF_State ) ) )
111: {
112: Log_AlertDlg(LOG_ERROR, "This memory snapshot includes IPF data different from the ones handled in this version of Hatari");
113: MemorySnapShot_Skip( StructSize ); /* Ignore the IPF data */
114: return; /* Continue restoring the rest of the memory snapshot */
115: }
116:
117: if ( StructSize > 0 )
118: {
119: MemorySnapShot_Store(&IPF_State, sizeof(IPF_State));
120:
121: #ifdef HAVE_CAPSIMAGE
122: /* For IPF structures, we need to update some pointers in Fdc/Drive/CapsImage */
123: /* drive : PUBYTE trackbuf, PUDWORD timebuf */
124: /* fdc : PCAPSDRIVE driveprc, PCAPSDRIVE drive, CAPSFDCHOOK callback functions */
125: CAPSFdcInvalidateTrack ( &IPF_State.Fdc , 0 ); /* Invalidate buffered track data for drive 0 */
126: CAPSFdcInvalidateTrack ( &IPF_State.Fdc , 1 ); /* Invalidate buffered track data for drive 1 */
127:
128: IPF_State.Fdc.drive = IPF_State.Drive; /* Connect drives array to the FDC */
129: if ( IPF_State.Fdc.driveprc != NULL ) /* Recompute active drive's pointer */
130: IPF_State.Fdc.driveprc = IPF_State.Fdc.drive + IPF_State.Fdc.driveact;
131:
132: /* Set callback functions */
133: IPF_State.Fdc.cbirq = IPF_CallBack_Irq;
134: IPF_State.Fdc.cbdrq = IPF_CallBack_Drq;
135: IPF_State.Fdc.cbtrk = IPF_CallBack_Trk;
136: #endif
137:
138: /* Call IPF_Insert to recompute IPF_State.CapsImage[ Drive ] */
139: for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
140: if ( EmulationDrives[Drive].ImageType == FLOPPY_IMAGE_TYPE_IPF )
141: if ( IPF_Insert ( Drive , EmulationDrives[Drive].pBuffer , EmulationDrives[Drive].nImageBytes ) == false )
142: {
143: Log_AlertDlg(LOG_ERROR, "Error restoring IPF image %s in drive %d" ,
144: EmulationDrives[Drive].sFileName , Drive );
145: return;
146: }
147:
148: fprintf ( stderr , "ipf load ok\n" );
149: }
150: }
151: }
152:
153:
154:
155:
156: /*-----------------------------------------------------------------------*/
157: /**
158: * Does filename end with a .IPF or .RAW or .CTR extension ? If so, return true.
159: * .RAW and .CTR support requires caps lib >= 5.1
160: */
161: bool IPF_FileNameIsIPF(const char *pszFileName, bool bAllowGZ)
162: {
163: return ( File_DoesFileExtensionMatch(pszFileName,".ipf" )
164: || ( bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".ipf.gz") )
165: #if CAPS_LIB_REL_REV >= 501
166: || File_DoesFileExtensionMatch(pszFileName,".raw" )
167: || ( bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".raw.gz") )
168: || File_DoesFileExtensionMatch(pszFileName,".ctr" )
169: || ( bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".ctr.gz") )
170: #endif
171: );
172: }
173:
174:
175: /*-----------------------------------------------------------------------*/
176: /**
177: * Load .IPF file into memory, set number of bytes loaded and return a pointer
178: * to the buffer.
179: */
180: Uint8 *IPF_ReadDisk(int Drive, const char *pszFileName, long *pImageSize, int *pImageType)
181: {
182: #ifndef HAVE_CAPSIMAGE
183: Log_AlertDlg(LOG_ERROR, "This version of Hatari was not built with IPF support, this disk image can't be handled.");
184: return NULL;
185:
186: #else
187: Uint8 *pIPFFile;
188:
189: *pImageSize = 0;
190:
191: /* Just load directly a buffer, and set ImageSize accordingly */
192: pIPFFile = File_Read(pszFileName, pImageSize, NULL);
193: if (!pIPFFile)
194: {
195: *pImageSize = 0;
196: return NULL;
197: }
198:
199: *pImageType = FLOPPY_IMAGE_TYPE_IPF;
200: return pIPFFile;
201: #endif
202: }
203:
204:
205: /*-----------------------------------------------------------------------*/
206: /**
207: * Save .IPF file from memory buffer. Returns true is all OK.
208: */
209: bool IPF_WriteDisk(int Drive, const char *pszFileName, Uint8 *pBuffer, int ImageSize)
210: {
211: /* saving is not supported for IPF files */
212: return false;
213: }
214:
215:
216:
217:
218: /*
219: * Init the FDC and the drives used to handle IPF images
220: */
221: bool IPF_Init ( void )
222: {
223: #ifndef HAVE_CAPSIMAGE
224: return true;
225:
226: #else
227: int i;
228: struct CapsVersionInfo caps_vi;
229:
230: fprintf ( stderr , "IPF : IPF_Init\n" );
231:
232: if ( CAPSInit() != imgeOk )
233: {
234: fprintf ( stderr , "IPF : Could not initialize the capsimage library\n" );
235: return false;
236: }
237:
238: if ( CAPSGetVersionInfo ( &caps_vi , 0 ) != imgeOk )
239: {
240: fprintf ( stderr , "IPF : CAPSVersionInfo failed\n" );
241: return false;
242: }
243: fprintf ( stderr , "IPF : capsimage library version release=%d revision=%d\n" , (int)caps_vi.release , (int)caps_vi.revision );
244: IPF_State.CapsLibRelease = caps_vi.release;
245: IPF_State.CapsLibRevision = caps_vi.revision;
246:
247: /* Default values for each physical drive */
248: memset ( IPF_State.Drive , 0 , sizeof ( IPF_State.Drive ) );
249: for ( i=0 ; i < MAX_FLOPPYDRIVES ; i++ )
250: {
251: IPF_State.Drive[ i ].type = sizeof ( struct CapsDrive );
252: IPF_State.Drive[ i ].rpm = CAPSDRIVE_35DD_RPM;
253: IPF_State.Drive[ i ].maxtrack = CAPSDRIVE_35DD_HST;
254:
255: IPF_State.Rev_Track[ i ] = -1;
256: IPF_State.Rev_Side[ i ] = -1;
257:
258: IPF_State.DriveEnabled[ i ] = true;
259: IPF_State.DoubleSided[ i ] = true;
260: }
261:
262: /* Init FDC with 2 physical drives */
263: memset ( &IPF_State.Fdc , 0 , sizeof ( IPF_State.Fdc ) );
264: IPF_State.Fdc.type = sizeof( struct CapsFdc );
265: IPF_State.Fdc.model = cfdcmWD1772;
266: IPF_State.Fdc.drive = IPF_State.Drive;
267: IPF_State.Fdc.drivecnt = MAX_FLOPPYDRIVES;
268:
269: if ( CAPSFdcInit ( &IPF_State.Fdc ) != imgeOk)
270: {
271: fprintf ( stderr , "IPF : CAPSFdcInit failed\n" );
272: return false;
273: }
274:
275: /* 2 drives by default */
276: IPF_State.Fdc.drivemax = MAX_FLOPPYDRIVES;
277: /* Update drives' state in case we have some drives ON or OFF in config or parameters */
278: IPF_Drive_Update_Enable_Side ();
279:
280: /* FDC clock */
281: IPF_State.Fdc.clockfrq = 8000000;
282:
283: /* Set callback functions */
284: IPF_State.Fdc.cbirq = IPF_CallBack_Irq;
285: IPF_State.Fdc.cbdrq = IPF_CallBack_Drq;
286: IPF_State.Fdc.cbtrk = IPF_CallBack_Trk;
287:
288: CAPSFdcReset ( &IPF_State.Fdc );
289:
290: return true;
291: #endif
292: }
293:
294:
295:
296:
297: /*
298: * Exit
299: */
300: void IPF_Exit ( void )
301: {
302: #ifndef HAVE_CAPSIMAGE
303: #else
304: CAPSExit();
305: #endif
306: }
307:
308:
309:
310:
311: /*
312: * Init the ressources to handle the IPF image inserted into a drive (0=A: 1=B:)
313: */
314: bool IPF_Insert ( int Drive , Uint8 *pImageBuffer , long ImageSize )
315: {
316: #ifndef HAVE_CAPSIMAGE
317: return false;
318:
319: #else
320: CapsLong ImageId;
321: CapsLong ImageType;
322:
323: ImageId = CAPSAddImage();
324: if ( ImageId < 0 )
325: {
326: fprintf ( stderr , "IPF : error CAPSAddImage\n" );
327: return false;
328: }
329:
330: #if CAPS_LIB_REL_REV >= 501
331: ImageType = CAPSGetImageTypeMemory ( pImageBuffer , ImageSize );
332: if ( ImageType == citError )
333: {
334: fprintf ( stderr , "IPF : error CAPSGetImageTypeMemory\n" );
335: return false;
336: }
337: else if ( ImageType == citUnknown )
338: {
339: fprintf ( stderr , "IPF : unknown image type\n" );
340: return false;
341: }
342:
343: fprintf ( stderr , "IPF : IPF_Insert drive=%d buf=%p size=%ld imageid=%d type=" , Drive , pImageBuffer , ImageSize , ImageId );
344: switch ( ImageType ) {
345: case citIPF: fprintf ( stderr , "IPF\n" ); break;
346: case citCTRaw: fprintf ( stderr , "CT RAW\n" ); break;
347: case citKFStream: fprintf ( stderr , "KF STREAM\n" ) ; break;
348: case citDraft: fprintf ( stderr , "DRAFT\n" ) ; break;
349: default : fprintf ( stderr , "NOT SUPPORTED\n" );
350: return false;
351: }
352: #endif
353:
354: if ( CAPSLockImageMemory ( ImageId , pImageBuffer , (CapsULong)ImageSize , DI_LOCK_MEMREF ) == imgeOk )
355: {
356: struct CapsImageInfo cii;
357: int i;
358:
359: /* Print some debug infos */
360: if ( CAPSGetImageInfo ( &cii , ImageId ) == imgeOk )
361: {
362: printf("Type: %d\n", (int)cii.type);
363: printf("Release: %d\n", (int)cii.release);
364: printf("Revision: %d\n", (int)cii.revision);
365: printf("Min Cylinder: %d\n", (int)cii.mincylinder);
366: printf("Max Cylinder: %d\n", (int)cii.maxcylinder);
367: printf("Min Head: %d\n", (int)cii.minhead);
368: printf("Max Head: %d\n", (int)cii.maxhead);
369: 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);
370: printf("Platforms:");
371: for (i = 0; i < CAPS_MAXPLATFORM; i++)
372: if (cii.platform[i] != ciipNA)
373: printf ( " %s" , CAPSGetPlatformName(cii.platform[i]) );
374: printf("\n");
375:
376: /* Some IPF disks are not correctly supported yet : display a warning */
377: if ( (int)cii.release == 3222 ) /* Sundog */
378: Log_AlertDlg ( LOG_INFO , "'Sundog' is not correctly supported yet, it requires write access." );
379: else if ( (int)cii.release == 3058 ) /* Lethal Xcess */
380: Log_AlertDlg ( LOG_INFO , "'Lethal Xcess' is not correctly supported yet, protection will fail" );
381: }
382: }
383: else
384: {
385: CAPSRemImage ( ImageId ) ;
386: return false;
387: }
388:
389: if ( CAPSLoadImage ( ImageId , DI_LOCK_DENALT | DI_LOCK_DENVAR | DI_LOCK_UPDATEFD ) != imgeOk )
390: {
391: fprintf ( stderr , "IPF : error CAPSLoadImage\n" );
392: CAPSUnlockImage ( ImageId );
393: CAPSRemImage ( ImageId ) ;
394: return false;
395: }
396:
397:
398: IPF_State.CapsImage[ Drive ] = ImageId;
399:
400: IPF_State.Drive[ Drive ].diskattr |= CAPSDRIVE_DA_IN; /* Disk inserted, keep the value for "write protect" */
401:
402: CAPSFdcInvalidateTrack ( &IPF_State.Fdc , Drive ); /* Invalidate previous buffered track data for drive, if any */
403:
404: IPF_State.Rev_Track[ Drive ] = -1; /* Invalidate previous track/side to handle revolution's count */
405: IPF_State.Rev_Side[ Drive ] = -1;
406:
407: return true;
408: #endif
409: }
410:
411:
412:
413:
414: /*
415: * When ejecting a disk, free the ressources associated with an IPF image
416: */
417: bool IPF_Eject ( int Drive )
418: {
419: #ifndef HAVE_CAPSIMAGE
420: return false;
421:
422: #else
423: fprintf ( stderr , "IPF : IPF_Eject drive=%d imageid=%d\n" , Drive , IPF_State.CapsImage[ Drive ] );
424:
425: CAPSFdcInvalidateTrack ( &IPF_State.Fdc , Drive ); /* Invalidate previous buffered track data for drive, if any */
426:
427: if ( CAPSUnlockImage ( IPF_State.CapsImage[ Drive ] ) < 0 )
428: {
429: fprintf ( stderr , "IPF : error CAPSUnlockImage\n" );
430: return false;
431: }
432:
433: if ( CAPSRemImage ( IPF_State.CapsImage[ Drive ] ) < 0 )
434: {
435: fprintf ( stderr , "IPF : error CAPSRemImage\n" );
436: return false;
437: }
438:
439: IPF_State.CapsImage[ Drive ] = -1;
440:
441: IPF_State.Drive[ Drive ].diskattr &= ~CAPSDRIVE_DA_IN;
442:
443: return true;
444: #endif
445: }
446:
447:
448:
449:
450: /*
451: * Reset FDC state when a reset signal was received
452: */
453: void IPF_Reset ( void )
454: {
455: #ifdef HAVE_CAPSIMAGE
456: CAPSFdcReset ( &IPF_State.Fdc );
457:
458: IPF_State.FdcClock = CyclesGlobalClockCounter;
459: #endif
460: }
461:
462:
463:
464:
465: /*
466: * Callback function used when track is changed.
467: * We need to update the track data by calling CAPSLockTrack
468: */
469: #ifdef HAVE_CAPSIMAGE
470: static void IPF_CallBack_Trk ( struct CapsFdc *pc , CapsULong State )
471: {
472: int Drive = State; /* State is the drive number in that case */
473: struct CapsDrive *pd = pc->drive+Drive; /* Current drive where the track change occurred */
474: struct CapsTrackInfoT1 cti;
475:
476: cti.type=1;
477: if ( CAPSLockTrack ( &cti , IPF_State.CapsImage[ Drive ] , pd->buftrack , pd->bufside ,
478: DI_LOCK_DENALT|DI_LOCK_DENVAR|DI_LOCK_UPDATEFD|DI_LOCK_TYPE ) != imgeOk )
479: return;
480:
481: LOG_TRACE(TRACE_FDC, "fdc ipf callback trk drive=%d buftrack=%d bufside=%d VBL=%d HBL=%d\n" , Drive ,
482: (int)pd->buftrack , (int)pd->bufside , nVBLs , nHBL );
483:
484: pd->ttype = cti.type;
485: pd->trackbuf = cti.trackbuf;
486: pd->timebuf = cti.timebuf;
487: pd->tracklen = cti.tracklen;
488: pd->overlap = cti.overlap;
489: }
490: #endif
491:
492:
493:
494:
495: /*
496: * Callback function used when the FDC change the IRQ signal
497: */
498: #ifdef HAVE_CAPSIMAGE
499: static void IPF_CallBack_Irq ( struct CapsFdc *pc , CapsULong State )
500: {
501: LOG_TRACE(TRACE_FDC, "fdc ipf callback irq state=0x%x VBL=%d HBL=%d\n" , (int)State , nVBLs , nHBL );
502:
503: if ( State )
504: FDC_SetIRQ ( FDC_IRQ_SOURCE_OTHER ); /* IRQ bit was set */
505: else
506: FDC_ClearIRQ (); /* IRQ bit was reset */
507: }
508: #endif
509:
510:
511:
512:
513: /*
514: * Callback function used when the FDC change the DRQ signal
515: * -> copy the byte to/from the DMA's FIFO if it's a read or a write to the disk
516: */
517: #ifdef HAVE_CAPSIMAGE
518: static void IPF_CallBack_Drq ( struct CapsFdc *pc , CapsULong State )
519: {
520: Uint8 Byte;
521:
522: if ( State == 0 )
523: return; /* DRQ bit was reset, do nothing */
524:
525: if ( FDC_DMA_GetModeControl_R_WR () != 0 ) /* DMA write mode */
526: {
527: Byte = FDC_DMA_FIFO_Pull (); /* Get a byte from the DMA FIFO */
528: CAPSFdcWrite ( &IPF_State.Fdc , 3 , Byte ); /* Write to FDC's reg 3 */
529:
530: 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 );
531: }
532:
533: else /* DMA read mode */
534: {
535: Byte = CAPSFdcRead ( &IPF_State.Fdc , 3 ); /* Read from FDC's reg 3 */
536: FDC_DMA_FIFO_Push ( Byte ); /* Add byte to the DMA FIFO */
537:
538: 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 );
539: }
540: }
541: #endif
542:
543:
544:
545: /*
546: * This function is used to enable/disable a drive when
547: * using the UI or command line parameters
548: *
549: * NOTE : for now, IPF only supports changing drive 1, drive 0
550: * is always ON.
551: */
552: void IPF_Drive_Set_Enable ( int Drive , bool value )
553: {
554: #ifndef HAVE_CAPSIMAGE
555: return;
556:
557: #else
558: IPF_State.DriveEnabled[ Drive ] = value; /* Store the new state */
559:
560: IPF_Drive_Update_Enable_Side (); /* Update IPF's internal state */
561: #endif
562: }
563:
564:
565: /*
566: * This function is used to configure a drive as single sided
567: * or double sided when using the UI or command line parameters
568: */
569: void IPF_Drive_Set_DoubleSided ( int Drive , bool value )
570: {
571: #ifndef HAVE_CAPSIMAGE
572: return;
573:
574: #else
575: IPF_State.DoubleSided[ Drive ] = value; /* Store the new state */
576:
577: IPF_Drive_Update_Enable_Side (); /* Update IPF's internal state */
578: #endif
579: }
580:
581:
582: /*
583: * Update IPF's internal state depending on which drives are ON or OFF
584: * and if the drive is single or double sided (for capslib >= 5.1)
585: */
586: #ifdef HAVE_CAPSIMAGE
587: static void IPF_Drive_Update_Enable_Side ( void )
588: {
589: int i;
590:
591: if ( IPF_State.DriveEnabled[ 1 ] )
592: IPF_State.Fdc.drivemax = MAX_FLOPPYDRIVES; /* Should be 2 */
593: else
594: IPF_State.Fdc.drivemax = MAX_FLOPPYDRIVES - 1; /* Should be 1 */
595:
596: #if CAPS_LIB_REL_REV >= 501
597: for ( i=0 ; i < MAX_FLOPPYDRIVES ; i++ )
598: {
599: if ( IPF_State.DoubleSided[ i ] )
600: IPF_State.Drive[ i ].diskattr &= ~CAPSDRIVE_DA_SS; /* Double sided */
601: else
602: IPF_State.Drive[ i ].diskattr |= CAPSDRIVE_DA_SS; /* Single sided */
603: }
604: #endif
605: }
606: #endif
607:
608:
609: /*
610: * Set the drive and the side to be used for the next FDC commands
611: * io_porta_old is the previous value, io_porta_new is the new value
612: * to take into account.
613: * We report a side change only when a drive is selected.
614: */
615: void IPF_SetDriveSide ( Uint8 io_porta_old , Uint8 io_porta_new )
616: {
617: #ifndef HAVE_CAPSIMAGE
618: return;
619:
620: #else
621: int Side;
622:
623: 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 );
624:
625: Side = ( (~io_porta_new) & 0x01 ); /* Side 0 or 1 */
626:
627: IPF_State.Fdc.drivenew = -1; /* By default, don't select any drive */
628:
629: /* Check drive 1 first */
630: if ( ( io_porta_new & 0x04 ) == 0 )
631: {
632: IPF_State.Drive[ 1 ].newside = Side;
633: IPF_State.Fdc.drivenew = 1; /* Select drive 1 */
634: }
635:
636: /* If both drive 0 and drive 1 are enabled, we keep only drive 0 as newdrive */
637: if ( ( io_porta_new & 0x02 ) == 0 )
638: {
639: IPF_State.Drive[ 0 ].newside = Side;
640: IPF_State.Fdc.drivenew = 0; /* Select drive 0 (and un-select drive 1 if set above) */
641: }
642:
643: IPF_Emulate(); /* Update emulation's state up to this point, then set new drive/side */
644: #endif
645: }
646:
647:
648:
649:
650: /*
651: * Write a byte into one of the FDC registers
652: * 0=command 1=track 2=sector 3=data
653: */
654: void IPF_FDC_WriteReg ( Uint8 Reg , Uint8 Byte )
655: {
656: #ifndef HAVE_CAPSIMAGE
657: return; /* This should not be reached (an IPF image can't be inserted without capsimage) */
658:
659: #else
660: LOG_TRACE(TRACE_FDC, "fdc ipf write reg=%d data=0x%x VBL=%d HBL=%d\n" , Reg , Byte , nVBLs , nHBL );
661:
662:
663: #if CAPS_LIB_REL_REV >= 501
664: /* In the case of CTR images, we must reset the revolution counter */
665: /* when a command access data on disk and track/side changed since last access */
666: if ( Reg == 0 )
667: {
668: int Type;
669: int Drive;
670:
671: Type = FDC_GetCmdType ( Byte );
672: if ( ( Type == 2 ) || ( Type == 3 ) )
673: {
674: Drive = IPF_State.Fdc.driveact;
675: if ( ( Drive >= 0 )
676: && ( ( IPF_State.Drive[ Drive ].side != IPF_State.Rev_Side[ Drive ] ) || ( IPF_State.Drive[ Drive ].track != IPF_State.Rev_Track[ Drive ] ) ) )
677: {
678: IPF_State.Rev_Side[ Drive ] = IPF_State.Drive[ Drive ].side;
679: IPF_State.Rev_Track[ Drive ] = IPF_State.Drive[ Drive ].track;
680: CAPSSetRevolution ( IPF_State.CapsImage[ Drive ] , 0 );
681: }
682: }
683: }
684: #endif
685:
686: IPF_Emulate(); /* Update emulation's state up to this point */
687:
688: CAPSFdcWrite ( &IPF_State.Fdc , Reg , Byte );
689: #endif
690: }
691:
692:
693:
694:
695: /*
696: * Read the content of one of the FDC registers
697: * 0=status 1=track 2=sector 3=data
698: */
699: Uint8 IPF_FDC_ReadReg ( Uint8 Reg )
700: {
701: #ifndef HAVE_CAPSIMAGE
702: return 0; /* This should not be reached (an IPF image can't be inserted without capsimage) */
703: #else
704: Uint8 Byte;
705:
706: IPF_Emulate(); /* Update emulation's state up to this point */
707:
708: Byte = CAPSFdcRead ( &IPF_State.Fdc , Reg );
709: LOG_TRACE(TRACE_FDC, "fdc ipf read reg=%d data=0x%x VBL=%d HBL=%d\n" , Reg , Byte , nVBLs , nHBL );
710:
711: return Byte;
712: #endif
713: }
714:
715:
716:
717:
718: /*
719: * Return the content of some registers to display them in the statusbar
720: * We should not call IPF_Emulate() or similar, reading should not change emulation's state
721: */
722: void IPF_FDC_StatusBar ( Uint8 *pCommand , Uint8 *pHead , Uint8 *pTrack , Uint8 *pSector , Uint8 *pSide )
723: {
724: #ifndef HAVE_CAPSIMAGE
725: return; /* This should not be reached (an IPF image can't be inserted without capsimage) */
726: #else
727: int Drive;
728:
729: Drive = IPF_State.Fdc.driveact;
730: if ( Drive < 0 ) /* If no drive enabled, use drive O for Head/Side */
731: Drive = 0;
732:
733: /* We read directly in the structures, to be sure we don't change emulation's state */
734: *pCommand = IPF_State.Fdc.r_command;
735: *pHead = IPF_State.Drive[ Drive ].track;
736: *pTrack = IPF_State.Fdc.r_track;
737: *pSector = IPF_State.Fdc.r_sector;
738: *pSide = IPF_State.Drive[ Drive ].side;
739: #endif
740: }
741:
742:
743:
744:
745: /*
746: * Run the FDC emulation during NbCycles cycles (relative to the 8MHz FDC's clock)
747: */
748: void IPF_Emulate ( void )
749: {
750: #ifndef HAVE_CAPSIMAGE
751: return;
752:
753: #else
754: int NbCycles;
755: int Drive;
756:
757: NbCycles = CyclesGlobalClockCounter - IPF_State.FdcClock; /* Number of cycles since last emulation */
758: if ( NbCycles < 0 )
759: NbCycles = 0; /* We should call CAPSFdcEmulate even when NbCycles=0 */
760:
761: // LOG_TRACE(TRACE_FDC, "fdc ipf emulate cycles=%d VBL=%d HBL=%d clock=%lld\n" , NbCycles , nVBLs , nHBL , CyclesGlobalClockCounter );
762:
763: /* Update Write Protect status for each drive */
764: for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
765: if ( Floppy_IsWriteProtected ( Drive ) )
766: IPF_State.Drive[ Drive ].diskattr |= CAPSDRIVE_DA_WP; /* Disk write protected */
767: else
768: IPF_State.Drive[ Drive ].diskattr &= ~CAPSDRIVE_DA_WP; /* Disk is not write protected */
769:
770:
771: CAPSFdcEmulate ( &IPF_State.Fdc , NbCycles ); /* Process at max NbCycles */
772: IPF_State.FdcClock += IPF_State.Fdc.clockact; /* clockact can be < NbCycle in some cases */
773:
774: /* Update UI's LEDs depending on Status Register */
775: FDC_Drive_Set_BusyLed ( (IPF_State.Fdc.r_st0 & ~IPF_State.Fdc.r_stm) | (IPF_State.Fdc.r_st1 & IPF_State.Fdc.r_stm) );
776: #endif
777: }
778:
779:
780:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.