xref: /netbsd-src/external/gpl3/gdb/dist/sim/m68hc11/dv-m68hc11sio.c (revision 5ba1f45f2a09259cc846f20c7c5501604d633c90)
14e98e3e1Schristos /*  dv-m68hc11sio.c -- Simulation of the 68HC11 serial device.
2*5ba1f45fSchristos     Copyright (C) 1999-2024 Free Software Foundation, Inc.
34e98e3e1Schristos     Written by Stephane Carrez (stcarrez@worldnet.fr)
44e98e3e1Schristos     (From a driver model Contributed by Cygnus Solutions.)
54e98e3e1Schristos 
64e98e3e1Schristos     This file is part of the program GDB, the GNU debugger.
74e98e3e1Schristos 
84e98e3e1Schristos     This program is free software; you can redistribute it and/or modify
94e98e3e1Schristos     it under the terms of the GNU General Public License as published by
104e98e3e1Schristos     the Free Software Foundation; either version 3 of the License, or
114e98e3e1Schristos     (at your option) any later version.
124e98e3e1Schristos 
134e98e3e1Schristos     This program is distributed in the hope that it will be useful,
144e98e3e1Schristos     but WITHOUT ANY WARRANTY; without even the implied warranty of
154e98e3e1Schristos     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
164e98e3e1Schristos     GNU General Public License for more details.
174e98e3e1Schristos 
184e98e3e1Schristos     You should have received a copy of the GNU General Public License
194e98e3e1Schristos     along with this program.  If not, see <http://www.gnu.org/licenses/>.
204e98e3e1Schristos 
214e98e3e1Schristos     */
224e98e3e1Schristos 
234b169a6bSchristos /* This must come before any other includes.  */
244b169a6bSchristos #include "defs.h"
254e98e3e1Schristos 
264e98e3e1Schristos #include "sim-main.h"
274e98e3e1Schristos #include "hw-main.h"
284e98e3e1Schristos #include "dv-sockser.h"
294e98e3e1Schristos #include "sim-assert.h"
304e98e3e1Schristos 
31*5ba1f45fSchristos #include "m68hc11-sim.h"
324e98e3e1Schristos 
334e98e3e1Schristos /* DEVICE
344e98e3e1Schristos 
354e98e3e1Schristos         m68hc11sio - m68hc11 serial I/O
364e98e3e1Schristos 
374e98e3e1Schristos 
384e98e3e1Schristos    DESCRIPTION
394e98e3e1Schristos 
404e98e3e1Schristos         Implements the m68hc11 serial I/O controller described in the m68hc11
414e98e3e1Schristos         user guide. The serial I/O controller is directly connected to the CPU
424e98e3e1Schristos         interrupt. The simulator implements:
434e98e3e1Schristos 
444e98e3e1Schristos             - baud rate emulation
454e98e3e1Schristos             - 8-bits transfers
464e98e3e1Schristos 
474e98e3e1Schristos    PROPERTIES
484e98e3e1Schristos 
494e98e3e1Schristos    backend {tcp | stdio}
504e98e3e1Schristos 
514e98e3e1Schristos         Use dv-sockser TCP-port backend or stdio for backend.  Default: stdio.
524e98e3e1Schristos 
534e98e3e1Schristos 
544e98e3e1Schristos    PORTS
554e98e3e1Schristos 
564e98e3e1Schristos    reset (input)
574e98e3e1Schristos 
584e98e3e1Schristos         Reset port. This port is only used to simulate a reset of the serial
594e98e3e1Schristos         I/O controller. It should be connected to the RESET output of the cpu.
604e98e3e1Schristos 
614e98e3e1Schristos    */
624e98e3e1Schristos 
634e98e3e1Schristos 
644e98e3e1Schristos 
654e98e3e1Schristos /* port ID's */
664e98e3e1Schristos 
674e98e3e1Schristos enum
684e98e3e1Schristos {
694e98e3e1Schristos   RESET_PORT
704e98e3e1Schristos };
714e98e3e1Schristos 
724e98e3e1Schristos 
734e98e3e1Schristos static const struct hw_port_descriptor m68hc11sio_ports[] =
744e98e3e1Schristos {
754e98e3e1Schristos   { "reset", RESET_PORT, 0, input_port, },
764e98e3e1Schristos   { NULL, },
774e98e3e1Schristos };
784e98e3e1Schristos 
794e98e3e1Schristos 
804e98e3e1Schristos /* Serial Controller information.  */
814e98e3e1Schristos struct m68hc11sio
824e98e3e1Schristos {
834e98e3e1Schristos   enum {sio_tcp, sio_stdio} backend; /* backend */
844e98e3e1Schristos 
854e98e3e1Schristos   /* Number of cpu cycles to send a bit on the wire.  */
864e98e3e1Schristos   unsigned long baud_cycle;
874e98e3e1Schristos 
884e98e3e1Schristos   /* Length in bits of characters sent, this includes the
894e98e3e1Schristos      start/stop and parity bits.  Together with baud_cycle, this
904e98e3e1Schristos      is used to find the number of cpu cycles to send/receive a data.  */
914e98e3e1Schristos   unsigned int  data_length;
924e98e3e1Schristos 
934e98e3e1Schristos   /* Information about next character to be transmited.  */
944e98e3e1Schristos   unsigned char tx_has_char;
954e98e3e1Schristos   unsigned char tx_char;
964e98e3e1Schristos 
974e98e3e1Schristos   unsigned char rx_char;
984e98e3e1Schristos   unsigned char rx_clear_scsr;
994e98e3e1Schristos 
1004e98e3e1Schristos   /* Periodic I/O polling.  */
1014e98e3e1Schristos   struct hw_event* tx_poll_event;
1024e98e3e1Schristos   struct hw_event* rx_poll_event;
1034e98e3e1Schristos };
1044e98e3e1Schristos 
1054e98e3e1Schristos 
1064e98e3e1Schristos 
1074e98e3e1Schristos /* Finish off the partially created hw device.  Attach our local
1084e98e3e1Schristos    callbacks.  Wire up our port names etc.  */
1094e98e3e1Schristos 
1104e98e3e1Schristos static hw_io_read_buffer_method m68hc11sio_io_read_buffer;
1114e98e3e1Schristos static hw_io_write_buffer_method m68hc11sio_io_write_buffer;
1124e98e3e1Schristos static hw_port_event_method m68hc11sio_port_event;
1134e98e3e1Schristos static hw_ioctl_method m68hc11sio_ioctl;
1144e98e3e1Schristos 
1154e98e3e1Schristos #define M6811_SCI_FIRST_REG (M6811_BAUD)
1164e98e3e1Schristos #define M6811_SCI_LAST_REG  (M6811_SCDR)
1174e98e3e1Schristos 
1184e98e3e1Schristos 
1194e98e3e1Schristos static void
1204e98e3e1Schristos attach_m68hc11sio_regs (struct hw *me,
1214e98e3e1Schristos                         struct m68hc11sio *controller)
1224e98e3e1Schristos {
1234e98e3e1Schristos   hw_attach_address (hw_parent (me), M6811_IO_LEVEL, io_map,
1244e98e3e1Schristos                      M6811_SCI_FIRST_REG,
1254e98e3e1Schristos                      M6811_SCI_LAST_REG - M6811_SCI_FIRST_REG + 1,
1264e98e3e1Schristos 		     me);
1274e98e3e1Schristos 
1284e98e3e1Schristos   if (hw_find_property(me, "backend") != NULL)
1294e98e3e1Schristos     {
1304e98e3e1Schristos       const char *value = hw_find_string_property(me, "backend");
1314e98e3e1Schristos       if(! strcmp(value, "tcp"))
1324e98e3e1Schristos 	controller->backend = sio_tcp;
1334e98e3e1Schristos       else if(! strcmp(value, "stdio"))
1344e98e3e1Schristos 	controller->backend = sio_stdio;
1354e98e3e1Schristos       else
1364e98e3e1Schristos 	hw_abort (me, "illegal value for backend parameter `%s':"
1374e98e3e1Schristos                   "use tcp or stdio", value);
1384e98e3e1Schristos     }
1394e98e3e1Schristos }
1404e98e3e1Schristos 
1414e98e3e1Schristos 
1424e98e3e1Schristos static void
1434e98e3e1Schristos m68hc11sio_finish (struct hw *me)
1444e98e3e1Schristos {
1454e98e3e1Schristos   struct m68hc11sio *controller;
1464e98e3e1Schristos 
1474e98e3e1Schristos   controller = HW_ZALLOC (me, struct m68hc11sio);
1484e98e3e1Schristos   set_hw_data (me, controller);
1494e98e3e1Schristos   set_hw_io_read_buffer (me, m68hc11sio_io_read_buffer);
1504e98e3e1Schristos   set_hw_io_write_buffer (me, m68hc11sio_io_write_buffer);
1514e98e3e1Schristos   set_hw_ports (me, m68hc11sio_ports);
1524e98e3e1Schristos   set_hw_port_event (me, m68hc11sio_port_event);
1534e98e3e1Schristos #ifdef set_hw_ioctl
1544e98e3e1Schristos   set_hw_ioctl (me, m68hc11sio_ioctl);
1554e98e3e1Schristos #else
1564e98e3e1Schristos   me->to_ioctl = m68hc11sio_ioctl;
1574e98e3e1Schristos #endif
1584e98e3e1Schristos 
1594e98e3e1Schristos   /* Preset defaults.  */
1604e98e3e1Schristos   controller->backend = sio_stdio;
1614e98e3e1Schristos 
1624e98e3e1Schristos   /* Attach ourself to our parent bus.  */
1634e98e3e1Schristos   attach_m68hc11sio_regs (me, controller);
1644e98e3e1Schristos 
1654e98e3e1Schristos   /* Initialize to reset state.  */
1664e98e3e1Schristos   controller->tx_poll_event = NULL;
1674e98e3e1Schristos   controller->rx_poll_event = NULL;
1684e98e3e1Schristos   controller->tx_char       = 0;
1694e98e3e1Schristos   controller->tx_has_char   = 0;
1704e98e3e1Schristos   controller->rx_clear_scsr = 0;
1714e98e3e1Schristos   controller->rx_char       = 0;
1724e98e3e1Schristos }
1734e98e3e1Schristos 
1744e98e3e1Schristos 
1754e98e3e1Schristos 
1764e98e3e1Schristos /* An event arrives on an interrupt port.  */
1774e98e3e1Schristos 
1784e98e3e1Schristos static void
1794e98e3e1Schristos m68hc11sio_port_event (struct hw *me,
1804e98e3e1Schristos                        int my_port,
1814e98e3e1Schristos                        struct hw *source,
1824e98e3e1Schristos                        int source_port,
1834e98e3e1Schristos                        int level)
1844e98e3e1Schristos {
1854e98e3e1Schristos   SIM_DESC sd;
1864e98e3e1Schristos   struct m68hc11sio *controller;
1874e98e3e1Schristos   sim_cpu *cpu;
188*5ba1f45fSchristos   struct m68hc11_sim_cpu *m68hc11_cpu;
1894b169a6bSchristos   uint8_t val;
1904e98e3e1Schristos 
1914e98e3e1Schristos   controller = hw_data (me);
1924e98e3e1Schristos   sd         = hw_system (me);
1934e98e3e1Schristos   cpu        = STATE_CPU (sd, 0);
194*5ba1f45fSchristos   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
1954e98e3e1Schristos   switch (my_port)
1964e98e3e1Schristos     {
1974e98e3e1Schristos     case RESET_PORT:
1984e98e3e1Schristos       {
1994e98e3e1Schristos 	HW_TRACE ((me, "SCI reset"));
2004e98e3e1Schristos 
2014e98e3e1Schristos         /* Reset the state of SCI registers.  */
2024e98e3e1Schristos         val = 0;
2034e98e3e1Schristos         m68hc11sio_io_write_buffer (me, &val, io_map,
2044e98e3e1Schristos                                     (unsigned_word) M6811_BAUD, 1);
2054e98e3e1Schristos         m68hc11sio_io_write_buffer (me, &val, io_map,
2064e98e3e1Schristos                                     (unsigned_word) M6811_SCCR1, 1);
2074e98e3e1Schristos         m68hc11sio_io_write_buffer (me, &val, io_map,
2084e98e3e1Schristos                                     (unsigned_word) M6811_SCCR2, 1);
2094e98e3e1Schristos 
210*5ba1f45fSchristos         m68hc11_cpu->ios[M6811_SCSR]    = M6811_TC | M6811_TDRE;
2114e98e3e1Schristos         controller->rx_char     = 0;
2124e98e3e1Schristos         controller->tx_char     = 0;
2134e98e3e1Schristos         controller->tx_has_char = 0;
2144e98e3e1Schristos         controller->rx_clear_scsr = 0;
2154e98e3e1Schristos         if (controller->rx_poll_event)
2164e98e3e1Schristos           {
2174e98e3e1Schristos             hw_event_queue_deschedule (me, controller->rx_poll_event);
2184e98e3e1Schristos             controller->rx_poll_event = 0;
2194e98e3e1Schristos           }
2204e98e3e1Schristos         if (controller->tx_poll_event)
2214e98e3e1Schristos           {
2224e98e3e1Schristos             hw_event_queue_deschedule (me, controller->tx_poll_event);
2234e98e3e1Schristos             controller->tx_poll_event = 0;
2244e98e3e1Schristos           }
2254e98e3e1Schristos 
2264e98e3e1Schristos         /* In bootstrap mode, initialize the SCI to 1200 bauds to
2274e98e3e1Schristos            simulate some initial setup by the internal rom.  */
228*5ba1f45fSchristos         if (((m68hc11_cpu->ios[M6811_HPRIO]) & (M6811_SMOD | M6811_MDA)) == M6811_SMOD)
2294e98e3e1Schristos           {
230*5ba1f45fSchristos             val = 0x33;
2314e98e3e1Schristos 
2324e98e3e1Schristos             m68hc11sio_io_write_buffer (me, &val, io_map,
2334e98e3e1Schristos                                         (unsigned_word) M6811_BAUD, 1);
2344e98e3e1Schristos             val = 0x12;
2354e98e3e1Schristos             m68hc11sio_io_write_buffer (me, &val, io_map,
2364e98e3e1Schristos                                         (unsigned_word) M6811_SCCR2, 1);
2374e98e3e1Schristos           }
2384e98e3e1Schristos         break;
2394e98e3e1Schristos       }
2404e98e3e1Schristos 
2414e98e3e1Schristos     default:
2424e98e3e1Schristos       hw_abort (me, "Event on unknown port %d", my_port);
2434e98e3e1Schristos       break;
2444e98e3e1Schristos     }
2454e98e3e1Schristos }
2464e98e3e1Schristos 
2474e98e3e1Schristos 
248796c32c9Schristos static void
2494e98e3e1Schristos m68hc11sio_rx_poll (struct hw *me, void *data)
2504e98e3e1Schristos {
2514e98e3e1Schristos   SIM_DESC sd;
2524e98e3e1Schristos   struct m68hc11sio *controller;
2534e98e3e1Schristos   sim_cpu *cpu;
254*5ba1f45fSchristos   struct m68hc11_sim_cpu *m68hc11_cpu;
2554e98e3e1Schristos   char cc;
2564e98e3e1Schristos   int cnt;
2574e98e3e1Schristos   int check_interrupt = 0;
2584e98e3e1Schristos 
2594e98e3e1Schristos   controller = hw_data (me);
2604e98e3e1Schristos   sd         = hw_system (me);
2614e98e3e1Schristos   cpu        = STATE_CPU (sd, 0);
262*5ba1f45fSchristos   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
2634e98e3e1Schristos   switch (controller->backend)
2644e98e3e1Schristos     {
2654e98e3e1Schristos     case sio_tcp:
2664e98e3e1Schristos       cnt = dv_sockser_read (sd);
2674e98e3e1Schristos       if (cnt != -1)
2684e98e3e1Schristos         {
2694e98e3e1Schristos           cc = (char) cnt;
2704e98e3e1Schristos           cnt = 1;
2714e98e3e1Schristos         }
2724e98e3e1Schristos       break;
2734e98e3e1Schristos 
2744e98e3e1Schristos     case sio_stdio:
2754e98e3e1Schristos       cnt = sim_io_poll_read (sd, 0 /* stdin */, &cc, 1);
2764e98e3e1Schristos       break;
2774e98e3e1Schristos 
2784e98e3e1Schristos     default:
2794e98e3e1Schristos       cnt = 0;
2804e98e3e1Schristos       break;
2814e98e3e1Schristos     }
2824e98e3e1Schristos 
2834e98e3e1Schristos   if (cnt == 1)
2844e98e3e1Schristos     {
2854e98e3e1Schristos       /* Raise the overrun flag if the previous character was not read.  */
286*5ba1f45fSchristos       if (m68hc11_cpu->ios[M6811_SCSR] & M6811_RDRF)
287*5ba1f45fSchristos         m68hc11_cpu->ios[M6811_SCSR] |= M6811_OR;
2884e98e3e1Schristos 
289*5ba1f45fSchristos       m68hc11_cpu->ios[M6811_SCSR]     |= M6811_RDRF;
2904e98e3e1Schristos       controller->rx_char       = cc;
2914e98e3e1Schristos       controller->rx_clear_scsr = 0;
2924e98e3e1Schristos       check_interrupt = 1;
2934e98e3e1Schristos     }
2944e98e3e1Schristos   else
2954e98e3e1Schristos     {
2964e98e3e1Schristos       /* handle idle line detect here.  */
2974e98e3e1Schristos       ;
2984e98e3e1Schristos     }
2994e98e3e1Schristos 
3004e98e3e1Schristos   if (controller->rx_poll_event)
3014e98e3e1Schristos     {
3024e98e3e1Schristos       hw_event_queue_deschedule (me, controller->rx_poll_event);
3034e98e3e1Schristos       controller->rx_poll_event = 0;
3044e98e3e1Schristos     }
3054e98e3e1Schristos 
306*5ba1f45fSchristos   if (m68hc11_cpu->ios[M6811_SCCR2] & M6811_RE)
3074e98e3e1Schristos     {
3084e98e3e1Schristos       unsigned long clock_cycle;
3094e98e3e1Schristos 
3104e98e3e1Schristos       /* Compute CPU clock cycles to wait for the next character.  */
3114e98e3e1Schristos       clock_cycle = controller->data_length * controller->baud_cycle;
3124e98e3e1Schristos 
3134e98e3e1Schristos       controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
3144e98e3e1Schristos                                                            m68hc11sio_rx_poll,
3154e98e3e1Schristos                                                            NULL);
3164e98e3e1Schristos     }
3174e98e3e1Schristos 
3184e98e3e1Schristos   if (check_interrupt)
319*5ba1f45fSchristos       interrupts_update_pending (&m68hc11_cpu->cpu_interrupts);
3204e98e3e1Schristos }
3214e98e3e1Schristos 
3224e98e3e1Schristos 
323796c32c9Schristos static void
3244e98e3e1Schristos m68hc11sio_tx_poll (struct hw *me, void *data)
3254e98e3e1Schristos {
3264e98e3e1Schristos   SIM_DESC sd;
3274e98e3e1Schristos   struct m68hc11sio *controller;
3284e98e3e1Schristos   sim_cpu *cpu;
329*5ba1f45fSchristos   struct m68hc11_sim_cpu *m68hc11_cpu;
3304e98e3e1Schristos 
3314e98e3e1Schristos   controller = hw_data (me);
3324e98e3e1Schristos   sd         = hw_system (me);
3334e98e3e1Schristos   cpu        = STATE_CPU (sd, 0);
334*5ba1f45fSchristos   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
3354e98e3e1Schristos 
336*5ba1f45fSchristos   m68hc11_cpu->ios[M6811_SCSR] |= M6811_TDRE;
337*5ba1f45fSchristos   m68hc11_cpu->ios[M6811_SCSR] |= M6811_TC;
3384e98e3e1Schristos 
3394e98e3e1Schristos   /* Transmitter is enabled and we have something to send.  */
340*5ba1f45fSchristos   if ((m68hc11_cpu->ios[M6811_SCCR2] & M6811_TE) && controller->tx_has_char)
3414e98e3e1Schristos     {
342*5ba1f45fSchristos       m68hc11_cpu->ios[M6811_SCSR] &= ~M6811_TDRE;
343*5ba1f45fSchristos       m68hc11_cpu->ios[M6811_SCSR] &= ~M6811_TC;
3444e98e3e1Schristos       controller->tx_has_char = 0;
3454e98e3e1Schristos       switch (controller->backend)
3464e98e3e1Schristos         {
3474e98e3e1Schristos         case sio_tcp:
3484e98e3e1Schristos           dv_sockser_write (sd, controller->tx_char);
3494e98e3e1Schristos           break;
3504e98e3e1Schristos 
3514e98e3e1Schristos         case sio_stdio:
3524b169a6bSchristos           sim_io_write_stdout (sd, (const char *)&controller->tx_char, 1);
3534e98e3e1Schristos           sim_io_flush_stdout (sd);
3544e98e3e1Schristos           break;
3554e98e3e1Schristos 
3564e98e3e1Schristos         default:
3574e98e3e1Schristos           break;
3584e98e3e1Schristos         }
3594e98e3e1Schristos     }
3604e98e3e1Schristos 
3614e98e3e1Schristos   if (controller->tx_poll_event)
3624e98e3e1Schristos     {
3634e98e3e1Schristos       hw_event_queue_deschedule (me, controller->tx_poll_event);
3644e98e3e1Schristos       controller->tx_poll_event = 0;
3654e98e3e1Schristos     }
3664e98e3e1Schristos 
367*5ba1f45fSchristos   if ((m68hc11_cpu->ios[M6811_SCCR2] & M6811_TE)
368*5ba1f45fSchristos       && ((m68hc11_cpu->ios[M6811_SCSR] & M6811_TC) == 0))
3694e98e3e1Schristos     {
3704e98e3e1Schristos       unsigned long clock_cycle;
3714e98e3e1Schristos 
3724e98e3e1Schristos       /* Compute CPU clock cycles to wait for the next character.  */
3734e98e3e1Schristos       clock_cycle = controller->data_length * controller->baud_cycle;
3744e98e3e1Schristos 
3754e98e3e1Schristos       controller->tx_poll_event = hw_event_queue_schedule (me, clock_cycle,
3764e98e3e1Schristos                                                            m68hc11sio_tx_poll,
3774e98e3e1Schristos                                                            NULL);
3784e98e3e1Schristos     }
3794e98e3e1Schristos 
380*5ba1f45fSchristos   interrupts_update_pending (&m68hc11_cpu->cpu_interrupts);
3814e98e3e1Schristos }
3824e98e3e1Schristos 
3834e98e3e1Schristos /* Descriptions of the SIO I/O ports.  These descriptions are only used to
3844e98e3e1Schristos    give information of the SIO device under GDB.  */
3854e98e3e1Schristos io_reg_desc sccr2_desc[] = {
3864e98e3e1Schristos   { M6811_TIE,   "TIE  ", "Transmit Interrupt Enable" },
3874e98e3e1Schristos   { M6811_TCIE,  "TCIE ", "Transmit Complete Interrupt Enable" },
3884e98e3e1Schristos   { M6811_RIE,   "RIE  ", "Receive Interrupt Enable" },
3894e98e3e1Schristos   { M6811_ILIE,  "ILIE ", "Idle Line Interrupt Enable" },
3904e98e3e1Schristos   { M6811_TE,    "TE   ", "Transmit Enable" },
3914e98e3e1Schristos   { M6811_RE,    "RE   ", "Receive Enable" },
3924e98e3e1Schristos   { M6811_RWU,   "RWU  ", "Receiver Wake Up" },
3934e98e3e1Schristos   { M6811_SBK,   "SBRK ", "Send Break" },
3944e98e3e1Schristos   { 0,  0, 0 }
3954e98e3e1Schristos };
3964e98e3e1Schristos 
3974e98e3e1Schristos io_reg_desc sccr1_desc[] = {
3984e98e3e1Schristos   { M6811_R8,    "R8   ", "Receive Data bit 8" },
3994e98e3e1Schristos   { M6811_T8,    "T8   ", "Transmit Data bit 8" },
4004e98e3e1Schristos   { M6811_M,     "M    ", "SCI Character length (0=8-bits, 1=9-bits)" },
4014e98e3e1Schristos   { M6811_WAKE,  "WAKE ", "Wake up method select (0=idle, 1=addr mark" },
4024e98e3e1Schristos   { 0,  0, 0 }
4034e98e3e1Schristos };
4044e98e3e1Schristos 
4054e98e3e1Schristos io_reg_desc scsr_desc[] = {
4064e98e3e1Schristos   { M6811_TDRE,  "TDRE ", "Transmit Data Register Empty" },
4074e98e3e1Schristos   { M6811_TC,    "TC   ", "Transmit Complete" },
4084e98e3e1Schristos   { M6811_RDRF,  "RDRF ", "Receive Data Register Full" },
4094e98e3e1Schristos   { M6811_IDLE,  "IDLE ", "Idle Line Detect" },
4104e98e3e1Schristos   { M6811_OR,    "OR   ", "Overrun Error" },
4114e98e3e1Schristos   { M6811_NF,    "NF   ", "Noise Flag" },
4124e98e3e1Schristos   { M6811_FE,    "FE   ", "Framing Error" },
4134e98e3e1Schristos   { 0,  0, 0 }
4144e98e3e1Schristos };
4154e98e3e1Schristos 
4164e98e3e1Schristos io_reg_desc baud_desc[] = {
4174e98e3e1Schristos   { M6811_TCLR,  "TCLR ", "Clear baud rate (test mode)" },
4184e98e3e1Schristos   { M6811_SCP1,  "SCP1 ", "SCI baud rate prescaler select (SCP1)" },
4194e98e3e1Schristos   { M6811_SCP0,  "SCP0 ", "SCI baud rate prescaler select (SCP0)" },
4204e98e3e1Schristos   { M6811_RCKB,  "RCKB ", "Baur Rate Clock Check (test mode)" },
4214e98e3e1Schristos   { M6811_SCR2,  "SCR2 ", "SCI Baud rate select (SCR2)" },
4224e98e3e1Schristos   { M6811_SCR1,  "SCR1 ", "SCI Baud rate select (SCR1)" },
4234e98e3e1Schristos   { M6811_SCR0,  "SCR0 ", "SCI Baud rate select (SCR0)" },
4244e98e3e1Schristos   { 0,  0, 0 }
4254e98e3e1Schristos };
4264e98e3e1Schristos 
4274e98e3e1Schristos static void
4284e98e3e1Schristos m68hc11sio_info (struct hw *me)
4294e98e3e1Schristos {
4304e98e3e1Schristos   SIM_DESC sd;
4314b169a6bSchristos   uint16_t base = 0;
4324e98e3e1Schristos   sim_cpu *cpu;
433*5ba1f45fSchristos   struct m68hc11_sim_cpu *m68hc11_cpu;
4344e98e3e1Schristos   struct m68hc11sio *controller;
4354b169a6bSchristos   uint8_t val;
4364e98e3e1Schristos   long clock_cycle;
4374e98e3e1Schristos 
4384e98e3e1Schristos   sd = hw_system (me);
4394e98e3e1Schristos   cpu = STATE_CPU (sd, 0);
440*5ba1f45fSchristos   m68hc11_cpu = M68HC11_SIM_CPU (cpu);
4414e98e3e1Schristos   controller = hw_data (me);
4424e98e3e1Schristos 
4434e98e3e1Schristos   sim_io_printf (sd, "M68HC11 SIO:\n");
4444e98e3e1Schristos 
4454e98e3e1Schristos   base = cpu_get_io_base (cpu);
4464e98e3e1Schristos 
447*5ba1f45fSchristos   val  = m68hc11_cpu->ios[M6811_BAUD];
4484e98e3e1Schristos   print_io_byte (sd, "BAUD ", baud_desc, val, base + M6811_BAUD);
4494e98e3e1Schristos   sim_io_printf (sd, " (%ld baud)\n",
450*5ba1f45fSchristos                  (m68hc11_cpu->cpu_frequency / 4) / controller->baud_cycle);
4514e98e3e1Schristos 
452*5ba1f45fSchristos   val = m68hc11_cpu->ios[M6811_SCCR1];
4534e98e3e1Schristos   print_io_byte (sd, "SCCR1", sccr1_desc, val, base + M6811_SCCR1);
4544e98e3e1Schristos   sim_io_printf (sd, "  (%d bits) (%dN1)\n",
4554e98e3e1Schristos                  controller->data_length, controller->data_length - 2);
4564e98e3e1Schristos 
457*5ba1f45fSchristos   val = m68hc11_cpu->ios[M6811_SCCR2];
4584e98e3e1Schristos   print_io_byte (sd, "SCCR2", sccr2_desc, val, base + M6811_SCCR2);
4594e98e3e1Schristos   sim_io_printf (sd, "\n");
4604e98e3e1Schristos 
461*5ba1f45fSchristos   val = m68hc11_cpu->ios[M6811_SCSR];
4624e98e3e1Schristos   print_io_byte (sd, "SCSR ", scsr_desc, val, base + M6811_SCSR);
4634e98e3e1Schristos   sim_io_printf (sd, "\n");
4644e98e3e1Schristos 
4654e98e3e1Schristos   clock_cycle = controller->data_length * controller->baud_cycle;
4664e98e3e1Schristos 
4674e98e3e1Schristos   if (controller->tx_poll_event)
4684e98e3e1Schristos     {
4694b169a6bSchristos       int64_t t;
4704e98e3e1Schristos       int n;
4714e98e3e1Schristos 
4724e98e3e1Schristos       t = hw_event_remain_time (me, controller->tx_poll_event);
4734e98e3e1Schristos       n = (clock_cycle - t) / controller->baud_cycle;
4744e98e3e1Schristos       n = controller->data_length - n;
4754e98e3e1Schristos       sim_io_printf (sd, "  Transmit finished in %s (%d bit%s)\n",
4764e98e3e1Schristos 		     cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE),
4774e98e3e1Schristos                      n, (n > 1 ? "s" : ""));
4784e98e3e1Schristos     }
4794e98e3e1Schristos   if (controller->rx_poll_event)
4804e98e3e1Schristos     {
4814b169a6bSchristos       int64_t t;
4824e98e3e1Schristos 
4834e98e3e1Schristos       t = hw_event_remain_time (me, controller->rx_poll_event);
4844e98e3e1Schristos       sim_io_printf (sd, "  Receive finished in %s\n",
4854e98e3e1Schristos 		     cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE));
4864e98e3e1Schristos     }
4874e98e3e1Schristos 
4884e98e3e1Schristos }
4894e98e3e1Schristos 
4904e98e3e1Schristos static int
4914e98e3e1Schristos m68hc11sio_ioctl (struct hw *me,
4924e98e3e1Schristos                   hw_ioctl_request request,
4934e98e3e1Schristos                   va_list ap)
4944e98e3e1Schristos {
4954e98e3e1Schristos   m68hc11sio_info (me);
4964e98e3e1Schristos   return 0;
4974e98e3e1Schristos }
4984e98e3e1Schristos 
4994e98e3e1Schristos /* generic read/write */
5004e98e3e1Schristos 
5014e98e3e1Schristos static unsigned
5024e98e3e1Schristos m68hc11sio_io_read_buffer (struct hw *me,
5034e98e3e1Schristos                            void *dest,
5044e98e3e1Schristos                            int space,
5054e98e3e1Schristos                            unsigned_word base,
5064e98e3e1Schristos                            unsigned nr_bytes)
5074e98e3e1Schristos {
5084e98e3e1Schristos   SIM_DESC sd;
5094e98e3e1Schristos   struct m68hc11sio *controller;
5104e98e3e1Schristos   sim_cpu *cpu;
511*5ba1f45fSchristos   struct m68hc11_sim_cpu *m68hc11_cpu;
5124b169a6bSchristos   uint8_t val;
5134e98e3e1Schristos 
5144e98e3e1Schristos   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
5154e98e3e1Schristos 
5164e98e3e1Schristos   sd  = hw_system (me);
5174e98e3e1Schristos   cpu = STATE_CPU (sd, 0);
518*5ba1f45fSchristos   m68hc11_cpu = M68HC11_SIM_CPU (cpu);
5194e98e3e1Schristos   controller = hw_data (me);
5204e98e3e1Schristos 
5214e98e3e1Schristos   switch (base)
5224e98e3e1Schristos     {
5234e98e3e1Schristos     case M6811_SCSR:
524*5ba1f45fSchristos       controller->rx_clear_scsr = m68hc11_cpu->ios[M6811_SCSR]
5254e98e3e1Schristos         & (M6811_RDRF | M6811_IDLE | M6811_OR | M6811_NF | M6811_FE);
526*5ba1f45fSchristos       ATTRIBUTE_FALLTHROUGH;
5274e98e3e1Schristos 
5284e98e3e1Schristos     case M6811_BAUD:
5294e98e3e1Schristos     case M6811_SCCR1:
5304e98e3e1Schristos     case M6811_SCCR2:
531*5ba1f45fSchristos       val = m68hc11_cpu->ios[base];
5324e98e3e1Schristos       break;
5334e98e3e1Schristos 
5344e98e3e1Schristos     case M6811_SCDR:
5354e98e3e1Schristos       if (controller->rx_clear_scsr)
5364e98e3e1Schristos         {
537*5ba1f45fSchristos           m68hc11_cpu->ios[M6811_SCSR] &= ~controller->rx_clear_scsr;
5384e98e3e1Schristos         }
5394e98e3e1Schristos       val = controller->rx_char;
5404e98e3e1Schristos       break;
5414e98e3e1Schristos 
5424e98e3e1Schristos     default:
5434e98e3e1Schristos       return 0;
5444e98e3e1Schristos     }
5454b169a6bSchristos   *((uint8_t*) dest) = val;
5464e98e3e1Schristos   return 1;
5474e98e3e1Schristos }
5484e98e3e1Schristos 
5494e98e3e1Schristos static unsigned
5504e98e3e1Schristos m68hc11sio_io_write_buffer (struct hw *me,
5514e98e3e1Schristos                             const void *source,
5524e98e3e1Schristos                             int space,
5534e98e3e1Schristos                             unsigned_word base,
5544e98e3e1Schristos                             unsigned nr_bytes)
5554e98e3e1Schristos {
5564e98e3e1Schristos   SIM_DESC sd;
5574e98e3e1Schristos   struct m68hc11sio *controller;
5584e98e3e1Schristos   sim_cpu *cpu;
559*5ba1f45fSchristos   struct m68hc11_sim_cpu *m68hc11_cpu;
5604b169a6bSchristos   uint8_t val;
5614e98e3e1Schristos 
5624e98e3e1Schristos   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
5634e98e3e1Schristos 
5644e98e3e1Schristos   sd  = hw_system (me);
5654e98e3e1Schristos   cpu = STATE_CPU (sd, 0);
566*5ba1f45fSchristos   m68hc11_cpu = M68HC11_SIM_CPU (cpu);
5674e98e3e1Schristos   controller = hw_data (me);
5684e98e3e1Schristos 
5694b169a6bSchristos   val = *((const uint8_t*) source);
5704e98e3e1Schristos   switch (base)
5714e98e3e1Schristos     {
5724e98e3e1Schristos     case M6811_BAUD:
5734e98e3e1Schristos       {
5744e98e3e1Schristos         long divisor;
5754e98e3e1Schristos         long baud;
5764e98e3e1Schristos 
577*5ba1f45fSchristos         m68hc11_cpu->ios[M6811_BAUD] = val;
5784e98e3e1Schristos         switch (val & (M6811_SCP1|M6811_SCP0))
5794e98e3e1Schristos           {
5804e98e3e1Schristos           case M6811_BAUD_DIV_1:
5814e98e3e1Schristos             divisor = 1 * 16;
5824e98e3e1Schristos             break;
5834e98e3e1Schristos 
5844e98e3e1Schristos           case M6811_BAUD_DIV_3:
5854e98e3e1Schristos             divisor = 3 * 16;
5864e98e3e1Schristos             break;
5874e98e3e1Schristos 
5884e98e3e1Schristos           case M6811_BAUD_DIV_4:
5894e98e3e1Schristos             divisor = 4 * 16;
5904e98e3e1Schristos             break;
5914e98e3e1Schristos 
5924e98e3e1Schristos           default:
5934e98e3e1Schristos           case M6811_BAUD_DIV_13:
5944e98e3e1Schristos             divisor = 13 * 16;
5954e98e3e1Schristos             break;
5964e98e3e1Schristos           }
5974e98e3e1Schristos         val &= (M6811_SCR2|M6811_SCR1|M6811_SCR0);
5984e98e3e1Schristos         divisor *= (1 << val);
5994e98e3e1Schristos 
600*5ba1f45fSchristos         baud = (m68hc11_cpu->cpu_frequency / 4) / divisor;
6014e98e3e1Schristos 
6024e98e3e1Schristos         HW_TRACE ((me, "divide rate %ld, baud rate %ld",
6034e98e3e1Schristos                    divisor, baud));
6044e98e3e1Schristos 
6054e98e3e1Schristos         controller->baud_cycle = divisor;
6064e98e3e1Schristos       }
6074e98e3e1Schristos       break;
6084e98e3e1Schristos 
6094e98e3e1Schristos     case M6811_SCCR1:
6104e98e3e1Schristos       {
6114e98e3e1Schristos         if (val & M6811_M)
6124e98e3e1Schristos           controller->data_length = 11;
6134e98e3e1Schristos         else
6144e98e3e1Schristos           controller->data_length = 10;
6154e98e3e1Schristos 
616*5ba1f45fSchristos         m68hc11_cpu->ios[M6811_SCCR1] = val;
6174e98e3e1Schristos       }
6184e98e3e1Schristos       break;
6194e98e3e1Schristos 
6204e98e3e1Schristos     case M6811_SCCR2:
6214e98e3e1Schristos       if ((val & M6811_RE) == 0)
6224e98e3e1Schristos         {
6234e98e3e1Schristos           val &= ~(M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF);
624*5ba1f45fSchristos           val |= (m68hc11_cpu->ios[M6811_SCCR2]
6254e98e3e1Schristos                   & (M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF));
626*5ba1f45fSchristos           m68hc11_cpu->ios[M6811_SCCR2] = val;
6274e98e3e1Schristos           break;
6284e98e3e1Schristos         }
6294e98e3e1Schristos 
6304e98e3e1Schristos       /* Activate reception.  */
6314e98e3e1Schristos       if (controller->rx_poll_event == 0)
6324e98e3e1Schristos         {
6334e98e3e1Schristos           long clock_cycle;
6344e98e3e1Schristos 
6354e98e3e1Schristos           /* Compute CPU clock cycles to wait for the next character.  */
6364e98e3e1Schristos           clock_cycle = controller->data_length * controller->baud_cycle;
6374e98e3e1Schristos 
6384e98e3e1Schristos           controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
6394e98e3e1Schristos                                                                m68hc11sio_rx_poll,
6404e98e3e1Schristos                                                                NULL);
6414e98e3e1Schristos         }
642*5ba1f45fSchristos       m68hc11_cpu->ios[M6811_SCCR2] = val;
643*5ba1f45fSchristos       interrupts_update_pending (&m68hc11_cpu->cpu_interrupts);
6444e98e3e1Schristos       break;
6454e98e3e1Schristos 
6464e98e3e1Schristos       /* No effect.  */
6474e98e3e1Schristos     case M6811_SCSR:
6484e98e3e1Schristos       return 1;
6494e98e3e1Schristos 
6504e98e3e1Schristos     case M6811_SCDR:
651*5ba1f45fSchristos       if (!(m68hc11_cpu->ios[M6811_SCSR] & M6811_TDRE))
6524e98e3e1Schristos         {
6534e98e3e1Schristos           return 0;
6544e98e3e1Schristos         }
6554e98e3e1Schristos 
6564e98e3e1Schristos       controller->tx_char     = val;
6574e98e3e1Schristos       controller->tx_has_char = 1;
658*5ba1f45fSchristos       if ((m68hc11_cpu->ios[M6811_SCCR2] & M6811_TE)
6594e98e3e1Schristos           && controller->tx_poll_event == 0)
6604e98e3e1Schristos         {
6614e98e3e1Schristos           m68hc11sio_tx_poll (me, NULL);
6624e98e3e1Schristos         }
6634e98e3e1Schristos       return 1;
6644e98e3e1Schristos 
6654e98e3e1Schristos     default:
6664e98e3e1Schristos       return 0;
6674e98e3e1Schristos     }
6684e98e3e1Schristos   return nr_bytes;
6694e98e3e1Schristos }
6704e98e3e1Schristos 
6714e98e3e1Schristos 
6724e98e3e1Schristos const struct hw_descriptor dv_m68hc11sio_descriptor[] = {
6734e98e3e1Schristos   { "m68hc11sio", m68hc11sio_finish },
6744e98e3e1Schristos   { "m68hc12sio", m68hc11sio_finish },
6754e98e3e1Schristos   { NULL },
6764e98e3e1Schristos };
6774e98e3e1Schristos 
678