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