|
|
1.1 root 1: /* 1.1.1.5 root 2: Hatari - gemdos.c 1.1 root 3: 1.1.1.21! 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: 7: GEMDOS intercept routines. 8: These are used mainly for hard drive redirection of high level file routines. 1.1 root 9: 1.1.1.17 root 10: Host file names are handled case insensitively, so files on GEMDOS 11: drive emulation directories may be either in lower or upper case. 12: 13: Too long file and directory names and names with invalid characters 14: are converted to TOS compatible 8+3 names, but matching them back to 15: host names is slower and may match several such filenames (of which 16: first one will be returned), so using them should be avoided. 1.1.1.6 root 17: 1.1.1.2 root 18: Bugs/things to fix: 1.1.1.17 root 19: * Host filenames are in many places limited to 255 chars (same as 20: on TOS), FILENAME_MAX should be used if that's a problem. 1.1.1.2 root 21: * rmdir routine, can't remove dir with files in it. (another tos/unix difference) 22: * Fix bugs, there are probably a few lurking around in here.. 23: */ 1.1.1.14 root 24: const char Gemdos_fileid[] = "Hatari gemdos.c : " __DATE__ " " __TIME__; 1.1.1.12 root 25: 26: #include <config.h> 1.1 root 27: 1.1.1.4 root 28: #include <sys/stat.h> 1.1.1.19 root 29: #if HAVE_STATVFS 30: #include <sys/statvfs.h> 31: #endif 1.1.1.17 root 32: #include <sys/types.h> 33: #include <utime.h> 1.1.1.4 root 34: #include <time.h> 35: #include <ctype.h> 36: #include <unistd.h> 1.1.1.12 root 37: #include <errno.h> 38: 1.1 root 39: #include "main.h" 40: #include "cart.h" 1.1.1.2 root 41: #include "tos.h" 1.1.1.6 root 42: #include "configuration.h" 1.1 root 43: #include "file.h" 44: #include "floppy.h" 1.1.1.3 root 45: #include "hdc.h" 1.1 root 46: #include "gemdos.h" 1.1.1.11 root 47: #include "gemdos_defines.h" 48: #include "log.h" 1.1 root 49: #include "m68000.h" 50: #include "memorySnapShot.h" 51: #include "printer.h" 52: #include "rs232.h" 1.1.1.13 root 53: #include "statusbar.h" 1.1.1.11 root 54: #include "scandir.h" 1.1 root 55: #include "stMemory.h" 1.1.1.13 root 56: #include "str.h" 1.1.1.12 root 57: #include "hatari-glue.h" 58: #include "maccess.h" 1.1.1.7 root 59: 1.1.1.6 root 60: 1.1.1.11 root 61: /* Maximum supported length of a GEMDOS path: */ 62: #define MAX_GEMDOS_PATH 256 1.1.1.3 root 63: 1.1.1.11 root 64: /* Have we re-directed GemDOS vector to our own routines yet? */ 1.1.1.13 root 65: bool bInitGemDOS; 1.1.1.11 root 66: 67: /* structure with all the drive-specific data for our emulated drives, 68: * used by GEMDOS_EMU_ON macro 69: */ 1.1.1.2 root 70: EMULATEDDRIVE **emudrives = NULL; 71: 1.1.1.11 root 72: #define ISHARDDRIVE(Drive) (Drive!=-1) 73: 74: /* 1.1.1.21! root 75: Disk Transfer Address (DTA) 1.1.1.11 root 76: */ 77: #define TOS_NAMELEN 14 78: 79: typedef struct { 80: Uint8 index[2]; 81: Uint8 magic[4]; 82: char dta_pat[TOS_NAMELEN]; 83: char dta_sattrib; 84: char dta_attrib; 85: Uint8 dta_time[2]; 86: Uint8 dta_date[2]; 87: Uint8 dta_size[4]; 88: char dta_name[TOS_NAMELEN]; 89: } DTA; 90: 91: #define DTA_MAGIC_NUMBER 0x12983476 92: #define MAX_DTAS_FILES 256 /* Must be ^2 */ 93: #define CALL_PEXEC_ROUTINE 3 /* Call our cartridge pexec routine */ 94: 95: #define BASE_FILEHANDLE 64 /* Our emulation handles - MUST not be valid TOS ones, but MUST be <256 */ 96: #define MAX_FILE_HANDLES 32 /* We can allow 32 files open at once */ 97: 1.1.1.15 root 98: /* 1.1.1.18 root 99: DateTime structure used by TOS call $57 f_dtatime 1.1.1.11 root 100: Changed to fix potential problem with alignment. 101: */ 102: typedef struct { 1.1.1.17 root 103: Uint16 timeword; 104: Uint16 dateword; 1.1.1.11 root 105: } DATETIME; 106: 1.1.1.21! root 107: #define UNFORCED_HANDLE -1 ! 108: static struct { ! 109: int Handle; ! 110: Uint32 Basepage; ! 111: } ForcedHandles[5]; /* (standard) handles aliased to emulated handles */ ! 112: 1.1.1.6 root 113: typedef struct 1.1 root 114: { 1.1.1.13 root 115: bool bUsed; 1.1.1.21! root 116: Uint32 Basepage; 1.1.1.9 root 117: FILE *FileHandle; 1.1.1.17 root 118: /* TODO: host path might not fit into this */ 1.1.1.11 root 119: char szActualName[MAX_GEMDOS_PATH]; /* used by F_DATIME (0x57) */ 1.1 root 120: } FILE_HANDLE; 121: 1.1.1.6 root 122: typedef struct 1.1.1.2 root 123: { 1.1.1.13 root 124: bool bUsed; 1.1.1.9 root 125: int nentries; /* number of entries in fs directory */ 126: int centry; /* current entry # */ 127: struct dirent **found; /* legal files */ 1.1.1.11 root 128: char path[MAX_GEMDOS_PATH]; /* sfirst path */ 1.1.1.2 root 129: } INTERNAL_DTA; 130: 1.1.1.11 root 131: static FILE_HANDLE FileHandles[MAX_FILE_HANDLES]; 132: static INTERNAL_DTA InternalDTAs[MAX_DTAS_FILES]; 133: static int DTAIndex; /* Circular index into above */ 134: static DTA *pDTA; /* Our GEMDOS hard drive Disk Transfer Address structure */ 135: static Uint16 CurrentDrive; /* Current drive (0=A,1=B,2=C etc...) */ 136: static Uint32 act_pd; /* Used to get a pointer to the current basepage */ 1.1.1.13 root 137: static Uint16 nAttrSFirst; /* File attribute for SFirst/Snext */ 1.1.1.6 root 138: 1.1.1.21! root 139: /* last program opened by GEMDOS emulation */ ! 140: static bool PexecCalled; ! 141: static char *LastProgramPath; 1.1 root 142: 1.1.1.15 root 143: #if defined(WIN32) && !defined(mkdir) 1.1.1.12 root 144: #define mkdir(name,mode) mkdir(name) 1.1.1.11 root 145: #endif /* WIN32 */ 146: 1.1.1.12 root 147: #ifndef S_IRGRP 148: #define S_IRGRP 0 149: #define S_IROTH 0 150: #endif 151: 1.1.1.18 root 152: /* set to 1 if you want to see debug output from pattern matching */ 153: #define DEBUG_PATTERN_MATCH 0 1.1.1.11 root 154: 1.1.1.3 root 155: 1.1.1.2 root 156: /*-------------------------------------------------------*/ 1.1.1.12 root 157: /** 1.1.1.17 root 158: * Routine to convert time and date to GEMDOS format. 1.1.1.12 root 159: * Originally from the STonX emulator. (cheers!) 160: */ 1.1.1.21! root 161: static void GemDOS_DateTime2Tos(time_t t, DATETIME *DateTime, const char *fname) 1.1.1.2 root 162: { 163: struct tm *x; 1.1.1.12 root 164: 1.1.1.18 root 165: /* localtime takes DST into account */ 1.1.1.12 root 166: x = localtime(&t); 1.1.1.15 root 167: 1.1.1.12 root 168: if (x == NULL) 1.1.1.21! root 169: { ! 170: Log_Printf(LOG_WARN, "WARNING: '%s' timestamp is invalid for (Windows?) localtime(), defaulting to TOS epoch!", fname); ! 171: DateTime->dateword = 1|(1<<5); /* 1980-01-01 */ ! 172: DateTime->timeword = 0; ! 173: return; ! 174: } 1.1.1.17 root 175: /* Bits: 0-4 = secs/2, 5-10 = mins, 11-15 = hours (24-hour format) */ 176: DateTime->timeword = (x->tm_sec>>1)|(x->tm_min<<5)|(x->tm_hour<<11); 177: 178: /* Bits: 0-4 = day (1-31), 5-8 = month (1-12), 9-15 = years (since 1980) */ 179: DateTime->dateword = x->tm_mday | ((x->tm_mon+1)<<5) 180: | (((x->tm_year-80 > 0) ? x->tm_year-80 : 0) << 9); 1.1.1.2 root 181: } 1.1 root 182: 1.1.1.17 root 183: /*-----------------------------------------------------------------------*/ 184: /** 185: * Populate a DATETIME structure with file info. Handle needs to be 186: * validated before calling. Return true on success. 187: */ 188: static bool GemDOS_GetFileInformation(int Handle, DATETIME *DateTime) 1.1.1.2 root 189: { 1.1.1.21! root 190: const char *fname = FileHandles[Handle].szActualName; ! 191: struct stat fstat; 1.1.1.12 root 192: 1.1.1.21! root 193: if (stat(fname, &fstat) == 0) ! 194: { ! 195: GemDOS_DateTime2Tos(fstat.st_mtime, DateTime, fname); ! 196: return true; ! 197: } ! 198: return false; 1.1.1.2 root 199: } 1.1 root 200: 1.1.1.2 root 201: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 202: /** 1.1.1.17 root 203: * Set given file date/time from given DATETIME. Handle needs to be 204: * validated before calling. Return true on success. 1.1.1.12 root 205: */ 1.1.1.17 root 206: static bool GemDOS_SetFileInformation(int Handle, DATETIME *DateTime) 1.1.1.2 root 207: { 1.1.1.17 root 208: const char *filename; 209: struct utimbuf timebuf; 1.1.1.9 root 210: struct stat filestat; 1.1.1.17 root 211: struct tm timespec; 1.1.1.9 root 212: 1.1.1.17 root 213: /* make sure Hatari itself doesn't need to write/modify 214: * the file after it's modification time is changed. 215: */ 216: fflush(FileHandles[Handle].FileHandle); 217: filename = FileHandles[Handle].szActualName; 218: 219: /* Bits: 0-4 = secs/2, 5-10 = mins, 11-15 = hours (24-hour format) */ 220: timespec.tm_sec = (DateTime->timeword & 0x1F) << 1; 221: timespec.tm_min = (DateTime->timeword & 0x7E0) >> 5; 222: timespec.tm_hour = (DateTime->timeword & 0xF800) >> 11; 223: /* Bits: 0-4 = day (1-31), 5-8 = month (1-12), 9-15 = years (since 1980) */ 224: timespec.tm_mday = (DateTime->dateword & 0x1F); 225: timespec.tm_mon = ((DateTime->dateword & 0x1E0) >> 5) - 1; 226: timespec.tm_year = ((DateTime->dateword & 0xFE00) >> 9) + 80; 1.1.1.18 root 227: /* check whether DST should be taken into account */ 228: timespec.tm_isdst = -1; 1.1.1.17 root 229: 230: /* set new modification time */ 231: timebuf.modtime = mktime(×pec); 232: 233: /* but keep previous access time */ 234: if (stat(filename, &filestat) != 0) 1.1.1.15 root 235: return false; 1.1.1.17 root 236: timebuf.actime = filestat.st_atime; 1.1.1.12 root 237: 1.1.1.17 root 238: if (utime(filename, &timebuf) != 0) 1.1.1.15 root 239: return false; 1.1.1.17 root 240: // fprintf(stderr, "set date '%s' for %s\n", asctime(×pec), name); 241: return true; 242: } 1.1.1.9 root 243: 244: 245: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 246: /** 1.1.1.15 root 247: * Convert from FindFirstFile/FindNextFile attribute to GemDOS format 1.1.1.12 root 248: */ 1.1.1.17 root 249: static Uint8 GemDOS_ConvertAttribute(mode_t mode) 1.1.1.9 root 250: { 1.1.1.17 root 251: Uint8 Attrib = 0; 1.1.1.9 root 252: 253: /* Directory attribute */ 254: if (S_ISDIR(mode)) 255: Attrib |= GEMDOS_FILE_ATTRIB_SUBDIRECTORY; 1.1.1.2 root 256: 1.1.1.9 root 257: /* Read-only attribute */ 258: if (!(mode & S_IWUSR)) 259: Attrib |= GEMDOS_FILE_ATTRIB_READONLY; 1.1.1.15 root 260: 1.1.1.17 root 261: /* TODO, Other attributes: 262: * - GEMDOS_FILE_ATTRIB_HIDDEN (file not visible on desktop/fsel) 263: * - GEMDOS_FILE_ATTRIB_ARCHIVE (file written after being backed up) 264: * ? 265: */ 1.1.1.9 root 266: return Attrib; 1.1.1.2 root 267: } 1.1.1.3 root 268: 269: 1.1.1.2 root 270: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 271: /** 1.1.1.13 root 272: * Populate the DTA buffer with file info. 273: * @return 0 if entry is ok, 1 if entry should be skipped, < 0 for errors. 1.1.1.12 root 274: */ 1.1.1.13 root 275: static int PopulateDTA(char *path, struct dirent *file) 1.1.1.2 root 276: { 1.1.1.17 root 277: /* TODO: host file path can be longer than MAX_GEMDOS_PATH */ 1.1.1.11 root 278: char tempstr[MAX_GEMDOS_PATH]; 1.1.1.9 root 279: struct stat filestat; 1.1.1.17 root 280: DATETIME DateTime; 281: int nFileAttr, nAttrMask; 1.1.1.9 root 282: 1.1.1.12 root 283: snprintf(tempstr, sizeof(tempstr), "%s%c%s", path, PATHSEP, file->d_name); 1.1.1.17 root 284: 285: if (stat(tempstr, &filestat) != 0) 1.1.1.12 root 286: { 287: perror(tempstr); 1.1.1.13 root 288: return -1; /* return on error */ 1.1.1.12 root 289: } 1.1.1.9 root 290: 291: if (!pDTA) 1.1.1.13 root 292: return -2; /* no DTA pointer set */ 293: 1.1.1.17 root 294: /* Check file attributes (check is done according to the Profibuch) */ 1.1.1.13 root 295: nFileAttr = GemDOS_ConvertAttribute(filestat.st_mode); 1.1.1.17 root 296: nAttrMask = nAttrSFirst|GEMDOS_FILE_ATTRIB_WRITECLOSE|GEMDOS_FILE_ATTRIB_READONLY; 297: if (nFileAttr != 0 && !(nAttrMask & nFileAttr)) 1.1.1.13 root 298: return 1; 299: 1.1.1.21! root 300: GemDOS_DateTime2Tos(filestat.st_mtime, &DateTime, tempstr); 1.1.1.17 root 301: 302: /* convert to atari-style uppercase */ 1.1.1.18 root 303: Str_Filename2TOSname(file->d_name, pDTA->dta_name); 304: #if DEBUG_PATTERN_MATCH 305: fprintf(stderr, "GEMDOS: host: %s -> GEMDOS: %s\n", 306: file->d_name, pDTA->dta_name); 307: #endif 1.1.1.9 root 308: do_put_mem_long(pDTA->dta_size, filestat.st_size); 1.1.1.17 root 309: do_put_mem_word(pDTA->dta_time, DateTime.timeword); 310: do_put_mem_word(pDTA->dta_date, DateTime.dateword); 1.1.1.13 root 311: pDTA->dta_attrib = nFileAttr; 1.1.1.2 root 312: 1.1.1.13 root 313: return 0; 1.1.1.2 root 314: } 315: 1.1.1.3 root 316: 1.1.1.2 root 317: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 318: /** 319: * Clear a used DTA structure. 320: */ 1.1.1.7 root 321: static void ClearInternalDTA(void) 322: { 1.1.1.9 root 323: int i; 1.1.1.2 root 324: 1.1.1.9 root 325: /* clear the old DTA structure */ 326: if (InternalDTAs[DTAIndex].found != NULL) 327: { 328: for (i=0; i < InternalDTAs[DTAIndex].nentries; i++) 329: free(InternalDTAs[DTAIndex].found[i]); 330: free(InternalDTAs[DTAIndex].found); 331: InternalDTAs[DTAIndex].found = NULL; 332: } 333: InternalDTAs[DTAIndex].nentries = 0; 1.1.1.15 root 334: InternalDTAs[DTAIndex].bUsed = false; 1.1.1.2 root 335: } 336: 1.1.1.3 root 337: 1.1.1.2 root 338: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 339: /** 1.1.1.17 root 340: * Match a TOS file name to a dir mask. 1.1.1.12 root 341: */ 1.1.1.17 root 342: static bool fsfirst_match(const char *pat, const char *name) 1.1.1.2 root 343: { 1.1.1.21! root 344: const char *dot, *p=pat, *n=name; 1.1.1.9 root 345: 346: if (name[0] == '.') 1.1.1.17 root 347: return false; /* skip .* files */ 1.1.1.9 root 348: if (strcmp(pat,"*.*")==0) 1.1.1.17 root 349: return true; /* match everything */ 1.1.1.9 root 350: if (strcasecmp(pat,name)==0) 1.1.1.17 root 351: return true; /* exact case insensitive match */ 1.1.1.9 root 352: 1.1.1.21! root 353: dot = strrchr(name, '.'); /* '*' matches everything except _last_ '.' */ 1.1.1.17 root 354: while (*n) 1.1.1.2 root 355: { 1.1.1.9 root 356: if (*p=='*') 357: { 1.1.1.21! root 358: while (*n && n != dot) 1.1.1.9 root 359: n++; 360: p++; 361: } 362: else 363: { 364: if (*p=='?' && *n) 365: { 366: n++; 367: p++; 368: } 369: else 370: { 371: if (toupper(*p++) != toupper(*n++)) 1.1.1.15 root 372: return false; 1.1.1.9 root 373: } 374: } 1.1.1.2 root 375: } 1.1.1.9 root 376: 1.1.1.17 root 377: /* The name matches the pattern if it ends here, too */ 378: return (*p == 0 || (*p == '*' && *(p+1) == 0)); 1.1.1.2 root 379: } 380: 1.1.1.9 root 381: 1.1.1.2 root 382: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 383: /** 384: * Parse directory from sfirst mask 385: * - e.g.: input: "hdemudir/auto/mask*.*" outputs: "hdemudir/auto" 386: */ 1.1.1.17 root 387: static void fsfirst_dirname(const char *string, char *newstr) 1.1.1.7 root 388: { 1.1.1.9 root 389: int i=0; 1.1.1.6 root 390: 1.1.1.15 root 391: strcpy(newstr, string); 1.1.1.11 root 392: 1.1.1.15 root 393: /* convert to front slashes and go to end of string. */ 394: while (newstr[i] != '\0') 1.1.1.9 root 395: { 1.1.1.15 root 396: if (newstr[i] == '\\') 397: newstr[i] = PATHSEP; 1.1.1.9 root 398: i++; 399: } 1.1.1.15 root 400: /* find last slash and terminate string */ 401: while (i && newstr[i] != PATHSEP) 402: i--; 403: newstr[i] = '\0'; 1.1 root 404: } 405: 1.1.1.11 root 406: 1.1.1.2 root 407: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 408: /** 1.1.1.17 root 409: * Return directory mask part from the given string 1.1.1.12 root 410: */ 1.1.1.17 root 411: static const char* fsfirst_dirmask(const char *string) 1.1.1.7 root 412: { 1.1.1.17 root 413: const char *lastsep; 1.1.1.9 root 414: 1.1.1.17 root 415: lastsep = strrchr(string, PATHSEP); 416: if (lastsep) 417: return lastsep + 1; 418: else 419: return string; 1.1.1.2 root 420: } 1.1 root 421: 1.1.1.21! root 422: /*-----------------------------------------------------------------------*/ ! 423: /** ! 424: * Close given internal file handle if it's still in use ! 425: * and (always) reset handle variables ! 426: */ ! 427: static void GemDOS_CloseFileHandle(int i) ! 428: { ! 429: if (FileHandles[i].bUsed) ! 430: fclose(FileHandles[i].FileHandle); ! 431: FileHandles[i].FileHandle = NULL; ! 432: FileHandles[i].Basepage = 0; ! 433: FileHandles[i].bUsed = false; ! 434: } ! 435: ! 436: /** ! 437: * Un-force given file handle ! 438: */ ! 439: static void GemDOS_UnforceFileHandle(int i) ! 440: { ! 441: ForcedHandles[i].Handle = UNFORCED_HANDLE; ! 442: ForcedHandles[i].Basepage = 0; ! 443: } ! 444: ! 445: /*-----------------------------------------------------------------------*/ ! 446: /** ! 447: * Return last host path used to execute a program ! 448: * (used by debugger) ! 449: */ ! 450: const char *GemDOS_GetLastProgramPath(void) ! 451: { ! 452: return LastProgramPath; ! 453: } ! 454: ! 455: static void GemDOS_RemoveLastProgram(void) ! 456: { ! 457: if (LastProgramPath) ! 458: { ! 459: free(LastProgramPath); ! 460: LastProgramPath = NULL; ! 461: } ! 462: } ! 463: ! 464: /** ! 465: * If program was executed, store path to it ! 466: * (should be called only by Fopen) ! 467: */ ! 468: static void GemDOS_UpdateLastProgram(int Handle) ! 469: { ! 470: Uint16 magic = 0; ! 471: size_t items; ! 472: long oldpos; ! 473: FILE *fp; ! 474: ! 475: /* only first Fopen after Pexec needs to be handled */ ! 476: if (!PexecCalled) ! 477: return; ! 478: PexecCalled = false; ! 479: ! 480: /* is file a TOS program? */ ! 481: fp = FileHandles[Handle].FileHandle; ! 482: oldpos = ftell(fp); ! 483: fseek(fp, 0, SEEK_SET); ! 484: items = fread(&magic, sizeof(magic), 1, fp); ! 485: fseek(fp, oldpos, SEEK_SET); ! 486: if (items != 1 || SDL_SwapBE16(magic) != 0x601A) ! 487: return; ! 488: ! 489: /* store program path */ ! 490: if (LastProgramPath) ! 491: free(LastProgramPath); ! 492: LastProgramPath = strdup(FileHandles[Handle].szActualName); ! 493: } 1.1.1.7 root 494: 1.1.1.2 root 495: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 496: /** 497: * Initialize GemDOS/PC file system 498: */ 1.1 root 499: void GemDOS_Init(void) 500: { 1.1.1.9 root 501: int i; 1.1.1.15 root 502: bInitGemDOS = false; 1.1.1.2 root 503: 1.1.1.9 root 504: /* Clear handles structure */ 1.1.1.17 root 505: memset(FileHandles, 0, sizeof(FileHandles)); 1.1.1.21! root 506: for(i = 0; i < ARRAYSIZE(ForcedHandles); i++) ! 507: { ! 508: GemDOS_UnforceFileHandle(i); ! 509: } 1.1.1.9 root 510: /* Clear DTAs */ 1.1.1.21! root 511: for(i = 0; i < ARRAYSIZE(InternalDTAs); i++) 1.1.1.9 root 512: { 1.1.1.15 root 513: InternalDTAs[i].bUsed = false; 1.1.1.9 root 514: InternalDTAs[i].nentries = 0; 515: InternalDTAs[i].found = NULL; 516: } 517: DTAIndex = 0; 1.1 root 518: } 519: 1.1.1.9 root 520: 1.1.1.2 root 521: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 522: /** 523: * Reset GemDOS file system 524: */ 1.1.1.7 root 525: void GemDOS_Reset(void) 1.1 root 526: { 1.1.1.9 root 527: int i; 528: 529: /* Init file handles table */ 1.1.1.21! root 530: for (i = 0; i < ARRAYSIZE(FileHandles); i++) 1.1.1.9 root 531: { 1.1.1.21! root 532: GemDOS_CloseFileHandle(i); ! 533: } ! 534: for(i = 0; i < ARRAYSIZE(ForcedHandles); i++) ! 535: { ! 536: GemDOS_UnforceFileHandle(i); 1.1.1.9 root 537: } 538: for (DTAIndex = 0; DTAIndex < MAX_DTAS_FILES; DTAIndex++) 539: { 540: ClearInternalDTA(); 541: } 542: DTAIndex = 0; 543: 544: /* Reset */ 1.1.1.15 root 545: bInitGemDOS = false; 1.1.1.9 root 546: CurrentDrive = nBootDrive; 1.1.1.21! root 547: GemDOS_RemoveLastProgram(); 1.1.1.9 root 548: pDTA = NULL; 1.1 root 549: } 550: 1.1.1.15 root 551: /*-----------------------------------------------------------------------*/ 552: /** 553: * Routine to check the Host OS HDD path for a Drive letter sub folder 554: */ 555: static bool GEMDOS_DoesHostDriveFolderExist(char* lpstrPath, int iDrive) 556: { 557: bool bExist = false; 558: 559: if (access(lpstrPath, F_OK) != 0 ) 560: { 561: /* Try lower case drive letter instead */ 562: int iIndex = strlen(lpstrPath)-1; 563: lpstrPath[iIndex] = tolower(lpstrPath[iIndex]); 564: } 565: 566: /* Check the file/folder is accessible (security basis) */ 567: if (access(lpstrPath, F_OK) == 0 ) 568: { 569: /* If its a HDD identifier (or other emulated device) */ 570: if (iDrive > 1) 571: { 572: struct stat status; 573: stat( lpstrPath, &status ); 574: if ( status.st_mode & S_IFDIR ) 575: bExist = true; 576: } 577: } 578: 579: return bExist; 580: } 581: 582: 583: /** 1.1.1.16 root 584: * Determine upper limit of partitions that should be emulated. 585: * 586: * @return true if multiple GEMDOS partitions should be emulated, false otherwise 1.1.1.15 root 587: */ 1.1.1.16 root 588: static bool GemDOS_DetermineMaxPartitions(int *pnMaxDrives) 1.1.1.15 root 589: { 590: struct dirent **files; 1.1.1.19 root 591: int count, i, last; 1.1.1.17 root 592: char letter; 1.1.1.16 root 593: bool bMultiPartitions; 594: 595: *pnMaxDrives = 0; 1.1.1.15 root 596: 597: /* Scan through the main directory to see whether there are just single 598: * letter sub-folders there (then use multi-partition mode) or if 599: * arbitrary sub-folders are there (then use single-partition mode */ 600: count = scandir(ConfigureParams.HardDisk.szHardDiskDirectories[0], &files, 0, alphasort); 601: if (count < 0) 602: { 603: perror("GemDOS_DetermineMaxPartitions"); 1.1.1.16 root 604: return false; 1.1.1.15 root 605: } 606: else if (count <= 2) 607: { 608: /* Empty directory Only "." and ".."), assume single partition mode */ 1.1.1.19 root 609: last = 1; 1.1.1.16 root 610: bMultiPartitions = false; 1.1.1.15 root 611: } 612: else 613: { 1.1.1.16 root 614: bMultiPartitions = true; 1.1.1.15 root 615: /* Check all files in the directory */ 1.1.1.19 root 616: last = 0; 1.1.1.15 root 617: for (i = 0; i < count; i++) 618: { 1.1.1.17 root 619: letter = toupper(files[i]->d_name[0]); 620: if (!letter || letter == '.') 1.1.1.15 root 621: { 1.1.1.17 root 622: /* Ignore hidden files like "." and ".." */ 1.1.1.15 root 623: continue; 624: } 1.1.1.17 root 625: 626: if (letter < 'C' || letter > 'Z' || files[i]->d_name[1]) 1.1.1.15 root 627: { 1.1.1.17 root 628: /* folder with name other than C-Z... 629: * (until Z under MultiTOS, to P otherwise) 1.1.1.15 root 630: * ... so use single partition mode! */ 1.1.1.19 root 631: last = 1; 1.1.1.16 root 632: bMultiPartitions = false; 1.1.1.15 root 633: break; 634: } 1.1.1.19 root 635: 636: /* alphasort isn't case insensitive */ 637: letter = letter - 'C' + 1; 638: if (letter > last) 639: last = letter; 1.1.1.15 root 640: } 641: } 642: 1.1.1.19 root 643: if (last > MAX_HARDDRIVES) 1.1.1.16 root 644: *pnMaxDrives = MAX_HARDDRIVES; 1.1.1.19 root 645: else 646: *pnMaxDrives = last; 1.1.1.15 root 647: 648: /* Free file list */ 649: for (i = 0; i < count; i++) 650: free(files[i]); 651: free(files); 652: 1.1.1.16 root 653: return bMultiPartitions; 1.1.1.15 root 654: } 1.1.1.3 root 655: 656: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 657: /** 658: * Initialize a GEMDOS drive. 1.1.1.15 root 659: * Supports up to MAX_HARDDRIVES HDD units. 1.1.1.12 root 660: */ 1.1.1.7 root 661: void GemDOS_InitDrives(void) 1.1.1.3 root 662: { 1.1.1.9 root 663: int i; 1.1.1.15 root 664: int nMaxDrives; 1.1.1.17 root 665: int DriveNumber; 1.1.1.16 root 666: bool bMultiPartitions; 1.1.1.9 root 667: 668: /* intialize data for harddrive emulation: */ 1.1.1.18 root 669: if (!emudrives) 1.1.1.9 root 670: { 1.1.1.15 root 671: emudrives = malloc(MAX_HARDDRIVES * sizeof(EMULATEDDRIVE *)); 672: if (!emudrives) 673: { 674: perror("GemDOS_InitDrives"); 1.1.1.18 root 675: abort(); /* fatal */ 1.1.1.15 root 676: } 677: memset(emudrives, 0, MAX_HARDDRIVES * sizeof(EMULATEDDRIVE *)); 1.1.1.9 root 678: } 1.1.1.3 root 679: 1.1.1.16 root 680: bMultiPartitions = GemDOS_DetermineMaxPartitions(&nMaxDrives); 1.1.1.15 root 681: 682: /* Now initialize all available drives */ 683: for(i = 0; i < nMaxDrives; i++) 1.1.1.9 root 684: { 1.1.1.21! root 685: // Create the letter equivalent string identifier for this drive 1.1.1.15 root 686: char sDriveLetter[] = { PATHSEP, (char)('C' + i), '\0' }; 1.1.1.6 root 687: 1.1.1.16 root 688: /* If single partition mode, skip to the right entry */ 689: if (!bMultiPartitions) 690: i += nPartitions; 691: 692: /* Allocate emudrives entry for this drive */ 1.1.1.15 root 693: emudrives[i] = malloc(sizeof(EMULATEDDRIVE)); 694: if (!emudrives[i]) 695: { 696: perror("GemDOS_InitDrives"); 697: continue; 698: } 1.1.1.9 root 699: 1.1.1.15 root 700: /* set emulation directory string */ 701: strcpy(emudrives[i]->hd_emulation_dir, ConfigureParams.HardDisk.szHardDiskDirectories[0]); 1.1.1.9 root 702: 1.1.1.15 root 703: /* remove trailing slash, if any in the directory name */ 704: File_CleanFileName(emudrives[i]->hd_emulation_dir); 1.1.1.9 root 705: 1.1.1.15 root 706: /* Add Requisit Folder ID */ 1.1.1.16 root 707: if (bMultiPartitions) 1.1.1.15 root 708: strcat(emudrives[i]->hd_emulation_dir, sDriveLetter); 709: 1.1.1.17 root 710: /* drive number (C: = 2, D: = 3, etc.) */ 711: DriveNumber = 2 + i; 712: 1.1.1.15 root 713: // Check host file system to see if the drive folder for THIS 714: // drive letter/number exists... 1.1.1.17 root 715: if (GEMDOS_DoesHostDriveFolderExist(emudrives[i]->hd_emulation_dir, DriveNumber)) 1.1.1.15 root 716: { 717: /* initialize current directory string, too (initially the same as hd_emulation_dir) */ 718: strcpy(emudrives[i]->fs_currpath, emudrives[i]->hd_emulation_dir); 719: File_AddSlashToEndFileName(emudrives[i]->fs_currpath); /* Needs trailing slash! */ 720: /* If the GemDos Drive letter is free then */ 721: if (i >= nPartitions) 722: { 1.1.1.17 root 723: Log_Printf(LOG_INFO, "GEMDOS HDD emulation, %c: <-> %s.\n", 724: 'A'+DriveNumber, emudrives[i]->hd_emulation_dir); 725: emudrives[i]->drive_number = DriveNumber; 1.1.1.15 root 726: nNumDrives = i + 3; 727: } 1.1.1.17 root 728: else /* This letter has already been allocated to the one supported physical disk image */ 1.1.1.15 root 729: { 1.1.1.17 root 730: Log_Printf(LOG_WARN, "Drive Letter %c is already mapped to HDD image (cannot map GEMDOS drive to %s).\n", 731: 'A'+DriveNumber, emudrives[i]->hd_emulation_dir); 1.1.1.15 root 732: free(emudrives[i]); 733: emudrives[i] = NULL; 734: } 735: } 736: else 737: { 738: free(emudrives[i]); // Deallocate Memory (save space) 739: emudrives[i] = NULL; 740: } 1.1.1.9 root 741: } 1.1.1.3 root 742: } 743: 1.1.1.6 root 744: 1.1.1.3 root 745: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 746: /** 747: * Un-init the GEMDOS drive 748: */ 1.1.1.7 root 749: void GemDOS_UnInitDrives(void) 1.1.1.3 root 750: { 1.1.1.9 root 751: int i; 1.1.1.3 root 752: 1.1.1.21! root 753: GemDOS_Reset(); /* Close all open files on emulated drive */ 1.1.1.3 root 754: 1.1.1.9 root 755: if (GEMDOS_EMU_ON) 756: { 1.1.1.21! root 757: for(i = 0; i < MAX_HARDDRIVES; i++) 1.1.1.9 root 758: { 1.1.1.15 root 759: if (emudrives[i]) 760: { 761: free(emudrives[i]); /* Release memory */ 762: emudrives[i] = NULL; 763: nNumDrives -= 1; 764: } 1.1.1.9 root 765: } 766: 767: free(emudrives); 768: emudrives = NULL; 769: } 1.1.1.3 root 770: } 771: 772: 1.1.1.2 root 773: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 774: /** 775: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type) 776: */ 1.1.1.13 root 777: void GemDOS_MemorySnapShot_Capture(bool bSave) 1.1 root 778: { 1.1.1.9 root 779: unsigned int Addr; 780: int i; 1.1.1.13 root 781: bool bEmudrivesAvailable; 782: 783: /* Save/Restore the emudrives structure */ 784: bEmudrivesAvailable = (emudrives != NULL); 785: MemorySnapShot_Store(&bEmudrivesAvailable, sizeof(bEmudrivesAvailable)); 786: if (bEmudrivesAvailable) 787: { 1.1.1.18 root 788: if (!emudrives) 1.1.1.13 root 789: { 1.1.1.18 root 790: /* As memory snapshot contained emulated drive(s), 791: * but currently there are none allocated yet... 792: * let's do it now! 793: */ 1.1.1.13 root 794: GemDOS_InitDrives(); 795: } 796: 1.1.1.21! root 797: for(i = 0; i < MAX_HARDDRIVES; i++) 1.1.1.13 root 798: { 1.1.1.15 root 799: int bDummyDrive = false; 800: if (!emudrives[i]) 801: { 802: /* Allocate a dummy drive */ 803: emudrives[i] = malloc(sizeof(EMULATEDDRIVE)); 804: if (!emudrives[i]) 1.1.1.18 root 805: { 1.1.1.15 root 806: perror("GemDOS_MemorySnapShot_Capture"); 1.1.1.18 root 807: continue; 808: } 1.1.1.15 root 809: memset(emudrives[i], 0, sizeof(EMULATEDDRIVE)); 810: bDummyDrive = true; 811: } 1.1.1.13 root 812: MemorySnapShot_Store(emudrives[i]->hd_emulation_dir, 813: sizeof(emudrives[i]->hd_emulation_dir)); 814: MemorySnapShot_Store(emudrives[i]->fs_currpath, 815: sizeof(emudrives[i]->fs_currpath)); 1.1.1.17 root 816: MemorySnapShot_Store(&emudrives[i]->drive_number, 817: sizeof(emudrives[i]->drive_number)); 1.1.1.15 root 818: if (bDummyDrive) 819: { 820: free(emudrives[i]); 821: emudrives[i] = NULL; 822: } 1.1.1.13 root 823: } 824: } 1.1 root 825: 1.1.1.9 root 826: /* Save/Restore details */ 827: MemorySnapShot_Store(&DTAIndex,sizeof(DTAIndex)); 828: MemorySnapShot_Store(&bInitGemDOS,sizeof(bInitGemDOS)); 1.1.1.13 root 829: MemorySnapShot_Store(&act_pd, sizeof(act_pd)); 1.1.1.9 root 830: if (bSave) 831: { 1.1.1.11 root 832: Addr = ((Uint8 *)pDTA - STRam); 1.1.1.9 root 833: MemorySnapShot_Store(&Addr,sizeof(Addr)); 834: } 835: else 836: { 837: MemorySnapShot_Store(&Addr,sizeof(Addr)); 838: pDTA = (DTA *)(STRam + Addr); 839: } 840: MemorySnapShot_Store(&CurrentDrive,sizeof(CurrentDrive)); 841: /* Don't save file handles as files may have changed which makes 1.1.1.21! root 842: * it impossible to get a valid handle back ! 843: */ 1.1.1.9 root 844: if (!bSave) 845: { 846: /* Clear file handles */ 1.1.1.21! root 847: for(i = 0; i < ARRAYSIZE(FileHandles); i++) ! 848: { ! 849: GemDOS_CloseFileHandle(i); ! 850: } ! 851: for(i = 0; i < ARRAYSIZE(ForcedHandles); i++) 1.1.1.9 root 852: { 1.1.1.21! root 853: GemDOS_UnforceFileHandle(i); 1.1.1.9 root 854: } 855: } 1.1 root 856: } 857: 1.1.1.9 root 858: 1.1.1.2 root 859: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 860: /** 861: * Return free PC file handle table index, or -1 if error 862: */ 1.1.1.7 root 863: static int GemDOS_FindFreeFileHandle(void) 1.1 root 864: { 1.1.1.9 root 865: int i; 1.1 root 866: 1.1.1.9 root 867: /* Scan our file list for free slot */ 1.1.1.21! root 868: for(i = 0; i < ARRAYSIZE(FileHandles); i++) 1.1.1.9 root 869: { 870: if (!FileHandles[i].bUsed) 871: return i; 872: } 1.1 root 873: 1.1.1.9 root 874: /* Cannot open any more files, return error */ 875: return -1; 1.1 root 876: } 877: 1.1.1.2 root 878: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 879: /** 1.1.1.21! root 880: * Check whether given basepage matches current program basepage ! 881: * or basepage for its parents. If yes, return true, otherwise false. ! 882: */ ! 883: static bool GemDOS_BasepageMatches(Uint32 checkbase) ! 884: { ! 885: int maxparents = 12; /* prevent basepage parent loops */ ! 886: Uint32 basepage = STMemory_ReadLong(act_pd); ! 887: while (maxparents-- > 0 && STMemory_ValidArea(basepage, 0x100)) ! 888: { ! 889: if (basepage == checkbase) ! 890: return true; ! 891: basepage = STMemory_ReadLong(basepage + 0x24); /* parent */ ! 892: } ! 893: return false; ! 894: } ! 895: ! 896: /** ! 897: * Check whether TOS handle is within our table range, or aliased, ! 898: * return (positive) internal Handle if yes, (negative) -1 for error. 1.1.1.12 root 899: */ 1.1.1.21! root 900: static int GemDOS_GetValidFileHandle(int Handle) 1.1 root 901: { 1.1.1.21! root 902: int Forced = -1; ! 903: ! 904: /* Has handle been aliased with Fforce()? */ ! 905: if (Handle >= 0 && Handle < ARRAYSIZE(ForcedHandles) ! 906: && ForcedHandles[Handle].Handle != UNFORCED_HANDLE) ! 907: { ! 908: if (GemDOS_BasepageMatches(ForcedHandles[Handle].Basepage)) ! 909: { ! 910: Forced = Handle; ! 911: Handle = ForcedHandles[Handle].Handle; ! 912: } ! 913: else ! 914: { ! 915: Log_Printf(LOG_WARN, "Removing (stale?) %d->%d file handle redirection.", ! 916: Handle, ForcedHandles[Handle].Handle); ! 917: GemDOS_UnforceFileHandle(Handle); ! 918: return -1; ! 919: } ! 920: } ! 921: else ! 922: { ! 923: Handle -= BASE_FILEHANDLE; ! 924: } ! 925: /* handle is valid for current program and in our handle table? */ ! 926: if (Handle >= 0 && Handle < ARRAYSIZE(FileHandles) 1.1.1.17 root 927: && FileHandles[Handle].bUsed) 928: { 1.1.1.21! root 929: Uint32 current = STMemory_ReadLong(act_pd); ! 930: if (FileHandles[Handle].Basepage == current || Forced >= 0) ! 931: return Handle; ! 932: /* bug in Atari program or in Hatari GEMDOS emu */ ! 933: Log_Printf(LOG_WARN, "PREVENTED: program 0x%x accessing program 0x%x file handle %d.", ! 934: current, FileHandles[Handle].Basepage, Handle); 1.1.1.17 root 935: } 936: /* invalid handle */ 1.1.1.21! root 937: return -1; 1.1 root 938: } 939: 1.1.1.2 root 940: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 941: /** 942: * Find drive letter from a filename, eg C,D... and return as drive ID(C:2, D:3...) 1.1.1.20 root 943: * returns the current drive number if no drive is specified. For special 944: * devices (CON:, AUX:, PRN:), returns an invalid drive number. 1.1.1.12 root 945: */ 1.1.1.7 root 946: static int GemDOS_FindDriveNumber(char *pszFileName) 1.1 root 947: { 1.1.1.9 root 948: /* Does have 'A:' or 'C:' etc.. at start of string? */ 1.1.1.20 root 949: if (pszFileName[0] != '\0' && pszFileName[1] == ':') 1.1.1.9 root 950: { 1.1.1.20 root 951: char letter = toupper(pszFileName[0]); 952: if (letter >= 'A' && letter <= 'Z') 953: return (letter-'A'); 954: } 955: else if (strlen(pszFileName) == 4 && pszFileName[3] == ':') 956: { 957: /* ':' can be used only as drive indicator, not otherwise, 958: * so no need to check even special device name. 959: */ 960: return 0; 1.1.1.9 root 961: } 962: return CurrentDrive; 1.1 root 963: } 964: 1.1.1.19 root 965: 966: /** 967: * Return true if drive ID (C:2, D:3 etc...) matches emulated hard-drive 968: */ 969: static bool GemDOS_IsDriveEmulated(int drive) 970: { 971: drive -= 2; 972: if (drive < 0 || drive >= MAX_HARDDRIVES) 973: return false; 974: if (!emudrives[drive]) 975: return false; 976: assert(emudrives[drive]->drive_number == drive+2); 977: return true; 978: } 979: 1.1.1.2 root 980: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 981: /** 982: * Return drive ID(C:2, D:3 etc...) or -1 if not one of our emulation hard-drives 983: */ 1.1.1.19 root 984: static int GemDOS_FileName2HardDriveID(char *pszFileName) 1.1 root 985: { 1.1.1.9 root 986: /* Do we even have a hard-drive? */ 987: if (GEMDOS_EMU_ON) 988: { 1.1.1.19 root 989: int DriveNumber; 990: 1.1.1.10 root 991: /* Find drive letter (as number) */ 1.1.1.17 root 992: DriveNumber = GemDOS_FindDriveNumber(pszFileName); 1.1.1.19 root 993: if (GemDOS_IsDriveEmulated(DriveNumber)) 994: return DriveNumber; 1.1.1.11 root 995: } 1.1.1.15 root 996: 1.1.1.10 root 997: /* Not a high-level redirected drive, let TOS handle it */ 1.1.1.9 root 998: return -1; 1.1 root 999: } 1000: 1.1.1.7 root 1001: 1002: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1003: /** 1.1.1.17 root 1004: * Check whether a file in given path matches given case-insensitive pattern. 1005: * Return first matched name which caller needs to free, or NULL for no match. 1006: */ 1007: static char* match_host_dir_entry(const char *path, const char *name, bool pattern) 1008: { 1009: struct dirent *entry; 1010: char *match = NULL; 1011: DIR *dir; 1012: 1013: dir = opendir(path); 1014: if (!dir) 1015: return NULL; 1016: 1.1.1.18 root 1017: #if DEBUG_PATTERN_MATCH 1018: fprintf(stderr, "GEMDOS match '%s'%s in '%s'", name, pattern?" (pattern)":"", path); 1019: #endif 1.1.1.17 root 1020: if (pattern) 1021: { 1022: while ((entry = readdir(dir))) 1023: { 1024: if (fsfirst_match(name, entry->d_name)) 1025: { 1026: match = strdup(entry->d_name); 1027: break; 1028: } 1029: } 1030: } 1031: else 1032: { 1033: while ((entry = readdir(dir))) 1034: { 1035: if (strcasecmp(name, entry->d_name) == 0) 1036: { 1037: match = strdup(entry->d_name); 1038: break; 1039: } 1040: } 1041: } 1042: closedir(dir); 1.1.1.18 root 1043: #if DEBUG_PATTERN_MATCH 1044: fprintf(stderr, "-> '%s'\n", match); 1045: #endif 1.1.1.17 root 1046: return match; 1047: } 1048: 1049: 1.1.1.21! root 1050: static int to_same(int ch) ! 1051: { ! 1052: return ch; ! 1053: } ! 1054: ! 1055: /** ! 1056: * Clip given file name to 8+3 length like TOS does, ! 1057: * return resulting name length. ! 1058: */ ! 1059: static int clip_to_83(char *name) ! 1060: { ! 1061: int diff, len; ! 1062: char *dot; ! 1063: ! 1064: dot = strrchr(name, '.'); ! 1065: if (dot) { ! 1066: diff = strlen(dot) - 4; ! 1067: if (diff > 0) ! 1068: { ! 1069: Log_Printf(LOG_WARN, "WARNING: have to clip %d chars from '%s' extension!\n", diff, name); ! 1070: dot[4] = '\0'; ! 1071: } ! 1072: diff = dot - name - 8; ! 1073: if (diff > 0) ! 1074: { ! 1075: Log_Printf(LOG_WARN, "WARNING: have to clip %d chars from '%s' base!\n", diff, name); ! 1076: memmove(name + 8, dot, strlen(dot) + 1); ! 1077: } ! 1078: return strlen(name); ! 1079: } ! 1080: len = strlen(name); ! 1081: if (len > 8) ! 1082: { ! 1083: Log_Printf(LOG_WARN, "WARNING: have to clip %d chars from '%s'!\n", len - 8, name); ! 1084: name[8] = '\0'; ! 1085: len = 8; ! 1086: } ! 1087: return len; ! 1088: } ! 1089: 1.1.1.17 root 1090: /*-----------------------------------------------------------------------*/ 1091: /** 1092: * Check whether given TOS file/dir exists in given host path. 1093: * If it does, add the matched host filename to the given path, 1094: * otherwise add the given filename as is to it. Guarantees 1095: * that the resulting string doesn't exceed maxlen+1. 1096: * 1097: * Return true if match found, false otherwise. 1098: */ 1099: static bool add_path_component(char *path, int maxlen, const char *origname, bool is_dir) 1100: { 1101: char *tmp, *match, name[strlen(origname) + 3]; 1102: int dot, namelen, pathlen; 1.1.1.21! root 1103: int (*chr_conv)(int); 1.1.1.17 root 1104: bool modified; 1105: 1106: /* append separator */ 1.1.1.21! root 1107: pathlen = strlen(path); 1.1.1.17 root 1108: if (pathlen >= maxlen) 1109: return false; 1110: path[pathlen++] = PATHSEP; 1111: path[pathlen] = '\0'; 1.1.1.21! root 1112: ! 1113: /* TOS clips names to 8+3 length */ ! 1114: strcpy(name, origname); ! 1115: namelen = clip_to_83(name); ! 1116: 1.1.1.17 root 1117: /* first try exact (case insensitive) match */ 1118: match = match_host_dir_entry(path, name, false); 1119: if (match) 1120: { 1121: /* use strncat so that string is always nul terminated */ 1122: strncat(path+pathlen, match, maxlen-pathlen); 1123: free(match); 1124: return true; 1125: } 1126: 1127: /* Here comes a work-around for a bug in the file selector 1128: * of TOS 1.02: When a folder name has exactly 8 characters, 1129: * it appends a '.' at the end of the name... 1130: */ 1131: if (is_dir && namelen == 9 && name[8] == '.') 1132: { 1133: name[8] = '\0'; 1134: match = match_host_dir_entry(path, name, false); 1135: if (match) 1136: { 1137: strncat(path+pathlen, match, maxlen-pathlen); 1138: free(match); 1139: return true; 1140: } 1141: } 1142: 1143: /* Assume there were invalid characters or that the host file 1144: * was too long to fit into GEMDOS 8+3 filename limits. 1.1.1.18 root 1145: * If that's the case, modify the name to a pattern that 1146: * will match such host files and try again. 1.1.1.17 root 1147: */ 1.1.1.18 root 1148: modified = false; 1.1.1.17 root 1149: 1150: /* catch potentially invalid characters */ 1151: for (tmp = name; *tmp; tmp++) 1152: { 1153: if (*tmp == INVALID_CHAR) 1154: { 1155: *tmp = '?'; 1156: modified = true; 1157: } 1158: } 1159: 1160: /* catch potentially too long extension */ 1161: for (dot = 0; name[dot] && name[dot] != '.'; dot++); 1162: if (namelen - dot > 3) 1163: { 1164: /* "emulated.too" -> "emulated.too*" */ 1165: name[namelen++] = '*'; 1166: name[namelen] = '\0'; 1167: modified = true; 1168: } 1169: /* catch potentially too long part before extension */ 1170: if (namelen > 8 && name[8] == '.') 1171: { 1172: /* "emulated.too*" -> "emulated*.too*" */ 1173: memmove(name+9, name+8, namelen-7); 1174: namelen++; 1175: name[8] = '*'; 1176: modified = true; 1177: } 1178: else if (namelen == 8) 1179: { 1180: /* "emulated" -> "emulated*" */ 1181: name[8] = '*'; 1182: name[9] = '\0'; 1183: namelen++; 1184: modified = true; 1185: } 1186: 1187: if (modified) 1188: { 1189: match = match_host_dir_entry(path, name, true); 1190: if (match) 1191: { 1192: strncat(path+pathlen, match, maxlen-pathlen); 1193: free(match); 1194: return true; 1195: } 1196: } 1197: 1198: /* not found, copy file/dirname as is */ 1.1.1.21! root 1199: switch (ConfigureParams.HardDisk.nGemdosCase) { ! 1200: case GEMDOS_UPPER: ! 1201: chr_conv = toupper; ! 1202: break; ! 1203: case GEMDOS_LOWER: ! 1204: chr_conv = tolower; ! 1205: break; ! 1206: default: ! 1207: chr_conv = to_same; ! 1208: } ! 1209: tmp = name; ! 1210: while (*origname) ! 1211: *tmp++ = chr_conv(*origname++); ! 1212: *tmp = '\0'; ! 1213: strncat(path+pathlen, name, maxlen-pathlen); 1.1.1.17 root 1214: return false; 1215: } 1216: 1217: 1218: /** 1219: * Join remaining path without matching. This helper is used after host 1220: * file name matching fails, to append the failing part of the TOS path 1221: * to the host path, so that it won't be a valid host path. 1222: * 1223: * Specifically, the path separators need to be converted, otherwise things 1224: * like Fcreate() could create files that have TOS directory names as part 1225: * of file names on Unix (as \ is valid filename char on Unix). Fcreate() 1226: * needs to create them only when just the file name isn't found, but all 1227: * the directory components have. 1.1.1.12 root 1228: */ 1.1.1.17 root 1229: static void add_remaining_path(const char *src, char *dstpath, int dstlen) 1.1.1.7 root 1230: { 1.1.1.17 root 1231: char *dst; 1232: int i; 1233: 1234: dstlen--; 1235: i = strlen(dstpath); 1236: for (dst = dstpath + i; *src && i < dstlen; dst++, src++, i++) 1237: { 1238: if (*src == '\\') 1239: *dst = PATHSEP; 1240: else 1241: *dst = *src; 1242: } 1243: *dst = '\0'; 1.1.1.6 root 1244: } 1245: 1.1.1.21! root 1246: 1.1.1.2 root 1247: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1248: /** 1.1.1.17 root 1249: * Use hard-drive directory, current ST directory and filename 1250: * to create correct path to host file system. If given filename 1251: * isn't found on host file system, just append GEMDOS filename 1252: * to the path as is. 1253: * 1254: * TODO: currently there are many callers which give this dest buffer of 1255: * MAX_GEMDOS_PATH size i.e. don't take into account that host filenames 1.1.1.21! root 1256: * can be up to FILENAME_MAX long. Plain GEMDOS paths themselves may be 1.1.1.17 root 1257: * MAX_GEMDOS_PATH long even before host dir is prepended to it! 1258: * Way forward: allocate the host path here as FILENAME_MAX so that 1259: * it's always long enough and let callers free it. Assert if alloc 1260: * fails so that callers' don't need to. 1.1.1.12 root 1261: */ 1262: void GemDOS_CreateHardDriveFileName(int Drive, const char *pszFileName, 1263: char *pszDestName, int nDestNameLen) 1.1 root 1264: { 1.1.1.17 root 1265: const char *s, *filename = pszFileName; 1266: int minlen; 1.1.1.11 root 1267: 1268: /* Is it a valid hard drive? */ 1269: if (Drive < 2) 1270: return; 1.1.1.3 root 1271: 1.1.1.17 root 1272: /* Check for valid string */ 1273: if (filename[0] == '\0') 1274: return; 1.1.1.9 root 1275: 1.1.1.17 root 1276: /* make sure that more convenient strncat() can be used 1277: * on the destination string (it always null terminates 1278: * unlike strncpy()). 1279: */ 1280: *pszDestName = 0; 1281: /* strcat writes n+1 chars, se decrease len */ 1282: nDestNameLen--; 1283: 1284: /* full filename with drive "C:\foo\bar" */ 1285: if (filename[1] == ':') 1286: { 1287: strncat(pszDestName, emudrives[Drive-2]->hd_emulation_dir, nDestNameLen); 1288: filename += 2; 1.1.1.9 root 1289: } 1.1.1.17 root 1290: /* filename referenced from root: "\foo\bar" */ 1291: else if (filename[0] == '\\') 1.1.1.9 root 1292: { 1.1.1.17 root 1293: strncat(pszDestName, emudrives[Drive-2]->hd_emulation_dir, nDestNameLen); 1.1.1.9 root 1294: } 1.1.1.17 root 1295: /* filename relative to current directory */ 1.1.1.9 root 1296: else 1297: { 1.1.1.17 root 1298: strncat(pszDestName, emudrives[Drive-2]->fs_currpath, nDestNameLen); 1299: } 1300: 1301: minlen = strlen(emudrives[Drive-2]->hd_emulation_dir); 1302: /* this doesn't take into account possible long host filenames 1303: * that will make dest name longer than pszFileName 8.3 paths, 1304: * or GEMDOS paths using "../" which make it smaller. Both 1305: * should(?) be rare in paths, so this info to user should be 1306: * good enough. 1307: */ 1308: if (nDestNameLen < minlen + (int)strlen(pszFileName) + 2) 1309: { 1310: Log_AlertDlg(LOG_ERROR, "Appending GEMDOS path '%s' to HDD emu host root dir doesn't fit to %d chars (current Hatari limit)!", 1311: pszFileName, nDestNameLen); 1312: add_remaining_path(filename, pszDestName, nDestNameLen); 1313: return; 1.1.1.6 root 1314: } 1.1.1.9 root 1315: 1.1.1.17 root 1316: /* "../" handling breaks if there are extra slashes */ 1317: File_CleanFileName(pszDestName); 1318: 1319: /* go through path directory components, advacing 'filename' 1320: * pointer while parsing them. 1321: */ 1322: for (;;) 1.1.1.9 root 1323: { 1.1.1.17 root 1324: /* skip extra path separators */ 1325: while (*filename == '\\') 1326: filename++; 1327: 1328: // fprintf(stderr, "filename: '%s', path: '%s'\n", filename, pszDestName); 1329: 1330: /* skip "." references to current directory */ 1331: if (filename[0] == '.' && 1332: (filename[1] == '\\' || !filename[1])) 1.1.1.9 root 1333: { 1.1.1.17 root 1334: filename++; 1.1.1.9 root 1335: continue; 1336: } 1.1.1.17 root 1337: 1338: /* ".." path component -> strip last dir from dest path */ 1339: if (filename[0] == '.' && 1340: filename[1] == '.' && 1341: (filename[2] == '\\' || !filename[2])) 1.1.1.9 root 1342: { 1.1.1.17 root 1343: char *sep = strrchr(pszDestName, PATHSEP); 1344: if (sep) 1.1.1.9 root 1345: { 1.1.1.17 root 1346: if (sep - pszDestName < minlen) 1347: Log_Printf(LOG_WARN, "GEMDOS path '%s' tried to back out of GEMDOS drive!\n", pszFileName); 1348: else 1349: *sep = '\0'; 1.1.1.9 root 1350: } 1.1.1.17 root 1351: filename += 2; 1352: continue; 1353: } 1354: 1355: /* handle directory component */ 1356: if ((s = strchr(filename, '\\'))) 1357: { 1358: int dirlen = s - filename; 1359: char dirname[dirlen+1]; 1360: /* copy dirname */ 1361: strncpy(dirname, filename, dirlen); 1362: dirname[dirlen] = '\0'; 1363: /* and advance filename */ 1364: filename = s; 1365: 1366: if (strchr(dirname, '?') || strchr(dirname, '*')) 1367: Log_Printf(LOG_WARN, "GEMDOS dir name '%s' with wildcards in %s!\n", dirname, pszFileName); 1368: 1369: /* convert and append dirname to host path */ 1370: if (!add_path_component(pszDestName, nDestNameLen, dirname, true)) 1.1.1.9 root 1371: { 1.1.1.17 root 1372: Log_Printf(LOG_WARN, "No GEMDOS dir '%s'\n", pszDestName); 1373: add_remaining_path(filename, pszDestName, nDestNameLen); 1374: return; 1.1.1.9 root 1375: } 1.1.1.17 root 1376: continue; 1.1.1.9 root 1377: } 1.1.1.17 root 1378: 1379: /* path directory components done */ 1380: break; 1.1.1.9 root 1381: } 1382: 1.1.1.17 root 1383: if (*filename) 1.1.1.9 root 1384: { 1.1.1.17 root 1385: /* a wildcard instead of a complete file name? */ 1386: if (strchr(filename,'?') || strchr(filename,'*')) 1.1.1.9 root 1387: { 1.1.1.17 root 1388: int len = strlen(pszDestName); 1389: if (len < nDestNameLen) 1.1.1.9 root 1390: { 1.1.1.17 root 1391: pszDestName[len++] = PATHSEP; 1392: pszDestName[len] = '\0'; 1.1.1.9 root 1393: } 1.1.1.17 root 1394: /* use strncat so that string is always nul terminated */ 1395: strncat(pszDestName+len, filename, nDestNameLen-len); 1396: } 1397: else if (!add_path_component(pszDestName, nDestNameLen, filename, false)) 1398: { 1399: /* It's often normal, that GEM uses this to test for 1400: * existence of desktop.inf or newdesk.inf for example. 1401: */ 1402: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS didn't find filename %s\n", pszDestName); 1403: return; 1.1.1.9 root 1404: } 1405: } 1.1.1.17 root 1406: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS: %s -> host: %s\n", pszFileName, pszDestName); 1.1 root 1407: } 1408: 1.1.1.6 root 1409: 1.1.1.2 root 1410: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1411: /** 1412: * GEMDOS Set drive (0=A,1=B,2=C etc...) 1413: * Call 0xE 1414: */ 1.1.1.13 root 1415: static bool GemDOS_SetDrv(Uint32 Params) 1.1 root 1416: { 1.1.1.9 root 1417: /* Read details from stack for our own use */ 1.1.1.17 root 1418: CurrentDrive = STMemory_ReadWord(Params); 1.1 root 1419: 1.1.1.20 root 1420: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x0E Dsetdrv(0x%x)\n", (int)CurrentDrive); 1.1.1.15 root 1421: 1.1.1.9 root 1422: /* Still re-direct to TOS */ 1.1.1.15 root 1423: return false; 1.1 root 1424: } 1425: 1426: 1.1.1.2 root 1427: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1428: /** 1429: * GEMDOS Set Disk Transfer Address (DTA) 1430: * Call 0x1A 1431: */ 1.1.1.13 root 1432: static bool GemDOS_SetDTA(Uint32 Params) 1.1 root 1433: { 1.1.1.17 root 1434: /* Look up on stack to find where DTA is */ 1435: Uint32 nDTA = STMemory_ReadLong(Params); 1.1.1.15 root 1436: 1.1.1.20 root 1437: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x1A Fsetdta(0x%x)\n", nDTA); 1.1.1.15 root 1438: 1.1.1.17 root 1439: if (STMemory_ValidArea(nDTA, sizeof(DTA))) 1440: { 1441: /* Store as PC pointer */ 1442: pDTA = (DTA *)STRAM_ADDR(nDTA); 1443: } 1444: else 1445: { 1446: pDTA = NULL; 1447: Log_Printf(LOG_WARN, "GEMDOS Fsetdta() failed due to invalid DTA address 0x%x\n", nDTA); 1448: } 1.1.1.18 root 1449: /* redirect to TOS */ 1.1.1.15 root 1450: return false; 1.1 root 1451: } 1452: 1.1.1.2 root 1453: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1454: /** 1455: * GEMDOS Dfree Free disk space. 1.1.1.15 root 1456: * Call 0x36 1.1.1.12 root 1457: */ 1.1.1.13 root 1458: static bool GemDOS_DFree(Uint32 Params) 1.1.1.2 root 1459: { 1.1.1.19 root 1460: #ifdef HAVE_STATVFS 1461: struct statvfs buf; 1462: #endif 1463: int Drive, Total, Free; 1.1.1.10 root 1464: Uint32 Address; 1.1.1.9 root 1465: 1.1.1.17 root 1466: Address = STMemory_ReadLong(Params); 1467: Drive = STMemory_ReadWord(Params+SIZE_LONG); 1.1.1.15 root 1468: 1.1.1.19 root 1469: /* Note: Drive = 0 means current drive, 1 = A:, 2 = B:, 3 = C:, etc. */ 1.1.1.20 root 1470: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x36 Dfree(0x%x, %i)\n", Address, Drive); 1.1.1.19 root 1471: if (Drive == 0) 1472: Drive = CurrentDrive; 1473: else 1474: Drive--; 1.1.1.15 root 1475: 1.1.1.9 root 1476: /* is it our drive? */ 1.1.1.19 root 1477: if (!GemDOS_IsDriveEmulated(Drive)) 1.1.1.9 root 1478: { 1.1.1.19 root 1479: /* no, redirect to TOS */ 1480: return false; 1481: } 1482: /* Check that write is requested to valid memory area */ 1483: if (!STMemory_ValidArea(Address, 16)) 1484: { 1485: Log_Printf(LOG_WARN, "GEMDOS Dfree() failed due to invalid RAM range 0x%x+%i\n", Address, 16); 1486: Regs[REG_D0] = GEMDOS_ERANGE; 1487: return true; 1488: } 1489: 1490: #ifdef HAVE_STATVFS 1491: if (statvfs(emudrives[Drive-2]->hd_emulation_dir, &buf) == 0) 1492: { 1493: Total = buf.f_blocks/1024 * buf.f_frsize; 1494: if (buf.f_bavail) 1495: Free = buf.f_bavail; /* free for unprivileged user */ 1496: else 1497: Free = buf.f_bfree; 1498: Free = Free/1024 * buf.f_bsize; 1499: 1500: /* TOS version limits based on: 1501: * http://www.seimet.de/atari/en/hddriverfaq.html 1502: */ 1503: if (TosVersion >= 0x0400) 1.1.1.18 root 1504: { 1.1.1.19 root 1505: if (Total > 1024*1024) 1506: Total = 1024*1024; 1.1.1.18 root 1507: } 1.1.1.19 root 1508: else 1509: { 1510: if (TosVersion >= 0x0106) 1511: { 1512: if (Total > 512*1024) 1513: Total = 512*1024; 1514: } 1515: else 1516: { 1517: if (Total > 256*1024) 1518: Total = 256*1024; 1519: } 1520: } 1521: if (Free > Total) 1522: Free = Total; 1.1.1.9 root 1523: } 1.1.1.19 root 1524: else 1525: #endif 1526: { 1527: /* fake 32MB drive with 16MB free */ 1528: Total = 32*1024; 1529: Free = 16*1024; 1530: } 1531: STMemory_WriteLong(Address, Free); /* free clusters */ 1532: STMemory_WriteLong(Address+SIZE_LONG, Total); /* total clusters */ 1533: 1534: STMemory_WriteLong(Address+SIZE_LONG*2, 512); /* bytes per sector */ 1535: STMemory_WriteLong(Address+SIZE_LONG*3, 2); /* sectors per cluster (cluster = 1KB) */ 1.1.1.21! root 1536: Regs[REG_D0] = GEMDOS_EOK; 1.1.1.19 root 1537: return true; 1.1.1.2 root 1538: } 1539: 1540: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1541: /** 1542: * GEMDOS MkDir 1543: * Call 0x39 1544: */ 1.1.1.13 root 1545: static bool GemDOS_MkDir(Uint32 Params) 1.1.1.6 root 1546: { 1.1.1.17 root 1547: char *pDirName, *psDirPath; 1.1.1.9 root 1548: int Drive; 1549: 1550: /* Find directory to make */ 1.1.1.17 root 1551: pDirName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1.1.1.9 root 1552: 1.1.1.20 root 1553: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x39 Dcreate(\"%s\")\n", pDirName); 1.1.1.15 root 1554: 1.1.1.19 root 1555: Drive = GemDOS_FileName2HardDriveID(pDirName); 1.1.1.9 root 1556: 1.1.1.17 root 1557: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 1558: { 1.1.1.17 root 1559: /* redirect to TOS */ 1560: return false; 1561: } 1.1.1.9 root 1562: 1.1.1.17 root 1563: /* write protected device? */ 1564: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 1565: { 1566: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Dcreate(\"%s\")\n", pDirName); 1567: Regs[REG_D0] = GEMDOS_EWRPRO; 1568: return true; 1569: } 1.1.1.9 root 1570: 1.1.1.17 root 1571: psDirPath = malloc(FILENAME_MAX); 1572: if (!psDirPath) 1573: { 1574: perror("GemDOS_MkDir"); 1575: Regs[REG_D0] = GEMDOS_ENSMEM; 1.1.1.15 root 1576: return true; 1.1.1.9 root 1577: } 1.1.1.17 root 1578: 1579: /* Copy old directory, as if calls fails keep this one */ 1580: GemDOS_CreateHardDriveFileName(Drive, pDirName, psDirPath, FILENAME_MAX); 1581: 1582: /* Attempt to make directory */ 1583: if (mkdir(psDirPath, 0755) == 0) 1584: Regs[REG_D0] = GEMDOS_EOK; 1585: else 1586: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */ 1587: 1588: free(psDirPath); 1589: return true; 1.1 root 1590: } 1591: 1.1.1.2 root 1592: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1593: /** 1594: * GEMDOS RmDir 1595: * Call 0x3A 1596: */ 1.1.1.13 root 1597: static bool GemDOS_RmDir(Uint32 Params) 1.1.1.6 root 1598: { 1.1.1.17 root 1599: char *pDirName, *psDirPath; 1.1.1.9 root 1600: int Drive; 1601: 1602: /* Find directory to make */ 1.1.1.17 root 1603: pDirName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1.1.1.15 root 1604: 1.1.1.20 root 1605: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x3A Ddelete(\"%s\")\n", pDirName); 1.1.1.15 root 1606: 1.1.1.19 root 1607: Drive = GemDOS_FileName2HardDriveID(pDirName); 1.1.1.15 root 1608: 1.1.1.17 root 1609: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 1610: { 1.1.1.17 root 1611: /* redirect to TOS */ 1612: return false; 1613: } 1.1.1.9 root 1614: 1.1.1.17 root 1615: /* write protected device? */ 1616: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 1617: { 1618: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Ddelete(\"%s\")\n", pDirName); 1619: Regs[REG_D0] = GEMDOS_EWRPRO; 1620: return true; 1621: } 1.1.1.9 root 1622: 1.1.1.17 root 1623: psDirPath = malloc(FILENAME_MAX); 1624: if (!psDirPath) 1625: { 1626: perror("GemDOS_RmDir"); 1627: Regs[REG_D0] = GEMDOS_ENSMEM; 1.1.1.15 root 1628: return true; 1.1.1.9 root 1629: } 1.1.1.17 root 1630: 1631: /* Copy old directory, as if calls fails keep this one */ 1632: GemDOS_CreateHardDriveFileName(Drive, pDirName, psDirPath, FILENAME_MAX); 1633: 1634: /* Attempt to remove directory */ 1635: if (rmdir(psDirPath) == 0) 1636: Regs[REG_D0] = GEMDOS_EOK; 1637: else 1638: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */ 1639: 1640: free(psDirPath); 1641: return true; 1.1 root 1642: } 1643: 1.1.1.11 root 1644: 1.1.1.2 root 1645: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1646: /** 1647: * GEMDOS ChDir 1648: * Call 0x3B 1649: */ 1.1.1.13 root 1650: static bool GemDOS_ChDir(Uint32 Params) 1.1.1.6 root 1651: { 1.1.1.17 root 1652: char *pDirName, *psTempDirPath; 1653: struct stat buf; 1.1.1.9 root 1654: int Drive; 1.1 root 1655: 1.1.1.9 root 1656: /* Find new directory */ 1.1.1.17 root 1657: pDirName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1.1.1.3 root 1658: 1.1.1.20 root 1659: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x3B Dsetpath(\"%s\")\n", pDirName); 1.1.1.3 root 1660: 1.1.1.19 root 1661: Drive = GemDOS_FileName2HardDriveID(pDirName); 1.1.1.3 root 1662: 1.1.1.17 root 1663: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 1664: { 1.1.1.17 root 1665: /* redirect to TOS */ 1666: return false; 1667: } 1.1.1.3 root 1668: 1.1.1.17 root 1669: /* Allocate temporary memory for path name: */ 1670: psTempDirPath = malloc(FILENAME_MAX); 1671: if (!psTempDirPath) 1672: { 1673: perror("GemDOS_ChDir"); 1674: Regs[REG_D0] = GEMDOS_ENSMEM; 1675: return true; 1676: } 1.1.1.11 root 1677: 1.1.1.17 root 1678: GemDOS_CreateHardDriveFileName(Drive, pDirName, psTempDirPath, FILENAME_MAX); 1.1.1.15 root 1679: 1.1.1.17 root 1680: // Remove trailing slashes (stat on Windows does not like that) 1681: File_CleanFileName(psTempDirPath); 1.1.1.15 root 1682: 1.1.1.17 root 1683: if (stat(psTempDirPath, &buf)) 1684: { 1685: /* error */ 1686: free(psTempDirPath); 1687: Regs[REG_D0] = GEMDOS_EPTHNF; 1688: return true; 1689: } 1.1.1.9 root 1690: 1.1.1.17 root 1691: File_AddSlashToEndFileName(psTempDirPath); 1692: File_MakeAbsoluteName(psTempDirPath); 1.1.1.9 root 1693: 1.1.1.17 root 1694: /* Prevent '..' commands moving BELOW the root HDD folder */ 1695: /* by double checking if path is valid */ 1696: if (strncmp(psTempDirPath, emudrives[Drive-2]->hd_emulation_dir, 1.1.1.15 root 1697: strlen(emudrives[Drive-2]->hd_emulation_dir)) == 0) 1.1.1.17 root 1698: { 1699: strcpy(emudrives[Drive-2]->fs_currpath, psTempDirPath); 1700: Regs[REG_D0] = GEMDOS_EOK; 1701: } 1702: else 1703: { 1704: Regs[REG_D0] = GEMDOS_EPTHNF; 1.1.1.9 root 1705: } 1.1.1.17 root 1706: free(psTempDirPath); 1707: 1708: return true; 1.1.1.6 root 1709: 1.1.1.17 root 1710: } 1711: 1712: 1713: /*-----------------------------------------------------------------------*/ 1714: /** 1715: * Helper to check whether given file's path is missing. 1716: * Returns true if missing, false if found. 1717: * Modifies the argument buffer. 1718: */ 1719: static bool GemDOS_FilePathMissing(char *szActualFileName) 1720: { 1721: char *ptr = strrchr(szActualFileName, PATHSEP); 1722: if (ptr) 1723: { 1724: *ptr = 0; /* Strip filename from string */ 1.1.1.18 root 1725: if (!File_DirExists(szActualFileName)) 1.1.1.17 root 1726: return true; 1727: } 1728: return false; 1.1 root 1729: } 1730: 1.1.1.11 root 1731: 1.1.1.2 root 1732: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1733: /** 1734: * GEMDOS Create file 1735: * Call 0x3C 1736: */ 1.1.1.13 root 1737: static bool GemDOS_Create(Uint32 Params) 1.1 root 1738: { 1.1.1.17 root 1739: /* TODO: host filenames might not fit into this */ 1.1.1.11 root 1740: char szActualFileName[MAX_GEMDOS_PATH]; 1.1.1.17 root 1741: char *pszFileName; 1.1.1.21! root 1742: int Drive,Index, Mode; 1.1.1.9 root 1743: 1744: /* Find filename */ 1.1.1.17 root 1745: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1746: Mode = STMemory_ReadWord(Params+SIZE_LONG); 1.1.1.15 root 1747: 1.1.1.20 root 1748: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x3C Fcreate(\"%s\", 0x%x)\n", pszFileName, Mode); 1.1.1.15 root 1749: 1.1.1.19 root 1750: Drive = GemDOS_FileName2HardDriveID(pszFileName); 1.1.1.15 root 1751: 1.1.1.12 root 1752: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 1753: { 1.1.1.17 root 1754: /* redirect to TOS */ 1.1.1.15 root 1755: return false; 1.1.1.12 root 1756: } 1.1.1.9 root 1757: 1.1.1.12 root 1758: if (Mode == GEMDOS_FILE_ATTRIB_VOLUME_LABEL) 1759: { 1.1.1.17 root 1760: Log_Printf(LOG_WARN, "Warning: Hatari doesn't support GEMDOS volume" 1761: " label setting\n(for '%s')\n", pszFileName); 1.1.1.12 root 1762: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */ 1.1.1.15 root 1763: return true; 1.1.1.12 root 1764: } 1.1 root 1765: 1.1.1.17 root 1766: /* write protected device? */ 1767: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 1768: { 1769: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fcreate(\"%s\")\n", pszFileName); 1770: Regs[REG_D0] = GEMDOS_EWRPRO; 1771: return true; 1772: } 1773: 1.1.1.12 root 1774: /* Now convert to hard drive filename */ 1775: GemDOS_CreateHardDriveFileName(Drive, pszFileName, 1776: szActualFileName, sizeof(szActualFileName)); 1777: 1778: /* Find slot to store file handle, as need to return WORD handle for ST */ 1779: Index = GemDOS_FindFreeFileHandle(); 1.1.1.21! root 1780: if (Index == -1) 1.1.1.12 root 1781: { 1782: /* No free handles, return error code */ 1783: Regs[REG_D0] = GEMDOS_ENHNDL; /* No more handles */ 1.1.1.15 root 1784: return true; 1.1.1.12 root 1785: } 1.1.1.17 root 1786: 1787: /* truncate and open for reading & writing */ 1788: FileHandles[Index].FileHandle = fopen(szActualFileName, "wb+"); 1.1.1.15 root 1789: 1.1.1.12 root 1790: if (FileHandles[Index].FileHandle != NULL) 1791: { 1.1.1.17 root 1792: /* FIXME: implement other Mode attributes 1793: * - GEMDOS_FILE_ATTRIB_HIDDEN (FA_HIDDEN) 1794: * - GEMDOS_FILE_ATTRIB_SYSTEM_FILE (FA_SYSTEM) 1795: * - GEMDOS_FILE_ATTRIB_SUBDIRECTORY (FA_DIR) 1796: * - GEMDOS_FILE_ATTRIB_WRITECLOSE (FA_ARCHIVE) 1797: * (set automatically by GemDOS >= 0.15) 1798: */ 1799: if (Mode & GEMDOS_FILE_ATTRIB_READONLY) 1800: { 1801: /* after closing, file should be read-only */ 1802: chmod(szActualFileName, S_IRUSR|S_IRGRP|S_IROTH); 1803: } 1.1.1.21! root 1804: /* Tag handle table entry as used in this process and return handle */ 1.1.1.15 root 1805: FileHandles[Index].bUsed = true; 1.1.1.21! root 1806: FileHandles[Index].Basepage = STMemory_ReadLong(act_pd); 1.1.1.17 root 1807: snprintf(FileHandles[Index].szActualName, 1808: sizeof(FileHandles[Index].szActualName), 1809: "%s", szActualFileName); 1810: 1811: /* Return valid ST file handle from our range (from BASE_FILEHANDLE upwards) */ 1812: Regs[REG_D0] = Index+BASE_FILEHANDLE; 1.1.1.21! root 1813: LOG_TRACE(TRACE_OS_GEMDOS, "-> FD %d (%s)\n", Regs[REG_D0], 1.1.1.17 root 1814: Mode & GEMDOS_FILE_ATTRIB_READONLY ? "read-only":"read/write"); 1.1.1.15 root 1815: return true; 1.1.1.12 root 1816: } 1.1.1.2 root 1817: 1.1.1.17 root 1818: /* We failed to create the file, did we have required access rights? */ 1819: if (errno == EACCES || errno == EROFS || 1820: errno == EPERM || errno == EISDIR) 1.1.1.12 root 1821: { 1.1.1.17 root 1822: Log_Printf(LOG_WARN, "GEMDOS failed to create/truncate '%s'\n", 1823: szActualFileName); 1824: Regs[REG_D0] = GEMDOS_EACCDN; 1825: return true; 1.1.1.9 root 1826: } 1.1.1.17 root 1827: 1828: /* Or was path to file missing? (ST-Zip 2.6 relies on getting 1829: * correct error about that during extraction of ZIP files.) 1830: */ 1831: if (errno == ENOTDIR || GemDOS_FilePathMissing(szActualFileName)) 1832: { 1833: Regs[REG_D0] = GEMDOS_EPTHNF; /* Path not found */ 1834: return true; 1835: } 1836: 1.1.1.12 root 1837: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */ 1.1.1.15 root 1838: return true; 1.1 root 1839: } 1840: 1.1.1.21! root 1841: 1.1.1.2 root 1842: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1843: /** 1844: * GEMDOS Open file 1845: * Call 0x3D 1846: */ 1.1.1.13 root 1847: static bool GemDOS_Open(Uint32 Params) 1.1 root 1848: { 1.1.1.17 root 1849: /* TODO: host filenames might not fit into this */ 1.1.1.11 root 1850: char szActualFileName[MAX_GEMDOS_PATH]; 1.1.1.9 root 1851: char *pszFileName; 1.1.1.17 root 1852: const char *ModeStr; 1853: /* convert atari modes to stdio modes */ 1854: struct { 1855: const char *mode; 1856: const char *desc; 1857: } Modes[] = { 1858: { "rb", "read-only" }, 1859: /* FIXME: is actually read/write as "wb" would truncate */ 1860: { "rb+", "write-only" }, 1861: { "rb+", "read/write" }, 1862: { "rb+", "read/write" } 1863: }; 1864: int Drive, Index, Mode; 1.1.1.18 root 1865: FILE *AutostartHandle; 1.1.1.9 root 1866: 1867: /* Find filename */ 1.1.1.17 root 1868: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1869: Mode = STMemory_ReadWord(Params+SIZE_LONG); 1.1.1.15 root 1870: 1.1.1.20 root 1871: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x3D Fopen(\"%s\", %s)\n", pszFileName, Modes[Mode&0x03].desc); 1.1.1.15 root 1872: 1.1.1.19 root 1873: Drive = GemDOS_FileName2HardDriveID(pszFileName); 1.1.1.9 root 1874: 1.1.1.12 root 1875: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 1876: { 1.1.1.17 root 1877: /* redirect to TOS */ 1.1.1.20 root 1878: LOG_TRACE(TRACE_OS_GEMDOS, "-> to TOS\n"); 1.1.1.15 root 1879: return false; 1.1.1.12 root 1880: } 1.1.1.17 root 1881: 1.1.1.12 root 1882: /* Find slot to store file handle, as need to return WORD handle for ST */ 1883: Index = GemDOS_FindFreeFileHandle(); 1884: if (Index == -1) 1885: { 1886: /* No free handles, return error code */ 1887: Regs[REG_D0] = GEMDOS_ENHNDL; /* No more handles */ 1.1.1.15 root 1888: return true; 1.1.1.9 root 1889: } 1.1.1.6 root 1890: 1.1.1.17 root 1891: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 1892: { 1893: /* force all accesses to be read-only */ 1894: ModeStr = Modes[0].mode; 1895: } 1896: else 1897: { 1898: /* GEMDOS mount can be written, try open in requested mode */ 1899: ModeStr = Modes[Mode&0x03].mode; 1900: } 1901: 1.1.1.18 root 1902: 1903: if ((AutostartHandle = TOS_AutoStartOpen(pszFileName))) 1904: { 1905: strcpy(szActualFileName, pszFileName); 1906: FileHandles[Index].FileHandle = AutostartHandle; 1907: } 1908: else 1909: { 1910: /* Convert to hard drive filename */ 1911: GemDOS_CreateHardDriveFileName(Drive, pszFileName, 1912: szActualFileName, sizeof(szActualFileName)); 1913: 1914: /* FIXME: Open file 1915: * - fopen() modes don't allow write-only mode without truncating 1916: * which would be needed to implement mode 1 (write-only) correctly. 1917: * Fixing this requires using open() and file descriptors instead 1918: * of fopen() and FILE* pointers, but Windows doesn't support that. 1919: */ 1.1.1.21! root 1920: FileHandles[Index].FileHandle = fopen(szActualFileName, ModeStr); 1.1.1.18 root 1921: } 1.1.1.15 root 1922: 1.1.1.12 root 1923: if (FileHandles[Index].FileHandle != NULL) 1924: { 1.1.1.21! root 1925: /* Tag handle table entry as used in this process and return handle */ 1.1.1.15 root 1926: FileHandles[Index].bUsed = true; 1.1.1.21! root 1927: FileHandles[Index].Basepage = STMemory_ReadLong(act_pd); 1.1.1.17 root 1928: snprintf(FileHandles[Index].szActualName, 1929: sizeof(FileHandles[Index].szActualName), 1930: "%s", szActualFileName); 1931: 1.1.1.21! root 1932: GemDOS_UpdateLastProgram(Index); ! 1933: 1.1.1.17 root 1934: /* Return valid ST file handle from our range (BASE_FILEHANDLE upwards) */ 1935: Regs[REG_D0] = Index+BASE_FILEHANDLE; 1936: LOG_TRACE(TRACE_OS_GEMDOS, "-> FD %d (%s)\n", 1.1.1.21! root 1937: Regs[REG_D0], Modes[Mode&0x03].desc); 1.1.1.15 root 1938: return true; 1.1.1.12 root 1939: } 1.1.1.15 root 1940: 1.1.1.17 root 1941: if (errno == EACCES || errno == EROFS || 1942: errno == EPERM || errno == EISDIR) 1943: { 1944: Log_Printf(LOG_WARN, "GEMDOS missing %s permission to file '%s'\n", 1945: Modes[Mode&0x03].desc, szActualFileName); 1946: Regs[REG_D0] = GEMDOS_EACCDN; 1947: return true; 1948: } 1949: if (errno == ENOTDIR || GemDOS_FilePathMissing(szActualFileName)) 1950: { 1951: /* Path not found */ 1952: Regs[REG_D0] = GEMDOS_EPTHNF; 1953: return true; 1954: } 1955: /* File not found / error opening */ 1956: Regs[REG_D0] = GEMDOS_EFILNF; 1.1.1.15 root 1957: return true; 1.1 root 1958: } 1959: 1.1.1.2 root 1960: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 1961: /** 1962: * GEMDOS Close file 1963: * Call 0x3E 1964: */ 1.1.1.13 root 1965: static bool GemDOS_Close(Uint32 Params) 1.1 root 1966: { 1.1.1.21! root 1967: int i, Handle; 1.1 root 1968: 1.1.1.9 root 1969: /* Find our handle - may belong to TOS */ 1.1.1.17 root 1970: Handle = STMemory_ReadWord(Params); 1.1 root 1971: 1.1.1.20 root 1972: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x3E Fclose(%i)\n", Handle); 1.1.1.15 root 1973: 1.1.1.21! root 1974: /* Get internal handle */ ! 1975: if ((Handle = GemDOS_GetValidFileHandle(Handle)) < 0) 1.1.1.9 root 1976: { 1.1.1.17 root 1977: /* no, assume it was TOS one -> redirect */ 1.1.1.15 root 1978: return false; 1.1.1.9 root 1979: } 1.1.1.17 root 1980: 1981: /* Close file and free up handle table */ 1.1.1.21! root 1982: if (TOS_AutoStartClose(FileHandles[Handle].FileHandle)) 1.1.1.18 root 1983: { 1.1.1.21! root 1984: FileHandles[Handle].bUsed = false; 1.1.1.18 root 1985: } 1.1.1.21! root 1986: GemDOS_CloseFileHandle(Handle); 1.1.1.17 root 1987: 1.1.1.21! root 1988: /* unalias handle */ ! 1989: for (i = 0; i < ARRAYSIZE(ForcedHandles); i++) ! 1990: { ! 1991: if (ForcedHandles[i].Handle == Handle) ! 1992: GemDOS_UnforceFileHandle(i); ! 1993: } 1.1.1.17 root 1994: /* Return no error */ 1995: Regs[REG_D0] = GEMDOS_EOK; 1996: return true; 1.1 root 1997: } 1998: 1.1.1.2 root 1999: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2000: /** 2001: * GEMDOS Read file 2002: * Call 0x3F 2003: */ 1.1.1.13 root 2004: static bool GemDOS_Read(Uint32 Params) 1.1 root 2005: { 1.1.1.9 root 2006: char *pBuffer; 1.1.1.17 root 2007: long CurrentPos, FileSize, nBytesRead, nBytesLeft; 2008: Uint32 Addr; 2009: Uint32 Size; 1.1.1.9 root 2010: int Handle; 2011: 2012: /* Read details from stack */ 1.1.1.17 root 2013: Handle = STMemory_ReadWord(Params); 2014: Size = STMemory_ReadLong(Params+SIZE_WORD); 2015: Addr = STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG); 2016: pBuffer = (char *)STRAM_ADDR(Addr); 1.1.1.9 root 2017: 1.1.1.20 root 2018: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x3F Fread(%i, %i, 0x%x)\n", 1.1.1.17 root 2019: Handle, Size, Addr); 2020: 1.1.1.21! root 2021: /* Get internal handle */ ! 2022: if ((Handle = GemDOS_GetValidFileHandle(Handle)) < 0) 1.1.1.9 root 2023: { 1.1.1.17 root 2024: /* assume it was TOS one -> redirect */ 1.1.1.15 root 2025: return false; 1.1.1.9 root 2026: } 2027: 1.1.1.17 root 2028: /* Old TOS versions treat the Size parameter as signed */ 2029: if (TosVersion < 0x400 && (Size & 0x80000000)) 2030: { 2031: /* return -1 as original GEMDOS */ 2032: Regs[REG_D0] = -1; 2033: return true; 2034: } 2035: 2036: /* To quick check to see where our file pointer is and how large the file is */ 2037: CurrentPos = ftell(FileHandles[Handle].FileHandle); 2038: fseek(FileHandles[Handle].FileHandle, 0, SEEK_END); 2039: FileSize = ftell(FileHandles[Handle].FileHandle); 2040: fseek(FileHandles[Handle].FileHandle, CurrentPos, SEEK_SET); 2041: 2042: nBytesLeft = FileSize-CurrentPos; 2043: 2044: /* Check for bad size and End Of File */ 2045: if (Size <= 0 || nBytesLeft <= 0) 2046: { 2047: /* return zero (bytes read) as original GEMDOS/EmuTOS */ 2048: Regs[REG_D0] = 0; 2049: return true; 2050: } 1.1.1.9 root 2051: 1.1.1.17 root 2052: /* Limit to size of file to prevent errors */ 2053: if (Size > (Uint32)nBytesLeft) 2054: Size = nBytesLeft; 1.1.1.9 root 2055: 1.1.1.17 root 2056: /* Check that read is to valid memory area */ 2057: if (!STMemory_ValidArea(Addr, Size)) 2058: { 1.1.1.18 root 2059: Log_Printf(LOG_WARN, "GEMDOS Fread() failed due to invalid RAM range 0x%x+%i\n", Addr, Size); 1.1.1.17 root 2060: Regs[REG_D0] = GEMDOS_ERANGE; 2061: return true; 1.1.1.9 root 2062: } 1.1.1.17 root 2063: /* And read data in */ 2064: nBytesRead = fread(pBuffer, 1, Size, FileHandles[Handle].FileHandle); 2065: 2066: /* Return number of bytes read */ 2067: Regs[REG_D0] = nBytesRead; 2068: 2069: return true; 1.1 root 2070: } 2071: 1.1.1.2 root 2072: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2073: /** 2074: * GEMDOS Write file 2075: * Call 0x40 2076: */ 1.1.1.13 root 2077: static bool GemDOS_Write(Uint32 Params) 1.1 root 2078: { 1.1.1.9 root 2079: char *pBuffer; 1.1.1.17 root 2080: long nBytesWritten; 2081: Uint32 Addr; 2082: Sint32 Size; 1.1.1.9 root 2083: int Handle; 1.1.1.21! root 2084: FILE *fp; 1.1 root 2085: 1.1.1.9 root 2086: /* Read details from stack */ 1.1.1.17 root 2087: Handle = STMemory_ReadWord(Params); 2088: Size = STMemory_ReadLong(Params+SIZE_WORD); 2089: Addr = STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG); 2090: pBuffer = (char *)STRAM_ADDR(Addr); 2091: 1.1.1.20 root 2092: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x40 Fwrite(%i, %i, 0x%x)\n", 1.1.1.17 root 2093: Handle, Size, Addr); 1.1.1.9 root 2094: 1.1.1.21! root 2095: /* Get internal handle */ ! 2096: if ((Handle = GemDOS_GetValidFileHandle(Handle)) < 0) 1.1.1.9 root 2097: { 1.1.1.17 root 2098: /* assume it was TOS one -> redirect */ 2099: return false; 2100: } 1.1.1.9 root 2101: 1.1.1.17 root 2102: /* write protected device? */ 2103: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 2104: { 2105: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fwrite(%d,...)\n", Handle); 2106: Regs[REG_D0] = GEMDOS_EWRPRO; 1.1.1.15 root 2107: return true; 1.1.1.9 root 2108: } 1.1 root 2109: 1.1.1.17 root 2110: /* Check that write is from valid memory area */ 2111: if (!STMemory_ValidArea(Addr, Size)) 2112: { 1.1.1.18 root 2113: Log_Printf(LOG_WARN, "GEMDOS Fwrite() failed due to invalid RAM range 0x%x+%i\n", Addr, Size); 1.1.1.17 root 2114: Regs[REG_D0] = GEMDOS_ERANGE; 2115: return true; 2116: } 2117: 1.1.1.21! root 2118: fp = FileHandles[Handle].FileHandle; ! 2119: nBytesWritten = fwrite(pBuffer, 1, Size, fp); ! 2120: if (ferror(fp)) 1.1.1.17 root 2121: { 2122: Log_Printf(LOG_WARN, "GEMDOS failed to write to '%s'\n", 2123: FileHandles[Handle].szActualName ); 2124: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied (ie read-only) */ 2125: } 2126: else 2127: { 1.1.1.21! root 2128: fflush(fp); 1.1.1.17 root 2129: Regs[REG_D0] = nBytesWritten; /* OK */ 2130: } 2131: return true; 1.1 root 2132: } 2133: 1.1.1.21! root 2134: 1.1.1.2 root 2135: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2136: /** 2137: * GEMDOS Delete file 2138: * Call 0x41 2139: */ 1.1.1.13 root 2140: static bool GemDOS_FDelete(Uint32 Params) 1.1 root 2141: { 1.1.1.17 root 2142: char *pszFileName, *psActualFileName; 1.1.1.9 root 2143: int Drive; 2144: 2145: /* Find filename */ 1.1.1.17 root 2146: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1.1.1.15 root 2147: 1.1.1.20 root 2148: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x41 Fdelete(\"%s\")\n", pszFileName); 1.1.1.15 root 2149: 1.1.1.19 root 2150: Drive = GemDOS_FileName2HardDriveID(pszFileName); 1.1.1.15 root 2151: 1.1.1.17 root 2152: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 2153: { 1.1.1.17 root 2154: /* redirect to TOS */ 2155: return false; 2156: } 1.1 root 2157: 1.1.1.17 root 2158: /* write protected device? */ 2159: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 2160: { 2161: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fdelete(\"%s\")\n", pszFileName); 2162: Regs[REG_D0] = GEMDOS_EWRPRO; 2163: return true; 2164: } 1.1.1.9 root 2165: 1.1.1.17 root 2166: psActualFileName = malloc(FILENAME_MAX); 2167: if (!psActualFileName) 2168: { 2169: perror("GemDOS_FDelete"); 2170: Regs[REG_D0] = GEMDOS_ENSMEM; 1.1.1.15 root 2171: return true; 1.1.1.9 root 2172: } 1.1 root 2173: 1.1.1.17 root 2174: /* And convert to hard drive filename */ 2175: GemDOS_CreateHardDriveFileName(Drive, pszFileName, psActualFileName, FILENAME_MAX); 2176: 2177: /* Now delete file?? */ 2178: if (unlink(psActualFileName) == 0) 2179: Regs[REG_D0] = GEMDOS_EOK; /* OK */ 2180: else 2181: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */ 2182: 2183: free(psActualFileName); 2184: return true; 1.1 root 2185: } 2186: 1.1.1.21! root 2187: 1.1.1.2 root 2188: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2189: /** 2190: * GEMDOS File seek 2191: * Call 0x42 2192: */ 1.1.1.13 root 2193: static bool GemDOS_LSeek(Uint32 Params) 1.1 root 2194: { 1.1.1.9 root 2195: long Offset; 1.1.1.15 root 2196: int Handle, Mode; 2197: long nFileSize; 2198: long nOldPos, nDestPos; 2199: FILE *fhndl; 1.1.1.9 root 2200: 2201: /* Read details from stack */ 1.1.1.17 root 2202: Offset = (Sint32)STMemory_ReadLong(Params); 2203: Handle = STMemory_ReadWord(Params+SIZE_LONG); 2204: Mode = STMemory_ReadWord(Params+SIZE_LONG+SIZE_WORD); 1.1 root 2205: 1.1.1.20 root 2206: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x42 Fseek(%li, %i, %i)\n", Offset, Handle, Mode); 1.1.1.15 root 2207: 1.1.1.21! root 2208: /* get internal handle */ ! 2209: if ((Handle = GemDOS_GetValidFileHandle(Handle)) < 0) 1.1.1.9 root 2210: { 1.1.1.17 root 2211: /* assume it was TOS one -> redirect */ 1.1.1.15 root 2212: return false; 1.1.1.9 root 2213: } 1.1.1.15 root 2214: 2215: fhndl = FileHandles[Handle].FileHandle; 2216: 2217: /* Save old position in file */ 2218: nOldPos = ftell(fhndl); 2219: 2220: /* Determine the size of the file */ 2221: fseek(fhndl, 0L, SEEK_END); 2222: nFileSize = ftell(fhndl); 2223: 2224: switch (Mode) 2225: { 1.1.1.17 root 2226: case 0: nDestPos = Offset; break; /* positive offset */ 1.1.1.15 root 2227: case 1: nDestPos = nOldPos + Offset; break; 1.1.1.17 root 2228: case 2: nDestPos = nFileSize + Offset; break; /* negative offset */ 1.1.1.15 root 2229: default: 2230: /* Restore old position and return error */ 2231: fseek(fhndl, nOldPos, SEEK_SET); 2232: Regs[REG_D0] = GEMDOS_EINVFN; 2233: return true; 2234: } 2235: 2236: if (nDestPos < 0 || nDestPos > nFileSize) 1.1.1.9 root 2237: { 1.1.1.15 root 2238: /* Restore old position and return error */ 2239: fseek(fhndl, nOldPos, SEEK_SET); 2240: Regs[REG_D0] = GEMDOS_ERANGE; 2241: return true; 1.1.1.9 root 2242: } 1.1.1.15 root 2243: 2244: /* Seek to new position and return offset from start of file */ 2245: fseek(fhndl, nDestPos, SEEK_SET); 2246: Regs[REG_D0] = ftell(fhndl); 2247: 2248: return true; 1.1 root 2249: } 2250: 1.1.1.10 root 2251: 2252: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2253: /** 1.1.1.17 root 2254: * GEMDOS Fattrib() - get or set file and directory attributes 1.1.1.12 root 2255: * Call 0x43 2256: */ 1.1.1.13 root 2257: static bool GemDOS_Fattrib(Uint32 Params) 1.1.1.10 root 2258: { 1.1.1.17 root 2259: /* TODO: host filenames might not fit into this */ 1.1.1.11 root 2260: char sActualFileName[MAX_GEMDOS_PATH]; 1.1.1.10 root 2261: char *psFileName; 2262: int nDrive; 2263: int nRwFlag, nAttrib; 1.1.1.12 root 2264: struct stat FileStat; 1.1.1.10 root 2265: 2266: /* Find filename */ 1.1.1.17 root 2267: psFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 1.1.1.19 root 2268: nDrive = GemDOS_FileName2HardDriveID(psFileName); 1.1.1.10 root 2269: 1.1.1.17 root 2270: nRwFlag = STMemory_ReadWord(Params+SIZE_LONG); 2271: nAttrib = STMemory_ReadWord(Params+SIZE_LONG+SIZE_WORD); 1.1.1.10 root 2272: 1.1.1.20 root 2273: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x43 Fattrib(\"%s\", %d, 0x%x)\n", 1.1.1.15 root 2274: psFileName, nRwFlag, nAttrib); 1.1.1.10 root 2275: 1.1.1.12 root 2276: if (!ISHARDDRIVE(nDrive)) 1.1.1.10 root 2277: { 1.1.1.17 root 2278: /* redirect to TOS */ 1.1.1.15 root 2279: return false; 1.1.1.12 root 2280: } 1.1.1.10 root 2281: 1.1.1.12 root 2282: /* Convert to hard drive filename */ 2283: GemDOS_CreateHardDriveFileName(nDrive, psFileName, 2284: sActualFileName, sizeof(sActualFileName)); 1.1.1.10 root 2285: 1.1.1.12 root 2286: if (nAttrib == GEMDOS_FILE_ATTRIB_VOLUME_LABEL) 2287: { 1.1.1.17 root 2288: Log_Printf(LOG_WARN, "Warning: Hatari doesn't support GEMDOS volume label setting\n(for '%s')\n", sActualFileName); 1.1.1.12 root 2289: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */ 1.1.1.15 root 2290: return true; 1.1.1.12 root 2291: } 2292: if (stat(sActualFileName, &FileStat) != 0) 2293: { 2294: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */ 1.1.1.15 root 2295: return true; 1.1.1.12 root 2296: } 2297: if (nRwFlag == 0) 2298: { 2299: /* Read attributes */ 2300: Regs[REG_D0] = GemDOS_ConvertAttribute(FileStat.st_mode); 1.1.1.15 root 2301: return true; 1.1.1.12 root 2302: } 1.1.1.17 root 2303: 2304: /* write or auto protected device? */ 2305: if (ConfigureParams.HardDisk.nWriteProtection != WRITEPROT_OFF) 2306: { 2307: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fattrib(\"%s\",...)\n", psFileName); 2308: Regs[REG_D0] = GEMDOS_EWRPRO; 2309: return true; 2310: } 2311: 2312: if (nAttrib & GEMDOS_FILE_ATTRIB_SUBDIRECTORY) 2313: { 2314: if (!S_ISDIR(FileStat.st_mode)) 2315: { 2316: /* file, not dir -> path not found */ 2317: Regs[REG_D0] = GEMDOS_EPTHNF; 2318: return true; 2319: } 2320: } 2321: else 2322: { 2323: if (S_ISDIR(FileStat.st_mode)) 2324: { 2325: /* dir, not file -> file not found */ 2326: Regs[REG_D0] = GEMDOS_EFILNF; 2327: return true; 2328: } 2329: } 2330: 1.1.1.12 root 2331: if (nAttrib & GEMDOS_FILE_ATTRIB_READONLY) 2332: { 2333: /* set read-only (readable by all) */ 2334: if (chmod(sActualFileName, S_IRUSR|S_IRGRP|S_IROTH) == 0) 1.1.1.10 root 2335: { 1.1.1.17 root 2336: Regs[REG_D0] = nAttrib; 1.1.1.15 root 2337: return true; 1.1.1.10 root 2338: } 2339: } 1.1.1.17 root 2340: else 2341: { 2342: /* set writable (by user, readable by all) */ 2343: if (chmod(sActualFileName, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) == 0) 2344: { 2345: Regs[REG_D0] = nAttrib; 2346: return true; 2347: } 2348: } 2349: 2350: /* FIXME: support hidden/system/archive flags? 2351: * System flag is from DOS, not used by TOS. 2352: * Archive bit is cleared by backup programs 2353: * and set whenever file is written to. 2354: */ 1.1.1.12 root 2355: Regs[REG_D0] = GEMDOS_EACCDN; /* Acces denied */ 1.1.1.15 root 2356: return true; 1.1.1.10 root 2357: } 2358: 2359: 1.1.1.2 root 2360: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2361: /** 1.1.1.21! root 2362: * GEMDOS Force (file handle aliasing) ! 2363: * Call 0x46 ! 2364: */ ! 2365: static bool GemDOS_Force(Uint32 Params) ! 2366: { ! 2367: int std, own; ! 2368: ! 2369: /* Read details from stack */ ! 2370: std = STMemory_ReadWord(Params); ! 2371: own = STMemory_ReadWord(Params+SIZE_WORD); ! 2372: ! 2373: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x46 Fforce(%d, %d)\n", std, own); ! 2374: ! 2375: /* Get internal handle */ ! 2376: if (std > own) ! 2377: { ! 2378: int tmp = std; ! 2379: std = own; ! 2380: own = tmp; ! 2381: } ! 2382: if ((own = GemDOS_GetValidFileHandle(own)) < 0) ! 2383: { ! 2384: /* assume it was TOS one -> let TOS handle it */ ! 2385: return false; ! 2386: } ! 2387: if (std < 0 || std >= ARRAYSIZE(ForcedHandles)) ! 2388: { ! 2389: Log_Printf(LOG_WARN, "Warning: forcing of non-standard %d (> %d) handle ignored.\n", std, ARRAYSIZE(ForcedHandles)); ! 2390: return false; ! 2391: } ! 2392: /* mark given standard handle redirected by this process */ ! 2393: ForcedHandles[std].Basepage = STMemory_ReadLong(act_pd); ! 2394: ForcedHandles[std].Handle = own; ! 2395: ! 2396: Regs[REG_D0] = GEMDOS_EOK; ! 2397: return true; ! 2398: } ! 2399: ! 2400: ! 2401: /*-----------------------------------------------------------------------*/ ! 2402: /** 1.1.1.12 root 2403: * GEMDOS Get Directory 2404: * Call 0x47 2405: */ 1.1.1.18 root 2406: static bool GemDOS_GetDir(Uint32 Params) 1.1.1.9 root 2407: { 1.1.1.10 root 2408: Uint32 Address; 2409: Uint16 Drive; 1.1.1.9 root 2410: 1.1.1.17 root 2411: Address = STMemory_ReadLong(Params); 2412: Drive = STMemory_ReadWord(Params+SIZE_LONG); 1.1.1.15 root 2413: 1.1.1.19 root 2414: /* Note: Drive = 0 means current drive, 1 = A:, 2 = B:, 3 = C:, etc. */ 1.1.1.20 root 2415: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x47 Dgetpath(0x%x, %i)\n", Address, (int)Drive); 1.1.1.19 root 2416: if (Drive == 0) 2417: Drive = CurrentDrive; 2418: else 2419: Drive--; 1.1.1.15 root 2420: 1.1.1.9 root 2421: /* is it our drive? */ 1.1.1.19 root 2422: if (GemDOS_IsDriveEmulated(Drive)) 1.1.1.9 root 2423: { 1.1.1.11 root 2424: char path[MAX_GEMDOS_PATH]; 1.1.1.9 root 2425: int i,len,c; 2426: 1.1.1.17 root 2427: *path = '\0'; 2428: strncat(path,&emudrives[Drive-2]->fs_currpath[strlen(emudrives[Drive-2]->hd_emulation_dir)], sizeof(path)-1); 1.1.1.15 root 2429: 2430: // convert it to ST path (DOS) 1.1.1.17 root 2431: File_CleanFileName(path); 2432: len = strlen(path); 1.1.1.18 root 2433: /* Check that write is requested to valid memory area */ 2434: if (!STMemory_ValidArea(Address, len)) 2435: { 2436: Log_Printf(LOG_WARN, "GEMDOS Dgetpath() failed due to invalid RAM range 0x%x+%i\n", Address, len); 2437: Regs[REG_D0] = GEMDOS_ERANGE; 2438: return true; 2439: } 1.1.1.9 root 2440: for (i = 0; i <= len; i++) 2441: { 2442: c = path[i]; 1.1.1.12 root 2443: STMemory_WriteByte(Address+i, (c==PATHSEP ? '\\' : c) ); 1.1.1.9 root 2444: } 1.1.1.18 root 2445: LOG_TRACE(TRACE_OS_GEMDOS, "-> '%s'\n", (char *)STRAM_ADDR(Address)); 1.1.1.15 root 2446: 2447: Regs[REG_D0] = GEMDOS_EOK; /* OK */ 1.1.1.9 root 2448: 1.1.1.15 root 2449: return true; 1.1.1.9 root 2450: } 1.1.1.17 root 2451: /* redirect to TOS */ 2452: return false; 1.1 root 2453: } 2454: 2455: 1.1.1.2 root 2456: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2457: /** 2458: * GEMDOS PExec handler 2459: * Call 0x4B 2460: */ 1.1.1.10 root 2461: static int GemDOS_Pexec(Uint32 Params) 1.1 root 2462: { 1.1.1.17 root 2463: int Drive; 1.1.1.10 root 2464: Uint16 Mode; 1.1.1.17 root 2465: char *pszFileName; 1.1 root 2466: 1.1.1.9 root 2467: /* Find PExec mode */ 1.1.1.17 root 2468: Mode = STMemory_ReadWord(Params); 1.1.1.11 root 2469: 1.1.1.21! root 2470: if (LOG_TRACE_LEVEL(TRACE_OS_GEMDOS)) ! 2471: { ! 2472: Uint32 fname, cmdline, env_string; ! 2473: fname = STMemory_ReadLong(Params+SIZE_WORD); ! 2474: cmdline = STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG); ! 2475: env_string = STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG+SIZE_LONG); ! 2476: if (Mode == 0 || Mode == 3) ! 2477: { ! 2478: int cmdlen; ! 2479: const char *name, *cmd; ! 2480: name = (const char *)STRAM_ADDR(fname); ! 2481: cmd = (const char *)STRAM_ADDR(cmdline); ! 2482: cmdlen = *cmd++; ! 2483: fprintf(TraceFile, "GEMDOS 0x4B Pexec(%i, \"%s\", [%d]\"%s\", 0x%x)\n", Mode, name, cmdlen, cmdlen?cmd:"", env_string); ! 2484: } ! 2485: else ! 2486: { ! 2487: fprintf(TraceFile, "GEMDOS 0x4B Pexec(%i, 0x%x, 0x%x, 0x%x)\n", Mode, fname, cmdline, env_string); ! 2488: } ! 2489: } 1.1.1.15 root 2490: 1.1.1.9 root 2491: /* Re-direct as needed */ 2492: switch(Mode) 2493: { 2494: case 0: /* Load and go */ 2495: case 3: /* Load, don't go */ 1.1.1.17 root 2496: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD)); 1.1.1.19 root 2497: Drive = GemDOS_FileName2HardDriveID(pszFileName); 1.1.1.17 root 2498: 2499: /* If not using A: or B:, use my own routines to load */ 2500: if (ISHARDDRIVE(Drive)) 2501: { 2502: /* Redirect to cart' routine at address 0xFA1000 */ 1.1.1.21! root 2503: PexecCalled = true; 1.1.1.17 root 2504: return CALL_PEXEC_ROUTINE; 2505: } 2506: return false; 1.1.1.9 root 2507: case 4: /* Just go */ 1.1.1.15 root 2508: return false; 1.1.1.9 root 2509: case 5: /* Create basepage */ 1.1.1.15 root 2510: return false; 1.1.1.9 root 2511: case 6: 1.1.1.15 root 2512: return false; 1.1.1.9 root 2513: } 2514: 1.1.1.10 root 2515: /* Default: Still re-direct to TOS */ 1.1.1.15 root 2516: return false; 1.1 root 2517: } 2518: 1.1.1.4 root 2519: 2520: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2521: /** 2522: * GEMDOS Search Next 2523: * Call 0x4F 2524: */ 1.1.1.13 root 2525: static bool GemDOS_SNext(void) 1.1.1.4 root 2526: { 1.1.1.17 root 2527: struct dirent **temp; 2528: Uint32 nDTA; 1.1.1.9 root 2529: int Index; 1.1.1.13 root 2530: int ret; 1.1.1.9 root 2531: 1.1.1.20 root 2532: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x4F Fsnext()\n"); 1.1.1.15 root 2533: 1.1.1.9 root 2534: /* Refresh pDTA pointer (from the current basepage) */ 1.1.1.17 root 2535: nDTA = STMemory_ReadLong(STMemory_ReadLong(act_pd)+32); 2536: 2537: if (!STMemory_ValidArea(nDTA, sizeof(DTA))) 2538: { 2539: pDTA = NULL; 2540: Log_Printf(LOG_WARN, "GEMDOS Fsnext() failed due to invalid DTA address 0x%x\n", nDTA); 2541: Regs[REG_D0] = GEMDOS_EINTRN; /* "internal error */ 2542: return true; 2543: } 2544: pDTA = (DTA *)STRAM_ADDR(nDTA); 1.1.1.4 root 2545: 1.1.1.9 root 2546: /* Was DTA ours or TOS? */ 1.1.1.17 root 2547: if (do_get_mem_long(pDTA->magic) != DTA_MAGIC_NUMBER) 1.1.1.9 root 2548: { 1.1.1.17 root 2549: /* redirect to TOS */ 2550: return false; 2551: } 1.1.1.9 root 2552: 1.1.1.17 root 2553: /* Find index into our list of structures */ 2554: Index = do_get_mem_word(pDTA->index) & (MAX_DTAS_FILES-1); 1.1.1.6 root 2555: 1.1.1.17 root 2556: if (nAttrSFirst == GEMDOS_FILE_ATTRIB_VOLUME_LABEL) 2557: { 2558: /* Volume label was given already in Sfirst() */ 2559: Regs[REG_D0] = GEMDOS_ENMFIL; 2560: return true; 2561: } 2562: 2563: temp = InternalDTAs[Index].found; 2564: do 2565: { 2566: if (InternalDTAs[Index].centry >= InternalDTAs[Index].nentries) 1.1.1.9 root 2567: { 2568: Regs[REG_D0] = GEMDOS_ENMFIL; /* No more files */ 1.1.1.15 root 2569: return true; 1.1.1.9 root 2570: } 2571: 1.1.1.17 root 2572: ret = PopulateDTA(InternalDTAs[Index].path, 2573: temp[InternalDTAs[Index].centry++]); 2574: } while (ret == 1); 1.1.1.4 root 2575: 1.1.1.17 root 2576: if (ret < 0) 2577: { 2578: Log_Printf(LOG_WARN, "GEMDOS Fsnext(): Error setting DTA.\n"); 2579: Regs[REG_D0] = GEMDOS_EINTRN; 1.1.1.15 root 2580: return true; 1.1.1.9 root 2581: } 1.1.1.4 root 2582: 1.1.1.17 root 2583: Regs[REG_D0] = GEMDOS_EOK; 2584: return true; 1.1.1.4 root 2585: } 2586: 2587: 1.1.1.2 root 2588: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2589: /** 2590: * GEMDOS Find first file 2591: * Call 0x4E 2592: */ 1.1.1.13 root 2593: static bool GemDOS_SFirst(Uint32 Params) 1.1 root 2594: { 1.1.1.17 root 2595: /* TODO: host filenames might not fit into this */ 1.1.1.11 root 2596: char szActualFileName[MAX_GEMDOS_PATH]; 1.1.1.9 root 2597: char *pszFileName; 1.1.1.17 root 2598: const char *dirmask; 1.1.1.9 root 2599: struct dirent **files; 1.1.1.17 root 2600: Uint32 nDTA; 1.1.1.9 root 2601: int Drive; 2602: DIR *fsdir; 2603: int i,j,count; 2604: 2605: /* Find filename to search for */ 1.1.1.17 root 2606: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params)); 2607: nAttrSFirst = STMemory_ReadWord(Params+SIZE_LONG); 1.1.1.9 root 2608: 1.1.1.20 root 2609: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x4E Fsfirst(\"%s\", 0x%x)\n", pszFileName, nAttrSFirst); 1.1.1.15 root 2610: 1.1.1.19 root 2611: Drive = GemDOS_FileName2HardDriveID(pszFileName); 1.1.1.17 root 2612: if (!ISHARDDRIVE(Drive)) 1.1.1.9 root 2613: { 1.1.1.17 root 2614: /* redirect to TOS */ 2615: return false; 2616: } 1.1.1.9 root 2617: 1.1.1.17 root 2618: /* Convert to hard drive filename */ 2619: GemDOS_CreateHardDriveFileName(Drive, pszFileName, 1.1.1.12 root 2620: szActualFileName, sizeof(szActualFileName)); 1.1.1.9 root 2621: 1.1.1.17 root 2622: /* Refresh pDTA pointer (from the current basepage) */ 2623: nDTA = STMemory_ReadLong(STMemory_ReadLong(act_pd)+32); 1.1.1.9 root 2624: 1.1.1.17 root 2625: if (!STMemory_ValidArea(nDTA, sizeof(DTA))) 2626: { 2627: pDTA = NULL; 2628: Log_Printf(LOG_WARN, "GEMDOS Fsfirst() failed due to invalid DTA address 0x%x\n", nDTA); 2629: Regs[REG_D0] = GEMDOS_EINTRN; /* "internal error */ 2630: return true; 2631: } 2632: pDTA = (DTA *)STRAM_ADDR(nDTA); 1.1.1.9 root 2633: 1.1.1.17 root 2634: /* Populate DTA, set index for our use */ 2635: do_put_mem_word(pDTA->index, DTAIndex); 2636: /* set our dta magic num */ 2637: do_put_mem_long(pDTA->magic, DTA_MAGIC_NUMBER); 1.1.1.9 root 2638: 1.1.1.17 root 2639: if (InternalDTAs[DTAIndex].bUsed == true) 2640: ClearInternalDTA(); 2641: InternalDTAs[DTAIndex].bUsed = true; 2642: 2643: /* Were we looking for the volume label? */ 2644: if (nAttrSFirst == GEMDOS_FILE_ATTRIB_VOLUME_LABEL) 2645: { 2646: /* Volume name */ 2647: strcpy(pDTA->dta_name,"EMULATED.001"); 1.1.1.19 root 2648: pDTA->dta_name[11] = '0' + Drive; 1.1.1.17 root 2649: Regs[REG_D0] = GEMDOS_EOK; /* Got volume */ 2650: return true; 2651: } 1.1.1.9 root 2652: 1.1.1.17 root 2653: /* open directory 2654: * TODO: host path may not fit into InternalDTA 2655: */ 2656: fsfirst_dirname(szActualFileName, InternalDTAs[DTAIndex].path); 2657: fsdir = opendir(InternalDTAs[DTAIndex].path); 2658: 2659: if (fsdir == NULL) 2660: { 2661: Regs[REG_D0] = GEMDOS_EPTHNF; /* Path not found */ 2662: return true; 2663: } 2664: /* close directory */ 2665: closedir(fsdir); 2666: 2667: count = scandir(InternalDTAs[DTAIndex].path, &files, 0, alphasort); 2668: /* File (directory actually) not found */ 2669: if (count < 0) 2670: { 2671: Regs[REG_D0] = GEMDOS_EFILNF; 2672: return true; 2673: } 2674: 2675: InternalDTAs[DTAIndex].centry = 0; /* current entry is 0 */ 2676: dirmask = fsfirst_dirmask(szActualFileName);/* directory mask part */ 2677: InternalDTAs[DTAIndex].found = files; /* get files */ 2678: 2679: /* count & copy the entries that match our mask and discard the rest */ 2680: j = 0; 2681: for (i=0; i < count; i++) 2682: { 2683: if (fsfirst_match(dirmask, files[i]->d_name)) 1.1.1.13 root 2684: { 1.1.1.17 root 2685: InternalDTAs[DTAIndex].found[j] = files[i]; 2686: j++; 1.1.1.13 root 2687: } 1.1.1.17 root 2688: else 1.1.1.9 root 2689: { 1.1.1.17 root 2690: free(files[i]); 2691: files[i] = NULL; 1.1.1.9 root 2692: } 1.1.1.17 root 2693: } 2694: InternalDTAs[DTAIndex].nentries = j; /* set number of legal entries */ 1.1.1.9 root 2695: 1.1.1.17 root 2696: /* No files of that match, return error code */ 2697: if (j==0) 2698: { 2699: free(files); 2700: InternalDTAs[DTAIndex].found = NULL; 2701: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */ 1.1.1.15 root 2702: return true; 1.1.1.9 root 2703: } 1.1.1.17 root 2704: 2705: /* Scan for first file (SNext uses no parameters) */ 2706: GemDOS_SNext(); 2707: /* increment DTA index */ 2708: DTAIndex++; 1.1.1.21! root 2709: DTAIndex &= (MAX_DTAS_FILES-1); ! 2710: 1.1.1.17 root 2711: return true; 1.1 root 2712: } 2713: 2714: 1.1.1.2 root 2715: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 2716: /** 2717: * GEMDOS Rename 2718: * Call 0x56 2719: */ 1.1.1.13 root 2720: static bool GemDOS_Rename(Uint32 Params) 1.1 root 2721: { 1.1.1.9 root 2722: char *pszNewFileName,*pszOldFileName; 1.1.1.17 root 2723: /* TODO: host filenames might not fit into this */ 2724: char szNewActualFileName[MAX_GEMDOS_PATH]; 2725: char szOldActualFileName[MAX_GEMDOS_PATH]; 1.1.1.9 root 2726: int NewDrive, OldDrive; 2727: 1.1.1.17 root 2728: /* Read details from stack, skip first (dummy) arg */ 2729: pszOldFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD)); 2730: pszNewFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG)); 1.1.1.9 root 2731: 1.1.1.20 root 2732: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x56 Frename(\"%s\", \"%s\")\n", pszOldFileName, pszNewFileName); 1.1.1.15 root 2733: 1.1.1.19 root 2734: NewDrive = GemDOS_FileName2HardDriveID(pszNewFileName); 2735: OldDrive = GemDOS_FileName2HardDriveID(pszOldFileName); 1.1.1.17 root 2736: if (!(ISHARDDRIVE(NewDrive) && ISHARDDRIVE(OldDrive))) 1.1.1.9 root 2737: { 1.1.1.17 root 2738: /* redirect to TOS */ 2739: return false; 2740: } 1.1.1.9 root 2741: 1.1.1.17 root 2742: /* write protected device? */ 2743: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 2744: { 2745: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Frename(\"%s\", \"%s\")\n", pszOldFileName, pszNewFileName); 2746: Regs[REG_D0] = GEMDOS_EWRPRO; 1.1.1.15 root 2747: return true; 1.1.1.9 root 2748: } 1.1 root 2749: 1.1.1.17 root 2750: /* And convert to hard drive filenames */ 2751: GemDOS_CreateHardDriveFileName(NewDrive, pszNewFileName, 2752: szNewActualFileName, sizeof(szNewActualFileName)); 2753: GemDOS_CreateHardDriveFileName(OldDrive, pszOldFileName, 2754: szOldActualFileName, sizeof(szOldActualFileName)); 2755: 2756: /* Rename files */ 2757: if ( rename(szOldActualFileName,szNewActualFileName)==0 ) 2758: Regs[REG_D0] = GEMDOS_EOK; 2759: else 2760: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */ 2761: return true; 1.1 root 2762: } 2763: 1.1.1.15 root 2764: 1.1.1.18 root 2765: /*-----------------------------------------------------------------------*/ 2766: /** 2767: * GEMDOS GSDToF 2768: * Call 0x57 2769: */ 2770: static bool GemDOS_GSDToF(Uint32 Params) 2771: { 2772: DATETIME DateTime; 2773: Uint32 pBuffer; 2774: int Handle,Flag; 2775: 2776: /* Read details from stack */ 2777: pBuffer = STMemory_ReadLong(Params); 2778: Handle = STMemory_ReadWord(Params+SIZE_LONG); 2779: Flag = STMemory_ReadWord(Params+SIZE_LONG+SIZE_WORD); 2780: 1.1.1.20 root 2781: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x57 Fdatime(0x%x, %i, %i)\n", pBuffer, 1.1.1.18 root 2782: Handle, Flag); 2783: 1.1.1.21! root 2784: /* get internal handle */ ! 2785: if ((Handle = GemDOS_GetValidFileHandle(Handle)) < 0) 1.1.1.18 root 2786: { 2787: /* No, assume was TOS -> redirect */ 2788: return false; 2789: } 2790: 2791: if (Flag == 1) 2792: { 2793: /* write protected device? */ 2794: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON) 2795: { 2796: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fdatime(,%d,)\n", Handle); 2797: Regs[REG_D0] = GEMDOS_EWRPRO; 2798: return true; 2799: } 2800: DateTime.timeword = STMemory_ReadWord(pBuffer); 2801: DateTime.dateword = STMemory_ReadWord(pBuffer+SIZE_WORD); 2802: if (GemDOS_SetFileInformation(Handle, &DateTime) == true) 2803: Regs[REG_D0] = GEMDOS_EOK; 2804: else 2805: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */ 2806: return true; 2807: } 2808: 2809: if (GemDOS_GetFileInformation(Handle, &DateTime) == true) 2810: { 2811: /* Check that write is requested to valid memory area */ 2812: if (STMemory_ValidArea(pBuffer, 4)) 2813: { 2814: STMemory_WriteWord(pBuffer, DateTime.timeword); 2815: STMemory_WriteWord(pBuffer+SIZE_WORD, DateTime.dateword); 2816: Regs[REG_D0] = GEMDOS_EOK; 2817: } 2818: else 2819: { 2820: Log_Printf(LOG_WARN, "GEMDOS Fdatime() failed due to invalid RAM range 0x%x+%i\n", pBuffer, 4); 2821: Regs[REG_D0] = GEMDOS_ERANGE; 2822: } 2823: } 2824: else 2825: { 2826: Regs[REG_D0] = GEMDOS_ERROR; /* Generic error */ 2827: } 2828: return true; 2829: } 2830: 2831: 1.1.1.21! root 2832: /*-----------------------------------------------------------------------*/ ! 2833: /** ! 2834: * Do implicit file handle closing/unforcing on program termination ! 2835: */ ! 2836: static void GemDOS_TerminateClose(void) ! 2837: { ! 2838: int i, closed, unforced; ! 2839: Uint32 current = STMemory_ReadLong(act_pd); ! 2840: ! 2841: closed = 0; ! 2842: for (i = 0; i < ARRAYSIZE(FileHandles); i++) ! 2843: { ! 2844: if (FileHandles[i].Basepage == current) ! 2845: { ! 2846: GemDOS_CloseFileHandle(i); ! 2847: closed++; ! 2848: } ! 2849: } ! 2850: unforced = 0; ! 2851: for (i = 0; i < ARRAYSIZE(ForcedHandles); i++) ! 2852: { ! 2853: if (ForcedHandles[i].Basepage == current) ! 2854: { ! 2855: GemDOS_UnforceFileHandle(i); ! 2856: unforced++; ! 2857: } ! 2858: } ! 2859: if (!(closed || unforced)) ! 2860: return; ! 2861: Log_Printf(LOG_WARN, "Closing %d & unforcing %d file handle(s) remaining at program 0x%x exit.\n", ! 2862: closed, unforced, current); ! 2863: } ! 2864: ! 2865: /** ! 2866: * GEMDOS Pterm0 ! 2867: * Call 0x00 ! 2868: */ ! 2869: static bool GemDOS_Pterm0(Uint32 Params) ! 2870: { ! 2871: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x00 Pterm0()\n"); ! 2872: GemDOS_TerminateClose(); ! 2873: GemDOS_RemoveLastProgram(); ! 2874: return false; ! 2875: } ! 2876: ! 2877: /** ! 2878: * GEMDOS Ptermres ! 2879: * Call 0x31 ! 2880: */ ! 2881: static bool GemDOS_Ptermres(Uint32 Params) ! 2882: { ! 2883: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x31 Ptermres(0x%X, %d)\n", ! 2884: STMemory_ReadLong(Params), STMemory_ReadWord(Params+SIZE_WORD)); ! 2885: GemDOS_TerminateClose(); ! 2886: return false; ! 2887: } ! 2888: ! 2889: /** ! 2890: * GEMDOS Pterm ! 2891: * Call 0x4c ! 2892: */ ! 2893: static bool GemDOS_Pterm(Uint32 Params) ! 2894: { ! 2895: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x4C Pterm(%d)\n", ! 2896: STMemory_ReadWord(Params)); ! 2897: GemDOS_TerminateClose(); ! 2898: GemDOS_RemoveLastProgram(); ! 2899: return false; ! 2900: } ! 2901: ! 2902: 1.1.1.17 root 2903: #if ENABLE_TRACING 2904: /*-----------------------------------------------------------------------*/ 2905: /** 2906: * Map GEMDOS call opcodes to their names 1.1.1.21! root 2907: * ! 2908: * Mapping is based on TOSHYP information: ! 2909: * http://toshyp.atari.org/en/005013.html 1.1.1.17 root 2910: */ 2911: static const char* GemDOS_Opcode2Name(Uint16 opcode) 2912: { 2913: static const char* names[] = { 2914: "Pterm0", 2915: "Cconin", 2916: "Cconout", 2917: "Cauxin", 2918: "Cauxout", 2919: "Cprnout", 2920: "Crawio", 2921: "Crawcin", 2922: "Cnecin", 2923: "Cconws", 2924: "Cconrs", 2925: "Cconis", 1.1.1.18 root 2926: "-", /* 0C */ 2927: "-", /* 0D */ 1.1.1.17 root 2928: "Dsetdrv", 1.1.1.18 root 2929: "-", /* 0F */ 1.1.1.17 root 2930: "Cconos", 2931: "Cprnos", 2932: "Cauxis", 2933: "Cauxos", 2934: "Maddalt", 1.1.1.21! root 2935: "Srealloc", /* TOS4 */ 1.1.1.18 root 2936: "-", /* 16 */ 2937: "-", /* 17 */ 2938: "-", /* 18 */ 1.1.1.17 root 2939: "Dgetdrv", 2940: "Fsetdta", 1.1.1.18 root 2941: "-", /* 1B */ 2942: "-", /* 1C */ 2943: "-", /* 1D */ 2944: "-", /* 1E */ 2945: "-", /* 1F */ 1.1.1.17 root 2946: "Super", 1.1.1.18 root 2947: "-", /* 21 */ 2948: "-", /* 22 */ 2949: "-", /* 23 */ 2950: "-", /* 24 */ 2951: "-", /* 25 */ 2952: "-", /* 26 */ 2953: "-", /* 27 */ 2954: "-", /* 28 */ 2955: "-", /* 29 */ 1.1.1.17 root 2956: "Tgetdate", 2957: "Tsetdate", 2958: "Tgettime", 2959: "Tsettime", 1.1.1.18 root 2960: "-", /* 2E */ 1.1.1.17 root 2961: "Fgetdta", 2962: "Sversion", 2963: "Ptermres", 1.1.1.18 root 2964: "-", /* 32 */ 2965: "-", /* 33 */ 2966: "-", /* 34 */ 2967: "-", /* 35 */ 1.1.1.17 root 2968: "Dfree", 1.1.1.18 root 2969: "-", /* 37 */ 2970: "-", /* 38 */ 1.1.1.17 root 2971: "Dcreate", 2972: "Ddelete", 2973: "Dsetpath", 2974: "Fcreate", 2975: "Fopen", 2976: "Fclose", 2977: "Fread", 2978: "Fwrite", 2979: "Fdelete", 2980: "Fseek", 2981: "Fattrib", 2982: "Mxalloc", 2983: "Fdup", 2984: "Fforce", 2985: "Dgetpath", 2986: "Malloc", 2987: "Mfree", 2988: "Mshrink", 2989: "Pexec", 2990: "Pterm", 1.1.1.18 root 2991: "-", /* 4D */ 1.1.1.17 root 2992: "Fsfirst", 2993: "Fsnext", 1.1.1.18 root 2994: "-", /* 50 */ 2995: "-", /* 51 */ 2996: "-", /* 52 */ 2997: "-", /* 53 */ 2998: "-", /* 54 */ 2999: "-", /* 55 */ 1.1.1.17 root 3000: "Frename", 3001: "Fdatime" 3002: }; 3003: if (opcode < ARRAYSIZE(names)) 3004: return names[opcode]; 1.1.1.18 root 3005: return "MiNT?"; 1.1.1.17 root 3006: } 3007: 1.1.1.12 root 3008: /** 1.1.1.18 root 3009: * If bShowOpcodes is true, show GEMDOS call opcode/function name table, 3010: * otherwise GEMDOS HDD emulation information. 1.1.1.12 root 3011: */ 1.1.1.18 root 3012: void GemDOS_Info(Uint32 bShowOpcodes) 1.1 root 3013: { 1.1.1.18 root 3014: int i, used; 1.1.1.15 root 3015: 1.1.1.18 root 3016: if (bShowOpcodes) 3017: { 3018: Uint16 opcode; 3019: for (opcode = 0; opcode < 0x5A; ) 3020: { 3021: fprintf(stderr, "%02x %-9s", 3022: opcode, GemDOS_Opcode2Name(opcode)); 3023: if (++opcode % 6 == 0) 3024: fputs("\n", stderr); 3025: } 3026: return; 3027: } 1.1.1.17 root 3028: 1.1.1.18 root 3029: if (!GEMDOS_EMU_ON) 1.1.1.9 root 3030: { 1.1.1.18 root 3031: fputs("GEMDOS HDD emulation isn't enabled!\n", stderr); 3032: return; 1.1.1.9 root 3033: } 3034: 1.1.1.21! root 3035: /* GEMDOS vector set by Hatari can be overwritten e.g. MiNT */ ! 3036: fprintf(stderr, "Current GEMDOS handler: (0x84) = 0x%x, emu one = 0x%x\n", STMemory_ReadLong(0x0084), CART_GEMDOS); ! 3037: fprintf(stderr, "Stored GEMDOS handler: (0x%x) = 0x%x\n\n", CART_OLDGEMDOS, STMemory_ReadLong(CART_OLDGEMDOS)); ! 3038: 1.1.1.20 root 3039: fprintf(stderr, "Connected drives mask: 0x%x\n\n", ConnectedDriveMask); 1.1.1.18 root 3040: fputs("GEMDOS HDD emulation drives:\n", stderr); 1.1.1.21! root 3041: for(i = 0; i < MAX_HARDDRIVES; i++) 1.1.1.9 root 3042: { 1.1.1.18 root 3043: if (!emudrives[i]) 3044: continue; 3045: fprintf(stderr, "- %c: %s\n", 3046: 'A' + emudrives[i]->drive_number, 3047: emudrives[i]->hd_emulation_dir); 1.1.1.9 root 3048: } 3049: 1.1.1.18 root 3050: fputs("\nInternal Fsfirst() DTAs:\n", stderr); 1.1.1.21! root 3051: for(used = i = 0; i < ARRAYSIZE(InternalDTAs); i++) 1.1.1.9 root 3052: { 1.1.1.18 root 3053: int j, centry, entries; 3054: 3055: if (!InternalDTAs[i].bUsed) 3056: continue; 3057: 3058: fprintf(stderr, "+ %d: %s\n", i, InternalDTAs[i].path); 3059: 3060: centry = InternalDTAs[i].centry; 3061: entries = InternalDTAs[i].nentries; 3062: for (j = 0; j < entries; j++) 3063: { 3064: fprintf(stderr, " - %d: %s%s\n", 3065: j, InternalDTAs[i].found[j]->d_name, 3066: j == centry ? " *" : ""); 3067: } 3068: fprintf(stderr, " Fsnext entry = %d.\n", centry); 3069: used++; 1.1.1.9 root 3070: } 1.1.1.18 root 3071: if (!used) 3072: fputs("- None in use.\n", stderr); 3073: 3074: fputs("\nOpen GEMDOS HDD file handles:\n", stderr); 1.1.1.21! root 3075: for (used = i = 0; i < ARRAYSIZE(FileHandles); i++) 1.1.1.17 root 3076: { 1.1.1.18 root 3077: if (!FileHandles[i].bUsed) 3078: continue; 1.1.1.21! root 3079: fprintf(stderr, "- %d (0x%x): %s\n", i + BASE_FILEHANDLE, ! 3080: FileHandles[i].Basepage, FileHandles[i].szActualName); ! 3081: used++; ! 3082: } ! 3083: if (!used) ! 3084: fputs("- None.\n", stderr); ! 3085: fputs("\nForced GEMDOS HDD file handles:\n", stderr); ! 3086: for (used = i = 0; i < ARRAYSIZE(ForcedHandles); i++) ! 3087: { ! 3088: if (ForcedHandles[i].Handle == UNFORCED_HANDLE) ! 3089: continue; ! 3090: fprintf(stderr, "- %d -> %d (0x%x)\n", i, ! 3091: ForcedHandles[i].Handle + BASE_FILEHANDLE, ! 3092: ForcedHandles[i].Basepage); 1.1.1.18 root 3093: used++; 1.1.1.17 root 3094: } 1.1.1.18 root 3095: if (!used) 3096: fputs("- None.\n", stderr); 3097: } 3098: 3099: #else /* !ENABLE_TRACING */ 3100: void GemDOS_Info(Uint32 bShowOpcodes) 3101: { 3102: fputs("Hatari isn't configured with ENABLE_TRACING\n", stderr); 1.1 root 3103: } 1.1.1.18 root 3104: #endif /* !ENABLE_TRACING */ 1.1 root 3105: 1.1.1.9 root 3106: 1.1.1.12 root 3107: /** 3108: * Run GEMDos call, and re-direct if need to. Used to handle hard disk emulation etc... 3109: * This sets the condition codes (in SR), which are used in the 'cart_asm.s' program to 3110: * decide if we need to run old GEM vector, or PExec or nothing. 1.1.1.15 root 3111: * 1.1.1.21! root 3112: * This method keeps the stack and other states consistent with the original ST ! 3113: * which is very important for the PExec call and maximum compatibility through-out 1.1.1.12 root 3114: */ 1.1 root 3115: void GemDOS_OpCode(void) 3116: { 1.1.1.17 root 3117: Uint16 GemDOSCall, CallingSReg; 1.1.1.10 root 3118: Uint32 Params; 1.1.1.18 root 3119: int Finished; 1.1.1.12 root 3120: Uint16 SR; 1.1.1.9 root 3121: 1.1.1.12 root 3122: SR = M68000_GetSR(); 1.1.1.9 root 3123: 3124: /* Read SReg from stack to see if parameters are on User or Super stack */ 3125: CallingSReg = STMemory_ReadWord(Regs[REG_A7]); 3126: if ((CallingSReg&SR_SUPERMODE)==0) /* Calling from user mode */ 3127: Params = regs.usp; 3128: else 3129: { 1.1.1.21! root 3130: Params = Regs[REG_A7]+SIZE_WORD+SIZE_LONG; /* skip SR & PC pushed to super stack */ 1.1.1.12 root 3131: if (currprefs.cpu_level > 0) 1.1.1.9 root 3132: Params += SIZE_WORD; /* Skip extra word whe CPU is >=68010 */ 3133: } 3134: 3135: /* Default to run TOS GemDos (SR_NEG run Gemdos, SR_ZERO already done, SR_OVERFLOW run own 'Pexec' */ 1.1.1.18 root 3136: Finished = false; 1.1.1.9 root 3137: SR &= SR_CLEAR_OVERFLOW; 3138: SR &= SR_CLEAR_ZERO; 3139: SR |= SR_NEG; 1.1 root 3140: 1.1.1.9 root 3141: /* Find pointer to call parameters */ 3142: GemDOSCall = STMemory_ReadWord(Params); 1.1.1.17 root 3143: Params += SIZE_WORD; 1.1.1.2 root 3144: 1.1.1.9 root 3145: /* Intercept call */ 3146: switch(GemDOSCall) 3147: { 1.1.1.21! root 3148: case 0x00: ! 3149: Finished = GemDOS_Pterm0(Params); ! 3150: break; 1.1.1.20 root 3151: case 0x0e: 1.1.1.18 root 3152: Finished = GemDOS_SetDrv(Params); 1.1.1.9 root 3153: break; 3154: case 0x1a: 1.1.1.18 root 3155: Finished = GemDOS_SetDTA(Params); 1.1.1.9 root 3156: break; 1.1.1.21! root 3157: case 0x31: ! 3158: Finished = GemDOS_Ptermres(Params); ! 3159: break; 1.1.1.9 root 3160: case 0x36: 1.1.1.18 root 3161: Finished = GemDOS_DFree(Params); 1.1.1.9 root 3162: break; 3163: case 0x39: 1.1.1.18 root 3164: Finished = GemDOS_MkDir(Params); 1.1.1.9 root 3165: break; 3166: case 0x3a: 1.1.1.18 root 3167: Finished = GemDOS_RmDir(Params); 1.1.1.9 root 3168: break; 1.1.1.21! root 3169: case 0x3b: /* Dsetpath */ 1.1.1.18 root 3170: Finished = GemDOS_ChDir(Params); 1.1.1.9 root 3171: break; 3172: case 0x3c: 1.1.1.18 root 3173: Finished = GemDOS_Create(Params); 1.1.1.9 root 3174: break; 3175: case 0x3d: 1.1.1.18 root 3176: Finished = GemDOS_Open(Params); 1.1.1.9 root 3177: break; 3178: case 0x3e: 1.1.1.18 root 3179: Finished = GemDOS_Close(Params); 1.1.1.9 root 3180: break; 3181: case 0x3f: 1.1.1.18 root 3182: Finished = GemDOS_Read(Params); 1.1.1.9 root 3183: break; 3184: case 0x40: 1.1.1.18 root 3185: Finished = GemDOS_Write(Params); 1.1.1.9 root 3186: break; 3187: case 0x41: 1.1.1.18 root 3188: Finished = GemDOS_FDelete(Params); 1.1.1.9 root 3189: break; 3190: case 0x42: 1.1.1.18 root 3191: Finished = GemDOS_LSeek(Params); 1.1.1.9 root 3192: break; 1.1.1.10 root 3193: case 0x43: 1.1.1.18 root 3194: Finished = GemDOS_Fattrib(Params); 1.1.1.10 root 3195: break; 1.1.1.21! root 3196: case 0x46: ! 3197: Finished = GemDOS_Force(Params); ! 3198: break; ! 3199: case 0x47: /* Dgetpath */ 1.1.1.18 root 3200: Finished = GemDOS_GetDir(Params); 1.1.1.9 root 3201: break; 3202: case 0x4b: 1.1.1.18 root 3203: /* Either false or CALL_PEXEC_ROUTINE */ 3204: Finished = GemDOS_Pexec(Params); 1.1.1.9 root 3205: break; 1.1.1.21! root 3206: case 0x4c: ! 3207: Finished = GemDOS_Pterm(Params); ! 3208: break; 1.1.1.9 root 3209: case 0x4e: 1.1.1.18 root 3210: Finished = GemDOS_SFirst(Params); 1.1.1.9 root 3211: break; 3212: case 0x4f: 1.1.1.18 root 3213: Finished = GemDOS_SNext(); 1.1.1.9 root 3214: break; 3215: case 0x56: 1.1.1.18 root 3216: Finished = GemDOS_Rename(Params); 1.1.1.9 root 3217: break; 3218: case 0x57: 1.1.1.18 root 3219: Finished = GemDOS_GSDToF(Params); 1.1.1.9 root 3220: break; 1.1.1.20 root 3221: 1.1.1.21! root 3222: /* print args for other calls */ ! 3223: 1.1.1.20 root 3224: case 0x01: /* Conin */ 3225: case 0x03: /* Cauxin */ 3226: case 0x12: /* Cauxis */ 3227: case 0x13: /* Cauxos */ 3228: case 0x0B: /* Conis */ 3229: case 0x10: /* Conos */ 3230: case 0x08: /* Cnecin */ 3231: case 0x11: /* Cprnos */ 3232: case 0x07: /* Crawcin */ 3233: case 0x19: /* Dgetdrv */ 3234: case 0x2F: /* Fgetdta */ 3235: case 0x30: /* Sversion */ 3236: case 0x2A: /* Tgetdate */ 3237: case 0x2C: /* Tgettime */ 3238: /* commands with no args */ 3239: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x%2hX %s()\n", 3240: GemDOSCall, GemDOS_Opcode2Name(GemDOSCall)); 3241: break; 3242: 3243: case 0x02: /* Cconout */ 3244: case 0x04: /* Cauxout */ 3245: case 0x05: /* Cprnout */ 3246: case 0x06: /* Crawio */ 3247: case 0x2b: /* Tsetdate */ 3248: case 0x2d: /* Tsettime */ 3249: case 0x45: /* Fdup */ 3250: /* commands taking single word */ 3251: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x%2hX %s(0x%hX)\n", 3252: GemDOSCall, GemDOS_Opcode2Name(GemDOSCall), 3253: STMemory_ReadWord(Params)); 3254: break; 3255: 3256: case 0x09: /* Cconws */ 3257: case 0x0A: /* Cconrs */ 3258: case 0x20: /* Super */ 3259: case 0x48: /* Malloc */ 3260: case 0x49: /* Mfree */ 1.1.1.21! root 3261: /* commands taking long/pointer */ 1.1.1.20 root 3262: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x%2hX %s(0x%X)\n", 3263: GemDOSCall, GemDOS_Opcode2Name(GemDOSCall), 3264: STMemory_ReadLong(Params)); 3265: break; 3266: 1.1.1.21! root 3267: case 0x44: /* Mxalloc */ ! 3268: /* commands taking long/pointer + word */ ! 3269: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x44 Mxalloc(0x%X, 0x%hX)\n", ! 3270: STMemory_ReadLong(Params), ! 3271: STMemory_ReadWord(Params+SIZE_LONG)); ! 3272: break; ! 3273: case 0x14: /* Maddalt */ ! 3274: /* commands taking 2 longs/pointers */ ! 3275: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x14 Maddalt(0x%X, 0x%X)\n", ! 3276: STMemory_ReadLong(Params), ! 3277: STMemory_ReadLong(Params+SIZE_LONG)); ! 3278: case 0x4A: /* Mshrink */ ! 3279: /* Mshrink's two pointers are prefixed by reserved zero word: ! 3280: * http://toshyp.atari.org/en/00500c.html#Bindings_20for_20Mshrink ! 3281: */ ! 3282: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x4A Mshrink(0x%X, 0x%X)\n", ! 3283: STMemory_ReadLong(Params+SIZE_WORD), ! 3284: STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG)); ! 3285: break; ! 3286: 1.1.1.20 root 3287: default: 3288: /* rest of commands */ 3289: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS 0x%2hX (%s)\n", 1.1.1.17 root 3290: GemDOSCall, GemDOS_Opcode2Name(GemDOSCall)); 1.1.1.9 root 3291: } 1.1.1.15 root 3292: 1.1.1.18 root 3293: switch(Finished) 1.1.1.9 root 3294: { 1.1.1.18 root 3295: case true: 1.1.1.13 root 3296: /* skip over branch to pexec to RTE */ 1.1.1.9 root 3297: SR |= SR_ZERO; 1.1.1.13 root 3298: /* visualize GemDOS emu HD access? */ 3299: switch (GemDOSCall) 3300: { 3301: case 0x36: 3302: case 0x39: 3303: case 0x3a: 3304: case 0x3b: 3305: case 0x3c: 3306: case 0x3d: 3307: case 0x3e: 3308: case 0x3f: 3309: case 0x40: 3310: case 0x41: 3311: case 0x42: 3312: case 0x43: 3313: case 0x47: 3314: case 0x4e: 3315: case 0x4f: 3316: case 0x56: 1.1.1.21! root 3317: Statusbar_EnableHDLed( LED_STATE_ON ); 1.1.1.13 root 3318: } 1.1.1.9 root 3319: break; 1.1.1.13 root 3320: case CALL_PEXEC_ROUTINE: 3321: /* branch to pexec, then redirect to old gemdos. */ 1.1.1.9 root 3322: SR |= SR_OVERFLOW; 3323: break; 3324: } 1.1 root 3325: 1.1.1.12 root 3326: M68000_SetSR(SR); /* update the flags in the SR register */ 1.1 root 3327: } 3328: 1.1.1.9 root 3329: 1.1.1.2 root 3330: /*-----------------------------------------------------------------------*/ 1.1.1.12 root 3331: /** 1.1.1.21! root 3332: * GemDOS_Boot - routine called on the first occurrence of the gemdos opcode. 1.1.1.12 root 3333: * (this should be in the cartridge bootrom) 3334: * Sets up our gemdos handler (or, if we don't need one, just turn off keyclicks) 1.1.1.2 root 3335: */ 1.1.1.7 root 3336: void GemDOS_Boot(void) 1.1.1.2 root 3337: { 1.1.1.15 root 3338: bInitGemDOS = true; 1.1.1.2 root 3339: 1.1.1.15 root 3340: LOG_TRACE(TRACE_OS_GEMDOS, "Gemdos_Boot()\n" ); 1.1.1.9 root 3341: 3342: /* install our gemdos handler, if -e or --harddrive option used */ 1.1.1.17 root 3343: if (!GEMDOS_EMU_ON) 3344: return; 3345: 3346: /* Get the address of the p_run variable that points to the actual basepage */ 3347: if (TosVersion == 0x100) 1.1.1.9 root 3348: { 1.1.1.17 root 3349: /* We have to use fix addresses on TOS 1.00 :-( */ 3350: if ((STMemory_ReadWord(TosAddress+28)>>1) == 4) 3351: act_pd = 0x873c; /* Spanish TOS is different from others! */ 1.1.1.9 root 3352: else 1.1.1.17 root 3353: act_pd = 0x602c; 1.1.1.9 root 3354: } 1.1.1.17 root 3355: else 3356: { 3357: act_pd = STMemory_ReadLong(TosAddress + 0x28); 3358: } 3359: 1.1.1.21! root 3360: /* Save old GEMDOS handler address */ 1.1.1.17 root 3361: STMemory_WriteLong(CART_OLDGEMDOS, STMemory_ReadLong(0x0084)); 3362: /* Setup new GEMDOS handler, see "cart_asm.s" */ 3363: STMemory_WriteLong(0x0084, CART_GEMDOS); 1.1 root 3364: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.