sun1: refactor into multibus cage/card

* added more firmware revisions
* added mmu emulation
This commit is contained in:
Patrick Mackinlay 2025-02-09 15:33:16 +07:00
parent 3cec2ae67f
commit c2ccf4cf6e
6 changed files with 889 additions and 119 deletions

View File

@ -43703,7 +43703,6 @@ sothello
tonton
@source:sun/sun1.cpp
multibox
sun1
@source:sun/sun2.cpp

View File

@ -8,6 +8,7 @@
Sun-1
Processor(s): 68000
P/N: 270-0001
Notes: Large black desktop boxes with 17" monitors.
Uses the original Stanford-designed video board
and a parallel microswitch keyboard (type 1) and
@ -55,14 +56,18 @@
04/04/2011 Modernised, added terminal keyboard.
TODO:
- graphic cards
- network cards
- storage cards
****************************************************************************/
#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "machine/am9513.h"
#include "machine/z80sio.h"
#include "sun1_cpu.h"
#include "bus/multibus/multibus.h"
namespace {
@ -71,144 +76,51 @@ class sun1_state : public driver_device
public:
sun1_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_iouart(*this, "iouart")
, m_p_ram(*this, "p_ram")
, m_bus(*this, "slot")
{
}
void sun1(machine_config &config);
void cyb(machine_config &config);
private:
void sun1_mem(address_map &map) ATTR_COLD;
void cyb_mem(address_map &map) ATTR_COLD;
virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
required_device<cpu_device> m_maincpu;
required_device<upd7201_device> m_iouart;
required_shared_ptr<uint16_t> m_p_ram;
required_device<multibus_device> m_bus;
};
void sun1_state::sun1_mem(address_map &map)
void sun1_state::machine_start()
{
map.unmap_value_high();
map(0x00000000, 0x001fffff).ram().share("p_ram"); // 512 KB RAM / ROM at boot
map(0x00200000, 0x00203fff).rom().region("monitor", 0);
map(0x00600000, 0x00600007).mirror(0x1ffff8).rw(m_iouart, FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0xff00);
map(0x00800000, 0x00800003).mirror(0x1ffffc).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
map(0x00a00000, 0x00bfffff).unmaprw(); // page map
map(0x00c00000, 0x00dfffff).unmaprw(); // segment map
map(0x00e00000, 0x00ffffff).unmaprw(); // context register
}
void sun1_state::cyb_mem(address_map &map)
{
sun1_mem(map);
map(0x00400000, 0x00403fff).rom().region("cyb", 0);
}
/* Input ports */
static INPUT_PORTS_START( sun1 )
INPUT_PORTS_END
void sun1_state::machine_reset()
{
uint8_t* user1 = memregion("monitor")->base();
memcpy((uint8_t*)m_p_ram.target(),user1,0x4000);
}
static void sun1_cards(device_slot_interface &device)
{
device.option_add("sun1cpu", MULTIBUS_SUN1CPU);
}
void sun1_state::sun1(machine_config &config)
{
/* basic machine hardware */
M68000(config, m_maincpu, 16_MHz_XTAL / 2);
m_maincpu->set_addrmap(AS_PROGRAM, &sun1_state::sun1_mem);
// FIXME: CPU board can optionally drive Multibus clocks
MULTIBUS(config, m_bus, 19.6608_MHz_XTAL / 2);
am9513_device &timer(AM9513(config, "timer", 16_MHz_XTAL / 4));
timer.fout_cb().set("timer", FUNC(am9513_device::gate1_w));
timer.out1_cb().set_nop(); // Watchdog; generates BERR/Reset
timer.out2_cb().set_inputline(m_maincpu, M68K_IRQ_6); // User timer
timer.out3_cb().set_inputline(m_maincpu, M68K_IRQ_7); // Refresh timer (2 ms)
timer.out4_cb().set(m_iouart, FUNC(upd7201_device::rxca_w));
timer.out4_cb().append(m_iouart, FUNC(upd7201_device::txca_w));
timer.out5_cb().set(m_iouart, FUNC(upd7201_device::rxcb_w));
timer.out5_cb().append(m_iouart, FUNC(upd7201_device::txcb_w));
UPD7201(config, m_iouart, 16_MHz_XTAL / 4);
m_iouart->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
m_iouart->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
m_iouart->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
m_iouart->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
m_iouart->out_dtrb_callback().set("rs232b", FUNC(rs232_port_device::write_dtr));
m_iouart->out_rtsb_callback().set("rs232b", FUNC(rs232_port_device::write_rts));
m_iouart->out_int_callback().set_inputline(m_maincpu, M68K_IRQ_5);
rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
rs232a.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxa_w));
rs232a.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsa_w));
rs232a.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcda_w));
rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
rs232b.rxd_handler().set(m_iouart, FUNC(upd7201_device::rxb_w));
rs232b.cts_handler().set(m_iouart, FUNC(upd7201_device::ctsb_w));
rs232b.dcd_handler().set(m_iouart, FUNC(upd7201_device::dcdb_w));
// slot 1 is physically located at top, only slots 1-3 have P2
MULTIBUS_SLOT(config, "slot:1", sun1_cards, nullptr, false); // memory expansion 2
MULTIBUS_SLOT(config, "slot:2", sun1_cards, nullptr, false); // memory expansion 1
MULTIBUS_SLOT(config, "slot:3", sun1_cards, "sun1cpu", false); // processor
MULTIBUS_SLOT(config, "slot:4", sun1_cards, nullptr, false); // bus master 1
MULTIBUS_SLOT(config, "slot:5", sun1_cards, nullptr, false); // bus master 2
MULTIBUS_SLOT(config, "slot:6", sun1_cards, nullptr, false); // multibus memory, optional
MULTIBUS_SLOT(config, "slot:7", sun1_cards, nullptr, false); // graphics
}
void sun1_state::cyb(machine_config &config)
{
sun1(config);
m_maincpu->set_addrmap(AS_PROGRAM, &sun1_state::cyb_mem);
}
/* ROM definition */
ROM_START( sun1 )
ROM_REGION16_BE(0x4000, "monitor", 0)
ROM_DEFAULT_BIOS("1.0")
ROM_SYSTEM_BIOS(0, "1.0", "Sun Monitor 1.0")
ROMX_LOAD("v10.8.bin", 0x0000, 0x2000, CRC(3528a0f8) SHA1(be437dd93d1a44eccffa6f5e05935119482beab0), ROM_SKIP(1) | ROM_BIOS(0))
ROMX_LOAD("v10.0.bin", 0x0001, 0x2000, CRC(1ad4c52a) SHA1(4bc1a19e8f202378d5d7baa8b95319275c040a6d), ROM_SKIP(1) | ROM_BIOS(0))
ROM_SYSTEM_BIOS(1, "diag", "Interactive Tests")
ROMX_LOAD("8mhzdiag.8.bin", 0x0000, 0x2000, CRC(808a549e) SHA1(d2aba014a5507c1538f2c1a73e1d2524f28034f4), ROM_SKIP(1) | ROM_BIOS(1))
ROMX_LOAD("8mhzdiag.0.bin", 0x0001, 0x2000, CRC(7a92d506) SHA1(5df3800f7083293fc01bb6a7e7538ad425bbebfb), ROM_SKIP(1) | ROM_BIOS(1))
ROM_REGION(0x0a40, "gfx", 0)
ROM_LOAD("gfxu605.g4.bin", 0x0000, 0x0200, CRC(274b7b3d) SHA1(40d8be2cfcbd03512a05925991bb5030d5d4b5e9))
ROM_LOAD("gfxu308.g21.bin", 0x0200, 0x0200, CRC(35a6eed8) SHA1(25cb2dd8e5343cd7927c3045eb4cb96dc9935a37))
ROM_LOAD("gfxu108.g20.bin", 0x0400, 0x0200, CRC(ecee335e) SHA1(5f4d32dc918af15872cd6e700a04720caeb6c657))
ROM_LOAD("gfxu105.g0.bin", 0x0600, 0x0200, CRC(8e1a24b3) SHA1(dad2821c3a3137ad69e78b6fc29ab582e5d78646))
ROM_LOAD("gfxu104.g1.bin", 0x0800, 0x0200, CRC(86f7a483) SHA1(8eb3778f5497741cd4345e81ff1a903c9a63c8bb))
ROM_LOAD("gfxu307.g61.bin", 0x0a00, 0x0020, CRC(b190f25d) SHA1(80fbdc843f1eb68a2d3713499f04d99dab88ce83))
ROM_LOAD("gfxu107.g60.bin", 0x0a20, 0x0020, CRC(425d3a98) SHA1(9ae4ce3761c2f995d00bed8d752c55224d274062))
ROM_REGION(0x0240, "cpu", 0)
ROM_LOAD("cpuu503.p2.bin", 0x0000, 0x0200, CRC(12d9a6be) SHA1(fca99f9c5afc630ac67cbd4e5ba4e5242b826848))
ROM_LOAD("cpuu602.p1.bin", 0x0200, 0x0020, CRC(ee1e5a14) SHA1(0d3346cb3b647fa2475bd7b4fa36ea6ecfdaf805))
ROM_LOAD("cpuu502.p0.bin", 0x0220, 0x0020, CRC(20eb1183) SHA1(9b268792b28d858d6b6a1b6c4148af88a8d6b735))
ROM_END
ROM_START( multibox )
ROM_REGION16_BE(0x4000, "monitor", 0)
// "Sun Network Monitor, Version 0.10"
ROM_LOAD16_BYTE("cybu103-sun10v.bin", 0x0000, 0x2000, CRC(32e53691) SHA1(5b0a3c4b5352d4c19067d9eb4db6cd1f76427892))
ROM_LOAD16_BYTE("cybu101-sun10v.bin", 0x0001, 0x2000, CRC(900b725c) SHA1(90343de9e64ff8227c7e58c9d4df02783ecf5a76))
ROM_REGION16_BE(0x4000, "cyb", 0)
// "CYB Monitor - Version 2.1 6/23/84"
ROM_LOAD16_BYTE("cybu104-autov.bin", 0x0000, 0x2000, CRC(46570c0e) SHA1(42ad96ffb7cb0e2d9c0aa0ace283ff646eaa4584))
ROM_LOAD16_BYTE("cybu102-autov.bin", 0x0001, 0x2000, CRC(6198c5f6) SHA1(7d880395c4da03ac5130905c850ad638cd4b86e9))
ROM_START(sun1)
ROM_END
} // anonymous namespace
/* Driver */
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP( 1982, sun1, 0, 0, sun1, sun1, sun1_state, empty_init, "Sun Microsystems", "Sun-1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1984, multibox, 0, 0, cyb, sun1, sun1_state, empty_init, "CYB Systems", "Multibox", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP( 1982, sun1, 0, 0, sun1, 0, sun1_state, empty_init, "Sun Microsystems", "Sun-1", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )

408
src/mame/sun/sun1_cpu.cpp Normal file
View File

@ -0,0 +1,408 @@
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Patrick Mackinlay
/*
* This device emulates the Multibus Sun-1 CPU board, which consists of the
* following key components:
*
* - 68000 CPU @ 8/10MHz
* - Sun-1 memory management unit
* - 256KiB RAM + up to 32KiB EPROM
* - Am9513 system timing controller
* - i8247/μPD7201 dual UART
*
* Sources:
* - Sun-1 System Reference Manual, Draft Version 1.0, July 27, 1982, Sun Microsystems, Inc.
* - Sun 68000 Board User's Manual, Revision B, February 1983, Sun Microsystems Inc.
*
* TODO:
* - second generation Sun-1.5 variant
* - variants from other companies
* - keyboard, mouse
* - jumpers
*/
/*
* WIP:
* - bios 1: "t" enters CYB monitor
* - bios 6: top 3 bits of parallel register control tests
*/
#include "emu.h"
#include "sun1_cpu.h"
#include "bus/rs232/rs232.h"
#include "machine/clock.h"
#define LOG_DOG (1U << 2)
//#define VERBOSE (LOG_GENERAL|LOG_DOG)
#include "logmacro.h"
enum irq_mask : u8
{
WATCHDOG = 0x80,
};
DEFINE_DEVICE_TYPE(MULTIBUS_SUN1CPU, sun1cpu_device, "sun1cpu", "Sun-1 CPU")
sun1cpu_device::sun1cpu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
: device_t(mconfig, MULTIBUS_SUN1CPU, tag, owner, clock)
, device_multibus_interface(mconfig, *this)
, m_cpu(*this, "cpu")
, m_mmu(*this, "mmu")
, m_duart(*this, "duart")
, m_stc(*this, "stc")
, m_boot(*this, "boot")
{
}
ROM_START(sun1)
/*
* These different firmware versions expect slightly different hardware/timer
* configurations and probably come from CPU boards that were made or modified
* by other companies. The standard configuration according to the available
* documentation should be:
*
* - stc @ 5MHz
* - counter 1: watchdog (@2.98ms)
* - counter 2: user irq 6
* - cuonter 3: refresh irq 7 (@2ms)
* - counter 4: uarta (16x baud)
* - counter 5: uartb (16x baud)
*
* Most of these firmware versions do not configure timer 1 as a watchdog, and
* probably rely on a dedicated watchdog timer circuit like that described in
* the VSI schematics instead.
*
* The "aj" firmware is quite different: it does not use the STC for the DUART
* clocks, and uses counter 5 for refresh.
*
*/
ROM_SYSTEM_BIOS(0, "sun1", "Sun Monitor, Version 1.0") // osc=16, div=4
ROM_SYSTEM_BIOS(1, "cyb", "Sun Network Monitor, Version 0.10 (CYB)") // osc=16, div=4
ROM_SYSTEM_BIOS(2, "test", "Interactive Tests") // osc=16, div=2
ROM_SYSTEM_BIOS(3, "fti10_11", "Sun Monitor, Version 1.1 (FTI10)") // osc=19.6, div=8
ROM_SYSTEM_BIOS(4, "fti10_10", "Sun Monitor, Version 1.0 (FTI10)") // osc=19.6, div=8
ROM_SYSTEM_BIOS(5, "smi10", "Sun Monitor, Version 1.1 (SMI10)") // osc=19.6, div=4, watchdog=1
ROM_SYSTEM_BIOS(6, "aj", "Sun-1 Workstation Monitor; PROM 1&3: Rev AJ; PROM 2&4: Rev AJ") // osc=20, div=4, watchdog=1
/*
* The prom0 region at 0x02'0000 holds the primary firmware, and is also
* readable from address 0x00'0000 until boot mode is disabled.
*/
ROM_REGION16_BE(0x4000, "prom0", ROMREGION_ERASEFF)
// stc @ 4MHz (16MHz / 4)
// counter 3 mode 0b22 load=8000 == 2ms
// counter 4 mode 0b22 load=13
// counter 5 mode 0b22 load=13
ROMX_LOAD("v10.8.u103", 0x0000, 0x2000, CRC(3528a0f8) SHA1(be437dd93d1a44eccffa6f5e05935119482beab0), ROM_SKIP(1) | ROM_BIOS(0))
ROMX_LOAD("v10.0.u101", 0x0001, 0x2000, CRC(1ad4c52a) SHA1(4bc1a19e8f202378d5d7baa8b95319275c040a6d), ROM_SKIP(1) | ROM_BIOS(0))
// Sun Network Monitor, Version 0.10
// stc @ 4MHz (16MHz / 4)
// counter 3 mode 0b22 load=8000 == 2ms
// counter 4 mode 0b22 load=13
// counter 5 mode 0b22 load=13
ROMX_LOAD("cybu103-sun10v.u103", 0x0000, 0x2000, CRC(32e53691) SHA1(5b0a3c4b5352d4c19067d9eb4db6cd1f76427892), ROM_SKIP(1) | ROM_BIOS(1))
ROMX_LOAD("cybu101-sun10v.u101", 0x0001, 0x2000, CRC(900b725c) SHA1(90343de9e64ff8227c7e58c9d4df02783ecf5a76), ROM_SKIP(1) | ROM_BIOS(1))
// Interactive Tests
// stc @ 8MHz (16MHz / 2)
// counter 3 mode 0b22 load=16000 == 2ms
// counter 4 mode 0b22 load=26
// counter 5 mode 0b22 load=26
ROMX_LOAD("8mhzdiag.8.u103", 0x0000, 0x2000, CRC(808a549e) SHA1(d2aba014a5507c1538f2c1a73e1d2524f28034f4), ROM_SKIP(1) | ROM_BIOS(2))
ROMX_LOAD("8mhzdiag.0.u101", 0x0001, 0x2000, CRC(7a92d506) SHA1(5df3800f7083293fc01bb6a7e7538ad425bbebfb), ROM_SKIP(1) | ROM_BIOS(2))
// Sun Monitor, Version 1.1
// stc @ 2.4576MHz (19.6MHz / 8)
// counter 3 mode 0b22 load=4915 == ~2ms
// counter 4 mode 0b22 load=8
// counter 5 mode 0b22 load=8
ROMX_LOAD("fti10_11.u103", 0x0000, 0x1000, CRC(1a5befa5) SHA1(cd7fc4c88a76ae393ada6a3f427993fbacb8a9c6), ROM_SKIP(1) | ROM_BIOS(3))
ROMX_LOAD("fti10_11.u101", 0x0001, 0x1000, CRC(ac733618) SHA1(baff471248b355d761a5dab375fc88d861cb6853), ROM_SKIP(1) | ROM_BIOS(3))
// Sun Monitor, Version 1.0
// stc @ 2.4576MHz (19.6MHz / 8)
// counter 3 mode 0b22 load=4915 == ~2ms
// counter 4 mode 0b22 load=8
// counter 5 mode 0b22 load=8
ROMX_LOAD("fti10_10.u103", 0x0000, 0x1000, CRC(fb5e599b) SHA1(5ab1ac61034aac956fd6e39b964996e0f9d0da97), ROM_SKIP(1) | ROM_BIOS(4))
ROMX_LOAD("fti10_10.u101", 0x0001, 0x1000, CRC(e7371fd6) SHA1(5a377a4363b59c5508f941c356d8827594a72dc8), ROM_SKIP(1) | ROM_BIOS(4))
// Sun Monitor, Version 1.1
// stc @ 4.9152MHz (19.6MHz / 4)
// counter 1 mode 0621 load=7568 == ~3ms
// counter 3 mode 0622 load=4915 == ~2ms
// counter 4 mode 0b22 load=16
// counter 5 mode 0b22 load=16
ROMX_LOAD("smi10_11.u103", 0x0000, 0x1000, CRC(89e1a6ed) SHA1(2dfcba08fb6e4582e05b803c4507419c25dbdaa8), ROM_SKIP(1) | ROM_BIOS(5))
ROMX_LOAD("smi10_11.u101", 0x0001, 0x1000, CRC(432db053) SHA1(99a54a653f18b914ae15f230a35ac55c5d076a4c), ROM_SKIP(1) | ROM_BIOS(5))
// PROM 1&3: Rev AJ
// stc @ 5MHz (20MHz / 4)
// counter 1 mode 0621 load=7450 == 2.98ms
// counter 5 mode 0622 load=5000 == 2ms
// uarts fixed @ 9600
ROMX_LOAD("sun1c_1h.u103", 0x0000, 0x2000, CRC(4c58de6c) SHA1(f417a0a013d4ca3aaf565b0e7a88dcf3da1347c1), ROM_SKIP(1) | ROM_BIOS(6))
ROMX_LOAD("sun1c_1l.u101", 0x0001, 0x2000, CRC(41c47355) SHA1(a38df286ab8ba9a462862ab1cc9fa28b79fb9949), ROM_SKIP(1) | ROM_BIOS(6))
/*
* The prom1 region at 0x04'0000 holds secondary/optional diagnostic or
* boot firmware.
*/
ROM_REGION16_BE(0x4000, "prom1", ROMREGION_ERASEFF)
// CYB Monitor - Version 2.1 6/23/84
ROMX_LOAD("cybu104-autov.u104", 0x0000, 0x2000, CRC(46570c0e) SHA1(42ad96ffb7cb0e2d9c0aa0ace283ff646eaa4584), ROM_SKIP(1) | ROM_BIOS(1))
ROMX_LOAD("cybu102-autov.u102", 0x0001, 0x2000, CRC(6198c5f6) SHA1(7d880395c4da03ac5130905c850ad638cd4b86e9), ROM_SKIP(1) | ROM_BIOS(1))
// PROM 2&4: Rev AJ
ROMX_LOAD("sun1c_2h.u104", 0x0000, 0x2000, CRC(b5cbcb2b) SHA1(8f906dbdc9b2af90bc8f942eebe7006be0ff4e86), ROM_SKIP(1) | ROM_BIOS(6))
ROMX_LOAD("sun1c_2l.u102", 0x0001, 0x2000, CRC(dc82578b) SHA1(65bf18a8a40171ec08137a0c446da3b272b9ccbc), ROM_SKIP(1) | ROM_BIOS(6))
// TODO: move the following firmware to a graphics card
ROM_REGION(0x10000, "gfx", ROMREGION_ERASEFF)
ROM_LOAD("gfxu605.g4.bin", 0x0000, 0x0200, CRC(274b7b3d) SHA1(40d8be2cfcbd03512a05925991bb5030d5d4b5e9))
ROM_LOAD("gfxu308.g21.bin", 0x0200, 0x0200, CRC(35a6eed8) SHA1(25cb2dd8e5343cd7927c3045eb4cb96dc9935a37))
ROM_LOAD("gfxu108.g20.bin", 0x0400, 0x0200, CRC(ecee335e) SHA1(5f4d32dc918af15872cd6e700a04720caeb6c657))
ROM_LOAD("gfxu105.g0.bin", 0x0600, 0x0200, CRC(8e1a24b3) SHA1(dad2821c3a3137ad69e78b6fc29ab582e5d78646))
ROM_LOAD("gfxu104.g1.bin", 0x0800, 0x0200, CRC(86f7a483) SHA1(8eb3778f5497741cd4345e81ff1a903c9a63c8bb))
ROM_LOAD("gfxu307.g61.bin", 0x0a00, 0x0020, CRC(b190f25d) SHA1(80fbdc843f1eb68a2d3713499f04d99dab88ce83))
ROM_LOAD("gfxu107.g60.bin", 0x0a20, 0x0020, CRC(425d3a98) SHA1(9ae4ce3761c2f995d00bed8d752c55224d274062))
ROM_REGION(0x10000, "cpu", ROMREGION_ERASEFF)
ROM_LOAD("cpuu503.p2.bin", 0x0000, 0x0200, CRC(12d9a6be) SHA1(fca99f9c5afc630ac67cbd4e5ba4e5242b826848))
ROM_LOAD("cpuu602.p1.bin", 0x0200, 0x0020, CRC(ee1e5a14) SHA1(0d3346cb3b647fa2475bd7b4fa36ea6ecfdaf805))
ROM_LOAD("cpuu502.p0.bin", 0x0220, 0x0020, CRC(20eb1183) SHA1(9b268792b28d858d6b6a1b6c4148af88a8d6b735))
ROM_END
const tiny_rom_entry *sun1cpu_device::device_rom_region() const
{
return ROM_NAME(sun1);
}
static INPUT_PORTS_START(sun1)
INPUT_PORTS_END
ioport_constructor sun1cpu_device::device_input_ports() const
{
return INPUT_PORTS_NAME(sun1);
}
void sun1cpu_device::device_start()
{
save_item(NAME(m_irq));
save_item(NAME(m_watchdog));
save_item(NAME(m_parity));
m_cpu->space(AS_PROGRAM).specific(m_cpu_mem);
m_cpu->space(m68000_device::AS_CPU_SPACE).specific(m_cpu_spc);
m_irq = 0;
}
void sun1cpu_device::device_reset()
{
// HACK: configure oscillator, divisor and watchdog type to match selected
// firmware until more accurate board type/configuration can be determined
XTAL oscillator = 16_MHz_XTAL;
unsigned divisor = 4;
m_watchdog = false;
switch (system_bios())
{
case 1:
case 2:
break;
case 3:
divisor = 2;
break;
case 4:
case 5:
oscillator = 19.6608_MHz_XTAL;
divisor = 8;
break;
case 6:
oscillator = 19.6608_MHz_XTAL;
m_watchdog = true;
break;
case 7:
oscillator = 20_MHz_XTAL;
m_watchdog = true;
break;
}
// configure cpu, stc and duart clocks
m_cpu->set_clock(oscillator / 2);
m_stc->set_clock(oscillator / divisor);
m_duart->set_clock(oscillator / divisor);
m_parity = false;
if (!boot())
{
m_cpu->set_current_mmu(this);
// disable interrupts
for (unsigned i = 0; i < 7; i++)
if (BIT(m_irq, i))
m_cpu->set_input_line(M68K_IRQ_1 + i, CLEAR_LINE);
m_boot.select(0);
}
}
void sun1cpu_device::device_add_mconfig(machine_config &config)
{
M68000(config, m_cpu, 0);
m_cpu->set_current_mmu(this);
m_cpu->set_addrmap(AS_PROGRAM, &sun1cpu_device::cpu_mem);
// route multibus interrupts 1..4 to cpu
int_callback<1>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_1>)).invert();
int_callback<2>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_2>)).invert();
int_callback<3>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_3>)).invert();
int_callback<4>().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_4>)).invert();
SUN1MMU(config, m_mmu);
m_mmu->error().set(
[this](int state)
{
// check multibus access pending
if (state == sun1mmu_device::MMU_DEFER)
{
if (!m_watchdog || (m_irq & WATCHDOG))
{
LOGMASKED(LOG_DOG, "watchdog bus error\n");
m_cpu->trigger_bus_error();
m_mmu->reset();
}
else
m_cpu->defer_access();
}
else
m_cpu->trigger_bus_error();
});
// default configuration (1=watchdog, 2=user, 3=refresh, 4=uarta, 5=uartb)
AM9513(config, m_stc, 0);
m_stc->fout_cb().set(m_stc, FUNC(am9513_device::gate1_w));
m_stc->out1_cb().set(FUNC(sun1cpu_device::watchdog_w));
m_stc->out2_cb().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_6>));
m_stc->out3_cb().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_7>));
m_stc->out4_cb().set(m_duart, FUNC(upd7201_device::rxca_w));
m_stc->out4_cb().append(m_duart, FUNC(upd7201_device::txca_w));
m_stc->out5_cb().set(m_duart, FUNC(upd7201_device::rxcb_w));
m_stc->out5_cb().append(m_duart, FUNC(upd7201_device::txcb_w));
if (false)
{
// TODO: "aj" firmware has refresh on counter 5, and fixed-speed uarts
m_stc->out3_cb().set_nop();
m_stc->out4_cb().set_nop();
m_stc->out5_cb().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_7>));
// assume duart rxc/txc driven at 9600baud (x16 clock)
CLOCK(config, "duart_clock", 9600 * 16).signal_handler().set(
[this](int state)
{
m_duart->txca_w(state);
m_duart->rxca_w(state);
m_duart->txcb_w(state);
m_duart->rxcb_w(state);
});
}
// port A: txd, rxd, rts, cts, dsr, dtr
// port B: txd, rxd
UPD7201(config, m_duart, 0);
m_duart->out_txda_callback().set("rs232a", FUNC(rs232_port_device::write_txd));
m_duart->out_dtra_callback().set("rs232a", FUNC(rs232_port_device::write_dtr));
m_duart->out_rtsa_callback().set("rs232a", FUNC(rs232_port_device::write_rts));
m_duart->out_txdb_callback().set("rs232b", FUNC(rs232_port_device::write_txd));
m_duart->out_int_callback().set(FUNC(sun1cpu_device::irq_w<M68K_IRQ_5>));
rs232_port_device &rs232a(RS232_PORT(config, "rs232a", default_rs232_devices, "terminal"));
rs232a.rxd_handler().set(m_duart, FUNC(upd7201_device::rxa_w));
rs232a.cts_handler().set(m_duart, FUNC(upd7201_device::ctsa_w));
rs232a.dcd_handler().set(m_duart, FUNC(upd7201_device::dcda_w));
rs232_port_device &rs232b(RS232_PORT(config, "rs232b", default_rs232_devices, nullptr));
rs232b.rxd_handler().set(m_duart, FUNC(upd7201_device::rxb_w));
}
void sun1cpu_device::device_config_complete()
{
m_mmu.lookup()->set_space<0>(m_cpu, AS_PROGRAM);
m_mmu.lookup()->set_space<1>(m_cpu, m68000_device::AS_CPU_SPACE);
m_mmu.lookup()->set_space<2>(m_bus, AS_PROGRAM);
m_mmu.lookup()->set_space<3>(m_bus, AS_IO);
}
void sun1cpu_device::cpu_mem(address_map &map)
{
map(0x00'0000, 0x03'ffff).ram();
map(0x00'0000, 0x1f'ffff).view(m_boot);
m_boot[0](0x00'0000, 0x00'3fff).mirror(0x03'c000).rom().region("prom0", 0);
map(0x20'0000, 0x20'3fff).mirror(0x03'c000).rom().region("prom0", 0).w(FUNC(sun1cpu_device::prom0_w));
map(0x40'0000, 0x40'3fff).mirror(0x03'c000).rom().region("prom1", 0);
map(0x60'0000, 0x60'0007).mirror(0x1ffff8).rw(m_duart, FUNC(upd7201_device::ba_cd_r), FUNC(upd7201_device::ba_cd_w)).umask16(0xff00);
map(0x80'0000, 0x80'0003).mirror(0x1ffffc).rw(m_stc, FUNC(am9513_device::read16), FUNC(am9513_device::write16));
map(0xa0'0000, 0xbf'ffff).rw(m_mmu, FUNC(sun1mmu_device::page_r), FUNC(sun1mmu_device::page_w));
map(0xc0'0000, 0xdf'ffff).rw(m_mmu, FUNC(sun1mmu_device::segment_r), FUNC(sun1mmu_device::segment_w));
map(0xe0'0000, 0xe0'0001).mirror(0x1f'fffe).r(FUNC(sun1cpu_device::parallel_r));
map(0xe0'0000, 0xe0'0001).mirror(0x1f'fffe).w(m_mmu, FUNC(sun1mmu_device::context_w));
}
void sun1cpu_device::watchdog_w(int state)
{
if (state)
m_irq |= WATCHDOG;
else
m_irq &= ~WATCHDOG;
if (!BIT(m_irq, 6))
{
if (state)
{
LOGMASKED(LOG_DOG, "watchdog reset\n");
reset();
}
}
}
template <unsigned N> void sun1cpu_device::irq_w(int state)
{
if (state)
m_irq |= 1U << (N - M68K_IRQ_1);
else
m_irq &= ~(1U << (N - M68K_IRQ_1));
if (!boot())
m_cpu->set_input_line(N, state);
}
void sun1cpu_device::prom0_w(u16 data)
{
m_parity = BIT(data, 0);
if (boot())
{
LOG("boot mode disabled (%s)\n", machine().describe_context());
m_cpu->set_current_mmu(m_mmu);
// enable interrupts
for (unsigned i = 0; i < 7; i++)
if (BIT(m_irq, i))
m_cpu->set_input_line(M68K_IRQ_1 + i, ASSERT_LINE);
m_boot.disable();
}
}

69
src/mame/sun/sun1_cpu.h Normal file
View File

@ -0,0 +1,69 @@
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Patrick Mackinlay
#ifndef MAME_SUN_SUN1_CPU_H
#define MAME_SUN_SUN1_CPU_H
#pragma once
#include "sun1_mmu.h"
#include "bus/multibus/multibus.h"
#include "cpu/m68000/m68000.h"
#include "machine/am9513.h"
#include "machine/z80sio.h"
class sun1cpu_device
: public device_t
, public device_multibus_interface
, public m68000_device::mmu
{
public:
sun1cpu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
protected:
// device_t implementation
virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
virtual void device_config_complete() override ATTR_COLD;
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
void cpu_mem(address_map &map);
void prom0_w(u16 data);
u16 parallel_r() { return 0x1fff; }
void watchdog_w(int state);
template <unsigned N> void irq_w(int state);
// m68000_device::mmu boot mode implementation
virtual u16 read_program(offs_t logical, u16 mem_mask) override { return m_cpu_mem.read_word(logical, mem_mask); }
virtual void write_program(offs_t logical, u16 data, u16 mem_mask) override { m_cpu_mem.write_word(logical, data, mem_mask); }
virtual u16 read_data(offs_t logical, u16 mem_mask) override { return m_cpu_mem.read_word(logical, mem_mask); }
virtual void write_data(offs_t logical, u16 data, u16 mem_mask) override { m_cpu_mem.write_word(logical, data, mem_mask); }
virtual u16 read_cpu(offs_t logical, u16 mem_mask) override { return m_cpu_spc.read_word(logical, mem_mask); }
virtual void set_super(bool super) override {}
bool boot() const { return m_boot.entry().has_value(); }
private:
required_device<m68000_device> m_cpu;
required_device<sun1mmu_device> m_mmu;
required_device<upd7201_device> m_duart;
required_device<am9513_device> m_stc;
memory_view m_boot;
memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_mem;
memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_spc;
u8 m_irq;
bool m_watchdog;
bool m_parity;
};
DECLARE_DEVICE_TYPE(MULTIBUS_SUN1CPU, sun1cpu_device)
#endif // MAME_SUN_SUN1_CPU_H

309
src/mame/sun/sun1_mmu.cpp Normal file
View File

@ -0,0 +1,309 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
/*
* This device emulates the Sun-1 memory management unit.
*
* Sources:
* - Sun-1 System Reference Manual, Draft Version 1.0, July 27, 1982, Sun Microsystems, Inc.
* - Sun 68000 Board User's Manual, Revision B, February 1983, Sun Microsystems Inc.
*
* TODO:
* - everything
*/
#include "emu.h"
#include "sun1_mmu.h"
#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"
enum segment_mask : u16
{
SEGMENT_PPTR = 0x003f,
SEGMENT_PROT = 0x0f00,
SEGMENT_CTXT = 0xf000,
};
enum page_mask : u16
{
PAGE_ADDR = 0x0fff,
PAGE_TYPE = 0x3000, // 0=on-board memory, 1=nonexistent, 2=multibus memory, 3=multibus i/o
PAGE_MOD = 0x4000,
PAGE_ACC = 0x8000,
};
enum mode_mask : unsigned
{
P_R = 0x04, // read
P_W = 0x02, // write
P_X = 0x01, // execute
P_RX = P_R | P_X,
P_RW = P_R | P_W,
P_RWX = P_R | P_W | P_X,
};
DEFINE_DEVICE_TYPE(SUN1MMU, sun1mmu_device, "sun1mmu", "Sun-1 MMU")
sun1mmu_device::sun1mmu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
: device_t(mconfig, SUN1MMU, tag, owner, clock)
, m_space{
{*this, finder_base::DUMMY_TAG, 0},
{*this, finder_base::DUMMY_TAG, 1},
{*this, finder_base::DUMMY_TAG, 2},
{*this, finder_base::DUMMY_TAG, 3},
}
, m_error(*this)
, m_context(0)
, m_segment{}
, m_page{}
, m_super(true)
{
}
void sun1mmu_device::device_start()
{
save_item(NAME(m_context));
save_item(NAME(m_segment));
save_item(NAME(m_page));
save_item(NAME(m_stall));
save_item(NAME(m_super));
m_space[0]->specific(m_cpu_mem);
m_space[1]->specific(m_cpu_spc);
m_space[2]->specific(m_bus_mem);
m_space[3]->specific(m_bus_pio);
}
void sun1mmu_device::device_reset()
{
m_stall = false;
}
static bool access(bool const super, unsigned const code, unsigned const mode)
{
static constexpr unsigned protection[2][16] =
{
// user mode
{
0 , 0 , 0 , 0 , 0 , 0 , P_R , P_R ,
P_RW , P_RW , P_RX , P_RWX, P_RX , P_RX , P_X , P_RWX,
},
// supervisor mode
{
0 , P_X , P_R , P_RX , P_RW , P_RWX, P_R , P_RW ,
P_R , P_RW , P_RW , P_RW , P_RX , P_RWX, P_RWX, P_RWX,
},
};
return protection[super][code] & mode;
}
void sun1mmu_device::context_w(u16 data)
{
LOG("context 0x%04x (%s)\n", data, machine().describe_context());
m_context = data & 0xf000U;
}
u16 sun1mmu_device::segment_r(offs_t offset)
{
return m_context | m_segment[m_context >> 12][BIT(offset, 14, 6)];
}
void sun1mmu_device::segment_w(offs_t offset, u16 data, u16 mem_mask)
{
LOG("segment[0x%x][0x%02x] 0x%04x (%s)\n", m_context >> 12, BIT(offset, 14, 6), data, machine().describe_context());
m_segment[m_context >> 12][BIT(offset, 14, 6)] = data & 0x0fffU;
}
u16 sun1mmu_device::page_r(offs_t offset)
{
u16 const segment = m_segment[m_context >> 12][BIT(offset, 14, 6)];
return m_page[(segment & SEGMENT_PPTR) << 4 | BIT(offset, 10, 4)];
}
void sun1mmu_device::page_w(offs_t offset, u16 data, u16 mem_mask)
{
u16 const segment = m_segment[m_context >> 12][BIT(offset, 14, 6)];
LOG("page[0x%03x] 0x%04x (%s)\n", (segment & SEGMENT_PPTR) << 4 | BIT(offset, 10, 4), data, machine().describe_context());
m_page[(segment & SEGMENT_PPTR) << 4 | BIT(offset, 10, 4)] = data;
}
std::optional<std::pair<unsigned, offs_t>> sun1mmu_device::translate(offs_t const logical, unsigned const mode)
{
// check for mapped address
if (logical < 0x20'0000)
{
u16 const segment = m_segment[m_context >> 12][BIT(logical, 15, 6)];
// check segment map error
if (access(m_super, BIT(segment, 8, 4), mode) || machine().side_effects_disabled())
{
u16 &page = m_page[(segment & SEGMENT_PPTR) << 4 | BIT(logical, 11, 4)];
// update reference and modify bits
if (!machine().side_effects_disabled())
{
if (mode & P_W)
page |= PAGE_ACC | PAGE_MOD;
else
page |= PAGE_ACC;
}
return std::pair<unsigned, offs_t>(BIT(page, 12, 2), (page & PAGE_ADDR) << 11 | BIT(logical, 0, 11));
}
else
LOG("segment map error 0x%08x context %d segment 0x%04x mode %c (%s)\n",
logical, m_context, segment, (mode == P_R) ? 'R' : (mode == P_W) ? 'W' : 'X', machine().describe_context());
}
else if (m_super || machine().side_effects_disabled())
// unmapped supervisor access
return std::pair<unsigned, offs_t>(0, logical);
else
LOG("system space error 0x%08x (%s)\n", logical, machine().describe_context());
// protection error
return std::nullopt;
}
template <bool Execute> u16 sun1mmu_device::mmu_read(offs_t logical, u16 mem_mask)
{
u16 data = 0;
if (m_stall)
{
m_error(MMU_DEFER);
return data;
}
auto const t = translate(logical, Execute ? P_X : P_R);
if (t.has_value())
{
auto const [type, physical] = t.value();
u16 flags = 0;
switch (type)
{
case 0:
// on-board memory
data = m_cpu_mem.read_word(physical, mem_mask);
break;
case 1:
// nonexistent
LOG("nonexistent_r 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
m_error(MMU_ERROR);
break;
case 2:
// multibus memory
std::tie(data, flags) = m_bus_mem.read_word_flags(physical, mem_mask);
if (flags)
{
LOG("multibus_r mem 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
m_stall = true;
m_error(MMU_DEFER);
}
break;
case 3:
// mutibus i/o
std::tie(data, flags) = m_bus_pio.read_word_flags(physical, mem_mask);
if (flags)
{
LOG("multibus_r i/o 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
m_stall = true;
m_error(MMU_DEFER);
}
break;
}
}
else
m_error(MMU_ERROR);
return data;
}
void sun1mmu_device::mmu_write(offs_t logical, u16 data, u16 mem_mask)
{
if (m_stall)
{
m_error(MMU_DEFER);
return;
}
auto const t = translate(logical, P_W);
if (t.has_value())
{
auto const [type, physical] = t.value();
switch (type)
{
case 0:
// on-board memory
m_cpu_mem.write_word(physical, data, mem_mask);
break;
case 1:
// nonexistent
LOG("nonexistent write 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
m_error(MMU_ERROR);
break;
case 2:
// multibus memory
if (m_bus_mem.write_word_flags(physical, data, mem_mask))
{
LOG("multibus_w mem 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
m_stall = true;
m_error(MMU_DEFER);
}
break;
case 3:
// mutibus i/o
if (m_bus_pio.write_word_flags(physical, data, mem_mask))
{
LOG("multibus_w i/o 0x%08x translated 0x%08x (%s)\n", logical, physical, machine().describe_context());
m_stall = true;
m_error(MMU_DEFER);
}
break;
}
}
else
m_error(MMU_ERROR);
}
u16 sun1mmu_device::read_program(offs_t logical, u16 mem_mask)
{
return mmu_read<true>(logical, mem_mask);
}
void sun1mmu_device::write_program(offs_t logical, u16 data, u16 mem_mask)
{
mmu_write(logical, data, mem_mask);
}
u16 sun1mmu_device::read_data(offs_t logical, u16 mem_mask)
{
return mmu_read<false>(logical, mem_mask);
}
void sun1mmu_device::write_data(offs_t logical, u16 data, u16 mem_mask)
{
mmu_write(logical, data, mem_mask);
}
u16 sun1mmu_device::read_cpu(offs_t logical, u16 mem_mask)
{
return m_cpu_spc.read_word(logical, mem_mask);
}
void sun1mmu_device::set_super(bool super)
{
m_super = super;
}

73
src/mame/sun/sun1_mmu.h Normal file
View File

@ -0,0 +1,73 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
#ifndef MAME_SUN_SUN1_MMU_H
#define MAME_SUN_SUN1_MMU_H
#pragma once
#include "cpu/m68000/m68000.h"
class sun1mmu_device
: public device_t
, public m68000_device::mmu
{
public:
template <unsigned N, typename T> void set_space(T &&tag, int spacenum) { m_space[N].set_tag(std::forward<T>(tag), spacenum); }
template <unsigned N> void set_space(device_t &base, char const *tag, int spacenum) { m_space[N].set_tag(base, tag, spacenum); }
enum error_type : unsigned
{
MMU_DEFER = 0, // invalid multibus access
MMU_ERROR = 1, // bus error (system space, segment protection, nonexistent page)
};
auto error() { return m_error.bind(); }
sun1mmu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0);
// register access
void context_w(u16 data);
u16 segment_r(offs_t offset);
void segment_w(offs_t offset, u16 data, u16 mem_mask);
u16 page_r(offs_t offset);
void page_w(offs_t offset, u16 data, u16 mem_mask);
protected:
// device_t implementation
virtual void device_start() override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
// m68000_device::mmu implementation
virtual u16 read_program(offs_t addr, u16 mem_mask) override;
virtual void write_program(offs_t addr, u16 data, u16 mem_mask) override;
virtual u16 read_data(offs_t addr, u16 mem_mask) override;
virtual void write_data(offs_t addr, u16 data, u16 mem_mask) override;
virtual u16 read_cpu(offs_t addr, u16 mem_mask) override;
virtual void set_super(bool super) override;
std::optional<std::pair<unsigned, offs_t>> translate(offs_t const address, unsigned const mode);
template <bool Execute> u16 mmu_read(offs_t logical, u16 mem_mask);
void mmu_write(offs_t logical, u16 data, u16 mem_mask);
private:
required_address_space m_space[4];
devcb_write_line m_error;
memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_mem;
memory_access<24, 1, 0, ENDIANNESS_BIG>::specific m_cpu_spc;
// only 20 of the possible 24 memory address lines are connected
memory_access<20, 1, 0, ENDIANNESS_LITTLE>::specific m_bus_mem;
memory_access<16, 1, 0, ENDIANNESS_LITTLE>::specific m_bus_pio;
u16 m_context;
u16 m_segment[16][64];
u16 m_page[1024];
bool m_stall;
bool m_super;
};
DECLARE_DEVICE_TYPE(SUN1MMU, sun1mmu_device)
#endif // MAME_SUN_SUN1_MMU_H