mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 19:12:58 -04:00
827 lines
23 KiB
C
827 lines
23 KiB
C
/* $Id: sun3-mainbus.c,v 1.7 2009/08/30 14:17:53 fredette Exp $ */
|
|
|
|
/* machine/sun3/sun3-mainbus.c - implementation of Sun 3 emulation: */
|
|
|
|
/*
|
|
* Copyright (c) 2003, 2004 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: sun3-mainbus.c,v 1.7 2009/08/30 14:17:53 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include "sun3-impl.h"
|
|
#include <tme/ic/z8530.h>
|
|
#include <tme/ic/isil7170.h>
|
|
#include <stdio.h>
|
|
|
|
/* macros: */
|
|
#define TME_BUS_SIGNAL_INT_CLOCK TME_BUS_SIGNAL_INT(8)
|
|
|
|
/* this possibly updates that the interrupt priority level driven to the CPU: */
|
|
int
|
|
_tme_sun3_ipl_check(struct tme_sun3 *sun3)
|
|
{
|
|
tme_uint8_t interreg;
|
|
unsigned int ipl, ipl_index;
|
|
tme_uint8_t ipl_mask;
|
|
|
|
/* get the interrupt register: */
|
|
interreg = sun3->tme_sun3_ints;
|
|
|
|
/* assume that interrupts are completely masked: */
|
|
ipl = TME_M68K_IPL_NONE;
|
|
|
|
/* if interrupts are enabled: */
|
|
if (interreg & TME_SUN3_IREG_INTS_ENAB) {
|
|
|
|
/* find the highest ipl now asserted on the buses: */
|
|
for (ipl = TME_M68K_IPL_MAX;
|
|
ipl > TME_M68K_IPL_NONE;
|
|
ipl--) {
|
|
ipl_index = ipl >> 3;
|
|
ipl_mask = TME_BIT(ipl & 7);
|
|
if (sun3->tme_sun3_int_signals[ipl_index] & ipl_mask) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check the soft interrupts: */
|
|
if (interreg & TME_SUN3_IREG_SOFT_INT_3) {
|
|
ipl = TME_MAX(ipl, 3);
|
|
}
|
|
else if (interreg & TME_SUN3_IREG_SOFT_INT_2) {
|
|
ipl = TME_MAX(ipl, 2);
|
|
}
|
|
else if (interreg & TME_SUN3_IREG_SOFT_INT_1) {
|
|
ipl = TME_MAX(ipl, 1);
|
|
}
|
|
}
|
|
|
|
/* possibly update the CPU: */
|
|
if (ipl != sun3->tme_sun3_int_ipl_last) {
|
|
sun3->tme_sun3_int_ipl_last = ipl;
|
|
return ((*sun3->tme_sun3_m68k->tme_m68k_bus_interrupt)
|
|
(sun3->tme_sun3_m68k, ipl));
|
|
}
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* our mainbus signal handler: */
|
|
static int
|
|
_tme_sun3_bus_signal(struct tme_bus_connection *conn_bus_raiser, unsigned int signal)
|
|
{
|
|
struct tme_sun3 *sun3;
|
|
int signal_asserted;
|
|
unsigned int ipl, ipl_index;
|
|
tme_uint8_t ipl_mask;
|
|
|
|
/* recover our sun3: */
|
|
sun3 = (struct tme_sun3 *) conn_bus_raiser->tme_bus_connection.tme_connection_element->tme_element_private;
|
|
|
|
/* see whether the signal is asserted or negated: */
|
|
signal_asserted = TRUE;
|
|
switch (signal & TME_BUS_SIGNAL_LEVEL_MASK) {
|
|
case TME_BUS_SIGNAL_LEVEL_NEGATED:
|
|
signal_asserted = FALSE;
|
|
case TME_BUS_SIGNAL_LEVEL_ASSERTED:
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
signal = TME_BUS_SIGNAL_WHICH(signal);
|
|
|
|
/* ipl 8 doesn't really exist - it's the interrupt signal from the
|
|
isil7170. we must map it into either ipl 7 (NMI) or ipl 5
|
|
(clock) depending on the state of the interrupt register: */
|
|
if (signal == TME_BUS_SIGNAL_INT_CLOCK) {
|
|
|
|
/* if the signal is being asserted: */
|
|
if (signal_asserted) {
|
|
|
|
/* map the interrupt signal: */
|
|
if (sun3->tme_sun3_ints & TME_SUN3_IREG_CLOCK_ENAB_5) {
|
|
signal = TME_BUS_SIGNAL_INT(5);
|
|
}
|
|
else if (sun3->tme_sun3_ints & TME_SUN3_IREG_CLOCK_ENAB_7) {
|
|
signal = TME_BUS_SIGNAL_INT(7);
|
|
}
|
|
else {
|
|
/* the clock interrupt isn't connected to any ipl: */
|
|
signal = TME_BUS_SIGNAL_INT_UNSPEC;
|
|
}
|
|
|
|
/* remember the last clock interrupt signal: */
|
|
sun3->tme_sun3_int_signal_clock_last = signal;
|
|
}
|
|
|
|
else {
|
|
|
|
/* recover the last clock interrupt signal: */
|
|
signal = sun3->tme_sun3_int_signal_clock_last;
|
|
}
|
|
|
|
/* if we're supposed to ignore this signal: */
|
|
if (signal == TME_BUS_SIGNAL_INT_UNSPEC) {
|
|
return (TME_OK);
|
|
}
|
|
}
|
|
|
|
/* dispatch on the signal: */
|
|
|
|
/* halt: */
|
|
if (signal == TME_BUS_SIGNAL_HALT) {
|
|
abort();
|
|
}
|
|
|
|
/* reset: */
|
|
else if (signal == TME_BUS_SIGNAL_RESET) {
|
|
|
|
/* if this reset signal is coming from the m68k: */
|
|
if (conn_bus_raiser->tme_bus_connection.tme_connection_other
|
|
== &sun3->tme_sun3_m68k->tme_m68k_bus_connection.tme_bus_connection) {
|
|
|
|
/* XXX FIXME - note that the do_reset code is lazy and only
|
|
sends reset negation edges out to the busses. we are also
|
|
lazy (and also worried that sending out assertion edges might
|
|
break things), so we only send out reset negation edges too: */
|
|
if (signal_asserted) {
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* propagate this reset edge to all busses: */
|
|
(*sun3->tme_sun3_obio->tme_bus_signal)
|
|
(sun3->tme_sun3_obio,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
(*sun3->tme_sun3_obmem->tme_bus_signal)
|
|
(sun3->tme_sun3_obmem,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
(*sun3->tme_sun3_vmebus->tme_bus_signal)
|
|
(sun3->tme_sun3_vmebus,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
}
|
|
}
|
|
|
|
/* an interrupt signal: */
|
|
else if (TME_BUS_SIGNAL_IS_INT(signal)) {
|
|
ipl = TME_BUS_SIGNAL_INDEX_INT(signal);
|
|
if (ipl >= TME_M68K_IPL_MIN
|
|
&& ipl <= TME_M68K_IPL_MAX) {
|
|
|
|
/* update this ipl in the byte array: */
|
|
ipl_index = ipl >> 3;
|
|
ipl_mask = TME_BIT(ipl & 7);
|
|
sun3->tme_sun3_int_signals[ipl_index]
|
|
= ((sun3->tme_sun3_int_signals[ipl_index]
|
|
& ~ipl_mask)
|
|
| (signal_asserted
|
|
? ipl_mask
|
|
: 0));
|
|
|
|
/* possibly update the ipl being driven to the CPU: */
|
|
return (_tme_sun3_ipl_check(sun3));
|
|
}
|
|
}
|
|
|
|
/* an unknown signal: */
|
|
else {
|
|
abort();
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this handles a CPU interrupt acknowledge: */
|
|
static int
|
|
_tme_sun3_bus_intack(struct tme_bus_connection *conn_m68k, unsigned int ipl, int *vector)
|
|
{
|
|
struct tme_sun3 *sun3;
|
|
tme_uint8_t interreg;
|
|
unsigned int signal;
|
|
int rc;
|
|
|
|
/* recover our sun3: */
|
|
sun3 = (struct tme_sun3 *) conn_m68k->tme_bus_connection.tme_connection_element->tme_element_private;
|
|
|
|
/* acknowledge any soft interrupt: */
|
|
interreg = sun3->tme_sun3_ints;
|
|
if ((ipl == 3
|
|
&& (interreg & TME_SUN3_IREG_SOFT_INT_3))
|
|
|| (ipl == 2
|
|
&& (interreg & TME_SUN3_IREG_SOFT_INT_2))
|
|
|| (ipl == 1
|
|
&& (interreg & TME_SUN3_IREG_SOFT_INT_1))) {
|
|
*vector = TME_BUS_INTERRUPT_VECTOR_UNDEF;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* turn the ipl into a bus signal number: */
|
|
signal = TME_BUS_SIGNAL_INT(ipl);
|
|
|
|
/* try the acknowledge on these buses, in order: */
|
|
|
|
/* obio: */
|
|
rc = (*sun3->tme_sun3_obio->tme_bus_intack)
|
|
(sun3->tme_sun3_obio, signal, vector);
|
|
if (rc == ENOENT
|
|
&& signal == sun3->tme_sun3_int_signal_clock_last) {
|
|
rc = (*sun3->tme_sun3_obio->tme_bus_intack)
|
|
(sun3->tme_sun3_obio, TME_BUS_SIGNAL_INT_CLOCK, vector);
|
|
}
|
|
if (rc != ENOENT) {
|
|
return (rc);
|
|
}
|
|
/* obmem: */
|
|
rc = (*sun3->tme_sun3_obmem->tme_bus_intack)
|
|
(sun3->tme_sun3_obmem, signal, vector);
|
|
if (rc != ENOENT) {
|
|
return (rc);
|
|
}
|
|
/* VMEbus: */
|
|
rc = (*sun3->tme_sun3_vmebus->tme_bus_intack)
|
|
(sun3->tme_sun3_vmebus, signal, vector);
|
|
if (rc != ENOENT) {
|
|
return (rc);
|
|
}
|
|
|
|
/* done: */
|
|
return (rc);
|
|
}
|
|
|
|
/* our command function: */
|
|
static int
|
|
_tme_sun3_command(struct tme_element *element, const char * const * args, char **_output)
|
|
{
|
|
struct tme_sun3 *sun3;
|
|
int do_reset;
|
|
|
|
/* recover our sun3: */
|
|
sun3 = (struct tme_sun3 *) element->tme_element_private;
|
|
|
|
/* assume no reset: */
|
|
do_reset = FALSE;
|
|
|
|
/* the "power" command: */
|
|
if (TME_ARG_IS(args[1], "power")) {
|
|
|
|
if (TME_ARG_IS(args[2], "up")
|
|
&& args[3] == NULL) {
|
|
do_reset = TRUE;
|
|
}
|
|
|
|
else if (TME_ARG_IS(args[2], "down")
|
|
&& args[3] == NULL) {
|
|
/* nothing */
|
|
}
|
|
|
|
/* return an error: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s power [ up | down ]",
|
|
_("usage:"),
|
|
args[0]);
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
|
|
/* the "diag-switch" command: */
|
|
else if (TME_ARG_IS(args[1], "diag-switch")) {
|
|
|
|
if (args[2] == NULL) {
|
|
tme_output_append_error(_output,
|
|
"diag-switch %s",
|
|
(sun3->tme_sun3_enable & TME_SUN3_ENA_DIAG
|
|
? "true"
|
|
: "false"));
|
|
}
|
|
|
|
else if (TME_ARG_IS(args[2], "true")
|
|
&& args[3] == NULL) {
|
|
sun3->tme_sun3_enable |= TME_SUN3_ENA_DIAG;
|
|
}
|
|
|
|
else if (TME_ARG_IS(args[2], "false")
|
|
&& args[3] == NULL) {
|
|
sun3->tme_sun3_enable &= ~TME_SUN3_ENA_DIAG;
|
|
}
|
|
|
|
/* return an error: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s diag-switch [ true | false ]",
|
|
_("usage:"),
|
|
args[0]);
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
|
|
/* any other command: */
|
|
else {
|
|
if (args[1] != NULL) {
|
|
tme_output_append_error(_output,
|
|
"%s '%s', ",
|
|
_("unknown command"),
|
|
args[1]);
|
|
}
|
|
tme_output_append_error(_output,
|
|
_("available %s commands: %s"),
|
|
args[0],
|
|
"power");
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (do_reset) {
|
|
|
|
/* reset the CPU: */
|
|
(*sun3->tme_sun3_m68k->tme_m68k_bus_connection.tme_bus_signal)
|
|
(&sun3->tme_sun3_m68k->tme_m68k_bus_connection,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
|
|
/* reset all busses: */
|
|
(*sun3->tme_sun3_obio->tme_bus_signal)
|
|
(sun3->tme_sun3_obio,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
(*sun3->tme_sun3_obmem->tme_bus_signal)
|
|
(sun3->tme_sun3_obmem,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
(*sun3->tme_sun3_vmebus->tme_bus_signal)
|
|
(sun3->tme_sun3_vmebus,
|
|
TME_BUS_SIGNAL_RESET
|
|
| TME_BUS_SIGNAL_LEVEL_NEGATED
|
|
| TME_BUS_SIGNAL_EDGE);
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* the connection scorer: */
|
|
static int
|
|
_tme_sun3_connection_score(struct tme_connection *conn, unsigned int *_score)
|
|
{
|
|
struct tme_m68k_bus_connection *conn_m68k;
|
|
struct tme_sun3_bus_connection *conn_sun3;
|
|
struct tme_bus_connection *conn_bus;
|
|
struct tme_sun3 *sun3;
|
|
unsigned int score;
|
|
|
|
/* recover our sun3: */
|
|
sun3 = (struct tme_sun3 *) conn->tme_connection_element->tme_element_private;
|
|
|
|
/* assume that this connection is useless: */
|
|
score = 0;
|
|
|
|
/* dispatch on the connection type: */
|
|
conn_m68k = (struct tme_m68k_bus_connection *) conn->tme_connection_other;
|
|
conn_sun3 = (struct tme_sun3_bus_connection *) conn;
|
|
conn_bus = (struct tme_bus_connection *) conn->tme_connection_other;
|
|
switch (conn->tme_connection_type) {
|
|
|
|
/* this must be an m68k chip, and not another bus: */
|
|
case TME_CONNECTION_BUS_M68K:
|
|
if (conn_bus->tme_bus_tlb_set_add == NULL
|
|
&& conn_m68k->tme_m68k_bus_tlb_fill == NULL
|
|
&& conn_m68k->tme_m68k_bus_m6888x_enable != NULL) {
|
|
score = 10;
|
|
}
|
|
break;
|
|
|
|
/* if this connection is not for an obio master, this must be a bus,
|
|
and not a chip, and vice versa. if this connection is for a bus,
|
|
the bus must still be free: */
|
|
case TME_CONNECTION_BUS_GENERIC:
|
|
if (((conn_sun3->tme_sun3_bus_connection_which != TME_SUN3_CONN_OBIO_MASTER)
|
|
== (conn_bus->tme_bus_tlb_set_add != NULL
|
|
&& conn_bus->tme_bus_tlb_fill != NULL))
|
|
&& (conn_sun3->tme_sun3_bus_connection_which >= TME_SUN3_CONN_BUS_COUNT
|
|
|| sun3->tme_sun3_buses[conn_sun3->tme_sun3_bus_connection_which] == NULL)) {
|
|
score = 1;
|
|
}
|
|
break;
|
|
|
|
default: abort();
|
|
}
|
|
|
|
*_score = score;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this makes a new connection: */
|
|
static int
|
|
_tme_sun3_connection_make(struct tme_connection *conn, unsigned int state)
|
|
{
|
|
struct tme_sun3 *sun3;
|
|
struct tme_m68k_bus_connection *conn_m68k;
|
|
struct tme_sun3_bus_connection *conn_sun3;
|
|
struct tme_bus_connection *conn_bus;
|
|
struct tme_connection *conn_other;
|
|
|
|
/* recover our sun3: */
|
|
sun3 = (struct tme_sun3 *) conn->tme_connection_element->tme_element_private;
|
|
|
|
/* dispatch on the connection type: */
|
|
conn_other = conn->tme_connection_other;
|
|
conn_m68k = (struct tme_m68k_bus_connection *) conn_other;
|
|
conn_sun3 = (struct tme_sun3_bus_connection *) conn;
|
|
conn_bus = (struct tme_bus_connection *) conn_other;
|
|
switch (conn->tme_connection_type) {
|
|
|
|
case TME_CONNECTION_BUS_M68K:
|
|
sun3->tme_sun3_m68k = conn_m68k;
|
|
break;
|
|
|
|
case TME_CONNECTION_BUS_GENERIC:
|
|
|
|
/* if this connection is for a bus: */
|
|
if (conn_sun3->tme_sun3_bus_connection_which < TME_SUN3_CONN_BUS_COUNT) {
|
|
|
|
/* remember the connection to this bus: */
|
|
sun3->tme_sun3_buses[conn_sun3->tme_sun3_bus_connection_which] = conn_bus;
|
|
}
|
|
|
|
/* otherwise, if this connection is for the memory error register: */
|
|
else if (conn_sun3->tme_sun3_bus_connection_which == TME_SUN3_CONN_REG_MEMERR) {
|
|
|
|
/* remember the memory error register's bus connection: */
|
|
sun3->tme_sun3_memerr_bus = conn_bus;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this breaks a connection: */
|
|
static int
|
|
_tme_sun3_connection_break(struct tme_connection *conn, unsigned int state)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
/* this makes new connection sides: */
|
|
static int
|
|
_tme_sun3_connections_new(struct tme_element *element, const char * const *args, struct tme_connection **_conns, char **_output)
|
|
{
|
|
struct tme_m68k_bus_connection *conn_m68k;
|
|
struct tme_sun3_bus_connection *conn_sun3;
|
|
struct tme_bus_connection *conn_bus;
|
|
struct tme_connection *conn;
|
|
struct tme_sun3 *sun3;
|
|
char *free_buses;
|
|
int which_conn;
|
|
|
|
/* recover our sun3: */
|
|
sun3 = (struct tme_sun3 *) element->tme_element_private;
|
|
|
|
/* if we have no arguments, and we don't have a CPU yet, we can take
|
|
an m68k bus connection: */
|
|
if (args[1] == NULL
|
|
&& sun3->tme_sun3_m68k == NULL) {
|
|
|
|
/* create our side of an m68k bus connection: */
|
|
conn_m68k = tme_new0(struct tme_m68k_bus_connection, 1);
|
|
conn_bus = &conn_m68k->tme_m68k_bus_connection;
|
|
conn = &conn_bus->tme_bus_connection;
|
|
conn->tme_connection_next = *_conns;
|
|
|
|
/* fill in the generic connection: */
|
|
conn->tme_connection_type = TME_CONNECTION_BUS_M68K;
|
|
conn->tme_connection_score = _tme_sun3_connection_score;
|
|
conn->tme_connection_make = _tme_sun3_connection_make;
|
|
conn->tme_connection_break = _tme_sun3_connection_break;
|
|
|
|
/* fill in the generic bus connection: */
|
|
conn_bus->tme_bus_signal = _tme_sun3_bus_signal;
|
|
conn_bus->tme_bus_intack = _tme_sun3_bus_intack;
|
|
conn_bus->tme_bus_tlb_set_add = _tme_sun3_mmu_tlb_set_add;
|
|
|
|
/* full in the m68k bus connection: */
|
|
conn_m68k->tme_m68k_bus_tlb_fill = _tme_sun3_m68k_tlb_fill;
|
|
|
|
/* add in this connection side possibility: */
|
|
*_conns = conn;
|
|
}
|
|
|
|
/* create our side of a generic bus connection: */
|
|
conn_sun3 = tme_new0(struct tme_sun3_bus_connection, 1);
|
|
conn_bus = &conn_sun3->tme_sun3_bus_connection;
|
|
conn = &conn_bus->tme_bus_connection;
|
|
conn->tme_connection_next = *_conns;
|
|
|
|
/* fill in the generic connection: */
|
|
conn->tme_connection_type = TME_CONNECTION_BUS_GENERIC;
|
|
conn->tme_connection_score = _tme_sun3_connection_score;
|
|
conn->tme_connection_make = _tme_sun3_connection_make;
|
|
conn->tme_connection_break = _tme_sun3_connection_break;
|
|
|
|
/* fill in the generic bus connection: */
|
|
conn_bus->tme_bus_signal = _tme_sun3_bus_signal;
|
|
conn_bus->tme_bus_intack = NULL;
|
|
conn_bus->tme_bus_tlb_set_add = _tme_sun3_mmu_tlb_set_add;
|
|
conn_bus->tme_bus_tlb_fill = _tme_sun3_bus_tlb_fill;
|
|
|
|
/* if we have no argument: */
|
|
if (args[1] == NULL) {
|
|
|
|
/* make this connection for an obio master: */
|
|
conn_sun3->tme_sun3_bus_connection_which = TME_SUN3_CONN_OBIO_MASTER;
|
|
}
|
|
|
|
/* otherwise, we have at least one argument: */
|
|
else {
|
|
|
|
/* we must have no other arguments: */
|
|
if (args[2] != NULL) {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
args[2],
|
|
_("unexpected"));
|
|
tme_free(conn_sun3);
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* start the list of buses that we don't have yet: */
|
|
free_buses = NULL;
|
|
|
|
/* poison the which connection: */
|
|
which_conn = -1;
|
|
|
|
/* check each bus: */
|
|
|
|
/* obio: */
|
|
if (sun3->tme_sun3_obio == NULL) {
|
|
tme_output_append(&free_buses, " obio");
|
|
}
|
|
if (TME_ARG_IS(args[1], "obio")) {
|
|
which_conn = TME_SUN3_CONN_BUS_OBIO;
|
|
}
|
|
|
|
/* obmem: */
|
|
if (sun3->tme_sun3_obmem == NULL) {
|
|
tme_output_append(&free_buses, " obmem");
|
|
}
|
|
if (TME_ARG_IS(args[1], "obmem")) {
|
|
which_conn = TME_SUN3_CONN_BUS_OBMEM;
|
|
}
|
|
|
|
/* VMEbus: */
|
|
if (sun3->tme_sun3_vmebus == NULL) {
|
|
tme_output_append(&free_buses, " vme");
|
|
}
|
|
if (TME_ARG_IS(args[1], "vme")) {
|
|
which_conn = TME_SUN3_CONN_BUS_VME;
|
|
conn_bus->tme_bus_subregions.tme_bus_subregion_address_last
|
|
= TME_SUN3_DVMA_SIZE_VME;
|
|
}
|
|
|
|
/* random connections: */
|
|
|
|
if (TME_ARG_IS(args[1], "memerr")) {
|
|
which_conn = TME_SUN3_CONN_REG_MEMERR;
|
|
conn_bus->tme_bus_subregions.tme_bus_subregion_address_last
|
|
= TME_SUN3_MEMERR_SIZ_REG;
|
|
}
|
|
else if (TME_ARG_IS(args[1], "intreg")) {
|
|
which_conn = TME_SUN3_CONN_REG_INTREG;
|
|
conn_bus->tme_bus_subregions.tme_bus_subregion_address_last
|
|
= sizeof(sun3->tme_sun3_ints);
|
|
}
|
|
|
|
/* if the which connection is still poison, or if this is trying
|
|
to connect a bus that we already have, complain: */
|
|
if (which_conn < 0
|
|
|| (which_conn < TME_SUN3_CONN_BUS_COUNT
|
|
&& sun3->tme_sun3_buses[which_conn] != NULL)) {
|
|
if (which_conn < 0) {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
_("unknown bus or register:"),
|
|
args[1]);
|
|
}
|
|
if (free_buses != NULL) {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
_("remaining buses:"),
|
|
free_buses);
|
|
tme_free(free_buses);
|
|
}
|
|
else {
|
|
tme_output_append_error(_output, _("all buses present"));
|
|
}
|
|
tme_free(conn_sun3);
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* free the remaining bus list: */
|
|
if (free_buses != NULL) {
|
|
tme_free(free_buses);
|
|
}
|
|
|
|
/* fill in the sun3 connection: */
|
|
conn_sun3->tme_sun3_bus_connection_which = which_conn;
|
|
}
|
|
|
|
/* add in this connection side possibility: */
|
|
*_conns = conn;
|
|
|
|
/* done: */
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this creates a new sun3 element: */
|
|
TME_ELEMENT_NEW_DECL(tme_machine_sun3) {
|
|
int usage;
|
|
struct tme_sun3 *sun3;
|
|
const char *idprom_filename;
|
|
FILE *idprom_fp;
|
|
tme_uint8_t idprom[TME_SUN_IDPROM_SIZE];
|
|
int arg_i;
|
|
|
|
arg_i = 1;
|
|
usage = FALSE;
|
|
|
|
/* our first argument is the filename to load our IDPROM contents from: */
|
|
idprom_filename = args[arg_i++];
|
|
if (idprom_filename == NULL) {
|
|
usage = TRUE;
|
|
}
|
|
|
|
/* we must have no more arguments: */
|
|
if (args[arg_i] != NULL) {
|
|
tme_output_append_error(_output,
|
|
"%s %s, ",
|
|
args[arg_i],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
}
|
|
|
|
/* if our usage was bad: */
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s IDPROM%s",
|
|
_("usage:"),
|
|
args[0],
|
|
_("-FILENAME"));
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* try to read in the IDPROM: */
|
|
idprom_fp = fopen(idprom_filename, "r");
|
|
if (idprom_fp == NULL) {
|
|
tme_output_append_error(_output, "%s", idprom_filename);
|
|
return (errno);
|
|
}
|
|
if (fread(idprom, sizeof(tme_uint8_t), sizeof(idprom), idprom_fp) != sizeof(idprom)) {
|
|
tme_output_append_error(_output, "%s", idprom_filename);
|
|
fclose(idprom_fp);
|
|
return (ENOEXEC);
|
|
}
|
|
fclose(idprom_fp);
|
|
|
|
/* allocate and initialize the new sun3: */
|
|
sun3 = tme_new0(struct tme_sun3, 1);
|
|
sun3->tme_sun3_element = element;
|
|
|
|
/* set the IDPROM: */
|
|
memcpy(sun3->tme_sun3_idprom_contents, idprom, sizeof(idprom));
|
|
|
|
/* the context register: */
|
|
sun3->tme_sun3_context = 0;
|
|
|
|
/* the diagnostics register: */
|
|
sun3->tme_sun3_diag = 0;
|
|
|
|
/* the bus error register: */
|
|
sun3->tme_sun3_buserr = 0;
|
|
|
|
/* the enable register: */
|
|
sun3->tme_sun3_enable = 0;
|
|
|
|
/* the memory error register: */
|
|
sun3->tme_sun3_memerr_csr = 0;
|
|
sun3->tme_sun3_memerr_vaddr = 0;
|
|
|
|
/* the interrupt register: */
|
|
sun3->tme_sun3_ints = 0;
|
|
|
|
/* the MMU: */
|
|
_tme_sun3_mmu_new(sun3);
|
|
|
|
/* the busses: */
|
|
sun3->tme_sun3_obio = NULL;
|
|
sun3->tme_sun3_obmem = NULL;
|
|
sun3->tme_sun3_vmebus = NULL;
|
|
|
|
/* the last clock interrupt signal: */
|
|
sun3->tme_sun3_int_signal_clock_last = TME_BUS_SIGNAL_INT_UNSPEC;
|
|
|
|
/* fill the element: */
|
|
element->tme_element_private = sun3;
|
|
element->tme_element_connections_new = _tme_sun3_connections_new;
|
|
element->tme_element_command = _tme_sun3_command;
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this creates a new Sun-3 isil7170: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_machine_sun3,clock) {
|
|
struct tme_isil7170_socket socket;
|
|
const char *sub_args[4];
|
|
int arg_i;
|
|
|
|
/* create the isil7170 socket: */
|
|
memset (&socket, 0, sizeof(socket));
|
|
socket.tme_isil7170_socket_version = TME_ISIL7170_SOCKET_0;
|
|
socket.tme_isil7170_socket_addr_shift = 0;
|
|
socket.tme_isil7170_socket_port_least_lane = 3; /* D31-D24 */
|
|
socket.tme_isil7170_socket_clock_basic = TME_ISIL7170_FREQ_32K;
|
|
socket.tme_isil7170_socket_int_signal = TME_BUS_SIGNAL_INT_CLOCK;
|
|
|
|
/* create the isil7170. we allow at most two arguments to pass
|
|
through, which is an awful hack: */
|
|
sub_args[0] = "tme/ic/isil7170";
|
|
for (arg_i = 1;; arg_i++) {
|
|
if (arg_i == TME_ARRAY_ELS(sub_args)) {
|
|
abort();
|
|
}
|
|
sub_args[arg_i] = args[arg_i];
|
|
if (args[arg_i] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
return (tme_element_new(element, (const char * const *) sub_args, &socket, _output));
|
|
}
|
|
|
|
/* this creates a new Sun-3 z8530: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_machine_sun3,zs) {
|
|
struct tme_z8530_socket socket = TME_SUN_Z8530_SOCKET_INIT;
|
|
char *sub_args[2];
|
|
|
|
/* create the z8530: */
|
|
sub_args[0] = "tme/ic/z8530";
|
|
sub_args[1] = NULL;
|
|
return (tme_element_new(element, (const char * const *) sub_args, &socket, _output));
|
|
}
|
|
|
|
/* this creates a new Sun-3 obie: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_machine_sun3,obie) {
|
|
return (tme_sun_obie(element, args, _output));
|
|
}
|
|
|
|
/* this creates a new Sun-3 bwtwo: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_machine_sun3,bwtwo) {
|
|
return (tme_sun_bwtwo(element, args, _output));
|
|
}
|
|
|
|
/* this creates a new Sun-3 si: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_machine_sun3,si) {
|
|
return (tme_sun_si(element, args, _output));
|
|
}
|
|
|
|
/* this creates a new Sun-3 cgtwo: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_machine_sun3,cgtwo) {
|
|
return (tme_sun_cgtwo(element, args, _output));
|
|
}
|