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

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

unix.superglobalmegacorp.com

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