ubsan: score: left shift of negative value
[deliverable/binutils-gdb.git] / sim / mn10300 / dv-mn103ser.c
index 041cf7a40b174e8f1004a4fcfc422e01e2a994f6..02d0aa54c766a3547586a9b260308fffba2bdc91 100644 (file)
@@ -1,26 +1,27 @@
 /*  This file is part of the program GDB, the GNU debugger.
     
-    Copyright (C) 1998 Free Software Foundation, Inc.
+    Copyright (C) 1998-2020 Free Software Foundation, Inc.
     Contributed by Cygnus Solutions.
     
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
+    the Free Software Foundation; either version 3 of the License, or
     (at your option) any later version.
-    
+
     This program 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 General Public License for more details.
-    
+
     You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     
     */
 
 #include "sim-main.h"
 #include "hw-main.h"
+#include "dv-sockser.h"
+
 
 /* DEVICE
 
@@ -55,25 +56,40 @@ struct mn103ser_block {
 
 enum serial_register_types {
     SC0CTR,
-    SC0ICR,
-    SC0TXB,
-    SC0RXB,
-    SC0STR,
     SC1CTR,
-    SC1ICR,
-    SC1TXB,
-    SC1RXB,
-    SC1STR,
     SC2CTR,
+    SC0ICR,
+    SC1ICR,
     SC2ICR,
+    SC0TXB,
+    SC1TXB,
     SC2TXB,
+    SC0RXB,
+    SC1RXB,
     SC2RXB,
+    SC0STR,
+    SC1STR,
     SC2STR,
+    SC2TIM,
 };
 
 
+#define NR_SERIAL_DEVS  3
+#define SIO_STAT_RRDY 0x0010
+
+typedef struct _mn10300_serial {
+  unsigned16 status, control;
+  unsigned8  txb, rxb, intmode;
+  struct hw_event *event;
+} mn10300_serial;
+
+
+
 struct mn103ser {
   struct mn103ser_block block;
+  mn10300_serial device[NR_SERIAL_DEVS];
+  unsigned8      serial2_timer_reg;
+  do_hw_poll_read_method *reader;
 };
 
 /* output port ID's */
@@ -81,10 +97,10 @@ struct mn103ser {
 /* for mn103002 */
 enum {
   SERIAL0_RECEIVE,
-  SERIAL0_SEND,
   SERIAL1_RECEIVE,
-  SERIAL1_SEND,
   SERIAL2_RECEIVE,
+  SERIAL0_SEND,
+  SERIAL1_SEND,
   SERIAL2_SEND,
 };
 
@@ -92,10 +108,10 @@ enum {
 static const struct hw_port_descriptor mn103ser_ports[] = {
 
   { "serial-0-receive",  SERIAL0_RECEIVE, 0, output_port, },
-  { "serial-0-transmit", SERIAL0_SEND, 0, output_port, },
   { "serial-1-receive",  SERIAL1_RECEIVE, 0, output_port, },
-  { "serial-1-transmit", SERIAL1_SEND, 0, output_port, },
   { "serial-2-receive",  SERIAL2_RECEIVE, 0, output_port, },
+  { "serial-0-transmit", SERIAL0_SEND, 0, output_port, },
+  { "serial-1-transmit", SERIAL1_SEND, 0, output_port, },
   { "serial-2-transmit", SERIAL2_SEND, 0, output_port, },
 
   { NULL, },
@@ -154,7 +170,27 @@ mn103ser_finish (struct hw *me)
   /* Attach ourself to our parent bus */
   attach_mn103ser_regs (me, serial);
 
+  /* If so configured, enable polled input */
+  if (hw_find_property (me, "poll?") != NULL
+      && hw_find_boolean_property (me, "poll?"))
+    {
+      serial->reader = sim_io_poll_read;
+    }
+  else
+    {
+      serial->reader = sim_io_read;
+    }
+
   /* Initialize the serial device registers. */
+  for ( i=0; i<NR_SERIAL_DEVS; ++i )
+    {
+      serial->device[i].txb = 0;
+      serial->device[i].rxb = 0;
+      serial->device[i].status = 0;
+      serial->device[i].control = 0;
+      serial->device[i].intmode = 0;
+      serial->device[i].event = NULL;
+    }
 }
 
 
@@ -167,7 +203,6 @@ decode_addr (struct hw *me,
 {
   unsigned_word offset;
   offset = address - serial->block.base;
-
   switch (offset)
     {
     case 0x00: return SC0CTR;
@@ -185,6 +220,7 @@ decode_addr (struct hw *me,
     case 0x28: return SC2TXB;
     case 0x29: return SC2RXB;
     case 0x2C: return SC2STR;
+    case 0x2D: return SC2TIM;
     default: 
       {
        hw_abort (me, "bad address");
@@ -193,53 +229,231 @@ decode_addr (struct hw *me,
     }
 }
 
+static void
+do_polling_event (struct hw *me,
+                 void *data)
+{
+  SIM_DESC sd = hw_system (me);
+  struct mn103ser *serial = hw_data(me);
+  long serial_reg = (long) data;
+  char c;
+  int count, status;
+
+  status = dv_sockser_status (sd);
+  if (!(status & DV_SOCKSER_DISCONNECTED))
+    {
+      int rd;
+      rd = dv_sockser_read (sd);
+      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 next polling event */
+  serial->device[serial_reg].event
+    = hw_event_queue_schedule (me, 1000,
+                              do_polling_event, (void *)serial_reg);
+
+}
+
 static void
 read_control_reg (struct hw *me,
                  struct mn103ser *serial,
-                 unsigned_word addr,
+                 unsigned_word serial_reg,
                  void *dest,
                  unsigned  nr_bytes)
 {
+  /* really allow 1 byte read, too */
+  if ( nr_bytes == 2 )
+    {
+      *(unsigned16 *)dest = H2LE_2 (serial->device[serial_reg].control);
+    }
+  else
+    {
+      hw_abort (me, "bad read size of %d bytes from SC%dCTR.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
 read_intmode_reg (struct hw *me,
                  struct mn103ser *serial,
-                 unsigned_word addr,
+                 unsigned_word serial_reg,
                  void *dest,
                  unsigned  nr_bytes)
 {
+  if ( nr_bytes == 1 )
+    {
+      *(unsigned8 *)dest = serial->device[serial_reg].intmode;
+    }
+  else
+    {
+      hw_abort (me, "bad read size of %d bytes from SC%dICR.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
 read_txb (struct hw *me,
          struct mn103ser *serial,
-         unsigned_word addr,
+         unsigned_word serial_reg,
          void *dest,
          unsigned  nr_bytes)
 {
+  if ( nr_bytes == 1 )
+    {
+      *(unsigned8 *)dest = serial->device[serial_reg].txb;
+    }
+  else
+    {
+      hw_abort (me, "bad read size of %d bytes from SC%dTXB.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
 read_rxb (struct hw *me,
          struct mn103ser *serial,
-         unsigned_word addr,
+         unsigned_word serial_reg,
          void *dest,
          unsigned  nr_bytes)
 {
+  if ( nr_bytes == 1 )
+    {
+      *(unsigned8 *)dest = serial->device[serial_reg].rxb;
+      /* Reception buffer is now empty. */
+      serial->device[serial_reg].status &= ~SIO_STAT_RRDY;
+    }
+  else
+    {
+      hw_abort (me, "bad read size of %d bytes from SC%dRXB.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
 read_status_reg (struct hw *me,
                 struct mn103ser *serial,
-                unsigned_word addr,
+                unsigned_word serial_reg,
                 void *dest,
                 unsigned  nr_bytes)
 {
+  char c;
+  int count;
+
+  if ( (serial->device[serial_reg].status & SIO_STAT_RRDY) == 0 )
+    {
+      SIM_DESC sd = hw_system (me);
+      int status;
+
+      /* 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;
+       }
+
+      status = dv_sockser_status (sd);
+      if (!(status & DV_SOCKSER_DISCONNECTED))
+       {
+         int rd;
+         rd = dv_sockser_read (sd);
+         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
+read_serial2_timer_reg (struct hw *me,
+                       struct mn103ser *serial,
+                       void *dest,
+                       unsigned  nr_bytes)
+{
+  if ( nr_bytes == 1 )
+    {
+      * (unsigned8 *) dest = (unsigned8) serial->serial2_timer_reg;
+    }
+  else
+    {
+      hw_abort (me, "bad read size of %d bytes to SC2TIM.", nr_bytes);
+    }
 }
 
 
@@ -261,35 +475,50 @@ mn103ser_io_read_buffer (struct hw *me,
     case SC0CTR:
     case SC1CTR:
     case SC2CTR:
-      read_control_reg(me, serial, base, dest, nr_bytes);
+      read_control_reg(me, serial, serial_reg-SC0CTR, dest, nr_bytes);
+      HW_TRACE ((me, "read - ctrl reg%d has 0x%x\n", serial_reg-SC0CTR,
+                *(unsigned8 *)dest));
       break;
 
     /* interrupt mode registers */
     case SC0ICR:
     case SC1ICR:
     case SC2ICR:
-      read_intmode_reg(me, serial, base, dest, nr_bytes);
+      read_intmode_reg(me, serial, serial_reg-SC0ICR, dest, nr_bytes);
+      HW_TRACE ((me, "read - intmode reg%d has 0x%x\n", serial_reg-SC0ICR,
+                *(unsigned8 *)dest));
       break;
 
     /* transmission buffers */
     case SC0TXB:
     case SC1TXB:
     case SC2TXB:
-      read_txb(me, serial, base, dest, nr_bytes);
+      read_txb(me, serial, serial_reg-SC0TXB, dest, nr_bytes);
+      HW_TRACE ((me, "read - txb%d has %c\n", serial_reg-SC0TXB,
+                *(char *)dest));
       break;
 
     /* reception buffers */
     case SC0RXB: 
     case SC1RXB:
     case SC2RXB:
-      read_rxb(me, serial, base, dest, nr_bytes);
+      read_rxb(me, serial, serial_reg-SC0RXB, dest, nr_bytes);
+      HW_TRACE ((me, "read - rxb%d has %c\n", serial_reg-SC0RXB,
+                *(char *)dest));
      break;
 
     /* status registers */
     case SC0STR: 
     case SC1STR: 
     case SC2STR: 
-      read_status_reg(me, serial, base, dest, nr_bytes);
+      read_status_reg(me, serial, serial_reg-SC0STR, dest, nr_bytes);
+      HW_TRACE ((me, "read - status reg%d has 0x%x\n", serial_reg-SC0STR,
+                *(unsigned8 *)dest));
+      break;
+
+    case SC2TIM:
+      read_serial2_timer_reg(me, serial, dest, nr_bytes);
+      HW_TRACE ((me, "read - serial2 timer reg %d\n", *(unsigned8 *)dest));
       break;
 
     default:
@@ -303,50 +532,112 @@ mn103ser_io_read_buffer (struct hw *me,
 static void
 write_control_reg (struct hw *me,
                   struct mn103ser *serial,
-                  unsigned_word addr,
+                  unsigned_word serial_reg,
                   const void *source,
                   unsigned  nr_bytes)
 {
+  unsigned16 val = LE2H_2 (*(unsigned16 *)source);
+
+  /* really allow 1 byte write, too */
+  if ( nr_bytes == 2 )
+    {
+      if ( serial_reg == 2 && (val & 0x0C04) != 0 )
+       {
+         hw_abort(me, "Cannot write to read-only bits of SC2CTR.");
+       }
+      else
+       {
+         serial->device[serial_reg].control = val;
+       }
+    }
+  else
+    {
+      hw_abort (me, "bad read size of %d bytes from SC%dSTR.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
 write_intmode_reg (struct hw *me,
                   struct mn103ser *serial,
-                  unsigned_word addr,
+                  unsigned_word serial_reg,
                   const void *source,
                   unsigned  nr_bytes)
 {
-}
-
+unsigned8 val = *(unsigned8 *)source;
 
-static void
-write_txb (struct hw *me,
-          struct mn103ser *serial,
-          unsigned_word addr,
-          const void *source,
-          unsigned  nr_bytes)
-{
+  if ( nr_bytes == 1 )
+    {
+      /* Check for attempt to write to read-only bits of register. */
+      if ( ( serial_reg == 2 && (val & 0xCA) != 0 )
+          || ( serial_reg != 2 && (val & 0x4A) != 0 ) )
+       {
+         hw_abort(me, "Cannot write to read-only bits of SC%dICR.",
+                  serial_reg);
+       }
+      else
+       {
+         serial->device[serial_reg].intmode = val;
+       }
+    }
+  else
+    {
+      hw_abort (me, "bad write size of %d bytes to SC%dICR.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
-write_rxb (struct hw *me,
+write_txb (struct hw *me,
           struct mn103ser *serial,
-          unsigned_word addr,
+          unsigned_word serial_reg,
           const void *source,
           unsigned  nr_bytes)
 {
+  if ( nr_bytes == 1 )
+    {
+      SIM_DESC sd = hw_system (me);
+      int status;
+
+      serial->device[serial_reg].txb = *(unsigned8 *)source;
+
+      status = dv_sockser_status (sd);
+      if (!(status & DV_SOCKSER_DISCONNECTED))
+       {
+         dv_sockser_write(sd, * (char*) source);
+       }
+      else
+       {
+         sim_io_write_stdout(sd, (char *)source, 1);
+         sim_io_flush_stdout(sd);
+       }
+
+      hw_port_event (me, serial_reg+SERIAL0_SEND, 1);
+    }
+  else
+    {
+      hw_abort (me, "bad write size of %d bytes to SC%dTXB.", nr_bytes, 
+               serial_reg);
+    }
 }
 
 
 static void
-write_status_reg (struct hw *me,
-                 struct mn103ser *serial,
-                 unsigned_word addr,
-                 const void *source,
-                 unsigned  nr_bytes)
+write_serial2_timer_reg (struct hw *me,
+                        struct mn103ser *serial,
+                        const void *source,
+                        unsigned  nr_bytes)
 {
+  if ( nr_bytes == 1 )
+    {
+      serial->serial2_timer_reg = *(unsigned8 *)source;
+    }
+  else
+    {
+      hw_abort (me, "bad write size of %d bytes to SC2TIM.", nr_bytes); 
+    }
 }
 
 
@@ -368,35 +659,47 @@ mn103ser_io_write_buffer (struct hw *me,
     case SC0CTR:
     case SC1CTR:
     case SC2CTR:
-      write_control_reg(me, serial, base, source, nr_bytes);
+      HW_TRACE ((me, "write - ctrl reg%d has 0x%x, nrbytes=%d.\n",
+                serial_reg-SC0CTR, *(unsigned8 *)source, nr_bytes));
+      write_control_reg(me, serial, serial_reg-SC0CTR, source, nr_bytes);
       break;
 
     /* interrupt mode registers */
     case SC0ICR:
     case SC1ICR:
     case SC2ICR:
-      write_intmode_reg(me, serial, base, source, nr_bytes);
+      HW_TRACE ((me, "write - intmode reg%d has 0x%x, nrbytes=%d.\n",
+                serial_reg-SC0ICR, *(unsigned8 *)source, nr_bytes));
+      write_intmode_reg(me, serial, serial_reg-SC0ICR, source, nr_bytes);
       break;
 
     /* transmission buffers */
     case SC0TXB:
     case SC1TXB:
     case SC2TXB:
-      write_txb(me, serial, base, source, nr_bytes);
+      HW_TRACE ((me, "write - txb%d has %c, nrbytes=%d.\n",
+                serial_reg-SC0TXB, *(char *)source, nr_bytes));
+      write_txb(me, serial, serial_reg-SC0TXB, source, nr_bytes);
       break;
 
     /* reception buffers */
     case SC0RXB: 
     case SC1RXB:
     case SC2RXB:
-      write_rxb(me, serial, base, source, nr_bytes);
+      hw_abort(me, "Cannot write to reception buffer.");
      break;
 
     /* status registers */
     case SC0STR: 
     case SC1STR: 
     case SC2STR: 
-      write_status_reg(me, serial, base, source, nr_bytes);
+      hw_abort(me, "Cannot write to status register.");
+      break;
+
+    case SC2TIM:
+      HW_TRACE ((me, "read - serial2 timer reg %d (nrbytes=%d)\n",
+                *(unsigned8 *)source, nr_bytes));
+      write_serial2_timer_reg(me, serial, source, nr_bytes);
       break;
 
     default:
This page took 0.032878 seconds and 4 git commands to generate.