home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2074 / cpm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  19.1 KB  |  1,085 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. #ifdef    CPM_DEBUG
  439.         else if (isdigit(*entry))
  440.             dasm_flag = *entry - '0';
  441. #endif
  442.  
  443. #if 0
  444.         else if (*entry == '')
  445.         else if (*entry == '')
  446.         else if (*entry == '')
  447.         else if (*entry == '')
  448. #endif
  449.         else
  450.             fprintf(stderr, "\aUnknown command: %c\n", *entry);
  451.     }
  452. }
  453. #endif
  454.  
  455. #ifndef    COMPILE_TEST
  456. /* run a program */
  457. static int    run(char *program)
  458. {
  459.     byte    *mem_ptr = ram + 0x100;
  460.     char    *fn, fn2[128];
  461.     int        c, k, pc;
  462.     FILE    *fp;
  463.     FCB        *fcb = NULL;
  464.     long    f_pos;
  465.     struct stat    s;
  466.  
  467.     debug("Start run function");
  468.  
  469.     /* find the program name */
  470.     strcpy((char *)(mem_ptr), program);
  471.     addext((char *)(mem_ptr), "COM");
  472.  
  473.     /* open the command file - return error if not found */
  474.     if ((fp = fopen((char *)(mem_ptr), "rb")) == NULL)
  475.         return(-1);
  476.  
  477.     debug("");
  478.  
  479.     /* load command into memory */
  480.     while (fread(mem_ptr, 1, 0x100, fp))
  481.     {
  482.         if (mem_ptr > (ram + 0xf000))
  483.         {
  484.             fprintf(stderr, "\aCommand file too big\r\n");
  485.             return(-2);
  486.         }
  487.  
  488.         mem_ptr += 0x100;
  489.     }
  490.  
  491.     fclose(fp);
  492.     debug("");
  493.  
  494.     /* set up registers and page zero */
  495.     PC = 0x100;
  496.     SP = 0xfff0;
  497.  
  498.     /* following for test purposes */
  499.     A = 1;
  500.     BC = 0x2345;
  501.     DE = 0x6789;
  502.     HL = 0x0abc;
  503.  
  504.     debug("");
  505.     strcpy((char *)(ram + 0x80), tail);
  506.     debug("");
  507.     mem_ptr = (byte *)(chop_cmd(tail));
  508.     debug("");
  509.     mk_fcb((FCB *)(ram + 0x5c), tail);
  510.     debug("");
  511.     mk_fcb((FCB *)(ram + 0x6c), (char *)(mem_ptr));
  512.     debug("");
  513.     memcpy(ram, page_zero, sizeof(page_zero));
  514.     debug("");
  515.     dma = ram + 0x80;
  516.     debug("");
  517.  
  518.     debug("");
  519.  
  520.     /* BDOS, BIOS and default stack */
  521.     for (k = 0xfc00; k < 0x10000; k++)
  522.         ram[k] = 0;
  523.  
  524.     debug("");
  525.  
  526.     /* run program.  loop stops if PC = 0 - "JP 0" e.g. */
  527.     while (PC)
  528.     {
  529.  
  530. #ifdef    CPM_DEBUG
  531.         if (dasm_flag > 1)
  532.             dump_registers(stderr);
  533.  
  534.         if ((user_break && debugger()) || is_breakpoint(PC))
  535. #else
  536.         if (user_break)
  537. #endif
  538.         {
  539.             fprintf(stderr, "\r\n\n\a* Program Interrupted by user *\r\n", ram[PC]);
  540.             dump_registers(stderr);
  541.             return(-5);
  542.         }
  543.  
  544.         debug("");
  545.         pc = PC;
  546.  
  547.         /* check if PC = BDOS entry point */
  548.         if (PC == BDOS)
  549.         {
  550.             /* do CP/M service if so */
  551.             switch (C)
  552.             {
  553.                 case 0:                        /* system reset */
  554. #ifdef    CPM_DEBUG
  555.                     if (dasm_flag)
  556.                         fprintf(stderr, "BDOS: System reset\r\n");
  557. #endif
  558.                     return(0);
  559.  
  560.                 case 1:                        /* conin */
  561. #ifdef    CPM_DEBUG
  562.                     if (dasm_flag)
  563.                         fprintf(stderr, "BDOS: Console in\r\n");
  564. #endif
  565.  
  566.                     fputc((A = getch()), stdout);
  567.                     break;
  568.  
  569.                 case 2:                        /* conout */
  570. #ifdef    CPM_DEBUG
  571.                     if (dasm_flag)
  572.                         fprintf(stderr, "BDOS: Console out (%c)\r\n", E >= ' ' ? E : '.');
  573. #endif
  574.  
  575.                     fputc(E, stdout);
  576.                     break;
  577.  
  578.                 case 3:                        /* RDR */ 
  579. #ifdef    CPM_DEBUG
  580.                     if (dasm_flag)
  581.                         fprintf(stderr, "BDOS: Reader in\r\n");
  582. #endif
  583.  
  584.                     if (reader != NULL)
  585.                         A = fgetc(reader);
  586.                     break;
  587.  
  588.                 case 4:                        /* PUN */
  589. #ifdef    CPM_DEBUG
  590.                     if (dasm_flag)
  591.                         fprintf(stderr, "BDOS: Punch out (%c)\r\n", E >= ' ' ? E : '.');
  592. #endif
  593.  
  594.                     if (punch != NULL)
  595.                         fputc(E, punch);
  596.                     break;
  597.  
  598.                 case 5:                        /* LST */
  599. #ifdef    CPM_DEBUG
  600.                     if (dasm_flag)
  601.                         fprintf(stderr, "BDOS: List out (%c)\r\n", E >= ' ' ? E : '.');
  602. #endif
  603.  
  604.                     if (list != NULL)
  605.                         fputc(E, list);
  606.                     break;
  607.  
  608.                 case 6:                        /* CONIO */
  609. #ifdef    CPM_DEBUG
  610.                     if (dasm_flag)
  611.                     {
  612.                         fprintf(stderr, "BDOS: Conio ");
  613.                         if (E == 0xff)
  614.                             fprintf(stderr, "in\r\n");
  615.                         else
  616.                             fprintf(stderr, "out (%c)\r\n", E >= ' ' ? E : '.');
  617.                     }
  618. #endif
  619.  
  620.                     if (E == 0xff)
  621.                         A = getch();
  622.                     else
  623.                         fputc(E, stdout);
  624.  
  625.                     break;
  626.  
  627.                 case 7:                        /* get IOBYTE */
  628. #ifdef    CPM_DEBUG
  629.                     if (dasm_flag)
  630.                         fprintf(stderr, "BDOS: Get IOBYTE\r\n");
  631. #endif
  632.  
  633.                     A = 0x95;
  634.                     break;
  635.  
  636.                 case 8:                        /* set IOBYTE */
  637. #ifdef    CPM_DEBUG
  638.                     if (dasm_flag)
  639.                         fprintf(stderr, "BDOS: Set IOBYTE\r\n");
  640. #endif
  641.  
  642.                     break;
  643.  
  644.                 case 28:                    /* write protect disk */
  645. #ifdef    CPM_DEBUG
  646.                     if (dasm_flag)
  647.                         fprintf(stderr, "BDOS: Write protect disk\r\n");
  648. #endif
  649.  
  650.                     break;
  651.  
  652.                 case 9:                        /* prstr */
  653. #ifdef    CPM_DEBUG
  654.                     if (dasm_flag)
  655.                         fprintf(stderr, "BDOS: Print string\r\n");
  656. #endif
  657.  
  658.                     mem_ptr = ram + DE;
  659.                     while (*mem_ptr != out_delim)
  660.                         fputc(*mem_ptr++, stdout);
  661.                     break;
  662.  
  663.                 case 10:                    /* rdstr */
  664. #ifdef    CPM_DEBUG
  665.                     if (dasm_flag)
  666.                         fprintf(stderr, "BDOS: Read console buffer\r\n");
  667. #endif
  668.  
  669.                     ram[DE + 1] = get_str((char *)(ram) + DE + 2, ram[DE]);
  670.                     break;
  671.  
  672.                 case 11:                /* CONSTAT */
  673. #ifdef    CPM_DEBUG
  674.                     if (dasm_flag)
  675.                         fprintf(stderr, "BDOS: Get console status\r\n");
  676. #endif
  677.  
  678.                     A = kbhit() ? 0xff : 0;
  679.                     break;
  680.  
  681.                 case 12:                /* VERSION */
  682. #ifdef    CPM_DEBUG
  683.                     if (dasm_flag)
  684.                         fprintf(stderr, "BDOS: Return version number\r\n");
  685. #endif
  686.  
  687.                     HL = 0x0022;
  688.                     break;
  689.  
  690.                 case 13:                /* RSTDSK */
  691. #ifdef    CPM_DEBUG
  692.                     if (dasm_flag)
  693.                         fprintf(stderr, "BDOS: Reset disk system\r\n");
  694. #endif
  695.  
  696.                     break;
  697.  
  698.                 case 14:                /* SELDSK */
  699. #ifdef    CPM_DEBUG
  700.                     if (dasm_flag)
  701.                         fprintf(stderr, "BDOS: Select disk %c:\r\n", E + 'A');
  702. #endif
  703.  
  704.                     k = E + 1;
  705.                     A = 0xff;
  706.  
  707.                     if ((k < 1) || (k > 16))
  708.                         H = 4;
  709.                     else if (*cpm_drive[k] == 0)
  710.                         H = 1;
  711.                     else
  712.                     {
  713.                         def_drive = k;
  714.                         strcpy(cpm_drive[0], cpm_drive[k]);
  715.                         A = 0;
  716.                     }
  717.                     break;
  718.  
  719.                 case 15:                /* OPENF */
  720.                     fcb = (FCB *)(ram + DE);
  721.                     fn = fcb2real(fcb);
  722.                     memset(&fcb->fp, 0, 24);
  723.  
  724. #ifdef    CPM_DEBUG
  725.                     if (dasm_flag)
  726.                         fprintf(stderr, "BDOS: Open file %s\r\n", fn);
  727. #endif
  728.  
  729.                     A = 0xff;
  730.  
  731.                     if (strchr(fn, '?') != NULL)
  732.                         HL = 9;
  733.                     else if ((fcb->dr < 0) || (fcb->dr > 16))
  734.                         HL = 4;
  735.                     else if (*cpm_drive[fcb->dr] == 0)
  736.                         HL = 1;
  737.                     else if ((fcb->fp = fopen(fn, "r+")) == NULL)
  738.                         HL = 0;
  739.                     else
  740.                         A = HL = 0;
  741.  
  742.                     break;
  743.  
  744.                 case 16:
  745. #ifdef    CPM_DEBUG
  746.                     if (dasm_flag)
  747.                         fprintf(stderr, "BDOS: Close file\r\n");
  748. #endif
  749.  
  750.                     fcb = (FCB *)(ram + DE);
  751.  
  752.                     if (fcb->fp != NULL)
  753.                         fclose(fcb->fp);
  754.  
  755.                     fcb->fp = NULL;
  756.                     break;
  757.  
  758.                 case 19:
  759.                     fcb = (FCB *)(ram + DE);
  760.  
  761. #ifdef    CPM_DEBUG
  762.                     if (dasm_flag)
  763.                         fprintf(stderr, "BDOS: Delete file\r\n", fcb2real(fcb));
  764. #endif
  765.  
  766.                     unlink(fcb2real(fcb));
  767.                     fcb->fp = NULL;
  768.                     break;
  769.  
  770.                 case 20:                    /* READ */
  771.                 case 33:                    /* READ RANDOM */
  772. #ifdef    CPM_DEBUG
  773.                     if (dasm_flag)
  774.                     {
  775.                         fprintf(stderr, "BDOS: Read ");
  776.                         if (C == 20)
  777.                             fprintf(stderr, "sequential");
  778.                         else
  779.                             fprintf(stderr, "random");
  780.                     }
  781. #endif
  782.  
  783.                     if ((fcb = (FCB *)(ram + DE)) == NULL)
  784.                     {
  785.                         A = 9;
  786.                         break;
  787.                     }
  788.  
  789.                     memset(dma, 0x1a, 0x80);
  790.  
  791.                     if (C == 33)
  792.                     {
  793.                         f_pos = (fcb->r2 << 16) + (fcb->r1 << 8) + fcb->r0;
  794.                         fseek(fcb->fp, f_pos * 0x80, SEEK_SET);
  795.                     }
  796.  
  797.                     if (fread(dma, 1, 0x80, fcb->fp) == 0)
  798.                         A = 1;
  799.                     else
  800.                         A = 0;
  801.  
  802.                     break;
  803.  
  804.                 case 21:                    /* WRITE */
  805.                 case 34:                    /* WRITE RANDOM */
  806.                 case 40:                    /* Write Random Zero Fill */
  807. #ifdef    CPM_DEBUG
  808.                     if (dasm_flag)
  809.                     {
  810.                         fprintf(stderr, "BDOS: Write ");
  811.                         if (C == 21)
  812.                             fprintf(stderr, "sequential\r\n");
  813.                         else if (C == 34)
  814.                             fprintf(stderr, "random\r\n");
  815.                         else
  816.                             fprintf(stderr, "random with zero fill\r\n");
  817.                     }
  818. #endif
  819.  
  820.                     if ((fcb = (FCB *)(ram + DE)) == NULL)
  821.                     {
  822.                         A = 9;
  823.                         break;
  824.                     }
  825.  
  826.                     if (C == 34)
  827.                     {
  828.                         f_pos = (fcb->r2 << 16) + (fcb->r1 << 8) + fcb->r0;
  829.                         fseek(fcb->fp, f_pos * 0x80, SEEK_SET);
  830.                     }
  831.  
  832.                     if (fwrite(dma, 1, 0x80, fcb->fp) == 0)
  833.                         A = 1;
  834.                     else
  835.                         A = 0;
  836.  
  837.                     break;
  838.  
  839.                 case 22:                    /* MAKEF */
  840. #ifdef    CPM_DEBUG
  841.                     if (dasm_flag)
  842.                         fprintf(stderr, "BDOS: Make file\r\n");
  843. #endif
  844.  
  845.                     fcb = (FCB *)(ram + DE);
  846.                     fn = fcb2real(fcb);
  847.  
  848.                     if ((fcb->fp = fopen(fn, "r")) != NULL)
  849.                     {
  850.                         fclose(fcb->fp);
  851.                         A = 0xff;
  852.                         break;
  853.                     }
  854.  
  855.                     memset(&fcb->fp, 0, 24);
  856.                     A = 0xff;
  857.  
  858.                     if (strchr(fn, '?') != NULL)
  859.                         HL = 9;
  860.                     else if ((fcb->dr < 0) || (fcb->dr > 16))
  861.                         HL = 4;
  862.                     else if (*cpm_drive[fcb->dr] == 0)
  863.                         HL = 1;
  864.                     else if ((fcb->fp = fopen(fn, "w")) == NULL)
  865.                         HL = 0;
  866.                     else
  867.                         A = HL = 0;
  868.  
  869.                     break;
  870.     
  871.                 case 23:                    /* RENAME */
  872. #ifdef    CPM_DEBUG
  873.                     if (dasm_flag)
  874.                         fprintf(stderr, "BDOS: Rename file\r\n");
  875. #endif
  876.  
  877.                     fcb = (FCB *)(ram + DE);
  878.                     strcpy(fn2, fcb2real(fcb));
  879.                     fn = fcb2real(fcb + 16);
  880.  
  881.                     if (link(fn2, fn) == -1)
  882.                         A = 0xff;
  883.                     else
  884.                     {
  885.                         unlink(fn2);
  886.                         A = 0;
  887.                     }
  888.                     break;
  889.  
  890.                 case 24:                    /* get log in vector */
  891. #ifdef    CPM_DEBUG
  892.                     if (dasm_flag)
  893.                         fprintf(stderr, "BDOS: Get login vector\r\n");
  894. #endif
  895.  
  896.                     c = 1;
  897.                     HL = 0;
  898.  
  899.                     for (k = 1; k <= 16; k++)
  900.                     {
  901.                         if (*cpm_drive[k])
  902.                             HL |= c;
  903.  
  904.                         c <<= 1;
  905.                     }
  906.  
  907.                     A = L;
  908.                     break;
  909.  
  910.                 case 25:
  911. #ifdef    CPM_DEBUG
  912.                     if (dasm_flag)
  913.                         fprintf(stderr, "BDOS: Return current disk\r\n");
  914. #endif
  915.  
  916.                     A = def_drive - 1;
  917.                     break;
  918.  
  919.                 case 26:
  920. #ifdef    CPM_DEBUG
  921.                     if (dasm_flag)
  922.                         fprintf(stderr, "BDOS: Set DMA address\r\n");
  923. #endif
  924.  
  925.                     dma = ram + DE;
  926.                     break;
  927.  
  928.                 case 29:                    /*  get R/O vector */
  929. #ifdef    CPM_DEBUG
  930.                     if (dasm_flag)
  931.                         fprintf(stderr, "BDOS: Get read only vector\r\n");
  932. #endif
  933.  
  934.                     HL = 0;
  935.                     break;
  936.  
  937.                 case 35:                    /* get file size */
  938. #ifdef    CPM_DEBUG
  939.                     if (dasm_flag)
  940.                         fprintf(stderr, "BDOS: Compute file size\r\n");
  941. #endif
  942.                     fcb = (FCB *)(ram + DE);
  943.                     if (stat(fcb2real(fcb), &s) == -1)
  944.                     {
  945.                         A = 0xff;
  946.                         break;
  947.                     }
  948.  
  949.                     A = 0;
  950.                     /* fall through */
  951.  
  952.                 case 36:                    /* set random record */
  953. #ifdef    CPM_DEBUG
  954.                     if (dasm_flag)
  955.                         fprintf(stderr, "BDOS: Set random record\r\n");
  956. #endif
  957.  
  958.                     if (C == 36)
  959.                     {
  960.                         if ((fcb = (FCB *)(ram + DE)) == NULL)
  961.                             break;
  962.  
  963.                         s.st_size = ftell(fcb->fp);
  964.                     }
  965.  
  966.                     s.st_size >>= 7;
  967.                     fcb->r0 = s.st_size & 0xff;
  968.                     s.st_size >>= 8;
  969.                     fcb->r1 = s.st_size & 0xff;
  970.                     s.st_size >>= 8;
  971.                     fcb->r2 = s.st_size & 0xff;
  972.  
  973.                     break;
  974.  
  975.                 case 37:                    /* reset drive */
  976. #ifdef    CPM_DEBUG
  977.                     if (dasm_flag)
  978.                         fprintf(stderr, "BDOS: Reset drive\r\n");
  979. #endif
  980.  
  981.                     A = 0;
  982.                     break;
  983.  
  984.                 default:
  985.                     fprintf(stderr, "\a\r\nInvalid BDOS call %d\r\n", C);
  986.                     return(-3);
  987.             }
  988.         }
  989.         else if (PC >= BIOS)
  990.         {
  991.             if (PC % 3)
  992.             {
  993.                 fprintf(stderr, "\a\r\nInvalid BIOS jump 0%04.4x\r\n", pc);
  994.                 PC = pc;
  995.                 dump_registers(stderr);
  996.                 return(-5);
  997.             }
  998.  
  999. #ifdef    CPM_DEBUG
  1000.             if (dasm_flag)
  1001.                 fprintf(stderr, "BIOS: Function %d\r\n", (PC - BIOS)/3);
  1002. #endif
  1003.  
  1004.             switch (PC)
  1005.             {
  1006.                 case bios(0):
  1007.                     return(0);
  1008.  
  1009.                 default:
  1010.                     PC = pc;
  1011.                     fprintf(stderr, "Unimplemented BIOS jump 0%04.4xH\r\n", PC);
  1012.                     dump_registers(stderr);
  1013.                     return(-6);
  1014.             }
  1015.         }
  1016.  
  1017.         if (decode())
  1018.         {
  1019.             PC = pc;
  1020.             fprintf(stderr, "\a\r\nInvalid processor instruction 0x%02.2x\r\n", ram[PC]);
  1021.             dump_registers(stderr);
  1022.             return(-4);
  1023.         }
  1024.  
  1025. #ifdef    CPM_DEBUG
  1026.         if (dasm_flag > 1 && pc >= BDOS)
  1027.             getch();
  1028. #endif
  1029.     }
  1030.  
  1031.     return(0);
  1032. }
  1033. #endif
  1034.  
  1035. FILE    *open_device(char *dev, char *typ)
  1036. {
  1037.     FILE    *fp;
  1038.  
  1039.     if (*dev == '!')
  1040.         fp = popen(dev + 1, typ);
  1041.     else
  1042.         fp = fopen(dev, typ);
  1043.  
  1044.     if (fp != NULL)
  1045.         return(fp);
  1046.  
  1047.     fprintf(stderr, "Error on %s\r\n", dev);
  1048.     perror("Can't open virtual device");
  1049.     exit(1);
  1050.     return(NULL);
  1051. }
  1052.  
  1053. #ifndef    COMPILE_TEST
  1054. static int    do_command(char *cmd_str)
  1055. {
  1056.     char    entry[256];
  1057.     FILE    *fp;
  1058.  
  1059.     if ((*cmd_str == ';') || (*cmd_str == '#'))
  1060.         return(0);
  1061.  
  1062.     strcpy(entry, cmd_str);
  1063.  
  1064.     if (*entry == '!')
  1065.     {
  1066.         int        r;
  1067.  
  1068.         ioctl(console, TCSETA, &old_term);
  1069.         r = system(entry + 1);
  1070.         ioctl(console, TCSETA, &termp);
  1071.         return(r);
  1072.     }
  1073.  
  1074.     strtoup(entry);
  1075.     tail = chop_cmd(entry);
  1076.     user_break = 0;
  1077.  
  1078.     if ((isspace(entry[2]) || (entry[2] == 0)) && (entry[1] == ':'))
  1079.     {
  1080.         *entry -= '@';
  1081.  
  1082.         if ((*entry < 1) || (*entry > MAX_DRIVES) || (*cpm_drive[*entry] == 0))
  1083.         {
  1084.             fprintf(stderr, "\a\r\nInvalid drive specification\r\n");
  1085.