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