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

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

unix.superglobalmegacorp.com

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