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