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

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

unix.superglobalmegacorp.com

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