Annotation of hatari/src/gemdos.c, revision 1.1.1.26

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.