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