Annotation of hatari/src/floppy.c, revision 1.1.1.18

1.1       root        1: /*
1.1.1.5   root        2:   Hatari - floppy.c
1.1       root        3: 
1.1.1.5   root        4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
                      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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.