home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / emulator / unix / z80pack / cpmsim / srcsim / iosim.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-12  |  18.2 KB  |  821 lines

  1. /*
  2.  * Z80SIM  -  a Z80-CPU simulator
  3.  *
  4.  * Copyright (C) 1987-92 by Udo Munk
  5.  *
  6.  * This modul contains a complex I/O-simulation for the Z80-CPU
  7.  * simulation. Because this is an example, what you can do with
  8.  * the CPU-emulation, you may change this modul for your needs,
  9.  * and use it, or parts of it, in your own I/O-simulations.
  10.  *
  11.  * History:
  12.  * 28-SEP-87 Development on TARGON/35 with AT&T Unix System V.3
  13.  * 19-MAY-89 Additions for CP/M 3.0 und MP/M
  14.  * 23-DEC-90 Ported to COHERENT 3.0
  15.  * 10-JUN-92 Some optimization done
  16.  * 25-JUN-92 Flush output of stdout only at every OUT to port 0
  17.  * 25-JUN-92 Comments in english and ported to COHERENT 4.0
  18.  */
  19.  
  20. /*
  21.  *    This module contains the I/O handlers for a simulation
  22.  *    of the hardware required for a CP/M system.
  23.  *
  24.  *    Used I/O ports:
  25.  *
  26.  *       0 - console status
  27.  *       1 - console data
  28.  *
  29.  *       2 - printer status
  30.  *       3 - printer data
  31.  *
  32.  *       4 - auxilary status
  33.  *       5 - auxilary data
  34.  *
  35.  *      10 - FDC drive
  36.  *      11 - FDC track
  37.  *      12 - FDC sector
  38.  *      13 - FDC command
  39.  *      14 - FDC status
  40.  *
  41.  *      15 - DMA destination address low
  42.  *      16 - DMA destination address high
  43.  *
  44.  *      20 - MMU initialization
  45.  *      21 - MMU bank select
  46.  *
  47.  *      25 - clock command
  48.  *      26 - clock data
  49.  *
  50.  */
  51.  
  52. #include <stdio.h>
  53. #include <signal.h>
  54. #if defined(COHERENT) && !defined(_I386)
  55. #include <sys/fcntl.h>
  56. #else
  57. #include <fcntl.h>
  58. #endif
  59. #ifndef COHERENT
  60. #include <malloc.h>
  61. #include <memory.h>
  62. #endif
  63. #include <time.h>
  64. #include "sim.h"
  65. #include "simglb.h"
  66.  
  67. /*
  68.  *    Structure to describe a emulated floppy disk drive:
  69.  *        pointer to filename
  70.  *        pointer to file descriptor
  71.  *        number of tracks
  72.  *        number of sectors
  73.  */
  74. struct dskdef {
  75.     char *fn;
  76.     int *fd;
  77.     unsigned int tracks;
  78.     unsigned int sectors;
  79. };
  80.  
  81. static BYTE drive;      /* current drive A..P (0..15) */
  82. static BYTE track;      /* current track (0..255) */
  83. static BYTE sector;     /* current sektor (0..255) */
  84. static BYTE status;     /* status of last I/O operation on FDC */
  85. static BYTE dmadl;      /* current DMA adresse destination low */
  86. static BYTE dmadh;      /* current DMA adresse destination high */
  87. static BYTE clkcmd;     /* clock command */
  88. static int drivea;      /* fd for file "drivea.cpm" */
  89. static int driveb;      /* fd for file "driveb.cpm" */
  90. static int drivec;      /* fd for file "drivec.cpm" */
  91. static int drived;      /* fd for file "drived.cpm" */
  92. static int drivee;      /* fd for file "drivee.cpm" */
  93. static int drivef;      /* fd for file "drivef.cpm" */
  94. static int driveg;      /* fd for file "driveg.cpm" */
  95. static int driveh;      /* fd for file "driveh.cpm" */
  96. static int drivei;      /* fd for file "drivei.cpm" */
  97. static int drivej;      /* fd for file "drivej.cpm" */
  98. static int drivek;      /* fd for file "drivek.cpm" */
  99. static int drivel;      /* fd for file "drivel.cpm" */
  100. static int drivem;      /* fd for file "drivem.cpm" */
  101. static int driven;      /* fd for file "driven.cpm" */
  102. static int driveo;      /* fd for file "driveo.cpm" */
  103. static int drivep;      /* fd for file "drivep.cpm" */
  104. static int printer;     /* fd for file "printer.cpm" */
  105. static int auxin;       /* fd for pipe "auxin" */
  106. static int auxout;      /* fd for pipe "auxout" */
  107. static int aux_in_eof;  /* status of pipe "auxin" (<>0 means EOF) */
  108. static int pid_rec;     /* PID of the receiving process for auxiliary */
  109. static char last_char;  /* buffer for 1 character (console status) */
  110.  
  111. static struct dskdef disks[16] = {
  112.     { "disks/drivea.cpm", &drivea, 77, 26 },
  113.     { "disks/driveb.cpm", &driveb, 77, 26 },
  114.     { "disks/drivec.cpm", &drivec, 77, 26 },
  115.     { "disks/drived.cpm", &drived, 77, 26 },
  116.     { "disks/drivee.cpm", &drivee, -1, -1 },
  117.     { "disks/drivef.cpm", &drivef, -1, -1 },
  118.     { "disks/driveg.cpm", &driveg, -1, -1 },
  119.     { "disks/driveh.cpm", &driveh, -1, -1 },
  120.     { "disks/drivei.cpm", &drivei, -1, -1 },
  121.     { "disks/drivej.cpm", &drivej, -1, -1 },
  122.     { "disks/drivek.cpm", &drivek, -1, -1 },
  123.     { "disks/drivel.cpm", &drivel, -1, -1 },
  124.     { "disks/drivem.cpm", &drivem, -1, -1 },
  125.     { "disks/driven.cpm", &driven, -1, -1 },
  126.     { "disks/driveo.cpm", &driveo, -1, -1 },
  127.     { "disks/drivep.cpm", &drivep, -1, -1 }
  128. };
  129.  
  130. /*
  131.  *      MMU:
  132.  *      ===
  133.  *
  134.  *      +--------+
  135.  * 16KB | common |
  136.  *      +--------+
  137.  *      +--------+  +--------+  ..........  +--------+
  138.  *      |        |  |        |              |        |
  139.  * 48KB |        |  |        |  ..........  |        |
  140.  *      | bank 0 |  | bank 1 |              | bank n |
  141.  *      +--------+  +--------+  ..........  +--------+
  142.  */
  143. #define MAXSEG 16               /* max. number of memory banks */
  144. #define SEGSIZ 49152            /* size of one bank = 48KBytes */
  145. static char *mmu[MAXSEG];       /* MMU with pointers to the banks */
  146. static int selbnk;              /* current bank */
  147. static int maxbnk;              /* number of initialized banks */
  148.  
  149. /*
  150.  *      Forward declaration of the I/O handlers for all used ports
  151.  */
  152. BYTE io_trap();
  153. BYTE cond_in(), cond_out(), cons_in(), cons_out();
  154. BYTE prtd_in(), prtd_out(), prts_in(), prts_out();
  155. BYTE auxd_in(), auxd_out(), auxs_in(), auxs_out();
  156. BYTE fdcd_in(), fdcd_out();
  157. BYTE fdct_in(), fdct_out();
  158. BYTE fdcs_in(), fdcs_out();
  159. BYTE fdco_in(), fdco_out();
  160. BYTE fdcx_in(), fdcx_out();
  161. BYTE dmal_in(), dmal_out();
  162. BYTE dmah_in(), dmah_out();
  163. BYTE mmui_in(), mmui_out(), mmus_in(), mmus_out();
  164. BYTE clkc_in(), clkc_out(), clkd_in(), clkd_out();
  165.  
  166. /*
  167.  *    This array contains two function pointer for every
  168.  *    active port, one for input and one for output.
  169.  */
  170. static BYTE (*port[256][2]) () = {
  171.     { cons_in, cons_out },          /* port 0 */
  172.     { cond_in, cond_out },          /* port 1 */
  173.     { prts_in, prts_out },          /* port 2 */
  174.     { prtd_in, prtd_out },          /* port 3 */
  175.     { auxs_in, auxs_out },          /* port 4 */
  176.     { auxd_in, auxd_out },          /* port 5 */
  177.     { io_trap, io_trap  },          /* port 6 */
  178.     { io_trap, io_trap  },          /* port 7 */
  179.     { io_trap, io_trap  },          /* port 8 */
  180.     { io_trap, io_trap  },          /* port 9 */
  181.     { fdcd_in, fdcd_out },          /* port 10 */
  182.     { fdct_in, fdct_out },          /* port 11 */
  183.     { fdcs_in, fdcs_out },          /* port 12 */
  184.     { fdco_in, fdco_out },          /* port 13 */
  185.     { fdcx_in, fdcx_out },          /* port 14 */
  186.     { dmal_in, dmal_out },          /* port 15 */
  187.     { dmah_in, dmah_out },          /* port 16 */
  188.     { io_trap, io_trap  },          /* port 17 */
  189.     { io_trap, io_trap  },          /* port 18 */
  190.     { io_trap, io_trap  },          /* port 19 */
  191.     { mmui_in, mmui_out },          /* port 20 */
  192.     { mmus_in, mmus_out },          /* port 21 */
  193.     { io_trap, io_trap  },          /* port 22 */
  194.     { io_trap, io_trap  },          /* port 23 */
  195.     { io_trap, io_trap  },          /* port 24 */
  196.     { clkc_in, clkc_out },          /* port 25 */
  197.     { clkd_in, clkd_out }           /* port 26 */
  198. };
  199.  
  200. /*
  201.  *    This function initializes the I/O handlers:
  202.  *    1. Initialize all unused ports with the I/O trap handler.
  203.  *    2. Initialize the MMU with NULL pointers.
  204.  *    3. Open the files which emulates the disk drives. The file
  205.  *       for drive A must be opened, or CP/M can't be booted.
  206.  *       Errors for opening one of the other 15 drives results
  207.  *       in a NULL pointer for fd in the dskdef structure,
  208.  *       so that this drive can't be used.
  209.  *    4. Create and open the file "printer.cpm" for emulation
  210.  *       of a printer.
  211.  *    5. Fork the process for receiving from the serial port.
  212.  *    6. Open the named pipes "auxin" and "auxout" for simulation
  213.  *       of a serial port.
  214.  */
  215. void init_io()
  216. {
  217.     void exit(), perror();
  218.     register int i;
  219.  
  220.     for (i = 27; i <= 255; i++) {
  221.         port[i][0] = io_trap;
  222.         port[i][1] = io_trap;
  223.     }
  224.     for (i = 0; i < MAXSEG; i++)
  225.         mmu[i] = NULL;
  226.     if ((*disks[0].fd = open(disks[0].fn, O_RDWR)) == -1) {
  227.         perror("file disks/drivea.cpm");
  228.         exit(1);
  229.     }
  230.     for (i = 1; i <= 15; i++)
  231.         if ((*disks[i].fd = open(disks[i].fn, O_RDWR)) == -1)
  232.             disks[i].fd = NULL;
  233.     if ((printer = creat("printer.cpm", 0644)) == -1) {
  234.         perror("file printer.cpm");
  235.         exit(1);
  236.     }
  237.     pid_rec = fork();
  238.     switch (pid_rec) {
  239.     case -1:
  240.         puts("can't fork");
  241.         exit(1);
  242.     case 0:
  243.         execlp("receive", "receive", "auxiliary.cpm", 0);
  244.         puts("can't exec receive process");
  245.         exit(1);
  246.     }
  247.     if ((auxin = open("auxin", O_RDONLY | O_NDELAY)) == -1) {
  248.         perror("pipe auxin");
  249.         exit(1);
  250.     }
  251.     if ((auxout = open("auxout", O_WRONLY)) == -1) {
  252.         perror("pipe auxout");
  253.         exit(1);
  254.     }
  255. }
  256.  
  257. /*
  258.  *    This function stops the I/O handlers:
  259.  *
  260.  *    1. The files emulating the disk drives are closed.
  261.  *    2. The file "printer.com" emulating a printer is closed.
  262.  *    3. The named pipes "auxin" and "auxout" are closed.
  263.  *    4. The receiving process for the serial port is stopped.
  264.  */
  265. void exit_io()
  266. {
  267.     register int i;
  268.  
  269.     for (i = 0; i <= 15; i++)
  270.         if (disks[i].fd != NULL)
  271.             close(*disks[i].fd);
  272.     close(printer);
  273.     close(auxin);
  274.     close(auxout);
  275.     kill(pid_rec, SIGHUP);
  276. }
  277.  
  278. /*
  279.  *    This function is called for every IN opcode from the
  280.  *    CPU emulation. It calls the right handler for the
  281.  *    port, from which input is wanted.
  282.  */
  283. BYTE io_in(adr)
  284. register BYTE adr;
  285. {
  286.     return((*port[adr][0]) ());
  287. }
  288.  
  289. /*
  290.  *    This function is called for every OUT opcode from the
  291.  *    CPU emulation. It calls the right handler for the port,
  292.  *    to which output is wanted.
  293.  */
  294. BYTE io_out(adr, data)
  295. register BYTE adr, data;
  296. {
  297.     (*port[adr][1]) (data);
  298. }
  299.  
  300. /*
  301.  *      I/O trap handler
  302.  */
  303. static BYTE io_trap()
  304. {
  305.     cpu_error = IOTRAP;
  306.     cpu_state = STOPPED;
  307. }
  308.  
  309. /*
  310.  *      I/O handler for read console status:
  311.  *      0xff : input available
  312.  *      0x00 : no input available
  313.  */
  314. static BYTE cons_in()
  315. {
  316.     register int flags, readed;
  317.  
  318.     if (last_char)
  319.         return((BYTE) 0xff);
  320.     if (cntl_c)
  321.         return((BYTE) 0xff);
  322.     if (cntl_bs)
  323.         return((BYTE) 0xff);
  324.     else {
  325.         flags = fcntl(0, F_GETFL, 0);
  326.         fcntl(0, F_SETFL, flags | O_NDELAY);
  327.         readed = read(0, &last_char, 1);
  328.         fcntl(0, F_SETFL, flags);
  329.         if (readed == 1)
  330.             return((BYTE) 0xff);
  331.         else
  332.             return((BYTE) 0);
  333.     }
  334. }
  335.  
  336. /*
  337.  *      I/O handler for write console status:
  338.  *      no reaction
  339.  */
  340. static BYTE cons_out(data)
  341. register BYTE data;
  342. {
  343.     data = data;
  344. }
  345.  
  346. /*
  347.  *      I/O handler for read console data:
  348.  *      read one character from the terminal without echo
  349.  *      and character transformations
  350.  */
  351. static BYTE cond_in()
  352. {
  353.     char c;
  354.  
  355.     aborted:
  356.     if (last_char) {
  357.         c = last_char;
  358.         last_char = '\0';
  359.     } else if (cntl_c) {
  360.         cntl_c--;
  361.         c = 0x03;
  362.     } else if (cntl_bs) {
  363.         cntl_bs--;
  364.         c = 0x1c;
  365.     } else if (read(0, &c, 1) != 1) {
  366.            goto aborted;
  367.     }
  368.     return((BYTE) c);
  369. }
  370.  
  371. /*
  372.  *      I/O handler for write console data:
  373.  *      the output is written to the terminal
  374.  */
  375. static BYTE cond_out(data)
  376. register BYTE data;
  377. {
  378.     putchar(data & 0x7f);
  379.     if (data == '\f')
  380.         printf("\033H\033J");
  381.     fflush(stdout);
  382. }
  383.  
  384. /*
  385.  *      I/O handler for read printer status:
  386.  *      the printer is ready all the time
  387.  */
  388. static BYTE prts_in()
  389. {
  390.     return((BYTE) 0xff);
  391. }
  392.  
  393. /*
  394.  *      I/O handler for write printer status:
  395.  *      no reaction
  396.  */
  397. static BYTE prts_out(data)
  398. register BYTE data;
  399. {
  400.     data = data;
  401. }
  402.  
  403. /*
  404.  *      I/O handler for read printer data:
  405.  *      always read a 0 from the printer
  406.  */
  407. static BYTE prtd_in()
  408. {
  409.     return((BYTE) 0);
  410. }
  411.  
  412. /*
  413.  *      I/O handler for write printer data:
  414.  *    the output is written to file "printer.cpm"
  415.  */
  416. static BYTE prtd_out(data)
  417. BYTE data;
  418. {
  419.     if (data != '\r')
  420.         write(printer, (char *) &data, 1);
  421. }
  422.  
  423. /*
  424.  *      I/O handler for read aux status:
  425.  *    return EOF status of the aux device
  426.  */
  427. static BYTE auxs_in()
  428. {
  429.     return((BYTE) aux_in_eof);
  430. }
  431.  
  432. /*
  433.  *      I/O handler for write aux status:
  434.  *      change EOF status of the aux device
  435.  */
  436. static BYTE auxs_out(data)
  437. register BYTE data;
  438. {
  439.     aux_in_eof = data;
  440. }
  441.  
  442. /*
  443.  *      I/O handler for read aux data:
  444.  *      read next byte from pipe "auxin"
  445.  */
  446. static BYTE auxd_in()
  447. {
  448.     char c;
  449.  
  450.     if (read(auxin, &c, 1) == 1)
  451.         return((BYTE) c);
  452.     else {
  453.         aux_in_eof = 0xff;
  454.         return((BYTE) 0x1a);    /* CP/M EOF */
  455.     }
  456. }
  457.  
  458. /*
  459.  *      I/O handler for write aux data:
  460.  *      write output to pipe "auxout"
  461.  */
  462. static BYTE auxd_out(data)
  463. BYTE data;
  464. {
  465.     if (data != '\r')
  466.         write(auxout, (char *) &data, 1);
  467. }
  468.  
  469. /*
  470.  *      I/O handler for read FDC drive:
  471.  *      return the current drive
  472.  */
  473. static BYTE fdcd_in()
  474. {
  475.     return((BYTE) drive);
  476. }
  477.  
  478. /*
  479.  *      I/O handler for write FDC drive:
  480.  *      set the current drive
  481.  */
  482. static BYTE fdcd_out(data)
  483. register BYTE data;
  484. {
  485.     drive = data;
  486. }
  487.  
  488. /*
  489.  *      I/O handler for read FDC track:
  490.  *      return the current track
  491.  */
  492. static BYTE fdct_in()
  493. {
  494.     return((BYTE) track);
  495. }
  496.  
  497. /*
  498.  *      I/O handler for write FDC track:
  499.  *      set the current track
  500.  */
  501. static BYTE fdct_out(data)
  502. register BYTE data;
  503. {
  504.     track = data;
  505. }
  506.  
  507. /*
  508.  *      I/O handler for read FDC sector
  509.  *      return the current sector
  510.  */
  511. static BYTE fdcs_in()
  512. {
  513.     return((BYTE) sector);
  514. }
  515.  
  516. /*
  517.  *      I/O handler for write FDC sector:
  518.  *      set the current sector
  519.  */
  520. static BYTE fdcs_out(data)
  521. register BYTE data;
  522. {
  523.     sector = data;
  524. }
  525.  
  526. /*
  527.  *      I/O handler for read FDC command:
  528.  *      always returns 0
  529.  */
  530. static BYTE fdco_in()
  531. {
  532.     return((BYTE) 0);
  533. }
  534.  
  535. /*
  536.  *      I/O handler for write FDC command:
  537.  *      transfer one sector in the wanted direction,
  538.  *      0 = read, 1 = write
  539.  *
  540.  *      The status byte of the FDC is set as follows:
  541.  *        0 - ok
  542.  *        1 - illegal drive
  543.  *        2 - illegal track
  544.  *        3 - illegal sector
  545.  *        4 - seek error
  546.  *        5 - read error
  547.  *        6 - write error
  548.  *        7 - illegal command to FDC
  549.  */
  550. static BYTE fdco_out(data)
  551. register BYTE data;
  552. {
  553.     register long pos;
  554.     long lseek();
  555.  
  556.     if (disks[drive].fd == NULL) {
  557.         status = 1;
  558.         return;
  559.     }
  560.     if (track > disks[drive].tracks) {
  561.         status = 2;
  562.         return;
  563.     }
  564.     if (sector > disks[drive].sectors) {
  565.         status = 3;
  566.         return;
  567.     }
  568.     pos = (((long)track) * ((long)disks[drive].sectors) + sector - 1) << 7;
  569.     if (lseek(*disks[drive].fd, pos, 0) == -1L) {
  570.         status = 4;
  571.         return;
  572.     }
  573.     switch (data) {
  574.     case 0:                 /* read */
  575.         if (read(*disks[drive].fd, (char *) ram + (dmadh << 8) + dmadl, 128) != 128)
  576.             status = 5;
  577.         else
  578.             status = 0;
  579.         break;
  580.     case 1:                 /* write */
  581.         if (write(*disks[drive].fd, (char *) ram + (dmadh << 8) + dmadl, 128) != 128)
  582.             status = 6;
  583.         else
  584.             status = 0;
  585.         break;
  586.     default:                /* illegal command */
  587.         status = 7;
  588.         break;
  589.     }
  590. }
  591.  
  592. /*
  593.  *      I/O handler for read FDC status:
  594.  *      returns status of last FDC operation,
  595.  *      0 = ok, else some error
  596.  */
  597. static BYTE fdcx_in()
  598. {
  599.     return((BYTE) status);
  600. }
  601.  
  602. /*
  603.  *      I/O handler for write FDC status:
  604.  *      no reaction
  605.  */
  606. static BYTE fdcx_out(data)
  607. register BYTE data;
  608. {
  609.     data = data;
  610. }
  611.  
  612. /*
  613.  *      I/O handler for read lower byte of DMA address:
  614.  *      return lower byte of current DMA address
  615.  */
  616. static BYTE dmal_in()
  617. {
  618.     return((BYTE) dmadl);
  619. }
  620.  
  621. /*
  622.  *      I/O handler for write lower byte of DMA address:
  623.  *      set lower byte of DMA address
  624.  */
  625. static BYTE dmal_out(data)
  626. register BYTE data;
  627. {
  628.     dmadl = data;
  629. }
  630.  
  631. /*
  632.  *      I/O handler for read higher byte of DMA address:
  633.  *      return higher byte of current DMA address
  634.  */
  635. static BYTE dmah_in()
  636. {
  637.     return((BYTE) dmadh);
  638. }
  639.  
  640. /*
  641.  *      I/O handler for write higher byte of DMA address:
  642.  *      set higher byte of the DMA address
  643.  */
  644. static BYTE dmah_out(data)
  645. register BYTE data;
  646. {
  647.     dmadh = data;
  648. }
  649.  
  650. /*
  651.  *      I/O handler for read MMU initialization:
  652.  *      return number of initialized MMU banks
  653.  */
  654. static BYTE mmui_in()
  655. {
  656.     return((BYTE) maxbnk);
  657. }
  658.  
  659. /*
  660.  *      I/O handler for write MMU initialization:
  661.  *    for the FIRST call the memory for the wanted number of banks
  662.  *    is allocated and pointers to the memory is stored in the MMU array
  663.  */
  664. static BYTE mmui_out(data)
  665. register BYTE data;
  666. {
  667.     register int i;
  668.  
  669.     if (mmu[0] != NULL)
  670.         return;
  671.     if (data > MAXSEG) {
  672.         printf("Try to init %d banks, available %d banks\n", data, MAXSEG);
  673.         exit(1);
  674.     }
  675.     for (i = 0; i < data; i++) {
  676.         if ((mmu[i] = malloc(SEGSIZ)) == NULL) {
  677.             printf("can't allocate memory for bank %d\n", i+1);
  678.             exit(1);
  679.         }
  680.     }
  681.     maxbnk = data;
  682. }
  683.  
  684. /*
  685.  *      I/O handler for read MMU bank select:
  686.  *      return current selected MMU bank
  687.  */
  688. static BYTE mmus_in()
  689. {
  690.     return((BYTE) selbnk);
  691. }
  692.  
  693. /*
  694.  *      I/O handler for write MMU bank select:
  695.  *    if the current selected bank is not equal the wanted bank,
  696.  *    the current bank is saved. Then the memory of the wanted
  697.  *    bank is copied into the CPU address space and this bank is
  698.  *    set to be the current one now.
  699.  */
  700. static BYTE mmus_out(data)
  701. register BYTE data;
  702. {
  703.     if (data > maxbnk) {
  704.         printf("Try to select unallocated bank %d\n", data);
  705.         exit(1);
  706.     }
  707.     if (data == selbnk)
  708.         return;
  709.     memcpy(mmu[selbnk], (char *) ram, SEGSIZ);
  710.     memcpy((char *) ram, mmu[data], SEGSIZ);
  711.     selbnk = data;
  712. }
  713.  
  714. /*
  715.  *      I/O handler for read clock command:
  716.  *      return last clock command
  717.  */
  718. static BYTE clkc_in()
  719. {
  720.     return(clkcmd);
  721. }
  722.  
  723. /*
  724.  *      I/O handler for write clock command:
  725.  *      set the wanted clock command
  726.  */
  727. static BYTE clkc_out(data)
  728. register BYTE data;
  729. {
  730.     clkcmd = data;
  731. }
  732.  
  733. /*
  734.  *      I/O handler for read clock data:
  735.  *    dependent from the last clock command the following
  736.  *    informations are given from the system clock:
  737.  *              0 - secounds in BCD
  738.  *              1 - minutes in BCD
  739.  *              2 - hours in BCD
  740.  *              3 - low byte number of days since 1.1.1978
  741.  *              4 - high byte number of days since 1.1.1978
  742.  *      for every other clock command a 0 is returned
  743.  */
  744. static BYTE clkd_in()
  745. {
  746.     register struct tm *t;
  747.     register int val;
  748.     extern long time();
  749.     long Time;
  750.  
  751.     time(&Time);
  752.     t = localtime(&Time);
  753.     switch(clkcmd) {
  754.     case 0:                 /* secounds in BCD */
  755.         val = to_bcd(t->tm_sec);
  756.         break;
  757.     case 1:                 /* minutes in BCD */
  758.         val = to_bcd(t->tm_min);
  759.         break;
  760.     case 2:                 /* hours in BCD */
  761.         val = to_bcd(t->tm_hour);
  762.         break;
  763.     case 3:                 /* low byte days */
  764.         val = get_date(t) & 255;
  765.         break;
  766.     case 4:                 /* high byte days */
  767.         val = get_date(t) >> 8;
  768.         break;
  769.     default:
  770.         val = 0;
  771.         break;
  772.     }
  773.     return((BYTE) val);
  774. }
  775.  
  776. /*
  777.  *      I/O handler for write clock data:
  778.  *      under UNIX the system clock only can be set by the
  779.  *    super user, so we do nothing here
  780.  */
  781. static BYTE clkd_out(data)
  782. register BYTE data;
  783. {
  784.     data = data;
  785. }
  786.  
  787. /*
  788.  *      Convert a integer to BCD
  789.  */
  790. static int to_bcd(val)
  791. register int val;
  792. {
  793.     register int i = 0;
  794.  
  795.     while (val >= 10) {
  796.         i += val / 10;
  797.         i <<= 4;
  798.         val %= 10;
  799.     }
  800.     i += val;
  801.     return (i);
  802. }
  803.  
  804. /*
  805.  *    Calculate number of days since 1.1.1978
  806.  */
  807. static int get_date(t)
  808. register struct tm *t;
  809. {
  810.     register int i;
  811.     register int val = 0;
  812.  
  813.     for (i = 1978; i < 1900 + t->tm_year; i++) {
  814.         val += 365;
  815.         if (i % 4 == 0)
  816.             val++;
  817.     }
  818.     val += t->tm_yday + 1;
  819.     return(val);
  820. }
  821.