diff src/vdisk.c @ 57:2088fd998865

sbc09 directry clean up
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 23 Jul 2018 16:07:12 +0900
parents vdisk.c@4fa2bdb0c457
children 7c6dc25c2b05
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vdisk.c	Mon Jul 23 16:07:12 2018 +0900
@@ -0,0 +1,844 @@
+/********************************************************************
+* Virtual RBF - Random Block File Manager
+*
+*         Shinji KONO  (kono@ie.u-ryukyu.ac.jp)  2018/7/17
+*         GPL v1 license
+*/
+
+#define engine extern
+#include "v09.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+static int vdiskdebug = 0;  //   bit 1 trace, bit 2 filename
+
+
+Byte pmmu[8];  // process dat mmu
+
+extern char *prog ;   // for disass
+#ifdef USE_MMU
+extern Byte * mem0(Byte *iphymem, Word adr, Byte *immu) ;
+//  smem physical address using system mmu
+//  pmem physical address using caller's mmu
+#define smem(a)  mem0(phymem,a,&mem[0x20+IOPAGE])
+#define pmem(a)  mem0(phymem,a,pmmu)
+#else
+#define smem(a)  (&mem[a])
+#define pmem(a)  (&mem[a])
+#endif
+
+#define MAXPDV 256
+
+
+typedef struct pathDesc {
+    char *name;
+    FILE *fp;
+    int mode;
+    int inode ;  // lower 24 bit of unix inode, os9 lsn
+    int num ;
+    int sz ;     // used only for directory
+    char drv ;
+    char use ;
+    char dir;
+    char *fd ;
+    char *dirfp;
+} PathDesc, *PathDescPtr;
+
+static void 
+vdisklog(Word u,PathDesc *pd, Word pdptr, int curdir,FILE *fp) ;
+
+#define MAXVDRV 4
+
+static char *drvRoot[] = { ".",".",".","."};
+static PathDesc pdv[MAXPDV];
+
+/*
+ * us 0 system
+ *    1 caller
+ */
+static inline Word 
+getword(Byte *adr) {
+    Word *padr = (Word*)adr;
+    return ntohs(*padr);
+}
+
+static inline void 
+setword(Byte *adr,Word value) {
+    Word *padr = (Word*)adr;
+    *padr = htons(value);
+}
+
+static int 
+setVdisk(int drv,char *name) {
+    if (drv<0 || drv>=MAXVDRV) return -1;
+    drvRoot[drv] = name;
+    return 0;
+}
+
+static void 
+closepd(PathDesc *pd) {
+    if(pd->fp) fclose(pd->fp) ;
+    pd->dir = 0;
+    pd->use = 0;
+    pd->fp = 0;
+    pd->mode = 0;
+    free(pd->dirfp); pd->dirfp = 0;
+    free(pd->name); pd->name = 0;
+}
+
+/*
+ * keep track current directory ( most recently 256 entry )
+ * too easy approach
+ */
+char *cdt[512];
+static int cdtptr = 0;
+
+Byte
+setcd(char *name) {
+    int len;
+    for(int i=0;i<512;i++) {
+        if (cdt[i] && strcmp(name,cdt[i])==0) return i;
+    }
+    cdtptr &= 0x1ff;
+    if (cdt[cdtptr]) free(cdt[cdtptr]);
+    cdt[cdtptr] = (char*)malloc(len=strlen(name)+1);
+    strcpy(cdt[cdtptr],name);
+    return cdtptr++;
+}
+
+#define MAXPAHTLEN 256
+
+static void
+putOs9str(char *s,int max) {
+    if (s==0) {
+        printf("(null)");
+        return;
+    }
+    while(*s && (*s&0x7f)>=' ' && ((*s&0x80)==0) && --max !=0) {
+        putchar(*s); s++;
+    }
+    if (*s&0x80) putchar(*s&0x7f);
+}
+
+static void
+err(int error) {
+    printf("err %d\n",error);
+}
+
+static char *
+addCurdir(char *name, PathDesc *pd, int curdir) {
+    int ns =0 ;
+    char *n = name;
+if(vdiskdebug&0x2) { printf("addcur \""); putOs9str(name,0); printf("\" cur \""); putOs9str( cdt[curdir],0); printf("\"\n"); }
+    if (name[0]=='/') {
+        name++; while(*name !='/' && *name!=0) name ++ ; // skip /d0
+        while(name[ns]!=0) ns++;
+    } else if (!cdt[curdir] ) return 0; // no current directory
+    else ns = strlen(name);
+    int ps = ns;
+    char *base ; 
+    if (name[0]=='/') { 
+        base = drvRoot[pd->drv]; ps += strlen(drvRoot[pd->drv])+1;  name++;
+    } else if (name[0]==0) { 
+        base = drvRoot[pd->drv];
+        char *path = (char*)malloc(strlen(base)+1);   // we'll free this, malloc it.
+        int i = 0;
+        for(;base[i];i++) path[i] = base[i];
+        path[i]=0;
+        return path;
+    } else { 
+        base = cdt[curdir]; ps += strlen(cdt[curdir])+2; 
+    }
+    char *path = (char*)malloc(ps);
+    int i = 0;
+    for(;base[i];i++) path[i] = base[i];
+    path[i++] = '/';
+    for(int j=0;i<ps;j++,i++) path[i] = name[j];
+    path[i] = 0;
+    if (i>ps)
+        printf("overrun i=%d ps=%d\n",i,ps); // err(__LINE__);
+    return path;
+}
+
+static char * 
+checkFileName(char *path, PathDesc *pd, int curdir) {
+    char *p = path;
+    char *name = path;
+    int maxlen = MAXPAHTLEN;
+if(vdiskdebug&2) { printf("checkf \""); putOs9str(name,0); printf("\"\n"); }
+    while(*p!=0 && (*p&0x80)==0 && (*p&0x7f)>' ' && maxlen-->0) p++;
+    if (maxlen==MAXPAHTLEN) return 0;
+    if (*p) {  // 8th bit termination or non ascii termination
+        int eighth = ((*p&0x80)!=0);
+        name = (char *)malloc(p-path+1+eighth); 
+        int i;
+        for(i=0;i<p-path;i++) name[i] = path[i];
+        if (eighth) { name[i] = path[i]&0x7f; p++ ; i++; }
+        name[i] = 0;
+    }
+    char *name1 = addCurdir(name,pd,curdir);
+    if (name1!=name && name1!=path) free(name);
+    if (name1==0) return 0;
+    pd->name = name1;
+if(vdiskdebug&2) {
+    printf(" remain = \"");
+    char *p1 = p; int max=31;
+    while(*p1 && (*p1&0x80)==0 && max-->0) { if (*p1<0x20)  printf("(0x%02x)",*p1); else  putchar(*p1); p1++; }
+    if (*p1)  { if ((*p1&0x7f)<0x20)  printf("(0x%02x)",*p1); else  putchar(*p1&0x7f); }
+    printf("\" checkname result \""); putOs9str(pd->name,0); printf("\"\n");
+}
+    return p;
+}
+
+static void 
+os9setmode(Byte *os9mode,int mode) {
+    char m = 0;
+    if (mode&S_IFDIR) m|=0x80;
+    if (mode&S_IRUSR) m|=0x01;
+    if (mode&S_IWUSR) m|=0x02;
+    if (mode&S_IXUSR) m|=0x04;
+    if (mode&S_IROTH) m|=0x08;
+    if (mode&S_IWOTH) m|=0x10;
+    if (mode&S_IXOTH) m|=0x20;
+    m|=0x60; // always sharable
+    *os9mode = m;
+}
+
+static char * 
+os9toUnixAttr(Byte attr) {
+    if ((attr&0x1) && (attr&0x2)) return "r+";
+    if (!(attr&0x1) && (attr&0x2)) return "w";
+    if ((attr&0x1) && !(attr&0x2)) return "r";
+    return "r";
+}
+
+/*
+ *   os9 file descriptor
+ *   * File Descriptor Format
+ *
+ * The file descriptor is a sector that is present for every file
+ * on an RBF device.  It contains attributes, modification dates,
+ * and segment information on a file.
+ *
+ *               ORG       0
+ *FD.ATT         RMB       1                   Attributes
+ *FD.OWN         RMB       2                   Owner
+ *FD.DAT         RMB       5                   Date last modified
+ *FD.LNK         RMB       1                   Link count
+ *FD.SIZ         RMB       4                   File size
+ *FD.Creat       RMB       3                   File creation date (YY/MM/DD)
+ *FD.SEG         EQU       .                   Beginning of segment list
+ * Segment List Entry Format
+               ORG       0
+ * FDSL.A         RMB       3                   Segment beginning physical sector number
+ * FDSL.B         RMB       2                   Segment size
+ * FDSL.S         EQU       .                   Segment list entry size
+ * FD.LS1         EQU       FD.SEG+((256-FD.SEG)/FDSL.S-1)*FDSL.S
+ * FD.LS2         EQU       (256/FDSL.S-1)*FDSL.S
+ * MINSEC         SET       16
+ */
+
+#define FD_ATT 0 
+#define FD_OWN 1 
+#define FD_DAT 3 
+#define FD_LNK 8 
+#define FD_SIZ 9 
+#define FD_Creat 13 
+#define FD_SEG 16 
+
+
+/*
+ *   os9 directory structure
+ *
+ *               ORG       0
+ *DIR.NM         RMB       29                  File name
+ *DIR.FD         RMB       3                   File descriptor physical sector number
+ *DIR.SZ         EQU       .                   Directory record size
+ */
+#define DIR_SZ 32
+#define DIR_NM 29
+
+
+/* read direcotry entry */
+static int 
+os9opendir(PathDesc *pd) {
+    DIR *dir;
+    struct dirent *dp;
+    if (pd->dirfp) return 0; // already opened
+    dir = opendir(pd->name);
+    if (dir==0) return -1;
+    int dircount = 0;
+    while ((dp = readdir(dir)) != NULL) dircount++;   // pass 1 to determine the size
+    if (dircount==0) return 0;  // should contains . and .. at least
+    pd->sz = dircount*DIR_SZ;
+    pd->dirfp = (char *)malloc(dircount*DIR_SZ);
+    rewinddir(dir);
+    int i = 0;
+    while ((dp = readdir(dir)) != NULL && dircount-->=0) {
+        int j = 0;
+        for(j = 0; j < DIR_NM ; j++) {
+            if (j< dp->d_namlen)  {
+               pd->dirfp[i+j] = dp->d_name[j]&0x7f;
+               if (j== dp->d_namlen-1)  
+                  pd->dirfp[i+j] |= 0x80;    // os9 EOL
+            } else
+               pd->dirfp[i+j] = 0;
+        }
+        pd->dirfp[i+j] = (dp->d_ino&0xff0000)>>16;
+        pd->dirfp[i+j+1] = (dp->d_ino&0xff00)>>8;
+        pd->dirfp[i+j+2] = dp->d_ino&0xff;
+        i += DIR_SZ;
+        if (i>pd->sz) 
+            return 0;
+    }
+    pd->fp = fmemopen(pd->dirfp,pd->sz,"r");
+    return 0;
+}
+
+static void 
+os9setdate(Byte *d,struct timespec * unixtime) {
+    //   yymmddhhss
+    struct tm r;
+    localtime_r(&unixtime->tv_sec,&r);
+    d[0] = r.tm_year-2048;
+    d[1] = r.tm_mon + 1;
+    d[2] = r.tm_mday;
+    d[3] = r.tm_hour;
+    d[4] = r.tm_min;
+    d[5] = r.tm_sec;
+}
+
+/* read file descriptor of Path Desc 
+ *    create file descriptor sector if necessary
+ *    if buf!=0, copy it 
+ */
+static int 
+filedescriptor(Byte *buf, int len, Byte *name,int curdir) {
+    int err = 0x255;
+    PathDesc pd;  
+    if (len<13) return -1;
+    checkFileName((char*)name,&pd,curdir);
+    struct stat st;
+    if (stat(pd.name,&st)!=0) goto err1;
+    os9setmode(buf+FD_ATT,st.st_mode);
+    buf[FD_OWN]=(st.st_uid&0xff00)>>8;
+    buf[FD_OWN+1]=st.st_uid&0xff;
+    os9setdate(buf+ FD_DAT,&st.st_mtimespec);
+    buf[FD_LNK]=st.st_nlink&0xff;
+    buf[FD_SIZ+0]=(st.st_size&0xff000000)>>24;
+    buf[FD_SIZ+1]=(st.st_size&0xff0000)>>16;
+    buf[FD_SIZ+2]=(st.st_size&0xff00)>>8;
+    buf[FD_SIZ+3]=st.st_size&0xff;
+    os9setdate(buf+FD_Creat,&st.st_ctimespec);
+    err = 0;
+err1:
+    free(pd.name);
+    return err;
+}
+
+/* read direcotry entry for any file in the directory 
+ *     we only returns a file descriptor only in the current opened directory
+ *
+ *     inode==0 should return disk id section
+ *     inode==bitmap should return disk sector map for os9 free command
+ */
+static int 
+fdinfo(Byte *buf,int len, int inode, PathDesc *pd,int curdir) {
+    int i;
+    for(i=0;i<MAXPDV;i++) {
+        PathDesc *pd = pdv+i;
+        if (!pd->use || !pd->dir) continue;
+        //  find inode in directory
+        Byte *dir = (Byte*)pd->dirfp;
+        Byte *end = (Byte*)pd->dirfp + pd->sz;
+        while( dir < end ) {
+            Byte *p = (dir + DIR_NM);
+            int dinode = (p[0]<<16)+(p[1]<<8)+p[2];
+            if (inode == dinode) {
+                return filedescriptor(buf,len,dir,curdir);
+            }
+            dir += 0x20;
+        }
+    }
+    return 255;
+}
+
+void
+getDAT() {
+    Word ps = getword(smem(0x50));      // process structure
+    Byte *dat = smem(ps+0x40);  // process dat (dynamic address translation)
+    for(int i=0; i<8; i++) {
+        pmmu[i] = dat[i*2+1];
+    }
+}
+
+/*
+ *   each command should have preallocated os9 path descriptor on Y
+ *
+ *   name or buffer, can be in a user map, check that drive number ( mem[0x41+IOPAGE]  0 sys 1 user )
+ *   current directory path number                                        mem[0x42+IOPAGE]  
+ *   yreg has pd number
+ */
+void 
+do_vdisk(Byte cmd) {
+    int err;
+    int curdir = mem[0x44+IOPAGE];  // garbage until set
+    Byte attr ;
+    Word u = getword(&mem[0x45+IOPAGE]);   // caller's stack in system segment
+    Byte *frame = smem(u);
+    xreg = getword(frame+4);
+    yreg = getword(frame+6);
+    *areg = *smem(u+1);
+    *breg = *smem(u+2);
+    Byte mode = 0;
+    Byte *os9pd = smem(getword(&mem[0x47+IOPAGE]));
+    PathDesc *pd = pdv+*os9pd;
+
+    getDAT();
+    pd->num = *os9pd;
+    pd->drv = mem[0x41+IOPAGE];
+    char *path,*next,*buf;
+    if (vdiskdebug&1) vdisklog(u,pd,getword(&mem[0x47+IOPAGE]),curdir,stdout);
+
+    switch(cmd) {
+        /*
+        * I$Create Entry Point
+        *
+        * Entry: A = access mode desired
+        *        B = file attributes
+        *        X = address of the pathlist
+        *
+        * Exit:  A = pathnum
+        *        X = last byte of pathlist address
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xd1:
+            mode = *areg;
+            attr = *breg;
+            path = (char *)pmem(xreg);
+            next = pd->name  = checkFileName(path,pd,curdir);
+            pd->dir = 0;
+            pd->fp = fopen(pd->name, os9toUnixAttr(attr));
+            if (next!=0 && pd->fp ) {
+                xreg += ( next - path );
+                pd->use = 1;
+            } else  {
+                *breg = 0xff;
+                free(pd->name);
+                pd->use = 0;
+            }
+            break;
+
+        /*
+        * I$Open Entry Point
+        *
+        * Entry: A = access mode desired
+        *        X = address of the pathlist
+        *
+        * Exit:  A = pathnum
+        *        X = last byte of pathlist address
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xd2:
+            *breg = 0xff;
+            mode = *areg;
+            attr = *breg;
+            pd->fp = 0;
+            path = (char*)pmem(xreg);
+            next = checkFileName(path,pd,curdir);
+            *breg = 0xff;
+            if (next!=0) {
+                struct stat buf;
+                if (stat(pd->name,&buf)!=0) break;
+                if ((buf.st_mode & S_IFMT) == S_IFDIR) {
+                    pd->dir = 1;
+                    os9opendir(pd);
+                } else {
+                    pd->dir = 0;
+                    pd->fp = fopen( pd->name,os9toUnixAttr(mode));
+                }
+                pd->use = 1;
+            }
+            if (next!=0 && pd->fp !=0) {
+                *breg = 0;
+                *areg = pd->num;
+                *smem(u+1) = *areg ;
+                xreg += ( next - path );
+                pd->use = 1;
+            } else {
+                pd->use = 0;
+            }
+            break;
+
+        /*
+        * I$MakDir Entry Point
+        *
+        * Entry: X = address of the pathlist
+        *        B = directory attributes
+        *
+        * Exit:  X = last byte of pathlist address
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+
+        case 0xd3:
+            *breg = 0xff;
+            path = (char*)pmem(xreg);
+            next =  checkFileName(path,pd,curdir);
+            if (next!=0 &&  mkdir(pd->name,0)!= 0 ) {
+                xreg += ( next - path );
+                *breg = 0;
+            } 
+            closepd(pd);
+            break;
+
+        /*
+        * I$ChgDir Entry Point
+        *
+        *    data dir   P$DIO 3-5     contains open dir Path number 
+        *    exec dir   P$DIO 9-11    contains open dir Path number 
+        *
+        * Entry:
+        *
+        * *areg = access mode
+        *    0 = Use any special device capabilities
+        *    1 = Read only
+        *    2 = Write only
+        *    3 = Update (read and write)
+        * Entry: X = address of the pathlist
+        *
+        * Exit:  X = last byte of pathlist address
+        *        A = open directory Path Number
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        *
+        *
+        * we keep track a cwd and a cxd for a process using 8bit id
+        */
+        case 0xd4:
+            path = (char*)pmem(xreg);
+            next = checkFileName(path,pd,curdir);
+            if (next!=0) { 
+                struct stat buf;
+                if (stat(pd->name,&buf)!=0) break;
+                if ((buf.st_mode & S_IFMT) != S_IFDIR) break;
+                xreg += ( next - path );
+                *areg = setcd(pd->name);
+                *smem(u+1) = *areg ;
+                pd->use = 1;
+                pd->dir = 1;
+                *breg = 0;
+                break;
+            } 
+            *breg = 0xff;
+            break;
+
+        /*
+        * I$Delete Entry Point
+        *
+        * Entry:
+        *
+        * Exit:
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xd5: {
+            *breg = 0xff;
+            if (pd==0) break;
+            struct stat st;
+            path = (char*)pmem(xreg);
+            next = checkFileName(path,pd,curdir);
+            pd->use = 0;
+            if (next!=0 && stat(pd->name,&st)!=0) break;
+            if (next!=0 && ((st.st_mode&S_IFDIR)?rmdir(pd->name):unlink(pd->name)) == 0) {
+                xreg += ( next - path );
+                *breg = 0;
+            } 
+         }
+            break;
+
+        /*
+        * I$Seek Entry Point
+        *
+        * Entry Conditions
+        * A path number
+        * X MS 16 bits of the desired file position
+        * U LS 16 bits of the desired file position
+        * 
+        * Exit:
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xd6: {
+            *breg = 0xff;
+            ureg = (*smem(u+8)<<8)+*smem(u+9);
+            off_t seek = (xreg<<16)+ureg;
+            *breg = fseek(pd->fp,(off_t)seek,SEEK_SET);
+            break;
+            }
+        /*
+        * I$ReadLn Entry Point
+        *
+        * Entry:
+        * Entry Conditions     in correct mmu map
+        * A path number
+        * X address at which to store data
+        * Y maximum number of bytes to read
+        *
+        * Exit:
+        *
+        * Y number of bytes read
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        *
+        *
+        */
+        case 0xd7:
+            *breg = 0xff;
+            buf = (char*)pmem(xreg);
+            char *b;
+            if ((b=fgets(buf,yreg,pd->fp))) {
+                if (b==0) {
+                    *breg = 0xd3;
+                    break;
+                }
+                int i;
+                for(i=0;i<yreg && buf[i];i++);
+                if (i>0 && buf[i-1]=='\n') {
+                    buf[i-1] = '\r';
+                    yreg = i;
+                }
+                // set y 
+                setword(smem(u+6),yreg);
+                *breg = 0;
+            } 
+            break;
+
+        /*
+        * I$Read Entry Point
+        *
+        * Entry:
+        *
+        * Exit:
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xd8:
+            *breg = 0xff;
+            buf = (char*)pmem(xreg);
+            int i =  fread(buf,1,yreg,pd->fp);
+            // set y 
+            setword(smem(u+6),i);
+            *breg = (i==0?0xd3:0) ;
+            break;
+
+        /*
+        * I$WritLn Entry Point
+        *
+        * Entry:
+        * A path number
+        * X address of the data to write
+        * Y maximum number of bytes to read
+
+        *
+        * Exit:
+        * Y number of bytes written
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xd9: {
+            *breg = 0xff;
+            if (pd->dir) break;
+            int len = yreg;
+            int i = 0;
+            Byte *buf = pmem(xreg);
+            while(len>0 && *buf !='\r') {
+                fputc(buf[i++],pd->fp);
+                len--;
+            }
+            if (buf[i]=='\r') {
+                fputc('\n',pd->fp);
+                i++;
+            }
+            *breg = 0;
+            // set y 
+            setword(smem(u+6),i);
+            break; 
+         }
+
+        /*
+        * I$Write Entry Point
+        *
+        * Entry:
+        *
+        * Exit:
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xda :
+            *breg = 0xff;
+            if (!pd->dir) {
+                Byte *buf = pmem(xreg);
+                int len = yreg;
+                int err = fwrite(buf,1,len,pd->fp);
+                *breg = err?0:0xff;
+                // set y 
+                setword(smem(u+6),err);
+            }
+            break;
+
+        /* I$Close Entry Point
+        *
+        * Entry: A = path number
+        *
+        * Exit:
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        *
+        */
+        case 0xdb:
+            *breg = 0xff;
+            if (pd==0) break;
+            closepd(pd);
+            *breg = 0;
+            break;
+
+        /*
+        * I$GetStat Entry Point
+        *
+        * Entry:
+        *
+        * Exit:
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xdc: {
+            struct stat st;
+            off_t pos;
+            switch (*breg) {
+                case 01: // SS.Ready
+                    *breg = 0xff;
+                    if (pd==0) break;
+                    fstat(fileno(pd->fp),&st);
+                    if ((pos = ftell(pd->fp))) {
+                        xreg =  st.st_size - pos;
+                        *breg = 0;
+                    }
+                    break;
+                case 02: // SS.SIZ
+                    *breg = 0xff;
+                    if (pd==0) break;
+                    fstat(fileno(pd->fp),&st);
+                    xreg =  st.st_size ;
+                    *breg = 0;
+                    break;
+                case 05: // SS.Pos
+                    *breg = 0xff;
+                    if (pd==0) break;
+                    xreg =  ftell(pd->fp);
+                    *breg = 0;
+                    break;
+                case 15: // SS.FD
+                /*         SS.FD ($0F) - Returns a file descriptor
+                 *                          Entry: R$A=Path #
+                 *                                 R$B=SS.FD ($0F)
+                 *                                 R$X=Pointer to a 256 byte buffer
+                 *                                 R$Y=# bytes of FD required
+                 */
+                    *breg = 0xff;
+                    if (pd==0) break;
+                    *breg = filedescriptor(pmem(xreg), yreg,(Byte*)pd->name,curdir) ;
+                    break;
+                case 0x20: // Pos.FDInf    mandatry for dir command
+                /*         SS.FDInf ($20) - Directly reads a file descriptor from anywhere
+                 *                          on drive.
+                 *                          Entry: R$A=Path #
+                 *                                 R$B=SS.FDInf ($20)
+                 *                                 R$X=Pointer to a 256 byte buffer
+                 *                                 R$Y= MSB - Length of read
+                 *                                      LSB - MSB of logical sector #
+                 *                                 R$U= LSW of logical sector #
+                 */
+                    *breg = 0xff;
+                    if (pd==0) break;
+                    ureg = getword(smem(u+8));
+                    *breg  = fdinfo(pmem(xreg),(yreg&0xff),((yreg&0xff00)>>8)*0x10000+ureg,pd,curdir);
+                    break;
+                default:
+                *breg = 0xff;
+            }
+            break;
+          }
+
+        /*
+        * I$SetStat Entry Point
+        *
+        * Entry:
+        *
+        * Exit:
+        *
+        *
+        * Error: CC Carry set
+        *        B = errcode
+        */
+        case 0xdd:
+            switch (*breg) {
+                case 0: // SS.Opt
+                case 02: // SS.SIZ
+                case 15: // SS.FD
+                case 0x11: // SS.Lock
+                case 0x10: // SS.Ticks
+                case 0x20: // SS.RsBit
+                case 0x1c: // SS.Attr
+                default: 
+                    *breg = 0xff;
+            }
+            break;
+    }
+    if (vdiskdebug && *breg) printf("  vdisk call error %x\n",*breg);
+    // return value
+    mem[0x40+IOPAGE] = *breg;
+    *smem(u+2) = *breg ;
+    setword(smem(u+4),xreg);
+}
+
+static void 
+vdisklog(Word u,PathDesc *pd, Word pdptr, int curdir, FILE *fp) {
+    char *cd = cdt[curdir]?cdt[curdir]:"(null)";
+    fprintf(fp,"pd %d 0x%x cd[%d]=%s ",pd->num, pdptr, curdir,cd);
+    Byte *frame = smem(u);
+    sreg  = u;
+    ccreg = frame[0];
+    *areg = frame[1];
+    *breg = frame[2];
+    xreg  = getword(frame+4);
+    yreg  = getword(frame+6);
+    ureg  = getword(frame+8);
+    pcreg  = getword(frame+10)-3;
+    prog = (char*)(pmem(pcreg) - pcreg);
+    do_trace(fp);
+}
+
+
+/* end */