|
|
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...)
802: * returns the current drive number if none is specified.
803: */
1.1.1.7 root 804: static int GemDOS_FindDriveNumber(char *pszFileName)
1.1 root 805: {
1.1.1.9 root 806: /* Does have 'A:' or 'C:' etc.. at start of string? */
807: if ((pszFileName[0] != '\0') && (pszFileName[1] == ':'))
808: {
809: if ((pszFileName[0] >= 'a') && (pszFileName[0] <= 'z'))
810: return (pszFileName[0]-'a');
811: else if ((pszFileName[0] >= 'A') && (pszFileName[0] <= 'Z'))
812: return (pszFileName[0]-'A');
813: }
1.1 root 814:
1.1.1.9 root 815: return CurrentDrive;
1.1 root 816: }
817:
1.1.1.19! root 818:
! 819: /**
! 820: * Return true if drive ID (C:2, D:3 etc...) matches emulated hard-drive
! 821: */
! 822: static bool GemDOS_IsDriveEmulated(int drive)
! 823: {
! 824: drive -= 2;
! 825: if (drive < 0 || drive >= MAX_HARDDRIVES)
! 826: return false;
! 827: if (!emudrives[drive])
! 828: return false;
! 829: assert(emudrives[drive]->drive_number == drive+2);
! 830: return true;
! 831: }
! 832:
1.1.1.2 root 833: /*-----------------------------------------------------------------------*/
1.1.1.12 root 834: /**
835: * Return drive ID(C:2, D:3 etc...) or -1 if not one of our emulation hard-drives
836: */
1.1.1.19! root 837: static int GemDOS_FileName2HardDriveID(char *pszFileName)
1.1 root 838: {
1.1.1.9 root 839: /* Do we even have a hard-drive? */
840: if (GEMDOS_EMU_ON)
841: {
1.1.1.19! root 842: int DriveNumber;
! 843:
1.1.1.10 root 844: /* Find drive letter (as number) */
1.1.1.17 root 845: DriveNumber = GemDOS_FindDriveNumber(pszFileName);
1.1.1.19! root 846: if (GemDOS_IsDriveEmulated(DriveNumber))
! 847: return DriveNumber;
1.1.1.11 root 848: }
1.1.1.15 root 849:
1.1.1.10 root 850: /* Not a high-level redirected drive, let TOS handle it */
1.1.1.9 root 851: return -1;
1.1 root 852: }
853:
1.1.1.7 root 854:
855: /*-----------------------------------------------------------------------*/
1.1.1.12 root 856: /**
1.1.1.17 root 857: * Check whether a file in given path matches given case-insensitive pattern.
858: * Return first matched name which caller needs to free, or NULL for no match.
859: */
860: static char* match_host_dir_entry(const char *path, const char *name, bool pattern)
861: {
862: struct dirent *entry;
863: char *match = NULL;
864: DIR *dir;
865:
866: dir = opendir(path);
867: if (!dir)
868: return NULL;
869:
1.1.1.18 root 870: #if DEBUG_PATTERN_MATCH
871: fprintf(stderr, "GEMDOS match '%s'%s in '%s'", name, pattern?" (pattern)":"", path);
872: #endif
1.1.1.17 root 873: if (pattern)
874: {
875: while ((entry = readdir(dir)))
876: {
877: if (fsfirst_match(name, entry->d_name))
878: {
879: match = strdup(entry->d_name);
880: break;
881: }
882: }
883: }
884: else
885: {
886: while ((entry = readdir(dir)))
887: {
888: if (strcasecmp(name, entry->d_name) == 0)
889: {
890: match = strdup(entry->d_name);
891: break;
892: }
893: }
894: }
895: closedir(dir);
1.1.1.18 root 896: #if DEBUG_PATTERN_MATCH
897: fprintf(stderr, "-> '%s'\n", match);
898: #endif
1.1.1.17 root 899: return match;
900: }
901:
902:
903: /*-----------------------------------------------------------------------*/
904: /**
905: * Check whether given TOS file/dir exists in given host path.
906: * If it does, add the matched host filename to the given path,
907: * otherwise add the given filename as is to it. Guarantees
908: * that the resulting string doesn't exceed maxlen+1.
909: *
910: * Return true if match found, false otherwise.
911: */
912: static bool add_path_component(char *path, int maxlen, const char *origname, bool is_dir)
913: {
914: char *tmp, *match, name[strlen(origname) + 3];
915: int dot, namelen, pathlen;
916: bool modified;
917:
918: strcpy(name, origname);
919: namelen = strlen(name);
920: pathlen = strlen(path);
921:
922: /* append separator */
923: if (pathlen >= maxlen)
924: return false;
925: path[pathlen++] = PATHSEP;
926: path[pathlen] = '\0';
927:
928: /* first try exact (case insensitive) match */
929: match = match_host_dir_entry(path, name, false);
930: if (match)
931: {
932: /* use strncat so that string is always nul terminated */
933: strncat(path+pathlen, match, maxlen-pathlen);
934: free(match);
935: return true;
936: }
937:
938: /* Here comes a work-around for a bug in the file selector
939: * of TOS 1.02: When a folder name has exactly 8 characters,
940: * it appends a '.' at the end of the name...
941: */
942: if (is_dir && namelen == 9 && name[8] == '.')
943: {
944: name[8] = '\0';
945: match = match_host_dir_entry(path, name, false);
946: if (match)
947: {
948: strncat(path+pathlen, match, maxlen-pathlen);
949: free(match);
950: return true;
951: }
952: }
953:
954: /* Assume there were invalid characters or that the host file
955: * was too long to fit into GEMDOS 8+3 filename limits.
1.1.1.18 root 956: * If that's the case, modify the name to a pattern that
957: * will match such host files and try again.
1.1.1.17 root 958: */
1.1.1.18 root 959: modified = false;
1.1.1.17 root 960:
961: /* catch potentially invalid characters */
962: for (tmp = name; *tmp; tmp++)
963: {
964: if (*tmp == INVALID_CHAR)
965: {
966: *tmp = '?';
967: modified = true;
968: }
969: }
970:
971: /* catch potentially too long extension */
972: for (dot = 0; name[dot] && name[dot] != '.'; dot++);
973: if (namelen - dot > 3)
974: {
975: /* "emulated.too" -> "emulated.too*" */
976: name[namelen++] = '*';
977: name[namelen] = '\0';
978: modified = true;
979: }
980: /* catch potentially too long part before extension */
981: if (namelen > 8 && name[8] == '.')
982: {
983: /* "emulated.too*" -> "emulated*.too*" */
984: memmove(name+9, name+8, namelen-7);
985: namelen++;
986: name[8] = '*';
987: modified = true;
988: }
989: else if (namelen == 8)
990: {
991: /* "emulated" -> "emulated*" */
992: name[8] = '*';
993: name[9] = '\0';
994: namelen++;
995: modified = true;
996: }
997:
998: if (modified)
999: {
1000: match = match_host_dir_entry(path, name, true);
1001: if (match)
1002: {
1003: strncat(path+pathlen, match, maxlen-pathlen);
1004: free(match);
1005: return true;
1006: }
1007: }
1008:
1009: /* not found, copy file/dirname as is */
1010: strncat(path+pathlen, origname, maxlen-pathlen);
1011: return false;
1012: }
1013:
1014:
1015: /**
1016: * Join remaining path without matching. This helper is used after host
1017: * file name matching fails, to append the failing part of the TOS path
1018: * to the host path, so that it won't be a valid host path.
1019: *
1020: * Specifically, the path separators need to be converted, otherwise things
1021: * like Fcreate() could create files that have TOS directory names as part
1022: * of file names on Unix (as \ is valid filename char on Unix). Fcreate()
1023: * needs to create them only when just the file name isn't found, but all
1024: * the directory components have.
1.1.1.12 root 1025: */
1.1.1.17 root 1026: static void add_remaining_path(const char *src, char *dstpath, int dstlen)
1.1.1.7 root 1027: {
1.1.1.17 root 1028: char *dst;
1029: int i;
1030:
1031: dstlen--;
1032: i = strlen(dstpath);
1033: for (dst = dstpath + i; *src && i < dstlen; dst++, src++, i++)
1034: {
1035: if (*src == '\\')
1036: *dst = PATHSEP;
1037: else
1038: *dst = *src;
1039: }
1040: *dst = '\0';
1.1.1.6 root 1041: }
1042:
1.1.1.2 root 1043: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1044: /**
1.1.1.17 root 1045: * Use hard-drive directory, current ST directory and filename
1046: * to create correct path to host file system. If given filename
1047: * isn't found on host file system, just append GEMDOS filename
1048: * to the path as is.
1049: *
1050: * TODO: currently there are many callers which give this dest buffer of
1051: * MAX_GEMDOS_PATH size i.e. don't take into account that host filenames
1052: * can be upto FILENAME_MAX long. Plain GEMDOS paths themselves may be
1053: * MAX_GEMDOS_PATH long even before host dir is prepended to it!
1054: * Way forward: allocate the host path here as FILENAME_MAX so that
1055: * it's always long enough and let callers free it. Assert if alloc
1056: * fails so that callers' don't need to.
1.1.1.12 root 1057: */
1058: void GemDOS_CreateHardDriveFileName(int Drive, const char *pszFileName,
1059: char *pszDestName, int nDestNameLen)
1.1 root 1060: {
1.1.1.17 root 1061: const char *s, *filename = pszFileName;
1062: int minlen;
1.1.1.11 root 1063:
1064: /* Is it a valid hard drive? */
1065: if (Drive < 2)
1066: return;
1.1.1.3 root 1067:
1.1.1.17 root 1068: /* Check for valid string */
1069: if (filename[0] == '\0')
1070: return;
1.1.1.9 root 1071:
1.1.1.17 root 1072: /* make sure that more convenient strncat() can be used
1073: * on the destination string (it always null terminates
1074: * unlike strncpy()).
1075: */
1076: *pszDestName = 0;
1077: /* strcat writes n+1 chars, se decrease len */
1078: nDestNameLen--;
1079:
1080: /* full filename with drive "C:\foo\bar" */
1081: if (filename[1] == ':')
1082: {
1083: strncat(pszDestName, emudrives[Drive-2]->hd_emulation_dir, nDestNameLen);
1084: filename += 2;
1.1.1.9 root 1085: }
1.1.1.17 root 1086: /* filename referenced from root: "\foo\bar" */
1087: else if (filename[0] == '\\')
1.1.1.9 root 1088: {
1.1.1.17 root 1089: strncat(pszDestName, emudrives[Drive-2]->hd_emulation_dir, nDestNameLen);
1.1.1.9 root 1090: }
1.1.1.17 root 1091: /* filename relative to current directory */
1.1.1.9 root 1092: else
1093: {
1.1.1.17 root 1094: strncat(pszDestName, emudrives[Drive-2]->fs_currpath, nDestNameLen);
1095: }
1096:
1097: minlen = strlen(emudrives[Drive-2]->hd_emulation_dir);
1098: /* this doesn't take into account possible long host filenames
1099: * that will make dest name longer than pszFileName 8.3 paths,
1100: * or GEMDOS paths using "../" which make it smaller. Both
1101: * should(?) be rare in paths, so this info to user should be
1102: * good enough.
1103: */
1104: if (nDestNameLen < minlen + (int)strlen(pszFileName) + 2)
1105: {
1106: Log_AlertDlg(LOG_ERROR, "Appending GEMDOS path '%s' to HDD emu host root dir doesn't fit to %d chars (current Hatari limit)!",
1107: pszFileName, nDestNameLen);
1108: add_remaining_path(filename, pszDestName, nDestNameLen);
1109: return;
1.1.1.6 root 1110: }
1.1.1.9 root 1111:
1.1.1.17 root 1112: /* "../" handling breaks if there are extra slashes */
1113: File_CleanFileName(pszDestName);
1114:
1115: /* go through path directory components, advacing 'filename'
1116: * pointer while parsing them.
1117: */
1118: for (;;)
1.1.1.9 root 1119: {
1.1.1.17 root 1120: /* skip extra path separators */
1121: while (*filename == '\\')
1122: filename++;
1123:
1124: // fprintf(stderr, "filename: '%s', path: '%s'\n", filename, pszDestName);
1125:
1126: /* skip "." references to current directory */
1127: if (filename[0] == '.' &&
1128: (filename[1] == '\\' || !filename[1]))
1.1.1.9 root 1129: {
1.1.1.17 root 1130: filename++;
1.1.1.9 root 1131: continue;
1132: }
1.1.1.17 root 1133:
1134: /* ".." path component -> strip last dir from dest path */
1135: if (filename[0] == '.' &&
1136: filename[1] == '.' &&
1137: (filename[2] == '\\' || !filename[2]))
1.1.1.9 root 1138: {
1.1.1.17 root 1139: char *sep = strrchr(pszDestName, PATHSEP);
1140: if (sep)
1.1.1.9 root 1141: {
1.1.1.17 root 1142: if (sep - pszDestName < minlen)
1143: Log_Printf(LOG_WARN, "GEMDOS path '%s' tried to back out of GEMDOS drive!\n", pszFileName);
1144: else
1145: *sep = '\0';
1.1.1.9 root 1146: }
1.1.1.17 root 1147: filename += 2;
1148: continue;
1149: }
1150:
1151: /* handle directory component */
1152: if ((s = strchr(filename, '\\')))
1153: {
1154: int dirlen = s - filename;
1155: char dirname[dirlen+1];
1156: /* copy dirname */
1157: strncpy(dirname, filename, dirlen);
1158: dirname[dirlen] = '\0';
1159: /* and advance filename */
1160: filename = s;
1161:
1162: if (strchr(dirname, '?') || strchr(dirname, '*'))
1163: Log_Printf(LOG_WARN, "GEMDOS dir name '%s' with wildcards in %s!\n", dirname, pszFileName);
1164:
1165: /* convert and append dirname to host path */
1166: if (!add_path_component(pszDestName, nDestNameLen, dirname, true))
1.1.1.9 root 1167: {
1.1.1.17 root 1168: Log_Printf(LOG_WARN, "No GEMDOS dir '%s'\n", pszDestName);
1169: add_remaining_path(filename, pszDestName, nDestNameLen);
1170: return;
1.1.1.9 root 1171: }
1.1.1.17 root 1172: continue;
1.1.1.9 root 1173: }
1.1.1.17 root 1174:
1175: /* path directory components done */
1176: break;
1.1.1.9 root 1177: }
1178:
1.1.1.17 root 1179: if (*filename)
1.1.1.9 root 1180: {
1.1.1.17 root 1181: /* a wildcard instead of a complete file name? */
1182: if (strchr(filename,'?') || strchr(filename,'*'))
1.1.1.9 root 1183: {
1.1.1.17 root 1184: int len = strlen(pszDestName);
1185: if (len < nDestNameLen)
1.1.1.9 root 1186: {
1.1.1.17 root 1187: pszDestName[len++] = PATHSEP;
1188: pszDestName[len] = '\0';
1.1.1.9 root 1189: }
1.1.1.17 root 1190: /* use strncat so that string is always nul terminated */
1191: strncat(pszDestName+len, filename, nDestNameLen-len);
1192: }
1193: else if (!add_path_component(pszDestName, nDestNameLen, filename, false))
1194: {
1195: /* It's often normal, that GEM uses this to test for
1196: * existence of desktop.inf or newdesk.inf for example.
1197: */
1198: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS didn't find filename %s\n", pszDestName);
1199: return;
1.1.1.9 root 1200: }
1201: }
1.1.1.17 root 1202: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS: %s -> host: %s\n", pszFileName, pszDestName);
1.1 root 1203: }
1204:
1.1.1.6 root 1205:
1.1.1.2 root 1206: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1207: /**
1208: * GEMDOS Cauxin
1209: * Call 0x3
1210: */
1.1.1.7 root 1211: #if 0
1.1.1.13 root 1212: static bool GemDOS_Cauxin(Uint32 Params)
1.1 root 1213: {
1.1.1.17 root 1214: Uint8 c;
1.1 root 1215:
1.1.1.9 root 1216: /* Wait here until a character is ready */
1217: while(!RS232_GetStatus())
1218: ;
1.1 root 1219:
1.1.1.9 root 1220: /* And read character */
1.1.1.11 root 1221: RS232_ReadBytes(&c,1);
1222: Regs[REG_D0] = c;
1.1 root 1223:
1.1.1.15 root 1224: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Cauxin() = 0x%x\n", (int)c);
1225:
1226: return true;
1.1 root 1227: }
1.1.1.7 root 1228: #endif
1.1 root 1229:
1.1.1.2 root 1230: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1231: /**
1232: * GEMDOS Cauxout
1233: * Call 0x4
1234: */
1.1.1.7 root 1235: #if 0
1.1.1.13 root 1236: static bool GemDOS_Cauxout(Uint32 Params)
1.1 root 1237: {
1.1.1.17 root 1238: Uint8 c;
1.1 root 1239:
1.1.1.15 root 1240: /* Get character from the stack */
1.1.1.17 root 1241: c = STMemory_ReadWord(Params);
1.1.1.15 root 1242:
1243: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Cauxout(0x%x)\n", (int)c);
1244:
1245: /* Send character to RS232 */
1.1.1.11 root 1246: RS232_TransferBytesTo(&c, 1);
1.1 root 1247:
1.1.1.15 root 1248: return true;
1.1 root 1249: }
1.1.1.7 root 1250: #endif
1.1 root 1251:
1.1.1.2 root 1252: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1253: /**
1254: * GEMDOS Cprnout
1255: * Call 0x5
1256: */
1.1.1.13 root 1257: #if 0
1258: static bool GemDOS_Cprnout(Uint32 Params)
1.1 root 1259: {
1.1.1.17 root 1260: Uint8 c;
1.1 root 1261:
1.1.1.9 root 1262: /* Send character to printer(or file) */
1.1.1.17 root 1263: c = STMemory_ReadWord(Params);
1.1.1.15 root 1264: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Cprnout(0x%x)\n", (int)c);
1.1.1.11 root 1265: Printer_TransferByteTo(c);
1.1.1.9 root 1266: Regs[REG_D0] = -1; /* Printer OK */
1.1 root 1267:
1.1.1.15 root 1268: return true;
1.1 root 1269: }
1.1.1.13 root 1270: #endif
1.1 root 1271:
1.1.1.2 root 1272: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1273: /**
1274: * GEMDOS Set drive (0=A,1=B,2=C etc...)
1275: * Call 0xE
1276: */
1.1.1.13 root 1277: static bool GemDOS_SetDrv(Uint32 Params)
1.1 root 1278: {
1.1.1.9 root 1279: /* Read details from stack for our own use */
1.1.1.17 root 1280: CurrentDrive = STMemory_ReadWord(Params);
1.1 root 1281:
1.1.1.15 root 1282: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Dsetdrv(0x%x)\n", (int)CurrentDrive);
1283:
1.1.1.9 root 1284: /* Still re-direct to TOS */
1.1.1.15 root 1285: return false;
1.1 root 1286: }
1287:
1.1.1.2 root 1288: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1289: /**
1290: * GEMDOS Cprnos
1291: * Call 0x11
1292: */
1.1.1.13 root 1293: #if 0
1294: static bool GemDOS_Cprnos(Uint32 Params)
1.1 root 1295: {
1.1.1.13 root 1296: /* printer status depends if printing is enabled or not... */
1.1.1.9 root 1297: if (ConfigureParams.Printer.bEnablePrinting)
1298: Regs[REG_D0] = -1; /* Printer OK */
1299: else
1300: Regs[REG_D0] = 0; /* printer not ready if printing disabled */
1.1 root 1301:
1.1.1.15 root 1302: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Cprnos() = 0x%x\n", Regs[REG_D0]);
1303:
1304: return true;
1.1 root 1305: }
1.1.1.13 root 1306: #endif
1.1 root 1307:
1.1.1.2 root 1308: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1309: /**
1310: * GEMDOS Cauxis
1311: * Call 0x12
1312: */
1.1.1.7 root 1313: #if 0
1.1.1.13 root 1314: static bool GemDOS_Cauxis(Uint32 Params)
1.1 root 1315: {
1.1.1.9 root 1316: /* Read our RS232 state */
1317: if (RS232_GetStatus())
1318: Regs[REG_D0] = -1; /* Chars waiting */
1319: else
1320: Regs[REG_D0] = 0;
1.1 root 1321:
1.1.1.15 root 1322: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Cauxis() = 0x%x\n", Regs[REG_D0]);
1323:
1324: return true;
1.1 root 1325: }
1.1.1.7 root 1326: #endif
1.1 root 1327:
1.1.1.2 root 1328: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1329: /**
1330: * GEMDOS Cauxos
1331: * Call 0x13
1332: */
1.1.1.7 root 1333: #if 0
1.1.1.13 root 1334: static bool GemDOS_Cauxos(Uint32 Params)
1.1 root 1335: {
1.1.1.9 root 1336: Regs[REG_D0] = -1; /* Device ready */
1.1 root 1337:
1.1.1.15 root 1338: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Cauxos() = 0x%x\n", Regs[REG_D0]);
1339:
1340: return true;
1.1 root 1341: }
1.1.1.7 root 1342: #endif
1.1 root 1343:
1.1.1.2 root 1344: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1345: /**
1346: * GEMDOS Set Disk Transfer Address (DTA)
1347: * Call 0x1A
1348: */
1.1.1.13 root 1349: static bool GemDOS_SetDTA(Uint32 Params)
1.1 root 1350: {
1.1.1.17 root 1351: /* Look up on stack to find where DTA is */
1352: Uint32 nDTA = STMemory_ReadLong(Params);
1.1.1.15 root 1353:
1354: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fsetdta(0x%x)\n", nDTA);
1355:
1.1.1.17 root 1356: if (STMemory_ValidArea(nDTA, sizeof(DTA)))
1357: {
1358: /* Store as PC pointer */
1359: pDTA = (DTA *)STRAM_ADDR(nDTA);
1360: }
1361: else
1362: {
1363: pDTA = NULL;
1364: Log_Printf(LOG_WARN, "GEMDOS Fsetdta() failed due to invalid DTA address 0x%x\n", nDTA);
1365: }
1.1.1.18 root 1366: /* redirect to TOS */
1.1.1.15 root 1367: return false;
1.1 root 1368: }
1369:
1.1.1.2 root 1370: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1371: /**
1372: * GEMDOS Dfree Free disk space.
1.1.1.15 root 1373: * Call 0x36
1.1.1.12 root 1374: */
1.1.1.13 root 1375: static bool GemDOS_DFree(Uint32 Params)
1.1.1.2 root 1376: {
1.1.1.19! root 1377: #ifdef HAVE_STATVFS
! 1378: struct statvfs buf;
! 1379: #endif
! 1380: int Drive, Total, Free;
1.1.1.10 root 1381: Uint32 Address;
1.1.1.9 root 1382:
1.1.1.17 root 1383: Address = STMemory_ReadLong(Params);
1384: Drive = STMemory_ReadWord(Params+SIZE_LONG);
1.1.1.15 root 1385:
1.1.1.19! root 1386: /* Note: Drive = 0 means current drive, 1 = A:, 2 = B:, 3 = C:, etc. */
1.1.1.15 root 1387: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Dfree(0x%x, %i)\n", Address, Drive);
1.1.1.19! root 1388: if (Drive == 0)
! 1389: Drive = CurrentDrive;
! 1390: else
! 1391: Drive--;
1.1.1.15 root 1392:
1.1.1.9 root 1393: /* is it our drive? */
1.1.1.19! root 1394: if (!GemDOS_IsDriveEmulated(Drive))
1.1.1.9 root 1395: {
1.1.1.19! root 1396: /* no, redirect to TOS */
! 1397: return false;
! 1398: }
! 1399: /* Check that write is requested to valid memory area */
! 1400: if (!STMemory_ValidArea(Address, 16))
! 1401: {
! 1402: Log_Printf(LOG_WARN, "GEMDOS Dfree() failed due to invalid RAM range 0x%x+%i\n", Address, 16);
! 1403: Regs[REG_D0] = GEMDOS_ERANGE;
! 1404: return true;
! 1405: }
! 1406:
! 1407: #ifdef HAVE_STATVFS
! 1408: if (statvfs(emudrives[Drive-2]->hd_emulation_dir, &buf) == 0)
! 1409: {
! 1410: Total = buf.f_blocks/1024 * buf.f_frsize;
! 1411: if (buf.f_bavail)
! 1412: Free = buf.f_bavail; /* free for unprivileged user */
! 1413: else
! 1414: Free = buf.f_bfree;
! 1415: Free = Free/1024 * buf.f_bsize;
! 1416:
! 1417: /* TOS version limits based on:
! 1418: * http://www.seimet.de/atari/en/hddriverfaq.html
! 1419: */
! 1420: if (TosVersion >= 0x0400)
1.1.1.18 root 1421: {
1.1.1.19! root 1422: if (Total > 1024*1024)
! 1423: Total = 1024*1024;
1.1.1.18 root 1424: }
1.1.1.19! root 1425: else
! 1426: {
! 1427: if (TosVersion >= 0x0106)
! 1428: {
! 1429: if (Total > 512*1024)
! 1430: Total = 512*1024;
! 1431: }
! 1432: else
! 1433: {
! 1434: if (Total > 256*1024)
! 1435: Total = 256*1024;
! 1436: }
! 1437: }
! 1438: if (Free > Total)
! 1439: Free = Total;
1.1.1.9 root 1440: }
1.1.1.19! root 1441: else
! 1442: #endif
! 1443: {
! 1444: /* fake 32MB drive with 16MB free */
! 1445: Total = 32*1024;
! 1446: Free = 16*1024;
! 1447: }
! 1448: STMemory_WriteLong(Address, Free); /* free clusters */
! 1449: STMemory_WriteLong(Address+SIZE_LONG, Total); /* total clusters */
! 1450:
! 1451: STMemory_WriteLong(Address+SIZE_LONG*2, 512); /* bytes per sector */
! 1452: STMemory_WriteLong(Address+SIZE_LONG*3, 2); /* sectors per cluster (cluster = 1KB) */
! 1453: return true;
1.1.1.2 root 1454: }
1455:
1456: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1457: /**
1458: * GEMDOS MkDir
1459: * Call 0x39
1460: */
1.1.1.13 root 1461: static bool GemDOS_MkDir(Uint32 Params)
1.1.1.6 root 1462: {
1.1.1.17 root 1463: char *pDirName, *psDirPath;
1.1.1.9 root 1464: int Drive;
1465:
1466: /* Find directory to make */
1.1.1.17 root 1467: pDirName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1.1.1.9 root 1468:
1.1.1.15 root 1469: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Dcreate(\"%s\")\n", pDirName);
1470:
1.1.1.19! root 1471: Drive = GemDOS_FileName2HardDriveID(pDirName);
1.1.1.9 root 1472:
1.1.1.17 root 1473: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 1474: {
1.1.1.17 root 1475: /* redirect to TOS */
1476: return false;
1477: }
1.1.1.9 root 1478:
1.1.1.17 root 1479: /* write protected device? */
1480: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
1481: {
1482: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Dcreate(\"%s\")\n", pDirName);
1483: Regs[REG_D0] = GEMDOS_EWRPRO;
1484: return true;
1485: }
1.1.1.9 root 1486:
1.1.1.17 root 1487: psDirPath = malloc(FILENAME_MAX);
1488: if (!psDirPath)
1489: {
1490: perror("GemDOS_MkDir");
1491: Regs[REG_D0] = GEMDOS_ENSMEM;
1.1.1.15 root 1492: return true;
1.1.1.9 root 1493: }
1.1.1.17 root 1494:
1495: /* Copy old directory, as if calls fails keep this one */
1496: GemDOS_CreateHardDriveFileName(Drive, pDirName, psDirPath, FILENAME_MAX);
1497:
1498: /* Attempt to make directory */
1499: if (mkdir(psDirPath, 0755) == 0)
1500: Regs[REG_D0] = GEMDOS_EOK;
1501: else
1502: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */
1503:
1504: free(psDirPath);
1505: return true;
1.1 root 1506: }
1507:
1.1.1.2 root 1508: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1509: /**
1510: * GEMDOS RmDir
1511: * Call 0x3A
1512: */
1.1.1.13 root 1513: static bool GemDOS_RmDir(Uint32 Params)
1.1.1.6 root 1514: {
1.1.1.17 root 1515: char *pDirName, *psDirPath;
1.1.1.9 root 1516: int Drive;
1517:
1518: /* Find directory to make */
1.1.1.17 root 1519: pDirName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1.1.1.15 root 1520:
1521: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Ddelete(\"%s\")\n", pDirName);
1522:
1.1.1.19! root 1523: Drive = GemDOS_FileName2HardDriveID(pDirName);
1.1.1.15 root 1524:
1.1.1.17 root 1525: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 1526: {
1.1.1.17 root 1527: /* redirect to TOS */
1528: return false;
1529: }
1.1.1.9 root 1530:
1.1.1.17 root 1531: /* write protected device? */
1532: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
1533: {
1534: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Ddelete(\"%s\")\n", pDirName);
1535: Regs[REG_D0] = GEMDOS_EWRPRO;
1536: return true;
1537: }
1.1.1.9 root 1538:
1.1.1.17 root 1539: psDirPath = malloc(FILENAME_MAX);
1540: if (!psDirPath)
1541: {
1542: perror("GemDOS_RmDir");
1543: Regs[REG_D0] = GEMDOS_ENSMEM;
1.1.1.15 root 1544: return true;
1.1.1.9 root 1545: }
1.1.1.17 root 1546:
1547: /* Copy old directory, as if calls fails keep this one */
1548: GemDOS_CreateHardDriveFileName(Drive, pDirName, psDirPath, FILENAME_MAX);
1549:
1550: /* Attempt to remove directory */
1551: if (rmdir(psDirPath) == 0)
1552: Regs[REG_D0] = GEMDOS_EOK;
1553: else
1554: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */
1555:
1556: free(psDirPath);
1557: return true;
1.1 root 1558: }
1559:
1.1.1.11 root 1560:
1.1.1.2 root 1561: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1562: /**
1563: * GEMDOS ChDir
1564: * Call 0x3B
1565: */
1.1.1.13 root 1566: static bool GemDOS_ChDir(Uint32 Params)
1.1.1.6 root 1567: {
1.1.1.17 root 1568: char *pDirName, *psTempDirPath;
1569: struct stat buf;
1.1.1.9 root 1570: int Drive;
1.1 root 1571:
1.1.1.9 root 1572: /* Find new directory */
1.1.1.17 root 1573: pDirName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1.1.1.3 root 1574:
1.1.1.15 root 1575: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Dsetpath(\"%s\")\n", pDirName);
1.1.1.3 root 1576:
1.1.1.19! root 1577: Drive = GemDOS_FileName2HardDriveID(pDirName);
1.1.1.3 root 1578:
1.1.1.17 root 1579: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 1580: {
1.1.1.17 root 1581: /* redirect to TOS */
1582: return false;
1583: }
1.1.1.3 root 1584:
1.1.1.17 root 1585: /* Allocate temporary memory for path name: */
1586: psTempDirPath = malloc(FILENAME_MAX);
1587: if (!psTempDirPath)
1588: {
1589: perror("GemDOS_ChDir");
1590: Regs[REG_D0] = GEMDOS_ENSMEM;
1591: return true;
1592: }
1.1.1.11 root 1593:
1.1.1.17 root 1594: GemDOS_CreateHardDriveFileName(Drive, pDirName, psTempDirPath, FILENAME_MAX);
1.1.1.15 root 1595:
1.1.1.17 root 1596: // Remove trailing slashes (stat on Windows does not like that)
1597: File_CleanFileName(psTempDirPath);
1.1.1.15 root 1598:
1.1.1.17 root 1599: if (stat(psTempDirPath, &buf))
1600: {
1601: /* error */
1602: free(psTempDirPath);
1603: Regs[REG_D0] = GEMDOS_EPTHNF;
1604: return true;
1605: }
1.1.1.9 root 1606:
1.1.1.17 root 1607: File_AddSlashToEndFileName(psTempDirPath);
1608: File_MakeAbsoluteName(psTempDirPath);
1.1.1.9 root 1609:
1.1.1.17 root 1610: /* Prevent '..' commands moving BELOW the root HDD folder */
1611: /* by double checking if path is valid */
1612: if (strncmp(psTempDirPath, emudrives[Drive-2]->hd_emulation_dir,
1.1.1.15 root 1613: strlen(emudrives[Drive-2]->hd_emulation_dir)) == 0)
1.1.1.17 root 1614: {
1615: strcpy(emudrives[Drive-2]->fs_currpath, psTempDirPath);
1616: Regs[REG_D0] = GEMDOS_EOK;
1617: }
1618: else
1619: {
1620: Regs[REG_D0] = GEMDOS_EPTHNF;
1.1.1.9 root 1621: }
1.1.1.17 root 1622: free(psTempDirPath);
1623:
1624: return true;
1.1.1.6 root 1625:
1.1.1.17 root 1626: }
1627:
1628:
1629: /*-----------------------------------------------------------------------*/
1630: /**
1631: * Helper to check whether given file's path is missing.
1632: * Returns true if missing, false if found.
1633: * Modifies the argument buffer.
1634: */
1635: static bool GemDOS_FilePathMissing(char *szActualFileName)
1636: {
1637: char *ptr = strrchr(szActualFileName, PATHSEP);
1638: if (ptr)
1639: {
1640: *ptr = 0; /* Strip filename from string */
1.1.1.18 root 1641: if (!File_DirExists(szActualFileName))
1.1.1.17 root 1642: return true;
1643: }
1644: return false;
1.1 root 1645: }
1646:
1.1.1.11 root 1647:
1.1.1.2 root 1648: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1649: /**
1650: * GEMDOS Create file
1651: * Call 0x3C
1652: */
1.1.1.13 root 1653: static bool GemDOS_Create(Uint32 Params)
1.1 root 1654: {
1.1.1.17 root 1655: /* TODO: host filenames might not fit into this */
1.1.1.11 root 1656: char szActualFileName[MAX_GEMDOS_PATH];
1.1.1.17 root 1657: char *pszFileName;
1.1.1.9 root 1658: int Drive,Index,Mode;
1659:
1660: /* Find filename */
1.1.1.17 root 1661: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1662: Mode = STMemory_ReadWord(Params+SIZE_LONG);
1.1.1.15 root 1663:
1664: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fcreate(\"%s\", 0x%x)\n", pszFileName, Mode);
1665:
1.1.1.19! root 1666: Drive = GemDOS_FileName2HardDriveID(pszFileName);
1.1.1.15 root 1667:
1.1.1.12 root 1668: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 1669: {
1.1.1.17 root 1670: /* redirect to TOS */
1.1.1.15 root 1671: return false;
1.1.1.12 root 1672: }
1.1.1.9 root 1673:
1.1.1.12 root 1674: if (Mode == GEMDOS_FILE_ATTRIB_VOLUME_LABEL)
1675: {
1.1.1.17 root 1676: Log_Printf(LOG_WARN, "Warning: Hatari doesn't support GEMDOS volume"
1677: " label setting\n(for '%s')\n", pszFileName);
1.1.1.12 root 1678: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */
1.1.1.15 root 1679: return true;
1.1.1.12 root 1680: }
1.1 root 1681:
1.1.1.17 root 1682: /* write protected device? */
1683: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
1684: {
1685: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fcreate(\"%s\")\n", pszFileName);
1686: Regs[REG_D0] = GEMDOS_EWRPRO;
1687: return true;
1688: }
1689:
1.1.1.12 root 1690: /* Now convert to hard drive filename */
1691: GemDOS_CreateHardDriveFileName(Drive, pszFileName,
1692: szActualFileName, sizeof(szActualFileName));
1693:
1694: /* Find slot to store file handle, as need to return WORD handle for ST */
1695: Index = GemDOS_FindFreeFileHandle();
1696: if (Index==-1)
1697: {
1698: /* No free handles, return error code */
1699: Regs[REG_D0] = GEMDOS_ENHNDL; /* No more handles */
1.1.1.15 root 1700: return true;
1.1.1.12 root 1701: }
1.1.1.17 root 1702:
1703: /* truncate and open for reading & writing */
1704: FileHandles[Index].FileHandle = fopen(szActualFileName, "wb+");
1.1.1.15 root 1705:
1.1.1.12 root 1706: if (FileHandles[Index].FileHandle != NULL)
1707: {
1.1.1.17 root 1708: /* FIXME: implement other Mode attributes
1709: * - GEMDOS_FILE_ATTRIB_HIDDEN (FA_HIDDEN)
1710: * - GEMDOS_FILE_ATTRIB_SYSTEM_FILE (FA_SYSTEM)
1711: * - GEMDOS_FILE_ATTRIB_SUBDIRECTORY (FA_DIR)
1712: * - GEMDOS_FILE_ATTRIB_WRITECLOSE (FA_ARCHIVE)
1713: * (set automatically by GemDOS >= 0.15)
1714: */
1715: if (Mode & GEMDOS_FILE_ATTRIB_READONLY)
1716: {
1717: /* after closing, file should be read-only */
1718: chmod(szActualFileName, S_IRUSR|S_IRGRP|S_IROTH);
1719: }
1.1.1.12 root 1720: /* Tag handle table entry as used and return handle */
1.1.1.15 root 1721: FileHandles[Index].bUsed = true;
1.1.1.17 root 1722: snprintf(FileHandles[Index].szActualName,
1723: sizeof(FileHandles[Index].szActualName),
1724: "%s", szActualFileName);
1725:
1726: /* Return valid ST file handle from our range (from BASE_FILEHANDLE upwards) */
1727: Regs[REG_D0] = Index+BASE_FILEHANDLE;
1728: LOG_TRACE(TRACE_OS_GEMDOS, "-> FD %d (%s)\n", Index,
1729: Mode & GEMDOS_FILE_ATTRIB_READONLY ? "read-only":"read/write");
1.1.1.15 root 1730: return true;
1.1.1.12 root 1731: }
1.1.1.2 root 1732:
1.1.1.17 root 1733: /* We failed to create the file, did we have required access rights? */
1734: if (errno == EACCES || errno == EROFS ||
1735: errno == EPERM || errno == EISDIR)
1.1.1.12 root 1736: {
1.1.1.17 root 1737: Log_Printf(LOG_WARN, "GEMDOS failed to create/truncate '%s'\n",
1738: szActualFileName);
1739: Regs[REG_D0] = GEMDOS_EACCDN;
1740: return true;
1.1.1.9 root 1741: }
1.1.1.17 root 1742:
1743: /* Or was path to file missing? (ST-Zip 2.6 relies on getting
1744: * correct error about that during extraction of ZIP files.)
1745: */
1746: if (errno == ENOTDIR || GemDOS_FilePathMissing(szActualFileName))
1747: {
1748: Regs[REG_D0] = GEMDOS_EPTHNF; /* Path not found */
1749: return true;
1750: }
1751:
1.1.1.12 root 1752: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */
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 Open file
1759: * Call 0x3D
1760: */
1.1.1.13 root 1761: static bool GemDOS_Open(Uint32 Params)
1.1 root 1762: {
1.1.1.17 root 1763: /* TODO: host filenames might not fit into this */
1.1.1.11 root 1764: char szActualFileName[MAX_GEMDOS_PATH];
1.1.1.9 root 1765: char *pszFileName;
1.1.1.17 root 1766: const char *ModeStr;
1767: /* convert atari modes to stdio modes */
1768: struct {
1769: const char *mode;
1770: const char *desc;
1771: } Modes[] = {
1772: { "rb", "read-only" },
1773: /* FIXME: is actually read/write as "wb" would truncate */
1774: { "rb+", "write-only" },
1775: { "rb+", "read/write" },
1776: { "rb+", "read/write" }
1777: };
1778: int Drive, Index, Mode;
1.1.1.18 root 1779: FILE *AutostartHandle;
1.1.1.9 root 1780:
1781: /* Find filename */
1.1.1.17 root 1782: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1783: Mode = STMemory_ReadWord(Params+SIZE_LONG);
1.1.1.15 root 1784:
1785: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fopen(\"%s\", 0x%x)\n", pszFileName, Mode);
1786:
1.1.1.19! root 1787: Drive = GemDOS_FileName2HardDriveID(pszFileName);
1.1.1.9 root 1788:
1.1.1.12 root 1789: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 1790: {
1.1.1.17 root 1791: /* redirect to TOS */
1.1.1.15 root 1792: return false;
1.1.1.12 root 1793: }
1.1.1.17 root 1794:
1.1.1.12 root 1795: /* Find slot to store file handle, as need to return WORD handle for ST */
1796: Index = GemDOS_FindFreeFileHandle();
1797: if (Index == -1)
1798: {
1799: /* No free handles, return error code */
1800: Regs[REG_D0] = GEMDOS_ENHNDL; /* No more handles */
1.1.1.15 root 1801: return true;
1.1.1.9 root 1802: }
1.1.1.6 root 1803:
1.1.1.17 root 1804: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
1805: {
1806: /* force all accesses to be read-only */
1807: ModeStr = Modes[0].mode;
1808: }
1809: else
1810: {
1811: /* GEMDOS mount can be written, try open in requested mode */
1812: ModeStr = Modes[Mode&0x03].mode;
1813: }
1814:
1.1.1.18 root 1815:
1816: if ((AutostartHandle = TOS_AutoStartOpen(pszFileName)))
1817: {
1818: strcpy(szActualFileName, pszFileName);
1819: FileHandles[Index].FileHandle = AutostartHandle;
1820: }
1821: else
1822: {
1823: /* Convert to hard drive filename */
1824: GemDOS_CreateHardDriveFileName(Drive, pszFileName,
1825: szActualFileName, sizeof(szActualFileName));
1826:
1827: /* FIXME: Open file
1828: * - fopen() modes don't allow write-only mode without truncating
1829: * which would be needed to implement mode 1 (write-only) correctly.
1830: * Fixing this requires using open() and file descriptors instead
1831: * of fopen() and FILE* pointers, but Windows doesn't support that.
1832: */
1833: FileHandles[Index].FileHandle = fopen(szActualFileName, ModeStr);
1834: }
1.1.1.15 root 1835:
1.1.1.12 root 1836: if (FileHandles[Index].FileHandle != NULL)
1837: {
1838: /* Tag handle table entry as used and return handle */
1.1.1.15 root 1839: FileHandles[Index].bUsed = true;
1.1.1.17 root 1840: snprintf(FileHandles[Index].szActualName,
1841: sizeof(FileHandles[Index].szActualName),
1842: "%s", szActualFileName);
1843:
1844: /* Return valid ST file handle from our range (BASE_FILEHANDLE upwards) */
1845: Regs[REG_D0] = Index+BASE_FILEHANDLE;
1846: LOG_TRACE(TRACE_OS_GEMDOS, "-> FD %d (%s)\n",
1847: Index, Modes[Mode&0x03].desc);
1.1.1.15 root 1848: return true;
1.1.1.12 root 1849: }
1.1.1.15 root 1850:
1.1.1.17 root 1851: if (errno == EACCES || errno == EROFS ||
1852: errno == EPERM || errno == EISDIR)
1853: {
1854: Log_Printf(LOG_WARN, "GEMDOS missing %s permission to file '%s'\n",
1855: Modes[Mode&0x03].desc, szActualFileName);
1856: Regs[REG_D0] = GEMDOS_EACCDN;
1857: return true;
1858: }
1859: if (errno == ENOTDIR || GemDOS_FilePathMissing(szActualFileName))
1860: {
1861: /* Path not found */
1862: Regs[REG_D0] = GEMDOS_EPTHNF;
1863: return true;
1864: }
1865: /* File not found / error opening */
1866: Regs[REG_D0] = GEMDOS_EFILNF;
1.1.1.15 root 1867: return true;
1.1 root 1868: }
1869:
1.1.1.2 root 1870: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1871: /**
1872: * GEMDOS Close file
1873: * Call 0x3E
1874: */
1.1.1.13 root 1875: static bool GemDOS_Close(Uint32 Params)
1.1 root 1876: {
1.1.1.9 root 1877: int Handle;
1.1 root 1878:
1.1.1.9 root 1879: /* Find our handle - may belong to TOS */
1.1.1.17 root 1880: Handle = STMemory_ReadWord(Params);
1.1 root 1881:
1.1.1.15 root 1882: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fclose(%i)\n", Handle);
1883:
1.1.1.17 root 1884: Handle -= BASE_FILEHANDLE;
1885:
1.1.1.9 root 1886: /* Check handle was valid */
1887: if (GemDOS_IsInvalidFileHandle(Handle))
1888: {
1.1.1.17 root 1889: /* no, assume it was TOS one -> redirect */
1.1.1.15 root 1890: return false;
1.1.1.9 root 1891: }
1.1.1.17 root 1892:
1893: /* Close file and free up handle table */
1.1.1.18 root 1894: if (!TOS_AutoStartClose(FileHandles[Handle].FileHandle))
1895: {
1896: fclose(FileHandles[Handle].FileHandle);
1897: }
1.1.1.17 root 1898: FileHandles[Handle].bUsed = false;
1899:
1900: /* Return no error */
1901: Regs[REG_D0] = GEMDOS_EOK;
1902: return true;
1.1 root 1903: }
1904:
1.1.1.2 root 1905: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1906: /**
1907: * GEMDOS Read file
1908: * Call 0x3F
1909: */
1.1.1.13 root 1910: static bool GemDOS_Read(Uint32 Params)
1.1 root 1911: {
1.1.1.9 root 1912: char *pBuffer;
1.1.1.17 root 1913: long CurrentPos, FileSize, nBytesRead, nBytesLeft;
1914: Uint32 Addr;
1915: Uint32 Size;
1.1.1.9 root 1916: int Handle;
1917:
1918: /* Read details from stack */
1.1.1.17 root 1919: Handle = STMemory_ReadWord(Params);
1920: Size = STMemory_ReadLong(Params+SIZE_WORD);
1921: Addr = STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG);
1922: pBuffer = (char *)STRAM_ADDR(Addr);
1.1.1.9 root 1923:
1.1.1.17 root 1924: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fread(%i, %i, 0x%x)\n",
1925: Handle, Size, Addr);
1926:
1927: Handle -= BASE_FILEHANDLE;
1.1.1.15 root 1928:
1.1.1.9 root 1929: /* Check handle was valid */
1930: if (GemDOS_IsInvalidFileHandle(Handle))
1931: {
1.1.1.17 root 1932: /* assume it was TOS one -> redirect */
1.1.1.15 root 1933: return false;
1.1.1.9 root 1934: }
1935:
1.1.1.17 root 1936: /* Old TOS versions treat the Size parameter as signed */
1937: if (TosVersion < 0x400 && (Size & 0x80000000))
1938: {
1939: /* return -1 as original GEMDOS */
1940: Regs[REG_D0] = -1;
1941: return true;
1942: }
1943:
1944: /* To quick check to see where our file pointer is and how large the file is */
1945: CurrentPos = ftell(FileHandles[Handle].FileHandle);
1946: fseek(FileHandles[Handle].FileHandle, 0, SEEK_END);
1947: FileSize = ftell(FileHandles[Handle].FileHandle);
1948: fseek(FileHandles[Handle].FileHandle, CurrentPos, SEEK_SET);
1949:
1950: nBytesLeft = FileSize-CurrentPos;
1951:
1952: /* Check for bad size and End Of File */
1953: if (Size <= 0 || nBytesLeft <= 0)
1954: {
1955: /* return zero (bytes read) as original GEMDOS/EmuTOS */
1956: Regs[REG_D0] = 0;
1957: return true;
1958: }
1.1.1.9 root 1959:
1.1.1.17 root 1960: /* Limit to size of file to prevent errors */
1961: if (Size > (Uint32)nBytesLeft)
1962: Size = nBytesLeft;
1.1.1.9 root 1963:
1.1.1.17 root 1964: /* Check that read is to valid memory area */
1965: if (!STMemory_ValidArea(Addr, Size))
1966: {
1.1.1.18 root 1967: Log_Printf(LOG_WARN, "GEMDOS Fread() failed due to invalid RAM range 0x%x+%i\n", Addr, Size);
1.1.1.17 root 1968: Regs[REG_D0] = GEMDOS_ERANGE;
1969: return true;
1.1.1.9 root 1970: }
1.1.1.17 root 1971: /* And read data in */
1972: nBytesRead = fread(pBuffer, 1, Size, FileHandles[Handle].FileHandle);
1973:
1974: /* Return number of bytes read */
1975: Regs[REG_D0] = nBytesRead;
1976:
1977: return true;
1.1 root 1978: }
1979:
1.1.1.2 root 1980: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1981: /**
1982: * GEMDOS Write file
1983: * Call 0x40
1984: */
1.1.1.13 root 1985: static bool GemDOS_Write(Uint32 Params)
1.1 root 1986: {
1.1.1.9 root 1987: char *pBuffer;
1.1.1.17 root 1988: long nBytesWritten;
1989: Uint32 Addr;
1990: Sint32 Size;
1.1.1.9 root 1991: int Handle;
1.1 root 1992:
1.1.1.9 root 1993: /* Read details from stack */
1.1.1.17 root 1994: Handle = STMemory_ReadWord(Params);
1995: Size = STMemory_ReadLong(Params+SIZE_WORD);
1996: Addr = STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG);
1997: pBuffer = (char *)STRAM_ADDR(Addr);
1998:
1999: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fwrite(%i, %i, 0x%x)\n",
2000: Handle, Size, Addr);
1.1.1.9 root 2001:
1.1.1.17 root 2002: Handle -= BASE_FILEHANDLE;
1.1.1.15 root 2003:
1.1.1.17 root 2004: /* Check handle was valid */
2005: if (GemDOS_IsInvalidFileHandle(Handle))
1.1.1.9 root 2006: {
1.1.1.17 root 2007: /* assume it was TOS one -> redirect */
2008: return false;
2009: }
1.1.1.9 root 2010:
1.1.1.17 root 2011: /* write protected device? */
2012: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
2013: {
2014: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fwrite(%d,...)\n", Handle);
2015: Regs[REG_D0] = GEMDOS_EWRPRO;
1.1.1.15 root 2016: return true;
1.1.1.9 root 2017: }
1.1 root 2018:
1.1.1.17 root 2019: /* Check that write is from valid memory area */
2020: if (!STMemory_ValidArea(Addr, Size))
2021: {
1.1.1.18 root 2022: Log_Printf(LOG_WARN, "GEMDOS Fwrite() failed due to invalid RAM range 0x%x+%i\n", Addr, Size);
1.1.1.17 root 2023: Regs[REG_D0] = GEMDOS_ERANGE;
2024: return true;
2025: }
2026:
2027: nBytesWritten = fwrite(pBuffer, 1, Size, FileHandles[Handle].FileHandle);
2028: if (ferror(FileHandles[Handle].FileHandle))
2029: {
2030: Log_Printf(LOG_WARN, "GEMDOS failed to write to '%s'\n",
2031: FileHandles[Handle].szActualName );
2032: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied (ie read-only) */
2033: }
2034: else
2035: {
2036: Regs[REG_D0] = nBytesWritten; /* OK */
2037: }
2038: return true;
1.1 root 2039: }
2040:
1.1.1.2 root 2041: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2042: /**
2043: * GEMDOS Delete file
2044: * Call 0x41
2045: */
1.1.1.13 root 2046: static bool GemDOS_FDelete(Uint32 Params)
1.1 root 2047: {
1.1.1.17 root 2048: char *pszFileName, *psActualFileName;
1.1.1.9 root 2049: int Drive;
2050:
2051: /* Find filename */
1.1.1.17 root 2052: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1.1.1.15 root 2053:
2054: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fdelete(\"%s\")\n", pszFileName);
2055:
1.1.1.19! root 2056: Drive = GemDOS_FileName2HardDriveID(pszFileName);
1.1.1.15 root 2057:
1.1.1.17 root 2058: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 2059: {
1.1.1.17 root 2060: /* redirect to TOS */
2061: return false;
2062: }
1.1 root 2063:
1.1.1.17 root 2064: /* write protected device? */
2065: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
2066: {
2067: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fdelete(\"%s\")\n", pszFileName);
2068: Regs[REG_D0] = GEMDOS_EWRPRO;
2069: return true;
2070: }
1.1.1.9 root 2071:
1.1.1.17 root 2072: psActualFileName = malloc(FILENAME_MAX);
2073: if (!psActualFileName)
2074: {
2075: perror("GemDOS_FDelete");
2076: Regs[REG_D0] = GEMDOS_ENSMEM;
1.1.1.15 root 2077: return true;
1.1.1.9 root 2078: }
1.1 root 2079:
1.1.1.17 root 2080: /* And convert to hard drive filename */
2081: GemDOS_CreateHardDriveFileName(Drive, pszFileName, psActualFileName, FILENAME_MAX);
2082:
2083: /* Now delete file?? */
2084: if (unlink(psActualFileName) == 0)
2085: Regs[REG_D0] = GEMDOS_EOK; /* OK */
2086: else
2087: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */
2088:
2089: free(psActualFileName);
2090: return true;
1.1 root 2091: }
2092:
1.1.1.2 root 2093: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2094: /**
2095: * GEMDOS File seek
2096: * Call 0x42
2097: */
1.1.1.13 root 2098: static bool GemDOS_LSeek(Uint32 Params)
1.1 root 2099: {
1.1.1.9 root 2100: long Offset;
1.1.1.15 root 2101: int Handle, Mode;
2102: long nFileSize;
2103: long nOldPos, nDestPos;
2104: FILE *fhndl;
1.1.1.9 root 2105:
2106: /* Read details from stack */
1.1.1.17 root 2107: Offset = (Sint32)STMemory_ReadLong(Params);
2108: Handle = STMemory_ReadWord(Params+SIZE_LONG);
2109: Mode = STMemory_ReadWord(Params+SIZE_LONG+SIZE_WORD);
1.1 root 2110:
1.1.1.15 root 2111: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fseek(%li, %i, %i)\n", Offset, Handle, Mode);
2112:
1.1.1.17 root 2113: Handle -= BASE_FILEHANDLE;
2114:
1.1.1.9 root 2115: /* Check handle was valid */
2116: if (GemDOS_IsInvalidFileHandle(Handle))
2117: {
1.1.1.17 root 2118: /* assume it was TOS one -> redirect */
1.1.1.15 root 2119: return false;
1.1.1.9 root 2120: }
1.1.1.15 root 2121:
2122: fhndl = FileHandles[Handle].FileHandle;
2123:
2124: /* Save old position in file */
2125: nOldPos = ftell(fhndl);
2126:
2127: /* Determine the size of the file */
2128: fseek(fhndl, 0L, SEEK_END);
2129: nFileSize = ftell(fhndl);
2130:
2131: switch (Mode)
2132: {
1.1.1.17 root 2133: case 0: nDestPos = Offset; break; /* positive offset */
1.1.1.15 root 2134: case 1: nDestPos = nOldPos + Offset; break;
1.1.1.17 root 2135: case 2: nDestPos = nFileSize + Offset; break; /* negative offset */
1.1.1.15 root 2136: default:
2137: /* Restore old position and return error */
2138: fseek(fhndl, nOldPos, SEEK_SET);
2139: Regs[REG_D0] = GEMDOS_EINVFN;
2140: return true;
2141: }
2142:
2143: if (nDestPos < 0 || nDestPos > nFileSize)
1.1.1.9 root 2144: {
1.1.1.15 root 2145: /* Restore old position and return error */
2146: fseek(fhndl, nOldPos, SEEK_SET);
2147: Regs[REG_D0] = GEMDOS_ERANGE;
2148: return true;
1.1.1.9 root 2149: }
1.1.1.15 root 2150:
2151: /* Seek to new position and return offset from start of file */
2152: fseek(fhndl, nDestPos, SEEK_SET);
2153: Regs[REG_D0] = ftell(fhndl);
2154:
2155: return true;
1.1 root 2156: }
2157:
1.1.1.10 root 2158:
2159: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2160: /**
1.1.1.17 root 2161: * GEMDOS Fattrib() - get or set file and directory attributes
1.1.1.12 root 2162: * Call 0x43
2163: */
1.1.1.13 root 2164: static bool GemDOS_Fattrib(Uint32 Params)
1.1.1.10 root 2165: {
1.1.1.17 root 2166: /* TODO: host filenames might not fit into this */
1.1.1.11 root 2167: char sActualFileName[MAX_GEMDOS_PATH];
1.1.1.10 root 2168: char *psFileName;
2169: int nDrive;
2170: int nRwFlag, nAttrib;
1.1.1.12 root 2171: struct stat FileStat;
1.1.1.10 root 2172:
2173: /* Find filename */
1.1.1.17 root 2174: psFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
1.1.1.19! root 2175: nDrive = GemDOS_FileName2HardDriveID(psFileName);
1.1.1.10 root 2176:
1.1.1.17 root 2177: nRwFlag = STMemory_ReadWord(Params+SIZE_LONG);
2178: nAttrib = STMemory_ReadWord(Params+SIZE_LONG+SIZE_WORD);
1.1.1.10 root 2179:
1.1.1.15 root 2180: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fattrib(\"%s\", %d, 0x%x)\n",
2181: psFileName, nRwFlag, nAttrib);
1.1.1.10 root 2182:
1.1.1.12 root 2183: if (!ISHARDDRIVE(nDrive))
1.1.1.10 root 2184: {
1.1.1.17 root 2185: /* redirect to TOS */
1.1.1.15 root 2186: return false;
1.1.1.12 root 2187: }
1.1.1.10 root 2188:
1.1.1.12 root 2189: /* Convert to hard drive filename */
2190: GemDOS_CreateHardDriveFileName(nDrive, psFileName,
2191: sActualFileName, sizeof(sActualFileName));
1.1.1.10 root 2192:
1.1.1.12 root 2193: if (nAttrib == GEMDOS_FILE_ATTRIB_VOLUME_LABEL)
2194: {
1.1.1.17 root 2195: Log_Printf(LOG_WARN, "Warning: Hatari doesn't support GEMDOS volume label setting\n(for '%s')\n", sActualFileName);
1.1.1.12 root 2196: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */
1.1.1.15 root 2197: return true;
1.1.1.12 root 2198: }
2199: if (stat(sActualFileName, &FileStat) != 0)
2200: {
2201: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */
1.1.1.15 root 2202: return true;
1.1.1.12 root 2203: }
2204: if (nRwFlag == 0)
2205: {
2206: /* Read attributes */
2207: Regs[REG_D0] = GemDOS_ConvertAttribute(FileStat.st_mode);
1.1.1.15 root 2208: return true;
1.1.1.12 root 2209: }
1.1.1.17 root 2210:
2211: /* write or auto protected device? */
2212: if (ConfigureParams.HardDisk.nWriteProtection != WRITEPROT_OFF)
2213: {
2214: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fattrib(\"%s\",...)\n", psFileName);
2215: Regs[REG_D0] = GEMDOS_EWRPRO;
2216: return true;
2217: }
2218:
2219: if (nAttrib & GEMDOS_FILE_ATTRIB_SUBDIRECTORY)
2220: {
2221: if (!S_ISDIR(FileStat.st_mode))
2222: {
2223: /* file, not dir -> path not found */
2224: Regs[REG_D0] = GEMDOS_EPTHNF;
2225: return true;
2226: }
2227: }
2228: else
2229: {
2230: if (S_ISDIR(FileStat.st_mode))
2231: {
2232: /* dir, not file -> file not found */
2233: Regs[REG_D0] = GEMDOS_EFILNF;
2234: return true;
2235: }
2236: }
2237:
1.1.1.12 root 2238: if (nAttrib & GEMDOS_FILE_ATTRIB_READONLY)
2239: {
2240: /* set read-only (readable by all) */
2241: if (chmod(sActualFileName, S_IRUSR|S_IRGRP|S_IROTH) == 0)
1.1.1.10 root 2242: {
1.1.1.17 root 2243: Regs[REG_D0] = nAttrib;
1.1.1.15 root 2244: return true;
1.1.1.10 root 2245: }
2246: }
1.1.1.17 root 2247: else
2248: {
2249: /* set writable (by user, readable by all) */
2250: if (chmod(sActualFileName, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) == 0)
2251: {
2252: Regs[REG_D0] = nAttrib;
2253: return true;
2254: }
2255: }
2256:
2257: /* FIXME: support hidden/system/archive flags?
2258: * System flag is from DOS, not used by TOS.
2259: * Archive bit is cleared by backup programs
2260: * and set whenever file is written to.
2261: */
1.1.1.12 root 2262: Regs[REG_D0] = GEMDOS_EACCDN; /* Acces denied */
1.1.1.15 root 2263: return true;
1.1.1.10 root 2264: }
2265:
2266:
1.1.1.2 root 2267: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2268: /**
2269: * GEMDOS Get Directory
2270: * Call 0x47
2271: */
1.1.1.18 root 2272: static bool GemDOS_GetDir(Uint32 Params)
1.1.1.9 root 2273: {
1.1.1.10 root 2274: Uint32 Address;
2275: Uint16 Drive;
1.1.1.9 root 2276:
1.1.1.17 root 2277: Address = STMemory_ReadLong(Params);
2278: Drive = STMemory_ReadWord(Params+SIZE_LONG);
1.1.1.15 root 2279:
1.1.1.19! root 2280: /* Note: Drive = 0 means current drive, 1 = A:, 2 = B:, 3 = C:, etc. */
1.1.1.15 root 2281: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Dgetpath(0x%x, %i)\n", Address, (int)Drive);
1.1.1.19! root 2282: if (Drive == 0)
! 2283: Drive = CurrentDrive;
! 2284: else
! 2285: Drive--;
1.1.1.15 root 2286:
1.1.1.9 root 2287: /* is it our drive? */
1.1.1.19! root 2288: if (GemDOS_IsDriveEmulated(Drive))
1.1.1.9 root 2289: {
1.1.1.11 root 2290: char path[MAX_GEMDOS_PATH];
1.1.1.9 root 2291: int i,len,c;
2292:
1.1.1.17 root 2293: *path = '\0';
2294: strncat(path,&emudrives[Drive-2]->fs_currpath[strlen(emudrives[Drive-2]->hd_emulation_dir)], sizeof(path)-1);
1.1.1.15 root 2295:
2296: // convert it to ST path (DOS)
1.1.1.17 root 2297: File_CleanFileName(path);
2298: len = strlen(path);
1.1.1.18 root 2299: /* Check that write is requested to valid memory area */
2300: if (!STMemory_ValidArea(Address, len))
2301: {
2302: Log_Printf(LOG_WARN, "GEMDOS Dgetpath() failed due to invalid RAM range 0x%x+%i\n", Address, len);
2303: Regs[REG_D0] = GEMDOS_ERANGE;
2304: return true;
2305: }
1.1.1.9 root 2306: for (i = 0; i <= len; i++)
2307: {
2308: c = path[i];
1.1.1.12 root 2309: STMemory_WriteByte(Address+i, (c==PATHSEP ? '\\' : c) );
1.1.1.9 root 2310: }
1.1.1.18 root 2311: LOG_TRACE(TRACE_OS_GEMDOS, "-> '%s'\n", (char *)STRAM_ADDR(Address));
1.1.1.15 root 2312:
2313: Regs[REG_D0] = GEMDOS_EOK; /* OK */
1.1.1.9 root 2314:
1.1.1.15 root 2315: return true;
1.1.1.9 root 2316: }
1.1.1.17 root 2317: /* redirect to TOS */
2318: return false;
1.1 root 2319: }
2320:
2321:
1.1.1.2 root 2322: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2323: /**
2324: * GEMDOS PExec handler
2325: * Call 0x4B
2326: */
1.1.1.10 root 2327: static int GemDOS_Pexec(Uint32 Params)
1.1 root 2328: {
1.1.1.17 root 2329: int Drive;
1.1.1.10 root 2330: Uint16 Mode;
1.1.1.17 root 2331: char *pszFileName;
1.1 root 2332:
1.1.1.9 root 2333: /* Find PExec mode */
1.1.1.17 root 2334: Mode = STMemory_ReadWord(Params);
1.1.1.11 root 2335:
1.1.1.15 root 2336: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Pexec(%i, ...)\n", Mode);
2337:
1.1.1.9 root 2338: /* Re-direct as needed */
2339: switch(Mode)
2340: {
2341: case 0: /* Load and go */
2342: case 3: /* Load, don't go */
1.1.1.17 root 2343: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
1.1.1.19! root 2344: Drive = GemDOS_FileName2HardDriveID(pszFileName);
1.1.1.17 root 2345:
2346: /* If not using A: or B:, use my own routines to load */
2347: if (ISHARDDRIVE(Drive))
2348: {
2349: /* Redirect to cart' routine at address 0xFA1000 */
2350: return CALL_PEXEC_ROUTINE;
2351: }
2352: return false;
1.1.1.9 root 2353: case 4: /* Just go */
1.1.1.15 root 2354: return false;
1.1.1.9 root 2355: case 5: /* Create basepage */
1.1.1.15 root 2356: return false;
1.1.1.9 root 2357: case 6:
1.1.1.15 root 2358: return false;
1.1.1.9 root 2359: }
2360:
1.1.1.10 root 2361: /* Default: Still re-direct to TOS */
1.1.1.15 root 2362: return false;
1.1 root 2363: }
2364:
1.1.1.4 root 2365:
2366: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2367: /**
2368: * GEMDOS Search Next
2369: * Call 0x4F
2370: */
1.1.1.13 root 2371: static bool GemDOS_SNext(void)
1.1.1.4 root 2372: {
1.1.1.17 root 2373: struct dirent **temp;
2374: Uint32 nDTA;
1.1.1.9 root 2375: int Index;
1.1.1.13 root 2376: int ret;
1.1.1.9 root 2377:
1.1.1.15 root 2378: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fsnext()\n");
2379:
1.1.1.9 root 2380: /* Refresh pDTA pointer (from the current basepage) */
1.1.1.17 root 2381: nDTA = STMemory_ReadLong(STMemory_ReadLong(act_pd)+32);
2382:
2383: if (!STMemory_ValidArea(nDTA, sizeof(DTA)))
2384: {
2385: pDTA = NULL;
2386: Log_Printf(LOG_WARN, "GEMDOS Fsnext() failed due to invalid DTA address 0x%x\n", nDTA);
2387: Regs[REG_D0] = GEMDOS_EINTRN; /* "internal error */
2388: return true;
2389: }
2390: pDTA = (DTA *)STRAM_ADDR(nDTA);
1.1.1.4 root 2391:
1.1.1.9 root 2392: /* Was DTA ours or TOS? */
1.1.1.17 root 2393: if (do_get_mem_long(pDTA->magic) != DTA_MAGIC_NUMBER)
1.1.1.9 root 2394: {
1.1.1.17 root 2395: /* redirect to TOS */
2396: return false;
2397: }
1.1.1.9 root 2398:
1.1.1.17 root 2399: /* Find index into our list of structures */
2400: Index = do_get_mem_word(pDTA->index) & (MAX_DTAS_FILES-1);
1.1.1.6 root 2401:
1.1.1.17 root 2402: if (nAttrSFirst == GEMDOS_FILE_ATTRIB_VOLUME_LABEL)
2403: {
2404: /* Volume label was given already in Sfirst() */
2405: Regs[REG_D0] = GEMDOS_ENMFIL;
2406: return true;
2407: }
2408:
2409: temp = InternalDTAs[Index].found;
2410: do
2411: {
2412: if (InternalDTAs[Index].centry >= InternalDTAs[Index].nentries)
1.1.1.9 root 2413: {
2414: Regs[REG_D0] = GEMDOS_ENMFIL; /* No more files */
1.1.1.15 root 2415: return true;
1.1.1.9 root 2416: }
2417:
1.1.1.17 root 2418: ret = PopulateDTA(InternalDTAs[Index].path,
2419: temp[InternalDTAs[Index].centry++]);
2420: } while (ret == 1);
1.1.1.4 root 2421:
1.1.1.17 root 2422: if (ret < 0)
2423: {
2424: Log_Printf(LOG_WARN, "GEMDOS Fsnext(): Error setting DTA.\n");
2425: Regs[REG_D0] = GEMDOS_EINTRN;
1.1.1.15 root 2426: return true;
1.1.1.9 root 2427: }
1.1.1.4 root 2428:
1.1.1.17 root 2429: Regs[REG_D0] = GEMDOS_EOK;
2430: return true;
1.1.1.4 root 2431: }
2432:
2433:
1.1.1.2 root 2434: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2435: /**
2436: * GEMDOS Find first file
2437: * Call 0x4E
2438: */
1.1.1.13 root 2439: static bool GemDOS_SFirst(Uint32 Params)
1.1 root 2440: {
1.1.1.17 root 2441: /* TODO: host filenames might not fit into this */
1.1.1.11 root 2442: char szActualFileName[MAX_GEMDOS_PATH];
1.1.1.9 root 2443: char *pszFileName;
1.1.1.17 root 2444: const char *dirmask;
1.1.1.9 root 2445: struct dirent **files;
1.1.1.17 root 2446: Uint32 nDTA;
1.1.1.9 root 2447: int Drive;
2448: DIR *fsdir;
2449: int i,j,count;
2450:
2451: /* Find filename to search for */
1.1.1.17 root 2452: pszFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params));
2453: nAttrSFirst = STMemory_ReadWord(Params+SIZE_LONG);
1.1.1.9 root 2454:
1.1.1.15 root 2455: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fsfirst(\"%s\", 0x%x)\n", pszFileName, nAttrSFirst);
2456:
1.1.1.19! root 2457: Drive = GemDOS_FileName2HardDriveID(pszFileName);
1.1.1.17 root 2458: if (!ISHARDDRIVE(Drive))
1.1.1.9 root 2459: {
1.1.1.17 root 2460: /* redirect to TOS */
2461: return false;
2462: }
1.1.1.9 root 2463:
1.1.1.17 root 2464: /* Convert to hard drive filename */
2465: GemDOS_CreateHardDriveFileName(Drive, pszFileName,
1.1.1.12 root 2466: szActualFileName, sizeof(szActualFileName));
1.1.1.9 root 2467:
1.1.1.17 root 2468: /* Refresh pDTA pointer (from the current basepage) */
2469: nDTA = STMemory_ReadLong(STMemory_ReadLong(act_pd)+32);
1.1.1.9 root 2470:
1.1.1.17 root 2471: if (!STMemory_ValidArea(nDTA, sizeof(DTA)))
2472: {
2473: pDTA = NULL;
2474: Log_Printf(LOG_WARN, "GEMDOS Fsfirst() failed due to invalid DTA address 0x%x\n", nDTA);
2475: Regs[REG_D0] = GEMDOS_EINTRN; /* "internal error */
2476: return true;
2477: }
2478: pDTA = (DTA *)STRAM_ADDR(nDTA);
1.1.1.9 root 2479:
1.1.1.17 root 2480: /* Populate DTA, set index for our use */
2481: do_put_mem_word(pDTA->index, DTAIndex);
2482: /* set our dta magic num */
2483: do_put_mem_long(pDTA->magic, DTA_MAGIC_NUMBER);
1.1.1.9 root 2484:
1.1.1.17 root 2485: if (InternalDTAs[DTAIndex].bUsed == true)
2486: ClearInternalDTA();
2487: InternalDTAs[DTAIndex].bUsed = true;
2488:
2489: /* Were we looking for the volume label? */
2490: if (nAttrSFirst == GEMDOS_FILE_ATTRIB_VOLUME_LABEL)
2491: {
2492: /* Volume name */
2493: strcpy(pDTA->dta_name,"EMULATED.001");
1.1.1.19! root 2494: pDTA->dta_name[11] = '0' + Drive;
1.1.1.17 root 2495: Regs[REG_D0] = GEMDOS_EOK; /* Got volume */
2496: return true;
2497: }
1.1.1.9 root 2498:
1.1.1.17 root 2499: /* open directory
2500: * TODO: host path may not fit into InternalDTA
2501: */
2502: fsfirst_dirname(szActualFileName, InternalDTAs[DTAIndex].path);
2503: fsdir = opendir(InternalDTAs[DTAIndex].path);
2504:
2505: if (fsdir == NULL)
2506: {
2507: Regs[REG_D0] = GEMDOS_EPTHNF; /* Path not found */
2508: return true;
2509: }
2510: /* close directory */
2511: closedir(fsdir);
2512:
2513: count = scandir(InternalDTAs[DTAIndex].path, &files, 0, alphasort);
2514: /* File (directory actually) not found */
2515: if (count < 0)
2516: {
2517: Regs[REG_D0] = GEMDOS_EFILNF;
2518: return true;
2519: }
2520:
2521: InternalDTAs[DTAIndex].centry = 0; /* current entry is 0 */
2522: dirmask = fsfirst_dirmask(szActualFileName);/* directory mask part */
2523: InternalDTAs[DTAIndex].found = files; /* get files */
2524:
2525: /* count & copy the entries that match our mask and discard the rest */
2526: j = 0;
2527: for (i=0; i < count; i++)
2528: {
2529: if (fsfirst_match(dirmask, files[i]->d_name))
1.1.1.13 root 2530: {
1.1.1.17 root 2531: InternalDTAs[DTAIndex].found[j] = files[i];
2532: j++;
1.1.1.13 root 2533: }
1.1.1.17 root 2534: else
1.1.1.9 root 2535: {
1.1.1.17 root 2536: free(files[i]);
2537: files[i] = NULL;
1.1.1.9 root 2538: }
1.1.1.17 root 2539: }
2540: InternalDTAs[DTAIndex].nentries = j; /* set number of legal entries */
1.1.1.9 root 2541:
1.1.1.17 root 2542: /* No files of that match, return error code */
2543: if (j==0)
2544: {
2545: free(files);
2546: InternalDTAs[DTAIndex].found = NULL;
2547: Regs[REG_D0] = GEMDOS_EFILNF; /* File not found */
1.1.1.15 root 2548: return true;
1.1.1.9 root 2549: }
1.1.1.17 root 2550:
2551: /* Scan for first file (SNext uses no parameters) */
2552: GemDOS_SNext();
2553: /* increment DTA index */
2554: DTAIndex++;
2555: DTAIndex&=(MAX_DTAS_FILES-1);
2556:
2557: return true;
1.1 root 2558: }
2559:
2560:
1.1.1.2 root 2561: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2562: /**
2563: * GEMDOS Rename
2564: * Call 0x56
2565: */
1.1.1.13 root 2566: static bool GemDOS_Rename(Uint32 Params)
1.1 root 2567: {
1.1.1.9 root 2568: char *pszNewFileName,*pszOldFileName;
1.1.1.17 root 2569: /* TODO: host filenames might not fit into this */
2570: char szNewActualFileName[MAX_GEMDOS_PATH];
2571: char szOldActualFileName[MAX_GEMDOS_PATH];
1.1.1.9 root 2572: int NewDrive, OldDrive;
2573:
1.1.1.17 root 2574: /* Read details from stack, skip first (dummy) arg */
2575: pszOldFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD));
2576: pszNewFileName = (char *)STRAM_ADDR(STMemory_ReadLong(Params+SIZE_WORD+SIZE_LONG));
1.1.1.9 root 2577:
1.1.1.15 root 2578: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Frename(\"%s\", \"%s\")\n", pszOldFileName, pszNewFileName);
2579:
1.1.1.19! root 2580: NewDrive = GemDOS_FileName2HardDriveID(pszNewFileName);
! 2581: OldDrive = GemDOS_FileName2HardDriveID(pszOldFileName);
1.1.1.17 root 2582: if (!(ISHARDDRIVE(NewDrive) && ISHARDDRIVE(OldDrive)))
1.1.1.9 root 2583: {
1.1.1.17 root 2584: /* redirect to TOS */
2585: return false;
2586: }
1.1.1.9 root 2587:
1.1.1.17 root 2588: /* write protected device? */
2589: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
2590: {
2591: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Frename(\"%s\", \"%s\")\n", pszOldFileName, pszNewFileName);
2592: Regs[REG_D0] = GEMDOS_EWRPRO;
1.1.1.15 root 2593: return true;
1.1.1.9 root 2594: }
1.1 root 2595:
1.1.1.17 root 2596: /* And convert to hard drive filenames */
2597: GemDOS_CreateHardDriveFileName(NewDrive, pszNewFileName,
2598: szNewActualFileName, sizeof(szNewActualFileName));
2599: GemDOS_CreateHardDriveFileName(OldDrive, pszOldFileName,
2600: szOldActualFileName, sizeof(szOldActualFileName));
2601:
2602: /* Rename files */
2603: if ( rename(szOldActualFileName,szNewActualFileName)==0 )
2604: Regs[REG_D0] = GEMDOS_EOK;
2605: else
2606: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */
2607: return true;
1.1 root 2608: }
2609:
1.1.1.15 root 2610:
1.1.1.18 root 2611: /*-----------------------------------------------------------------------*/
2612: /**
2613: * GEMDOS GSDToF
2614: * Call 0x57
2615: */
2616: static bool GemDOS_GSDToF(Uint32 Params)
2617: {
2618: DATETIME DateTime;
2619: Uint32 pBuffer;
2620: int Handle,Flag;
2621:
2622: /* Read details from stack */
2623: pBuffer = STMemory_ReadLong(Params);
2624: Handle = STMemory_ReadWord(Params+SIZE_LONG);
2625: Flag = STMemory_ReadWord(Params+SIZE_LONG+SIZE_WORD);
2626:
2627: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS Fdatime(0x%x, %i, %i)\n", pBuffer,
2628: Handle, Flag);
2629:
2630: Handle -= BASE_FILEHANDLE;
2631:
2632: /* Check handle was valid */
2633: if (GemDOS_IsInvalidFileHandle(Handle))
2634: {
2635: /* No, assume was TOS -> redirect */
2636: return false;
2637: }
2638:
2639: if (Flag == 1)
2640: {
2641: /* write protected device? */
2642: if (ConfigureParams.HardDisk.nWriteProtection == WRITEPROT_ON)
2643: {
2644: Log_Printf(LOG_WARN, "PREVENTED: GEMDOS Fdatime(,%d,)\n", Handle);
2645: Regs[REG_D0] = GEMDOS_EWRPRO;
2646: return true;
2647: }
2648: DateTime.timeword = STMemory_ReadWord(pBuffer);
2649: DateTime.dateword = STMemory_ReadWord(pBuffer+SIZE_WORD);
2650: if (GemDOS_SetFileInformation(Handle, &DateTime) == true)
2651: Regs[REG_D0] = GEMDOS_EOK;
2652: else
2653: Regs[REG_D0] = GEMDOS_EACCDN; /* Access denied */
2654: return true;
2655: }
2656:
2657: if (GemDOS_GetFileInformation(Handle, &DateTime) == true)
2658: {
2659: /* Check that write is requested to valid memory area */
2660: if (STMemory_ValidArea(pBuffer, 4))
2661: {
2662: STMemory_WriteWord(pBuffer, DateTime.timeword);
2663: STMemory_WriteWord(pBuffer+SIZE_WORD, DateTime.dateword);
2664: Regs[REG_D0] = GEMDOS_EOK;
2665: }
2666: else
2667: {
2668: Log_Printf(LOG_WARN, "GEMDOS Fdatime() failed due to invalid RAM range 0x%x+%i\n", pBuffer, 4);
2669: Regs[REG_D0] = GEMDOS_ERANGE;
2670: }
2671: }
2672: else
2673: {
2674: Regs[REG_D0] = GEMDOS_ERROR; /* Generic error */
2675: }
2676: return true;
2677: }
2678:
2679:
1.1.1.17 root 2680: #if ENABLE_TRACING
2681: /*-----------------------------------------------------------------------*/
2682: /**
2683: * Map GEMDOS call opcodes to their names
2684: */
2685: static const char* GemDOS_Opcode2Name(Uint16 opcode)
2686: {
2687: static const char* names[] = {
2688: "Pterm0",
2689: "Cconin",
2690: "Cconout",
2691: "Cauxin",
2692: "Cauxout",
2693: "Cprnout",
2694: "Crawio",
2695: "Crawcin",
2696: "Cnecin",
2697: "Cconws",
2698: "Cconrs",
2699: "Cconis",
1.1.1.18 root 2700: "-", /* 0C */
2701: "-", /* 0D */
1.1.1.17 root 2702: "Dsetdrv",
1.1.1.18 root 2703: "-", /* 0F */
1.1.1.17 root 2704: "Cconos",
2705: "Cprnos",
2706: "Cauxis",
2707: "Cauxos",
2708: "Maddalt",
1.1.1.18 root 2709: "-", /* 15 */
2710: "-", /* 16 */
2711: "-", /* 17 */
2712: "-", /* 18 */
1.1.1.17 root 2713: "Dgetdrv",
2714: "Fsetdta",
1.1.1.18 root 2715: "-", /* 1B */
2716: "-", /* 1C */
2717: "-", /* 1D */
2718: "-", /* 1E */
2719: "-", /* 1F */
1.1.1.17 root 2720: "Super",
1.1.1.18 root 2721: "-", /* 21 */
2722: "-", /* 22 */
2723: "-", /* 23 */
2724: "-", /* 24 */
2725: "-", /* 25 */
2726: "-", /* 26 */
2727: "-", /* 27 */
2728: "-", /* 28 */
2729: "-", /* 29 */
1.1.1.17 root 2730: "Tgetdate",
2731: "Tsetdate",
2732: "Tgettime",
2733: "Tsettime",
1.1.1.18 root 2734: "-", /* 2E */
1.1.1.17 root 2735: "Fgetdta",
2736: "Sversion",
2737: "Ptermres",
1.1.1.18 root 2738: "-", /* 32 */
2739: "-", /* 33 */
2740: "-", /* 34 */
2741: "-", /* 35 */
1.1.1.17 root 2742: "Dfree",
1.1.1.18 root 2743: "-", /* 37 */
2744: "-", /* 38 */
1.1.1.17 root 2745: "Dcreate",
2746: "Ddelete",
2747: "Dsetpath",
2748: "Fcreate",
2749: "Fopen",
2750: "Fclose",
2751: "Fread",
2752: "Fwrite",
2753: "Fdelete",
2754: "Fseek",
2755: "Fattrib",
2756: "Mxalloc",
2757: "Fdup",
2758: "Fforce",
2759: "Dgetpath",
2760: "Malloc",
2761: "Mfree",
2762: "Mshrink",
2763: "Pexec",
2764: "Pterm",
1.1.1.18 root 2765: "-", /* 4D */
1.1.1.17 root 2766: "Fsfirst",
2767: "Fsnext",
1.1.1.18 root 2768: "-", /* 50 */
2769: "-", /* 51 */
2770: "-", /* 52 */
2771: "-", /* 53 */
2772: "-", /* 54 */
2773: "-", /* 55 */
1.1.1.17 root 2774: "Frename",
2775: "Fdatime"
2776: };
2777: if (opcode < ARRAYSIZE(names))
2778: return names[opcode];
1.1.1.18 root 2779: return "MiNT?";
1.1.1.17 root 2780: }
2781:
1.1.1.12 root 2782: /**
1.1.1.18 root 2783: * If bShowOpcodes is true, show GEMDOS call opcode/function name table,
2784: * otherwise GEMDOS HDD emulation information.
1.1.1.12 root 2785: */
1.1.1.18 root 2786: void GemDOS_Info(Uint32 bShowOpcodes)
1.1 root 2787: {
1.1.1.18 root 2788: int i, used;
1.1.1.15 root 2789:
1.1.1.18 root 2790: if (bShowOpcodes)
2791: {
2792: Uint16 opcode;
2793: for (opcode = 0; opcode < 0x5A; )
2794: {
2795: fprintf(stderr, "%02x %-9s",
2796: opcode, GemDOS_Opcode2Name(opcode));
2797: if (++opcode % 6 == 0)
2798: fputs("\n", stderr);
2799: }
2800: return;
2801: }
1.1.1.17 root 2802:
1.1.1.18 root 2803: if (!GEMDOS_EMU_ON)
1.1.1.9 root 2804: {
1.1.1.18 root 2805: fputs("GEMDOS HDD emulation isn't enabled!\n", stderr);
2806: return;
1.1.1.9 root 2807: }
2808:
1.1.1.18 root 2809: fputs("GEMDOS HDD emulation drives:\n", stderr);
2810: for(i = 0; i<MAX_HARDDRIVES; i++)
1.1.1.9 root 2811: {
1.1.1.18 root 2812: if (!emudrives[i])
2813: continue;
2814: fprintf(stderr, "- %c: %s\n",
2815: 'A' + emudrives[i]->drive_number,
2816: emudrives[i]->hd_emulation_dir);
1.1.1.9 root 2817: }
2818:
1.1.1.18 root 2819: fputs("\nInternal Fsfirst() DTAs:\n", stderr);
2820: for(used = i = 0; i < MAX_DTAS_FILES; i++)
1.1.1.9 root 2821: {
1.1.1.18 root 2822: int j, centry, entries;
2823:
2824: if (!InternalDTAs[i].bUsed)
2825: continue;
2826:
2827: fprintf(stderr, "+ %d: %s\n", i, InternalDTAs[i].path);
2828:
2829: centry = InternalDTAs[i].centry;
2830: entries = InternalDTAs[i].nentries;
2831: for (j = 0; j < entries; j++)
2832: {
2833: fprintf(stderr, " - %d: %s%s\n",
2834: j, InternalDTAs[i].found[j]->d_name,
2835: j == centry ? " *" : "");
2836: }
2837: fprintf(stderr, " Fsnext entry = %d.\n", centry);
2838: used++;
1.1.1.9 root 2839: }
1.1.1.18 root 2840: if (!used)
2841: fputs("- None in use.\n", stderr);
2842:
2843: fputs("\nOpen GEMDOS HDD file handles:\n", stderr);
2844: for (used = i = 0; i < MAX_FILE_HANDLES; i++)
1.1.1.17 root 2845: {
1.1.1.18 root 2846: if (!FileHandles[i].bUsed)
2847: continue;
2848: fprintf(stderr, "- %d: %s\n", i, FileHandles[i].szActualName);
2849: used++;
1.1.1.17 root 2850: }
1.1.1.18 root 2851: if (!used)
2852: fputs("- None.\n", stderr);
2853: }
2854:
2855: #else /* !ENABLE_TRACING */
2856: void GemDOS_Info(Uint32 bShowOpcodes)
2857: {
2858: fputs("Hatari isn't configured with ENABLE_TRACING\n", stderr);
1.1 root 2859: }
1.1.1.18 root 2860: #endif /* !ENABLE_TRACING */
1.1 root 2861:
1.1.1.9 root 2862:
1.1 root 2863: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2864: /**
2865: * Run GEMDos call, and re-direct if need to. Used to handle hard disk emulation etc...
2866: * This sets the condition codes (in SR), which are used in the 'cart_asm.s' program to
2867: * decide if we need to run old GEM vector, or PExec or nothing.
1.1.1.15 root 2868: *
1.1.1.12 root 2869: * This method keeps the stack and other states consistant with the original ST which is very important
2870: * for the PExec call and maximum compatibility through-out
2871: */
1.1 root 2872: void GemDOS_OpCode(void)
2873: {
1.1.1.17 root 2874: Uint16 GemDOSCall, CallingSReg;
1.1.1.10 root 2875: Uint32 Params;
1.1.1.18 root 2876: int Finished;
1.1.1.12 root 2877: Uint16 SR;
1.1.1.9 root 2878:
1.1.1.12 root 2879: SR = M68000_GetSR();
1.1.1.9 root 2880:
2881: /* Read SReg from stack to see if parameters are on User or Super stack */
2882: CallingSReg = STMemory_ReadWord(Regs[REG_A7]);
2883: if ((CallingSReg&SR_SUPERMODE)==0) /* Calling from user mode */
2884: Params = regs.usp;
2885: else
2886: {
2887: Params = Regs[REG_A7]+SIZE_WORD+SIZE_LONG; /* super stack */
1.1.1.12 root 2888: if (currprefs.cpu_level > 0)
1.1.1.9 root 2889: Params += SIZE_WORD; /* Skip extra word whe CPU is >=68010 */
2890: }
2891:
2892: /* Default to run TOS GemDos (SR_NEG run Gemdos, SR_ZERO already done, SR_OVERFLOW run own 'Pexec' */
1.1.1.18 root 2893: Finished = false;
1.1.1.9 root 2894: SR &= SR_CLEAR_OVERFLOW;
2895: SR &= SR_CLEAR_ZERO;
2896: SR |= SR_NEG;
1.1 root 2897:
1.1.1.9 root 2898: /* Find pointer to call parameters */
2899: GemDOSCall = STMemory_ReadWord(Params);
1.1.1.17 root 2900: Params += SIZE_WORD;
1.1.1.2 root 2901:
1.1.1.9 root 2902: /* Intercept call */
2903: switch(GemDOSCall)
2904: {
2905: /*
2906: case 0x3:
1.1.1.18 root 2907: Finished = GemDOS_Cauxin(Params);
1.1.1.9 root 2908: break;
2909: */
2910: /*
2911: case 0x4:
1.1.1.18 root 2912: Finished = GemDOS_Cauxout(Params);
1.1.1.9 root 2913: break;
2914: */
1.1.1.13 root 2915: /* direct printing via GEMDOS */
2916: /*
2917: case 0x5:
1.1.1.18 root 2918: Finished = GemDOS_Cprnout(Params);
1.1.1.9 root 2919: break;
1.1.1.13 root 2920: */
1.1.1.9 root 2921: case 0xe:
1.1.1.18 root 2922: Finished = GemDOS_SetDrv(Params);
1.1.1.9 root 2923: break;
1.1.1.13 root 2924: /* Printer status */
2925: /*
2926: case 0x11:
1.1.1.18 root 2927: Finished = GemDOS_Cprnos(Params);
1.1.1.9 root 2928: break;
1.1.1.13 root 2929: */
1.1.1.9 root 2930: /*
2931: case 0x12:
1.1.1.18 root 2932: Finished = GemDOS_Cauxis(Params);
1.1.1.9 root 2933: break;
2934: */
2935: /*
2936: case 0x13:
1.1.1.18 root 2937: Finished = GemDOS_Cauxos(Params);
1.1.1.9 root 2938: break;
2939: */
2940: case 0x1a:
1.1.1.18 root 2941: Finished = GemDOS_SetDTA(Params);
1.1.1.9 root 2942: break;
2943: case 0x36:
1.1.1.18 root 2944: Finished = GemDOS_DFree(Params);
1.1.1.9 root 2945: break;
2946: case 0x39:
1.1.1.18 root 2947: Finished = GemDOS_MkDir(Params);
1.1.1.9 root 2948: break;
2949: case 0x3a:
1.1.1.18 root 2950: Finished = GemDOS_RmDir(Params);
1.1.1.9 root 2951: break;
2952: case 0x3b:
1.1.1.18 root 2953: Finished = GemDOS_ChDir(Params);
1.1.1.9 root 2954: break;
2955: case 0x3c:
1.1.1.18 root 2956: Finished = GemDOS_Create(Params);
1.1.1.9 root 2957: break;
2958: case 0x3d:
1.1.1.18 root 2959: Finished = GemDOS_Open(Params);
1.1.1.9 root 2960: break;
2961: case 0x3e:
1.1.1.18 root 2962: Finished = GemDOS_Close(Params);
1.1.1.9 root 2963: break;
2964: case 0x3f:
1.1.1.18 root 2965: Finished = GemDOS_Read(Params);
1.1.1.9 root 2966: break;
2967: case 0x40:
1.1.1.18 root 2968: Finished = GemDOS_Write(Params);
1.1.1.9 root 2969: break;
2970: case 0x41:
1.1.1.18 root 2971: Finished = GemDOS_FDelete(Params);
1.1.1.9 root 2972: break;
2973: case 0x42:
1.1.1.18 root 2974: Finished = GemDOS_LSeek(Params);
1.1.1.9 root 2975: break;
1.1.1.10 root 2976: case 0x43:
1.1.1.18 root 2977: Finished = GemDOS_Fattrib(Params);
1.1.1.10 root 2978: break;
1.1.1.9 root 2979: case 0x47:
1.1.1.18 root 2980: Finished = GemDOS_GetDir(Params);
1.1.1.9 root 2981: break;
2982: case 0x4b:
1.1.1.18 root 2983: /* Either false or CALL_PEXEC_ROUTINE */
2984: Finished = GemDOS_Pexec(Params);
1.1.1.9 root 2985: break;
2986: case 0x4e:
1.1.1.18 root 2987: Finished = GemDOS_SFirst(Params);
1.1.1.9 root 2988: break;
2989: case 0x4f:
1.1.1.18 root 2990: Finished = GemDOS_SNext();
1.1.1.9 root 2991: break;
2992: case 0x56:
1.1.1.18 root 2993: Finished = GemDOS_Rename(Params);
1.1.1.9 root 2994: break;
2995: case 0x57:
1.1.1.18 root 2996: Finished = GemDOS_GSDToF(Params);
1.1.1.9 root 2997: break;
1.1.1.15 root 2998: default:
1.1.1.17 root 2999: LOG_TRACE(TRACE_OS_GEMDOS, "GEMDOS call 0x%X (%s)\n",
3000: GemDOSCall, GemDOS_Opcode2Name(GemDOSCall));
1.1.1.9 root 3001: }
1.1.1.15 root 3002:
1.1.1.18 root 3003: switch(Finished)
1.1.1.9 root 3004: {
1.1.1.18 root 3005: case true:
1.1.1.13 root 3006: /* skip over branch to pexec to RTE */
1.1.1.9 root 3007: SR |= SR_ZERO;
1.1.1.13 root 3008: /* visualize GemDOS emu HD access? */
3009: switch (GemDOSCall)
3010: {
3011: case 0x36:
3012: case 0x39:
3013: case 0x3a:
3014: case 0x3b:
3015: case 0x3c:
3016: case 0x3d:
3017: case 0x3e:
3018: case 0x3f:
3019: case 0x40:
3020: case 0x41:
3021: case 0x42:
3022: case 0x43:
3023: case 0x47:
3024: case 0x4e:
3025: case 0x4f:
3026: case 0x56:
3027: Statusbar_EnableHDLed();
3028: }
1.1.1.9 root 3029: break;
1.1.1.13 root 3030: case CALL_PEXEC_ROUTINE:
3031: /* branch to pexec, then redirect to old gemdos. */
1.1.1.9 root 3032: SR |= SR_OVERFLOW;
3033: break;
3034: }
1.1 root 3035:
1.1.1.12 root 3036: M68000_SetSR(SR); /* update the flags in the SR register */
1.1 root 3037: }
3038:
1.1.1.9 root 3039:
1.1.1.2 root 3040: /*-----------------------------------------------------------------------*/
1.1.1.12 root 3041: /**
3042: * GemDOS_Boot - routine called on the first occurence of the gemdos opcode.
3043: * (this should be in the cartridge bootrom)
3044: * Sets up our gemdos handler (or, if we don't need one, just turn off keyclicks)
1.1.1.2 root 3045: */
1.1.1.7 root 3046: void GemDOS_Boot(void)
1.1.1.2 root 3047: {
1.1.1.15 root 3048: bInitGemDOS = true;
1.1.1.2 root 3049:
1.1.1.15 root 3050: LOG_TRACE(TRACE_OS_GEMDOS, "Gemdos_Boot()\n" );
1.1.1.9 root 3051:
3052: /* install our gemdos handler, if -e or --harddrive option used */
1.1.1.17 root 3053: if (!GEMDOS_EMU_ON)
3054: return;
3055:
3056: /* Get the address of the p_run variable that points to the actual basepage */
3057: if (TosVersion == 0x100)
1.1.1.9 root 3058: {
1.1.1.17 root 3059: /* We have to use fix addresses on TOS 1.00 :-( */
3060: if ((STMemory_ReadWord(TosAddress+28)>>1) == 4)
3061: act_pd = 0x873c; /* Spanish TOS is different from others! */
1.1.1.9 root 3062: else
1.1.1.17 root 3063: act_pd = 0x602c;
1.1.1.9 root 3064: }
1.1.1.17 root 3065: else
3066: {
3067: act_pd = STMemory_ReadLong(TosAddress + 0x28);
3068: }
3069:
3070: /* Save old GEMDOS handler adress */
3071: STMemory_WriteLong(CART_OLDGEMDOS, STMemory_ReadLong(0x0084));
3072: /* Setup new GEMDOS handler, see "cart_asm.s" */
3073: STMemory_WriteLong(0x0084, CART_GEMDOS);
1.1 root 3074: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.