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