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

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

unix.superglobalmegacorp.com

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