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

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

unix.superglobalmegacorp.com

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