binutils-gdb/sim/m68hc11/dv-m68hc11sio.c
Joel Brobecker 61baf725ec update copyright year range in GDB files
This applies the second part of GDB's End of Year Procedure, which
updates the copyright year range in all of GDB's files.

gdb/ChangeLog:

        Update copyright year range in all GDB files.
2017-01-01 10:52:34 +04:00

662 lines
18 KiB
C

/* dv-m68hc11sio.c -- Simulation of the 68HC11 serial device.
Copyright (C) 1999-2017 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
This file is part of the program GDB, the GNU debugger.
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 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, see <http://www.gnu.org/licenses/>.
*/
#include "sim-main.h"
#include "hw-main.h"
#include "dv-sockser.h"
#include "sim-assert.h"
/* DEVICE
m68hc11sio - m68hc11 serial I/O
DESCRIPTION
Implements the m68hc11 serial I/O controller described in the m68hc11
user guide. The serial I/O controller is directly connected to the CPU
interrupt. The simulator implements:
- baud rate emulation
- 8-bits transfers
PROPERTIES
backend {tcp | stdio}
Use dv-sockser TCP-port backend or stdio for backend. Default: stdio.
PORTS
reset (input)
Reset port. This port is only used to simulate a reset of the serial
I/O controller. It should be connected to the RESET output of the cpu.
*/
/* port ID's */
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11sio_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
/* Serial Controller information. */
struct m68hc11sio
{
enum {sio_tcp, sio_stdio} backend; /* backend */
/* Number of cpu cycles to send a bit on the wire. */
unsigned long baud_cycle;
/* Length in bits of characters sent, this includes the
start/stop and parity bits. Together with baud_cycle, this
is used to find the number of cpu cycles to send/receive a data. */
unsigned int data_length;
/* Information about next character to be transmited. */
unsigned char tx_has_char;
unsigned char tx_char;
unsigned char rx_char;
unsigned char rx_clear_scsr;
/* Periodic I/O polling. */
struct hw_event* tx_poll_event;
struct hw_event* rx_poll_event;
};
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc. */
static hw_io_read_buffer_method m68hc11sio_io_read_buffer;
static hw_io_write_buffer_method m68hc11sio_io_write_buffer;
static hw_port_event_method m68hc11sio_port_event;
static hw_ioctl_method m68hc11sio_ioctl;
#define M6811_SCI_FIRST_REG (M6811_BAUD)
#define M6811_SCI_LAST_REG (M6811_SCDR)
static void
attach_m68hc11sio_regs (struct hw *me,
struct m68hc11sio *controller)
{
hw_attach_address (hw_parent (me), M6811_IO_LEVEL, io_map,
M6811_SCI_FIRST_REG,
M6811_SCI_LAST_REG - M6811_SCI_FIRST_REG + 1,
me);
if (hw_find_property(me, "backend") != NULL)
{
const char *value = hw_find_string_property(me, "backend");
if(! strcmp(value, "tcp"))
controller->backend = sio_tcp;
else if(! strcmp(value, "stdio"))
controller->backend = sio_stdio;
else
hw_abort (me, "illegal value for backend parameter `%s':"
"use tcp or stdio", value);
}
}
static void
m68hc11sio_finish (struct hw *me)
{
struct m68hc11sio *controller;
controller = HW_ZALLOC (me, struct m68hc11sio);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11sio_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11sio_io_write_buffer);
set_hw_ports (me, m68hc11sio_ports);
set_hw_port_event (me, m68hc11sio_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11sio_ioctl);
#else
me->to_ioctl = m68hc11sio_ioctl;
#endif
/* Preset defaults. */
controller->backend = sio_stdio;
/* Attach ourself to our parent bus. */
attach_m68hc11sio_regs (me, controller);
/* Initialize to reset state. */
controller->tx_poll_event = NULL;
controller->rx_poll_event = NULL;
controller->tx_char = 0;
controller->tx_has_char = 0;
controller->rx_clear_scsr = 0;
controller->rx_char = 0;
}
/* An event arrives on an interrupt port. */
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;
}
}
static 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);
}
static void
m68hc11sio_tx_poll (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
cpu->ios[M6811_SCSR] |= M6811_TDRE;
cpu->ios[M6811_SCSR] |= M6811_TC;
/* Transmitter is enabled and we have something to send. */
if ((cpu->ios[M6811_SCCR2] & M6811_TE) && controller->tx_has_char)
{
cpu->ios[M6811_SCSR] &= ~M6811_TDRE;
cpu->ios[M6811_SCSR] &= ~M6811_TC;
controller->tx_has_char = 0;
switch (controller->backend)
{
case sio_tcp:
dv_sockser_write (sd, controller->tx_char);
break;
case sio_stdio:
sim_io_write_stdout (sd, &controller->tx_char, 1);
sim_io_flush_stdout (sd);
break;
default:
break;
}
}
if (controller->tx_poll_event)
{
hw_event_queue_deschedule (me, controller->tx_poll_event);
controller->tx_poll_event = 0;
}
if ((cpu->ios[M6811_SCCR2] & M6811_TE)
&& ((cpu->ios[M6811_SCSR] & M6811_TC) == 0))
{
unsigned long clock_cycle;
/* Compute CPU clock cycles to wait for the next character. */
clock_cycle = controller->data_length * controller->baud_cycle;
controller->tx_poll_event = hw_event_queue_schedule (me, clock_cycle,
m68hc11sio_tx_poll,
NULL);
}
interrupts_update_pending (&cpu->cpu_interrupts);
}
/* Descriptions of the SIO I/O ports. These descriptions are only used to
give information of the SIO device under GDB. */
io_reg_desc sccr2_desc[] = {
{ M6811_TIE, "TIE ", "Transmit Interrupt Enable" },
{ M6811_TCIE, "TCIE ", "Transmit Complete Interrupt Enable" },
{ M6811_RIE, "RIE ", "Receive Interrupt Enable" },
{ M6811_ILIE, "ILIE ", "Idle Line Interrupt Enable" },
{ M6811_TE, "TE ", "Transmit Enable" },
{ M6811_RE, "RE ", "Receive Enable" },
{ M6811_RWU, "RWU ", "Receiver Wake Up" },
{ M6811_SBK, "SBRK ", "Send Break" },
{ 0, 0, 0 }
};
io_reg_desc sccr1_desc[] = {
{ M6811_R8, "R8 ", "Receive Data bit 8" },
{ M6811_T8, "T8 ", "Transmit Data bit 8" },
{ M6811_M, "M ", "SCI Character length (0=8-bits, 1=9-bits)" },
{ M6811_WAKE, "WAKE ", "Wake up method select (0=idle, 1=addr mark" },
{ 0, 0, 0 }
};
io_reg_desc scsr_desc[] = {
{ M6811_TDRE, "TDRE ", "Transmit Data Register Empty" },
{ M6811_TC, "TC ", "Transmit Complete" },
{ M6811_RDRF, "RDRF ", "Receive Data Register Full" },
{ M6811_IDLE, "IDLE ", "Idle Line Detect" },
{ M6811_OR, "OR ", "Overrun Error" },
{ M6811_NF, "NF ", "Noise Flag" },
{ M6811_FE, "FE ", "Framing Error" },
{ 0, 0, 0 }
};
io_reg_desc baud_desc[] = {
{ M6811_TCLR, "TCLR ", "Clear baud rate (test mode)" },
{ M6811_SCP1, "SCP1 ", "SCI baud rate prescaler select (SCP1)" },
{ M6811_SCP0, "SCP0 ", "SCI baud rate prescaler select (SCP0)" },
{ M6811_RCKB, "RCKB ", "Baur Rate Clock Check (test mode)" },
{ M6811_SCR2, "SCR2 ", "SCI Baud rate select (SCR2)" },
{ M6811_SCR1, "SCR1 ", "SCI Baud rate select (SCR1)" },
{ M6811_SCR0, "SCR0 ", "SCI Baud rate select (SCR0)" },
{ 0, 0, 0 }
};
static void
m68hc11sio_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11sio *controller;
uint8 val;
long clock_cycle;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
sim_io_printf (sd, "M68HC11 SIO:\n");
base = cpu_get_io_base (cpu);
val = cpu->ios[M6811_BAUD];
print_io_byte (sd, "BAUD ", baud_desc, val, base + M6811_BAUD);
sim_io_printf (sd, " (%ld baud)\n",
(cpu->cpu_frequency / 4) / controller->baud_cycle);
val = cpu->ios[M6811_SCCR1];
print_io_byte (sd, "SCCR1", sccr1_desc, val, base + M6811_SCCR1);
sim_io_printf (sd, " (%d bits) (%dN1)\n",
controller->data_length, controller->data_length - 2);
val = cpu->ios[M6811_SCCR2];
print_io_byte (sd, "SCCR2", sccr2_desc, val, base + M6811_SCCR2);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_SCSR];
print_io_byte (sd, "SCSR ", scsr_desc, val, base + M6811_SCSR);
sim_io_printf (sd, "\n");
clock_cycle = controller->data_length * controller->baud_cycle;
if (controller->tx_poll_event)
{
signed64 t;
int n;
t = hw_event_remain_time (me, controller->tx_poll_event);
n = (clock_cycle - t) / controller->baud_cycle;
n = controller->data_length - n;
sim_io_printf (sd, " Transmit finished in %s (%d bit%s)\n",
cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE),
n, (n > 1 ? "s" : ""));
}
if (controller->rx_poll_event)
{
signed64 t;
t = hw_event_remain_time (me, controller->rx_poll_event);
sim_io_printf (sd, " Receive finished in %s\n",
cycle_to_string (cpu, t, PRINT_TIME | PRINT_CYCLE));
}
}
static int
m68hc11sio_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11sio_info (me);
return 0;
}
/* generic read/write */
static unsigned
m68hc11sio_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
switch (base)
{
case M6811_SCSR:
controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
& (M6811_RDRF | M6811_IDLE | M6811_OR | M6811_NF | M6811_FE);
case M6811_BAUD:
case M6811_SCCR1:
case M6811_SCCR2:
val = cpu->ios[base];
break;
case M6811_SCDR:
if (controller->rx_clear_scsr)
{
cpu->ios[M6811_SCSR] &= ~controller->rx_clear_scsr;
}
val = controller->rx_char;
break;
default:
return 0;
}
*((unsigned8*) dest) = val;
return 1;
}
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;
}
const struct hw_descriptor dv_m68hc11sio_descriptor[] = {
{ "m68hc11sio", m68hc11sio_finish },
{ "m68hc12sio", m68hc11sio_finish },
{ NULL },
};