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

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

unix.superglobalmegacorp.com

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