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