Logo Search packages:      
Sourcecode: bochs version File versions

serial.cc

/////////////////////////////////////////////////////////////////////////
// $Id: serial.cc,v 1.57 2004/09/05 21:09:46 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2004  MandrakeSoft S.A.
//
//    MandrakeSoft S.A.
//    43, rue d'Aboukir
//    75002 Paris - France
//    http://www.linux-mandrake.com/
//    http://www.mandrakesoft.com/
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA


// Peter Grehan (grehan@iprg.nokia.com) coded the original version of this
// serial emulation. He implemented a single 8250, and allow terminal
// input/output to stdout on FreeBSD.
// The current version emulates up to 4 UART 16550A with FIFO. Terminal
// input/output now works on some more platforms.


// Define BX_PLUGGABLE in files that can be compiled into plugins.  For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE 
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE

#include "iodev.h"

#if USE_RAW_SERIAL
#include "serial_raw.h"
#endif // USE_RAW_SERIAL

#define LOG_THIS theSerialDevice->

bx_serial_c *theSerialDevice = NULL;

  int
libserial_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
{
  theSerialDevice = new bx_serial_c ();
  bx_devices.pluginSerialDevice = theSerialDevice;
  BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theSerialDevice, BX_PLUGIN_SERIAL);
  return(0); // Success
}

  void
libserial_LTX_plugin_fini(void)
{
}

bx_serial_c::bx_serial_c(void)
{
  put("SER");
  settype(SERLOG);
  for (int i=0; i<BX_SERIAL_MAXDEV; i++) {
    s[i].tty_id = -1;
    s[i].tx_timer_index = BX_NULL_TIMER_HANDLE;
    s[i].rx_timer_index = BX_NULL_TIMER_HANDLE;
    s[i].fifo_timer_index = BX_NULL_TIMER_HANDLE;
  }
}

bx_serial_c::~bx_serial_c(void)
{
  for (int i=0; i<BX_SERIAL_MAXDEV; i++) {
    if (bx_options.com[i].Oenabled->get ()) {
      switch (BX_SER_THIS s[i].io_mode) {
        case BX_SER_MODE_FILE:
          if (BX_SER_THIS s[i].output != NULL)
            fclose(BX_SER_THIS s[i].output);
          break;
        case BX_SER_MODE_TERM:
#if defined(SERIAL_ENABLE)
          if (s[i].tty_id >= 0) {
            tcsetattr(s[i].tty_id, TCSAFLUSH, &s[i].term_orig);
          }
#endif
          break;
        case BX_SER_MODE_RAW:
#if USE_RAW_SERIAL
          delete [] BX_SER_THIS s[i].raw;
#endif
          break;
      }
    }
  }
}


  void
bx_serial_c::init(void)
{
  Bit16u ports[BX_SERIAL_MAXDEV] = {0x03f8, 0x02f8, 0x03e8, 0x02e8};
  char name[16];

  /*
   * Put the UART registers into their RESET state
   */
  for (unsigned i=0; i<BX_N_SERIAL_PORTS; i++) {
    if (bx_options.com[i].Oenabled->get ()) {
      sprintf(name, "Serial Port %d", i + 1);
      /* serial interrupt */
      BX_SER_THIS s[i].IRQ = 4 - (i & 1);
      if (i < 2) {
        DEV_register_irq(BX_SER_THIS s[i].IRQ, name);
      }
      /* internal state */
      BX_SER_THIS s[i].ls_ipending = 0;
      BX_SER_THIS s[i].ms_ipending = 0;
      BX_SER_THIS s[i].rx_ipending = 0;
      BX_SER_THIS s[i].fifo_ipending = 0;
      BX_SER_THIS s[i].ls_interrupt = 0;
      BX_SER_THIS s[i].ms_interrupt = 0;
      BX_SER_THIS s[i].rx_interrupt = 0;
      BX_SER_THIS s[i].tx_interrupt = 0;
      BX_SER_THIS s[i].fifo_interrupt = 0;

      if (BX_SER_THIS s[i].tx_timer_index == BX_NULL_TIMER_HANDLE) {
        BX_SER_THIS s[i].tx_timer_index =
          bx_pc_system.register_timer(this, tx_timer_handler, 0,
                                      0,0, "serial.tx"); // one-shot, inactive
      }

      if (BX_SER_THIS s[i].rx_timer_index == BX_NULL_TIMER_HANDLE) {
        BX_SER_THIS s[i].rx_timer_index =
          bx_pc_system.register_timer(this, rx_timer_handler, 0,
                                      0,0, "serial.rx"); // one-shot, inactive
      }
      if (BX_SER_THIS s[i].fifo_timer_index == BX_NULL_TIMER_HANDLE) {
        BX_SER_THIS s[i].fifo_timer_index =
          bx_pc_system.register_timer(this, fifo_timer_handler, 0,
                                      0,0, "serial.fifo"); // one-shot, inactive
      }
      BX_SER_THIS s[i].rx_pollstate = BX_SER_RXIDLE;

      /* int enable: b0000 0000 */
      BX_SER_THIS s[i].int_enable.rxdata_enable = 0;
      BX_SER_THIS s[i].int_enable.txhold_enable = 0;
      BX_SER_THIS s[i].int_enable.rxlstat_enable = 0;
      BX_SER_THIS s[i].int_enable.modstat_enable = 0;

      /* int ID: b0000 0001 */
      BX_SER_THIS s[i].int_ident.ipending = 1;
      BX_SER_THIS s[i].int_ident.int_ID = 0;

      /* FIFO control: b0000 0000 */
      BX_SER_THIS s[i].fifo_cntl.enable = 0;
      BX_SER_THIS s[i].fifo_cntl.rxtrigger = 0;
      BX_SER_THIS s[i].rx_fifo_end = 0;
      BX_SER_THIS s[i].tx_fifo_end = 0;

      /* Line Control reg: b0000 0000 */
      BX_SER_THIS s[i].line_cntl.wordlen_sel = 0;
      BX_SER_THIS s[i].line_cntl.stopbits = 0;
      BX_SER_THIS s[i].line_cntl.parity_enable = 0;
      BX_SER_THIS s[i].line_cntl.evenparity_sel = 0;
      BX_SER_THIS s[i].line_cntl.stick_parity = 0;
      BX_SER_THIS s[i].line_cntl.break_cntl = 0;
      BX_SER_THIS s[i].line_cntl.dlab = 0;

      /* Modem Control reg: b0000 0000 */
      BX_SER_THIS s[i].modem_cntl.dtr = 0;
      BX_SER_THIS s[i].modem_cntl.rts = 0;
      BX_SER_THIS s[i].modem_cntl.out1 = 0;
      BX_SER_THIS s[i].modem_cntl.out2 = 0;
      BX_SER_THIS s[i].modem_cntl.local_loopback = 0;

      /* Line Status register: b0110 0000 */
      BX_SER_THIS s[i].line_status.rxdata_ready = 0;
      BX_SER_THIS s[i].line_status.overrun_error = 0;
      BX_SER_THIS s[i].line_status.parity_error = 0;
      BX_SER_THIS s[i].line_status.framing_error = 0;
      BX_SER_THIS s[i].line_status.break_int = 0;
      BX_SER_THIS s[i].line_status.thr_empty = 1;
      BX_SER_THIS s[i].line_status.tsr_empty = 1;
      BX_SER_THIS s[i].line_status.fifo_error = 0;

      /* Modem Status register: bXXXX 0000 */
      BX_SER_THIS s[i].modem_status.delta_cts = 0;
      BX_SER_THIS s[i].modem_status.delta_dsr = 0;
      BX_SER_THIS s[i].modem_status.ri_trailedge = 0;
      BX_SER_THIS s[i].modem_status.delta_dcd = 0;
      BX_SER_THIS s[i].modem_status.cts = 0;
      BX_SER_THIS s[i].modem_status.dsr = 0;
      BX_SER_THIS s[i].modem_status.ri = 0;
      BX_SER_THIS s[i].modem_status.dcd = 0;

      BX_SER_THIS s[i].scratch = 0;      /* scratch register */
      BX_SER_THIS s[i].divisor_lsb = 1;  /* divisor-lsb register */
      BX_SER_THIS s[i].divisor_msb = 0;  /* divisor-msb register */

      BX_SER_THIS s[i].baudrate = 115200;

      for (unsigned addr=ports[i]; addr<(unsigned)(ports[i]+8); addr++) {
        BX_DEBUG(("com%d register read/write: 0x%04x",i+1, addr));
        DEV_register_ioread_handler(this, read_handler, addr, name, 1);
        DEV_register_iowrite_handler(this, write_handler, addr, name, 1);
      }

      BX_SER_THIS s[i].io_mode = BX_SER_MODE_NULL;
      char *mode = bx_options.com[i].Omode->get_choice(bx_options.com[i].Omode->get());
      if (!strcmp(mode, "file")) {
        if (strlen(bx_options.com[i].Odev->getptr ()) > 0) {
          BX_SER_THIS s[i].output = fopen(bx_options.com[i].Odev->getptr (), "wb");
          if (BX_SER_THIS s[i].output)
            BX_SER_THIS s[i].io_mode = BX_SER_MODE_FILE;
        }
      } else if (!strcmp(mode, "term")) {
#if defined(SERIAL_ENABLE)
        if (strlen(bx_options.com[i].Odev->getptr ()) > 0) {
          BX_SER_THIS s[i].tty_id = open(bx_options.com[i].Odev->getptr (), O_RDWR|O_NONBLOCK,600);
          if (BX_SER_THIS s[i].tty_id < 0) {
            BX_PANIC(("open of com%d (%s) failed\n", i+1, bx_options.com[i].Odev->getptr ()));
          } else {
            BX_SER_THIS s[i].io_mode = BX_SER_MODE_TERM;
            BX_DEBUG(("com%d tty_id: %d", i+1, BX_SER_THIS s[i].tty_id));
            tcgetattr(BX_SER_THIS s[i].tty_id, &BX_SER_THIS s[i].term_orig);
            bcopy((caddr_t) &BX_SER_THIS s[i].term_orig, (caddr_t) &BX_SER_THIS s[i].term_new, sizeof(struct termios));
            cfmakeraw(&BX_SER_THIS s[i].term_new);
            BX_SER_THIS s[i].term_new.c_oflag |= OPOST | ONLCR;  // Enable NL to CR-NL translation
#ifndef TRUE_CTLC
            // ctl-C will exit Bochs, or trap to the debugger
            BX_SER_THIS s[i].term_new.c_iflag &= ~IGNBRK;
            BX_SER_THIS s[i].term_new.c_iflag |= BRKINT;
            BX_SER_THIS s[i].term_new.c_lflag |= ISIG;
#else
            // ctl-C will be delivered to the serial port
            BX_SER_THIS s[i].term_new.c_iflag |= IGNBRK;
            BX_SER_THIS s[i].term_new.c_iflag &= ~BRKINT;
#endif    /* !def TRUE_CTLC */
            BX_SER_THIS s[i].term_new.c_iflag = 0;
            BX_SER_THIS s[i].term_new.c_oflag = 0;
            BX_SER_THIS s[i].term_new.c_cflag = CS8|CREAD|CLOCAL;
            BX_SER_THIS s[i].term_new.c_lflag = 0;
            BX_SER_THIS s[i].term_new.c_cc[VMIN] = 1;
            BX_SER_THIS s[i].term_new.c_cc[VTIME] = 0;
            //BX_SER_THIS s[i].term_new.c_iflag |= IXOFF;
            tcsetattr(BX_SER_THIS s[i].tty_id, TCSAFLUSH, &BX_SER_THIS s[i].term_new);
          }
        }
#else
        BX_PANIC(("serial terminal support not available"));
#endif   /* def SERIAL_ENABLE */
      } else if (!strcmp(mode, "raw")) {
#if USE_RAW_SERIAL
        BX_SER_THIS s[i].raw = new serial_raw(bx_options.com[i].Odev->getptr ());
        BX_SER_THIS s[i].io_mode = BX_SER_MODE_RAW;
#else
        BX_PANIC(("raw serial support not present"));
#endif
      } else if (strcmp(mode, "null")) {
        BX_PANIC(("unknown serial i/o mode"));
      }
      // simulate device connected
      if (BX_SER_THIS s[i].io_mode != BX_SER_MODE_RAW) {
        BX_SER_THIS s[i].modem_status.cts = 1;
        BX_SER_THIS s[i].modem_status.dsr = 1;
      }
      BX_INFO(("com%d at 0x%04x irq %d", i+1, ports[i], BX_SER_THIS s[i].IRQ));
    }
  }
}

  void
bx_serial_c::reset(unsigned type)
{
}

  void
bx_serial_c::lower_interrupt(Bit8u port)
{
  /* If there are no more ints pending, clear the irq */
  if ((BX_SER_THIS s[port].rx_interrupt == 0) &&
      (BX_SER_THIS s[port].tx_interrupt == 0) &&
      (BX_SER_THIS s[port].ls_interrupt == 0) &&
      (BX_SER_THIS s[port].ms_interrupt == 0) &&
      (BX_SER_THIS s[port].fifo_interrupt == 0)) {
    DEV_pic_lower_irq(BX_SER_THIS s[port].IRQ);
  }
}

  void
bx_serial_c::raise_interrupt(Bit8u port, int type)
{
  bx_bool gen_int = 0;

  switch (type) {
    case BX_SER_INT_IER: /* IER has changed */
      gen_int = 1;
      break;
    case BX_SER_INT_RXDATA:
      if (BX_SER_THIS s[port].int_enable.rxdata_enable) {
        BX_SER_THIS s[port].rx_interrupt = 1;
        gen_int = 1;
      } else {
        BX_SER_THIS s[port].rx_ipending = 1;
      }
      break;
    case BX_SER_INT_TXHOLD:
      if (BX_SER_THIS s[port].int_enable.txhold_enable) {
        BX_SER_THIS s[port].tx_interrupt = 1;
        gen_int = 1;
      }
      break;
    case BX_SER_INT_RXLSTAT:
      if (BX_SER_THIS s[port].int_enable.rxlstat_enable) {
        BX_SER_THIS s[port].ls_interrupt = 1;
        gen_int = 1;
      } else {
        BX_SER_THIS s[port].ls_ipending = 1;
      }
      break;
    case BX_SER_INT_MODSTAT:
      if ((BX_SER_THIS s[port].ms_ipending == 1) &&
          (BX_SER_THIS s[port].int_enable.modstat_enable == 1)) {
        BX_SER_THIS s[port].ms_interrupt = 1;
        BX_SER_THIS s[port].ms_ipending = 0;
        gen_int = 1;
      }
      break;
    case BX_SER_INT_FIFO:
      if (BX_SER_THIS s[port].int_enable.rxdata_enable) {
        BX_SER_THIS s[port].fifo_interrupt = 1;
        gen_int = 1;
      } else {
        BX_SER_THIS s[port].fifo_ipending = 1;
      }
      break;
  }
  if (gen_int && BX_SER_THIS s[port].modem_cntl.out2) {
    DEV_pic_raise_irq(BX_SER_THIS s[port].IRQ);
  }
}

  // static IO port read callback handler
  // redirects to non-static class handler to avoid virtual functions

  Bit32u
bx_serial_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
{
#if !BX_USE_SER_SMF
  bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;

  return( class_ptr->read(address, io_len) );
}


  Bit32u
bx_serial_c::read(Bit32u address, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif  // !BX_USE_SER_SMF
  bx_bool prev_cts, prev_dsr, prev_ri, prev_dcd;
  Bit8u offset, val;
  Bit8u port = 0;

  BX_DEBUG(("register read from address 0x%04x - ", address));

  offset = address & 0x07;
  switch (address & 0x03f8) {
    case 0x03f8: port = 0; break;
    case 0x02f8: port = 1; break;
    case 0x03e8: port = 2; break;
    case 0x02e8: port = 3; break;
  }

  switch (offset) {
    case BX_SER_RBR: /* receive buffer, or divisor latch LSB if DLAB set */
      if (BX_SER_THIS s[port].line_cntl.dlab) {
        val = BX_SER_THIS s[port].divisor_lsb;
      } else {
        if (BX_SER_THIS s[port].fifo_cntl.enable) {
          val = BX_SER_THIS s[port].rx_fifo[0];
          if (BX_SER_THIS s[port].rx_fifo_end > 0) {
            memcpy(&BX_SER_THIS s[port].rx_fifo[0], &BX_SER_THIS s[port].rx_fifo[1], 15);
            BX_SER_THIS s[port].rx_fifo_end--;
          }
          if (BX_SER_THIS s[port].rx_fifo_end == 0) {
            BX_SER_THIS s[port].line_status.rxdata_ready = 0;
            BX_SER_THIS s[port].rx_interrupt = 0;
            BX_SER_THIS s[port].rx_ipending = 0;
            BX_SER_THIS s[port].fifo_interrupt = 0;
            BX_SER_THIS s[port].fifo_ipending = 0;
            lower_interrupt(port);
          }
        } else {
          val = BX_SER_THIS s[port].rxbuffer;
          BX_SER_THIS s[port].line_status.rxdata_ready = 0;
          BX_SER_THIS s[port].rx_interrupt = 0;
          BX_SER_THIS s[port].rx_ipending = 0;
          lower_interrupt(port);
        }
      }
      break;

    case BX_SER_IER: /* interrupt enable register, or div. latch MSB */
      if (BX_SER_THIS s[port].line_cntl.dlab) {
        val = BX_SER_THIS s[port].divisor_msb;
      } else {
        val = BX_SER_THIS s[port].int_enable.rxdata_enable |
              (BX_SER_THIS s[port].int_enable.txhold_enable  << 1) |
              (BX_SER_THIS s[port].int_enable.rxlstat_enable << 2) |
              (BX_SER_THIS s[port].int_enable.modstat_enable << 3);
      }
      break;

    case BX_SER_IIR: /* interrupt ID register */
      /*
       * Set the interrupt ID based on interrupt source
       */
      if (BX_SER_THIS s[port].ls_interrupt) {
        BX_SER_THIS s[port].int_ident.int_ID = 0x3;
        BX_SER_THIS s[port].int_ident.ipending = 0;
      } else if (BX_SER_THIS s[port].fifo_interrupt) {
        BX_SER_THIS s[port].int_ident.int_ID = 0x6;
        BX_SER_THIS s[port].int_ident.ipending = 0;
      } else if (BX_SER_THIS s[port].rx_interrupt) {
        BX_SER_THIS s[port].int_ident.int_ID = 0x2;
        BX_SER_THIS s[port].int_ident.ipending = 0;
      } else if (BX_SER_THIS s[port].tx_interrupt) {
        BX_SER_THIS s[port].int_ident.int_ID = 0x1;
        BX_SER_THIS s[port].int_ident.ipending = 0;
      } else if (BX_SER_THIS s[port].ms_interrupt) {
        BX_SER_THIS s[port].int_ident.int_ID = 0x0;
        BX_SER_THIS s[port].int_ident.ipending = 0;
      } else {
        BX_SER_THIS s[port].int_ident.int_ID = 0x0;
        BX_SER_THIS s[port].int_ident.ipending = 1;
      }
      BX_SER_THIS s[port].tx_interrupt = 0;
      lower_interrupt(port);

      val = BX_SER_THIS s[port].int_ident.ipending  |
            (BX_SER_THIS s[port].int_ident.int_ID << 1) |
            (BX_SER_THIS s[port].fifo_cntl.enable ? 0xc0 : 0x00);
      break;

    case BX_SER_LCR: /* Line control register */
      val = BX_SER_THIS s[port].line_cntl.wordlen_sel |
            (BX_SER_THIS s[port].line_cntl.stopbits       << 2) |
            (BX_SER_THIS s[port].line_cntl.parity_enable  << 3) |
            (BX_SER_THIS s[port].line_cntl.evenparity_sel << 4) |
            (BX_SER_THIS s[port].line_cntl.stick_parity   << 5) |
            (BX_SER_THIS s[port].line_cntl.break_cntl     << 6) |
            (BX_SER_THIS s[port].line_cntl.dlab           << 7);
      break;

    case BX_SER_MCR: /* MODEM control register */
      val = BX_SER_THIS s[port].modem_cntl.dtr |
            (BX_SER_THIS s[port].modem_cntl.rts << 1) |
            (BX_SER_THIS s[port].modem_cntl.out1 << 2) |
            (BX_SER_THIS s[port].modem_cntl.out2 << 3) |
            (BX_SER_THIS s[port].modem_cntl.local_loopback << 4);
      break;

    case BX_SER_LSR: /* Line status register */
      val = BX_SER_THIS s[port].line_status.rxdata_ready |
            (BX_SER_THIS s[port].line_status.overrun_error  << 1) |
            (BX_SER_THIS s[port].line_status.parity_error   << 2) |
            (BX_SER_THIS s[port].line_status.framing_error  << 3) |
            (BX_SER_THIS s[port].line_status.break_int      << 4) |
            (BX_SER_THIS s[port].line_status.thr_empty      << 5) |
            (BX_SER_THIS s[port].line_status.tsr_empty      << 6) |
            (BX_SER_THIS s[port].line_status.fifo_error     << 7);
      BX_SER_THIS s[port].line_status.overrun_error = 0;
      BX_SER_THIS s[port].line_status.framing_error = 0;
      BX_SER_THIS s[port].line_status.break_int = 0;
      BX_SER_THIS s[port].ls_interrupt = 0;
      BX_SER_THIS s[port].ls_ipending = 0;
      lower_interrupt(port);
      break;

    case BX_SER_MSR: /* MODEM status register */
      prev_cts = BX_SER_THIS s[port].modem_status.cts;
      prev_dsr = BX_SER_THIS s[port].modem_status.dsr;
      prev_ri  = BX_SER_THIS s[port].modem_status.ri;
      prev_dcd = BX_SER_THIS s[port].modem_status.dcd;
      if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
        val = BX_SER_THIS s[port].raw->get_modem_status();
        BX_SER_THIS s[port].modem_status.cts = (val & 0x10) >> 4;
        BX_SER_THIS s[port].modem_status.dsr = (val & 0x20) >> 5;
        BX_SER_THIS s[port].modem_status.ri  = (val & 0x40) >> 6;
        BX_SER_THIS s[port].modem_status.dcd = (val & 0x80) >> 7;
        if (BX_SER_THIS s[port].modem_status.cts != prev_cts) {
          BX_SER_THIS s[port].modem_status.delta_cts = 1;
        }
        if (BX_SER_THIS s[port].modem_status.dsr != prev_dsr) {
          BX_SER_THIS s[port].modem_status.delta_dsr = 1;
        }
        if ((BX_SER_THIS s[port].modem_status.ri == 0) && (prev_ri == 1))
          BX_SER_THIS s[port].modem_status.ri_trailedge = 1;
        if (BX_SER_THIS s[port].modem_status.dcd != prev_dcd) {
          BX_SER_THIS s[port].modem_status.delta_dcd = 1;
        }
#endif
      }
      val = BX_SER_THIS s[port].modem_status.delta_cts |
            (BX_SER_THIS s[port].modem_status.delta_dsr    << 1) |
            (BX_SER_THIS s[port].modem_status.ri_trailedge << 2) |
            (BX_SER_THIS s[port].modem_status.delta_dcd    << 3) |
            (BX_SER_THIS s[port].modem_status.cts          << 4) |
            (BX_SER_THIS s[port].modem_status.dsr          << 5) |
            (BX_SER_THIS s[port].modem_status.ri           << 6) |
            (BX_SER_THIS s[port].modem_status.dcd          << 7);
      BX_SER_THIS s[port].modem_status.delta_cts = 0;
      BX_SER_THIS s[port].modem_status.delta_dsr = 0;
      BX_SER_THIS s[port].modem_status.ri_trailedge = 0;
      BX_SER_THIS s[port].modem_status.delta_dcd = 0;
      BX_SER_THIS s[port].ms_interrupt = 0;
      BX_SER_THIS s[port].ms_ipending = 0;
      lower_interrupt(port);
      break;

    case BX_SER_SCR: /* scratch register */
      val = BX_SER_THIS s[port].scratch;
      break;

    default:
      val = 0; // keep compiler happy
      BX_PANIC(("unsupported io read from address=0x%04x!", address));
      break;
  }

  BX_DEBUG(("val =  0x%02x", (unsigned) val));

  return(val);
}


  // static IO port write callback handler
  // redirects to non-static class handler to avoid virtual functions

void
bx_serial_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
#if !BX_USE_SER_SMF
  bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;

  class_ptr->write(address, value, io_len);
}

void
bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif  // !BX_USE_SER_SMF
  bx_bool prev_cts, prev_dsr, prev_ri, prev_dcd;
  bx_bool new_b0, new_b1, new_b2, new_b3;
  bx_bool new_b4, new_b5, new_b6, new_b7;
  bx_bool gen_int = 0;
  Bit8u offset, new_wordlen;
#if USE_RAW_SERIAL
  bx_bool mcr_changed = 0;
  Bit8u p_mode;
#endif
  Bit8u port = 0;

  BX_DEBUG(("write to address: 0x%04x = 0x%02x", address, value));

  offset = address & 0x07;
  switch (address & 0x03f8) {
    case 0x03f8: port = 0; break;
    case 0x02f8: port = 1; break;
    case 0x03e8: port = 2; break;
    case 0x02e8: port = 3; break;
  }
  new_b0 = value & 0x01;
  new_b1 = (value & 0x02) >> 1;
  new_b2 = (value & 0x04) >> 2;
  new_b3 = (value & 0x08) >> 3;
  new_b4 = (value & 0x10) >> 4;
  new_b5 = (value & 0x20) >> 5;
  new_b6 = (value & 0x40) >> 6;
  new_b7 = (value & 0x80) >> 7;

  switch (offset) {
    case BX_SER_THR: /* transmit buffer, or divisor latch LSB if DLAB set */
      if (BX_SER_THIS s[port].line_cntl.dlab) {
        BX_SER_THIS s[port].divisor_lsb = value;

        if ((value != 0) || (BX_SER_THIS s[port].divisor_msb != 0)) {
          BX_SER_THIS s[port].baudrate = (int) (BX_PC_CLOCK_XTL /
                                         (16 * ((BX_SER_THIS s[port].divisor_msb << 8) |
                                         BX_SER_THIS s[port].divisor_lsb)));
        }
      } else {
        Bit8u bitmask = 0xff >> (3 - BX_SER_THIS s[port].line_cntl.wordlen_sel);
        if (BX_SER_THIS s[port].line_status.thr_empty) {
          if (BX_SER_THIS s[port].fifo_cntl.enable) {
            BX_SER_THIS s[port].tx_fifo[BX_SER_THIS s[port].tx_fifo_end++] = value & bitmask;
          } else {
            BX_SER_THIS s[port].thrbuffer = value & bitmask;
          }
          BX_SER_THIS s[port].line_status.thr_empty = 0;
          if (BX_SER_THIS s[port].line_status.tsr_empty) {
            if (BX_SER_THIS s[port].fifo_cntl.enable) {
              BX_SER_THIS s[port].tsrbuffer = BX_SER_THIS s[port].tx_fifo[0];
              memcpy(&BX_SER_THIS s[port].tx_fifo[0], &BX_SER_THIS s[port].tx_fifo[1], 15);
              BX_SER_THIS s[port].line_status.thr_empty = (--BX_SER_THIS s[port].tx_fifo_end == 0);
            } else {
              BX_SER_THIS s[port].tsrbuffer = BX_SER_THIS s[port].thrbuffer;
              BX_SER_THIS s[port].line_status.thr_empty = 1;
            }
            BX_SER_THIS s[port].line_status.tsr_empty = 0;
            raise_interrupt(port, BX_SER_INT_TXHOLD);
            bx_pc_system.activate_timer(BX_SER_THIS s[port].tx_timer_index,
                                        (int) (1000000.0 / BX_SER_THIS s[port].baudrate *
                                        (BX_SER_THIS s[port].line_cntl.wordlen_sel + 5)),
                                        0); /* not continuous */
          } else {
            BX_SER_THIS s[port].tx_interrupt = 0;
            lower_interrupt(port);
          }
        } else {
          if (BX_SER_THIS s[port].fifo_cntl.enable) {
            if (BX_SER_THIS s[port].tx_fifo_end < 16) {
              BX_SER_THIS s[port].tx_fifo[BX_SER_THIS s[port].tx_fifo_end++] = value & bitmask;
            } else {
              BX_ERROR(("com%d: transmit FIFO overflow", port+1));
            }
          } else {
            BX_ERROR(("com%d: write to tx hold register when not empty", port+1));
          }
        }
      }
      break;

    case BX_SER_IER: /* interrupt enable register, or div. latch MSB */
      if (BX_SER_THIS s[port].line_cntl.dlab) {
        BX_SER_THIS s[port].divisor_msb = value;

        if ((value != 0) || (BX_SER_THIS s[port].divisor_lsb != 0)) {
          BX_SER_THIS s[port].baudrate = (int) (BX_PC_CLOCK_XTL /
                                         (16 * ((BX_SER_THIS s[port].divisor_msb << 8) |
                                         BX_SER_THIS s[port].divisor_lsb)));
        }
      } else {
        if (new_b3 != BX_SER_THIS s[port].int_enable.modstat_enable) {
          BX_SER_THIS s[port].int_enable.modstat_enable  = new_b3;
          if (BX_SER_THIS s[port].int_enable.modstat_enable == 1) {
            if (BX_SER_THIS s[port].ms_ipending == 1) {
              BX_SER_THIS s[port].ms_interrupt = 1;
              BX_SER_THIS s[port].ms_ipending = 0;
              gen_int = 1;
            }
          } else {
            if (BX_SER_THIS s[port].ms_interrupt == 1) {
              BX_SER_THIS s[port].ms_interrupt = 0;
              BX_SER_THIS s[port].ms_ipending = 1;
              lower_interrupt(port);
            }
          }
        }
        if (new_b1 != BX_SER_THIS s[port].int_enable.txhold_enable) {
          BX_SER_THIS s[port].int_enable.txhold_enable  = new_b1;
          if (BX_SER_THIS s[port].int_enable.txhold_enable == 1) {
            BX_SER_THIS s[port].tx_interrupt = BX_SER_THIS s[port].line_status.thr_empty;
            if (BX_SER_THIS s[port].tx_interrupt) gen_int = 1;
          } else {
            BX_SER_THIS s[port].tx_interrupt = 0;
            lower_interrupt(port);
          }
        }
        if (new_b0 != BX_SER_THIS s[port].int_enable.rxdata_enable) {
          BX_SER_THIS s[port].int_enable.rxdata_enable  = new_b0;
          if (BX_SER_THIS s[port].int_enable.rxdata_enable == 1) {
            if (BX_SER_THIS s[port].fifo_ipending == 1) {
              BX_SER_THIS s[port].fifo_interrupt = 1;
              BX_SER_THIS s[port].fifo_ipending = 0;
              gen_int = 1;
            }
            if (BX_SER_THIS s[port].rx_ipending == 1) {
              BX_SER_THIS s[port].rx_interrupt = 1;
              BX_SER_THIS s[port].rx_ipending = 0;
              gen_int = 1;
            }
          } else {
            if (BX_SER_THIS s[port].rx_interrupt == 1) {
              BX_SER_THIS s[port].rx_interrupt = 0;
              BX_SER_THIS s[port].rx_ipending = 1;
              lower_interrupt(port);
            }
            if (BX_SER_THIS s[port].fifo_interrupt == 1) {
              BX_SER_THIS s[port].fifo_interrupt = 0;
              BX_SER_THIS s[port].fifo_ipending = 1;
              lower_interrupt(port);
            }
          }
        }
        if (new_b2 != BX_SER_THIS s[port].int_enable.rxlstat_enable) {
          BX_SER_THIS s[port].int_enable.rxlstat_enable  = new_b2;
          if (BX_SER_THIS s[port].int_enable.rxlstat_enable == 1) {
            if (BX_SER_THIS s[port].ls_ipending == 1) {
              BX_SER_THIS s[port].ls_interrupt = 1;
              BX_SER_THIS s[port].ls_ipending = 0;
              gen_int = 1;
            }
          } else {
            if (BX_SER_THIS s[port].ls_interrupt == 1) {
              BX_SER_THIS s[port].ls_interrupt = 0;
              BX_SER_THIS s[port].ls_ipending = 1;
              lower_interrupt(port);
            }
          }
        }
        if (gen_int) raise_interrupt(port, BX_SER_INT_IER);
      }
      break;

    case BX_SER_FCR: /* FIFO control register */
      if (new_b0 && !BX_SER_THIS s[port].fifo_cntl.enable) {
        BX_INFO(("com%d: FIFO enabled", port+1));
        BX_SER_THIS s[port].rx_fifo_end = 0;
        BX_SER_THIS s[port].tx_fifo_end = 0;
      }
      BX_SER_THIS s[port].fifo_cntl.enable = new_b0;
      if (new_b1) {
        BX_SER_THIS s[port].rx_fifo_end = 0;
      }
      if (new_b2) {
        BX_SER_THIS s[port].tx_fifo_end = 0;
      }
      BX_SER_THIS s[port].fifo_cntl.rxtrigger = (value & 0xc0) >> 6;
      break;

    case BX_SER_LCR: /* Line control register */
      new_wordlen = value & 0x03;
      if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
        if (BX_SER_THIS s[port].line_cntl.wordlen_sel != new_wordlen) {
          BX_SER_THIS s[port].raw->set_data_bits(new_wordlen + 5);
        }
        if (new_b2 != BX_SER_THIS s[port].line_cntl.stopbits) {
          BX_SER_THIS s[port].raw->set_stop_bits(new_b2 ? 2 : 1);
        }
        if ((new_b3 != BX_SER_THIS s[port].line_cntl.parity_enable) ||
            (new_b4 != BX_SER_THIS s[port].line_cntl.evenparity_sel) ||
            (new_b5 != BX_SER_THIS s[port].line_cntl.stick_parity)) {
          if (new_b3 == 0) {
            p_mode = P_NONE;
          } else {
            p_mode = ((value & 0x30) >> 4) + 1;
          }
          BX_SER_THIS s[port].raw->set_parity_mode(p_mode);
        }
        if ((new_b6 != BX_SER_THIS s[port].line_cntl.break_cntl) &&
            (!BX_SER_THIS s[port].modem_cntl.local_loopback)) {
          BX_SER_THIS s[port].raw->set_break(new_b6);
        }
#endif // USE_RAW_SERIAL
      }
      BX_SER_THIS s[port].line_cntl.wordlen_sel = new_wordlen;
      /* These are ignored, but set them up so they can be read back */
      BX_SER_THIS s[port].line_cntl.stopbits = new_b2;
      BX_SER_THIS s[port].line_cntl.parity_enable = new_b3;
      BX_SER_THIS s[port].line_cntl.evenparity_sel = new_b4;
      BX_SER_THIS s[port].line_cntl.stick_parity = new_b5;
      BX_SER_THIS s[port].line_cntl.break_cntl = new_b6;
      if (BX_SER_THIS s[port].modem_cntl.local_loopback &&
          BX_SER_THIS s[port].line_cntl.break_cntl) {
        BX_SER_THIS s[port].line_status.break_int = 1;
        BX_SER_THIS s[port].line_status.framing_error = 1;
        rx_fifo_enq(port, 0x00);
      }
      /* used when doing future writes */
      if (!new_b7 && BX_SER_THIS s[port].line_cntl.dlab) {
        // Start the receive polling process if not already started
        // and there is a valid baudrate.
        if (BX_SER_THIS s[port].rx_pollstate == BX_SER_RXIDLE &&
            BX_SER_THIS s[port].baudrate != 0) {
          BX_SER_THIS s[port].rx_pollstate = BX_SER_RXPOLL;
          bx_pc_system.activate_timer(BX_SER_THIS s[port].rx_timer_index,
                                      (int) (1000000.0 / BX_SER_THIS s[port].baudrate *
                                      (BX_SER_THIS s[port].line_cntl.wordlen_sel + 5)),
                                      0); /* not continuous */
        }
        if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
          BX_SER_THIS s[port].raw->set_baudrate(BX_SER_THIS s[port].baudrate);
#endif // USE_RAW_SERIAL
        }
        BX_DEBUG(("com%d: baud rate set - %d", port+1, BX_SER_THIS s[port].baudrate));
      }
      BX_SER_THIS s[port].line_cntl.dlab = new_b7;
      break;

    case BX_SER_MCR: /* MODEM control register */
      if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
        mcr_changed = (BX_SER_THIS s[port].modem_cntl.dtr != new_b0) |
                      (BX_SER_THIS s[port].modem_cntl.rts != new_b1);
#endif
      }
      BX_SER_THIS s[port].modem_cntl.dtr  = new_b0;
      BX_SER_THIS s[port].modem_cntl.rts  = new_b1;
      BX_SER_THIS s[port].modem_cntl.out1 = new_b2;
      BX_SER_THIS s[port].modem_cntl.out2 = new_b3;

      if (new_b4 != BX_SER_THIS s[port].modem_cntl.local_loopback) {
        BX_SER_THIS s[port].modem_cntl.local_loopback = new_b4;
        if (BX_SER_THIS s[port].modem_cntl.local_loopback) {
          /* transition to loopback mode */
          if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
            if (BX_SER_THIS s[port].modem_cntl.dtr ||
                BX_SER_THIS s[port].modem_cntl.rts) {
              BX_SER_THIS s[port].raw->set_modem_control(0);
            }
#endif
          }
          if (BX_SER_THIS s[port].line_cntl.break_cntl) {
            if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
              BX_SER_THIS s[port].raw->set_break(0);
#endif
            }
            BX_SER_THIS s[port].line_status.break_int = 1;
            BX_SER_THIS s[port].line_status.framing_error = 1;
            rx_fifo_enq(port, 0x00);
          }
        } else {
          /* transition to normal mode */
          if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
            mcr_changed = 1;
            if (BX_SER_THIS s[port].line_cntl.break_cntl) {
              BX_SER_THIS s[port].raw->set_break(0);
            }
#endif
          }
        }
      }

      if (BX_SER_THIS s[port].modem_cntl.local_loopback) {
        prev_cts = BX_SER_THIS s[port].modem_status.cts;
        prev_dsr = BX_SER_THIS s[port].modem_status.dsr;
        prev_ri  = BX_SER_THIS s[port].modem_status.ri;
        prev_dcd = BX_SER_THIS s[port].modem_status.dcd;
        BX_SER_THIS s[port].modem_status.cts = BX_SER_THIS s[port].modem_cntl.rts;
        BX_SER_THIS s[port].modem_status.dsr = BX_SER_THIS s[port].modem_cntl.dtr;
        BX_SER_THIS s[port].modem_status.ri  = BX_SER_THIS s[port].modem_cntl.out1;
        BX_SER_THIS s[port].modem_status.dcd = BX_SER_THIS s[port].modem_cntl.out2;
        if (BX_SER_THIS s[port].modem_status.cts != prev_cts) {
          BX_SER_THIS s[port].modem_status.delta_cts = 1;
          BX_SER_THIS s[port].ms_ipending = 1;
        }
        if (BX_SER_THIS s[port].modem_status.dsr != prev_dsr) {
          BX_SER_THIS s[port].modem_status.delta_dsr = 1;
          BX_SER_THIS s[port].ms_ipending = 1;
        }
        if (BX_SER_THIS s[port].modem_status.ri != prev_ri)
          BX_SER_THIS s[port].ms_ipending = 1;
        if ((BX_SER_THIS s[port].modem_status.ri == 0) && (prev_ri == 1))
          BX_SER_THIS s[port].modem_status.ri_trailedge = 1;
        if (BX_SER_THIS s[port].modem_status.dcd != prev_dcd) {
          BX_SER_THIS s[port].modem_status.delta_dcd = 1;
          BX_SER_THIS s[port].ms_ipending = 1;
        }
        raise_interrupt(port, BX_SER_INT_MODSTAT);
      } else {
        if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
          if (mcr_changed) {
            BX_SER_THIS s[port].raw->set_modem_control(value & 0x03);
          }
#endif
        } else {
          /* simulate device connected */
          BX_SER_THIS s[port].modem_status.cts = 1;
          BX_SER_THIS s[port].modem_status.dsr = 1;
          BX_SER_THIS s[port].modem_status.ri  = 0;
          BX_SER_THIS s[port].modem_status.dcd = 0;
        }
      }
      break;

    case BX_SER_LSR: /* Line status register */
      BX_ERROR(("com%d: write to line status register ignored", port+1));
      break;

    case BX_SER_MSR: /* MODEM status register */
      BX_ERROR(("com%d: write to MODEM status register ignored", port+1));
      break;

    case BX_SER_SCR: /* scratch register */
      BX_SER_THIS s[port].scratch = value;
      break;

    default:
      BX_PANIC(("unsupported io write to address=0x%04x, value = 0x%02x!",
        (unsigned) address, (unsigned) value));
      break;
  }
}


void
bx_serial_c::rx_fifo_enq(Bit8u port, Bit8u data)
{
  bx_bool gen_int = 0;

  if (BX_SER_THIS s[port].fifo_cntl.enable) {
    if (BX_SER_THIS s[port].rx_fifo_end == 16) {
      BX_ERROR(("com%d: receive FIFO overflow", port+1));
      BX_SER_THIS s[port].line_status.overrun_error = 1;
      raise_interrupt(port, BX_SER_INT_RXLSTAT);
    } else {
      BX_SER_THIS s[port].rx_fifo[BX_SER_THIS s[port].rx_fifo_end++] = data;
      switch (BX_SER_THIS s[port].fifo_cntl.rxtrigger) {
        case 1:
          if (BX_SER_THIS s[port].rx_fifo_end == 4) gen_int = 1;
          break;
        case 2:
          if (BX_SER_THIS s[port].rx_fifo_end == 8) gen_int = 1;
          break;
        case 3:
          if (BX_SER_THIS s[port].rx_fifo_end == 14) gen_int = 1;
          break;
        default:
          gen_int = 1;
      }
      if (gen_int) {
        bx_pc_system.deactivate_timer(BX_SER_THIS s[port].fifo_timer_index);
        BX_SER_THIS s[port].line_status.rxdata_ready = 1;
        raise_interrupt(port, BX_SER_INT_RXDATA);
      } else {
        bx_pc_system.activate_timer(BX_SER_THIS s[port].fifo_timer_index,
                                    (int) (1000000.0 / BX_SER_THIS s[port].baudrate *
                                    (BX_SER_THIS s[port].line_cntl.wordlen_sel + 5) * 16),
                                    0); /* not continuous */
      }
    }
  } else {
    if (BX_SER_THIS s[port].line_status.rxdata_ready == 1) {
      BX_ERROR(("com%d: overrun error", port+1));
      BX_SER_THIS s[port].line_status.overrun_error = 1;
      raise_interrupt(port, BX_SER_INT_RXLSTAT);
    }
    BX_SER_THIS s[port].rxbuffer = data;
    BX_SER_THIS s[port].line_status.rxdata_ready = 1;
    raise_interrupt(port, BX_SER_INT_RXDATA);
  }
}


void
bx_serial_c::tx_timer_handler(void *this_ptr)
{
  bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;

  class_ptr->tx_timer();
}


void
bx_serial_c::tx_timer(void)
{
  bx_bool gen_int = 0;
  Bit8u port = 0;
  int timer_id;

  timer_id = bx_pc_system.triggeredTimerID();
  if (timer_id == BX_SER_THIS s[0].tx_timer_index) {
    port = 0;
  } else if (timer_id == BX_SER_THIS s[1].tx_timer_index) {
    port = 1;
  } else if (timer_id == BX_SER_THIS s[2].tx_timer_index) {
    port = 2;
  } else if (timer_id == BX_SER_THIS s[3].tx_timer_index) {
    port = 3;
  }
  if (BX_SER_THIS s[port].modem_cntl.local_loopback) {
    rx_fifo_enq(port, BX_SER_THIS s[port].tsrbuffer);
  } else {
    switch (BX_SER_THIS s[port].io_mode) {
      case BX_SER_MODE_FILE:
        fputc(BX_SER_THIS s[port].tsrbuffer, BX_SER_THIS s[port].output);
        fflush(BX_SER_THIS s[port].output);
        break;
      case BX_SER_MODE_TERM:
#if defined(SERIAL_ENABLE)
        BX_DEBUG(("com%d: write: '%c'", port+1, BX_SER_THIS s[port].tsrbuffer));
        if (BX_SER_THIS s[port].tty_id >= 0) {
          write(BX_SER_THIS s[port].tty_id, (bx_ptr_t) & BX_SER_THIS s[port].tsrbuffer, 1);
        }
#endif
        break;
      case BX_SER_MODE_RAW:
#if USE_RAW_SERIAL
        if (!BX_SER_THIS s[port].raw->ready_transmit())
          BX_PANIC(("com%d: not ready to transmit", port+1));
        BX_SER_THIS s[port].raw->transmit(BX_SER_THIS s[port].tsrbuffer);
#endif
        break;
    }
  }

  BX_SER_THIS s[port].line_status.tsr_empty = 1;
  if (BX_SER_THIS s[port].fifo_cntl.enable && (BX_SER_THIS s[port].tx_fifo_end > 0)) {
    BX_SER_THIS s[port].tsrbuffer = BX_SER_THIS s[port].tx_fifo[0];
    BX_SER_THIS s[port].line_status.tsr_empty = 0;
    memcpy(&BX_SER_THIS s[port].tx_fifo[0], &BX_SER_THIS s[port].tx_fifo[1], 15);
    gen_int = (--BX_SER_THIS s[port].tx_fifo_end == 0);
  } else if (!BX_SER_THIS s[port].line_status.thr_empty) {
    BX_SER_THIS s[port].tsrbuffer = BX_SER_THIS s[port].thrbuffer;
    BX_SER_THIS s[port].line_status.tsr_empty = 0;
    gen_int = 1;
  }
  if (!BX_SER_THIS s[port].line_status.tsr_empty) {
    if (gen_int) {
      BX_SER_THIS s[port].line_status.thr_empty = 1;
      raise_interrupt(port, BX_SER_INT_TXHOLD);
    }
    bx_pc_system.activate_timer(BX_SER_THIS s[port].tx_timer_index,
                                (int) (1000000.0 / BX_SER_THIS s[port].baudrate *
                                (BX_SER_THIS s[port].line_cntl.wordlen_sel + 5)),
                                0); /* not continuous */
  }
}


void
bx_serial_c::rx_timer_handler(void *this_ptr)
{
  bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;

  class_ptr->rx_timer();
}


void
bx_serial_c::rx_timer(void)
{
#if BX_HAVE_SELECT && defined(SERIAL_ENABLE)
  struct timeval tval;
  fd_set fds;
#endif
  Bit8u port = 0;
  int timer_id;
  bx_bool data_ready = 0;

  timer_id = bx_pc_system.triggeredTimerID();
  if (timer_id == BX_SER_THIS s[0].rx_timer_index) {
    port = 0;
  } else if (timer_id == BX_SER_THIS s[1].rx_timer_index) {
    port = 1;
  } else if (timer_id == BX_SER_THIS s[2].rx_timer_index) {
    port = 2;
  } else if (timer_id == BX_SER_THIS s[3].rx_timer_index) {
    port = 3;
  }
  int bdrate = BX_SER_THIS s[port].baudrate / (BX_SER_THIS s[port].line_cntl.wordlen_sel + 5);
  unsigned char chbuf = 0;

  if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_TERM) {
#if BX_HAVE_SELECT && defined(SERIAL_ENABLE)
    tval.tv_sec  = 0;
    tval.tv_usec = 0;

// MacOS: I'm not sure what to do with this, since I don't know
// what an fd_set is or what FD_SET() or select() do. They aren't
// declared in the CodeWarrior standard library headers. I'm just
// leaving it commented out for the moment.

    FD_ZERO(&fds);
    if (BX_SER_THIS s[port].tty_id >= 0) FD_SET(BX_SER_THIS s[port].tty_id, &fds);
#endif
  }
  if ((BX_SER_THIS s[port].line_status.rxdata_ready == 0) ||
      (BX_SER_THIS s[port].fifo_cntl.enable)) {
    if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_RAW) {
#if USE_RAW_SERIAL
      int data;
      if ((data_ready = BX_SER_THIS s[port].raw->ready_receive())) {
        data = BX_SER_THIS s[port].raw->receive();
        if (data < 0 ) {
          data_ready = 0;
          switch (data) {
            case RAW_EVENT_BREAK:
              BX_SER_THIS s[port].line_status.break_int = 1;
              raise_interrupt(port, BX_SER_INT_RXLSTAT);
              break;
            case RAW_EVENT_FRAME:
              BX_SER_THIS s[port].line_status.framing_error = 1;
              raise_interrupt(port, BX_SER_INT_RXLSTAT);
              break;
            case RAW_EVENT_OVERRUN:
              BX_SER_THIS s[port].line_status.overrun_error = 1;
              raise_interrupt(port, BX_SER_INT_RXLSTAT);
              break;
            case RAW_EVENT_PARITY:
              BX_SER_THIS s[port].line_status.parity_error = 1;
              raise_interrupt(port, BX_SER_INT_RXLSTAT);
              break;
            case RAW_EVENT_CTS_ON:
            case RAW_EVENT_CTS_OFF:
            case RAW_EVENT_DSR_ON:
            case RAW_EVENT_DSR_OFF:
            case RAW_EVENT_RING_ON:
            case RAW_EVENT_RING_OFF:
            case RAW_EVENT_RLSD_ON:
            case RAW_EVENT_RLSD_OFF:
              raise_interrupt(port, BX_SER_INT_MODSTAT);
              break;
          }
        }
      }
      if (data_ready) {
        chbuf = data;
      }
#endif
    } else if (BX_SER_THIS s[port].io_mode == BX_SER_MODE_TERM) {
#if BX_HAVE_SELECT && defined(SERIAL_ENABLE)
      if ((BX_SER_THIS s[port].tty_id >= 0) && (select(BX_SER_THIS s[port].tty_id + 1, &fds, NULL, NULL, &tval) == 1)) {
        (void) read(BX_SER_THIS s[port].tty_id, &chbuf, 1);
        BX_DEBUG(("com%d: read: '%c'", port+1, chbuf));
        data_ready = 1;
      }
#endif
    }
    if (data_ready) {
      if (!BX_SER_THIS s[port].modem_cntl.local_loopback) {
        rx_fifo_enq(port, chbuf);
      }
    } else {
      if (!BX_SER_THIS s[port].fifo_cntl.enable) {
        bdrate = (int) (1000000.0 / 100000); // Poll frequency is 100ms
      }
    }
  } else {
    // Poll at 4x baud rate to see if the next-char can
    // be read
    bdrate *= 4;
  }

  bx_pc_system.activate_timer(BX_SER_THIS s[port].rx_timer_index,
                              (int) (1000000.0 / bdrate),
                              0); /* not continuous */
}


void
bx_serial_c::fifo_timer_handler(void *this_ptr)
{
  bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;

  class_ptr->fifo_timer();
}


void
bx_serial_c::fifo_timer(void)
{
  Bit8u port = 0;
  int timer_id;

  timer_id = bx_pc_system.triggeredTimerID();
  if (timer_id == BX_SER_THIS s[0].fifo_timer_index) {
    port = 0;
  } else if (timer_id == BX_SER_THIS s[1].fifo_timer_index) {
    port = 1;
  } else if (timer_id == BX_SER_THIS s[2].fifo_timer_index) {
    port = 2;
  } else if (timer_id == BX_SER_THIS s[3].fifo_timer_index) {
    port = 3;
  }
  BX_SER_THIS s[port].line_status.rxdata_ready = 1;
  raise_interrupt(port, BX_SER_INT_FIFO);
}

Generated by  Doxygen 1.6.0   Back to index