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