|
|
1.1 root 1: /* 1.1.1.5 root 2: Hatari - floppy.c 1.1 root 3: 1.1.1.19! root 4: This file is distributed under the GNU General Public License, version 2 ! 5: or at your option any later version. Read the file gpl.txt for details. 1.1.1.5 root 6: 1.1.1.12 root 7: This is where we read/write sectors to/from the disk image buffers. 8: NOTE: these buffers are in memory so we only need to write routines for 9: the .ST format. When the buffer is to be saved (ie eject disk) we save 10: it back to the original file in the correct format (.ST or .MSA). 1.1.1.5 root 11: 12: There are some important notes about image accessing - as we use TOS and the 1.1.1.9 root 13: FDC to access the disk the boot-sector MUST be valid. Sometimes this is NOT 14: the case! In these situations we must guess at the disk format. Eg, some disk 1.1.1.5 root 15: images have a a boot sector which states single-sided, but the images have 16: been created as double-sided. As sides are interleaved we need to read the 17: image as if it was double-sided. Also note that 'NumBytesPerSector' is ALWAYS 18: 512 bytes, even if the boot-sector states otherwise. 1.1.1.4 root 19: Also note that old versions of the MAKEDISK utility do not set the correct 20: boot sector structure for a real ST (and also Hatari) to read it correctly. 21: (PaCifiST will, however, read/write to these images as it does not perform 22: FDC access as on a real ST) 1.1 root 23: */ 1.1.1.13 root 24: const char Floppy_fileid[] = "Hatari floppy.c : " __DATE__ " " __TIME__; 1.1.1.8 root 25: 26: #include <sys/stat.h> 1.1.1.12 root 27: #include <assert.h> 1.1.1.4 root 28: #include <SDL_endian.h> 29: 1.1 root 30: #include "main.h" 1.1.1.6 root 31: #include "configuration.h" 1.1.1.7 root 32: #include "dim.h" 1.1 root 33: #include "file.h" 34: #include "floppy.h" 1.1.1.9 root 35: #include "gemdos.h" 36: #include "hdc.h" 1.1.1.8 root 37: #include "log.h" 1.1 root 38: #include "memorySnapShot.h" 39: #include "msa.h" 40: #include "st.h" 1.1.1.6 root 41: #include "zip.h" 1.1.1.18 root 42: #include "screen.h" 43: #include "video.h" 1.1 root 44: 1.1.1.10 root 45: 1.1.1.12 root 46: /* Emulation drive details, eg FileName, Inserted, Changed etc... */ 47: EMULATION_DRIVE EmulationDrives[MAX_FLOPPYDRIVES]; 48: /* Drive A is the default */ 49: int nBootDrive = 0; 1.1 root 50: 1.1.1.18 root 51: 1.1.1.9 root 52: /* Possible disk image file extensions to scan for */ 1.1.1.10 root 53: static const char * const pszDiskImageNameExts[] = 1.1.1.5 root 54: { 1.1.1.10 root 55: ".msa", 56: ".st", 57: ".dim", 58: NULL 1.1 root 59: }; 60: 61: 1.1.1.13 root 62: /* local functions */ 1.1.1.18 root 63: static bool Floppy_EjectBothDrives(void); 64: static void Floppy_DriveTransitionSetState ( int Drive , int State ); 1.1.1.13 root 65: 66: 1.1.1.2 root 67: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 68: /** 69: * Initialize emulation floppy drives 70: */ 1.1 root 71: void Floppy_Init(void) 72: { 1.1.1.10 root 73: int i; 1.1 root 74: 1.1.1.10 root 75: /* Clear drive structures */ 1.1.1.12 root 76: for (i = 0; i < MAX_FLOPPYDRIVES; i++) 1.1.1.10 root 77: { 1.1.1.12 root 78: /* Clear structs and if floppies available, insert them */ 1.1.1.10 root 79: memset(&EmulationDrives[i], 0, sizeof(EMULATION_DRIVE)); 1.1.1.12 root 80: if (strlen(ConfigureParams.DiskImage.szDiskFileName[i]) > 0) 81: Floppy_InsertDiskIntoDrive(i); 1.1.1.10 root 82: } 1.1 root 83: } 84: 1.1.1.2 root 85: 86: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 87: /** 88: * UnInitialize drives 89: */ 1.1 root 90: void Floppy_UnInit(void) 91: { 1.1.1.10 root 92: Floppy_EjectBothDrives(); 1.1 root 93: } 94: 1.1.1.2 root 95: 96: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 97: /** 1.1.1.18 root 98: * Called on Warm/Cold Reset 99: */ 100: void Floppy_Reset(void) 101: { 102: int i; 103: 104: /* Cancel any pending disk change transitions */ 105: for (i = 0; i < MAX_FLOPPYDRIVES; i++) 106: { 107: EmulationDrives[i].TransitionState1 = 0; 108: EmulationDrives[i].TransitionState2 = 0; 109: } 110: } 111: 112: 113: /*-----------------------------------------------------------------------*/ 114: /** 1.1.1.11 root 115: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type) 116: */ 1.1.1.12 root 117: void Floppy_MemorySnapShot_Capture(bool bSave) 1.1 root 118: { 1.1.1.10 root 119: int i; 1.1 root 120: 1.1.1.10 root 121: /* If restoring then eject old drives first! */ 122: if (!bSave) 123: Floppy_EjectBothDrives(); 124: 125: /* Save/Restore details */ 1.1.1.12 root 126: for (i = 0; i < MAX_FLOPPYDRIVES; i++) 1.1.1.10 root 127: { 128: MemorySnapShot_Store(&EmulationDrives[i].bDiskInserted, sizeof(EmulationDrives[i].bDiskInserted)); 129: MemorySnapShot_Store(&EmulationDrives[i].nImageBytes, sizeof(EmulationDrives[i].nImageBytes)); 130: if (!bSave && EmulationDrives[i].bDiskInserted) 131: { 132: EmulationDrives[i].pBuffer = malloc(EmulationDrives[i].nImageBytes); 133: if (!EmulationDrives[i].pBuffer) 134: perror("Floppy_MemorySnapShot_Capture"); 135: } 136: if (EmulationDrives[i].pBuffer) 137: MemorySnapShot_Store(EmulationDrives[i].pBuffer, EmulationDrives[i].nImageBytes); 1.1.1.13 root 138: MemorySnapShot_Store(EmulationDrives[i].sFileName, sizeof(EmulationDrives[i].sFileName)); 1.1.1.10 root 139: MemorySnapShot_Store(&EmulationDrives[i].bContentsChanged,sizeof(EmulationDrives[i].bContentsChanged)); 140: MemorySnapShot_Store(&EmulationDrives[i].bOKToSave,sizeof(EmulationDrives[i].bOKToSave)); 1.1.1.18 root 141: MemorySnapShot_Store(&EmulationDrives[i].TransitionState1,sizeof(EmulationDrives[i].TransitionState1)); 142: MemorySnapShot_Store(&EmulationDrives[i].TransitionState1_VBL,sizeof(EmulationDrives[i].TransitionState1_VBL)); 143: MemorySnapShot_Store(&EmulationDrives[i].TransitionState2,sizeof(EmulationDrives[i].TransitionState2)); 144: MemorySnapShot_Store(&EmulationDrives[i].TransitionState2_VBL,sizeof(EmulationDrives[i].TransitionState2_VBL)); 1.1.1.10 root 145: } 1.1 root 146: } 147: 1.1.1.2 root 148: 149: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 150: /** 151: * Find which device to boot from (hard drive or floppy). 152: */ 1.1 root 153: void Floppy_GetBootDrive(void) 154: { 1.1.1.15 root 155: /* Default to drive A: */ 156: nBootDrive = 0; 157: 158: /* Boot only from hard drive if user wants this */ 159: if (!ConfigureParams.HardDisk.bBootFromHardDisk) 160: return; 161: 1.1.1.16 root 162: if (ACSI_EMU_ON || ConfigureParams.HardDisk.bUseIdeMasterHardDiskImage) 1.1.1.15 root 163: { 1.1.1.10 root 164: nBootDrive = 2; /* Drive C */ 1.1.1.15 root 165: } 166: else if (GEMDOS_EMU_ON) 167: { 168: int i; 169: for (i = 0; i < MAX_HARDDRIVES; i++) 170: { 171: if (emudrives[i]) 172: { 1.1.1.16 root 173: nBootDrive = emudrives[i]->drive_number; 1.1.1.15 root 174: break; 175: } 176: } 177: } 1.1 root 178: } 179: 1.1.1.2 root 180: 181: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 182: /** 183: * Test if disk image is write protected. Write protection can be configured 184: * in the GUI. When set to "automatic", we check the file permissions of the 185: * floppy disk image to decide. 186: */ 1.1.1.12 root 187: bool Floppy_IsWriteProtected(int Drive) 1.1.1.8 root 188: { 1.1.1.10 root 189: if (ConfigureParams.DiskImage.nWriteProtection == WRITEPROT_OFF) 190: { 1.1.1.14 root 191: return false; 1.1.1.10 root 192: } 193: else if (ConfigureParams.DiskImage.nWriteProtection == WRITEPROT_ON) 194: { 1.1.1.14 root 195: return true; 1.1.1.10 root 196: } 197: else 198: { 199: struct stat FloppyStat; 200: /* Check whether disk is writable */ 1.1.1.13 root 201: if (stat(EmulationDrives[Drive].sFileName, &FloppyStat) == 0 1.1.1.12 root 202: && (FloppyStat.st_mode & S_IWUSR)) 1.1.1.14 root 203: return false; 1.1.1.10 root 204: else 1.1.1.14 root 205: return true; 1.1.1.10 root 206: } 1.1.1.8 root 207: } 208: 209: 210: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 211: /** 212: * Test disk image for valid boot-sector. 213: * It has been noticed that some disks, eg blank images made by the MakeDisk 214: * utility or PaCifiST emulator fill in the boot-sector with incorrect information. 215: * Such images cannot be read correctly using a real ST, and also Hatari. 216: * To try and prevent data loss, we check for this error and flag the drive so 217: * the image will not be saved back to the file. 218: */ 1.1.1.12 root 219: static bool Floppy_IsBootSectorOK(int Drive) 1.1 root 220: { 1.1.1.10 root 221: Uint8 *pDiskBuffer; 1.1 root 222: 1.1.1.10 root 223: /* Does our drive have a disk in? */ 224: if (EmulationDrives[Drive].bDiskInserted) 225: { 226: pDiskBuffer = EmulationDrives[Drive].pBuffer; 227: 228: /* Check SPC (byte 13) for !=0 value. If is '0', invalid image and Hatari 229: * won't be-able to read (nor will a real ST)! */ 230: if (pDiskBuffer[13] != 0) 231: { 1.1.1.14 root 232: return true; /* Disk sector is OK! */ 1.1.1.10 root 233: } 234: else 235: { 236: Log_AlertDlg(LOG_WARN, "Disk in drive %c: maybe suffers from the Pacifist/Makedisk bug.\n" 237: "If it does not work, please repair the disk first!\n", 'A' + Drive); 238: } 239: } 1.1 root 240: 1.1.1.14 root 241: return false; /* Bad sector */ 1.1 root 242: } 243: 1.1.1.2 root 244: 245: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 246: /** 247: * Try to create disk B filename, eg 'auto_100a' becomes 'auto_100b' 248: * Return new filename if think we should try, otherwise NULL 1.1.1.12 root 249: * 250: * TODO: doesn't work with images in ZIP archives 1.1.1.11 root 251: */ 252: static char* Floppy_CreateDiskBFileName(const char *pSrcFileName) 1.1 root 253: { 1.1.1.10 root 254: char *szDir, *szName, *szExt; 1.1.1.12 root 255: 1.1.1.10 root 256: /* Allocate temporary memory for strings: */ 257: szDir = malloc(3 * FILENAME_MAX); 258: if (!szDir) 259: { 260: perror("Floppy_CreateDiskBFileName"); 1.1.1.14 root 261: return false; 1.1.1.10 root 262: } 263: szName = szDir + FILENAME_MAX; 264: szExt = szName + FILENAME_MAX; 265: 266: /* So, first split name into parts */ 1.1.1.11 root 267: File_SplitPath(pSrcFileName, szDir, szName, szExt); 1.1.1.10 root 268: 269: /* All OK? */ 270: if (strlen(szName) > 0) 271: { 1.1.1.12 root 272: char *last = &(szName[strlen(szName)-1]); 273: /* Now, did filename end with an 'A' or 'a' */ 274: if (*last == 'A' || *last == 'a') 1.1.1.10 root 275: { 1.1.1.11 root 276: char *szFull; 1.1.1.10 root 277: /* Change 'A' to a 'B' */ 1.1.1.12 root 278: *last += 1; 1.1.1.10 root 279: /* And re-build name into destination */ 1.1.1.11 root 280: szFull = File_MakePath(szDir, szName, szExt); 281: if (szFull) 282: { 283: /* Does file exist? */ 284: if (File_Exists(szFull)) 285: { 286: free(szDir); 287: return szFull; 288: } 289: free(szFull); 290: } 1.1.1.10 root 291: } 292: } 293: free(szDir); 1.1.1.11 root 294: return NULL; 1.1 root 295: } 296: 1.1.1.2 root 297: 298: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 299: /** 1.1.1.12 root 300: * Set floppy image to be ejected 301: */ 302: const char* Floppy_SetDiskFileNameNone(int Drive) 303: { 304: assert(Drive >= 0 && Drive < MAX_FLOPPYDRIVES); 305: ConfigureParams.DiskImage.szDiskFileName[Drive][0] = '\0'; 306: return ConfigureParams.DiskImage.szDiskFileName[Drive]; 1.1.1.6 root 307: } 308: 1.1.1.12 root 309: /*-----------------------------------------------------------------------*/ 310: /** 311: * Set given floppy drive image file name and handle 312: * different image extensions. 313: * Return corrected file name on success and NULL on failure. 1.1.1.11 root 314: */ 1.1.1.12 root 315: const char* Floppy_SetDiskFileName(int Drive, const char *pszFileName, const char *pszZipPath) 1.1.1.6 root 316: { 1.1.1.11 root 317: char *filename; 1.1.1.16 root 318: int i; 1.1 root 319: 1.1.1.12 root 320: /* setting to empty or "none" ejects */ 321: if (!*pszFileName || strcasecmp(pszFileName, "none") == 0) 322: { 323: return Floppy_SetDiskFileNameNone(Drive); 324: } 1.1.1.11 root 325: /* See if file exists, and if not, get/add correct extension */ 1.1.1.12 root 326: if (!File_Exists(pszFileName)) 1.1.1.11 root 327: filename = File_FindPossibleExtFileName(pszFileName, pszDiskImageNameExts); 328: else 329: filename = strdup(pszFileName); 1.1.1.12 root 330: if (!filename) 331: { 332: Log_AlertDlg(LOG_INFO, "Image '%s' not found", pszFileName); 333: return NULL; 334: } 335: 336: /* If we insert a disk into Drive A, should we try to put disk 2 into drive B? */ 337: if (Drive == 0 && ConfigureParams.DiskImage.bAutoInsertDiskB) 338: { 339: /* Attempt to make up second filename, eg was 'auto_100a' to 'auto_100b' */ 340: char *szDiskBFileName = Floppy_CreateDiskBFileName(filename); 341: if (szDiskBFileName) 342: { 343: /* recurse with Drive B */ 344: Floppy_SetDiskFileName(1, szDiskBFileName, pszZipPath); 345: free(szDiskBFileName); 346: } 347: } 348: 1.1.1.16 root 349: /* validity checks */ 1.1.1.12 root 350: assert(Drive >= 0 && Drive < MAX_FLOPPYDRIVES); 1.1.1.16 root 351: for (i = 0; i < MAX_FLOPPYDRIVES; i++) 352: { 353: if (i == Drive) 354: continue; 355: /* prevent inserting same image to multiple drives */ 356: if (strcmp(filename, ConfigureParams.DiskImage.szDiskFileName[i]) == 0) 357: { 358: Log_AlertDlg(LOG_ERROR, "ERROR: Cannot insert same floppy to multiple drives!"); 359: return NULL; 360: } 361: } 362: 363: /* do the changes */ 1.1.1.12 root 364: if (pszZipPath) 365: strcpy(ConfigureParams.DiskImage.szDiskZipPath[Drive], pszZipPath); 366: else 367: ConfigureParams.DiskImage.szDiskZipPath[Drive][0] = '\0'; 368: strcpy(ConfigureParams.DiskImage.szDiskFileName[Drive], filename); 369: free(filename); 370: //File_MakeAbsoluteName(ConfigureParams.DiskImage.szDiskFileName[Drive]); 371: return ConfigureParams.DiskImage.szDiskFileName[Drive]; 372: } 373: 374: /*-----------------------------------------------------------------------*/ 375: /** 1.1.1.18 root 376: * Update the drive when a disk is inserted or ejected. Depending on the state, 377: * we change the Write Protect bit for the drive (the TOS and other programs 378: * monitor this bit to detect that a disk was changed in the drive ; see fdc.c) 379: * The floppy drive transition can be a single action ("eject" or "insert"), or 380: * two actions ("eject then insert" or "insert then eject"). 381: * First action is stored in State1 ; State2 store the second (or last) action. 382: * In case the user eject/insert several disks before returning to emulation, 383: * State1 will contain the first action, and State2 the latest action (intermediate 384: * actions are ignored, as they wouldn't be seen while the emulation is paused). 385: * Each action will take FLOPPY_DRIVE_TRANSITION_DELAY_VBL * 2 VBLs to execute, 386: * see fdc.c for details. 387: */ 388: static void Floppy_DriveTransitionSetState ( int Drive , int State ) 389: { 390: /* First, update State1 and State2 depending on the current VBL number */ 391: /* (we discard the return value as we don't want to update FDC.STR now) */ 392: Floppy_DriveTransitionUpdateState ( Drive ); 393: 394: /* If State1 is not defined yet, we set it */ 395: if ( EmulationDrives[Drive].TransitionState1 == 0 ) 396: { 397: EmulationDrives[Drive].TransitionState1 = State; 398: EmulationDrives[Drive].TransitionState1_VBL = nVBLs; 399: /* Cancel State2 in case we start a new transition before State2 was over */ 400: EmulationDrives[Drive].TransitionState2 = 0; 401: } 402: 403: /* State1 is already set, so we set State2 */ 404: else 405: { 406: /* If State2 == State1, ignore it (eg : two inserts in a row) */ 407: if ( EmulationDrives[Drive].TransitionState1 == State ) 408: EmulationDrives[Drive].TransitionState2 = 0; 409: else 410: { 411: /* Set State2 just after State1 ends */ 412: EmulationDrives[Drive].TransitionState2 = State; 413: EmulationDrives[Drive].TransitionState2_VBL = EmulationDrives[Drive].TransitionState1_VBL + FLOPPY_DRIVE_TRANSITION_DELAY_VBL * 2; 414: } 415: } 416: //fprintf ( stderr , "drive transition state1 %d %d state2 %d %d\n" , 417: // EmulationDrives[Drive].TransitionState1 , EmulationDrives[Drive].TransitionState1_VBL, 418: // EmulationDrives[Drive].TransitionState2 , EmulationDrives[Drive].TransitionState2_VBL ); 419: } 420: 421: 422: /*-----------------------------------------------------------------------*/ 423: /** 424: * When a disk is inserted or ejected, each transition has 2 phases that 425: * lasts FLOPPY_DRIVE_TRANSITION_DELAY_VBL VBLs. This function checks if 426: * we're during one of these transition phases and tells if the Write 427: * Protect signal should be overwritten. 428: * Returns 0 if there's no change, 1 if WPRT should be forced to 1 and 429: * -1 if WPRT should be forced to 0 (see fdc.c for details). 430: */ 431: int Floppy_DriveTransitionUpdateState ( int Drive ) 432: { 433: int Force = 0; 434: 435: if ( EmulationDrives[Drive].TransitionState1 != 0 ) 436: { 437: if ( nVBLs >= EmulationDrives[Drive].TransitionState1_VBL + FLOPPY_DRIVE_TRANSITION_DELAY_VBL * 2 ) 438: EmulationDrives[Drive].TransitionState1 = 0; /* State1's delay elapsed */ 439: else if ( nVBLs >= EmulationDrives[Drive].TransitionState1_VBL + FLOPPY_DRIVE_TRANSITION_DELAY_VBL ) 440: { 441: if ( EmulationDrives[Drive].TransitionState1 == FLOPPY_DRIVE_TRANSITION_STATE_INSERT ) 442: Force = -1; /* Insert phase 2 : clear WPRT */ 443: else 444: Force = 1; /* Eject phase 2 : set WPRT */ 445: } 446: else 447: { 448: if ( EmulationDrives[Drive].TransitionState1 == FLOPPY_DRIVE_TRANSITION_STATE_INSERT ) 449: Force = 1; /* Insert phase 1 : set WPRT */ 450: else 451: Force = -1; /* Eject phase 1 : clear WPRT */ 452: } 453: } 454: 455: if ( ( EmulationDrives[Drive].TransitionState2 != 0 ) 456: && ( nVBLs >= EmulationDrives[Drive].TransitionState2_VBL ) ) 457: { 458: if ( nVBLs >= EmulationDrives[Drive].TransitionState2_VBL + FLOPPY_DRIVE_TRANSITION_DELAY_VBL * 2 ) 459: EmulationDrives[Drive].TransitionState2 = 0; /* State2's delay elapsed */ 460: else if ( nVBLs >= EmulationDrives[Drive].TransitionState2_VBL + FLOPPY_DRIVE_TRANSITION_DELAY_VBL ) 461: { 462: if ( EmulationDrives[Drive].TransitionState2 == FLOPPY_DRIVE_TRANSITION_STATE_INSERT ) 463: Force = -1; /* Insert phase 2 : clear WPRT */ 464: else 465: Force = 1; /* Eject phase 2 : set WPRT */ 466: } 467: else 468: { 469: if ( EmulationDrives[Drive].TransitionState2 == FLOPPY_DRIVE_TRANSITION_STATE_INSERT ) 470: Force = 1; /* Insert phase 1 : set WPRT */ 471: else 472: Force = -1; /* Eject phase 1 : clear WPRT */ 473: } 474: } 475: 476: 477: return Force; 478: } 479: 480: 481: /*-----------------------------------------------------------------------*/ 482: /** 1.1.1.12 root 483: * Insert previously set disk file image into floppy drive. 484: * The WHOLE image is copied into Hatari drive buffers, and 485: * uncompressed if necessary. 486: * Return TRUE on success, false otherwise. 487: */ 488: bool Floppy_InsertDiskIntoDrive(int Drive) 489: { 490: long nImageBytes = 0; 491: char *filename; 492: 493: /* Eject disk, if one is inserted (doesn't inform user) */ 494: assert(Drive >= 0 && Drive < MAX_FLOPPYDRIVES); 495: Floppy_EjectDiskFromDrive(Drive); 496: 497: filename = ConfigureParams.DiskImage.szDiskFileName[Drive]; 498: if (!filename[0]) 499: { 1.1.1.14 root 500: return true; /* only do eject */ 1.1.1.12 root 501: } 502: if (!File_Exists(filename)) 503: { 504: Log_AlertDlg(LOG_INFO, "Image '%s' not found", filename); 1.1.1.14 root 505: return false; 1.1.1.12 root 506: } 507: 1.1.1.10 root 508: /* Check disk image type and read the file: */ 1.1.1.14 root 509: if (MSA_FileNameIsMSA(filename, true)) 1.1.1.11 root 510: EmulationDrives[Drive].pBuffer = MSA_ReadDisk(filename, &nImageBytes); 1.1.1.14 root 511: else if (ST_FileNameIsST(filename, true)) 1.1.1.11 root 512: EmulationDrives[Drive].pBuffer = ST_ReadDisk(filename, &nImageBytes); 1.1.1.14 root 513: else if (DIM_FileNameIsDIM(filename, true)) 1.1.1.11 root 514: EmulationDrives[Drive].pBuffer = DIM_ReadDisk(filename, &nImageBytes); 515: else if (ZIP_FileNameIsZIP(filename)) 1.1.1.12 root 516: { 517: const char *zippath = ConfigureParams.DiskImage.szDiskZipPath[Drive]; 518: EmulationDrives[Drive].pBuffer = ZIP_ReadDisk(filename, zippath, &nImageBytes); 519: } 1.1.1.11 root 520: 521: if (EmulationDrives[Drive].pBuffer == NULL) 522: { 1.1.1.14 root 523: return false; 1.1.1.11 root 524: } 1.1.1.13 root 525: 526: /* Store image filename (required for ejecting the disk later!) */ 527: strcpy(EmulationDrives[Drive].sFileName, filename); 528: 529: /* Store size and set drive states */ 1.1.1.11 root 530: EmulationDrives[Drive].nImageBytes = nImageBytes; 1.1.1.14 root 531: EmulationDrives[Drive].bDiskInserted = true; 532: EmulationDrives[Drive].bContentsChanged = false; 1.1.1.11 root 533: EmulationDrives[Drive].bOKToSave = Floppy_IsBootSectorOK(Drive); 1.1.1.18 root 534: Floppy_DriveTransitionSetState ( Drive , FLOPPY_DRIVE_TRANSITION_STATE_INSERT ); 1.1.1.12 root 535: Log_Printf(LOG_INFO, "Inserted disk '%s' to drive %c:.", 536: filename, 'A'+Drive); 1.1.1.14 root 537: return true; 1.1 root 538: } 539: 1.1.1.2 root 540: 541: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 542: /** 543: * Eject disk from floppy drive, save contents back to PCs hard-drive if 544: * they have been changed. 1.1.1.14 root 545: * Return true if there was something to eject. 1.1.1.11 root 546: */ 1.1.1.12 root 547: bool Floppy_EjectDiskFromDrive(int Drive) 1.1 root 548: { 1.1.1.14 root 549: bool bEjected = false; 1.1.1.12 root 550: 1.1.1.10 root 551: /* Does our drive have a disk in? */ 552: if (EmulationDrives[Drive].bDiskInserted) 553: { 1.1.1.16 root 554: bool bSaved = false; 1.1.1.13 root 555: char *psFileName = EmulationDrives[Drive].sFileName; 1.1.1.12 root 556: 1.1.1.10 root 557: /* OK, has contents changed? If so, need to save */ 558: if (EmulationDrives[Drive].bContentsChanged) 559: { 560: /* Is OK to save image (if boot-sector is bad, don't allow a save) */ 561: if (EmulationDrives[Drive].bOKToSave && !Floppy_IsWriteProtected(Drive)) 562: { 563: /* Save as .MSA or .ST image? */ 1.1.1.14 root 564: if (MSA_FileNameIsMSA(psFileName, true)) 1.1.1.16 root 565: bSaved = MSA_WriteDisk(psFileName, EmulationDrives[Drive].pBuffer, EmulationDrives[Drive].nImageBytes); 1.1.1.14 root 566: else if (ST_FileNameIsST(psFileName, true)) 1.1.1.16 root 567: bSaved = ST_WriteDisk(psFileName, EmulationDrives[Drive].pBuffer, EmulationDrives[Drive].nImageBytes); 1.1.1.14 root 568: else if (DIM_FileNameIsDIM(psFileName, true)) 1.1.1.16 root 569: bSaved = DIM_WriteDisk(psFileName, EmulationDrives[Drive].pBuffer, EmulationDrives[Drive].nImageBytes); 1.1.1.12 root 570: else if (ZIP_FileNameIsZIP(psFileName)) 1.1.1.16 root 571: bSaved = ZIP_WriteDisk(psFileName, EmulationDrives[Drive].pBuffer, EmulationDrives[Drive].nImageBytes); 572: if (bSaved) 573: Log_Printf(LOG_INFO, "Updated the contents of floppy image '%s'.", psFileName); 574: else 575: Log_Printf(LOG_INFO, "Writing of this format failed or not supported, discarded the contents\n of floppy image '%s'.", psFileName); 576: } else 577: Log_Printf(LOG_INFO, "Writing not possible, discarded the contents of floppy image\n '%s'.", psFileName); 1.1.1.10 root 578: } 579: 580: /* Inform user that disk has been ejected! */ 1.1.1.12 root 581: Log_Printf(LOG_INFO, "Floppy %c: has been removed from drive.", 582: 'A'+Drive); 583: 1.1.1.18 root 584: Floppy_DriveTransitionSetState ( Drive , FLOPPY_DRIVE_TRANSITION_STATE_EJECT ); 1.1.1.14 root 585: bEjected = true; 1.1.1.10 root 586: } 587: 588: /* Drive is now empty */ 589: if (EmulationDrives[Drive].pBuffer != NULL) 590: { 591: free(EmulationDrives[Drive].pBuffer); 592: EmulationDrives[Drive].pBuffer = NULL; 593: } 1.1.1.12 root 594: 1.1.1.13 root 595: EmulationDrives[Drive].sFileName[0] = '\0'; 1.1.1.10 root 596: EmulationDrives[Drive].nImageBytes = 0; 1.1.1.14 root 597: EmulationDrives[Drive].bDiskInserted = false; 598: EmulationDrives[Drive].bContentsChanged = false; 599: EmulationDrives[Drive].bOKToSave = false; 1.1.1.12 root 600: 601: return bEjected; 1.1 root 602: } 603: 1.1.1.2 root 604: 605: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 606: /** 607: * Eject all disk image from floppy drives - call when quit. 1.1.1.14 root 608: * Return true if there was something to eject. 1.1.1.11 root 609: */ 1.1.1.13 root 610: static bool Floppy_EjectBothDrives(void) 1.1 root 611: { 1.1.1.12 root 612: bool bEjectedA, bEjectedB; 613: 1.1.1.10 root 614: /* Eject disk images from drives 'A' and 'B' */ 1.1.1.12 root 615: bEjectedA = Floppy_EjectDiskFromDrive(0); 616: bEjectedB = Floppy_EjectDiskFromDrive(1); 617: 618: return bEjectedA || bEjectedB; 1.1 root 619: } 620: 1.1.1.2 root 621: 622: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 623: /** 624: * Double-check information read from boot-sector as this is sometimes found to 1.1.1.17 root 625: * be incorrect. The .ST image file should be divisible by the sector size, 626: * the sectors per track. the number of tracks and the number of sides. 1.1.1.11 root 627: * NOTE - Pass information from boot-sector to this function (if we can't 628: * decide we leave it alone). 629: */ 1.1.1.17 root 630: static void Floppy_DoubleCheckFormat(long nDiskSize, long nSectorsPerDisk, Uint16 *pnSides, Uint16 *pnSectorsPerTrack) 1.1 root 631: { 1.1.1.17 root 632: long TotalSectors; 633: int Sides_fixed; 634: int SectorsPerTrack_fixed; 1.1 root 635: 1.1.1.10 root 636: /* Now guess at number of sides */ 1.1.1.17 root 637: if ( nDiskSize < (500*1024) ) /* If size >500k assume 2 sides */ 638: Sides_fixed = 1; 1.1.1.10 root 639: else 1.1.1.17 root 640: Sides_fixed = 2; 1.1.1.10 root 641: 1.1.1.17 root 642: /* Number of 512 bytes sectors for this disk image */ 643: TotalSectors = nDiskSize / 512; 644: 645: /* Check some common values */ 646: if ( TotalSectors == 80*9*Sides_fixed ) { SectorsPerTrack_fixed = 9; } 647: else if ( TotalSectors == 81*9*Sides_fixed ) { SectorsPerTrack_fixed = 9; } 648: else if ( TotalSectors == 82*9*Sides_fixed ) { SectorsPerTrack_fixed = 9; } 1.1.1.18 root 649: else if ( TotalSectors == 83*9*Sides_fixed ) { SectorsPerTrack_fixed = 9; } 650: else if ( TotalSectors == 84*9*Sides_fixed ) { SectorsPerTrack_fixed = 9; } 1.1.1.17 root 651: else if ( TotalSectors == 80*10*Sides_fixed ) { SectorsPerTrack_fixed = 10; } 652: else if ( TotalSectors == 81*10*Sides_fixed ) { SectorsPerTrack_fixed = 10; } 653: else if ( TotalSectors == 82*10*Sides_fixed ) { SectorsPerTrack_fixed = 10; } 1.1.1.18 root 654: else if ( TotalSectors == 83*10*Sides_fixed ) { SectorsPerTrack_fixed = 10; } 655: else if ( TotalSectors == 84*10*Sides_fixed ) { SectorsPerTrack_fixed = 10; } 1.1.1.17 root 656: else if ( TotalSectors == 80*11*Sides_fixed ) { SectorsPerTrack_fixed = 11; } 657: else if ( TotalSectors == 81*11*Sides_fixed ) { SectorsPerTrack_fixed = 11; } 658: else if ( TotalSectors == 82*11*Sides_fixed ) { SectorsPerTrack_fixed = 11; } 1.1.1.18 root 659: else if ( TotalSectors == 83*11*Sides_fixed ) { SectorsPerTrack_fixed = 11; } 660: else if ( TotalSectors == 84*11*Sides_fixed ) { SectorsPerTrack_fixed = 11; } 1.1.1.17 root 661: else if ( TotalSectors == 80*12*Sides_fixed ) { SectorsPerTrack_fixed = 12; } 662: else if ( TotalSectors == 81*12*Sides_fixed ) { SectorsPerTrack_fixed = 12; } 663: else if ( TotalSectors == 82*12*Sides_fixed ) { SectorsPerTrack_fixed = 12; } 1.1.1.18 root 664: else if ( TotalSectors == 83*12*Sides_fixed ) { SectorsPerTrack_fixed = 12; } 665: else if ( TotalSectors == 84*12*Sides_fixed ) { SectorsPerTrack_fixed = 12; } 1.1.1.17 root 666: 667: /* unknown combination, assume boot sector is correct */ 668: else { SectorsPerTrack_fixed = *pnSectorsPerTrack; } 669: 670: /* Valid new values if necessary */ 671: if ( ( *pnSides != Sides_fixed ) || ( *pnSectorsPerTrack != SectorsPerTrack_fixed ) ) 1.1.1.10 root 672: { 1.1.1.17 root 673: #if 0 674: int TracksPerDisk_fixed = TotalSectors / ( SectorsPerTrack_fixed * Sides_fixed ); 675: Log_Printf(LOG_WARN, "Floppy_DoubleCheckFormat: boot sector doesn't match disk image's size :" 676: " total sectors %ld->%ld sides %d->%d sectors %d->%d tracks %d\n", 677: nSectorsPerDisk , TotalSectors , *pnSides , Sides_fixed , *pnSectorsPerTrack , SectorsPerTrack_fixed , TracksPerDisk_fixed ); 678: #endif 679: *pnSides = Sides_fixed; 680: *pnSectorsPerTrack = SectorsPerTrack_fixed; 1.1.1.10 root 681: } 1.1 root 682: } 683: 1.1.1.2 root 684: 685: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 686: /** 687: * Find details of disk image. We need to do this via a function as sometimes the boot-block 688: * is not actually correct with the image - some demos/game disks have incorrect bytes in the 689: * boot sector and this attempts to find the correct values. 690: */ 1.1.1.9 root 691: void Floppy_FindDiskDetails(const Uint8 *pBuffer, int nImageBytes, 1.1.1.12 root 692: Uint16 *pnSectorsPerTrack, Uint16 *pnSides) 1.1 root 693: { 1.1.1.17 root 694: Uint16 nSectorsPerTrack, nSides, nSectorsPerDisk; 1.1 root 695: 1.1.1.10 root 696: /* First do check to find number of sectors and bytes per sector */ 697: nSectorsPerTrack = SDL_SwapLE16(*(const Uint16 *)(pBuffer+24)); /* SPT */ 698: nSides = SDL_SwapLE16(*(const Uint16 *)(pBuffer+26)); /* SIDE */ 1.1.1.17 root 699: nSectorsPerDisk = pBuffer[19] | (pBuffer[20] << 8); /* total sectors */ 1.1.1.10 root 700: 701: /* If the number of sectors announced is incorrect, the boot-sector may 702: * contain incorrect information, eg the 'Eat.st' demo, or wrongly imaged 703: * single/double sided floppies... */ 1.1.1.17 root 704: if (nSectorsPerDisk != nImageBytes/512) 705: Floppy_DoubleCheckFormat(nImageBytes, nSectorsPerDisk, &nSides, &nSectorsPerTrack); 1.1.1.10 root 706: 707: /* And set values */ 708: if (pnSectorsPerTrack) 709: *pnSectorsPerTrack = nSectorsPerTrack; 710: if (pnSides) 711: *pnSides = nSides; 1.1 root 712: } 713: 1.1.1.2 root 714: 715: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 716: /** 717: * Read sectors from floppy disk image, return TRUE if all OK 718: * NOTE Pass -ve as Count to read whole track 719: */ 1.1.1.12 root 720: bool Floppy_ReadSectors(int Drive, Uint8 *pBuffer, Uint16 Sector, 721: Uint16 Track, Uint16 Side, short Count, 1.1.1.18 root 722: int *pnSectorsPerTrack, int *pSectorSize) 1.1 root 723: { 1.1.1.10 root 724: Uint8 *pDiskBuffer; 1.1.1.12 root 725: Uint16 nSectorsPerTrack, nSides, nBytesPerTrack; 1.1.1.10 root 726: long Offset; 727: int nImageTracks; 728: 729: /* Do we have a disk in our drive? */ 730: if (EmulationDrives[Drive].bDiskInserted) 731: { 732: /* Looks good */ 733: pDiskBuffer = EmulationDrives[Drive].pBuffer; 734: 735: /* Find #sides and #sectors per track */ 736: Floppy_FindDiskDetails(EmulationDrives[Drive].pBuffer,EmulationDrives[Drive].nImageBytes,&nSectorsPerTrack,&nSides); 737: nImageTracks = ((EmulationDrives[Drive].nImageBytes / NUMBYTESPERSECTOR) / nSectorsPerTrack) / nSides; 738: 739: /* Need to read whole track? */ 740: if (Count<0) 741: Count = nSectorsPerTrack; 742: /* Write back number of sector per track */ 743: if (pnSectorsPerTrack) 744: *pnSectorsPerTrack = nSectorsPerTrack; 745: 1.1.1.18 root 746: if (pSectorSize) 747: *pSectorSize = NUMBYTESPERSECTOR; /* Size is 512 bytes for ST/MSA */ 748: 1.1.1.10 root 749: /* Debug check as if we read over the end of a track we read into side 2! */ 750: if (Count > nSectorsPerTrack) 751: { 752: Log_Printf(LOG_DEBUG, "Floppy_ReadSectors: reading over single track\n"); 753: } 754: 755: /* Check that the side number (0 or 1) does not exceed the amount of sides (1 or 2). 756: * (E.g. some games like Drakkhen or Bolo can load additional data from the 757: * second disk side, but they also work with single side floppy drives) */ 758: if (Side >= nSides) 759: { 760: Log_Printf(LOG_DEBUG, "Floppy_ReadSectors: Program tries to read from side %i " 761: "of a disk image with %i sides!\n", Side+1, nSides); 1.1.1.14 root 762: return false; 1.1.1.10 root 763: } 764: 765: /* Check if track number is in range */ 766: if (Track >= nImageTracks) 767: { 768: Log_Printf(LOG_DEBUG, "Floppy_ReadSectors: Program tries to read from track %i " 769: "of a disk image with only %i tracks!\n", Track, nImageTracks); 1.1.1.14 root 770: return false; 1.1.1.10 root 771: } 772: 773: /* Check if sector number is in range */ 774: if (Sector <= 0 || Sector > nSectorsPerTrack) 775: { 776: Log_Printf(LOG_DEBUG, "Floppy_ReadSectors: Program tries to read from sector %i " 777: "of a disk image with %i sectors per track!\n", Sector, nSectorsPerTrack); 1.1.1.14 root 778: return false; 1.1.1.10 root 779: } 780: 781: /* Seek to sector */ 782: nBytesPerTrack = NUMBYTESPERSECTOR*nSectorsPerTrack; 783: Offset = nBytesPerTrack*Side; /* First seek to side */ 784: Offset += (nBytesPerTrack*nSides)*Track; /* Then seek to track */ 785: Offset += (NUMBYTESPERSECTOR*(Sector-1)); /* And finally to sector */ 1.1.1.6 root 786: 1.1.1.10 root 787: /* Read sectors (usually 512 bytes per sector) */ 788: memcpy(pBuffer, pDiskBuffer+Offset, (int)Count*NUMBYTESPERSECTOR); 1.1 root 789: 1.1.1.14 root 790: return true; 1.1.1.10 root 791: } 1.1 root 792: 1.1.1.14 root 793: return false; 1.1 root 794: } 795: 1.1.1.2 root 796: 797: /*-----------------------------------------------------------------------*/ 1.1.1.11 root 798: /** 799: * Write sectors from floppy disk image, return TRUE if all OK 800: * NOTE Pass -ve as Count to write whole track 801: */ 1.1.1.12 root 802: bool Floppy_WriteSectors(int Drive, Uint8 *pBuffer, Uint16 Sector, 803: Uint16 Track, Uint16 Side, short Count, 1.1.1.18 root 804: int *pnSectorsPerTrack, int *pSectorSize) 1.1 root 805: { 1.1.1.10 root 806: Uint8 *pDiskBuffer; 1.1.1.12 root 807: Uint16 nSectorsPerTrack, nSides, nBytesPerTrack; 1.1.1.10 root 808: long Offset; 809: int nImageTracks; 810: 811: /* Do we have a writable disk in our drive? */ 812: if (EmulationDrives[Drive].bDiskInserted && !Floppy_IsWriteProtected(Drive)) 813: { 814: /* Looks good */ 815: pDiskBuffer = EmulationDrives[Drive].pBuffer; 816: 817: /* Find #sides and #sectors per track */ 818: Floppy_FindDiskDetails(EmulationDrives[Drive].pBuffer,EmulationDrives[Drive].nImageBytes,&nSectorsPerTrack,&nSides); 819: nImageTracks = ((EmulationDrives[Drive].nImageBytes / NUMBYTESPERSECTOR) / nSectorsPerTrack) / nSides; 820: 821: /* Need to write whole track? */ 822: if (Count<0) 823: Count = nSectorsPerTrack; 824: /* Write back number of sector per track */ 825: if (pnSectorsPerTrack) 826: *pnSectorsPerTrack = nSectorsPerTrack; 827: 1.1.1.18 root 828: if (pSectorSize) 829: *pSectorSize = NUMBYTESPERSECTOR; /* Size is 512 bytes for ST/MSA */ 830: 1.1.1.10 root 831: /* Debug check as if we write over the end of a track we write into side 2! */ 832: if (Count > nSectorsPerTrack) 833: { 834: Log_Printf(LOG_DEBUG, "Floppy_WriteSectors: writing over single track\n"); 835: } 836: 837: /* Check that the side number (0 or 1) does not exceed the amount of sides (1 or 2). */ 838: if (Side >= nSides) 839: { 840: Log_Printf(LOG_DEBUG, "Floppy_WriteSectors: Program tries to write to side %i " 841: "of a disk image with %i sides!\n", Side+1, nSides); 1.1.1.14 root 842: return false; 1.1.1.10 root 843: } 844: 845: /* Check if track number is in range */ 846: if (Track >= nImageTracks) 847: { 848: Log_Printf(LOG_DEBUG, "Floppy_WriteSectors: Program tries to write to track %i " 849: "of a disk image with only %i tracks!\n", Track, nImageTracks); 1.1.1.14 root 850: return false; 1.1.1.10 root 851: } 852: 853: /* Check if sector number is in range */ 854: if (Sector <= 0 || Sector > nSectorsPerTrack) 855: { 856: Log_Printf(LOG_DEBUG, "Floppy_WriteSectors: Program tries to write to sector %i " 857: "of a disk image with %i sectors per track!\n", Sector, nSectorsPerTrack); 1.1.1.14 root 858: return false; 1.1.1.10 root 859: } 860: 861: /* Seek to sector */ 862: nBytesPerTrack = NUMBYTESPERSECTOR*nSectorsPerTrack; 863: Offset = nBytesPerTrack*Side; /* First seek to side */ 864: Offset += (nBytesPerTrack*nSides)*Track; /* Then seek to track */ 865: Offset += (NUMBYTESPERSECTOR*(Sector-1)); /* And finally to sector */ 866: 867: /* Write sectors (usually 512 bytes per sector) */ 868: memcpy(pDiskBuffer+Offset, pBuffer, (int)Count*NUMBYTESPERSECTOR); 869: /* And set 'changed' flag */ 1.1.1.14 root 870: EmulationDrives[Drive].bContentsChanged = true; 1.1 root 871: 1.1.1.14 root 872: return true; 1.1.1.10 root 873: } 1.1 root 874: 1.1.1.14 root 875: return false; 1.1 root 876: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.