home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / EMULATOR / UNIX / CAIN1 / CPM.C < prev    next >
C/C++ Source or Header  |  2000-06-30  |  27KB  |  1,423 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    <stdlib.h>
  15. #include    <getopt.h>
  16. #include    <ctype.h>
  17. #include    <errno.h>
  18. #include    <termio.h>
  19. #include    <io.h>
  20. #include    <signal.h>
  21. #include    <time.h>
  22. #include    <sys/stat.h>
  23. #include    <sys/fcntl.h>
  24. #include    "cpm.h"
  25.  
  26. #define        FILLER_SIZE    (16 - sizeof(FILE *))
  27.  
  28. #ifdef        DEBUG
  29. #define        debug(x)    fprintf(stderr, "%s: %4d  %s\n", __FILE__, __LINE__, x)
  30. #else
  31. #define        debug(x)
  32. #endif
  33.  
  34. typedef struct {
  35.     byte    dr;            /* drive: 0 = default, 1 = A, 2 = B, etc. */
  36.     char    name[8];    /* file name up to 8 characters */
  37.     char    typ[3];        /* file type up to 3 characters */
  38.     byte    ex, s1, s2, rc;
  39.     FILE    *fp;        /* Unix file pointer */
  40.     byte    filler[FILLER_SIZE];
  41.     byte    cr, r0, r1, r2;
  42. } FCB;
  43.  
  44. #ifdef        CPM_DEBUG
  45. #define        dump_registers(output) \
  46.     fprintf(output, reg_dump, A, BC, DE, HL, SP, PC,\
  47.                     SIGN ? 'S' : '-',\
  48.                     ZERO ? 'Z' : '-',\
  49.                     HALF_CARRY ? 'H' : '-',\
  50.                     PARITY ? 'P' : '-',\
  51.                     BCD ? 'N' : '-',\
  52.                     CARRY ? 'C' : '-',\
  53.                     ram[PC], dasm(ram + PC))
  54.  
  55. static int        dasm_flag = 0;
  56. static char    *reg_dump =
  57.     "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";
  58. #else
  59. #define        dump_registers(output)
  60. #endif
  61.  
  62.  
  63. struct termio    old_term, termp;
  64. static int        console, user_break;
  65.  
  66. #ifndef        COMPILE_TEST
  67. static byte        *dma;
  68. static char        *tail;
  69. static int        out_delim = '$', def_drive = 1;
  70. static FILE        *reader = NULL, *punch = NULL, *list = NULL;
  71. #endif
  72.  
  73. /* clean up routine */
  74. void    cleanup(int sig)
  75. {
  76.     if (sig == SIGINT)
  77.     {
  78.         user_break = 1;
  79.         signal(SIGINT, cleanup);
  80.         return;
  81.     }
  82.  
  83.     ioctl(console, TCSETA, &old_term);
  84.     printf("\nWe now return you to your regularly scheduled OS\n");
  85.     exit(0);
  86. }
  87.  
  88. /* How CP/M drives map to Unix: */
  89. /* below is an array of 17 strings.  This corresponds to the allowable */
  90. /* drive names in CP/M (A to P) plus the default drive which actually */
  91. /* is a duplicate of one of the next 16.  At startup, The current Unix */
  92. /* directory is copied into cpm_drive[1] and becomes drive A.  This may */
  93. /* be modified by the startup sequence.  As well, the other drives may */
  94. /* be set up to other directories.  At the end of the startup sequence */
  95. /* the "strcpy(cpm_drive[0], cpm_drive[1]) causes drive A to be the CP/M */
  96. /* default drive.  From that point on, a switch to a new drive involves */
  97. /* simply copying that drive's directory into cpm_drive[0].  I did this */
  98. /* in this way since I expect changing drives to occur less frequently */
  99. /* than accessing files. */
  100.  
  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. #ifndef        COMPILE_TEST
  114. /* take a string, terminate it at the first white space and return the
  115.    string that follows.  I.E: "DIR *.COM" puts a 0 in the first space
  116.    and returns a pointer to "*.COM".  Note that "DIR" returns a pointer
  117.    to a NULL string - NOT a NULL pointer. */
  118. static char    *chop_cmd(char *buf)
  119. {
  120.     char    *ptr = buf;
  121.  
  122.     /* discard leading space */
  123.     while (isspace(*ptr))
  124.         ptr++;
  125.  
  126.     /* quad left the string */
  127.     strcpy(buf, ptr);
  128.  
  129.     /* terminate first word */
  130.     ptr = buf;
  131.     while (!isspace(*ptr) && *ptr)
  132.         ptr++;
  133.  
  134.     /* is there more? */
  135.     if (*ptr)
  136.     {
  137.         /* terminate first word */
  138.         *ptr++ = 0;
  139.  
  140.         /* skip any leading space */
  141.         while (isspace(*ptr))
  142.             ptr++;
  143.  
  144.     }
  145.  
  146.     return(ptr);
  147. }
  148.  
  149. /* given a drive unit (0 - 16) and a file name, returns Unix file name */
  150. static char *mk_name(int dr, char *fname)
  151. {
  152.     static char    full_name[148];
  153.  
  154.     sprintf(full_name, "%s/%s", cpm_drive[dr], fname);
  155.  
  156.     if (strchr(fname, '.') == NULL)
  157.         strcat(full_name, ".");
  158.  
  159.     return(full_name);
  160. }
  161.  
  162. /* given a file spec in standard CP/M format returns Unix file name */
  163. static char    *real_name(char *fname)
  164. {
  165.     /* does it include a drive letter? */
  166.     if (fname[1] == ':')
  167.         return(mk_name(*fname - '@', fname + 2));
  168.  
  169.     /* else use default drive */
  170.     return(mk_name(0, fname));
  171. }
  172.  
  173.  
  174. /* given a pointer to an FCB, returns real file name */
  175. char    *fcb2real(FCB *buf)
  176. {
  177.     char    temp[16], *p = temp;
  178.     int        k = 0;
  179.  
  180.     for (k = 0; k < 8; k++)
  181.         if (!isspace(buf->name[k]))
  182.             *p++ = buf->name[k];
  183.  
  184.     *p++ = '.';
  185.  
  186.     for (k = 0; k < 3; k++)
  187.         if (!isspace(buf->typ[k]))
  188.             *p++ = buf->typ[k];
  189.  
  190.     *p = 0;
  191.     return(mk_name(buf->dr, temp));
  192. }
  193.  
  194. /* calls system command with CP/M file name converted to Unix */
  195. static void        fsystem(const char *s, char *file)
  196. {
  197.     char    command[256];
  198.  
  199.     sprintf(command, s, real_name(file));
  200.     ioctl(console, TCSETA, &old_term);
  201.     system(command);
  202.     ioctl(console, TCSETA, &termp);
  203. }
  204.  
  205. /* formats a CP/M file name into an FCB */
  206. static void    mk_fcb(FCB *buf, char *fname)
  207. {
  208.     char    *p = fname;
  209.     int        k, l;
  210.  
  211.     /* clear FCB to start with */
  212.     memset(buf, 0, 16);
  213.  
  214.     /* check for drive name */
  215.     if (p[1] == ':')
  216.     {
  217.         debug("");
  218.         buf->dr = *p - '@';
  219.         p += 2;
  220.     }
  221.  
  222.     k = l = 0;
  223.  
  224.     /* format primary name */
  225.     for (k = 0; k < 8; k++)
  226.     {
  227.         debug("");
  228.  
  229.         if ((p[l] == '.') || (p[l] == 0))
  230.         {
  231.             debug("");
  232.  
  233.             while (k < 8)
  234.             {
  235.                 debug("");
  236.                 buf->name[k++] = ' ';
  237.             }
  238.         }
  239.         else if (p[l] == '*')
  240.         {
  241.             debug("");
  242.  
  243.             while (k < 8)
  244.                 buf->name[k++] = '?';
  245.  
  246.             debug("");
  247.             while (p[l] && (p[l] != '.'))
  248.                 l++;
  249.  
  250.             debug("");
  251.         }
  252.         else
  253.             buf->name[k] = p[l];
  254.  
  255.         debug("");
  256.         l++;
  257.     }
  258.  
  259.     debug("");
  260.  
  261.     /* format file type */
  262.     for (k = 0; k < 3; k++)
  263.     {
  264.         debug("");
  265.         if ((p[l] == '.') || (p[l] == 0))
  266.             while (k < 3)
  267.                 buf->typ[k++] = ' ';
  268.         else if (p[l] == '*')
  269.             while (k < 3)
  270.                 buf->typ[k++] = '?';
  271.         else
  272.             buf->typ[k] = p[l];
  273.  
  274.         debug("");
  275.         l++;
  276.     }
  277.  
  278.     debug("");
  279.     return;
  280. }
  281.  
  282. /* add extension to file name.  replace current one if necessary */
  283. static void    addext(char *s1, char *s2)
  284. {
  285.     char    *p;
  286.  
  287.     if ((p = strchr(s1, '.')) == NULL)
  288.         strcat(s1, ".");
  289.  
  290.     strcat(s1, s2);
  291. }
  292. #endif
  293.  
  294. /* get a character from the terminal */
  295. int        getch(void)
  296. {
  297.     byte    c = 0;
  298.     int        ret_val;
  299.  
  300.     while ((ret_val = read(console, &c, 1)) != 1)
  301.         if (ret_val == -1 && errno == EINTR)
  302.             return(-1);
  303.  
  304.     return(c);
  305. }
  306.  
  307. /* see if character waiting */
  308. #define        kbhit()        ioctl(console, FIORDCHK, NULL)
  309.  
  310. /* get a string */
  311. int        get_str(char *buffer, int maxlen)
  312. {
  313.     int        k = 0, c;
  314.  
  315.     /* break will interrupt input as if nothing entered */
  316.     while ((c = getch()) != '\r' && c != '\n' && c != -1)
  317.     {
  318.         if (k == maxlen)
  319.             c = '\a';
  320.         else if (c == '\b')
  321.         {
  322.             if (k)
  323.             {
  324.                 fprintf(stderr, "\b \b");
  325.                 k--;
  326.             }
  327.         }
  328.         else
  329.         {
  330.             fputc(c, stdout);
  331.             buffer[k++] = c;
  332.         }
  333.  
  334.     }
  335.  
  336.     fprintf(stderr, "\r\n");
  337.     return(c == -1 ? 0 : k);
  338. }
  339.  
  340. #ifdef    CPM_DEBUG
  341. #define        is_breakpoint(x)    breakpoint(0, (x))
  342. #define        list_breakpoints()    breakpoint(0, 0)
  343. #define        add_breakpoint(x)    breakpoint(1, (x))
  344. #define        del_breakpoint(x)    breakpoint(2, (x))
  345.  
  346. int        breakpoint(int cmd, int bpoint)
  347. {
  348.     static int    bp[64];
  349.     int            k;
  350.  
  351.     switch(cmd)
  352.     {
  353.         case 0:        /* set breakpoint if not 0 or print all */
  354.             for (k = 0; k < 64; k++)
  355.             {
  356.                 if (bp[k])
  357.                 {
  358.                     if (!bpoint)
  359.                         fprintf(stderr, "Breakpoint %2d: 0x%04.4x\r\n");
  360.                     else if (bp[k] == bpoint)
  361.                         return(1);
  362.                 }
  363.             }
  364.  
  365.             return(0);
  366.             break;
  367.  
  368.         case 1:        /* add breakpoint */
  369.             /* check if already in table */
  370.             for (k = 0; k < 64; k++)
  371.                 if (bp[k] == bpoint)
  372.                     return(k);
  373.  
  374.             /* else put it there if there is room */
  375.             for (k = 0; k < 64; k++)
  376.             {
  377.                 if (!bp[k])
  378.                 {
  379.                     bp[k] = bpoint;
  380.                     return(k);
  381.                 }
  382.             }
  383.  
  384.             fprintf(stderr, "Too many breakpoints\r\n");
  385.             return(-1);
  386.             break;
  387.  
  388.         case 2:        /* delete a breakpoint */
  389.             for (k = 0; k < 64; k++)
  390.                 if (bp[k] == bpoint)
  391.                     bp[k] = 0;
  392.  
  393.             return(0);
  394.             break;
  395.     }
  396.  
  397.     return(-1);
  398. }
  399.  
  400.                 
  401. int        debugger()
  402. {
  403.     char    entry[128], *ptr;
  404.     int        c;
  405.  
  406.     user_break = 0;
  407.  
  408.     for (;;)
  409.     {
  410.         fprintf(stderr, "\r\nDEBUG> ");
  411.         ptr = entry;
  412.  
  413.         while ((c = getch()) != '\n')
  414.         {
  415.             if (c == -1)
  416.                 return(1);
  417.  
  418.             if ((*ptr = c) == '\b')
  419.             {
  420.                 if (ptr > entry)
  421.                 {
  422.                     fprintf(stderr, "\b \b");
  423.                     ptr--;
  424.                 }
  425.             }
  426.             else
  427.                 fputc(*ptr++, stdout);
  428.         }
  429.  
  430.         *ptr = 0;
  431.         strtoup(entry);
  432.         fprintf(stderr, "\r\n");
  433.  
  434.         if (!*entry)
  435.             ;
  436.         else if (*entry == 'G')
  437.             return(0);
  438.         else if (*entry == 'Q')
  439.             return(1);
  440.         else if (*entry == 'R')
  441.             dump_registers(stdout);
  442.         else if (*entry == '+')
  443.             add_breakpoint(atoi(entry + 1));
  444.         else if (*entry == '-')
  445.             del_breakpoint(atoi(entry + 1));
  446.         else if (*entry == 'L')
  447.             list_breakpoints();
  448.         else if (isdigit(*entry))
  449.             dasm_flag = *entry - '0';
  450.         else if (*entry == '?')
  451.         {
  452.             printf("  G     Run from current PC\r\n");
  453.             printf("  Q     Quit to command interpreter\r\n");
  454.             printf("  R     Dump registers\r\n");
  455.             printf("  +###  Add breakpoint at numberr\r\n");
  456.             printf("  -###  Delete breakpoint at number\r\n");
  457.             printf("  L     list current breakpoints\r\n");
  458.             printf("  ###   Set debug level to number\r\n");
  459.         }
  460.         else
  461.             fprintf(stderr, "\aUnknown command: %c\n", *entry);
  462.     }
  463. }
  464. #endif
  465.  
  466. #ifndef    COMPILE_TEST
  467. /* run a program */
  468. static int    run(char *program)
  469. {
  470.     byte    *mem_ptr = ram + 0x100;
  471.     char    *fn, fn2[128];
  472.     int        c, k, pc;
  473.     FILE    *fp;
  474.     FCB        *fcb = NULL;
  475.     long    f_pos;
  476.     struct stat    s;
  477.  
  478.     debug("Start run function");
  479.  
  480.     /* find the program name */
  481.     strcpy((char *)(mem_ptr), program);
  482.     addext((char *)(mem_ptr), "COM");
  483.  
  484.     /* open the command file - return error if not found */
  485.     if ((fp = fopen((char *)(mem_ptr), "rb")) == NULL)
  486.         return(-1);
  487.  
  488.     debug("");
  489.  
  490.     /* load command into memory */
  491.     while (fread(mem_ptr, 1, 0x100, fp))
  492.     {
  493.         if (mem_ptr > (ram + 0xf000))
  494.         {
  495.             fprintf(stderr, "\aCommand file too big\r\n");
  496.             return(-2);
  497.         }
  498.  
  499.         mem_ptr += 0x100;
  500.     }
  501.  
  502.     fclose(fp);
  503.     debug("");
  504.  
  505.     /* set up registers and page zero */
  506.     PC = 0x100;
  507.     SP = 0xfff0;
  508.  
  509.     /* following for test purposes */
  510.     A = 1;
  511.     BC = 0x2345;
  512.     DE = 0x6789;
  513.     HL = 0xabcd;
  514.  
  515.     debug("");
  516.     strcpy((char *)(ram + 0x80), tail);
  517.     debug("");
  518.     mem_ptr = (byte *)(chop_cmd(tail));
  519.     debug("");
  520.     mk_fcb((FCB *)(ram + 0x5c), tail);
  521.     debug("");
  522.     mk_fcb((FCB *)(ram + 0x6c), (char *)(mem_ptr));
  523.     debug("");
  524.     memcpy(ram, page_zero, sizeof(page_zero));
  525.     debug("");
  526.     dma = ram + 0x80;
  527.     debug("");
  528.  
  529.     debug("");
  530.  
  531.     /* BDOS, BIOS and default stack */
  532.     for (k = 0xfc00; k < 0x10000; k++)
  533.         ram[k] = 0;
  534.  
  535.     debug("");
  536.  
  537.     /* run program.  loop stops if PC = 0 - "JP 0" e.g. */
  538.     while (PC)
  539.     {
  540.  
  541. #ifdef    CPM_DEBUG
  542.         if (dasm_flag > 1)
  543.             dump_registers(stderr);
  544.  
  545.         if ((user_break && debugger()) || is_breakpoint(PC))
  546. #else
  547.         if (user_break)
  548. #endif
  549.         {
  550.             fprintf(stderr, "\r\n\n\a* Program Interrupted by user *\r\n", ram[PC]);
  551.             dump_registers(stderr);
  552.             return(-5);
  553.         }
  554.  
  555.         debug("");
  556.         pc = PC;
  557.  
  558.         /* check if PC = BDOS entry point */
  559.         if (PC == BDOS)
  560.         {
  561.             /* do CP/M service if so */
  562.             switch (C)
  563.             {
  564.                 case 0:                        /* system reset */
  565. #ifdef    CPM_DEBUG
  566.                     if (dasm_flag)
  567.                         fprintf(stderr, "BDOS: System reset\r\n");
  568. #endif
  569.                     return(0);
  570.  
  571.                 case 1:                        /* conin */
  572. #ifdef    CPM_DEBUG
  573.                     if (dasm_flag)
  574.                         fprintf(stderr, "BDOS: Console in\r\n");
  575. #endif
  576.  
  577.                     if ((A = getch()) == -1)
  578.                         A = '*';
  579.  
  580.                     fputc(A, stdout);
  581.                     break;
  582.  
  583.                 case 2:                        /* conout */
  584. #ifdef    CPM_DEBUG
  585.                     if (dasm_flag)
  586.                         fprintf(stderr, "BDOS: Console out (%c)\r\n", E >= ' ' ? E : '.');
  587. #endif
  588.  
  589.                     fputc(E, stdout);
  590.                     break;
  591.  
  592.                 case 3:                        /* RDR */ 
  593. #ifdef    CPM_DEBUG
  594.                     if (dasm_flag)
  595.                         fprintf(stderr, "BDOS: Reader in\r\n");
  596. #endif
  597.  
  598.                     if (reader != NULL)
  599.                         A = fgetc(reader);
  600.                     break;
  601.  
  602.                 case 4:                        /* PUN */
  603. #ifdef    CPM_DEBUG
  604.                     if (dasm_flag)
  605.                         fprintf(stderr, "BDOS: Punch out (%c)\r\n", E >= ' ' ? E : '.');
  606. #endif
  607.  
  608.                     if (punch != NULL)
  609.                         fputc(E, punch);
  610.                     break;
  611.  
  612.                 case 5:                        /* LST */
  613. #ifdef    CPM_DEBUG
  614.                     if (dasm_flag)
  615.                         fprintf(stderr, "BDOS: List out (%c)\r\n", E >= ' ' ? E : '.');
  616. #endif
  617.  
  618.                     if (list != NULL)
  619.                         fputc(E, list);
  620.                     break;
  621.  
  622.                 case 6:                        /* CONIO */
  623. #ifdef    CPM_DEBUG
  624.                     if (dasm_flag)
  625.                     {
  626.                         fprintf(stderr, "BDOS: Conio ");
  627.                         if (E == 0xff)
  628.                             fprintf(stderr, "in\r\n");
  629.                         else
  630.                             fprintf(stderr, "out (%c)\r\n", E >= ' ' ? E : '.');
  631.                     }
  632. #endif
  633.  
  634.                     if (E == 0xff)
  635.                         A = getch();
  636.                     else
  637.                         fputc(E, stdout);
  638.  
  639.                     break;
  640.  
  641.                 case 7:                        /* get IOBYTE */
  642. #ifdef    CPM_DEBUG
  643.                     if (dasm_flag)
  644.                         fprintf(stderr, "BDOS: Get IOBYTE\r\n");
  645. #endif
  646.  
  647.                     A = 0x95;
  648.                     break;
  649.  
  650.                 case 8:                        /* set IOBYTE */
  651. #ifdef    CPM_DEBUG
  652.                     if (dasm_flag)
  653.                         fprintf(stderr, "BDOS: Set IOBYTE\r\n");
  654. #endif
  655.  
  656.                     break;
  657.  
  658.                 case 28:                    /* write protect disk */
  659. #ifdef    CPM_DEBUG
  660.                     if (dasm_flag)
  661.                         fprintf(stderr, "BDOS: Write protect disk\r\n");
  662. #endif
  663.  
  664.                     break;
  665.  
  666.                 case 9:                        /* prstr */
  667. #ifdef    CPM_DEBUG
  668.                     if (dasm_flag)
  669.                         fprintf(stderr, "BDOS: Print string\r\n");
  670. #endif
  671.  
  672.                     mem_ptr = ram + DE;
  673.                     while (*mem_ptr != out_delim)
  674.                         fputc(*mem_ptr++, stdout);
  675.                     break;
  676.  
  677.                 case 10:                    /* rdstr */
  678. #ifdef    CPM_DEBUG
  679.                     if (dasm_flag)
  680.                         fprintf(stderr, "BDOS: Read console buffer\r\n");
  681. #endif
  682.  
  683.                     ram[DE + 1] = get_str((char *)(ram) + DE + 2, ram[DE]);
  684.                     break;
  685.  
  686.                 case 11:                /* CONSTAT */
  687. #ifdef    CPM_DEBUG
  688.                     if (dasm_flag)
  689.                         fprintf(stderr, "BDOS: Get console status\r\n");
  690. #endif
  691.  
  692.                     A = kbhit() ? 0xff : 0;
  693.                     break;
  694.  
  695.                 case 12:                /* VERSION */
  696. #ifdef    CPM_DEBUG
  697.                     if (dasm_flag)
  698.                         fprintf(stderr, "BDOS: Return version number\r\n");
  699. #endif
  700.  
  701.                     HL = 0x0022;
  702.                     break;
  703.  
  704.                 case 13:                /* RSTDSK */
  705. #ifdef    CPM_DEBUG
  706.                     if (dasm_flag)
  707.                         fprintf(stderr, "BDOS: Reset disk system\r\n");
  708. #endif
  709.  
  710.                     break;
  711.  
  712.                 case 14:                /* SELDSK */
  713. #ifdef    CPM_DEBUG
  714.                     if (dasm_flag)
  715.                         fprintf(stderr, "BDOS: Select disk %c:\r\n", E + 'A');
  716. #endif
  717.  
  718.                     k = E + 1;
  719.                     A = 0xff;
  720.  
  721.                     if ((k < 1) || (k > 16))
  722.                         H = 4;
  723.                     else if (*cpm_drive[k] == 0)
  724.                         H = 1;
  725.                     else
  726.                     {
  727.                         def_drive = k;
  728.                         strcpy(cpm_drive[0], cpm_drive[k]);
  729.                         A = 0;
  730.                     }
  731.                     break;
  732.  
  733.                 case 15:                /* OPENF */
  734.                     fcb = (FCB *)(ram + DE);
  735.                     fn = fcb2real(fcb);
  736.                     memset(&fcb->fp, 0, 24);
  737.  
  738. #ifdef    CPM_DEBUG
  739.                     if (dasm_flag)
  740.                         fprintf(stderr, "BDOS: Open file %s\r\n", fn);
  741. #endif
  742.  
  743.                     A = 0xff;
  744.  
  745.                     if (strchr(fn, '?') != NULL)
  746.                         HL = 9;
  747.                     else if ((fcb->dr < 0) || (fcb->dr > 16))
  748.                         HL = 4;
  749.                     else if (*cpm_drive[fcb->dr] == 0)
  750.                         HL = 1;
  751.                     else if ((fcb->fp = fopen(fn, "r+")) == NULL)
  752.                         HL = 0;
  753.                     else
  754.                         A = HL = 0;
  755.  
  756.                     break;
  757.  
  758.                 case 16:
  759. #ifdef    CPM_DEBUG
  760.                     if (dasm_flag)
  761.                         fprintf(stderr, "BDOS: Close file\r\n");
  762. #endif
  763.  
  764.                     fcb = (FCB *)(ram + DE);
  765.  
  766.                     if (fcb->fp != NULL)
  767.                         fclose(fcb->fp);
  768.  
  769.                     fcb->fp = NULL;
  770.                     break;
  771.  
  772.                 case 19:
  773.                     fcb = (FCB *)(ram + DE);
  774.  
  775. #ifdef    CPM_DEBUG
  776.                     if (dasm_flag)
  777.                         fprintf(stderr, "BDOS: Delete file\r\n", fcb2real(fcb));
  778. #endif
  779.  
  780.                     unlink(fcb2real(fcb));
  781.                     fcb->fp = NULL;
  782.                     break;
  783.  
  784.                 case 20:                    /* READ */
  785.                 case 33:                    /* READ RANDOM */
  786. #ifdef    CPM_DEBUG
  787.                     if (dasm_flag)
  788.                     {
  789.                         fprintf(stderr, "BDOS: Read ");
  790.                         if (C == 20)
  791.                             fprintf(stderr, "sequential");
  792.                         else
  793.                             fprintf(stderr, "random");
  794.                     }
  795. #endif
  796.  
  797.                     if ((fcb = (FCB *)(ram + DE)) == NULL)
  798.                     {
  799.                         A = 9;
  800.                         break;
  801.                     }
  802.  
  803.                     memset(dma, 0x1a, 0x80);
  804.  
  805.                     if (C == 33)
  806.                     {
  807.                         f_pos = (fcb->r2 << 16) + (fcb->r1 << 8) + fcb->r0;
  808.                         fseek(fcb->fp, f_pos * 0x80, SEEK_SET);
  809.                     }
  810.  
  811.                     if (fread(dma, 1, 0x80, fcb->fp) == 0)
  812.                         A = 1;
  813.                     else
  814.                         A = 0;
  815.  
  816.                     break;
  817.  
  818.                 case 21:                    /* WRITE */
  819.                 case 34:                    /* WRITE RANDOM */
  820.                 case 40:                    /* Write Random Zero Fill */
  821. #ifdef    CPM_DEBUG
  822.                     if (dasm_flag)
  823.                     {
  824.                         fprintf(stderr, "BDOS: Write ");
  825.                         if (C == 21)
  826.                             fprintf(stderr, "sequential\r\n");
  827.                         else if (C == 34)
  828.                             fprintf(stderr, "random\r\n");
  829.                         else
  830.                             fprintf(stderr, "random with zero fill\r\n");
  831.                     }
  832. #endif
  833.  
  834.                     if ((fcb = (FCB *)(ram + DE)) == NULL)
  835.                     {
  836.                         A = 9;
  837.                         break;
  838.                     }
  839.  
  840.                     if (C == 34)
  841.                     {
  842.                         f_pos = (fcb->r2 << 16) + (fcb->r1 << 8) + fcb->r0;
  843.                         fseek(fcb->fp, f_pos * 0x80, SEEK_SET);
  844.                     }
  845.  
  846.                     if (fwrite(dma, 1, 0x80, fcb->fp) == 0)
  847.                         A = 1;
  848.                     else
  849.                         A = 0;
  850.  
  851.                     break;
  852.  
  853.                 case 22:                    /* MAKEF */
  854. #ifdef    CPM_DEBUG
  855.                     if (dasm_flag)
  856.                         fprintf(stderr, "BDOS: Make file\r\n");
  857. #endif
  858.  
  859.                     fcb = (FCB *)(ram + DE);
  860.                     fn = fcb2real(fcb);
  861.  
  862.                     if ((fcb->fp = fopen(fn, "r")) != NULL)
  863.                     {
  864.                         fclose(fcb->fp);
  865.                         A = 0xff;
  866.                         break;
  867.                     }
  868.  
  869.                     memset(&fcb->fp, 0, 24);
  870.                     A = 0xff;
  871.  
  872.                     if (strchr(fn, '?') != NULL)
  873.                         HL = 9;
  874.                     else if ((fcb->dr < 0) || (fcb->dr > 16))
  875.                         HL = 4;
  876.                     else if (*cpm_drive[fcb->dr] == 0)
  877.                         HL = 1;
  878.                     else if ((fcb->fp = fopen(fn, "w")) == NULL)
  879.                         HL = 0;
  880.                     else
  881.                         A = HL = 0;
  882.  
  883.                     break;
  884.     
  885.                 case 23:                    /* RENAME */
  886. #ifdef    CPM_DEBUG
  887.                     if (dasm_flag)
  888.                         fprintf(stderr, "BDOS: Rename file\r\n");
  889. #endif
  890.  
  891.                     fcb = (FCB *)(ram + DE);
  892.                     strcpy(fn2, fcb2real(fcb));
  893.                     fn = fcb2real(fcb + 16);
  894.  
  895.                     if (link(fn2, fn) == -1)
  896.                         A = 0xff;
  897.                     else
  898.                     {
  899.                         unlink(fn2);
  900.                         A = 0;
  901.                     }
  902.                     break;
  903.  
  904.                 case 24:                    /* get log in vector */
  905. #ifdef    CPM_DEBUG
  906.                     if (dasm_flag)
  907.                         fprintf(stderr, "BDOS: Get login vector\r\n");
  908. #endif
  909.  
  910.                     c = 1;
  911.                     HL = 0;
  912.  
  913.                     for (k = 1; k <= 16; k++)
  914.                     {
  915.                         if (*cpm_drive[k])
  916.                             HL |= c;
  917.  
  918.                         c <<= 1;
  919.                     }
  920.  
  921.                     A = L;
  922.                     break;
  923.  
  924.                 case 25:
  925. #ifdef    CPM_DEBUG
  926.                     if (dasm_flag)
  927.                         fprintf(stderr, "BDOS: Return current disk\r\n");
  928. #endif
  929.  
  930.                     A = def_drive - 1;
  931.                     break;
  932.  
  933.                 case 26:
  934. #ifdef    CPM_DEBUG
  935.                     if (dasm_flag)
  936.                         fprintf(stderr, "BDOS: Set DMA address\r\n");
  937. #endif
  938.  
  939.                     dma = ram + DE;
  940.                     break;
  941.  
  942.                 case 29:                    /*  get R/O vector */
  943. #ifdef    CPM_DEBUG
  944.                     if (dasm_flag)
  945.                         fprintf(stderr, "BDOS: Get read only vector\r\n");
  946. #endif
  947.  
  948.                     HL = 0;
  949.                     break;
  950.  
  951.                 case 35:                    /* get file size */
  952. #ifdef    CPM_DEBUG
  953.                     if (dasm_flag)
  954.                         fprintf(stderr, "BDOS: Compute file size\r\n");
  955. #endif
  956.                     fcb = (FCB *)(ram + DE);
  957.                     if (stat(fcb2real(fcb), &s) == -1)
  958.                     {
  959.                         A = 0xff;
  960.                         break;
  961.                     }
  962.  
  963.                     A = 0;
  964.                     /* fall through */
  965.  
  966.                 case 36:                    /* set random record */
  967. #ifdef    CPM_DEBUG
  968.                     if (dasm_flag)
  969.                         fprintf(stderr, "BDOS: Set random record\r\n");
  970. #endif
  971.  
  972.                     if (C == 36)
  973.                     {
  974.                         if ((fcb = (FCB *)(ram + DE)) == NULL)
  975.                             break;
  976.  
  977.                         s.st_size = ftell(fcb->fp);
  978.                     }
  979.  
  980.                     s.st_size >>= 7;
  981.                     fcb->r0 = s.st_size & 0xff;
  982.                     s.st_size >>= 8;
  983.                     fcb->r1 = s.st_size & 0xff;
  984.                     s.st_size >>= 8;
  985.                     fcb->r2 = s.st_size & 0xff;
  986.  
  987.                     break;
  988.  
  989.                 case 37:                    /* reset drive */
  990. #ifdef    CPM_DEBUG
  991.                     if (dasm_flag)
  992.                         fprintf(stderr, "BDOS: Reset drive\r\n");
  993. #endif
  994.  
  995.                     A = 0;
  996.                     break;
  997.  
  998.                 default:
  999.                     fprintf(stderr, "\a\r\nInvalid BDOS call %d\r\n", C);
  1000.                     return(-3);
  1001.             }
  1002.         }
  1003.         else if (PC >= BIOS)
  1004.         {
  1005.             if (PC % 3)
  1006.             {
  1007.                 fprintf(stderr, "\a\r\nInvalid BIOS jump 0%04.4x\r\n", pc);
  1008.                 PC = pc;
  1009.                 dump_registers(stderr);
  1010.                 return(-5);
  1011.             }
  1012.  
  1013. #ifdef    CPM_DEBUG
  1014.             if (dasm_flag)
  1015.                 fprintf(stderr, "BIOS: Function %d\r\n", (PC - BIOS)/3);
  1016. #endif
  1017.  
  1018.             switch (PC)
  1019.             {
  1020.                 case bios(0):
  1021.                     return(0);
  1022.  
  1023.                 default:
  1024.                     PC = pc;
  1025.                     fprintf(stderr, "Unimplemented BIOS jump 0%04.4xH\r\n", PC);
  1026.                     dump_registers(stderr);
  1027.                     return(-6);
  1028.             }
  1029.         }
  1030.  
  1031.         if (decode())
  1032.         {
  1033.             PC = pc;
  1034.             fprintf(stderr, "\a\r\nInvalid processor instruction 0x%02.2x\r\n", ram[PC]);
  1035.             dump_registers(stderr);
  1036.             return(-4);
  1037.         }
  1038.  
  1039. #ifdef    CPM_DEBUG
  1040.         if (dasm_flag > 1 && pc >= BDOS)
  1041.             getch();
  1042. #endif
  1043.     }
  1044.  
  1045.     return(0);
  1046. }
  1047. #endif
  1048.  
  1049. FILE    *open_device(char *dev, char *typ)
  1050. {
  1051.     FILE    *fp;
  1052.  
  1053.     if (*dev == '!')
  1054.         fp = popen(dev + 1, typ);
  1055.     else
  1056.         fp = fopen(dev, typ);
  1057.  
  1058.     if (fp != NULL)
  1059.         return(fp);
  1060.  
  1061.     fprintf(stderr, "Error on %s\r\n", dev);
  1062.     perror("Can't open virtual device");
  1063.     exit(1);
  1064.     return(NULL);
  1065. }
  1066.  
  1067. #ifndef    COMPILE_TEST
  1068. static int    do_command(char *cmd_str)
  1069. {
  1070.     char    entry[256];
  1071.     FILE    *fp;
  1072.  
  1073.     if ((*cmd_str == ';') || (*cmd_str == '#'))
  1074.         return(0);
  1075.  
  1076.     strcpy(entry, cmd_str);
  1077.  
  1078.     if (*entry == '!')
  1079.     {
  1080.         int        r;
  1081.  
  1082.         ioctl(console, TCSETA, &old_term);
  1083.         r = system(entry + 1);
  1084.         ioctl(console, TCSETA, &termp);
  1085.         return(r);
  1086.     }
  1087.  
  1088.     strtoup(entry);
  1089.     tail = chop_cmd(entry);
  1090.     user_break = 0;
  1091.  
  1092.     if ((isspace(entry[2]) || (entry[2] == 0)) && (entry[1] == ':'))
  1093.     {
  1094.         *entry -= '@';
  1095.  
  1096.         if ((*entry < 1) || (*entry > MAX_DRIVES) || (*cpm_drive[*entry] == 0))
  1097.         {
  1098.             fprintf(stderr, "\a\r\nInvalid drive specification\r\n");
  1099.             return(-1);
  1100.         }
  1101.  
  1102.         def_drive = *entry;
  1103.         strcpy(cpm_drive[0],cpm_drive[def_drive]);
  1104.         entry[0] = entry[1] = ' ';
  1105.         tail = chop_cmd(entry);
  1106.     }
  1107.  
  1108.     if (*entry == 0)
  1109.         return(0);
  1110.  
  1111.     debug(entry);
  1112.  
  1113.     if (strcmp(entry, "DIR") == 0)
  1114.         fsystem("ls -C %s", tail);
  1115.     else if (strcmp(entry, "DUMP") == 0)
  1116.         fsystem("hd %s", tail);
  1117.     else if (strcmp(entry, "ED") == 0)
  1118.         fsystem("$EDITOR %s", tail);
  1119.     else if (strcmp(entry, "ERA") == 0)
  1120.         fsystem("rm %s", tail);
  1121. #ifdef    CPM_DEBUG
  1122.     else if (strcmp(entry, "DASM") == 0)
  1123.     {
  1124.         if(++dasm_flag > 2)
  1125.             dasm_flag = 0;
  1126.  
  1127.         fprintf(stderr, "DASM is %d\r\n", dasm_flag);
  1128.     }
  1129. #endif
  1130.     else if (strcmp(entry, "SAVE") == 0)
  1131.     {
  1132.         char    *fname = chop_cmd(tail);
  1133.         int        p = atoi(tail);
  1134.  
  1135.         if ((p == 0) || (*fname == 0))
  1136.             fprintf(stderr, "Usage: SAVE #pages filename\r\n");
  1137.         else
  1138.         {
  1139.             if ((fp = fopen(real_name(fname), "wb")) == NULL)
  1140.                 perror("\aCan't open save file");
  1141.             else
  1142.             {
  1143.                 if (fwrite(ram + 256, 256, p, fp) != p)
  1144.                     perror("\aCan't write to file");
  1145.  
  1146.                 fclose(fp);
  1147.             }
  1148.         }
  1149.     }
  1150.     else if (strcmp(entry, "TYPE") == 0)
  1151.     {
  1152.         char    *ptr;
  1153.  
  1154.         while (*tail)
  1155.         {
  1156.             ptr = tail;
  1157.             tail = chop_cmd(ptr);
  1158.             fsystem("cat %s", ptr);
  1159.         }
  1160.     }
  1161.     else if (strcmp(entry, "EXIT") == 0)
  1162.         cleanup(0);
  1163.     else
  1164. #ifdef    CPM_DEBUG
  1165.     {
  1166.         time_t    start = time(NULL);
  1167.         int        r = run(real_name(entry));
  1168.  
  1169.         fprintf(stderr, "Run took %ld seconds\n\r", time(NULL) - start);
  1170.         return(r);
  1171.     }
  1172. #else
  1173.         return(run(real_name(entry)));
  1174. #endif
  1175.  
  1176.     return(0);
  1177. }
  1178. #endif
  1179.  
  1180. void    main(int argc, char **argv)
  1181. {
  1182. #ifdef    COMPILE_TEST
  1183.     /* test code useful for testing different architectures */
  1184.     int        test;
  1185.  
  1186.     dasm_flag = 1;
  1187.     test = (int)(&acc);
  1188.     printf("Position of A = %d\n", (int)(&A) - test);
  1189.     printf("Position of FLAGS = %d\n", (int)(&FLAGS) - test);
  1190.  
  1191.     test = (int)(&gr);
  1192.     printf("Position of B = %d\n", (int)(&B) - test);
  1193.     printf("Position of C = %d\n", (int)(&C) - test);
  1194.     printf("Position of BC = %d\n", (int)(&BC) - test);
  1195.     printf("Position of D = %d\n", (int)(&D) - test);
  1196.     printf("Position of E = %d\n", (int)(&E) - test);
  1197.     printf("Position of DE = %d\n", (int)(&DE) - test);
  1198.     printf("Position of H = %d\n", (int)(&H) - test);
  1199.     printf("Position of L = %d\n", (int)(&L) - test);
  1200.     printf("Position of HL = %d\n", (int)(&HL) - test);
  1201.  
  1202.     AF = 0x1234;
  1203.     printf("AF = %04.4x, A = %02.2x, FLAGS = %02.2x\n", AF, A, FLAGS);
  1204.     printf("Flags: S=%d Z=%d H=%d P=%d N=%d C=%d\n",
  1205.             SIGN, ZERO, HALF_CARRY, PARITY, BCD, CARRY);
  1206.     acc_bank = 1;
  1207.     AF = 0x4321;
  1208.     printf("AF = %04.4x, A = %02.2x, FLAGS = %02.2x\n", AF, A, FLAGS);
  1209.     printf("Flags: S=%d Z=%d H=%d P=%d N=%d C=%d\n",
  1210.             SIGN, ZERO, HALF_CARRY, PARITY, BCD, CARRY);
  1211.  
  1212.     BC = 0x2345;
  1213.     printf("BC = %04.4x, B = %02.2x, C = %02.2x\n", BC, B, C);
  1214.     gr_bank = 1;
  1215.     BC = 0x5432;
  1216.     printf("BC = %04.4x, B = %02.2x, C = %02.2x\n", BC, B, C);
  1217.     gr_bank = 0;
  1218.  
  1219.     DE = 0x3456;
  1220.     printf("DE = %04.4x, D = %02.2x, E = %02.2x\n", DE, D, E);
  1221.     gr_bank = 1;
  1222.     DE = 0x6543;
  1223.     printf("DE = %04.4x, D = %02.2x, E = %02.2x\n", DE, D, E);
  1224.     gr_bank = 0;
  1225.  
  1226.     HL = 0x4567;
  1227.     printf("HL = %04.4x, H = %02.2x, L = %02.2x\n", HL, H, L);
  1228.     gr_bank = 1;
  1229.     HL = 0x7654;
  1230.     printf("HL = %04.4x, H = %02.2x, L = %02.2x\n", HL, H, L);
  1231.     gr_bank = 0;
  1232.  
  1233.     A = BC = DE = HL = SP = PC = 0;
  1234.  
  1235.     while (PC < TEST_SIZE)
  1236.     {
  1237.         dump_registers(stdout);
  1238.  
  1239.         if (decode())
  1240.         {
  1241.             printf("* * * Processor error * * *\n");
  1242.             exit(1);
  1243.         }
  1244.     }
  1245.  
  1246.     dump_registers(stdout);
  1247.  
  1248.     for (test = 0; test < 0x10; test++)
  1249.         printf("%02.2x ", ram[test]);
  1250.  
  1251.     printf("\n");
  1252.     for (test = 0xfff0; test < 0x10000; test++)
  1253.         printf("%02.2x ", ram[test]);
  1254.  
  1255.     printf("\nTest code ended normally\n");
  1256.     exit(0);
  1257. #else
  1258.  
  1259.     char    entry[256];
  1260.     int        c;
  1261.  
  1262.     getcwd(cpm_drive[1], 127);
  1263.     entry[0] = 0;
  1264.     debug("");
  1265.  
  1266.     while ((c = getopt(argc, argv, "d:c:r:p:h")) != -1)
  1267.     {
  1268.         char    *ptr;
  1269.         int        k;
  1270.  
  1271.         switch (c)
  1272.         {
  1273.             case 'd':
  1274.                 ptr = optarg + 2;
  1275.  
  1276.                 if (optarg[1] == ':')
  1277.                     k = toupper(*optarg) - '@';
  1278.                 else
  1279.                 {
  1280.                     k = 1;
  1281.                     ptr = optarg;
  1282.  
  1283.                     while ((k < 17) && (*cpm_drive[k]))
  1284.                         k++;
  1285.                 }
  1286.  
  1287.                 if ((k < 1) || (k > 16))
  1288.                 {
  1289.                     fprintf(stderr, "Can't set up %s\n", optarg);
  1290.                     exit(1);
  1291.                 }
  1292.  
  1293.                 strcpy(cpm_drive[k], ptr);
  1294.                 break;
  1295.  
  1296.             case 'c':
  1297.                 strcpy(entry, optarg);
  1298.                 break;
  1299.  
  1300.             case 'r':
  1301.                 reader = open_device(optarg, "r");
  1302.                 break;
  1303.  
  1304.             case 'p':
  1305.                 punch = open_device(optarg, "w");
  1306.                 break;
  1307.  
  1308.             case 'l':
  1309.                 list = open_device(optarg, "w");
  1310.                 break;
  1311.  
  1312.             default:
  1313.                 fprintf(stderr,
  1314. "Usage:\n");
  1315.                 fprintf(stderr,
  1316. "cpm [options]\n");
  1317.                 fprintf(stderr,
  1318. "  Options:\n");
  1319.                 fprintf(stderr,
  1320. "    -d [d:]directory    map CP/M drive to Unix directory.  If the\n");
  1321.                 fprintf(stderr,
  1322. "                        second character is not a colon then the next\n");
  1323.                 fprintf(stderr,
  1324. "                        available drive letter is used otherwise the\n");
  1325.                 fprintf(stderr,
  1326. "                        letter preceding the colon is used.\n");
  1327.                 fprintf(stderr,
  1328. "    -c command          runs the command file then exits.  builtins\n");
  1329.                 fprintf(stderr,
  1330. "                        not supported.  COM file must exist\n");
  1331.                 fprintf(stderr,
  1332. "    -[r|p|l] dev        This allows the I/O to be routed through the\n");
  1333.                 fprintf(stderr,
  1334. "                        Unix file system.  The devices mapped are as\n");
  1335.                 fprintf(stderr,
  1336. "                        follows: r = RDR input, p = PUN output and l for\n");
  1337.                 fprintf(stderr,
  1338. "                        LST output.  The dev argument is opened as a Unix\n");
  1339.                 fprintf(stderr,
  1340. "                        file and I/O for specified device is done through\n");
  1341.                 fprintf(stderr,
  1342. "                        it.  If the first character is '!' then the rest\n");
  1343.                 fprintf(stderr,
  1344. "                        of the line is taken as a command and popen() is\n");
  1345.                 fprintf(stderr,
  1346. "                        called to handle the I/O.\n");
  1347.                 fprintf(stderr,
  1348. "    -h                  Show this help screen\n");
  1349.  
  1350.                 exit(1);
  1351.         }
  1352.     }
  1353.  
  1354.     strcpy(cpm_drive[0], cpm_drive[1]);
  1355.     def_drive = 1;
  1356.  
  1357.     if ((console = open("/dev/tty", O_RDWR)) == -1)
  1358.     {
  1359.         perror("Can't open terminal");
  1360.         exit(1);
  1361.     }
  1362.  
  1363.     if (ioctl(console, TCGETA, &old_term) == -1)
  1364.     {
  1365.         perror("Can't get terminal parameters");
  1366.         exit(-1);
  1367.     }
  1368.  
  1369.     termp = old_term;
  1370.     termp.c_oflag =  0;
  1371.     termp.c_lflag =  ISIG;
  1372.     termp.c_cc[VEOF] = 1;
  1373.     termp.c_cc[VSWTCH] = 0xff;
  1374.     
  1375.     if (ioctl(console, TCSETA, &termp) == -1)
  1376.     {
  1377.         perror("Can't set terminal parameters");
  1378.         exit(1);
  1379.     }
  1380.  
  1381.     signal(SIGHUP, cleanup);
  1382.     signal(SIGINT, cleanup);
  1383.     signal(SIGQUIT, cleanup);
  1384.     signal(SIGTERM, cleanup);
  1385.     debug("Signals captured");
  1386.  
  1387.     setbuf(stdout, NULL);
  1388.  
  1389.     fprintf(stderr, "\nCP/U - Control Program for Unix\r\n");
  1390.     fprintf(stderr, "CP/M emulator Version 0.900\r\n");
  1391.     fprintf(stderr, "Written by D'Arcy J.M. Cain\r\n");
  1392.     fprintf(stderr, "darcy@druid.UUCP\r\n");
  1393.  
  1394.     if (*entry)
  1395.     {
  1396.         do_command(entry);
  1397.         ioctl(console, TCSETA, &old_term);
  1398.         exit(0);
  1399.     }
  1400.  
  1401.     debug("Start main loop");
  1402.  
  1403.     for (;;)
  1404.     {
  1405.         fprintf(stderr, "%c> ", def_drive + '@');
  1406.         entry[get_str(entry, 128)] = 0;
  1407.  
  1408.         if (*entry)
  1409.         {
  1410.             if (do_command(entry) == -1)
  1411.             {
  1412.                 debug("chop_cmd(entry)");
  1413.                 chop_cmd(entry);
  1414.                 debug("strtoup(entry)");
  1415.                 strtoup(entry);
  1416.                 debug("fprintf(stderr, \"%s?\r\n\", entry)");
  1417.                 fprintf(stderr, "%s?\r\n", entry);
  1418.             }
  1419.         }
  1420.     }
  1421. #endif
  1422. }
  1423.