--- BasiliskII/src/disk.cpp 1999/10/03 14:16:25 1.1.1.1 +++ BasiliskII/src/disk.cpp 2008/01/01 09:40:31 1.19 @@ -1,7 +1,7 @@ /* * disk.cpp - Generic disk driver * - * Basilisk II (C) 1997-1999 Christian Bauer + * Basilisk II (C) 1997-2008 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,9 +26,15 @@ * Technote FL 24: "Don't Look at ioPosOffset for Devices" */ +#include "sysdeps.h" + #include +#include + +#ifndef NO_STD_NAMESPACE +using std::vector; +#endif -#include "sysdeps.h" #include "cpu_emulation.h" #include "main.h" #include "macos_util.h" @@ -65,45 +71,77 @@ const uint8 DiskIcon[258] = { // Struct for each drive -struct DriveInfo { - DriveInfo() - { - next = NULL; - num = 0; - fh = NULL; - read_only = false; - status = 0; - } +struct disk_drive_info { + disk_drive_info() : num(0), fh(NULL), start_byte(0), read_only(false), status(0) {} + disk_drive_info(void *fh_, bool ro) : num(0), fh(fh_), read_only(ro), status(0) {} + + void close_fh(void) { Sys_close(fh); } - DriveInfo *next; // Pointer to next DriveInfo (must be first in struct!) int num; // Drive number void *fh; // File handle + loff_t start_byte; // Start of HFS partition on disk uint32 num_blocks; // Size in 512-byte blocks bool to_be_mounted; // Flag: drive must be mounted in accRun bool read_only; // Flag: force write protection uint32 status; // Mac address of drive status record }; -// Linked list of DriveInfos -static DriveInfo *first_drive_info; +// List of drives handled by this driver +typedef vector drive_vec; +static drive_vec drives; // Icon address (Mac address space, set by PatchROM()) uint32 DiskIconAddr; +// Flag: Control(accRun) has been called, interrupt routine is now active +static bool acc_run_called = false; + /* - * Get pointer to drive info, NULL = invalid drive number + * Get pointer to drive info or drives.end() if not found */ -static DriveInfo *get_drive_info(int num) +static drive_vec::iterator get_drive_info(int num) { - DriveInfo *info = first_drive_info; - while (info != NULL) { + drive_vec::iterator info, end = drives.end(); + for (info = drives.begin(); info != end; ++info) { if (info->num == num) return info; - info = info->next; } - return NULL; + return info; +} + + +/* + * Find HFS partition, set info->start_byte and info->num_blocks + * (0 = no partition map or HFS partition found, assume flat disk image) + */ + +static void find_hfs_partition(disk_drive_info &info) +{ + info.start_byte = 0; + info.num_blocks = 0; + uint8 *map = new uint8[512]; + + // Search first 64 blocks for HFS partition + for (int i=0; i<64; i++) { + if (Sys_read(info.fh, map, i * 512, 512) != 512) + break; + + // Not a partition map block? Then look at next block + uint16 sig = (map[0] << 8) | map[1]; + if (sig != 0x504d) + continue; + + // Partition map block found, Apple HFS partition? + if (strcmp((char *)(map + 48), "Apple_HFS") == 0) { + info.start_byte = (loff_t)((map[8] << 24) | (map[9] << 16) | (map[10] << 8) | map[11]) << 9; + info.num_blocks = (map[12] << 24) | (map[13] << 16) | (map[14] << 8) | map[15]; + D(bug(" HFS partition found at %d, %d blocks\n", info.start_byte, info.num_blocks)); + break; + } + } + delete[] map; } @@ -113,14 +151,12 @@ static DriveInfo *get_drive_info(int num void DiskInit(void) { - first_drive_info = NULL; - // No drives specified in prefs? Then add defaults if (PrefsFindString("disk", 0) == NULL) SysAddDiskPrefs(); // Add drives specified in preferences - int32 index = 0; + int index = 0; const char *str; while ((str = PrefsFindString("disk", index++)) != NULL) { bool read_only = false; @@ -129,16 +165,8 @@ void DiskInit(void) str++; } void *fh = Sys_open(str, read_only); - if (fh) { - D(bug(" adding drive '%s'\n", str)); - DriveInfo *info = new DriveInfo; - info->fh = fh; - info->read_only = SysIsReadOnly(fh); - DriveInfo *p = (DriveInfo *)&first_drive_info; - while (p->next != NULL) - p = p->next; - p->next = info; - } + if (fh) + drives.push_back(disk_drive_info(fh, SysIsReadOnly(fh))); } } @@ -149,13 +177,10 @@ void DiskInit(void) void DiskExit(void) { - DriveInfo *info = first_drive_info, *next; - while (info != NULL) { - Sys_close(info->fh); - next = info->next; - delete info; - info = next; - } + drive_vec::iterator info, end = drives.end(); + for (info = drives.begin(); info != end; ++info) + info->close_fh(); + drives.clear(); } @@ -165,14 +190,17 @@ void DiskExit(void) bool DiskMountVolume(void *fh) { - DriveInfo *info; - for (info = first_drive_info; info != NULL && info->fh != fh; info = info->next) ; - if (info) { + drive_vec::iterator info = drives.begin(), end = drives.end(); + while (info != end && info->fh != fh) + ++info; + if (info != end) { if (SysIsDiskInserted(info->fh)) { info->read_only = SysIsReadOnly(info->fh); WriteMacInt8(info->status + dsDiskInPlace, 1); // Inserted removable disk WriteMacInt8(info->status + dsWriteProt, info->read_only ? 0xff : 0); - info->num_blocks = SysGetFileSize(info->fh) / 512; + find_hfs_partition(*info); + if (info->start_byte == 0) + info->num_blocks = SysGetFileSize(info->fh) / 512; WriteMacInt16(info->status + dsDriveSize, info->num_blocks & 0xffff); WriteMacInt16(info->status + dsDriveS1, info->num_blocks >> 16); info->to_be_mounted = true; @@ -184,6 +212,37 @@ bool DiskMountVolume(void *fh) /* + * Mount volumes for which the to_be_mounted flag is set + * (called during interrupt time) + */ + +static void mount_mountable_volumes(void) +{ + drive_vec::iterator info, end = drives.end(); + for (info = drives.begin(); info != end; ++info) { + + // Disk in drive? + if (!ReadMacInt8(info->status + dsDiskInPlace)) { + + // No, check if disk was inserted + if (SysIsDiskInserted(info->fh)) + DiskMountVolume(info->fh); + } + + // Mount disk if flagged + if (info->to_be_mounted) { + D(bug(" mounting drive %d\n", info->num)); + M68kRegisters r; + r.d[0] = info->num; + r.a[0] = 7; // diskEvent + Execute68kTrap(0xa02f, &r); // PostEvent() + info->to_be_mounted = false; + } + } +} + + +/* * Driver Open() routine */ @@ -193,9 +252,11 @@ int16 DiskOpen(uint32 pb, uint32 dce) // Set up DCE WriteMacInt32(dce + dCtlPosition, 0); + acc_run_called = false; // Install drives - for (DriveInfo *info = first_drive_info; info; info = info->next) { + drive_vec::iterator info, end = drives.end(); + for (info = drives.begin(); info != end; ++info) { info->num = FindFreeDriveNumber(1); info->to_be_mounted = false; @@ -225,10 +286,12 @@ int16 DiskOpen(uint32 pb, uint32 dce) if (disk_in_place) { D(bug(" disk inserted\n")); WriteMacInt8(info->status + dsWriteProt, info->read_only ? 0x80 : 0); - info->num_blocks = SysGetFileSize(info->fh) / 512; + find_hfs_partition(*info); + if (info->start_byte == 0) + info->num_blocks = SysGetFileSize(info->fh) / 512; info->to_be_mounted = true; } - D(bug(" %ld blocks\n", info->num_blocks)); + D(bug(" %d blocks\n", info->num_blocks)); WriteMacInt16(info->status + dsDriveSize, info->num_blocks & 0xffff); WriteMacInt16(info->status + dsDriveS1, info->num_blocks >> 16); @@ -252,8 +315,8 @@ int16 DiskPrime(uint32 pb, uint32 dce) WriteMacInt32(pb + ioActCount, 0); // Drive valid and disk inserted? - DriveInfo *info; - if ((info = get_drive_info(ReadMacInt16(pb + ioVRefNum))) == NULL) + drive_vec::iterator info = get_drive_info(ReadMacInt16(pb + ioVRefNum)); + if (info == drives.end()) return nsDrvErr; if (!ReadMacInt8(info->status + dsDiskInPlace)) return offLinErr; @@ -263,7 +326,7 @@ int16 DiskPrime(uint32 pb, uint32 dce) size_t length = ReadMacInt32(pb + ioReqCount); loff_t position = ReadMacInt32(dce + dCtlPosition); if (ReadMacInt16(pb + ioPosMode) & 0x100) // 64 bit positioning - position = ((loff_t)ReadMacInt32(pb + ioWPosOffset) << 32) || ReadMacInt32(pb + ioWPosOffset + 4); + position = ((loff_t)ReadMacInt32(pb + ioWPosOffset) << 32) | ReadMacInt32(pb + ioWPosOffset + 4); if ((length & 0x1ff) || (position & 0x1ff)) return paramErr; @@ -271,7 +334,7 @@ int16 DiskPrime(uint32 pb, uint32 dce) if ((ReadMacInt16(pb + ioTrap) & 0xff) == aRdCmd) { // Read - actual = Sys_read(info->fh, buffer, position, length); + actual = Sys_read(info->fh, buffer, position + info->start_byte, length); if (actual != length) return readErr; @@ -280,7 +343,7 @@ int16 DiskPrime(uint32 pb, uint32 dce) // Write if (info->read_only) return wPrErr; - actual = Sys_write(info->fh, buffer, position, length); + actual = Sys_write(info->fh, buffer, position + info->start_byte, length); if (actual != length) return writErr; } @@ -306,37 +369,17 @@ int16 DiskControl(uint32 pb, uint32 dce) case 1: // KillIO return noErr; - case 65: { // Periodic action ("insert" disks on startup and check for disk changes) - DriveInfo *info = first_drive_info; - while (info != NULL) { - - // Disk in drive? - if (!ReadMacInt8(info->status + dsDiskInPlace)) { - - // No, check if disk was inserted - if (SysIsDiskInserted(info->fh)) - DiskMountVolume(info->fh); - } - - // Mount disk if flagged - if (info->to_be_mounted) { - D(bug(" mounting drive %d\n", info->num)); - M68kRegisters r; - r.d[0] = info->num; - r.a[0] = 7; // diskEvent - Execute68kTrap(0xa02f, &r); // PostEvent() - info->to_be_mounted = false; - } - - info = info->next; - } + case 65: { // Periodic action (accRun, "insert" disks on startup) + mount_mountable_volumes(); + WriteMacInt16(dce + dCtlFlags, ReadMacInt16(dce + dCtlFlags) & ~0x2000); // Disable periodic action + acc_run_called = true; return noErr; } } // Drive valid? - DriveInfo *info; - if ((info = get_drive_info(ReadMacInt16(pb + ioVRefNum))) == NULL) + drive_vec::iterator info = get_drive_info(ReadMacInt16(pb + ioVRefNum)); + if (info == drives.end()) return nsDrvErr; // Drive-specific codes @@ -400,54 +443,54 @@ int16 DiskControl(uint32 pb, uint32 dce) int16 DiskStatus(uint32 pb, uint32 dce) { - DriveInfo *info = get_drive_info(ReadMacInt16(pb + ioVRefNum)); + drive_vec::iterator info = get_drive_info(ReadMacInt16(pb + ioVRefNum)); uint16 code = ReadMacInt16(pb + csCode); D(bug("DiskStatus %d\n", code)); - // General codes + // General codes (we can get these even if the drive was invalid) switch (code) { case 43: { // Driver gestalt uint32 sel = ReadMacInt32(pb + csParam); D(bug(" driver gestalt %c%c%c%c\n", sel >> 24, sel >> 16, sel >> 8, sel)); switch (sel) { - case 'vers': // Version + case FOURCC('v','e','r','s'): // Version WriteMacInt32(pb + csParam + 4, 0x01008000); break; - case 'devt': // Device type - if (info != NULL) { + case FOURCC('d','e','v','t'): // Device type + if (info != drives.end()) { if (ReadMacInt8(info->status + dsDiskInPlace) == 8) - WriteMacInt32(pb + csParam + 4, 'disk'); + WriteMacInt32(pb + csParam + 4, FOURCC('d','i','s','k')); else - WriteMacInt32(pb + csParam + 4, 'rdsk'); + WriteMacInt32(pb + csParam + 4, FOURCC('r','d','s','k')); } else - WriteMacInt32(pb + csParam + 4, 'disk'); + WriteMacInt32(pb + csParam + 4, FOURCC('d','i','s','k')); break; - case 'intf': // Interface type - WriteMacInt32(pb + csParam + 4, 'basi'); + case FOURCC('i','n','t','f'): // Interface type + WriteMacInt32(pb + csParam + 4, EMULATOR_ID_4); break; - case 'sync': // Only synchronous operation? + case FOURCC('s','y','n','c'): // Only synchronous operation? WriteMacInt32(pb + csParam + 4, 0x01000000); break; - case 'boot': // Boot ID - if (info != NULL) + case FOURCC('b','o','o','t'): // Boot ID + if (info != drives.end()) WriteMacInt16(pb + csParam + 4, info->num); else WriteMacInt16(pb + csParam + 4, 0); WriteMacInt16(pb + csParam + 6, (uint16)DiskRefNum); break; - case 'wide': // 64-bit access supported? + case FOURCC('w','i','d','e'): // 64-bit access supported? WriteMacInt16(pb + csParam + 4, 0x0100); break; - case 'purg': // Purge flags + case FOURCC('p','u','r','g'): // Purge flags WriteMacInt32(pb + csParam + 4, 0); break; - case 'ejec': // Eject flags + case FOURCC('e','j','e','c'): // Eject flags WriteMacInt32(pb + csParam + 4, 0x00030003); // Don't eject on shutdown/restart break; - case 'flus': // Flush flags + case FOURCC('f','l','u','s'): // Flush flags WriteMacInt16(pb + csParam + 4, 0); break; - case 'vmop': // Virtual memory attributes + case FOURCC('v','m','o','p'): // Virtual memory attributes WriteMacInt32(pb + csParam + 4, 0); // Drive not available for VM break; default: @@ -458,17 +501,46 @@ int16 DiskStatus(uint32 pb, uint32 dce) } // Drive valid? - if (info == NULL) + if (info == drives.end()) return nsDrvErr; // Drive-specific codes switch (code) { case 8: // Get drive status - memcpy(Mac2HostAddr(pb + csParam), Mac2HostAddr(info->status), 22); + Mac2Mac_memcpy(pb + csParam, info->status, 22); return noErr; + case 44: // get startup partition status: http://developer.apple.com/documentation/Hardware/DeviceManagers/ata/ata_ref/ATA.21.html + printf("WARNING: DiskStatus(44:'get startup partition status') Not Implemented\n"); + return statusErr; + + case 45: // get partition write protect status: http://developer.apple.com/documentation/Hardware/DeviceManagers/ata/ata_ref/ATA.23.html + printf("WARNING: DiskStatus(45:'get partition write protect status') Not Implemented\n"); + return statusErr; + + case 46: // get partition mount status: http://developer.apple.com/documentation/Hardware/DeviceManagers/ata/ata_ref/ATA.22.html + printf("WARNING: DiskStatus(46:'get partition mount status') Not Implemented\n"); + return statusErr; + + case 70: // get power mode status: http://developer.apple.com/documentation/Hardware/DeviceManagers/ata/ata_ref/ATA.24.html + printf("WARNING: DiskStatus(70:'get power mode status') Not Implemented\n"); + return statusErr; + default: printf("WARNING: Unknown DiskStatus(%d)\n", code); return statusErr; } } + + +/* + * Driver interrupt routine (1Hz) - check for volumes to be mounted + */ + +void DiskInterrupt(void) +{ + if (!acc_run_called) + return; + + mount_mountable_volumes(); +}