Esempio n. 1
0
static void
deliver_mn103cpu_interrupt (struct hw *me,
			    void *data)
{
  struct mn103cpu *controller = hw_data (me);
  SIM_DESC simulator = hw_system (me);
  sim_cpu *cpu = STATE_CPU (simulator, 0);

  if (controller->pending_reset)
    {
      controller->pending_reset = 0;
      /* need to clear all registers et.al! */
      HW_TRACE ((me, "Reset!"));
      hw_abort (me, "Reset!");
    }
  else if (controller->pending_nmi)
    {
      controller->pending_nmi = 0;
      store_word (SP - 4, CPU_PC_GET (cpu));
      store_half (SP - 8, PSW);
      PSW &= ~PSW_IE;
      SP = SP - 8;
      CPU_PC_SET (cpu, 0x40000008);
      HW_TRACE ((me, "nmi pc=0x%08lx psw=0x%04x sp=0x%08lx",
		 (long) CPU_PC_GET (cpu), (unsigned) PSW, (long) SP));
    }
  else if ((controller->pending_level < EXTRACT_PSW_LM)
	   && (PSW & PSW_IE))
    {
      /* Don't clear pending level.  Request continues to be pending
         until the interrupt controller clears/changes it */
      store_word (SP - 4, CPU_PC_GET (cpu));
      store_half (SP - 8, PSW);
      PSW &= ~PSW_IE;
      PSW &= ~PSW_LM;
      PSW |= INSERT_PSW_LM (controller->pending_level);
      SP = SP - 8;
      CPU_PC_SET (cpu, 0x40000000 + controller->interrupt_vector[controller->pending_level]);
      HW_TRACE ((me, "port-out ack %d", controller->pending_level));
      hw_port_event (me, ACK_PORT, controller->pending_level);
      HW_TRACE ((me, "int level=%d pc=0x%08lx psw=0x%04x sp=0x%08lx",
		 controller->pending_level,
		 (long) CPU_PC_GET (cpu), (unsigned) PSW, (long) SP));
    }

  if (controller->pending_level < 7) /* FIXME */
    {
      /* As long as there is the potential need to deliver an
	 interrupt we keep rescheduling this routine. */
      if (controller->pending_handler != NULL)
	controller->pending_handler =
	  hw_event_queue_schedule (me, 1, deliver_mn103cpu_interrupt, NULL);
    }
  else
    {
      /* Don't bother re-scheduling the interrupt handler as there is
         nothing to deliver */
      controller->pending_handler = NULL;
    }

}
static unsigned
m68hc11spi_io_write_buffer (struct hw *me,
                            const void *source,
                            int space,
                            unsigned_word base,
                            unsigned nr_bytes)
{
  SIM_DESC sd;
  struct m68hc11spi *controller;
  sim_cpu *cpu;
  unsigned8 val;

  HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));

  sd  = hw_system (me);
  cpu = STATE_CPU (sd, 0);
  controller = hw_data (me);
  
  val = *((const unsigned8*) source);
  switch (base)
    {
    case M6811_SPCR:
      cpu->ios[M6811_SPCR] = val;

      /* The SPI clock rate is 2, 4, 16, 32 of the internal CPU clock.
         We have to drive the clock pin and need a 2x faster clock.  */
      switch (val & (M6811_SPR1 | M6811_SPR0))
        {
        case 0:
          controller->clock = 1;
          break;

        case 1:
          controller->clock = 2;
          break;

        case 2:
          controller->clock = 8;
          break;

        default:
          controller->clock = 16;
          break;
        }

      /* Set the clock pin.  */
      if ((val & M6811_CPOL)
          && (controller->spi_event == 0
              || ((val & M6811_CPHA) && controller->mode == 1)))
        controller->clk_pin = 1;
      else
        controller->clk_pin = 0;

      set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
      break;
      
      /* Can't write to SPSR.  */
    case M6811_SPSR:
      break;
      
    case M6811_SPDR:
      if (!(cpu->ios[M6811_SPCR] & M6811_SPE))
        {
          return 0;
        }

      if (controller->rx_clear_scsr)
        {
          cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
          controller->rx_clear_scsr = 0;
          interrupts_update_pending (&cpu->cpu_interrupts);
        }

      /* If transfer is taking place, a write to SPDR
         generates a collision.  */
      if (controller->spi_event)
        {
          cpu->ios[M6811_SPSR] |= M6811_WCOL;
          break;
        }

      /* Refuse the write if there was no read of SPSR.  */
      /* ???? TBD. */

      /* Prepare to send a byte.  */
      controller->tx_char = val;
      controller->mode   = SPI_START_BYTE;

      /* Toggle clock pin internal value when CPHA is 0 so that
         it will really change in the middle of a bit.  */
      if (!(cpu->ios[M6811_SPCR] & M6811_CPHA))
        controller->clk_pin = ~controller->clk_pin;

      cpu->ios[M6811_SPDR] = val;

      /* Activate transmission.  */
      m68hc11spi_clock (me, NULL);
      break;

    default:
      return 0;
    }
  return nr_bytes;
}     
static unsigned
m68hc11eepr_io_write_buffer (struct hw *me,
			     const void *source,
			     int space,
			     unsigned_word base,
			     unsigned nr_bytes)
{
  SIM_DESC sd;
  struct m68hc11eepr *controller;
  sim_cpu *cpu;
  uint8 val;

  HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));

  sd         = hw_system (me);
  controller = hw_data (me);
  cpu        = STATE_CPU (sd, 0);

  /* Programming several bytes at a time is not possible.  */
  if (space != io_map && nr_bytes != 1)
    {
      sim_memory_error (cpu, SIM_SIGBUS, base,
			"EEprom write error (only 1 byte can be programmed)");
      return 0;
    }
  
  if (nr_bytes != 1)
    hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");

  val = *((const uint8*) source);

  /* Write to the EEPROM control register.  */
  if (space == io_map && base == M6811_PPROG)
    {
      uint8 wrong_bits;
      uint16 addr;
      
      addr = base + cpu_get_io_base (cpu);

      /* Setting EELAT and EEPGM at the same time is an error.
         Clearing them both is ok.  */
      wrong_bits = (cpu->ios[M6811_PPROG] ^ val) & val;
      wrong_bits &= (M6811_EELAT | M6811_EEPGM);

      if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
	{
	  sim_memory_error (cpu, SIM_SIGBUS, addr,
			    "Wrong eeprom programing value");
	  return 0;
	}

      if ((val & M6811_EELAT) == 0)
	{
	  val = 0;
	}
      if ((val & M6811_EEPGM) && !(cpu->ios[M6811_PPROG] & M6811_EELAT))
	{
	  sim_memory_error (cpu, SIM_SIGBUS, addr,
			    "EEProm high voltage applied after EELAT");
	}
      if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
	{
	  sim_memory_error (cpu, SIM_SIGSEGV, addr,
			    "EEProm high voltage applied without address");
	}
      if (val & M6811_EEPGM)
	{
	  controller->eeprom_wcycle = cpu_current_cycle (cpu);
	}
      else if (cpu->ios[M6811_PPROG] & M6811_PPROG)
	{
	  int i;
	  unsigned long t = cpu_current_cycle (cpu);

	  t -= controller->eeprom_wcycle;
	  if (t < controller->eeprom_min_cycles)
	    {
	      sim_memory_error (cpu, SIM_SIGILL, addr,
				"EEprom programmed only for %lu cycles",
				t);
	    }

	  /* Program the byte by clearing some bits.  */
	  if (!(cpu->ios[M6811_PPROG] & M6811_ERASE))
	    {
	      controller->eeprom[controller->eeprom_waddr]
		&= controller->eeprom_wbyte;
	    }

	  /* Erase a byte, row or the complete eeprom.  Erased value is 0xFF.
             Ignore row or complete eeprom erase when we are programming the
             CONFIG register (last EEPROM byte).  */
	  else if ((cpu->ios[M6811_PPROG] & M6811_BYTE)
                   || controller->eeprom_waddr == controller->size - 1)
	    {
	      controller->eeprom[controller->eeprom_waddr] = 0xff;
	    }
	  else if (cpu->ios[M6811_BYTE] & M6811_ROW)
	    {
              size_t max_size;

              /* Size of EEPROM (-1 because the last byte is the
                 CONFIG register.  */
              max_size = controller->size;
	      controller->eeprom_waddr &= 0xFFF0;
	      for (i = 0; i < 16
                     && controller->eeprom_waddr < max_size; i++)
		{
		  controller->eeprom[controller->eeprom_waddr] = 0xff;
		  controller->eeprom_waddr ++;
		}
	    }
	  else
	    {
              size_t max_size;

              max_size = controller->size;
	      for (i = 0; i < max_size; i++)
		{
		  controller->eeprom[i] = 0xff;
		}
	    }

	  /* Save the eeprom in a file.  We have to save after each
	     change because the simulator can be stopped or crash...  */
	  if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
	    {
	      sim_memory_error (cpu, SIM_SIGABRT, addr,
				"EEPROM programing failed: errno=%d", errno);
	    }
	  controller->eeprom_wmode = 0;
	}
      cpu->ios[M6811_PPROG] = val;
      return 1;
    }

  /* The CONFIG IO register is mapped at end of EEPROM.
     It's not visible.  */
  if (space == io_map && base == M6811_CONFIG)
    {
      base = controller->size - 1;
    }
  else
    {
      base = base - controller->base_address;
    }

  /* Writing the memory is allowed for the Debugger or simulator
     (cpu not running).  */
  if (cpu_is_running (cpu))
    {
      if ((cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
	{
	  sim_memory_error (cpu, SIM_SIGSEGV, base,
			    "EEprom not configured for writing");
	  return 0;
	}
      if (controller->eeprom_wmode != 0)
	{
	  sim_memory_error (cpu, SIM_SIGSEGV, base,
			    "EEprom write error");
	  return 0;
	}
      controller->eeprom_wmode = 1;
      controller->eeprom_waddr = base;
      controller->eeprom_wbyte = val;
    }
  else
    {
      controller->eeprom[base] = val;
      m6811eepr_memory_rw (controller, O_WRONLY);
    }
  
  return 1;
}
Esempio n. 4
0
signed64
hw_event_queue_time (struct hw *me)
{
  return sim_events_time (hw_system (me));
}
void
m68hc11spi_clock (struct hw *me, void *data)
{
  SIM_DESC sd;
  struct m68hc11spi* controller;
  sim_cpu *cpu;
  int check_interrupt = 0;
  
  controller = hw_data (me);
  sd         = hw_system (me);
  cpu        = STATE_CPU (sd, 0);

  /* Cleanup current event.  */
  if (controller->spi_event)
    {
      hw_event_queue_deschedule (me, controller->spi_event);
      controller->spi_event = 0;
    }

  /* Change a bit of data at each two SPI event.  */
  if (controller->mode == SPI_START_BIT)
    {
      /* Reflect the bit value on bit 2 of port D.  */
      set_bit_port (me, cpu, M6811_PORTD, (1 << 2),
                    (controller->tx_char & (1 << controller->tx_bit)));
      controller->tx_bit--;
      controller->mode = SPI_MIDDLE_BIT;
    }
  else if (controller->mode == SPI_MIDDLE_BIT)
    {
      controller->mode = SPI_START_BIT;
    }

  if (controller->mode == SPI_START_BYTE)
    {
      /* Start a new SPI transfer.  */
      
      /* TBD: clear SS output.  */
      controller->mode = SPI_START_BIT;
      controller->tx_bit = 7;
      set_bit_port (me, cpu, M6811_PORTD, (1 << 4), ~controller->clk_pin);
    }
  else
    {
      /* Change the SPI clock at each event on bit 4 of port D.  */
      controller->clk_pin = ~controller->clk_pin;
      set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
    }
  
  /* Transmit is now complete for this byte.  */
  if (controller->mode == SPI_START_BIT && controller->tx_bit < 0)
    {
      controller->rx_clear_scsr = 0;
      cpu->ios[M6811_SPSR] |= M6811_SPIF;
      if (cpu->ios[M6811_SPCR] & M6811_SPIE)
        check_interrupt = 1;
    }
  else
    {
      controller->spi_event = hw_event_queue_schedule (me, controller->clock,
                                                       m68hc11spi_clock,
                                                       NULL);
    }

  if (check_interrupt)
    interrupts_update_pending (&cpu->cpu_interrupts);
}
struct hw *
hw_create (struct sim_state *sd,
	   struct hw *parent,
	   const char *family,
	   const char *name,
	   const char *unit,
	   const char *args)
{
 /* NOTE: HW must be allocated using ZALLOC, others use HW_ZALLOC */
  struct hw *hw = ZALLOC (struct hw);

  /* our identity */
  hw->family_of_hw = hw_strdup (hw, family);
  hw->name_of_hw = hw_strdup (hw, name);
  hw->args_of_hw = hw_strdup (hw, args);

  /* a hook into the system */
  if (sd != NULL)
    hw->system_of_hw = sd;
  else if (parent != NULL)
    hw->system_of_hw = hw_system (parent);
  else
    hw_abort (parent, "No system found");

  /* in a tree */
  if (parent != NULL)
    {
      struct hw **sibling = &parent->child_of_hw;
      while ((*sibling) != NULL)
	sibling = &(*sibling)->sibling_of_hw;
      *sibling = hw;
      hw->parent_of_hw = parent;
    }

  /* top of tree */
  if (parent != NULL)
    {
      struct hw *root = parent;
      while (root->parent_of_hw != NULL)
	root = root->parent_of_hw;
      hw->root_of_hw = root;
    }
  
  /* a unique identifier for the device on the parents bus */
  if (parent != NULL)
    {
      hw_unit_decode (parent, unit, &hw->unit_address_of_hw);
    }

  /* Determine our path */
  if (parent != NULL)
    hw->path_of_hw = full_name_of_hw (hw, NULL, 0);
  else
    hw->path_of_hw = "/";

  /* create our base type */
  hw->base_of_hw = HW_ZALLOC (hw, struct hw_base_data);
  hw->base_of_hw->finished_p = 0;

  /* our callbacks */
  set_hw_io_read_buffer (hw, panic_hw_io_read_buffer);
  set_hw_io_write_buffer (hw, panic_hw_io_write_buffer);
  set_hw_dma_read_buffer (hw, passthrough_hw_dma_read_buffer);
  set_hw_dma_write_buffer (hw, passthrough_hw_dma_write_buffer);
  set_hw_unit_decode (hw, generic_hw_unit_decode);
  set_hw_unit_encode (hw, generic_hw_unit_encode);
  set_hw_unit_address_to_attach_address (hw, generic_hw_unit_address_to_attach_address);
  set_hw_unit_size_to_attach_size (hw, generic_hw_unit_size_to_attach_size);
  set_hw_attach_address (hw, passthrough_hw_attach_address);
  set_hw_detach_address (hw, passthrough_hw_detach_address);
  set_hw_delete (hw, ignore_hw_delete);

  /* locate a descriptor */
  {
    const struct hw_descriptor **table;
    for (table = hw_descriptors;
	 *table != NULL;
	 table++)
      {
	const struct hw_descriptor *entry;
	for (entry = *table;
	     entry->family != NULL;
	     entry++)
	  {
	    if (strcmp (family, entry->family) == 0)
	      {
		hw->base_of_hw->descriptor = entry;
		break;
	      }
	  }
      }
    if (hw->base_of_hw->descriptor == NULL)
      {
	hw_abort (parent, "Unknown device `%s'", family);
      }
  }

  /* Attach dummy ports */
  create_hw_alloc_data (hw);
  create_hw_property_data (hw);
  create_hw_port_data (hw);
  create_hw_event_data (hw);
  create_hw_handle_data (hw);
  create_hw_instance_data (hw);
  
  return hw;
}
static unsigned
m68hc11sio_io_write_buffer (struct hw *me,
                            const void *source,
                            int space,
                            unsigned_word base,
                            unsigned nr_bytes)
{
  SIM_DESC sd;
  struct m68hc11sio *controller;
  sim_cpu *cpu;
  unsigned8 val;

  HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));

  sd  = hw_system (me);
  cpu = STATE_CPU (sd, 0);
  controller = hw_data (me);
  
  val = *((const unsigned8*) source);
  switch (base)
    {
    case M6811_BAUD:
      {
        long divisor;
        long baud;

        cpu->ios[M6811_BAUD] = val;        
        switch (val & (M6811_SCP1|M6811_SCP0))
          {
          case M6811_BAUD_DIV_1:
            divisor = 1 * 16;
            break;

          case M6811_BAUD_DIV_3:
            divisor = 3 * 16;
            break;

          case M6811_BAUD_DIV_4:
            divisor = 4 * 16;
            break;

          default:
          case M6811_BAUD_DIV_13:
            divisor = 13 * 16;
            break;
          }
        val &= (M6811_SCR2|M6811_SCR1|M6811_SCR0);
        divisor *= (1 << val);

        baud = (cpu->cpu_frequency / 4) / divisor;

        HW_TRACE ((me, "divide rate %ld, baud rate %ld",
                   divisor, baud));

        controller->baud_cycle = divisor;
      }
      break;
      
    case M6811_SCCR1:
      {
        if (val & M6811_M)
          controller->data_length = 11;
        else
          controller->data_length = 10;

        cpu->ios[M6811_SCCR1] = val;
      }
      break;
      
    case M6811_SCCR2:
      if ((val & M6811_RE) == 0)
        {
          val &= ~(M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF);
          val |= (cpu->ios[M6811_SCCR2]
                  & (M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF));
          cpu->ios[M6811_SCCR2] = val;
          break;
        }

      /* Activate reception.  */
      if (controller->rx_poll_event == 0)
        {
          long clock_cycle;
          
          /* Compute CPU clock cycles to wait for the next character.  */
          clock_cycle = controller->data_length * controller->baud_cycle;

          controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
                                                               m68hc11sio_rx_poll,
                                                               NULL);
        }      
      cpu->ios[M6811_SCCR2] = val;
      interrupts_update_pending (&cpu->cpu_interrupts);
      break;

      /* No effect.  */
    case M6811_SCSR:
      return 1;
      
    case M6811_SCDR:
      if (!(cpu->ios[M6811_SCSR] & M6811_TDRE))
        {
          return 0;
        }

      controller->tx_char     = val;
      controller->tx_has_char = 1;
      if ((cpu->ios[M6811_SCCR2] & M6811_TE)
          && controller->tx_poll_event == 0)
        {
          m68hc11sio_tx_poll (me, NULL);
        }
      return 1;
      
    default:
      return 0;
    }
  return nr_bytes;
}     
Esempio n. 8
0
static void
read_status_reg (struct hw *me,
		 struct mn103ser *serial,
		 unsigned_word serial_reg,
		 void *dest,
		 unsigned  nr_bytes)
{
  char c;
  int count;

  if ( (serial->device[serial_reg].status & SIO_STAT_RRDY) == 0 )
    {
      /* FIFO is empty */
      /* Kill current poll event */
      if ( NULL != serial->device[serial_reg].event )
	{
	  hw_event_queue_deschedule (me, serial->device[serial_reg].event);
	  serial->device[serial_reg].event = NULL;
	}

      if(USE_SOCKSER_P)
	{
	  int rd;
	  rd = dv_sockser_read (hw_system (me));
	  if(rd != -1)
	    {
	      c = (char) rd;
	      count = 1;
	    }
	  else
	    {
	      count = HW_IO_NOT_READY;
	    }
	}
      else
	{
	  count = do_hw_poll_read (me, serial->reader,
				   0/*STDIN*/, &c, sizeof(c));
	}

      switch (count)
	{
	case HW_IO_NOT_READY:
	case HW_IO_EOF:
	  serial->device[serial_reg].rxb = 0;
	  serial->device[serial_reg].status &= ~SIO_STAT_RRDY;
	  break;
	default:
	  serial->device[serial_reg].rxb = c;
	  serial->device[serial_reg].status |= SIO_STAT_RRDY;
	  hw_port_event (me, serial_reg+SERIAL0_RECEIVE, 1);
	}

      /* schedule polling event */
      serial->device[serial_reg].event
	= hw_event_queue_schedule (me, 1000,
				   do_polling_event,
				   (void *) (long) serial_reg);
    }

  if ( nr_bytes == 1 )
    {
      *(unsigned8 *)dest = (unsigned8)serial->device[serial_reg].status;
    }
  else if ( nr_bytes == 2 && serial_reg != SC2STR )
    {
      *(unsigned16 *)dest = H2LE_2 (serial->device[serial_reg].status);
    }
  else
    {
      hw_abort (me, "bad read size of %d bytes from SC%dSTR.", nr_bytes, 
		serial_reg);
    }
}
static void
m68hc11sio_port_event (struct hw *me,
                       int my_port,
                       struct hw *source,
                       int source_port,
                       int level)
{
  SIM_DESC sd;
  struct m68hc11sio *controller;
  sim_cpu *cpu;
  unsigned8 val;
  
  controller = hw_data (me);
  sd         = hw_system (me);
  cpu        = STATE_CPU (sd, 0);  
  switch (my_port)
    {
    case RESET_PORT:
      {
	HW_TRACE ((me, "SCI reset"));

        /* Reset the state of SCI registers.  */
        val = 0;
        m68hc11sio_io_write_buffer (me, &val, io_map,
                                    (unsigned_word) M6811_BAUD, 1);
        m68hc11sio_io_write_buffer (me, &val, io_map,
                                    (unsigned_word) M6811_SCCR1, 1);
        m68hc11sio_io_write_buffer (me, &val, io_map,
                                    (unsigned_word) M6811_SCCR2, 1);
        
        cpu->ios[M6811_SCSR]    = M6811_TC | M6811_TDRE;
        controller->rx_char     = 0;
        controller->tx_char     = 0;
        controller->tx_has_char = 0;
        controller->rx_clear_scsr = 0;
        if (controller->rx_poll_event)
          {
            hw_event_queue_deschedule (me, controller->rx_poll_event);
            controller->rx_poll_event = 0;
          }
        if (controller->tx_poll_event)
          {
            hw_event_queue_deschedule (me, controller->tx_poll_event);
            controller->tx_poll_event = 0;
          }

        /* In bootstrap mode, initialize the SCI to 1200 bauds to
           simulate some initial setup by the internal rom.  */
        if (((cpu->ios[M6811_HPRIO]) & (M6811_SMOD | M6811_MDA)) == M6811_SMOD)
          {
            unsigned char val = 0x33;
            
            m68hc11sio_io_write_buffer (me, &val, io_map,
                                        (unsigned_word) M6811_BAUD, 1);
            val = 0x12;
            m68hc11sio_io_write_buffer (me, &val, io_map,
                                        (unsigned_word) M6811_SCCR2, 1);
          }
        break;
      }

    default:
      hw_abort (me, "Event on unknown port %d", my_port);
      break;
    }
}
Esempio n. 10
0
static unsigned
m68hc11tim_io_write_buffer (struct hw *me,
                            const void *source,
                            int space,
                            unsigned_word base,
                            unsigned nr_bytes)
{
  SIM_DESC sd;
  struct m68hc11tim *controller;
  sim_cpu *cpu;
  unsigned8 val, n;
  signed64 adj;
  int reset_compare = 0;
  int reset_overflow = 0;
  int cnt = 0;
  
  HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));

  sd  = hw_system (me);
  cpu = STATE_CPU (sd, 0);
  controller = hw_data (me);

  while (nr_bytes)
    {
      val = *((const unsigned8*) source);
      switch (base)
        {
          /* Set the timer counter low part, trying to preserve the low part.
             We compute the absolute cycle adjustment that we have to apply
             to obtain the timer current value.  Computation must be made
             in 64-bit to avoid overflow problems.  */
        case M6811_TCTN_L:
          adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
                 / (controller->clock_prescaler * (signed64) 256)) & 0x0FF;
          adj = cpu->cpu_absolute_cycle
            - (adj * controller->clock_prescaler * (signed64) 256)
            - ((signed64) adj * controller->clock_prescaler);
          controller->tcnt_adjust = adj;
          reset_compare = 1;
          reset_overflow = 1;
          break;

        case M6811_TCTN_H:
          adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
                 / controller->clock_prescaler) & 0x0ff;
          adj = cpu->cpu_absolute_cycle
            - ((signed64) val * controller->clock_prescaler * (signed64) 256)
            - (adj * controller->clock_prescaler);
          controller->tcnt_adjust = adj;
          reset_compare = 1;
          reset_overflow = 1;
          break;

        case M6811_TMSK2:

          /* Timer prescaler cannot be changed after 64 bus cycles.  */
          if (cpu->cpu_absolute_cycle >= 64)
            {
              val &= ~(M6811_PR1 | M6811_PR0);
              val |= cpu->ios[M6811_TMSK2] & (M6811_PR1 | M6811_PR0);
            }
          switch (val & (M6811_PR1 | M6811_PR0))
            {
            case 0:
              n = 1;
              break;
            case M6811_PR0:
              n = 4;
              break;
            case M6811_PR1:
              n = 8;
              break;
            default:
            case M6811_PR1 | M6811_PR0:
              n = 16;
              break;
            }
          if (cpu->cpu_absolute_cycle < 64)
            {
              reset_overflow = 1;
              controller->clock_prescaler = n;
            }
          cpu->ios[base] = val;
          interrupts_update_pending (&cpu->cpu_interrupts);
          break;

        case M6811_PACTL:
          n = (1 << ((val & (M6811_RTR1 | M6811_RTR0))));
          cpu->ios[base] = val;

          controller->rti_delay = (long) (n) * 8192;
          m68hc11tim_timer_event (me, (void*) (RTI_EVENT| 0x100));
          break;
      
        case M6811_TFLG2:
          val &= cpu->ios[M6811_TFLG2];
          cpu->ios[M6811_TFLG2] &= ~val;
          interrupts_update_pending (&cpu->cpu_interrupts);
          break;

        case M6811_TMSK1:
          cpu->ios[M6811_TMSK1] = val;
          interrupts_update_pending (&cpu->cpu_interrupts);
          reset_compare = 1;
          break;

        case M6811_TFLG1:
          val &= cpu->ios[M6811_TFLG1];
          cpu->ios[M6811_TFLG1] &= ~val;
          interrupts_update_pending (&cpu->cpu_interrupts);          
          break;

        case M6811_TOC1:
        case M6811_TOC2:
        case M6811_TOC3:
        case M6811_TOC4:
        case M6811_TOC5:
          cpu->ios[base] = val;
          reset_compare = 1;
          break;

        case M6811_TCTL1:
        case M6811_TCTL2:
          cpu->ios[base] = val;
          break;

        default:
          cpu->ios[base] = val;
          break;
        }

      base++;
      nr_bytes--;
      cnt++;
      source = (char*) source + 1;
    }

  /* Re-compute the next timer compare event.  */
  if (reset_compare)
    {
      m68hc11tim_timer_event (me, (void*) (COMPARE_EVENT));
    }
  if (reset_overflow)
    {
      m68hc11tim_timer_event (me, (void*) (OVERFLOW_EVENT| 0x100));
    }
  return cnt;
}     
/* Send enqueued characters from tx_fifo and trigger TX interrupt.
Receive characters into rx_fifo and trigger RX interrupt. */
void
tx3904sio_tickle(struct hw *me)
{
    struct tx3904sio* controller = hw_data(me);
    int c;
    char cc;
    unsigned_4 last_int, next_int;

    /* HW_TRACE ((me, "tickle backend: %02x", controller->backend)); */
    switch(controller->backend)
    {
    case sio_tcp:

        while(tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
        {
            cc = tx3904sio_fifo_pop(me, & controller->tx_fifo);
            dv_sockser_write(hw_system(me), cc);
            HW_TRACE ((me, "tcp output: %02x", cc));
        }

        c = dv_sockser_read(hw_system(me));
        while(c != -1)
        {
            cc = (char) c;
            HW_TRACE ((me, "tcp input: %02x", cc));
            tx3904sio_fifo_push(me, & controller->rx_fifo, cc);
            c = dv_sockser_read(hw_system(me));
        }
        break;

    case sio_stdio:

        while(tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
        {
            cc = tx3904sio_fifo_pop(me, & controller->tx_fifo);
            sim_io_write_stdout(hw_system(me), & cc, 1);
            sim_io_flush_stdout(hw_system(me));
            HW_TRACE ((me, "stdio output: %02x", cc));
        }

        c = sim_io_poll_read(hw_system(me), 0 /* stdin */, & cc, 1);
        while(c == 1)
        {
            HW_TRACE ((me, "stdio input: %02x", cc));
            tx3904sio_fifo_push(me, & controller->rx_fifo, cc);
            c = sim_io_poll_read(hw_system(me), 0 /* stdin */, & cc, 1);
        }

        break;

    default:
        hw_abort(me, "Illegal backend mode: %d", controller->backend);
    }

    /* Update RDIS / TDIS flags */
    last_int = controller->sdisr & controller->sdicr;
    /* HW_TRACE ((me, "tickle - sdisr %08x sdicr %08x", controller->sdisr, controller->sdicr)); */
    if(tx3904sio_fifo_nonempty(me, & controller->rx_fifo))
        SDISR_SET_RDIS(controller);
    if(! tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
        SDISR_SET_TDIS(controller);
    next_int = controller->sdisr & controller->sdicr;
    /* HW_TRACE ((me, "tickle + sdisr %08x sdicr %08x", controller->sdisr, controller->sdicr)); */

    if(~last_int & next_int) /* any bits set? */
        hw_port_event(me, INT_PORT, 1);
    if(last_int & ~next_int) /* any bits cleared? */
        hw_port_event(me, INT_PORT, 0);

    /* Add periodic polling for this port, if it's not already going. */
    if(controller->poll_event == NULL)
    {
        controller->poll_event = hw_event_queue_schedule (me, 1000,
                                 tx3904sio_poll, NULL);

    }
}
Esempio n. 12
0
static void
m68hc11tim_info (struct hw *me)
{
  SIM_DESC sd;
  uint16 base = 0;
  sim_cpu *cpu;
  struct m68hc11tim *controller;
  uint8 val;
  uint16 val16;
  
  sd = hw_system (me);
  cpu = STATE_CPU (sd, 0);
  controller = hw_data (me);
  
  sim_io_printf (sd, "M68HC11 Timer:\n");

  base = cpu_get_io_base (cpu);

  /* Info for TIC1 */
  val16  = (cpu->ios[M6811_TIC1_H] << 8) + cpu->ios[M6811_TIC1_L];
  print_io_word (sd, "TIC1 ", 0, val16, base + M6811_TIC1);
  sim_io_printf (sd, "\n");

  /* Info for TIC2 */
  val16  = (cpu->ios[M6811_TIC2_H] << 8) + cpu->ios[M6811_TIC2_L];
  print_io_word (sd, "TIC2 ", 0, val16, base + M6811_TIC2);
  sim_io_printf (sd, "\n");

  /* Info for TIC3 */
  val16  = (cpu->ios[M6811_TIC3_H] << 8) + cpu->ios[M6811_TIC3_L];
  print_io_word (sd, "TIC3 ", 0, val16, base + M6811_TIC3);
  sim_io_printf (sd, "\n");

  /* Info for TOC1 */
  val16  = (cpu->ios[M6811_TOC1_H] << 8) + cpu->ios[M6811_TOC1_L];
  print_io_word (sd, "TOC1 ", 0, val16, base + M6811_TOC1);
  sim_io_printf (sd, "\n");

  /* Info for TOC2 */
  val16  = (cpu->ios[M6811_TOC2_H] << 8) + cpu->ios[M6811_TOC2_L];
  print_io_word (sd, "TOC2 ", 0, val16, base + M6811_TOC2);
  sim_io_printf (sd, "\n");

  /* Info for TOC3 */
  val16  = (cpu->ios[M6811_TOC3_H] << 8) + cpu->ios[M6811_TOC3_L];
  print_io_word (sd, "TOC3 ", 0, val16, base + M6811_TOC3);
  sim_io_printf (sd, "\n");

  /* Info for TOC4 */
  val16  = (cpu->ios[M6811_TOC4_H] << 8) + cpu->ios[M6811_TOC4_L];
  print_io_word (sd, "TOC4 ", 0, val16, base + M6811_TOC4);
  sim_io_printf (sd, "\n");

  /* Info for TOC5 */
  val16  = (cpu->ios[M6811_TOC5_H] << 8) + cpu->ios[M6811_TOC5_L];
  print_io_word (sd, "TOC5 ", 0, val16, base + M6811_TOC5);
  sim_io_printf (sd, "\n");

  /* Info for TMSK1 */
  val  = cpu->ios[M6811_TMSK1];
  print_io_byte (sd, "TMSK1 ", tmsk1_desc, val, base + M6811_TMSK1);
  sim_io_printf (sd, "\n");

  /* Info for TFLG1 */
  val = cpu->ios[M6811_TFLG1];
  print_io_byte (sd, "TFLG1", tflg1_desc, val, base + M6811_TFLG1);
  sim_io_printf (sd, "\n");

  val  = cpu->ios[M6811_TMSK2];
  print_io_byte (sd, "TMSK2 ", tmsk2_desc, val, base + M6811_TMSK2);
  sim_io_printf (sd, "\n");

  val = cpu->ios[M6811_TFLG2];
  print_io_byte (sd, "TFLG2", tflg2_desc, val, base + M6811_TFLG2);
  sim_io_printf (sd, "\n");

  val = cpu->ios[M6811_PACTL];
  print_io_byte (sd, "PACTL", pactl_desc, val, base + M6811_PACTL);
  sim_io_printf (sd, "\n");

  val = cpu->ios[M6811_PACNT];
  print_io_byte (sd, "PACNT", 0, val, base + M6811_PACNT);
  sim_io_printf (sd, "\n");

  /* Give info about the next timer interrupts.  */
  m68hc11tim_print_timer (me, "RTI", controller->rti_timer_event);
  m68hc11tim_print_timer (me, "COP", controller->cop_timer_event);
  m68hc11tim_print_timer (me, "OVERFLOW", controller->tof_timer_event);
  m68hc11tim_print_timer (me, "COMPARE", controller->cmp_timer_event);
}
Esempio n. 13
0
static void
m68hc11tim_timer_event (struct hw *me, void *data)
{
  SIM_DESC sd;
  struct m68hc11tim *controller;
  sim_cpu *cpu;
  enum event_type type;
  unsigned long delay;
  struct hw_event **eventp;
  int check_interrupt = 0;
  unsigned mask;
  unsigned flags;
  unsigned long tcnt_internal;
  unsigned long tcnt, tcnt_prev;
  signed64 tcnt_insn_end;
  signed64 tcnt_insn_start;
  int i;
  sim_events *events;
  
  controller = hw_data (me);
  sd         = hw_system (me);
  cpu        = STATE_CPU (sd, 0);
  type       = (enum event_type) ((long) data) & 0x0FF;
  events     = STATE_EVENTS (sd);

  delay = 0;
  switch (type)
    {
    case COP_EVENT:
      eventp = &controller->cop_timer_event;
      delay  = controller->cop_delay;
      delay  = controller->cop_prev_interrupt + controller->cop_delay;
      controller->cop_prev_interrupt = delay;
      delay  = delay - cpu->cpu_absolute_cycle;
      check_interrupt = 1;
      delay += events->nr_ticks_to_process;
      break;

    case RTI_EVENT:
      eventp = &controller->rti_timer_event;
      delay  = controller->rti_prev_interrupt + controller->rti_delay;
      
      if (((long) (data) & 0x0100) == 0)
        {
          cpu->ios[M6811_TFLG2] |= M6811_RTIF;
          check_interrupt = 1;
          controller->rti_prev_interrupt = delay;
          delay += controller->rti_delay;
        }
      delay = delay - cpu->cpu_absolute_cycle;
      delay += events->nr_ticks_to_process;
      break;

    case OVERFLOW_EVENT:
      /* Compute the 68HC11 internal free running counter.  */
      tcnt_internal = (cpu->cpu_absolute_cycle - controller->tcnt_adjust);

      /* We must take into account the prescaler that comes
         before the counter (it's a power of 2).  */
      tcnt_internal &= 0x0ffff * controller->clock_prescaler;

      /* Compute the time when the overflow will occur.  It occurs when
         the counter increments from 0x0ffff to 0x10000 (and thus resets).  */
      delay = (0x10000 * controller->clock_prescaler) - tcnt_internal;

      /* The 'nr_ticks_to_process' will be subtracted when the event
         is scheduled.  */
      delay += events->nr_ticks_to_process;

      eventp = &controller->tof_timer_event;
      if (((long) (data) & 0x100) == 0)
        {
          cpu->ios[M6811_TFLG2] |= M6811_TOF;
          check_interrupt = 1;
        }
      break;

    case COMPARE_EVENT:
      /* Compute value of TCNT register (64-bit precision) at beginning
         and end of instruction.  */
      tcnt_insn_end = (cpu->cpu_absolute_cycle - controller->tcnt_adjust);
      tcnt_insn_start = (tcnt_insn_end - cpu->cpu_current_cycle);

      /* TCNT value at beginning of current instruction.  */
      tcnt_prev = (tcnt_insn_start / controller->clock_prescaler) & 0x0ffff;

      /* TCNT value at end of current instruction.  */
      tcnt = (tcnt_insn_end / controller->clock_prescaler) & 0x0ffff;

      /* We must take into account the prescaler that comes
         before the counter (it's a power of 2).  */
      tcnt_internal = tcnt_insn_end;
      tcnt_internal &= 0x0ffff * controller->clock_prescaler;

      flags = cpu->ios[M6811_TMSK1];
      mask  = 0x80;
      delay = 65536 * controller->clock_prescaler;

      /* Scan each output compare register to see if one matches
         the free running counter.  Set the corresponding OCi flag
         if the output compare is enabled.  */
      for (i = M6811_TOC1; i <= M6811_TOC5; i += 2, mask >>= 1)
        {
          unsigned long compare;

          compare = (cpu->ios[i] << 8) + cpu->ios[i + 1];

          /* See if compare is reached; handle wrap arround.  */
          if ((compare >= tcnt_prev && compare <= tcnt && tcnt_prev < tcnt)
              || (compare >= tcnt_prev && tcnt_prev > tcnt)
              || (compare < tcnt && tcnt_prev > tcnt))
            {
              unsigned dt;

              if (compare > tcnt)
                dt = 0x10000 - compare - tcnt;
              else
                dt = tcnt - compare;

              cpu->ios[M6811_TFLG1] |= mask;

              /* Raise interrupt now at the correct CPU cycle so that
                 we can find the interrupt latency.  */
              cpu->cpu_absolute_cycle -= dt;
              interrupts_update_pending (&cpu->cpu_interrupts);
              cpu->cpu_absolute_cycle += dt;
            }

          /* Compute how many times for the next match.
             Use the internal counter value to take into account the
             prescaler accurately.  */
          compare = compare * controller->clock_prescaler;
          if (compare > tcnt_internal)
            compare = compare - tcnt_internal;
          else
            compare = compare - tcnt_internal
              + 65536 * controller->clock_prescaler;

          if (compare < delay)
            delay = compare;
        }

      /* Deactivate the compare timer if no output compare is enabled.  */
      if ((flags & 0xF8) == 0)
        delay = 0;
      else
        delay += events->nr_ticks_to_process;

      eventp = &controller->cmp_timer_event;
      break;

    default:
      eventp = 0;
      break;
    }

  if (*eventp)
    {
      hw_event_queue_deschedule (me, *eventp);
      *eventp = 0;
    }

  if (delay != 0)
    {
      *eventp = hw_event_queue_schedule (me, delay,
                                         m68hc11tim_timer_event,
                                         (void*) type);
    }

  if (check_interrupt)
    interrupts_update_pending (&cpu->cpu_interrupts);
}
Esempio n. 14
0
static void
m68hc11tim_port_event (struct hw *me,
                       int my_port,
                       struct hw *source,
                       int source_port,
                       int level)
{
  SIM_DESC sd;
  struct m68hc11tim *controller;
  sim_cpu *cpu;
  unsigned8 val;
  unsigned16 tcnt;

  controller = hw_data (me);
  sd         = hw_system (me);
  cpu        = STATE_CPU (sd, 0);
  switch (my_port)
    {
    case RESET_PORT:
      {
	HW_TRACE ((me, "Timer reset"));

        /* Cancel all timer events.  */
        if (controller->rti_timer_event)
          {
            hw_event_queue_deschedule (me, controller->rti_timer_event);
            controller->rti_timer_event = 0;
            controller->rti_prev_interrupt = 0;
          }
        if (controller->cop_timer_event)
          {
            hw_event_queue_deschedule (me, controller->cop_timer_event);
            controller->cop_timer_event = 0;
            controller->cop_prev_interrupt = 0;
          }
        if (controller->tof_timer_event)
          {
            hw_event_queue_deschedule (me, controller->tof_timer_event);
            controller->tof_timer_event = 0;
          }
        if (controller->cmp_timer_event)
          {
            hw_event_queue_deschedule (me, controller->cmp_timer_event);
            controller->cmp_timer_event = 0;
          }

        /* Reset the state of Timer registers.  This also restarts
           the timer events (overflow and RTI clock).  The pending
           flags (TFLG2) must be cleared explicitly here.  */
        val = 0;
        cpu->ios[M6811_TFLG2] = 0;
        m68hc11tim_io_write_buffer (me, &val, io_map,
                                    (unsigned_word) M6811_TMSK2, 1);
        m68hc11tim_io_write_buffer (me, &val, io_map,
                                    (unsigned_word) M6811_PACTL, 1);
        break;
      }

    case CAPTURE:
      tcnt = (uint16) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
                       / controller->clock_prescaler);
      switch (level)
        {
        case M6811_TIC1:
        case M6811_TIC2:
        case M6811_TIC3:
          cpu->ios[level] = tcnt >> 8;
          cpu->ios[level + 1] = tcnt;
          break;

        default:
          hw_abort (me, "Invalid event parameter %d", level);
          break;
        }
      break;

    default:
      hw_abort (me, "Event on unknown port %d", my_port);
      break;
    }
}
Esempio n. 15
0
struct _sim_cpu *
hw_system_cpu (struct hw *me)
{
  return STATE_HW (hw_system (me))->cpu;
}
void
m68hc11sio_rx_poll (struct hw *me, void *data)
{
  SIM_DESC sd;
  struct m68hc11sio *controller;
  sim_cpu *cpu;
  char cc;
  int cnt;
  int check_interrupt = 0;
  
  controller = hw_data (me);
  sd         = hw_system (me);
  cpu        = STATE_CPU (sd, 0);
  switch (controller->backend)
    {
    case sio_tcp:
      cnt = dv_sockser_read (sd);
      if (cnt != -1)
        {
          cc = (char) cnt;
          cnt = 1;
        }
      break;

    case sio_stdio:
      cnt = sim_io_poll_read (sd, 0 /* stdin */, &cc, 1);
      break;

    default:
      cnt = 0;
      break;
    }

  if (cnt == 1)
    {
      /* Raise the overrun flag if the previous character was not read.  */
      if (cpu->ios[M6811_SCSR] & M6811_RDRF)
        cpu->ios[M6811_SCSR] |= M6811_OR;

      cpu->ios[M6811_SCSR]     |= M6811_RDRF;
      controller->rx_char       = cc;
      controller->rx_clear_scsr = 0;
      check_interrupt = 1;
    }
  else
    {
      /* handle idle line detect here.  */
      ;
    }

  if (controller->rx_poll_event)
    {
      hw_event_queue_deschedule (me, controller->rx_poll_event);
      controller->rx_poll_event = 0;
    }

  if (cpu->ios[M6811_SCCR2] & M6811_RE)
    {
      unsigned long clock_cycle;

      /* Compute CPU clock cycles to wait for the next character.  */
      clock_cycle = controller->data_length * controller->baud_cycle;

      controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
                                                           m68hc11sio_rx_poll,
                                                           NULL);
    }

  if (check_interrupt)
      interrupts_update_pending (&cpu->cpu_interrupts);
}
Esempio n. 17
0
static void
m68hc11eepr_port_event (struct hw *me,
                        int my_port,
                        struct hw *source,
                        int source_port,
                        int level)
{
  SIM_DESC sd;
  struct m68hc11eepr *controller;
  sim_cpu *cpu;
  
  controller = hw_data (me);
  sd         = hw_system (me);
  cpu        = STATE_CPU (sd, 0);
  switch (my_port)
    {
    case RESET_PORT:
      {
	HW_TRACE ((me, "EEPROM reset"));

        /* Re-read the EEPROM from the file.  This gives the chance
           to users to erase this file before doing a reset and have
           a fresh EEPROM taken into account.  */
        m6811eepr_memory_rw (controller, O_RDONLY);

        /* Reset the state of EEPROM programmer.  The CONFIG register
           is also initialized from the EEPROM/file content.  */
        cpu->ios[M6811_PPROG]    = 0;
        if (cpu->cpu_use_local_config)
          cpu->ios[M6811_CONFIG] = cpu->cpu_config;
        else
          cpu->ios[M6811_CONFIG]   = controller->eeprom[controller->size-1];
        controller->eeprom_wmode = 0;
        controller->eeprom_waddr = 0;
        controller->eeprom_wbyte = 0;

        /* Attach or detach to the bus depending on the EEPROM enable bit.
           The EEPROM CONFIG register is still enabled and can be programmed
           for a next configuration (taken into account only after a reset,
           see Motorola spec).  */
        if (!(cpu->ios[M6811_CONFIG] & M6811_EEON))
          {
            if (controller->mapped)
              hw_detach_address (hw_parent (me), M6811_EEPROM_LEVEL,
                                 controller->attach_space,
                                 controller->base_address,
                                 controller->size - 1,
                                 me);
            controller->mapped = 0;
          }
        else
          {
            if (!controller->mapped)
              hw_attach_address (hw_parent (me), M6811_EEPROM_LEVEL,
                                 controller->attach_space,
                                 controller->base_address,
                                 controller->size - 1,
                                 me);
            controller->mapped = 1;
          }
        break;
      }

    default:
      hw_abort (me, "Event on unknown port %d", my_port);
      break;
    }
}
Esempio n. 18
0
/* Deliver a clock tick to the counter. */
static void
deliver_tx3904tmr_tick (struct hw *me,
			void *data)
{
  struct tx3904tmr *controller = hw_data (me);
  SIM_DESC sd = hw_system (me);
  signed_8 this_ticks = sim_events_time(sd);

  signed_8 warp;
  signed_8 divisor;
  signed_8 quotient, remainder;

  /* compute simulation ticks between last tick and this tick */
  if(controller->last_ticks != 0)
    warp = this_ticks - controller->last_ticks + controller->roundoff_ticks;
  else
    {
      controller->last_ticks = this_ticks; /* initialize */
      warp = controller->roundoff_ticks;
    }

  if(controller->event != NULL)
    hw_event_queue_deschedule(me, controller->event);
  controller->event = NULL;

  /* Check whether the timer ticking is enabled at this moment.  This
     largely a function of the TCE bit, but is also slightly
     mode-dependent. */
  switch((int) GET_TCR_TMODE(controller))
    {
    case 0: /* interval */
      /* do not advance counter if TCE = 0 or if holding at count = CPRA */
      if(GET_TCR_TCE(controller) == 0 ||
	 controller->trr == controller->cpra)
	return;
      break;

    case 1: /* pulse generator */
      /* do not advance counter if TCE = 0 */
      if(GET_TCR_TCE(controller) == 0)
	return;
      break;

    case 2: /* watchdog */
      /* do not advance counter if TCE = 0 and WDIS = 1 */
      if(GET_TCR_TCE(controller) == 0 &&
	 GET_WTMR_WDIS(controller) == 1)
	return;
      break;

    case 3: /* disabled */
      /* regardless of TCE, do not advance counter */
      return;
    }

  /* In any of the above cases that return, a subsequent register
     write will be needed to restart the timer.  A tick event is
     scheduled by any register write, so it is more efficient not to
     reschedule dummy events here. */


  /* find appropriate divisor etc. */ 
  if(GET_TCR_CCS(controller) == 0) /* internal system clock */
    {
      /* apply internal clock divider */
      if(GET_TCR_CCDE(controller)) /* divisor circuit enabled? */
	divisor = controller->clock_ticks * (1 << (1 + GET_CCDR_CDR(controller)));
      else
	divisor = controller->clock_ticks;
    }
  else
    {
      divisor = controller->ext_ticks;
    }

  /* how many times to increase counter? */
  quotient = warp / divisor;
  remainder = warp % divisor;

  /* NOTE: If the event rescheduling code works properly, the quotient
     should never be larger than 1.  That is, we should receive events
     here at least as frequently as the simulated counter is supposed
     to decrement.  So the remainder (-> roundoff_ticks) will slowly
     accumulate, with the quotient == 0.  Once in a while, quotient
     will equal 1. */

  controller->roundoff_ticks = remainder;
  controller->last_ticks = this_ticks;
  while(quotient > 0) /* Is it time to increment counter? */
    {
      /* next 24-bit counter value */
      unsigned_4 next_trr = (controller->trr + 1) % (1 << 24);
      quotient --;
      
      switch((int) GET_TCR_TMODE(controller))
	{
	case 0: /* interval timer mode */
	  {
	    /* Current or next counter value matches CPRA value?  The
	       first case covers counter holding at maximum before
	       reset.  The second case covers normal counting
	       behavior. */
	    if(controller->trr == controller->cpra ||
	       next_trr == controller->cpra)
	      {
		/* likely hold CPRA value */
		if(controller->trr == controller->cpra)
		  next_trr = controller->cpra;

		SET_TISR_TIIS(controller);

		/* Signal an interrupt if it is enabled with TIIE,
		   and if we just arrived at CPRA.  Don't repeatedly
		   interrupt if holding due to TZCE=0 */
		if(GET_ITMR_TIIE(controller) &&
		   next_trr != controller->trr)
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

		/* Reset counter? */
		if(GET_ITMR_TZCE(controller))
		  {
		    next_trr = 0;
		  }
	      }
	  }
	break;

	case 1: /* pulse generator mode */
	  {
	    /* first trip point */
	    if(next_trr == controller->cpra)
	      {
		/* flip flip-flop & report */
		controller->ff ^= 1;
		hw_port_event(me, FF_PORT, controller->ff);
		SET_TISR_TPIAS(controller);

		/* signal interrupt */
		if(GET_PMGR_TPIAE(controller))
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

	      }
	    /* second trip point */
	    else if(next_trr == controller->cprb)
	      {
		/* flip flip-flop & report */
		controller->ff ^= 1;
		hw_port_event(me, FF_PORT, controller->ff);
		SET_TISR_TPIBS(controller);

		/* signal interrupt */
		if(GET_PMGR_TPIBE(controller))
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

		/* clear counter */
		next_trr = 0;
	      }
	  }
	break;

	case 2: /* watchdog timer mode */
	  {
	    /* watchdog timer expiry */
	    if(next_trr == controller->cpra)
	      {
		SET_TISR_TWIS(controller);

		/* signal interrupt */
		if(GET_WTMR_TWIE(controller))
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

		/* clear counter */
		next_trr = 0;
	      }
	  }
	break;

	case 3: /* disabled */
	default:
	  break;
	}

      /* update counter and report */
      controller->trr = next_trr;
      /* HW_TRACE ((me, "counter trr %ld tisr %lx",
	 (long) controller->trr, (long) controller->tisr)); */
    } /* end quotient loop */

  /* Reschedule a timer event in near future, so we can increment the
     counter again.  Set the event about 75% of divisor time away, so
     we will experience roughly 1.3 events per counter increment. */
  controller->event = hw_event_queue_schedule(me, divisor*3/4, deliver_tx3904tmr_tick, NULL);
}