home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2562 / cpm.c < prev    next >
C/C++ Source or Header  |  1991-01-22  |  20KB  |  1,084 lines

  1. /*
  2. cpm
  3.  
  4. CP/M emulator.
  5. Written by D'Arcy J.M. Cain
  6. darcy@druid
  7.  
  8. */
  9.  
  10. #define        CPM_DATA
  11.  
  12. #include    <stdio.h>
  13. #include    <string.h>
  14. #include    <unistd.h>
  15. #include    <getopt.h>
  16. #include    <ctype.h>
  17. #include    <termio.h>
  18. #include    <io.h>
  19. #include    <signal.h>
  20. #include    <time.h>
  21. #include    <sys/stat.h>
  22. #include    <sys/fcntl.h>
  23. #include    "cpm.h"
  24.  
  25. #define        FILLER_SIZE    (16 - sizeof(FILE *))
  26.  
  27. #ifdef        DEBUG
  28. #define        debug(x)    fprintf(stderr, "%s: %4d  %s\n", __FILE__, __LINE__, x)
  29. #else
  30. #define        debug(x)
  31. #endif
  32.  
  33. typedef struct {
  34.     byte    dr;            /* drive: 0 = default, 1 = A, 2 = B, etc. */
  35.     char    name[8];    /* file name up to 8 characters */
  36.     char    typ[3];        /* file type up to 3 characters */
  37.     byte    ex, s1, s2, rc;
  38.     FILE    *fp;        /* Unix file pointer */
  39.     byte    filler[FILLER_SIZE];
  40.     byte    cr, r0, r1, r2;
  41. } FCB;
  42.  
  43. #ifdef        CPM_DEBUG
  44. #define        dump_registers(x) \
  45.     fprintf(x, reg_dump, A, BC, DE, HL, SP, PC,\
  46.                     SIGN ? 'S' : '-',\
  47.                     ZERO ? 'Z' : '-',\
  48.                     HALF_CARRY ? 'H' : '-',\
  49.                     PARITY ? 'P' : '-',\
  50.                     BCD ? 'N' : '-',\
  51.                     CARRY ? 'C' : '-',\
  52.                     ram[PC], dasm(ram + PC))
  53.  
  54. static int        dasm_flag = 0;
  55. static char    *reg_dump =
  56.     "A=%02.2x BC=%4.4x DE=%04.4x HL=%04.4x SP=%04.4x PC=%04.4x %c%c%c%c%c%c %02.2x %s\r\n";
  57. #else
  58. #define        dump_registers(x)
  59. #endif
  60.  
  61.  
  62. struct termio    old_term, termp;
  63. static int        console, user_break;
  64.  
  65. #ifndef        COMPILE_TEST
  66. static byte        *dma;
  67. static char        *tail;
  68. static int        out_delim = '$', def_drive = 1;
  69. static FILE        *reader = NULL, *punch = NULL, *list = NULL;
  70. #endif
  71.  
  72. /* clean up routine */
  73. void    cleanup(int sig)
  74. {
  75.     if (sig == SIGINT)
  76.     {
  77.         user_break = 1;
  78.         signal(SIGINT, cleanup);
  79.         return;
  80.     }
  81.  
  82.     ioctl(console, TCSETA, &old_term);
  83.     printf("\nWe now return you to your regularly scheduled OS\n");
  84.     exit(0);
  85. }
  86.  
  87. /* How CP/M drives map to Unix: */
  88. /* below is an array of 17 strings.  This corresponds to the allowable */
  89. /* drive names in CP/M (A to P) plus the default drive which actually */
  90. /* is a duplicate of one of the next 16.  At startup, The current Unix */
  91. /* directory is copied into cpm_drive[1] and becomes drive A.  This may */
  92. /* be modified by the startup sequence.  As well, the other drives may */
  93. /* be set up to other directories.  At the end of the startup sequence */
  94. /* the "strcpy(cpm_drive[0], cpm_drive[1]) causes drive A to be the CP/M */
  95. /* default drive.  From that point on, a switch to a new drive involves */
  96. /* simply copying that drive's directory into cpm_drive[0].  I did this */
  97. /* in this way since I expect changing drives to occur less frequently */
  98. /* than accessing files. */
  99.  
  100. #ifndef        COMPILE_TEST
  101. static char        cpm_drive[17][128];
  102.  
  103. /* Convert string to upper case */
  104. static void        strtoup(char *s)
  105. {
  106.     while (*s)
  107.     {
  108.         *s = toupper(*s);
  109.         s++;
  110.     }
  111. }
  112.  
  113. /* take a string, terminate it at the first white space and return the
  114.    string that follows.  I.E: "DIR *.COM" puts a 0 in the first space
  115.    and returns a pointer to "*.COM".  Note that "DIR" returns a pointer
  116.    to a NULL string - NOT a NULL pointer. */
  117. static char    *chop_cmd(char *buf)
  118. {
  119.     char    *ptr = buf;
  120.  
  121.     /* discard leading space */
  122.     while (isspace(*ptr))
  123.         ptr++;
  124.  
  125.     /* quad left the string */
  126.     strcpy(buf, ptr);
  127.  
  128.     /* terminate first word */
  129.     ptr = buf;
  130.     while (!isspace(*ptr) && *ptr)
  131.         ptr++;
  132.  
  133.     /* is there more? */
  134.     if (*ptr)
  135.     {
  136.         /* terminate first word */
  137.         *ptr++ = 0;
  138.  
  139.         /* skip any leading space */
  140.         while (isspace(*ptr))
  141.             ptr++;
  142.  
  143.     }
  144.  
  145.     return(ptr);
  146. }
  147.  
  148. /* given a drive unit (0 - 16) and a file name, returns Unix file name */
  149. static char *mk_name(int dr, char *fname)
  150. {
  151.     static char    full_name[148];
  152.  
  153.     sprintf(full_name, "%s/%s", cpm_drive[dr], fname);
  154.  
  155.     if (strchr(fname, '.') == NULL)
  156.         strcat(full_name, ".");
  157.  
  158.     return(full_name);
  159. }
  160.  
  161. /* given a file spec in standard CP/M format returns Unix file name */
  162. static char    *real_name(char *fname)
  163. {
  164.     /* does it include a drive letter? */
  165.     if (fname[1] == ':')
  166.         return(mk_name(*fname - '@', fname + 2));
  167.  
  168.     /* else use default drive */
  169.     return(mk_name(0, fname));
  170. }
  171.  
  172.  
  173. /* given a pointer to an FCB, returns real file name */
  174. char    *fcb2real(FCB *buf)
  175. {
  176.     char    temp[16], *p = temp;
  177.     int        k = 0;
  178.  
  179.     for (k = 0; k < 8; k++)
  180.         if (!isspace(buf->name[k]))
  181.             *p++ = buf->name[k];
  182.  
  183.     *p++ = '.';
  184.  
  185.     for (k = 0; k < 3; k++)
  186.         if (!isspace(buf->typ[k]))
  187.             *p++ = buf->typ[k];
  188.  
  189.     *p = 0;
  190.     return(mk_name(buf->dr, temp));
  191. }
  192.  
  193. /* calls system command with CP/M file name converted to Unix */
  194. static void        fsystem(const char *s, char *file)
  195. {
  196.     char    command[256];
  197.  
  198.     sprintf(command, s, real_name(file));
  199.     ioctl(console, TCSETA, &old_term);
  200.     system(command);
  201.     ioctl(console, TCSETA, &termp);
  202. }
  203.  
  204. /* formats a CP/M file name into an FCB */
  205. static void    mk_fcb(FCB *buf, char *fname)
  206. {
  207.     char    *p = fname;
  208.     int        k, l;
  209.  
  210.     /* clear FCB to start with */
  211.     memset(buf, 0, 16);
  212.  
  213.     /* check for drive name */
  214.     if (p[1] == ':')
  215.     {
  216.         debug("");
  217.         buf->dr = *p - '@';
  218.         p += 2;
  219.     }
  220.  
  221.     k = l = 0;
  222.  
  223.     /* format primary name */
  224.     for (k = 0; k < 8; k++)
  225.     {
  226.         debug("");
  227.  
  228.         if ((p[l] == '.') || (p[l] == 0))
  229.         {
  230.             debug("");
  231.  
  232.             while (k < 8)
  233.             {
  234.                 debug("");
  235.                 buf->name[k++] = ' ';
  236.             }
  237.         }
  238.         else if (p[l] == '*')
  239.         {
  240.             debug("");
  241.  
  242.             while (k < 8)
  243.                 buf->name[k++] = '?';
  244.  
  245.             debug("");
  246.             while (p[l] && (p[l] != '.'))
  247.                 l++;
  248.  
  249.             debug("");
  250.         }
  251.         else
  252.             buf->name[k] = p[l];
  253.  
  254.         debug("");
  255.         l++;
  256.     }
  257.  
  258.     debug("");
  259.  
  260.     /* format file type */
  261.     for (k = 0; k < 3; k++)
  262.     {
  263.         debug("");
  264.         if ((p[l] == '.') || (p[l] == 0))
  265.             while (k < 3)
  266.                 buf->typ[k++] = ' ';
  267.         else if (p[l] == '*')
  268.             while (k < 3)
  269.                 buf->typ[k++] = '?';
  270.         else
  271.             buf->typ[k] = p[l];
  272.  
  273.         debug("");
  274.         l++;
  275.     }
  276.  
  277.     debug("");
  278.     return;
  279. }
  280.  
  281. /* add extension to file name.  replace current one if necessary */
  282. static void    addext(char *s1, char *s2)
  283. {
  284.     char    *p;
  285.  
  286.     if ((p = strchr(s1, '.')) == NULL)
  287.         strcat(s1, ".");
  288.  
  289.     strcat(s1, s2);
  290. }
  291. #endif
  292.  
  293. /* get a character from the terminal */
  294. byte    getch(void)
  295. {
  296.     byte    c = 0;
  297.  
  298.     while (read(console, &c, 1) != 1)
  299.         ;
  300.  
  301.     return(c);
  302. }
  303.  
  304. /* see if character waiting */
  305. #define        kbhit()        ioctl(console, FIORDCHK, NULL)
  306.  
  307. /* get a string */
  308. int        get_str(char *buffer, int maxlen)
  309. {
  310.     int        k = 0, c;
  311.  
  312.     while ((c = getch()) != '\r' && c != '\n')
  313.     {
  314.         if (k == maxlen)
  315.             c = '\a';
  316.         else if (c == '\b')
  317.         {
  318.             if (k)
  319.             {
  320.                 fprintf(stderr, "\b \b");
  321.                 k--;
  322.             }
  323.         }
  324.         else
  325.         {
  326.             fputc(c, stdout);
  327.             buffer[k++] = c;
  328.         }
  329.  
  330.     }
  331.  
  332.     fprintf(stderr, "\r\n");
  333.     return(k);
  334. }
  335.  
  336. #ifdef    CPM_DEBUG
  337. #define        is_breakpoint(x)    breakpoint(0, (x))
  338. #define        list_breakpoints()    breakpoint(0, 0)
  339. #define        add_breakpoint(x)    breakpoint(1, (x))
  340. #define        del_breakpoint(x)    breakpoint(2, (x))
  341.  
  342. int        breakpoint(int cmd, int bpoint)
  343. {
  344.     static int    bp[64];
  345.     int            k;
  346.  
  347.     switch(cmd)
  348.     {
  349.         case 0:
  350.             for (k = 0; k < 64; k++)
  351.             {
  352.                 if (bp[k])
  353.                 {
  354.                     if (!bpoint)
  355.                         fprintf(stderr, "Breakpoint %2d: 0x%04.4x\r\n");
  356.                     else if (bp[k] == bpoint)
  357.                         return(1);
  358.                 }
  359.             }
  360.  
  361.             return(0);
  362.             break;
  363.  
  364.         case 1:
  365.             for (k = 0; k < 64; k++)
  366.                 if (bp[k] == bpoint)
  367.                     return(k);
  368.  
  369.             for (k = 0; k < 64; k++)
  370.             {
  371.                 if (!bp[k])
  372.                 {
  373.                     bp[k] = bpoint;
  374.                     return(k);
  375.                 }
  376.             }
  377.  
  378.             fprintf(stderr, "Too many breakpoints\r\n");
  379.             return(-1);
  380.             break;
  381.  
  382.         case 2:
  383.             for (k = 0; k < 64; k++)
  384.                 if (bp[k] == bpoint)
  385.                     bp[k] = 0;
  386.  
  387.             return(0);
  388.             break;
  389.     }
  390.  
  391.     return(-1);
  392. }
  393.  
  394.                 
  395. int        debugger()
  396. {
  397.     char    entry[128], *ptr;
  398.  
  399.     user_break = 0;
  400.  
  401.     for (;;)
  402.     {
  403.         fprintf(stderr, "\r\nDEBUG> ");
  404.         ptr = entry;
  405.  
  406.         while ((*ptr = getch()) != '\n')
  407.         {
  408.             if (*ptr == '\b')
  409.             {
  410.                 if (ptr > entry)
  411.                 {
  412.                     fprintf(stderr, "\b \b");
  413.                     ptr--;
  414.                 }
  415.             }
  416.             else
  417.                 fputc(*ptr++, stdout);
  418.         }
  419.  
  420.         *ptr = 0;
  421.         strtoup(entry);
  422.         fprintf(stderr, "\r\n");
  423.  
  424.         if (!*entry)
  425.             ;
  426.         else if (*entry == 'G')
  427.             return(0);
  428.         else if (*entry == 'Q')
  429.             return(1);
  430.         else if (*entry == 'R')
  431.             dump_registers(stdout);
  432.         else if (*entry == '+')
  433.             add_breakpoint(atoi(entry + 1));
  434.         else if (*entry == '-')
  435.             del_breakpoint(atoi(entry + 1));
  436.         else if (*entry == 'L')
  437.             list_breakpoints();
  438.         else if (isdigit(*entry))
  439.             dasm_flag = *entry - '0';
  440.  
  441. #if 0
  442.         else if (*entry == '')
  443.         else if (*entry == '')
  444.         else if (*entry == '')
  445.         else if (*entry == '')
  446. #endif
  447.         else
  448.             fprintf(stderr, "\aUnknown command: %c\n", *entry);
  449.     }
  450. }
  451. #endif
  452.  
  453. #ifndef    COMPILE_TEST
  454. /* run a program */
  455. static int    run(char *program)
  456. {
  457.     byte    *mem_ptr = ram + 0x100;
  458.     char    *fn, fn2[128];
  459.     int        c, k, pc;
  460.     FILE    *fp;
  461.     FCB        *fcb = NULL;
  462.     long    f_pos;
  463.     struct stat    s;
  464.  
  465.     debug("Start run function");
  466.  
  467.     /* find the program name */
  468.     strcpy((char *)(mem_ptr), program);
  469.     addext((char *)(mem_ptr), "COM");
  470.  
  471.     /* open the command file - return error if not found */
  472.     if ((fp = fopen((char *)(mem_ptr), "rb")) == NULL)
  473.         return(-1);
  474.  
  475.     debug("");
  476.  
  477.     /* load command into memory */
  478.     while (fread(mem_ptr, 1, 0x100, fp))
  479.     {
  480.         if (mem_ptr > (ram + 0xf000))
  481.         {
  482.             fprintf(stderr, "\aCommand file too big\r\n");
  483.             return(-2);
  484.         }
  485.  
  486.         mem_ptr += 0x100;
  487.     }
  488.  
  489.     fclose(fp);
  490.     debug("");
  491.  
  492.     /* set up registers and page zero */
  493.     PC = 0x100;
  494.     SP = 0xfff0;
  495.  
  496.     /* following for test purposes */
  497.     A = 1;
  498.     BC = 0x2345;
  499.     DE = 0x6789;
  500.     HL = 0xabcd;
  501.  
  502.     debug("");
  503.     strcpy((char *)(ram + 0x80), tail);
  504.     debug("");
  505.     mem_ptr = (byte *)(chop_cmd(tail));
  506.     debug("");
  507.     mk_fcb((FCB *)(ram + 0x5c), tail);
  508.     debug("");
  509.     mk_fcb((FCB *)(ram + 0x6c), (char *)(mem_ptr));
  510.     debug("");
  511.     memcpy(ram, page_zero, sizeof(page_zero));
  512.     debug("");
  513.     dma = ram + 0x80;
  514.     debug("");
  515.  
  516.     debug("");
  517.  
  518.     /* BDOS, BIOS and default stack */
  519.     for (k = 0xfc00; k < 0x10000; k++)
  520.         ram[k] = 0;
  521.  
  522.     debug("");
  523.  
  524.     /* run program.  loop stops if PC = 0 - "JP 0" e.g. */
  525.     while (PC)
  526.     {
  527.  
  528. #ifdef    CPM_DEBUG
  529.         if (dasm_flag > 1)
  530.             dump_registers(stderr);
  531.  
  532.         if ((user_break && debugger()) || is_breakpoint(PC))
  533. #else
  534.         if (user_break)
  535. #endif
  536.         {
  537.             fprintf(stderr, "\r\n\n\a* Program Interrupted by user *\r\n", ram[PC]);
  538.             dump_registers(stderr);
  539.             return(-5);
  540.         }
  541.  
  542.         debug("");
  543.         pc = PC;
  544.  
  545.         /* check if PC = BDOS entry point */
  546.         if (PC == BDOS)
  547.         {
  548.             /* do CP/M service if so */
  549.             switch (C)
  550.             {
  551.                 case 0:                        /* system reset */
  552. #ifdef    CPM_DEBUG
  553.                     if (dasm_flag)
  554.                         fprintf(stderr, "BDOS: System reset\r\n");
  555. #endif
  556.                     return(0);
  557.  
  558.                 case 1:                        /* conin */
  559. #ifdef    CPM_DEBUG
  560.                     if (dasm_flag)
  561.                         fprintf(stderr, "BDOS: Console in\r\n");
  562. #endif
  563.  
  564.                     fputc((A = getch()), stdout);
  565.                     break;
  566.  
  567.                 case 2:                        /* conout */
  568. #ifdef    CPM_DEBUG
  569.                     if (dasm_flag)
  570.                         fprintf(stderr, "BDOS: Console out (%c)\r\n", E >= ' ' ? E : '.');
  571. #endif
  572.  
  573.                     fputc(E, stdout);
  574.                     break;
  575.  
  576.                 case 3:                        /* RDR */ 
  577. #ifdef    CPM_DEBUG
  578.                     if (dasm_flag)
  579.                         fprintf(stderr, "BDOS: Reader in\r\n");
  580. #endif
  581.  
  582.                     if (reader != NULL)
  583.                         A = fgetc(reader);
  584.                     break;
  585.  
  586.                 case 4:                        /* PUN */
  587. #ifdef    CPM_DEBUG
  588.                     if (dasm_flag)
  589.                         fprintf(stderr, "BDOS: Punch out (%c)\r\n", E >= ' ' ? E : '.');
  590. #endif
  591.  
  592.                     if (punch != NULL)
  593.                         fputc(E, punch);
  594.                     break;
  595.  
  596.                 case 5:                        /* LST */
  597. #ifdef    CPM_DEBUG
  598.                     if (dasm_flag)
  599.                         fprintf(stderr, "BDOS: List out (%c)\r\n", E >= ' ' ? E : '.');
  600. #endif
  601.  
  602.                     if (list != NULL)
  603.                         fputc(E, list);
  604.                     break;
  605.  
  606.                 case 6:                        /* CONIO */
  607. #ifdef    CPM_DEBUG
  608.                     if (dasm_flag)
  609.                     {
  610.                         fprintf(stderr, "BDOS: Conio ");
  611.                         if (E == 0xff)
  612.                             fprintf(stderr, "in\r\n");
  613.                         else
  614.                             fprintf(stderr, "out (%c)\r\n", E >= ' ' ? E : '.');
  615.                     }
  616. #endif
  617.  
  618.                     if (E == 0xff)
  619.                         A = getch();
  620.                     else
  621.                         fputc(E, stdout);
  622.  
  623.                     break;
  624.  
  625.                 case 7:                        /* get IOBYTE */
  626. #ifdef    CPM_DEBUG
  627.                     if (dasm_flag)
  628.                         fprintf(stderr, "BDOS: Get IOBYTE\r\n");
  629. #endif
  630.  
  631.                     A = 0x95;
  632.                     break;
  633.  
  634.                 case 8:                        /* set IOBYTE */
  635. #ifdef    CPM_DEBUG
  636.                     if (dasm_flag)
  637.                         fprintf(stderr, "BDOS: Set IOBYTE\r\n");
  638. #endif
  639.  
  640.                     break;
  641.  
  642.                 case 28:                    /* write protect disk */
  643. #ifdef    CPM_DEBUG
  644.                     if (dasm_flag)
  645.                         fprintf(stderr, "BDOS: Write protect disk\r\n");
  646. #endif
  647.  
  648.                     break;
  649.  
  650.                 case 9:                        /* prstr */
  651. #ifdef    CPM_DEBUG
  652.                     if (dasm_flag)
  653.                         fprintf(stderr, "BDOS: Print string\r\n");
  654. #endif
  655.  
  656.                     mem_ptr = ram + DE;
  657.                     while (*mem_ptr != out_delim)
  658.                         fputc(*mem_ptr++, stdout);
  659.                     break;
  660.  
  661.                 case 10:                    /* rdstr */
  662. #ifdef    CPM_DEBUG
  663.                     if (dasm_flag)
  664.                         fprintf(stderr, "BDOS: Read console buffer\r\n");
  665. #endif
  666.  
  667.                     ram[DE + 1] = get_str((char *)(ram) + DE + 2, ram[DE]);
  668.                     break;
  669.  
  670.                 case 11:                /* CONSTAT */
  671. #ifdef    CPM_DEBUG
  672.                     if (dasm_flag)
  673.                         fprintf(stderr, "BDOS: Get console status\r\n");
  674. #endif
  675.  
  676.                     A = kbhit() ? 0xff : 0;
  677.                     break;
  678.  
  679.                 case 12:                /* VERSION */
  680. #ifdef    CPM_DEBUG
  681.                     if (dasm_flag)
  682.                         fprintf(stderr, "BDOS: Return version number\r\n");
  683. #endif
  684.  
  685.                     HL = 0x0022;
  686.                     break;
  687.  
  688.                 case 13:                /* RSTDSK */
  689. #ifdef    CPM_DEBUG
  690.                     if (dasm_flag)
  691.                         fprintf(stderr, "BDOS: Reset disk system\r\n");
  692. #endif
  693.  
  694.                     break;
  695.  
  696.                 case 14:                /* SELDSK */
  697. #ifdef    CPM_DEBUG
  698.                     if (dasm_flag)
  699.                         fprintf(stderr, "BDOS: Select disk %c:\r\n", E + 'A');
  700. #endif
  701.  
  702.                     k = E + 1;
  703.                     A = 0xff;
  704.  
  705.                     if ((k < 1) || (k > 16))
  706.                         H = 4;
  707.                     else if (*cpm_drive[k] == 0)
  708.                         H = 1;
  709.                     else
  710.                     {
  711.                         def_drive = k;
  712.                         strcpy(cpm_drive[0], cpm_drive[k]);
  713.                         A = 0;
  714.                     }
  715.                     break;
  716.  
  717.                 case 15:                /* OPENF */
  718.                     fcb = (FCB *)(ram + DE);
  719.                     fn = fcb2real(fcb);
  720.                     memset(&fcb->fp, 0, 24);
  721.  
  722. #ifdef    CPM_DEBUG
  723.                     if (dasm_flag)
  724.                         fprintf(stderr, "BDOS: Open file %s\r\n", fn);
  725. #endif
  726.  
  727.                     A = 0xff;
  728.  
  729.                     if (strchr(fn, '?') != NULL)
  730.                         HL = 9;
  731.                     else if ((fcb->dr < 0) || (fcb->dr > 16))
  732.                         HL = 4;
  733.                     else if (*cpm_drive[fcb->dr] == 0)
  734.                         HL = 1;
  735.                     else if ((fcb->fp = fopen(fn, "r+")) == NULL)
  736.                         HL = 0;
  737.                     else
  738.                         A = HL = 0;
  739.  
  740.                     break;
  741.  
  742.                 case 16:
  743. #ifdef    CPM_DEBUG
  744.                     if (dasm_flag)
  745.                         fprintf(stderr, "BDOS: Close file\r\n");
  746. #endif
  747.  
  748.                     fcb = (FCB *)(ram + DE);
  749.  
  750.                     if (fcb->fp != NULL)
  751.                         fclose(fcb->fp);
  752.  
  753.                     fcb->fp = NULL;
  754.                     break;
  755.  
  756.                 case 19:
  757.                     fcb = (FCB *)(ram + DE);
  758.  
  759. #ifdef    CPM_DEBUG
  760.                     if (dasm_flag)
  761.                         fprintf(stderr, "BDOS: Delete file\r\n", fcb2real(fcb));
  762. #endif
  763.  
  764.                     unlink(fcb2real(fcb));
  765.                     fcb->fp = NULL;
  766.                     break;
  767.  
  768.                 case 20:                    /* READ */
  769.                 case 33:                    /* READ RANDOM */
  770. #ifdef    CPM_DEBUG
  771.                     if (dasm_flag)
  772.                     {
  773.                         fprintf(stderr, "BDOS: Read ");
  774.                         if (C == 20)
  775.                             fprintf(stderr, "sequential");
  776.                         else
  777.                             fprintf(stderr, "random");
  778.                     }
  779. #endif
  780.  
  781.                     if ((fcb = (FCB *)(ram + DE)) == NULL)
  782.                     {
  783.                         A = 9;
  784.                         break;
  785.                     }
  786.  
  787.                     memset(dma, 0x1a, 0x80);
  788.  
  789.                     if (C == 33)
  790.                     {
  791.                         f_pos = (fcb->r2 << 16) + (fcb->r1 << 8) + fcb->r0;
  792.                         fseek(fcb->fp, f_pos * 0x80, SEEK_SET);
  793.                     }
  794.  
  795.                     if (fread(dma, 1, 0x80, fcb->fp) == 0)
  796.                         A = 1;
  797.                     else
  798.                         A = 0;
  799.  
  800.                     break;
  801.  
  802.                 case 21:                    /* WRITE */
  803.                 case 34:                    /* WRITE RANDOM */
  804.                 case 40:                    /* Write Random Zero Fill */
  805. #ifdef    CPM_DEBUG
  806.                     if (dasm_flag)
  807.                     {
  808.                         fprintf(stderr, "BDOS: Write ");
  809.                         if (C == 21)
  810.                             fprintf(stderr, "sequential\r\n");
  811.                         else if (C == 34)
  812.                             fprintf(stderr, "random\r\n");
  813.                         else
  814.                             fprintf(stderr, "random with zero fill\r\n");
  815.                     }
  816. #endif
  817.  
  818.                     if ((fcb = (FCB *)(ram + DE)) == NULL)
  819.                     {
  820.                         A = 9;
  821.                         break;
  822.                     }
  823.  
  824.                     if (C == 34)
  825.                     {
  826.                         f_pos = (fcb->r2 << 16) + (fcb->r1 << 8) + fcb->r0;
  827.                         fseek(fcb->fp, f_pos * 0x80, SEEK_SET);
  828.                     }
  829.  
  830.                     if (fwrite(dma, 1, 0x80, fcb->fp) == 0)
  831.                         A = 1;
  832.                     else
  833.                         A = 0;
  834.  
  835.                     break;
  836.  
  837.                 case 22:                    /* MAKEF */
  838. #ifdef    CPM_DEBUG
  839.                     if (dasm_flag)
  840.                         fprintf(stderr, "BDOS: Make file\r\n");
  841. #endif
  842.  
  843.                     fcb = (FCB *)(ram + DE);
  844.                     fn = fcb2real(fcb);
  845.  
  846.                     if ((fcb->fp = fopen(fn, "r")) != NULL)
  847.                     {
  848.                         fclose(fcb->fp);
  849.                         A = 0xff;
  850.                         break;
  851.                     }
  852.  
  853.                     memset(&fcb->fp, 0, 24);
  854.                     A = 0xff;
  855.  
  856.                     if (strchr(fn, '?') != NULL)
  857.                         HL = 9;
  858.                     else if ((fcb->dr < 0) || (fcb->dr > 16))
  859.                         HL = 4;
  860.                     else if (*cpm_drive[fcb->dr] == 0)
  861.                         HL = 1;
  862.                     else if ((fcb->fp = fopen(fn, "w")) == NULL)
  863.                         HL = 0;
  864.                     else
  865.                         A = HL = 0;
  866.  
  867.                     break;
  868.     
  869.                 case 23:                    /* RENAME */
  870. #ifdef    CPM_DEBUG
  871.                     if (dasm_flag)
  872.                         fprintf(stderr, "BDOS: Rename file\r\n");
  873. #endif
  874.  
  875.                     fcb = (FCB *)(ram + DE);
  876.                     strcpy(fn2, fcb2real(fcb));
  877.                     fn = fcb2real(fcb + 16);
  878.  
  879.                     if (link(fn2, fn) == -1)
  880.                         A = 0xff;
  881.                     else
  882.                     {
  883.                         unlink(fn2);
  884.                         A = 0;
  885.                     }
  886.                     break;
  887.  
  888.                 case 24:                    /* get log in vector */
  889. #ifdef    CPM_DEBUG
  890.                     if (dasm_flag)
  891.                         fprintf(stderr, "BDOS: Get login vector\r\n");
  892. #endif
  893.  
  894.                     c = 1;
  895.                     HL = 0;
  896.  
  897.                     for (k = 1; k <= 16; k++)
  898.                     {
  899.                         if (*cpm_drive[k])
  900.                             HL |= c;
  901.  
  902.                         c <<= 1;
  903.                     }
  904.  
  905.                     A = L;
  906.                     break;
  907.  
  908.                 case 25:
  909. #ifdef    CPM_DEBUG
  910.                     if (dasm_flag)
  911.                         fprintf(stderr, "BDOS: Return current disk\r\n");
  912. #endif
  913.  
  914.                     A = def_drive - 1;
  915.                     break;
  916.  
  917.                 case 26:
  918. #ifdef    CPM_DEBUG
  919.                     if (dasm_flag)
  920.                         fprintf(stderr, "BDOS: Set DMA address\r\n");
  921. #endif
  922.  
  923.                     dma = ram + DE;
  924.                     break;
  925.  
  926.                 case 29:                    /*  get R/O vector */
  927. #ifdef    CPM_DEBUG
  928.                     if (dasm_flag)
  929.                         fprintf(stderr, "BDOS: Get read only vector\r\n");
  930. #endif
  931.  
  932.                     HL = 0;
  933.                     break;
  934.  
  935.                 case 35:                    /* get file size */
  936. #ifdef    CPM_DEBUG
  937.                     if (dasm_flag)
  938.                         fprintf(stderr, "BDOS: Compute file size\r\n");
  939. #endif
  940.                     fcb = (FCB *)(ram + DE);
  941.                     if (stat(fcb2real(fcb), &s) == -1)
  942.                     {
  943.                         A = 0xff;
  944.                         break;
  945.                     }
  946.  
  947.                     A = 0;
  948.                     /* fall through */
  949.  
  950.                 case 36:                    /* set random record */
  951. #ifdef    CPM_DEBUG
  952.                     if (dasm_flag)
  953.                         fprintf(stderr, "BDOS: Set random record\r\n");
  954. #endif
  955.  
  956.                     if (C == 36)
  957.                     {
  958.                         if ((fcb = (FCB *)(ram + DE)) == NULL)
  959.                             break;
  960.  
  961.                         s.st_size = ftell(fcb->fp);
  962.                     }
  963.  
  964.                     s.st_size >>= 7;
  965.                     fcb->r0 = s.st_size & 0xff;
  966.                     s.st_size >>= 8;
  967.                     fcb->r1 = s.st_size & 0xff;
  968.                     s.st_size >>= 8;
  969.                     fcb->r2 = s.st_size & 0xff;
  970.  
  971.                     break;
  972.  
  973.                 case 37:                    /* reset drive */
  974. #ifdef    CPM_DEBUG
  975.                     if (dasm_flag)
  976.                         fprintf(stderr, "BDOS: Reset drive\r\n");
  977. #endif
  978.  
  979.                     A = 0;
  980.                     break;
  981.  
  982.                 default:
  983.                     fprintf(stderr, "\a\r\nInvalid BDOS call %d\r\n", C);
  984.                     return(-3);
  985.             }
  986.         }
  987.         else if (PC >= BIOS)
  988.         {
  989.             if (PC % 3)
  990.             {
  991.                 fprintf(stderr, "\a\r\nInvalid BIOS jump 0%04.4x\r\n", pc);
  992.                 PC = pc;
  993.                 dump_registers(stderr);
  994.                 return(-5);
  995.             }
  996.  
  997. #ifdef    CPM_DEBUG
  998.             if (dasm_flag)
  999.                 fprintf(stderr, "BIOS: Function %d\r\n", (PC - BIOS)/3);
  1000. #endif
  1001.  
  1002.             switch (PC)
  1003.             {
  1004.                 case bios(0):
  1005.                     return(0);
  1006.  
  1007.                 default:
  1008.                     PC = pc;
  1009.                     fprintf(stderr, "Unimplemented BIOS jump 0%04.4xH\r\n", PC);
  1010.                     dump_registers(stderr);
  1011.                     return(-6);
  1012.             }
  1013.         }
  1014.  
  1015.         if (decode())
  1016.         {
  1017.             PC = pc;
  1018.             fprintf(stderr, "\a\r\nInvalid processor instruction 0x%02.2x\r\n", ram[PC]);
  1019.             dump_registers(stderr);
  1020.             return(-4);
  1021.         }
  1022.  
  1023. #ifdef    CPM_DEBUG
  1024.         if (dasm_flag > 1 && pc >= BDOS)
  1025.             getch();
  1026. #endif
  1027.     }
  1028.  
  1029.     return(0);
  1030. }
  1031. #endif
  1032.  
  1033. FILE    *open_device(char *dev, char *typ)
  1034. {
  1035.     FILE    *fp;
  1036.  
  1037.     if (*dev == '!')
  1038.         fp = popen(dev + 1, typ);
  1039.     else
  1040.         fp = fopen(dev, typ);
  1041.  
  1042.     if (fp != NULL)
  1043.         return(fp);
  1044.  
  1045.     fprintf(stderr, "Error on %s\r\n", dev);
  1046.     perror("Can't open virtual device");
  1047.     exit(1);
  1048.     return(NULL);
  1049. }
  1050.  
  1051. #ifndef    COMPILE_TEST
  1052. static int    do_command(char *cmd_str)
  1053. {
  1054.     char    entry[256];
  1055.     FILE    *fp;
  1056.  
  1057.     if ((*cmd_str == ';') || (*cmd_str == '#'))
  1058.         return(0);
  1059.  
  1060.     strcpy(entry, cmd_str);
  1061.  
  1062.     if (*entry == '!')
  1063.     {
  1064.         int        r;
  1065.  
  1066.         ioctl(console, TCSETA, &old_term);
  1067.         r = system(entry + 1);
  1068.         ioctl(console, TCSETA, &termp);
  1069.         return(r);
  1070.     }
  1071.  
  1072.     strtoup(entry);
  1073.     tail = chop_cmd(entry);
  1074.     user_break = 0;
  1075.  
  1076.     if ((isspace(entry[2]) || (entry[2] == 0)) && (entry[1] == ':'))
  1077.     {
  1078.         *entry -= '@';
  1079.  
  1080.         if ((*entry < 1) || (*entry > MAX_DRIVES) || (*cpm_drive[*entry] == 0))
  1081.         {
  1082.             fprintf(stderr, "\a\r\nInvalid drive specification\r\n");
  1083.             return(-1);
  1084.