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