home *** CD-ROM | disk | FTP | other *** search
/ ftp.whtech.com / ftp.whtech.com.7z / ftp.whtech.com / emulators / v9t9 / linux / sources / V9t9 / source / Modules / dsr_emudisk.c < prev    next >
Encoding:
C/C++ Source or Header  |  2006-10-19  |  67.0 KB  |  2,951 lines

  1. #include <fcntl.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4.  
  5. #include "v9t9_common.h"
  6. #include "v9t9.h"
  7. #include "roms.h"
  8. #include "cru.h"
  9. #include "command.h"
  10. #include "memory.h"
  11. #include "dsr.h"
  12. #include "vdp.h"
  13. #include "9900.h"
  14. #include "moduleconfig.h"
  15. #include "v9t9.h"
  16. #include "timer.h"
  17.  
  18. #include "pab.h"
  19. #include "fiad.h"
  20.  
  21. #define _L     LOG_EMUDISK | LOG_INFO
  22. #define _LL     LOG_EMUDISK | LOG_INFO | LOG_USER
  23.  
  24. #define EMUDISKCRUBASE    0x1000
  25.  
  26. /******************************************/
  27.  
  28. #define VDP_PTR(x)    FLAT_MEMORY_PTR(md_video, (x)&0x3fff)
  29. #define VDP_ADDR(p)    ((p) - VDP_PTR(0))
  30.  
  31. /*    These are the codes we add to OP_DSR to specify
  32.     which DSR function we're emulating. */
  33.  
  34. /*    If these numbers change, fix DSR ROM files */
  35. enum {
  36.     /* this first group doubles as device codes */
  37.     D_DSK = 0,                // standard file operation on DSK.XXXX.[YYYY]
  38.     D_DSK1 = 1,            // standard file operation on DSK1.[YYYY]
  39.     D_DSK2 = 2,            // ...
  40.     D_DSK3 = 3,            // ...
  41.     D_DSK4 = 4,            // ...
  42.     D_DSK5 = 5,            // ...
  43.  
  44.     D_INIT = 6,            // initialize disk DSR
  45.     D_DSKSUB = 7,            // subroutines
  46.  
  47.     D_SECRW = 7,            // sector read/write    (10)
  48.     D_FMTDISK = 8,            // format disk          (11)
  49.     D_PROT = 9,            // file protection      (12)
  50.     D_RENAME = 10,            // rename file          (13)
  51.     D_DINPUT = 11,            // direct input file    (14)
  52.     D_DOUTPUT = 12,        // direct output file   (15)
  53.     D_16 = 13,                // um... I forgot       (16)
  54.  
  55.     D_FILES = 14            // setup # files (CALL FILES)
  56. };
  57.  
  58.  
  59. /*    Error codes for subroutines */
  60. enum {
  61.     es_okay = 0,
  62.     es_outofspace = 0x4,
  63.     es_cantopenfile = 0x1,
  64.     es_filenotfound = 0x1,
  65.     es_badfuncerr = 0x7,    // format
  66.     es_fileexists = 0x7,    // rename
  67.     es_badvalerr = 0x1,
  68.     es_hardware = 0x6        // ??? made this up
  69. };
  70.  
  71.  
  72. #define    MAXFILES    9            /* max # open files */
  73. #define    MAXDRIVE    5            /* max drive number */
  74.  
  75. /******************************************/
  76.  
  77. char        emudiskfilename[OS_NAMESIZE] = "emudisk.bin";
  78. char        emu2diskfilename[OS_NAMESIZE] = "emu2disk.bin";
  79.  
  80. u8          emudiskdsr[8192];
  81. OSPathSpec  emudiskpath[5];
  82.  
  83. int         keepfileformat = 1;
  84. int         newfileformat = F_V9t9;
  85. int         unknownfileistext = 0;
  86. int            repairbadfiles = 0;
  87. int            fixupoldv9t9filenames = 1;
  88. int            generateoldv9t9filenames = 0;
  89. int         allowlongcatalogs = 0;
  90.  
  91. u16         vdpnamebuffer;    // location of VDP name buffer (for compatibility)
  92.  
  93. /*    Info maintained for open files  */
  94. typedef struct fiad_pabfile
  95. {
  96.     u8            *fnptr;        // pointer to filename part of PAB in VDP RAM,
  97.                             // the key for distinguishing open files
  98.     pabrec        pab;        // copy of PAB used for local operations
  99.     fiad_tifile    tf;            // info about open file
  100.  
  101.     bool        is_catalog;    // does pab represent catalog?
  102. }    fiad_pabfile;
  103.  
  104. /*    Our array of open files */
  105. static fiad_pabfile files[MAXFILES];
  106.  
  107. /*    Our disk catalogs (via DSKx. or sector access) */
  108. static fiad_catalog DskCat[MAXDRIVE];
  109. /*    Flags set by timer, used to throttle directory reads */
  110. static bool DskCatFlag[MAXDRIVE];
  111. static int DskCatTag[MAXDRIVE];
  112.  
  113. // last sector read on each drive, used for catalog --
  114. // = if we read a sector beyond the catalog limit
  115. // or re-read sectors 0 or 1, we force a reread of catalog
  116. // from local disk
  117. // = set to 0xffff when known invalid
  118.  
  119. static u16 last_sector_read[MAXDRIVE];
  120.  
  121.  
  122. /*    Translations for DSKx. files' catalog function
  123.     from FDR type to catalog value (pairs) */
  124. u8          DrcTrans[5][2] = { 
  125.     {0, 1}, {ff_program, 5},
  126.     {ff_internal, 3}, {ff_variable, 2},
  127.     {ff_variable + ff_internal, 4}
  128. };
  129.  
  130. /*    Dirty the VDP where we messed around */
  131. static void
  132. VDPUpdate(u16 addr, u16 len)
  133. {
  134.     while (len--)
  135.         vdp_touch(addr++);
  136. }
  137.  
  138. /*    Copy data safely to VDP */
  139. static      u16
  140. VDPCopy(u16 addr, u8 * data, u32 len)
  141. {
  142.     while (len--) {
  143.         domain_write_byte(md_video, addr++, *data++);
  144.     }
  145.     return addr;
  146. }
  147.  
  148. /*    Copy data safely from VDP */
  149. static      u16
  150. VDPRead(u8 * data, u16 addr, u32 len)
  151. {
  152.     while (len--) {
  153.         *data++ = domain_read_byte(md_video, addr++);
  154.     }
  155.     return addr;
  156. }
  157.  
  158.  
  159.  
  160. ////////////////////////////////////
  161.  
  162. /*
  163.   Set error directly to PAB in VDP; this allows short-circuit
  164.   subroutine exit without storing PAB back to VDP.
  165.  */
  166. static void
  167. pab_set_vdp_error(u8 *fnptr, u8 paberror)
  168. {
  169.     u16 addr = VDP_ADDR(fnptr) - 9 + 1;
  170.     u8 byt = domain_read_byte(md_video, addr);
  171.     domain_write_byte(md_video, addr, (byt & ~m_error) | (paberror & m_error));
  172. }
  173.  
  174. /*
  175.   Set error in pabfile's PAB.
  176.  */
  177. static void
  178. pab_set_error(fiad_pabfile *pf, u8 paberror)
  179. {
  180.     pab_set_vdp_error(pf->fnptr, paberror);
  181.     pf->pab.pflags = (pf->pab.pflags & ~m_error) | (paberror & m_error);
  182. }
  183.  
  184. static void
  185. pab_vdp_error(u8 *fn, OSError err, char *str, u8 paberror)
  186. {
  187.     pab_set_vdp_error(fn, paberror);
  188.     if (paberror) {
  189.         logger(_L | L_1, "FIAD server: %.*s: got PAB error %d [%s (%s)]\n", 
  190.                *fn, fn+1,
  191.                paberror >> 5,
  192.                str, err ? OS_GetErrText(err) : "");
  193.     }
  194. }
  195.  
  196. static void
  197. pab_error(fiad_pabfile *pf, OSError err, char *str, u8 paberror)
  198. {
  199.     pab_set_error(pf, paberror);
  200.     pab_vdp_error(pf->fnptr, err, str, paberror);
  201. }
  202.  
  203. /*
  204.   Match an open pab file.
  205.  
  206.   We distinguish open files by their fnptr being set.
  207. */
  208. static fiad_pabfile *
  209. pab_find_open_file(u8 *fn)
  210. {
  211.     fiad_pabfile     *pf = files;
  212.  
  213.     while (pf < files + MAXFILES) {
  214.         if (pf->fnptr == fn && (pf->tf.open || pf->is_catalog))
  215.             return pf;
  216.         pf++;
  217.     }
  218.     return NULL;
  219. }
  220.  
  221. /*
  222.   Allocate a new pab file.
  223. */
  224. static fiad_pabfile *
  225. pab_get_new_file(u8 *fn)
  226. {
  227.     fiad_pabfile    *pf = files;
  228.  
  229.     while (pf < files + MAXFILES) {
  230.         if (pf->fnptr == fn) {
  231.             // already open, close it
  232.             if (pf->tf.open) {
  233.                 logger(_L|LOG_ERROR, "%.*s:  existing PAB not closed\n",
  234.                        *fn, fn+1);
  235.                 fiad_tifile_close_file(&pf->tf);
  236.             }
  237.             pf->is_catalog = false;
  238.             return pf;
  239.         } else if (pf->fnptr == NULL) {
  240.             pf->fnptr = fn;
  241.             pf->is_catalog = false;
  242.             fiad_tifile_clear(&pf->tf);
  243.             return pf;
  244.         }
  245.         pf++;
  246.     }
  247.     return NULL;
  248. }
  249.  
  250. /*
  251.  *    Copy PAB from VDP into file struct before a file operation
  252.  *
  253.  *    If opening is true, allow flags/reclen to be updated
  254.  */
  255. static void
  256. pab_fetch_from_vdp(fiad_pabfile *pf, bool opening)
  257. {
  258.     pabrec mypab;
  259.     VDPRead((void *)&mypab, VDP_ADDR(pf->fnptr-9), sizeof(pabrec));
  260.  
  261.     pf->pab.opcode = mypab.opcode;
  262.     pf->pab.addr = TI2HOST(mypab.addr);
  263.     pf->pab.charcount = mypab.charcount;
  264.     pf->pab.recnum = TI2HOST(mypab.recnum);
  265.     pf->pab.scrnoffs = mypab.scrnoffs;
  266.     pf->pab.namelen = mypab.namelen;
  267.  
  268.     /* 
  269.        TI BASIC appears to trash these bytes after
  270.        opening the file.  We stubbornly continue to
  271.        use them, however, so don't reread from the
  272.        real PAB after opening. 
  273.     */
  274.     if (opening)
  275.     {
  276.         pf->pab.pflags = mypab.pflags;
  277.         pf->pab.preclen = mypab.preclen;
  278.  
  279.         logger(_L | L_3, "PAB contents: flags=>%02X, reclen=%d, addr=>%04X, charcount=%d, recnum=%d\n",
  280.                pf->pab.pflags, pf->pab.preclen,
  281.                pf->pab.addr, pf->pab.charcount, pf->pab.recnum);
  282.     }
  283.     else
  284.     {
  285.         logger(_L | L_3, "PAB contents: addr=>%04X, charcount=%d, recnum=%d\n",
  286.                pf->pab.addr, pf->pab.charcount, pf->pab.recnum);
  287.  
  288.     }
  289. }
  290.  
  291. /*
  292.  *    Copy PAB from file struct into VDP after a file operation
  293.  */
  294. static void
  295. pab_store_to_vdp(fiad_pabfile *pf)
  296. {
  297.     pabrec mypab;
  298.  
  299.     mypab.opcode = pf->pab.opcode;
  300.     mypab.addr = HOST2TI(pf->pab.addr);
  301.     mypab.charcount = pf->pab.charcount;
  302.     mypab.recnum = HOST2TI(pf->pab.recnum);
  303.     mypab.scrnoffs = pf->pab.scrnoffs;
  304.     mypab.namelen =  pf->pab.namelen;
  305.  
  306.     mypab.pflags = pf->pab.pflags;
  307.     mypab.preclen = pf->pab.preclen;
  308.  
  309.     VDPCopy(VDP_ADDR(pf->fnptr-9), 
  310.             (u8 *)&mypab, 
  311.             sizeof(pabrec));
  312. }
  313.  
  314. ///////////////
  315.  
  316. #if 0
  317. static u8   sub_error_map[] = {
  318.     es_outofspace,                // E_nobuffer
  319.     es_hardware,                // E_cantmakespec [disk paths are bad]
  320.     es_filenotfound,            // E_filenotfound
  321.     es_badvalerr,                // E_cantmodifyfile
  322.     es_hardware,                // E_formaterror
  323.     es_hardware,                // E_shortread  [should find data]
  324.     es_outofspace,                // E_shortwrite
  325.     es_hardware,                // E_sectornotfound [should find data]
  326.     es_hardware,                // E_unexpectederror [whatever]
  327.     es_badfuncerr,                // E_illegalop
  328.     es_hardware                    // E_endoffile
  329. };
  330. #endif
  331.  
  332. static void
  333. sub_set_error(u8 errcode)
  334. {
  335.     memory_write_byte(0x8350, errcode);
  336.     if (errcode) {
  337.         logger(_L | L_1, "FIAD server: got subroutine error %d\n",
  338.               errcode);
  339.     }
  340. }
  341.  
  342. static void
  343. sub_error(fiad_tifile * tf, OSError err, char *str, int errcode)
  344. {
  345.     sub_set_error( errcode);
  346.     if (errcode) {
  347.         logger(_L | L_1, "FIAD server: got subroutine error %d [%s (%s)]\n",
  348.               errcode, str, err ? OS_GetErrText(err) : "");
  349.         fiad_tifile_close_file(tf);
  350.     }
  351. }
  352.  
  353. /*    Close a PAB file */
  354. static void
  355. pab_close_file(fiad_pabfile *pf)
  356. {
  357.     // not true for catalogs
  358.     if (pf->tf.open) {
  359.         fiad_tifile_close_file(&pf->tf);
  360.     }
  361.     pf->fnptr = 0L;
  362. }
  363.  
  364. /*    Close all files */
  365. static void
  366. pab_close_all_files(void)
  367. {
  368.     fiad_pabfile *pf = files;
  369.     int dsk;
  370.  
  371.     while (pf < files + MAXFILES) {
  372.         pab_close_file(pf);
  373.         pf++;
  374.     }
  375.  
  376.     for (dsk = 0; dsk < MAXDRIVE; dsk++) {
  377.         last_sector_read[dsk] = 0xffff;
  378.     }
  379. }
  380.  
  381.  
  382. /////////////////////////////////////
  383.  
  384. /*    Read the FDR from a file,
  385.     return 0 if it's bad. */
  386. static int
  387. pab_read_fdr(fiad_pabfile * pf)
  388. {
  389.     int    ret = fiad_tifile_read_fdr(&pf->tf);
  390.     if (!ret) {
  391.         pab_set_error(pf, e_hardwarefailure);
  392.     }
  393.     return ret;
  394. }
  395.  
  396. /*    Write FDR to file, 
  397.     return 0 and report error if error. */
  398. static int
  399. pab_write_fdr(fiad_pabfile * pf)
  400. {
  401.     int ret = fiad_tifile_write_fdr(&pf->tf);
  402.     
  403.     if (!ret) {
  404.         pab_set_error(pf, e_hardwarefailure);
  405.     }
  406.     return ret;
  407. }
  408.  
  409. /*
  410.     Compare the FDR with the PAB, making sure the
  411.     PAB operation is compatile with the FDR, and that record sizes and
  412.     file types match.  
  413.  
  414.     If mismatching, report the error and return 0, else return 1. 
  415. */
  416. static int
  417. pab_compare_fdr_and_pab(fiad_pabfile * pf)
  418. {
  419.     u8          pflags, fflags;
  420.     char       *pptr;
  421.     u8          len;
  422.     pabrec        *pab = &pf->pab;
  423.     fiad_tifile    *tf = &pf->tf;
  424.  
  425.     /* compare name */
  426.     len = fiad_filename_strlen(tf->fdr.filenam);
  427.  
  428.     pptr = (char *) pf->fnptr + pab->namelen;
  429.     while (pptr > (char *) pf->fnptr && *(pptr - 1) != '.')
  430.         pptr--;
  431.  
  432.     if (strncasecmp(tf->fdr.filenam, pptr, len))
  433.         logger(_LL | LOG_WARN,
  434.               "FIAD server: filename in FDR doesn't match filename ('%.*s' | '%.*s')\n",
  435.               len, tf->fdr.filenam, pab->namelen - (pptr - (char *)pf->fnptr), pptr);
  436.  
  437.     pflags = pab->pflags;
  438.     fflags = tf->fdr.flags;
  439.  
  440.     /* program files are easy */
  441.     if (pab->opcode == f_load || pab->opcode == f_save)
  442.         if (fflags & ff_program)
  443.             return 1;
  444.  
  445.     /* both must be fixed or variable */
  446.     if (!!(pflags & fp_variable) == !!(fflags & ff_variable)) {
  447.         /*  fixup the PAB if it doesn't know record size */
  448.         if (!pab->preclen)
  449.             pab->preclen = tf->fdr.reclen;
  450.  
  451.         /* both must have same record length */
  452.         if (pab->preclen != tf->fdr.reclen) {
  453.             logger(_L | L_1, "DSKcomparefdrandpab:  record length differs\n");
  454.         } else {
  455.             /* and no "var,relative" files */
  456.             if ((pflags & (fp_relative | fp_variable)) ==
  457.                 (fp_relative | fp_variable)) {
  458.                 logger(_L | L_1, "DSKcomparefdrandpab:  var + relative file\n");
  459.             } else
  460.                 return 1;
  461.         }
  462.     } else {
  463.         logger(_L | L_1, "DSKcomparefdrandpab: fixed/variable flag differs\n");
  464.     }
  465.  
  466.     pab_error(pf, 0, "file type mismatch on file open", e_badfiletype);
  467.     return 0;
  468. }
  469.  
  470. /*    Compare the FDR with the ten-byte filename at fptr,
  471.     making sure the filename matches.  Always return 1. */
  472. static int
  473. DSKcomparefdrandsub(fiad_tifile * tf, char *fptr)
  474. {
  475.     if (tf->format == F_V9t9 && strncasecmp(tf->fdr.filenam, fptr, 10)) {
  476.         logger(_LL,
  477.              "DSKcomparefdrandsub:  filenames don't match ('%.*s' | '%.*s')\n",
  478.              10, tf->fdr.filenam, 10, fptr);
  479.     }
  480.     return 1;
  481. }
  482.  
  483. /*
  484.     Create an FDR from a PAB.
  485.     The filename is in the FDR already.
  486. */
  487. static void
  488. pab_make_fdr(fiad_pabfile * pf)
  489. {
  490.     fiad_tifile *tf = &pf->tf;
  491.     pabrec *pab = &pf->pab;
  492.  
  493.     fiad_fdr_setup(&tf->fdr, 
  494.                    pab->opcode == f_save, 
  495.                    ((pab->pflags & fp_variable) ? ff_variable : 0) |
  496.                    ((pab->pflags & fp_internal) ? ff_internal : 0),
  497.                    pab->preclen,
  498.                    0 /*size*/);
  499.  
  500.     pab->preclen = tf->fdr.reclen;
  501. }
  502.  
  503. /*    Read a sector from the PAB file */
  504. static int
  505. pab_read_sector(fiad_pabfile * pf)
  506. {
  507.     int ret = fiad_tifile_read_sector(&pf->tf);
  508.     if (!ret) {
  509.         char msg[64];
  510.         sprintf(msg, "reading sector %d", pf->tf.cursec);
  511.         pab_error(pf, pf->tf.error, msg, e_hardwarefailure);
  512.     }
  513.     return ret;
  514. }
  515.  
  516. /*    Write current sector to PAB file */
  517. static int
  518. pab_write_sector(fiad_pabfile * pf)
  519. {
  520.     int ret = fiad_tifile_write_sector(&pf->tf);
  521.     if (!ret) {
  522.         char msg[64];
  523.         sprintf(msg, "writing sector %d", pf->tf.cursec);
  524.         pab_error(pf, 0, msg, e_hardwarefailure);
  525.     }
  526.     return ret;
  527. }
  528.  
  529. ////////////////////
  530.  
  531. /*    
  532.     Routine to create the OSSpec for the file
  533.     using the PAB and the current disk, and copy the
  534.     name into the file's FDR.
  535.     
  536.     Return 0 for success,
  537.         1 if the filename is empty, which indicates
  538.             a catalog request on a disk, or
  539.         -1 if the path was invalid.
  540. */
  541. static int
  542. pab_setup_file(fiad_pabfile *pf, u8 dev, u8 *fn)
  543. {
  544.     /*  0x8356 holds a pointer into VDP RAM to the end
  545.        of the device name (RS232|, DSK|., DSK1|.ed) */
  546.     u8          len;
  547.     u16         fnaddr;
  548.     char       *fname;
  549.     OSError     err;
  550.  
  551.     pf->fnptr = fn;
  552.     fiad_tifile_clear(&pf->tf);
  553.  
  554.     len = *fn;                    /* length of device+filename */
  555.     logger(_L | L_2, "getfilespec_pab:  pab len = %d\n", len);
  556.     fnaddr = memory_read_word(0x8356) + 1;    /* addr of filename (skip period) */
  557.     logger(_L | L_2, "getfilespec_pab:  fnaddr++ at 0x8356 = %20.20s\n",
  558.          VDP_PTR(0) + fnaddr);
  559.     len -= memory_read_word(0x8354) + 1;    /* minus length of device + period */
  560.     logger(_L | L_2, "getfilespec_pab:  length of device 0x8354 = %04X\n",
  561.          memory_read_word(0x8354));
  562.     fname = (char *) VDP_PTR(fnaddr);
  563.  
  564.     if (memchr(fname, '.', len) != NULL) {
  565.         pab_error(pf, 0, "bad characters in filename", e_badfiletype);
  566.         return -1;
  567.     }
  568.  
  569.     err = fiad_tifile_setup_spec_with_file(&pf->tf, 
  570.                                            &emudiskpath[dev - 1], 
  571.                                            fname, 
  572.                                            len);
  573.  
  574.     if (err != OS_NOERR) {
  575.         pab_error(pf, err, "couldn't make spec", e_badfiletype);
  576.         return -1;
  577.     } else if (len == 0)
  578.         return 1;                /* no filename means catalog */
  579.     else
  580.         return 0;
  581. }
  582.  
  583. /*    Create the host OSSpec for the ten-character filename,
  584.     as seen in a subroutine call. */
  585. static int
  586. sub_setup_file(fiad_tifile *tf, u8 dsknum, u16 fnaddr)
  587. {
  588.     u8          len = 0;
  589.     const char    *chptr = (char *)VDP_PTR(fnaddr);
  590.  
  591.     while (len < 10 && (*chptr != ' ' &&
  592.                         *chptr != 0 &&
  593.                         *chptr != '.')) {
  594.         len++;
  595.         chptr++;
  596.     }
  597.  
  598.     // a dot is illegal
  599.     if (*chptr == '.') {
  600.         sub_error(tf, 0, "bad characters in filename", es_filenotfound);
  601.         return -1;
  602.     }
  603.  
  604.     if (fiad_filename_to_spec(&emudiskpath[dsknum - 1], 
  605.                               (char *) VDP_PTR(fnaddr), 
  606.                               len,
  607.                               &tf->spec) != OS_NOERR) 
  608.         return -1;
  609.     else if (len == 0)
  610.         return 1;                /* no filename */
  611.     else
  612.         return 0;
  613. }
  614.  
  615. ////////////////////////////////////
  616.  
  617. /*
  618.     Match a emulated disk by volume name.  
  619.     Return length of volume plus dot, and disk device (>0) or 0 if not matched.  
  620.     We're passed a name terminated by a period, space, or 0. 
  621. */
  622. static u8
  623. matchdrive(char *volume, int *len)
  624. {
  625.     u8          dev;
  626.  
  627.     *len = 1;
  628.     while (volume[*len - 1] != '.' &&
  629.            volume[*len - 1] != ' ' && volume[*len - 1] != 0)
  630.         (*len)++;
  631.  
  632.     /*  we don't match DSK1 and DSK2 if
  633.        the real disk is going */
  634.     dev = dsr_is_emu_disk(1) ? 1 : 3;
  635.  
  636.     while (dev <= MAXDRIVE) {
  637.         char        path[OS_PATHSIZE];
  638.         char       *ptr;
  639.         char        tivol[10];
  640.         int         len;
  641.         int         idx;
  642.         int         matched;
  643.  
  644.         OS_PathSpecToString2(&emudiskpath[dev - 1], path);
  645.  
  646.         logger(_L | L_1, "trying to match '%s'\n", path);
  647.  
  648.         /* clear trailing slash, colon, etc */
  649.         path[strlen(path) - 1] = 0;
  650.  
  651.         /* ptr has leaf name */
  652.         ptr = (char *) OS_GetFileNamePtr(path);
  653.  
  654.         len = fiad_filename_host2ti(ptr, tivol);
  655.         VDPCopy(vdpnamebuffer, (u8 *)tivol, 10);
  656.  
  657.         /* compare */
  658.         idx = 0;
  659.         matched = 1;
  660.         while (idx < 10 && volume[idx] &&
  661.                volume[idx] != '.' && volume[idx] != 0) {
  662.             /*  don't be picky about capitalization,
  663.                since these things are hard to figure out ;) */
  664.             if (tolower(volume[idx]) != tolower(tivol[idx])) {
  665.                 matched = 0;
  666.                 break;
  667.             }
  668.             idx++;
  669.         }
  670.         if (matched)
  671.             return dev;
  672.  
  673.         dev++;
  674.     }
  675.     return 0;
  676. }
  677.  
  678. ////////////////////////////////////
  679.  
  680. #if 0
  681. #pragma mark -
  682. #pragma mark "Catalog read"
  683. #endif
  684.  
  685. /*    Convert and push an integer into a TI floating point record:
  686.     [8 bytes] [0x40+log num] 9*[sig figs, 0-99]
  687.     Return pointer past end of float.
  688. */
  689. static u8  *
  690. int_to_tifloat(unsigned x, u8 * buf)
  691. {
  692.     u8         *start;
  693.  
  694.     *buf++ = 8;                    // bytes in length
  695.     memset(buf, 0, 8);
  696.     if (x == 0)
  697.         return buf + 8;
  698.  
  699.     start = buf;
  700.     buf[0] = 0x3F;
  701.     while (x > 0) {
  702.         buf[0]++;
  703.         memmove(buf + 2, buf + 1, 6);
  704.         buf[1] = x % 100;
  705.         x /= 100;
  706.     }
  707.  
  708.     return buf + 8;
  709. }
  710.  
  711.  
  712. /*
  713.   Setup a pab file for use as a catalog iterator.
  714.  */
  715. static int
  716. DSKinitcatalog(fiad_pabfile * pf, u8 dsk)
  717. {
  718.     OSError err;
  719.     int max;
  720.     char path[OS_PATHSIZE];
  721.     OSPathSpec *spec;
  722.  
  723.     pf->is_catalog = dsk--;
  724.     spec = &emudiskpath[dsk];
  725.  
  726.     /* Don't re-read catalog more often than necessary. */
  727.     if (!OS_EqualPathSpec(spec, &DskCat[dsk].path) 
  728.         || DskCatFlag[dsk]) 
  729.     {
  730.         logger(_L|L_1, "Re-reading catalog for DSK%d\n",dsk+1);
  731.         OS_PathSpecToString2(spec, path);
  732.         if ((err = fiad_catalog_read_catalog(&DskCat[dsk], path)) != OS_NOERR) {
  733.             pab_error(pf, err, "couldn't open directory for catalog",
  734.                       e_hardwarefailure);
  735.             return 0;
  736.         }
  737.  
  738.         /* Sort by name */
  739.         fiad_catalog_sort_catalog(&DskCat[dsk], 
  740.                                   FIAD_CATALOG_SORT_BY_NAME, 
  741.                                   true /*ascending*/);
  742.  
  743.         /* Set timer to prevent over-reading catalog
  744.            (Microsoft Multiplan does this) */
  745.         DskCatFlag[dsk] = 0;
  746.         TM_SetEvent(DskCatTag[dsk], TM_HZ*100, 0, 0 /*flags*/, 
  747.                     &DskCatFlag[dsk]);
  748.     }
  749.  
  750.     /* Restrict number of entries we return */
  751.     max = (allowlongcatalogs ? 32767 : 127);
  752.     if (DskCat[dsk].entries > max) DskCat[dsk].entries = max;
  753.  
  754.     return 1;
  755. }
  756.  
  757. /*    For a file-based catalog, we are guaranteed 128 records. 
  758.     The first is the volume, and the rest are for files.  Each
  759.     record is a maximum of 38 bytes long, but is fixed at
  760.     the file's open record length by padding it with zeroes.
  761.     
  762.     For a volume record, the format is:
  763.         [length of volume] "volume"
  764.         [8] [float: 0]
  765.         [8] [float: total # sectors] 
  766.         [8] [float: remaining # sectors]
  767.         [0...]
  768.         
  769.     For a file record, the format is:
  770.         [length of filename] "filename"
  771.         [8] [float: type]
  772.         [8] [float: # sectors used + FDR]
  773.         [8] [float: record length] 
  774.  
  775.     Assume that any errors are due to reading a non-V9t9
  776.     file, and return 0 for those.
  777. */
  778. static int
  779. DSKreadcatalog(fiad_pabfile *pf, u8 dsk, u8 *cr)
  780. {
  781.     OSError     err;
  782.     fdrrec        fdr;
  783.     u8         *ptr;
  784.     fiad_catalog *cat = &DskCat[dsk - 1];
  785.  
  786.     logger(_L | L_1, "reading catalog entry\n");
  787.     memset(cr, 0, pf->pab.preclen);
  788.  
  789.     // volume record?
  790.     if (pf->pab.recnum == 0) {
  791.  
  792.         /*  Get volume name from path. */
  793.         ptr = cr;
  794.         *ptr = fiad_path_disk2ti(&cat->path, (char *)ptr + 1);
  795.  
  796.         // copy disk name to name buffer
  797.         VDPCopy(vdpnamebuffer, ptr+1, 10);
  798.  
  799.         ptr += *ptr + 1;
  800.  
  801.         // zero field
  802.         ptr = int_to_tifloat(0, ptr);
  803.  
  804.         // total space
  805.         ptr = int_to_tifloat(cat->total_sectors, ptr);
  806.  
  807.         // free space
  808.         ptr = int_to_tifloat(cat->free_sectors, ptr);
  809.  
  810.         return 1;
  811.     }
  812.  
  813.     // read file record; restrict it to 127 entries
  814.     // in case naive programs will die...
  815.     err = 0;
  816.     if (pf->pab.recnum >= cat->entries) {
  817.         logger(_L | L_1, "DSKreadcatalog: reached end of directory\n");
  818.  
  819.         // make an empty record     
  820.         ptr = cr;
  821.         *ptr++ = 0;
  822.         ptr = int_to_tifloat(0, ptr);
  823.         ptr = int_to_tifloat(0, ptr);
  824.         ptr = int_to_tifloat(0, ptr);
  825.         return 1;
  826.     }
  827.     
  828.     // Get file info
  829.     if (!fiad_catalog_get_file_info(cat, pf->pab.recnum, &fdr))
  830.         return 0;
  831.  
  832.     // first field is the string representing the
  833.     // file or volume name
  834.     ptr = cr;
  835.     *ptr = fiad_filename_strlen(fdr.filenam);
  836.     memcpy(ptr+1, fdr.filenam, *ptr);
  837.     VDPCopy(vdpnamebuffer, (u8 *)fdr.filenam, 10);
  838.     ptr += *ptr + 1;
  839.  
  840.     // second field is file type
  841.     {
  842.         int         idx;
  843.  
  844.         for (idx = 0; idx < sizeof(DrcTrans) / sizeof(DrcTrans[0]); idx++)
  845.             if (DrcTrans[idx][0] ==
  846.                 (fdr.flags & (ff_internal | ff_program | ff_variable))) {
  847.                 ptr = int_to_tifloat(DrcTrans[idx][1], ptr);
  848.                 break;
  849.             }
  850.         // no match == program
  851.         if (idx >= sizeof(DrcTrans) / sizeof(DrcTrans[0])) {
  852.             ptr = int_to_tifloat(1, ptr);
  853.         }
  854.     }
  855.  
  856.     // third field is file size, one sector for fdr
  857.     ptr = int_to_tifloat(1 + fdr.secsused, ptr);
  858.  
  859.     // fourth field is record size
  860.     ptr = int_to_tifloat(fdr.reclen, ptr);
  861.  
  862.     return 1;
  863. }
  864.  
  865.  
  866. ////////////////////////////////////
  867.  
  868. /*    These DSKXxx routines handle file operations on PABs. */
  869.  
  870. static void
  871. DSKClose(u8 dev, u8 *fn);
  872.  
  873. static void
  874. DSKOpen(u8 dev, u8 *fn)
  875. {
  876.     fiad_pabfile    *pf;
  877.     fiad_tifile    *tf;
  878.     pabrec    *pab;
  879.  
  880.     int            ret;
  881.     OSError     err;
  882.  
  883.     logger(_L | L_1, "DSKOpen\n");
  884.  
  885.     /* get a pabfile for PAB */
  886.     if ((pf = pab_get_new_file(fn)) == NULL) {
  887.         pab_vdp_error(fn, 0, "no free files", e_outofspace);
  888.         return;
  889.     }
  890.  
  891.     tf = &pf->tf;
  892.     pab = &pf->pab;
  893.  
  894.     /* read PAB */
  895.     pab_fetch_from_vdp(pf, true /*opening*/);
  896.  
  897.     /* sanity check */
  898.     if (pab->preclen == 255 && (pab->pflags & fp_variable)) {
  899.         pab_error(pf, 0, "can't have variable record size of 255",
  900.                     e_badopenmode);
  901.         goto open_error;
  902.     }
  903.  
  904.     /*  Fix PAB reclen to default if needed  */
  905. //    if (!pab->preclen) {
  906. //        pab->preclen = 80;
  907. //    }
  908.  
  909.     /* get local file spec */
  910.     ret = pab_setup_file(pf, dev, fn);
  911.  
  912.     /* failure? */
  913.     if (ret < 0)
  914.         goto open_error;
  915.  
  916.     /* catalog request? */
  917.     if (ret > 0) {
  918.         /* make sure it's a legal open mode */
  919.         if ((pab->pflags & (fp_internal | m_input)) != (fp_internal | m_input)) {
  920.             pab_error(pf, 0, "bad catalog open flags", e_badopenmode);
  921.             goto open_error;
  922.         }
  923.  
  924.         if (!DSKinitcatalog(pf, dev))
  925.             goto open_error;
  926.         fiad_tifile_init_file_pointers(tf);
  927.         pf->pab.preclen = 38;
  928.     
  929.         /*  store the PAB */
  930.         pab_store_to_vdp(pf);
  931.         return;
  932.     }
  933.  
  934.     /* normal file open */
  935.  
  936.     pab_set_error(pf, 0);
  937.  
  938.     switch (pab->pflags & m_openmode) {
  939.  
  940.         // update/append mode: either create new file, or
  941.         // open existing one.
  942.  
  943.     case m_append:
  944.         // can't append to FIXED file
  945.         if (!(pab->pflags & fp_variable) /*&&
  946.            !(pab->pflags & fp_internal)*/) {
  947.             pab_error(pf, 0, "can't append to FIXED file", e_badopenmode);
  948.             goto open_error;
  949.         }
  950.         // fall through
  951.  
  952.     case m_update:
  953.         err = fiad_tifile_open_file(tf, 
  954.                                     newfileformat,
  955.                                     true /*create*/, 
  956.                                     false /*always*/,
  957.                                     false /*readonly*/);
  958.         if (err != OS_NOERR) {
  959.             pab_error(pf, err, "could not open for update", 
  960.                       err == OS_PERMERR ? e_readonly :
  961.                       e_badopenmode);
  962.             goto open_error;
  963.         }
  964.  
  965.         // tf->changed set if we know we created it;
  966.         // if not, and we can't read the FDR, create it anyway
  967.         if (!tf->changed && pab_read_fdr(pf)) {
  968.             logger(_L | L_1, "read FDR successfully\n");
  969.             if (!pab_compare_fdr_and_pab(pf))
  970.                 goto open_error;
  971.         } else {
  972.             logger(_L | L_1, "setting up new FDR\n");
  973.             pab_set_error(pf, 0);
  974.             pab_make_fdr(pf);
  975.             if (!pab_write_fdr(pf)) {
  976.                 pab_set_error(pf, e_hardwarefailure);
  977.                 goto open_error;
  978.             }
  979.             my_assert(pab_compare_fdr_and_pab(pf));
  980.         }
  981.         
  982.         // when appending, start at end
  983.         if ((pab->pflags & m_openmode) == m_append) {
  984.             fiad_tifile_seek_to_end(tf);
  985.         }
  986.  
  987.         // cache sector (may not exist for new file)
  988.         if (!fiad_tifile_read_sector(tf)) {
  989.             pab_set_error(pf, e_hardwarefailure);
  990.             goto open_error;
  991.         }
  992.         break;
  993.  
  994.         // output mode: always create new file
  995.     case m_output:
  996.         err = fiad_tifile_open_file(tf,
  997.                                     newfileformat,
  998.                                     true /*create*/,
  999.                                     true /*always*/,
  1000.                                     false /*readonly*/);
  1001.         if (err != OS_NOERR) {
  1002.             pab_error(pf, err, "could not open for output",
  1003.                       err == OS_PERMERR ? e_readonly :
  1004.                       e_badopenmode);
  1005.             goto open_error;
  1006.         }
  1007.  
  1008.         logger(_L | L_1, "setting up new FDR\n");
  1009.         pab_make_fdr(pf);
  1010.         my_assert(pab_compare_fdr_and_pab(pf));
  1011.         break;
  1012.  
  1013.         // input mode: always open existing file
  1014.     case m_input:
  1015.         err = fiad_tifile_open_file(tf, 
  1016.                                     F_UNKNOWN,
  1017.                                     false /*create*/, 
  1018.                                     false /*always*/,
  1019.                                     true /*readonly*/);
  1020.         if (err != OS_NOERR) {
  1021.             pab_error(pf, err, "could not open for input", 
  1022.                       e_badopenmode);
  1023.             goto open_error;
  1024.         }
  1025.  
  1026.         if (!pab_compare_fdr_and_pab(pf))
  1027.             goto open_error;
  1028.         
  1029.         // cache first sector
  1030.         if (!fiad_tifile_read_sector(tf)) {
  1031.             pab_set_error(pf, e_hardwarefailure);
  1032.             goto open_error;
  1033.         }
  1034.         break;
  1035.     }
  1036.  
  1037.     /*  store the PAB back to VDP */
  1038.     pab_store_to_vdp(pf);
  1039.     return;
  1040.  
  1041. open_error:
  1042.     pf->fnptr = NULL;
  1043. }
  1044.  
  1045. void
  1046. DSKClose(u8 dev, u8 *fn)
  1047. {
  1048.     fiad_pabfile     *pf;
  1049.  
  1050.     logger(_L | L_1, "DSKClose\n");
  1051.  
  1052.     // if the file isn't open, um...
  1053.     if ((pf = pab_find_open_file(fn)) == NULL)
  1054.         pab_set_vdp_error(fn, e_badfiletype);
  1055.     else
  1056.         pab_close_file(pf);
  1057. }
  1058.  
  1059.  
  1060. #if 0
  1061. #pragma mark "File read"
  1062. #endif
  1063.  
  1064. static void
  1065. DSKRead(u8 dev, u8 *fn)
  1066. {
  1067.     fiad_pabfile    *pf;
  1068.     fiad_tifile        *tf;
  1069.     pabrec             *pab;
  1070.     u8                len;
  1071.     int                err;
  1072.  
  1073.     logger(_L | L_1, "DSKRead\n");
  1074.  
  1075.     if ((pf = pab_find_open_file(fn)) == NULL) {
  1076.         pab_vdp_error(fn, 0, "DSKRead:  error due to not finding open file", e_badfiletype);
  1077.         return;
  1078.     }
  1079.  
  1080.     tf = &pf->tf;
  1081.     pab = &pf->pab;
  1082.  
  1083.     /* check operation */
  1084.     if ((pab->pflags & m_openmode) == m_output ||
  1085.         (pab->pflags & m_openmode) == m_append) {
  1086.         pab_error(pf, 0, "read from append/output file", e_illegal);
  1087.         return;
  1088.     }
  1089.  
  1090.     /* get pab info */
  1091.     pab_fetch_from_vdp(pf, false /*opening*/);
  1092.  
  1093.     /* catalog read? */
  1094.     if (pf->is_catalog) {
  1095.         u8          cr[256];
  1096.         int         x;
  1097.  
  1098.         DSKreadcatalog(pf, dev, cr);
  1099.         VDPCopy(pab->addr, cr, pab->preclen);
  1100.  
  1101.         logger(_L | L_3, "dump of catalog record:");
  1102.         for (x = 0; x < pab->preclen; x++) {
  1103.             logger(_L |  0 | L_3, "%02X ", cr[x]);
  1104.         }
  1105.         logger(_L | L_3, "\n");
  1106.  
  1107.         tf->currec++;
  1108.         pab->charcount = pab->preclen;
  1109.         pab->recnum = tf->currec;
  1110.  
  1111.         pab_store_to_vdp(pf);
  1112.         return;
  1113.     }
  1114.  
  1115.     /* normal file read */
  1116.  
  1117.     /* for fixed file, limit record # and update */
  1118.     if (!(pab->pflags & fp_variable)) {
  1119.         pf->pab.recnum &= 0x7fff;
  1120.         tf->currec = pf->pab.recnum;
  1121.         err = fiad_tifile_read_record(tf, VDP_PTR(pab->addr), &len);
  1122.         pf->pab.recnum = tf->currec;
  1123.     } else {
  1124.         /* variable file */
  1125.         err = fiad_tifile_read_record(tf, VDP_PTR(pab->addr), &len);
  1126.     }
  1127.  
  1128.     if (err == 0) {
  1129.         VDPUpdate(pab->addr, len);
  1130.         pab->charcount = len;
  1131.     } else if (err < 0) {
  1132.         // hardware failure
  1133.         pab_set_error(pf, e_hardwarefailure);
  1134.     } else {
  1135.         // end of file
  1136.         pab_set_error(pf, e_endoffile);
  1137.         pab_close_file(pf);
  1138.     }
  1139.  
  1140.     /* save changed PAB info */
  1141.     pab_store_to_vdp(pf);
  1142. }
  1143.  
  1144. static void
  1145. DSKWrite(u8 dev, u8 *fn)
  1146. {
  1147.     fiad_pabfile    *pf;
  1148.     fiad_tifile        *tf;
  1149.     pabrec             *pab;
  1150.     int                err;
  1151.  
  1152.     logger(_L | L_1, "DSKWrite\n");
  1153.  
  1154.     if ((pf = pab_find_open_file(fn)) == NULL) {
  1155.         pab_vdp_error(fn, 0, "DSKWrite:  error due to not finding open file", e_badfiletype);
  1156.         return;
  1157.     }
  1158.  
  1159.     tf = &pf->tf;
  1160.     pab = &pf->pab;
  1161.  
  1162.     /* check operation */
  1163.     if ((pab->pflags & m_openmode) == m_input) {
  1164.         pab_error(pf, 0, "writing to input file", e_illegal);
  1165.         return;
  1166.     }
  1167.  
  1168.     if (pf->is_catalog) {
  1169.         pab_error(pf, 0, "writing to catalog", e_illegal);
  1170.         return;
  1171.     }
  1172.  
  1173.     /* get pab info */
  1174.     pab_fetch_from_vdp(pf, false /*opening*/);
  1175.  
  1176.     /* for fixed file, limit record # and update */
  1177.     if (!(pab->pflags & fp_variable)) {
  1178.         pf->pab.recnum &= 0x7fff;
  1179.         tf->currec = pf->pab.recnum;
  1180.         err = fiad_tifile_write_record(tf, VDP_PTR(pab->addr), pab->preclen);
  1181.         pf->pab.recnum = tf->currec;
  1182.     } else {
  1183.         /* variable file */
  1184.         err = fiad_tifile_write_record(tf, VDP_PTR(pab->addr), pab->charcount);
  1185.     }
  1186.  
  1187.     if (err == 0) {
  1188.         // no error
  1189.     } else if (err < 0) {
  1190.         // hardware failure
  1191.         pab_set_error(pf, e_hardwarefailure);
  1192.     } else if (err > 0) {
  1193.         // disk full
  1194.         pab_set_error(pf, e_outofspace);
  1195.     }
  1196.  
  1197.     /* save changed PAB info */
  1198.     pab_store_to_vdp(pf);
  1199. }
  1200.  
  1201. static void
  1202. DSKSeek(u8 dev, u8 *fn)
  1203. {
  1204.     fiad_pabfile    *pf;
  1205.     fiad_tifile        *tf;
  1206.     pabrec             *pab;
  1207.     int                err;
  1208.  
  1209.     logger(_L | L_1, "DSKSeek\n");
  1210.  
  1211.     if ((pf = pab_find_open_file(fn)) == NULL) {
  1212.         pab_vdp_error(fn, 0, "DSKSeek:  error due to not finding open file", e_badfiletype);
  1213.         return;
  1214.     }
  1215.  
  1216.     tf = &pf->tf;
  1217.     pab = &pf->pab;
  1218.  
  1219.     if (pf->is_catalog) {
  1220.         pab_error(pf, 0, "DSKSeek:  error due to seeking catalog", e_illegal);
  1221.         return;
  1222.     }
  1223.  
  1224.     /* get pab info */
  1225.     pab_fetch_from_vdp(pf, false /*opening*/);
  1226.  
  1227.     /* variable files are restored to record 0,
  1228.        and this can only be done in update/input mode */
  1229.     if ((pab->pflags & fp_variable) &&     
  1230.         ((pab->pflags & m_openmode) == m_append || 
  1231.          (pab->pflags & m_openmode) == m_output)) {
  1232.             pab_error(pf, 0, "append/output mode can't seek", e_illegal);
  1233.             return;
  1234.     }
  1235.  
  1236.     if (!(pab->pflags & fp_variable)) {
  1237.         // maximum record # is 32767
  1238.         pab->recnum &= 0x7fff;
  1239.         err = fiad_tifile_seek_to_record(tf, pab->recnum);
  1240.     } else {
  1241.         pab->recnum = 0;
  1242.         err = fiad_tifile_seek_to_record(tf, 0);
  1243.     }
  1244.  
  1245.     if (err == 0) {
  1246.         // no error
  1247.     } else {
  1248.         pab_set_error(pf, (err > 0) ? e_outofspace : e_hardwarefailure);
  1249.     }
  1250.  
  1251.     /* save changed PAB info */
  1252.     pab_store_to_vdp(pf);
  1253. }
  1254.  
  1255. #if 0
  1256. #pragma mark "LOAD MEMORY IMAGE"
  1257. #endif
  1258.  
  1259. static void
  1260. DSKLoad(u8 dev, u8 *fn)
  1261. {
  1262.     fiad_pabfile pf;
  1263.     fiad_tifile    *tf;
  1264.     pabrec    *pab;
  1265.  
  1266.     int            ret;
  1267.     OSError     err;
  1268.     u16            len;
  1269.  
  1270.     logger(_L | L_0, "DSKLoad\n");
  1271.  
  1272.     tf = &pf.tf;
  1273.     pab = &pf.pab;
  1274.  
  1275.     /* get local file spec */
  1276.     ret = pab_setup_file(&pf, dev, fn);
  1277.  
  1278.     /* failure? */
  1279.     if (ret < 0)
  1280.         return;
  1281.  
  1282.     /* catalog request? */
  1283.     if (ret > 0) {
  1284.         pab_error(&pf, 0, "can't open catalog as binary", e_illegal);
  1285.         return;
  1286.     }
  1287.  
  1288.     logger(_L | L_1, "trying to open binary image\n");
  1289.  
  1290.     err = fiad_tifile_open_file(tf,
  1291.                                 F_UNKNOWN,
  1292.                                 false /*create*/, 
  1293.                                 false /*always*/,
  1294.                                 true /*readonly*/);
  1295.     if (err != OS_NOERR) {
  1296.         pab_error(&pf, err, "could not open for input", 
  1297.                   e_badfiletype);
  1298.         return;
  1299.     }
  1300.  
  1301.     /* read PAB */
  1302.     pab_fetch_from_vdp(&pf, true /*opening*/);
  1303.  
  1304.     if (!pab_compare_fdr_and_pab(&pf))
  1305.         return;
  1306.         
  1307.     pab_set_error(&pf, 0);
  1308.  
  1309.     ret = fiad_tifile_read_binary_image(tf, VDP_PTR(pab->addr), 
  1310.                                         pab->recnum, &len);
  1311.     VDPUpdate(pab->addr, len);
  1312.  
  1313.     if (ret >= 0) {
  1314.         // no error or EOF (which is okay for DSKLoad)
  1315.         pab->recnum = len;
  1316.     } else {
  1317.         // failure
  1318.         pab_set_error(&pf, e_hardwarefailure);
  1319.     }
  1320.  
  1321.     /* save changed PAB info */
  1322.     pab_store_to_vdp(&pf);
  1323.  
  1324.     /* close file */
  1325.     pab_close_file(&pf);
  1326. }
  1327.  
  1328. #if 0
  1329. #pragma mark "SAVE MEMORY IMAGE"
  1330. #endif
  1331.  
  1332. static void
  1333. DSKSave(u8 dev, u8 *fn)
  1334. {
  1335.     fiad_pabfile pf;
  1336.     fiad_tifile    *tf;
  1337.     pabrec    *pab;
  1338.  
  1339.     int            ret;
  1340.     OSError     err;
  1341.     u16            len;
  1342.  
  1343.     logger(_L | 0, "DSKSave\n");
  1344.  
  1345.     tf = &pf.tf;
  1346.     pab = &pf.pab;
  1347.  
  1348.     /* get local file spec */
  1349.     ret = pab_setup_file(&pf, dev, fn);
  1350.  
  1351.     /* failure? */
  1352.     if (ret < 0)
  1353.         return;
  1354.  
  1355.     /* catalog request? */
  1356.     if (ret > 0) {
  1357.         pab_error(&pf, 0, "can't save catalog as binary", e_illegal);
  1358.         return;
  1359.     }
  1360.  
  1361.     logger(_L | L_1, "trying to save binary image\n");
  1362.  
  1363.     err = fiad_tifile_open_file(tf,
  1364.                                 newfileformat,
  1365.                                 true /*create*/,
  1366.                                 true /*always*/,
  1367.                                 false /*readonly*/);
  1368.     if (err != OS_NOERR) {
  1369.         pab_error(&pf, err, "could not open for output",
  1370.                   err == OS_PERMERR ? e_readonly :
  1371.                   e_badfiletype);
  1372.         return;
  1373.     }
  1374.  
  1375.     /* read PAB */
  1376.     pab_fetch_from_vdp(&pf, true /*opening*/);
  1377.  
  1378.     logger(_L | L_1, "setting up new FDR\n");
  1379.     pab_make_fdr(&pf);
  1380.     my_assert(pab_compare_fdr_and_pab(&pf));
  1381.  
  1382.     pab_set_error(&pf, 0);
  1383.  
  1384.     ret = fiad_tifile_write_binary_image(tf, VDP_PTR(pab->addr), 
  1385.                                          pab->recnum, &len);
  1386.  
  1387.     if (ret == 0) {
  1388.         // no error
  1389.     } else {
  1390.         // failure
  1391.         pab_set_error(&pf, ret > 0 ? e_outofspace : e_hardwarefailure);
  1392.     }
  1393.  
  1394.     /* no pab changes */
  1395.  
  1396.     /* close file */
  1397.     pab_close_file(&pf);
  1398. }
  1399.  
  1400. static void
  1401. DSKDelete(u8 dev, u8 *fn)
  1402. {
  1403.     fiad_pabfile pf;
  1404.     fiad_tifile *tf;
  1405.     pabrec        *pab;
  1406.     int         ret;
  1407.     OSError     err;
  1408.  
  1409.     logger(_L | 0, "DSKDelete\n");
  1410.  
  1411.     tf = &pf.tf;
  1412.     pab = &pf.pab;
  1413.  
  1414.     /* read PAB */
  1415.     pab_fetch_from_vdp(&pf, true /*opening*/);
  1416.  
  1417.     /* get local file spec */
  1418.     ret = pab_setup_file(&pf, dev, fn);
  1419.  
  1420.     /* failure? */
  1421.     if (ret < 0)
  1422.         return;
  1423.  
  1424.     /* catalog request? */
  1425.     if (ret > 0) {
  1426.         pab_error(&pf, 0, "can't delete catalog", e_illegal);
  1427.         return;
  1428.     }
  1429.  
  1430.     logger(_L | L_1, "trying to delete file\n");
  1431.  
  1432.     pab_set_error(&pf, 0);
  1433.  
  1434.     err = OS_Delete(&tf->spec);
  1435.     if (err != OS_NOERR) {
  1436.         pab_error(&pf, err, "deleting file\n",
  1437.                     err == OS_PERMERR ? e_readonly : e_badfiletype);
  1438.     }
  1439. }
  1440.  
  1441. static void
  1442. DSKScratch(u8 dev, u8 *fn)
  1443. {
  1444.     logger(_L | 0, "DSKScratch");
  1445.  
  1446.     // nothing supports this
  1447.     pab_set_vdp_error(fn, e_illegal);
  1448. }
  1449.  
  1450. /*    Get status bits for a file */
  1451. static u8
  1452. fiad_tifile_get_status(fiad_tifile *tf, u8 status)
  1453. {
  1454.     bool is_protected = true;
  1455.     OSSize total, free, blocksize;
  1456.     
  1457.     if ((tf->fdr.flags & ff_protected) ||
  1458.         OS_CheckProtection(&tf->spec, &is_protected) == OS_NOERR)
  1459.         status |= is_protected ? st_readonly : 0;
  1460.  
  1461.     if (tf->open) {
  1462.         if (tf->fdr.flags & ff_internal)
  1463.             status |= st_internal;
  1464.  
  1465.         if (tf->fdr.flags & ff_program)
  1466.             status |= st_program;
  1467.  
  1468.         if (tf->fdr.flags & ff_variable)
  1469.             status |= st_variable;
  1470.     }
  1471.     
  1472.     if (OS_GetDiskStats(&tf->spec.path, &blocksize, &total, &free) == OS_NOERR
  1473.         && total == free)
  1474.         status |= st_diskfull;
  1475.         
  1476.     // check EOF
  1477.     if (status & st_variable) {
  1478.  
  1479.         if (tf->cursec + 1 >= tf->fdr.secsused &&
  1480.             tf->sector[tf->curoffs] == 0xff) {
  1481.             status |= st_eof;
  1482.             logger(_L | L_2, "DSKStatus:  EOF on variable file");
  1483.         }
  1484.     } else {
  1485.         if (tf->currec >= tf->fdr.numrecs) {
  1486.             status |= st_eof;
  1487.             logger(_L | L_2, "DSKStatus:  EOF on fixed file\n");
  1488.         }
  1489.     }
  1490.     return status;
  1491. }
  1492.  
  1493. static void
  1494. DSKStatus(u8 dev, u8 *fn)
  1495. {
  1496.     int file_was_open = 0;
  1497.     fiad_pabfile *pf, local;
  1498.     fiad_tifile *tf;
  1499.     pabrec *pab;
  1500.     u8 status = 0;
  1501.     int ret;
  1502.     OSError err;
  1503.  
  1504.     logger(_L | L_2, "DSKStatus\n");
  1505.  
  1506.     if ((pf = pab_find_open_file(fn)) == NULL) {
  1507.  
  1508.         /* file is not open, open it now */
  1509.  
  1510.         logger(_L | L_2, "DSKStatus:  no PAB file yet...\n");
  1511.  
  1512.         /* use local file */
  1513.         pf = &local;
  1514.         tf = &pf->tf;
  1515.         pab = &pf->pab;
  1516.  
  1517.         /* get local file spec */
  1518.         ret = pab_setup_file(pf, dev, fn);
  1519.  
  1520.         /* failure? */
  1521.         if (ret < 0) {
  1522.             logger(_L | L_2, "DSKStatus:  file not found\n");
  1523.             status = st_filenotfound;
  1524.             pab->scrnoffs = status;
  1525.             pab_store_to_vdp(pf);
  1526.             return;
  1527.         }
  1528.  
  1529.         /* catalog request? */
  1530.         if (ret > 0) {
  1531.             logger(_L | L_2, "DSKStatus:  catalog status\n");
  1532.             status = st_internal | st_readonly;
  1533.             pab->scrnoffs = status;
  1534.             pab_store_to_vdp(pf);
  1535.             return;
  1536.         }
  1537.  
  1538.         logger(_L | L_2, "DSKStatus:  opening file\n");
  1539.  
  1540.         /* open file to look at it */
  1541.         err = fiad_tifile_open_file(tf, 
  1542.                                     F_UNKNOWN,
  1543.                                     false /*create*/, 
  1544.                                     false /*always*/,
  1545.                                     true /*readonly*/);
  1546.         if (err != OS_NOERR) {
  1547.             logger(_L | L_2, "DSKStatus:  file not found\n");
  1548.             status |= st_filenotfound;
  1549.             pab->scrnoffs = status;
  1550.             pab_store_to_vdp(pf);
  1551.             return;
  1552.         }
  1553.     } else {
  1554.  
  1555.         /* file was already open */
  1556.  
  1557.         logger(_L | L_2, "DSKStatus:  file was open\n");
  1558.  
  1559.         tf = &pf->tf;
  1560.         pab = &pf->pab;
  1561.  
  1562.         // they might have seeked on us
  1563.         if (!(pab->pflags & fp_variable)) {
  1564.             pab->recnum &= 0x7fff;
  1565.             fiad_tifile_seek_to_record(tf, pab->recnum);
  1566.         }
  1567.  
  1568.         file_was_open = 1;
  1569.     }
  1570.  
  1571.     status |= fiad_tifile_get_status(tf, status);
  1572.  
  1573.     logger(_L | L_1, "DSKStatus:  flags=%02X\n\n", status);
  1574.  
  1575.     pab->scrnoffs = status;
  1576.     pab_set_error(pf, 0);
  1577.  
  1578.     /* save changed PAB bits */
  1579.     pab_store_to_vdp(pf);
  1580.  
  1581.     /* close file if we just opened it */
  1582.     if (!file_was_open) {
  1583.         pab_close_file(pf);
  1584.     }
  1585. }
  1586.  
  1587. ////////////////////////////////////
  1588.  
  1589. #if 0
  1590. #pragma mark -
  1591. #pragma mark "Fake catalog sectors routines"
  1592. #endif
  1593.  
  1594. static void 
  1595. setup_catalog(u8 dsk)
  1596. {
  1597.     char path[OS_PATHSIZE];
  1598.     OS_PathSpecToString2(&emudiskpath[dsk], path);
  1599.     fiad_catalog_read_catalog(&DskCat[dsk], path);
  1600. }
  1601.  
  1602. /*
  1603.  *    Don't always free the catalog, since fiad.c will 
  1604.  *    free it for us next time we read it
  1605.  */
  1606. static void
  1607. free_catalog(u8 dsk)
  1608. {
  1609.     fiad_catalog_free_catalog(&DskCat[dsk]);
  1610. }
  1611.  
  1612. static void
  1613. create_volume_sector(u8 dsk, u8 *sector)
  1614. {
  1615.     int         len;
  1616.     fiad_catalog *cat = &DskCat[dsk];
  1617.  
  1618.     logger(_L | L_1, "creating catalog sector 0\n");
  1619.  
  1620.     // reread catalog at sector 0
  1621.     if (!DskCat[dsk].entries ||
  1622.         !OS_EqualPathSpec(&emudiskpath[dsk], &DskCat[dsk].path) ||
  1623.         (last_sector_read[dsk] != 0 &&
  1624.         last_sector_read[dsk] != 1)) 
  1625.     {
  1626.         setup_catalog(dsk);
  1627.     }
  1628.  
  1629.     memset(sector, 0, 256);
  1630.  
  1631.     // use directory name as volume name
  1632.     len = fiad_path_disk2ti(&cat->path, (char *)sector);
  1633.  
  1634.     // copy to VDP name buffer
  1635.     VDPCopy(vdpnamebuffer, sector, 10);
  1636.  
  1637.     // fill in info for a 90k disk
  1638.     sector[10] = 0x1; sector[11] = 0x68;
  1639.     sector[12] = 9;            // secs per track
  1640.     sector[13] = 'D';
  1641.     sector[14] = 'S';
  1642.     sector[15] = 'K';
  1643.     sector[17] = 40;        // # tracks
  1644.     sector[18] = 1;            // # sides?  (may be density)
  1645.     sector[19] = 1;            // density?  (may be sides)
  1646.  
  1647.     // disk is full
  1648.     memset(sector + 56, 0xff, 256 - 56);
  1649. }
  1650.  
  1651. static void
  1652. create_index_sector(u8 dsk, u16 * sector)
  1653. {
  1654.     int x,l;
  1655.  
  1656.     logger(_L | L_1, "creating catalog sector 1\n");
  1657.  
  1658.     // reread catalog at sector 1
  1659.     if (!DskCat[dsk].entries ||
  1660.         !OS_EqualPathSpec(&emudiskpath[dsk], &DskCat[dsk].path) ||
  1661.         (last_sector_read[dsk] != 0 &&
  1662.         last_sector_read[dsk] != 1)) {
  1663.         setup_catalog(dsk);
  1664.     }
  1665.  
  1666.     memset(sector, 0, 256);
  1667.  
  1668.     /* entries were already sorted */
  1669.     l = DskCat[dsk].entries > 127 ? 127 : DskCat[dsk].entries;
  1670.     for (x = 0; x < l; x++) {
  1671.         u16 sec = x+2;
  1672.         sector[x] = HOST2TI(sec);
  1673.     }
  1674. }
  1675.  
  1676. static int
  1677. create_fdr_sector(u8 dsk, u16 ent, u8 * sector)
  1678. {
  1679.     fdrrec fdr;
  1680.     v9t9_fdr   *v9f;
  1681.     fiad_catalog *cat = &DskCat[dsk];
  1682.  
  1683.     logger(_L | L_1, "creating catalog directory sector %d\n", ent + 2);
  1684.  
  1685.     // reread catalog at unknown FDR sector
  1686.     if (!DskCat[dsk].entries ||
  1687.         !OS_EqualPathSpec(&emudiskpath[dsk], &DskCat[dsk].path) ||
  1688.         (last_sector_read[dsk] == 0xffff)) 
  1689.     {
  1690.         setup_catalog(dsk);
  1691.     }
  1692.  
  1693.     if (!fiad_catalog_get_file_info(cat, ent, &fdr))
  1694.         return 0;
  1695.  
  1696.     memset(sector, 0, 256);
  1697.  
  1698.     v9f = (v9t9_fdr *) sector;
  1699.  
  1700.     memcpy(v9f->filenam, fdr.filenam, 10);
  1701.     VDPCopy(vdpnamebuffer, (u8 *)fdr.filenam, 10);
  1702.  
  1703.     v9f->flags = fdr.flags;
  1704.     v9f->recspersec = fdr.recspersec;
  1705.     v9f->secsused = HOST2TI(fdr.secsused);
  1706.     v9f->byteoffs = fdr.byteoffs;
  1707.     v9f->reclen = fdr.reclen;
  1708.     v9f->numrecs = SWAPTI(fdr.numrecs);
  1709.     return 1;
  1710. }
  1711.  
  1712. #if 0
  1713. #pragma mark "Read/write sector"
  1714. #endif
  1715.  
  1716. /*    Read/write sector.
  1717.     Only allow access to the volume sector, index sector,
  1718.     and catalog sectors; all of by faking it.  The
  1719.     catalogs DskCat[] contain a list of files scanned
  1720.     in the directory when sector 1 is read.  If any sector
  1721.     greater than 1 is read without reading sector 1 first,
  1722.     there will be invalid sector info returned.
  1723.  
  1724. in:        byte 0x834C: drive
  1725.         byte 0x834d: 0xff=read, 0x00=write
  1726.         word 0x834E: vdp addr
  1727.         word 0x8350: sector #
  1728.     
  1729. out:    word 0x834A: sector read
  1730.         byte 0x8350: status
  1731. */
  1732.  
  1733. static void
  1734. DskSectorRW(u8 dev, u8 rw, u16 addr, u16 secnum)
  1735. {
  1736.     u8          sector[256];
  1737.  
  1738.     logger(_L | L_1, "DskSectorRW\n");
  1739.  
  1740.     // don't write!
  1741.     if (!rw) {
  1742.         logger(_LL | 0, "FIAD server:  ignoring sector write request");
  1743.         sub_set_error(0);
  1744.         return;
  1745.     }
  1746.     if (secnum == 0) {
  1747.         // make volume sector
  1748.         create_volume_sector(dev - 1, sector);
  1749.     } else if (secnum == 1) {
  1750.         // make index sector
  1751.         create_index_sector(dev - 1, (u16 *) sector);
  1752.     } else /* (secnum >= 2) */ {
  1753.         // make fdr sector
  1754.         if (!create_fdr_sector(dev - 1, secnum - 2, sector)) {
  1755.             free_catalog(dev-1);
  1756.             memset(sector, 0, 256);
  1757.             secnum = 0xffff;
  1758.             return;
  1759.         }
  1760.     }
  1761.     last_sector_read[dev-1] = secnum;
  1762.  
  1763.     VDPCopy(addr, sector, 256);
  1764.     sub_set_error(0);
  1765. }
  1766.  
  1767. #if 0
  1768. #pragma mark "Format disk"
  1769. #endif
  1770.  
  1771. /*    Format disk. 
  1772.  
  1773. in:        byte 0x834C: drive
  1774.         byte 0x834d: # tracks
  1775.         word 0x834e: track layout
  1776.         byte 0x8350: density (1,2)
  1777.         byte 0x8351: sides (1,2)
  1778.  
  1779. out:    byte 0x8350: status
  1780. */
  1781. static void
  1782. DskFormatDisk(u8 dev, u8 tracks, u16 tinfo, u16 densid)
  1783. {
  1784.     logger(_L | 0, "DskFormatDisk\n");
  1785.     sub_set_error(e_illegal);
  1786. }
  1787.  
  1788. #if 0
  1789. #pragma mark "Modify file protection"
  1790. #endif
  1791.  
  1792. /*    Modify file protection 
  1793.  
  1794. in:        byte 0x834c: drive
  1795.         byte 0x834d: 0=none, <>0=locked
  1796.         word 0x834e: filename
  1797. */
  1798. static void
  1799. DskProtect(u8 dev, u8 lock, u16 fname, u16 addr2)
  1800. {
  1801.     fiad_tifile    local, *tf = &local;
  1802.     OSError err;
  1803.  
  1804.     logger(_L | 0, "DskProtect\n");
  1805.  
  1806.     if (sub_setup_file(tf, dev, fname) != 0) {
  1807.         sub_set_error(es_badfuncerr /*es_filenotfound */ );
  1808.         return;
  1809.     }
  1810.  
  1811.     err = fiad_tifile_open_file(tf, 
  1812.                                 F_UNKNOWN,
  1813.                                 false /*create*/, 
  1814.                                 false /*always*/,
  1815.                                 false /*readonly*/);
  1816.     if (err != OS_NOERR) {
  1817.         sub_error(tf, err, "could not open to modify protection", 
  1818.                   err == OS_PERMERR ? es_hardware :
  1819.                   es_badfuncerr /* es_filenotfound */);
  1820.         return;
  1821.     }
  1822.  
  1823.     DSKcomparefdrandsub(tf, (char *) VDP_PTR(fname));
  1824.  
  1825.     // update FDR
  1826.     tf->fdr.flags = (tf->fdr.flags & ~ff_protected) | (lock ? ff_protected : 0);
  1827.     tf->changedfdr = true;
  1828.  
  1829.     fiad_tifile_close_file(tf);
  1830.  
  1831.     // do NOT modify the real file
  1832.  
  1833.     sub_set_error(es_okay);
  1834. }
  1835.  
  1836. #if 0
  1837. #pragma mark "Rename file"
  1838. #endif
  1839.  
  1840. /*    Rename file
  1841.  
  1842. in:        byte 0x834c: drive
  1843.         word 0x834e: new name
  1844.         word 0x8350: old name
  1845.         
  1846. out:    byte 0x8350: status        
  1847. */
  1848. static void
  1849. DskRename(u8 dev, u8 unk, u16 newname, u16 oldname)
  1850. {
  1851.     fiad_tifile    local, *tf = &local;
  1852.  
  1853.     char        newpath[OS_PATHSIZE];
  1854.     OSSpec      orig;
  1855.     OSSpec      renamed;
  1856.     OSError     err;
  1857.  
  1858.     logger(_L | 0, "DskRename\n");
  1859.  
  1860.     if (sub_setup_file(tf, dev, oldname) != 0) {
  1861.         sub_set_error(es_badfuncerr /*es_filenotfound */ );
  1862.         return;
  1863.     }
  1864.  
  1865.     // save original spec
  1866.     orig = tf->spec;
  1867.  
  1868.     // rename real file
  1869.     OS_PathSpecToString2(&orig.path, newpath);
  1870.     fiad_filename_ti2host((char *) VDP_PTR(newname),
  1871.             strcspn((char *) VDP_PTR(newname), " ."),
  1872.             newpath + strlen(newpath));
  1873.  
  1874.     // can't fathom new name?
  1875.     if ((err = OS_MakeFileSpec(newpath, &renamed)) != OS_NOERR) {
  1876.         sub_set_error(es_hardware);
  1877.         return;
  1878.     }
  1879.  
  1880.     // destination already exists?
  1881.     if (OS_Status(&renamed) == OS_NOERR) {
  1882.         sub_set_error(es_fileexists);
  1883.         return;
  1884.     }
  1885.  
  1886.     if ((err = OS_Rename(&orig, &renamed)) != OS_NOERR) {
  1887.         sub_set_error(es_hardware);
  1888.         return;
  1889.     }
  1890.    
  1891.     // now open the file and rename the FDR
  1892.     if (sub_setup_file(tf, dev, newname) != 0) {
  1893.         sub_set_error(es_badfuncerr /*es_filenotfound */ );
  1894.         return;
  1895.     }
  1896.  
  1897.     err = fiad_tifile_open_file(tf, 
  1898.                                 F_UNKNOWN,
  1899.                                 false /*create*/, 
  1900.                                 false /*always*/,
  1901.                                 false /*readonly*/);
  1902.     if (err != OS_NOERR) {
  1903.         sub_error(tf, err, "could not open to modify filename", 
  1904.                   err == OS_PERMERR ? es_hardware :
  1905.                   es_badfuncerr /* es_filenotfound */);
  1906.         return;
  1907.     }
  1908.  
  1909.     // update FDR
  1910.     memcpy(tf->fdr.filenam, VDP_PTR(newname), 10);
  1911.     tf->changedfdr = true;
  1912.  
  1913.     logger(_L | 0, "Renamed '%s' to '%s'\n\n\n", OS_SpecToString1(&orig), newpath);
  1914.  
  1915.     fiad_tifile_close_file(tf);
  1916.  
  1917.     sub_set_error(es_okay);
  1918. }
  1919.  
  1920. #if 0
  1921. #pragma mark "Direct input file"
  1922. #endif
  1923.  
  1924. /*    Access direct input file 
  1925.  
  1926. in:        byte 0x834c: drive
  1927.         byte 0x834d: 0=get FDR info, <>0=get # sectors
  1928.         word 0x834e: filename
  1929.         byte 0x8350: 0x8300+# = ptr to:
  1930.         -- if get FDR info:
  1931.             word <unused>, word <# sectors>, 
  1932.             byte <flags>, byte <recspersrec>, 
  1933.             byte <eof>, byte <reclen>, word <numrecs>
  1934.         -- if read sectors:
  1935.             word <buffer addr>, word <sector #>
  1936.  
  1937. out:    byte 0x834D: # sectors read
  1938.         byte 0x8350: status
  1939. */
  1940. static void
  1941. DskDirectInput(u8 dev, u8 secs, u16 fname, u16 parms)
  1942. {
  1943.     fiad_tifile    local, *tf = &local;
  1944.     OSError err;
  1945.  
  1946.     logger(_L | 0, "DskDirectInput\n");
  1947.  
  1948.     // error condition
  1949.     memory_write_byte(0x834d, 0);
  1950.  
  1951.     if (sub_setup_file(tf, dev, fname) != 0) {
  1952.         sub_set_error(es_filenotfound);
  1953.         return;
  1954.     }
  1955.  
  1956.     err = fiad_tifile_open_file(tf, 
  1957.                                 F_UNKNOWN,
  1958.                                 false /*create*/, 
  1959.                                 false /*always*/,
  1960.                                 true /*readonly*/);
  1961.     if (err != OS_NOERR) {
  1962.         sub_error(tf, err, "could not open to read sectors", 
  1963.                   es_badfuncerr /* es_filenotfound */);
  1964.         return;
  1965.     }
  1966.  
  1967.     DSKcomparefdrandsub(tf, (char *) VDP_PTR(fname));
  1968.  
  1969.     parms = 0x8300 + (parms >> 8);
  1970.  
  1971.     if (!secs) {
  1972.         // read FDR info
  1973.         memory_write_word(parms + 2, tf->fdr.secsused);
  1974.         memory_write_byte(parms + 4, tf->fdr.flags);
  1975.         memory_write_byte(parms + 5, tf->fdr.recspersec);
  1976.         memory_write_byte(parms + 6, tf->fdr.byteoffs);
  1977.         memory_write_byte(parms + 7, tf->fdr.reclen);
  1978.         memory_write_word(parms + 8, HOST2TI(tf->fdr.numrecs));
  1979.  
  1980.         sub_set_error(0);
  1981.     } else {
  1982.         // read sectors
  1983.         u16         vaddr = memory_read_word(parms);
  1984.         u16         secnum = memory_read_word(parms + 2);
  1985.         u16         red = 0;
  1986.         int            ret;
  1987.  
  1988.         logger(_L | L_1,
  1989.              "reading %d sectors from sector #%d from file, storing to >%04X\n\n",
  1990.              secs, secnum, vaddr);
  1991. //debugger_enable(true);
  1992.  
  1993.         sub_set_error(0);
  1994.  
  1995.         if (!fiad_tifile_seek_to_sector(tf, secnum)) {
  1996.             sub_error(tf, tf->error, "DskDirectInput:  Could not seek to sector", es_hardware);
  1997.         } else {
  1998.             ret = fiad_tifile_read_binary_image(tf, VDP_PTR(vaddr), secs * 256, &red);
  1999.             if (ret < 0) {
  2000.                 sub_error(tf, tf->error, "DskDirectInput:  Could not read sectors", es_hardware);
  2001.                 sub_set_error(es_hardware);
  2002.             }
  2003.  
  2004.             // error will be set if sector read failed
  2005.             memory_write_byte(0x834D, (red + 255) >> 8);
  2006.         }
  2007.     }
  2008.     fiad_tifile_close_file(tf);
  2009. }
  2010.  
  2011. #if 0
  2012. #pragma mark "Direct output file"
  2013. #endif
  2014.  
  2015. /*    Access direct output file
  2016.  
  2017. in:        byte 0x834c: drive
  2018.         byte 0x834d: 0=make FDR, <>0=write # sectors
  2019.         word 0x834e: filename
  2020.         byte 0x8350: +0x8300 = ptr to:
  2021.         -- if make FDR:
  2022.             word <unused>, word <# sectors>, 
  2023.             byte <flags>, byte <recspersrec>, 
  2024.             byte <eof>, byte <reclen>, word <numrecs>
  2025.         -- if write sectors:
  2026.             word <buffer addr>, word <sector #>
  2027.  
  2028. out:    byte 0x834d: # sectors written (or 0)
  2029.         byte 0x8350: status
  2030. */
  2031. static void
  2032. DskDirectOutput(u8 dev, u8 secs, u16 fname, u16 parms)
  2033. {
  2034.     fiad_tifile    local, *tf = &local;
  2035.     OSError err;
  2036.  
  2037.     logger(_L | 0, "DskDirectOutput\n");
  2038.  
  2039.     if (sub_setup_file(tf, dev, fname) != 0) {
  2040.         sub_set_error(es_outofspace);
  2041.         return;
  2042.     }
  2043.  
  2044.     err = fiad_tifile_open_file(tf, 
  2045.                                 newfileformat,
  2046.                                 2 /*create*/, 
  2047.                                 false /*always*/,
  2048.                                 false /*readonly*/);
  2049.     if (err != OS_NOERR) {
  2050.         sub_error(tf, err, "could not open to write sectors", 
  2051.                   err == OS_PERMERR ? es_hardware :
  2052.                   es_badfuncerr /* es_filenotfound */);
  2053.         return;
  2054.     }
  2055.  
  2056.     DSKcomparefdrandsub(tf, (char *) VDP_PTR(fname));
  2057.  
  2058.     parms = 0x8300 + (parms >> 8);
  2059.  
  2060.     if (!secs) {
  2061.         // write FDR info
  2062.         tf->fdr.secsused = memory_read_word(parms + 2);
  2063.         tf->fdr.flags = memory_read_byte(parms + 4);
  2064.         tf->fdr.recspersec = memory_read_byte(parms + 5);
  2065.         tf->fdr.byteoffs = memory_read_byte(parms + 6);
  2066.         tf->fdr.reclen = memory_read_byte(parms + 7);
  2067.         tf->fdr.numrecs = TI2HOST(memory_read_word(parms + 8));
  2068.  
  2069.         // get filename
  2070.         memcpy(tf->fdr.filenam, VDP_PTR(fname), 10);
  2071.         tf->changedfdr = true;
  2072.  
  2073.         logger(_L|L_2, "Setting size of '%s' to %d sectors\n", 
  2074.                OS_SpecToString1(&tf->spec), 
  2075.                tf->fdr.secsused);
  2076.  
  2077.         // set file to given length
  2078.         if (!fiad_tifile_seek_to_sector(tf, tf->fdr.secsused)
  2079.         ||    !fiad_tifile_set_file_size(tf))
  2080.         {
  2081.             sub_set_error(es_hardware);
  2082.         }
  2083.         else
  2084.             sub_set_error(0);
  2085.  
  2086.         // Reset any bogus flags in here
  2087.         tf->changedfdr = fiad_fdr_repair(&tf->fdr, tf->fdr.secsused * 256);
  2088.  
  2089.         //logger(_L|L_2, "Number of sectors now %d\n", tf->fdr.secsused);
  2090.     } else {
  2091.         // write sectors
  2092.         u16         vaddr = memory_read_word(parms);
  2093.         u16         secnum = memory_read_word(parms + 2);
  2094.         u16         wrt = 0;
  2095.         int            ret;
  2096.  
  2097.         logger(_L | L_1,
  2098.              "writing %d sectors at sector #%d to file, reading from >%04X\n\n",
  2099.              secs, secnum, vaddr);
  2100.  
  2101.         sub_set_error(0);
  2102.  
  2103.         if (!fiad_tifile_seek_to_sector(tf, secnum)) {
  2104.             sub_set_error(es_hardware);
  2105.         } else {
  2106.             ret = fiad_tifile_write_binary_image(tf, VDP_PTR(vaddr), secs*256, &wrt);
  2107.             if (ret < 0) {
  2108.                 sub_set_error(es_hardware);
  2109.             }
  2110.  
  2111.             memory_write_byte(0x834D, (wrt + 255) >> 8);
  2112.         }
  2113.     }
  2114.     fiad_tifile_close_file(tf);
  2115. }
  2116.  
  2117. ////////////////////////////////////
  2118.  
  2119. extern vmModule emuDiskDSR;
  2120.  
  2121. mrstruct    dsr_rom_emudisk_handler = {
  2122.     emudiskdsr, emudiskdsr, NULL,            /* ROM */
  2123.     NULL,
  2124.     NULL,
  2125.     NULL,
  2126.     NULL
  2127. };
  2128.  
  2129. static      u32
  2130. cruwEmuDiskROM(u32 addr, u32 data, u32 num)
  2131. {
  2132.     if (data) {
  2133.         logger(_L | L_3, "emu disk DSR on\n");
  2134.         report_status(STATUS_DISK_ACCESS, 0, true);
  2135. //      debugger_enable(true);
  2136.         dsr_set_active(&emuDiskDSR);
  2137.         SET_AREA_HANDLER(0x4000, 0x2000, &dsr_rom_emudisk_handler);
  2138.         logger(_L | L_3, "DSR Read >4000 = >%2X\n\n", memory_read_byte(0x4000));
  2139.     } else {
  2140.         logger(_L | L_3, "emu disk DSR off\n");
  2141.         report_status(STATUS_DISK_ACCESS, 0, false);
  2142. //      debugger_enable(false);
  2143.         dsr_set_active(NULL);
  2144.         SET_AREA_HANDLER(0x4000, 0x2000, &zero_memory_handler);
  2145.     }
  2146.     return 0;
  2147. }
  2148.  
  2149. /********************************************/
  2150.  
  2151. static      vmResult
  2152. emudisk_getcrubase(u32 * base)
  2153. {
  2154.     *base = EMUDISKCRUBASE;
  2155.     return vmOk;
  2156. }
  2157.  
  2158. static      vmResult
  2159. emudisk_filehandler(u32 code)
  2160. {
  2161.     /*  If the real disk DSR is also installed, we limit
  2162.        our functional drives from DSK3 to DSK5, and pass
  2163.        references to DSK1 and DSK2 to the real DSR.
  2164.        (The order of the CRU allows this, since we're EMUDISKCRUBASE
  2165.        and the real DSR is 0x1100.) */
  2166.  
  2167.     my_assert(sizeof(pabrec) == 10);
  2168.  
  2169.     logger(_L | L_3, "handlefileoperations\n");
  2170. #ifdef REAL_DISK_DSR
  2171.     logger(_L | L_3, "code = %d\n\n", code);
  2172.  
  2173.     /*  pass control when the code is for the real disk 
  2174.        we didn't skip the return at *R11, so the TI ROM 
  2175.        will continue to next DSR */
  2176.  
  2177.     /*  this shouldn't happen unless non-shared emulated
  2178.        disk DSR was loaded. */
  2179.     if (dsr_is_real_disk(code - D_DSK1 + 1)) {
  2180.         logger(_L | L_3, "passing control to real disk");
  2181.         return vmOk;
  2182.     }
  2183.  
  2184.     /*  extended ops store drive [1-x] in low nybble */
  2185.     if (code >= D_DSKSUB && dsr_is_real_disk(memory_read_byte(0x834C) & 0xf)) {
  2186.         logger(_L | L_3, "passing control to real disk\n");
  2187.         return vmOk;
  2188.     }
  2189. #endif
  2190.  
  2191.     logger(_L | L_3, "handling code %d\n\n", code);
  2192.  
  2193.     /*
  2194.        match any of the drives for the volume name at 
  2195.        the VDP address in >8356.
  2196.  
  2197.        After matching the volume, add the length of the name
  2198.        to >8354 and >8356.
  2199.      */
  2200.     if (code == D_DSK) {
  2201.         char       *vname = (char *) VDP_PTR(memory_read_word(0x8356));
  2202.         u8          dev;
  2203.         int         len;
  2204.  
  2205.         if (*vname++ != '.')
  2206.             return vmOk;        /* bad device */
  2207.  
  2208.         dev = matchdrive(vname, &len);
  2209.         if (dev == 0) {
  2210.             logger(_L | L_1, "did not match DSK.%.*s\n", len, vname);
  2211.             return vmOk;        /* not matched */
  2212.         } else {
  2213.             logger(_L | L_1, "matched DSK.%.*s on DSK%d\n", len, vname, dev);
  2214.         }
  2215.  
  2216.         /*  Update ptrs  */
  2217.         memory_write_word(0x8356, memory_read_word(0x8356) + len);
  2218.         memory_write_word(0x8354, memory_read_word(0x8354) + len);
  2219.  
  2220.         /*  use this device  */
  2221.  
  2222.         code = dev;
  2223.     }
  2224.  
  2225.     switch (code) {
  2226.         /* PAB file operation on DSKx */
  2227.     case D_DSK1:
  2228.     case D_DSK2:
  2229.     case D_DSK3:
  2230.     case D_DSK4:
  2231.     case D_DSK5:
  2232.     {
  2233.         u16         pabaddr =
  2234.             memory_read_word(0x8356) - 
  2235.             memory_read_word(0x8354) -
  2236.             sizeof(pabrec);
  2237.         u8 opcode;
  2238.         u8 *fnptr;
  2239.  
  2240.         fnptr = VDP_PTR(pabaddr+9);
  2241.  
  2242.         opcode = *(fnptr-9);
  2243.  
  2244.         /* illegal opcode? */
  2245.         if (opcode > 9)
  2246.             pab_set_vdp_error(fnptr, e_illegal);
  2247.         else {
  2248.             static void (*opcodehandlers[]) (u8 dev, u8 *fn) = {
  2249.                 DSKOpen, DSKClose, DSKRead, DSKWrite,
  2250.                 DSKSeek, DSKLoad, DSKSave, DSKDelete,
  2251.                 DSKScratch, DSKStatus
  2252.             };
  2253.  
  2254.             logger(_L | L_2, "doing operation %d on DSK%d\n", opcode, code);
  2255.             opcodehandlers[opcode] (code, fnptr);
  2256.         }
  2257.  
  2258.         /*  return, indicating that the DSR handled the operation */
  2259.         register(11) += 2;
  2260.         break;
  2261.     }
  2262.  
  2263.         /* init disk dsr */
  2264.     case D_INIT:
  2265.     {
  2266.         int         x;
  2267.  
  2268.         for (x = 0; x < MAXFILES; x++) {
  2269.             memset((void *)&files[x], 0, sizeof(files[0]));
  2270.         }
  2271.  
  2272.         /* Set up timer stuff for catalogs */
  2273.         for (x = 0; x < MAXDRIVE; x++) {
  2274.             DskCatFlag[x] = 1;
  2275.             if (!DskCatTag[x])
  2276.                 DskCatTag[x] = TM_UniqueTag();
  2277.         }
  2278.  
  2279.         /*  also steal some RAM for the name compare buffer,
  2280.            so dependent programs can function */
  2281.         vdpnamebuffer = memory_read_word(0x8370) - 9;
  2282.         memory_write_word(0x8370, vdpnamebuffer - 1);
  2283.         break;
  2284.     }
  2285.  
  2286.         /* ???? */
  2287.     case D_16:
  2288.     {
  2289.         memory_write_byte(0x8350, 0);    /* no error */
  2290.         register(11) += 2;
  2291.         break;
  2292.     }
  2293.  
  2294.         /* call files(x) 
  2295.            just ignore it */
  2296.     case D_FILES:
  2297.         memory_write_word(0x832C, memory_read_word(0x832C) + 12);
  2298.         memory_write_byte(0x8342, 0);
  2299.         memory_write_byte(0x8350, 0);
  2300.         register(11) += 2;
  2301.         break;
  2302.  
  2303.  
  2304.     case D_SECRW:
  2305.     case D_FMTDISK:
  2306.     case D_PROT:
  2307.     case D_RENAME:
  2308.     case D_DINPUT:
  2309.     case D_DOUTPUT:
  2310.     {
  2311.         static void (*handlers[]) (u8 dev, u8 opt, u16 addr1, u16 addr2) = {
  2312.             DskSectorRW, DskFormatDisk, DskProtect,
  2313.             DskRename, DskDirectInput, DskDirectOutput
  2314.         };
  2315.         u8          dev;
  2316.         u8          opt;
  2317.         u16         addr1, addr2;
  2318.  
  2319.         dev = memory_read_byte(0x834c);
  2320.         opt = memory_read_byte(0x834d);
  2321.         addr1 = memory_read_word(0x834e);
  2322.         addr2 = memory_read_word(0x8350);
  2323.  
  2324.         logger(_L | L_2, "doing operation %d on DSK%d [%d, %04X, %04X]\n",
  2325.              code, dev, opt, addr1, addr2);
  2326.  
  2327.         if (dev <= MAXDRIVE) {
  2328.             handlers[code - D_SECRW] (dev, opt, addr1, addr2);
  2329.             register(11) += 2;
  2330.         }
  2331.         break;
  2332.     }
  2333.  
  2334.     default:
  2335.         logger(_L | L_1, "ignoring code = %d\n", code);
  2336.         return vmNotAvailable;
  2337.     }
  2338.     return vmOk;
  2339. }
  2340.  
  2341. /*************************************************/
  2342.  
  2343. //    Called to load/save each of the files[].
  2344. //
  2345. //    Since we're using one variable for each of these fields,
  2346. //    the second argument must be a string.  Sigh.
  2347. //    Since the arg is a string buffer, we can use a simple routine
  2348. //    to format numeric strings for us.
  2349. //
  2350. //    A special config var should close all the file[] entries
  2351. //    so we can sanely open them up at fixed positions again.
  2352.  
  2353. static char *num2str(int x)
  2354. {
  2355.     static char buf[32];
  2356.     sprintf(buf, "%d", x);
  2357.     return buf;
  2358. }
  2359.  
  2360. static int str2num(const char *str)
  2361. {
  2362.     int x;
  2363.  
  2364.     if (!strcasecmp(str,"true") || !strcasecmp(str, "on"))
  2365.         return 1;
  2366.     else if (!strcasecmp(str,"false") || !strcasecmp(str, "off"))
  2367.         return 0;
  2368.     x = atoi(str);
  2369.     return x;
  2370. }
  2371.  
  2372. //    Number of fields to save:
  2373.  
  2374. //    fiad_pabfile.fnptr (VDP addr)
  2375. //    fiad_pabfile.pab (binary)
  2376. //    fiad_pabfile.is_catalog (0/1)
  2377. //    fiad_pabfile.tf:
  2378. //        spec (pathname)
  2379. //        open (0/1)
  2380. //        readonly (0/1)
  2381. //        error (number)
  2382. //        cursec (number)
  2383. //        cureof (number)
  2384. //        curnrecs (number)
  2385. //        currec (number)
  2386. //    
  2387. //        handle, changed, changedfdr, fdr, sector
  2388. //        are reconstructed at re-load time
  2389.  
  2390. #define fiad_tifile_ENTS    12
  2391.  
  2392. static
  2393. DECL_SYMBOL_ACTION(emudisk_config_files)
  2394. {
  2395.     fiad_pabfile *f;
  2396.     char tmp[256];
  2397.     char *field;
  2398.     char *str;
  2399.     int val;
  2400.     OSError err;
  2401.  
  2402.     if (task == csa_READ) {
  2403.         if (iter >= (fiad_tifile_ENTS * (sizeof(files)/sizeof(fiad_tifile))))
  2404.             return 0;
  2405.  
  2406.         f = &files[iter / fiad_tifile_ENTS];
  2407.  
  2408.         // first arg is file #
  2409.         command_arg_set_num(SYM_ARG_1st, iter / fiad_tifile_ENTS);
  2410.  
  2411.         switch (iter % fiad_tifile_ENTS) {
  2412.         case 0: // fnptr
  2413.             // prework:  save changed stuff if necessary
  2414.             fiad_tifile_flush(&f->tf);
  2415.             command_arg_set_string(SYM_ARG_2nd, "vdp_filename_addr");
  2416.             command_arg_set_string(SYM_ARG_3rd, 
  2417.                                    !f->fnptr ? num2str(-1) :
  2418.                                    num2str(f->fnptr - VDP_PTR(0)));
  2419.             break;
  2420.  
  2421.         case 1: // pab 
  2422.             command_arg_set_string(SYM_ARG_2nd, "vdp_pab_data");
  2423.             emulate_bin2hex((u8 *)&f->pab, tmp, sizeof(f->pab));
  2424.             command_arg_set_string(SYM_ARG_3rd, tmp);
  2425.             break;
  2426.  
  2427.         case 2: // is_catalog
  2428.             command_arg_set_string(SYM_ARG_2nd, "is_catalog");
  2429.             command_arg_set_string(SYM_ARG_3rd, num2str(f->is_catalog));
  2430.             break;
  2431.  
  2432.             // start of fiad_pabfile stuff    
  2433.  
  2434.         case 3:    // spec
  2435.             command_arg_set_string(SYM_ARG_2nd, "path");
  2436.             command_arg_set_string(SYM_ARG_3rd, OS_SpecToString1(&f->tf.spec));
  2437.             break;
  2438.  
  2439.         case 4: // open
  2440.             command_arg_set_string(SYM_ARG_2nd, "is_open");
  2441.             command_arg_set_string(SYM_ARG_3rd, f->tf.open ? "true" : "false");
  2442.             break;
  2443.  
  2444.         case 5: // readonly
  2445.             command_arg_set_string(SYM_ARG_2nd, "is_read_only");
  2446.             command_arg_set_string(SYM_ARG_3rd, f->tf.readonly ? "true" : "false");
  2447.             break;
  2448.  
  2449.         case 6: // error
  2450.             command_arg_set_string(SYM_ARG_2nd, "error");
  2451.             command_arg_set_string(SYM_ARG_3rd, num2str(f->tf.error));
  2452.             break;
  2453.  
  2454.         case 7: // cursec
  2455.             command_arg_set_string(SYM_ARG_2nd, "sector");
  2456.             command_arg_set_string(SYM_ARG_3rd, num2str(f->tf.cursec));
  2457.             break;
  2458.  
  2459.         case 8: // curoffs
  2460.             command_arg_set_string(SYM_ARG_2nd, "sector_offset");
  2461.             command_arg_set_string(SYM_ARG_3rd, num2str(f->tf.curoffs));
  2462.             break;
  2463.  
  2464.         case 9: // curnrecs
  2465.             command_arg_set_string(SYM_ARG_2nd, "sector_record_count");
  2466.             command_arg_set_string(SYM_ARG_3rd, num2str(f->tf.curnrecs));
  2467.             break;
  2468.  
  2469.         case 10: // currec
  2470.             command_arg_set_string(SYM_ARG_2nd, "record_number");
  2471.             command_arg_set_string(SYM_ARG_3rd, num2str(f->tf.currec));
  2472.             break;
  2473.  
  2474.         case 11: // reopen (trigger)
  2475.             // this tells the loader to reopen the file
  2476.             command_arg_set_string(SYM_ARG_2nd, "reopen");
  2477.             command_arg_set_string(SYM_ARG_3rd, "");
  2478.             break;
  2479.  
  2480.         default:
  2481.             my_assert("error in emudisk_config_files");
  2482.         }
  2483.         return 1;
  2484.     }
  2485.  
  2486.     //    Load from config file
  2487.     //    spec should be the first thing we see
  2488.  
  2489.     command_arg_get_num(SYM_ARG_1st, &val);
  2490.  
  2491.     if (val < 0 || val >= sizeof(files) / sizeof(fiad_tifile)) {
  2492.         logger(_L | LOG_USER | LOG_ERROR, "Invalid file number\n");
  2493.         return 0;
  2494.     }
  2495.  
  2496.     f = &files[val];
  2497.     command_arg_get_string(SYM_ARG_2nd, &field);
  2498.     command_arg_get_string(SYM_ARG_3rd, &str);
  2499.  
  2500.     if (!strcasecmp(field, "vdp_filename_addr")) {
  2501.         int addr = str2num(str);
  2502.         f->fnptr = (addr == -1) ? 0L : VDP_PTR(addr & 0x3fff);
  2503.     } else if (!strcasecmp(field, "vdp_pab_data")) {
  2504.         emulate_hex2bin(str, (u8 *)&f->pab, sizeof(f->pab));
  2505.     } else if (!strcasecmp(field, "is_catalog")) {
  2506.         f->is_catalog = str2num(str);
  2507.     } else if (!strcasecmp(field, "path")) {
  2508.         if (OS_MakeSpec(str, &f->tf.spec, NULL) != OS_NOERR) {
  2509.             logger(_L | LOG_USER | LOG_ERROR, "Invalid file name '%s'\n", str);
  2510.             return 0;
  2511.         }
  2512.     } else if (!strcasecmp(field, "is_open")) {
  2513.         f->tf.open = !!str2num(str);
  2514.     } else if (!strcasecmp(field, "is_read_only")) {
  2515.         f->tf.readonly = !!str2num(str);
  2516.     } else if (!strcasecmp(field, "error")) {
  2517.         f->tf.error = str2num(str);
  2518.     } else if (!strcasecmp(field, "sector")) {
  2519.         f->tf.cursec = str2num(str);
  2520.     } else if (!strcasecmp(field, "sector_offset")) {
  2521.         f->tf.curoffs = str2num(str) & 0xff;
  2522.     } else if (!strcasecmp(field, "sector_record_count")) {
  2523.         f->tf.curnrecs = str2num(str);
  2524.     } else if (!strcasecmp(field, "record_number")) {
  2525.         f->tf.currec = str2num(str);
  2526.     } else if (!strcasecmp(field, "reopen")) {
  2527.  
  2528.         if (f->is_catalog) {
  2529.             if (!DSKinitcatalog(f, f->is_catalog)) {
  2530.                 logger(_L | LOG_ERROR | LOG_USER, "Could not reopen catalog at '%s'\n",
  2531.                        OS_PathSpecToString1(&f->tf.spec.path));
  2532.                 return 0;
  2533.             }
  2534.         } else if (f->tf.open) {
  2535.             err = fiad_tifile_reopen_file(&f->tf, 
  2536.                                         newfileformat,
  2537.                                         f->tf.readonly /*readonly*/);
  2538.  
  2539.             if (err != OS_NOERR) {
  2540.                 logger(_L | LOG_ERROR | LOG_USER, "Could not reopen file '%s'\n",
  2541.                        OS_SpecToString1(&f->tf.spec));
  2542.                 return 0;
  2543.             }
  2544.         }
  2545.  
  2546.         // read fdr and current sector for normal file
  2547.         if (f->tf.open) {
  2548.             if (!fiad_tifile_read_fdr(&f->tf))
  2549.                 return 0;
  2550.             if (!fiad_tifile_read_sector(&f->tf))
  2551.                 return 0;
  2552.         }
  2553.     } else {
  2554.         logger(_L | LOG_USER | LOG_ERROR, "Unrecognized file field: '%s'\n",
  2555.                field);
  2556.         return 0;
  2557.     }
  2558.  
  2559.     return 1;
  2560. }
  2561.  
  2562. static 
  2563. DECL_SYMBOL_ACTION(emudisk_close_files)
  2564. {
  2565.     pab_close_all_files();
  2566.     return 1;
  2567. }
  2568.  
  2569. /*************************************************/
  2570. static
  2571. DECL_SYMBOL_ACTION(emudisk_check_disk)
  2572. {
  2573.     int         dsknum = sym->name[3] - '0';
  2574.  
  2575.     if (!dsr_is_emu_disk(dsknum))
  2576.         logger(_LL | LOG_WARN,
  2577.               "FIAD server:  DSK%d (%s) is inaccessible when real (DOAD) disk DSR is loaded\n",
  2578.               dsknum, OS_PathSpecToString1(&emudiskpath[dsknum - 1]));
  2579.  
  2580.     return 1;
  2581. }
  2582.  
  2583.  
  2584. /*************************************************/
  2585.  
  2586. static      vmResult
  2587. emudisk_detect(void)
  2588. {
  2589.     return vmOk;
  2590. }
  2591.  
  2592. static      vmResult
  2593. emudisk_init(void)
  2594. {
  2595.     command_symbol_table *emudiskcommands =
  2596.         command_symbol_table_new("Emulated Disk DSR Options",
  2597.                                  "These commands control the emulated files-in-a-directory (FIAD) emulation",
  2598.  
  2599.      command_symbol_new("DSK1Path",
  2600.                         "Set DSK1 directory",
  2601.                         c_STATIC,
  2602.                         emudisk_check_disk
  2603.                         /* action */ ,
  2604.                         RET_FIRST_ARG,
  2605.                         command_arg_new_pathspec
  2606.                         ("dir",
  2607.                          "directory containing V9t9 files",
  2608.                          NULL /* action */ ,
  2609.                          &emudiskpath[0],
  2610.                          NULL /* next */ )
  2611.                         ,
  2612.      command_symbol_new("DSK2Path",
  2613.                         "Set DSK2 directory",
  2614.                         c_STATIC,
  2615.                         emudisk_check_disk
  2616.                         /* action */ ,
  2617.                         RET_FIRST_ARG,
  2618.                         command_arg_new_pathspec
  2619.                         ("dir",
  2620.                          "directory containing V9t9 files",
  2621.                          NULL /* action */ ,
  2622.                          &emudiskpath[1],
  2623.                          NULL /* next */ )
  2624.                         ,
  2625.      command_symbol_new("DSK3Path",
  2626.                         "Set DSK3 directory",
  2627.                         c_STATIC,
  2628.                         emudisk_check_disk
  2629.                         /* action */ ,
  2630.                         RET_FIRST_ARG,
  2631.                         command_arg_new_pathspec
  2632.                         ("dir",
  2633.                          "directory containing V9t9 files",
  2634.                          NULL /* action */ ,
  2635.                          &emudiskpath[2],
  2636.                          NULL /* next */ )
  2637.                         ,
  2638.      command_symbol_new("DSK4Path",
  2639.                         "Set DSK4 directory",
  2640.                         c_STATIC,
  2641.                         emudisk_check_disk
  2642.                         /* action */ ,
  2643.                         RET_FIRST_ARG,
  2644.                         command_arg_new_pathspec
  2645.                         ("dir",
  2646.                          "directory containing V9t9 files",
  2647.                          NULL /* action */ ,
  2648.                          &emudiskpath[3],
  2649.                          NULL /* next */ )
  2650.                         ,
  2651.      command_symbol_new("DSK5Path",
  2652.                         "Set DSK5 directory",
  2653.                         c_STATIC,
  2654.                         emudisk_check_disk
  2655.                         /* action */ ,
  2656.                         RET_FIRST_ARG,
  2657.                         command_arg_new_pathspec
  2658.                         ("dir",
  2659.                          "directory containing V9t9 files",
  2660.                          NULL /* action */ ,
  2661.                          &emudiskpath[4],
  2662.                          NULL /* next */ )
  2663.                         ,
  2664.  
  2665.     command_symbol_new("EmuDiskDSRFileName",
  2666.                        "Name of emulated DSR ROM image which fits in the CPU address space >4000...>5FFF; "
  2667.                        "this DSR defines DSK1 through DSK5",
  2668.                        c_STATIC,
  2669.                        NULL /* action */ ,
  2670.                        RET_FIRST_ARG,
  2671.                        command_arg_new_string("file",
  2672.                                               "name of binary image",
  2673.                                               NULL /* action */ ,
  2674.                                               ARG_STR(emudiskfilename),
  2675.                                               NULL /* next */ )
  2676.                        ,
  2677.     command_symbol_new("EmuDiskSharedDSRFileName|Emu2DiskDSRFileName",
  2678.                        "Name of emulated DSR ROM image which fits in the CPU address space >4000...>5FFF; "
  2679.                        "this DSR defines DSK3 through DSK5 and can share space with the real (DOAD) disk DSR",
  2680.                        c_STATIC,
  2681.                        NULL /* action */ ,
  2682.                        RET_FIRST_ARG,
  2683.                        command_arg_new_string("file",
  2684.                                               "name of binary image",
  2685.                                               NULL /* action */ ,
  2686.                                               ARG_STR(emu2diskfilename),
  2687.                                               NULL /* next */ )
  2688.                        ,
  2689.  
  2690.     command_symbol_new("KeepFileFormat",
  2691.                         "Toggle preservation of original file type (V9t9 or TIFILES)",
  2692.                         c_STATIC,
  2693.                         NULL /* action */ ,
  2694.                         RET_FIRST_ARG,
  2695.                         command_arg_new_num("on|off",
  2696.                                             "on: don't change existing file's type; "
  2697.                                             "off: change type to NewFileFormat",
  2698.                                             NULL    /* action */,
  2699.                                             ARG_NUM(keepfileformat),
  2700.                                             NULL /* next */ )
  2701.                         ,
  2702.  
  2703.     command_symbol_new("NewFileFormat",
  2704.                         "Select type for new files or converted files",
  2705.                         c_STATIC,
  2706.                         NULL    /* action */,
  2707.                         RET_FIRST_ARG,
  2708.                         command_arg_new_num("F_V9t9|F_TIFILES",
  2709.                                             "v9t9: original V9t9 file type; "
  2710.                                             "tifiles: TIFILES (XMODEM) format",
  2711.                                             NULL    /* action */,
  2712.                                             ARG_NUM
  2713.                                             (newfileformat),
  2714.                                             NULL /* next */ )
  2715.                         ,
  2716.  
  2717. /* NOTE: these two symbols cannot be seen unless surrounded in parentheses;
  2718.    I've gone ahead and added tokens for these */
  2719.  
  2720.                         command_symbol_new
  2721.                         ("F_V9t9",
  2722.                          NULL,
  2723.                          c_DONT_SAVE,
  2724.                          NULL    /* action */,
  2725.                          command_arg_new_num
  2726.                          (NULL, NULL,
  2727.                           NULL  /* action */ ,
  2728.                           NEW_ARG_CONST_NUM(F_V9t9),
  2729.                           NULL    /* next */),
  2730.                          NULL    /* args */
  2731.                      ,
  2732.                      command_symbol_new
  2733.                      ("F_TIFILES",
  2734.                       NULL,
  2735.                       c_DONT_SAVE,
  2736.                       NULL  /* action */ ,
  2737.                       command_arg_new_num
  2738.                       (NULL, NULL,
  2739.                        NULL
  2740.                        /* action */ ,
  2741.                        NEW_ARG_CONST_NUM
  2742.                        (F_TIFILES),
  2743.                        NULL    /* next */
  2744.                       ),
  2745.                       NULL    /* args */
  2746.                       ,
  2747.  
  2748.     command_symbol_new("UnknownFileIsText",
  2749.                         "Toggle treatment of unknown (non-V9t9 and non-TIFILES) files as "
  2750.                         "DOS/Unix/Mac text files",
  2751.                         c_STATIC,
  2752.                         NULL  /* action */ ,
  2753.                         RET_FIRST_ARG,
  2754.                         command_arg_new_num("on|off",
  2755.                                             "on:  read unknown file as text; off:  generate error",
  2756.                                             NULL
  2757.                                             /* action */ ,
  2758.                                             ARG_NUM(unknownfileistext),
  2759.                                             NULL /* next */ ),
  2760.  
  2761.     command_symbol_new ("AllowLongCatalogs",
  2762.                         "Allow catalogs read through DSKx. to return more than 127 records; "
  2763.                         "some programs may depend on this limit",
  2764.                         c_STATIC,
  2765.                         NULL/* action */ ,
  2766.                         RET_FIRST_ARG,
  2767.                         command_arg_new_num("on|off",
  2768.                                             "on: allow up to 32767 entries, off: restrict to 127 entries",
  2769.                                             NULL /* action */,
  2770.                                             ARG_NUM(allowlongcatalogs),
  2771.                                             NULL /* next */ ),
  2772.  
  2773.     command_symbol_new ("RepairDamagedFiles",
  2774.                         "Repair files with bad file sizes, even when opened read-only.  "
  2775.                         "This is a bit dangerous if you try to open a non-V9t9 file, "
  2776.                         "which will (obviously) appear damaged.  "
  2777.                         "V9t9 will try to rule out files that don't pass enough sanity checks, though.",
  2778.                         c_STATIC|c_CONFIG_ONLY,
  2779.                         NULL/* action */ ,
  2780.                         RET_FIRST_ARG,
  2781.                         command_arg_new_num("on|off",
  2782.                                             "on: repair damaged files, off: leave them alone",
  2783.                                             NULL /* action */,
  2784.                                             ARG_NUM(repairbadfiles),
  2785.                                             NULL /* next */ ),
  2786.  
  2787.     command_symbol_new ("FixupOldV9t9Filenames",
  2788.                         "Rename older V9t9 files which were mangled to fit in "
  2789.                         "the DOS 8.3 filename format.  These files were split at "
  2790.                         "the 8th character with a '.' and all illegal DOS characters "
  2791.                         "(" DOS_illegalchars ") were biased by 128 to make them "
  2792.                         "representable on that filesystem.\n"
  2793.                         "New V9t9 file mangling rules assume filesystems that "
  2794.                         "allow long filenames, so there is no splitting at "
  2795.                         "the 8th character, and illegal characters are translated "
  2796.                         "HTML-like into '&#xx;' where 'xx' is the hexadecimal "
  2797.                         "ASCII code for the characters.\n"
  2798.                         "Files renamed to the new format will not be compatible "
  2799.                         "with older versions of V9t9, unless, under Windows, "
  2800.                         "you refer to the files with the short format "
  2801.                         "(i.e., 'longfilenm' --> 'longfi~1').",
  2802.                         c_STATIC|c_CONFIG_ONLY,
  2803.                         NULL/* action */ ,
  2804.                         RET_FIRST_ARG,
  2805.                         command_arg_new_num("on|off",
  2806.                                             "on: rename old V9t9 files, "
  2807.                                             "off: leave them alone",
  2808.                                             NULL /* action */,
  2809.                                             ARG_NUM(fixupoldv9t9filenames),
  2810.                                             NULL /* next */ ),
  2811.  
  2812.     command_symbol_new ("GenerateOldV9t9Filenames",
  2813.                         "Generate filenames that conform to the old V9t9 DOS-"
  2814.                         "mangled format (see above) instead of the new format. "
  2815.                         "Not recommended unless you actively use the DOS version.",
  2816.                         c_STATIC|c_CONFIG_ONLY,
  2817.                         NULL/* action */ ,
  2818.                         RET_FIRST_ARG,
  2819.                         command_arg_new_num("on|off",
  2820.                                             "on: generate old V9t9 filenames, "
  2821.                                             "off: generate current V9t9 filenames",
  2822.                                             NULL /* action */,
  2823.                                             ARG_NUM(generateoldv9t9filenames),
  2824.                                             NULL /* next */ ),
  2825.  
  2826.     // these commands must precede EmuDiskFileInfo
  2827.     // so that reloading a session will do the right thing
  2828.     command_symbol_new("CloseAllFiles",
  2829.                        NULL /* help */,
  2830.                        c_STATIC|c_SESSION_ONLY,
  2831.                        emudisk_close_files,
  2832.                        NULL /* ret */,
  2833.                        NULL /* args */,
  2834.  
  2835.    command_symbol_new("dsrEmuDiskTopOfRam",
  2836.                       "Top address used in VDP RAM",
  2837.                       c_STATIC,
  2838.                       NULL /* action */,
  2839.                       RET_FIRST_ARG,
  2840.                       command_arg_new_num("address",
  2841.                                           "VDP RAM address, minus one",
  2842.                                           NULL /* action */,
  2843.                                           ARG_NUM(vdpnamebuffer),
  2844.                                           NULL /* next */)
  2845.     ,
  2846.  
  2847.     command_symbol_new("EmuDiskFileInfo",
  2848.                        NULL /* help */,
  2849.                        c_DYNAMIC|c_SESSION_ONLY,
  2850.                        emudisk_config_files,
  2851.                        NULL /* ret */,
  2852.                        command_arg_new_num("file number",
  2853.                                              "index of files[] array",
  2854.                                              NULL /* action */,
  2855.                                              NEW_ARG_NUM(u8),
  2856.                        command_arg_new_string("field name",
  2857.                                                 "name of field in pab_tifile to save",
  2858.                                                 NULL /* action */,
  2859.                                                 NEW_ARG_NEW_STRBUF,
  2860.                        command_arg_new_string("field contents",
  2861.                                                 "contents of field",
  2862.                                                 NULL /* action */,
  2863.                                                 NEW_ARG_NEW_STRBUF,
  2864.  
  2865.                        NULL /* next */))),
  2866.  
  2867.     NULL
  2868.     /* next */
  2869.  
  2870.  
  2871.         ))))))))))))))))))),
  2872.  
  2873.   NULL /* sub */ ,
  2874.  
  2875.   NULL    /* next */
  2876.   );
  2877.  
  2878.     command_symbol_table_add_subtable(universe, emudiskcommands);
  2879.  
  2880.     features |= FE_emudisk;
  2881.     return vmOk;
  2882. }
  2883.  
  2884. static      vmResult
  2885. emudisk_enable(void)
  2886. {
  2887.     logger(_L | L_1, "setting up emulated disk DSR ROM\n");
  2888.  
  2889.     /*  load shared ROM if real disk is also loaded */
  2890.     if (0 == loaddsr(romspath, systemromspath,
  2891. #ifdef REAL_DISK_DSR
  2892.                      (realDiskDSR.runtimeflags & vmRTInUse) ? emu2diskfilename :
  2893. #endif
  2894.                      emudiskfilename, "Emulated disk DSR ROM", emudiskdsr))
  2895.         return vmNotAvailable;
  2896.  
  2897.     if (cruadddevice(CRU_WRITE, EMUDISKCRUBASE, 1, cruwEmuDiskROM)) {
  2898.         return vmOk;
  2899.     } else
  2900.         return vmNotAvailable;
  2901. }
  2902.  
  2903. static      vmResult
  2904. emudisk_disable(void)
  2905. {
  2906.     crudeldevice(CRU_WRITE, EMUDISKCRUBASE, 1, cruwEmuDiskROM);
  2907.     return vmOk;
  2908. }
  2909.  
  2910. static      vmResult
  2911. emudisk_restart(void)
  2912. {
  2913.     return vmOk;
  2914. }
  2915.  
  2916. static      vmResult
  2917. emudisk_restop(void)
  2918. {
  2919.     return vmOk;
  2920. }
  2921.  
  2922. static      vmResult
  2923. emudisk_term(void)
  2924. {
  2925.     return vmOk;
  2926. }
  2927.  
  2928. static vmDSRModule emuDiskModule = {
  2929.     1,
  2930.     emudisk_getcrubase,
  2931.     emudisk_filehandler
  2932. };
  2933.  
  2934. vmModule    emuDiskDSR = {
  2935.     3,
  2936.     "Emulated disk DSR",
  2937.     "dsrEmuDisk",
  2938.  
  2939.     vmTypeDSR,
  2940.     vmFlagsNone,
  2941.  
  2942.     emudisk_detect,
  2943.     emudisk_init,
  2944.     emudisk_term,
  2945.     emudisk_enable,
  2946.     emudisk_disable,
  2947.     emudisk_restart,
  2948.     emudisk_restop,
  2949.     {(vmGenericModule *) & emuDiskModule}
  2950. };
  2951.