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