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

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

unix.superglobalmegacorp.com

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