summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2022-06-13 22:08:22 +0200
committer Olivier Galibert <galibert@pobox.com>2022-06-14 12:07:55 +0200
commit58650bd6812a92dfcfd8fa5bc6e225b34d4aa479 (patch)
tree31b1d1294bac51d40e73aa9088e2f2a9aba8b011 /src/devices/machine
parent540426ea9172304ab0f3961832ef1dfafa6027e8 (diff)
at, cammu: kick upstairs too
Diffstat (limited to 'src/devices/machine')
-rw-r--r--src/devices/machine/at.cpp426
-rw-r--r--src/devices/machine/at.h107
-rw-r--r--src/devices/machine/cammu.cpp814
-rw-r--r--src/devices/machine/cammu.h665
4 files changed, 2012 insertions, 0 deletions
diff --git a/src/devices/machine/at.cpp b/src/devices/machine/at.cpp
new file mode 100644
index 00000000000..75b57c8777d
--- /dev/null
+++ b/src/devices/machine/at.cpp
@@ -0,0 +1,426 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol, Miodrag Milanovic, Carl
+/***************************************************************************
+
+ IBM AT Compatibles
+
+***************************************************************************/
+
+#include "emu.h"
+#include "machine/at.h"
+#include "cpu/i86/i286.h"
+#include "cpu/i386/i386.h"
+#include "softlist_dev.h"
+#include "speaker.h"
+
+#define LOG_PORT80 0
+
+DEFINE_DEVICE_TYPE(AT_MB, at_mb_device, "at_mb", "PC/AT Motherboard")
+
+at_mb_device::at_mb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, AT_MB, tag, owner, clock),
+ m_maincpu(*this, ":maincpu"),
+ m_isabus(*this, "isabus"),
+ m_pic8259_slave(*this, "pic8259_slave"),
+ m_dma8237_1(*this, "dma8237_1"),
+ m_dma8237_2(*this, "dma8237_2"),
+ m_pit8254(*this, "pit8254"),
+ m_speaker(*this, "speaker"),
+ m_mc146818(*this, "rtc"),
+ m_keybc(*this, "keybc")
+{
+}
+
+void at_mb_device::device_reset()
+{
+ m_at_spkrdata = 0;
+ m_pit_out2 = 1;
+ m_dma_channel = -1;
+ m_cur_eop = false;
+}
+
+void at_mb_device::device_start()
+{
+ if(!strncmp(m_maincpu->shortname(), "i80286", 6))
+ downcast<i80286_cpu_device *>(m_maincpu.target())->set_a20_callback(i80286_cpu_device::a20_cb(&at_mb_device::a20_286, this));
+}
+
+void at_mb_device::at_softlists(machine_config &config)
+{
+ /* software lists */
+ SOFTWARE_LIST(config, "pc_disk_list").set_original("ibm5150");
+ SOFTWARE_LIST(config, "at_disk_list").set_original("ibm5170");
+ SOFTWARE_LIST(config, "at_cdrom_list").set_original("ibm5170_cdrom");
+ SOFTWARE_LIST(config, "at_hdd_list").set_original("ibm5170_hdd");
+ SOFTWARE_LIST(config, "midi_disk_list").set_compatible("midi_flop");
+}
+
+void at_mb_device::device_add_mconfig(machine_config &config)
+{
+ PIT8254(config, m_pit8254);
+ m_pit8254->set_clk<0>(4772720/4); /* heartbeat IRQ */
+ m_pit8254->out_handler<0>().set("pic8259_master", FUNC(pic8259_device::ir0_w));
+ m_pit8254->set_clk<1>(4772720/4); /* dram refresh */
+ m_pit8254->set_clk<2>(4772720/4); /* pio port c pin 4, and speaker polling enough */
+ m_pit8254->out_handler<2>().set(FUNC(at_mb_device::pit8254_out2_changed));
+
+ AM9517A(config, m_dma8237_1, 14.318181_MHz_XTAL / 3);
+ m_dma8237_1->out_hreq_callback().set(m_dma8237_2, FUNC(am9517a_device::dreq0_w));
+ m_dma8237_1->out_eop_callback().set(FUNC(at_mb_device::dma8237_out_eop));
+ m_dma8237_1->in_memr_callback().set(FUNC(at_mb_device::dma_read_byte));
+ m_dma8237_1->out_memw_callback().set(FUNC(at_mb_device::dma_write_byte));
+ m_dma8237_1->in_ior_callback<0>().set(FUNC(at_mb_device::dma8237_0_dack_r));
+ m_dma8237_1->in_ior_callback<1>().set(FUNC(at_mb_device::dma8237_1_dack_r));
+ m_dma8237_1->in_ior_callback<2>().set(FUNC(at_mb_device::dma8237_2_dack_r));
+ m_dma8237_1->in_ior_callback<3>().set(FUNC(at_mb_device::dma8237_3_dack_r));
+ m_dma8237_1->out_iow_callback<0>().set(FUNC(at_mb_device::dma8237_0_dack_w));
+ m_dma8237_1->out_iow_callback<1>().set(FUNC(at_mb_device::dma8237_1_dack_w));
+ m_dma8237_1->out_iow_callback<2>().set(FUNC(at_mb_device::dma8237_2_dack_w));
+ m_dma8237_1->out_iow_callback<3>().set(FUNC(at_mb_device::dma8237_3_dack_w));
+ m_dma8237_1->out_dack_callback<0>().set(FUNC(at_mb_device::dack0_w));
+ m_dma8237_1->out_dack_callback<1>().set(FUNC(at_mb_device::dack1_w));
+ m_dma8237_1->out_dack_callback<2>().set(FUNC(at_mb_device::dack2_w));
+ m_dma8237_1->out_dack_callback<3>().set(FUNC(at_mb_device::dack3_w));
+
+ AM9517A(config, m_dma8237_2, 14.318181_MHz_XTAL / 3);
+ m_dma8237_2->out_hreq_callback().set(FUNC(at_mb_device::dma_hrq_changed));
+ m_dma8237_2->out_eop_callback().set(FUNC(at_mb_device::dma8237_2_out_eop));
+ m_dma8237_2->in_memr_callback().set(FUNC(at_mb_device::dma_read_word));
+ m_dma8237_2->out_memw_callback().set(FUNC(at_mb_device::dma_write_word));
+ m_dma8237_2->in_ior_callback<1>().set(FUNC(at_mb_device::dma8237_5_dack_r));
+ m_dma8237_2->in_ior_callback<2>().set(FUNC(at_mb_device::dma8237_6_dack_r));
+ m_dma8237_2->in_ior_callback<3>().set(FUNC(at_mb_device::dma8237_7_dack_r));
+ m_dma8237_2->out_iow_callback<1>().set(FUNC(at_mb_device::dma8237_5_dack_w));
+ m_dma8237_2->out_iow_callback<2>().set(FUNC(at_mb_device::dma8237_6_dack_w));
+ m_dma8237_2->out_iow_callback<3>().set(FUNC(at_mb_device::dma8237_7_dack_w));
+ m_dma8237_2->out_dack_callback<0>().set(FUNC(at_mb_device::dack4_w));
+ m_dma8237_2->out_dack_callback<1>().set(FUNC(at_mb_device::dack5_w));
+ m_dma8237_2->out_dack_callback<2>().set(FUNC(at_mb_device::dack6_w));
+ m_dma8237_2->out_dack_callback<3>().set(FUNC(at_mb_device::dack7_w));
+
+ pic8259_device &pic8259_master(PIC8259(config, "pic8259_master"));
+ pic8259_master.out_int_callback().set_inputline(":maincpu", 0);
+ pic8259_master.in_sp_callback().set_constant(1);
+ pic8259_master.read_slave_ack_callback().set(FUNC(at_mb_device::get_slave_ack));
+
+ PIC8259(config, m_pic8259_slave);
+ m_pic8259_slave->out_int_callback().set("pic8259_master", FUNC(pic8259_device::ir2_w));
+ m_pic8259_slave->in_sp_callback().set_constant(0);
+
+ ISA16(config, m_isabus, 0);
+ m_isabus->set_memspace(":maincpu", AS_PROGRAM);
+ m_isabus->set_iospace(":maincpu", AS_IO);
+ m_isabus->irq2_callback().set(m_pic8259_slave, FUNC(pic8259_device::ir1_w)); // in place of irq 2 on at irq 9 is used
+ m_isabus->irq3_callback().set("pic8259_master", FUNC(pic8259_device::ir3_w));
+ m_isabus->irq4_callback().set("pic8259_master", FUNC(pic8259_device::ir4_w));
+ m_isabus->irq5_callback().set("pic8259_master", FUNC(pic8259_device::ir5_w));
+ m_isabus->irq6_callback().set("pic8259_master", FUNC(pic8259_device::ir6_w));
+ m_isabus->irq7_callback().set("pic8259_master", FUNC(pic8259_device::ir7_w));
+ m_isabus->irq10_callback().set(m_pic8259_slave, FUNC(pic8259_device::ir3_w));
+ m_isabus->irq11_callback().set(m_pic8259_slave, FUNC(pic8259_device::ir4_w));
+ m_isabus->irq12_callback().set(m_pic8259_slave, FUNC(pic8259_device::ir5_w));
+ m_isabus->irq14_callback().set(m_pic8259_slave, FUNC(pic8259_device::ir6_w));
+ m_isabus->irq15_callback().set(m_pic8259_slave, FUNC(pic8259_device::ir7_w));
+ m_isabus->drq0_callback().set(m_dma8237_1, FUNC(am9517a_device::dreq0_w));
+ m_isabus->drq1_callback().set(m_dma8237_1, FUNC(am9517a_device::dreq1_w));
+ m_isabus->drq2_callback().set(m_dma8237_1, FUNC(am9517a_device::dreq2_w));
+ m_isabus->drq3_callback().set(m_dma8237_1, FUNC(am9517a_device::dreq3_w));
+ m_isabus->drq5_callback().set(m_dma8237_2, FUNC(am9517a_device::dreq1_w));
+ m_isabus->drq6_callback().set(m_dma8237_2, FUNC(am9517a_device::dreq2_w));
+ m_isabus->drq7_callback().set(m_dma8237_2, FUNC(am9517a_device::dreq3_w));
+ m_isabus->iochck_callback().set(FUNC(at_mb_device::iochck_w));
+
+ MC146818(config, m_mc146818, 32.768_kHz_XTAL);
+ m_mc146818->irq().set(m_pic8259_slave, FUNC(pic8259_device::ir0_w));
+ m_mc146818->set_century_index(0x32);
+
+ /* sound hardware */
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
+
+ at_keyboard_controller_device &keybc(AT_KEYBOARD_CONTROLLER(config, "keybc", 12_MHz_XTAL));
+ keybc.hot_res().set_inputline(":maincpu", INPUT_LINE_RESET);
+ keybc.gate_a20().set_inputline(":maincpu", INPUT_LINE_A20);
+ keybc.kbd_irq().set("pic8259_master", FUNC(pic8259_device::ir1_w));
+}
+
+
+void at_mb_device::map(address_map &map)
+{
+ map(0x0000, 0x001f).rw("dma8237_1", FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0xffff);
+ map(0x0020, 0x003f).rw("pic8259_master", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0xffff);
+ map(0x0040, 0x005f).rw(m_pit8254, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask16(0xffff);
+ map(0x0061, 0x0061).rw(FUNC(at_mb_device::portb_r), FUNC(at_mb_device::portb_w));
+ map(0x0060, 0x0060).rw("keybc", FUNC(at_keyboard_controller_device::data_r), FUNC(at_keyboard_controller_device::data_w));
+ map(0x0064, 0x0064).rw("keybc", FUNC(at_keyboard_controller_device::status_r), FUNC(at_keyboard_controller_device::command_w));
+ map(0x0070, 0x007f).r("rtc", FUNC(mc146818_device::read)).umask16(0xffff).w(FUNC(at_mb_device::write_rtc)).umask16(0xffff);
+ map(0x0080, 0x009f).rw(FUNC(at_mb_device::page8_r), FUNC(at_mb_device::page8_w)).umask16(0xffff);
+ map(0x00a0, 0x00bf).rw("pic8259_slave", FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0xffff);
+ map(0x00c0, 0x00df).rw("dma8237_2", FUNC(am9517a_device::read), FUNC(am9517a_device::write)).umask16(0x00ff);
+}
+
+/*************************************************************
+ *
+ * pic8259 configuration
+ *
+ *************************************************************/
+uint8_t at_mb_device::get_slave_ack(offs_t offset)
+{
+ if (offset==2) // IRQ = 2
+ return m_pic8259_slave->acknowledge();
+
+ return 0x00;
+}
+
+/*************************************************************************
+ *
+ * PC Speaker related
+ *
+ *************************************************************************/
+
+void at_mb_device::speaker_set_spkrdata(uint8_t data)
+{
+ m_at_spkrdata = data ? 1 : 0;
+ m_speaker->level_w(m_at_spkrdata & m_pit_out2);
+}
+
+
+
+/*************************************************************
+ *
+ * pit8254 configuration
+ *
+ *************************************************************/
+
+WRITE_LINE_MEMBER( at_mb_device::pit8254_out2_changed )
+{
+ m_pit_out2 = state ? 1 : 0;
+ m_speaker->level_w(m_at_spkrdata & m_pit_out2);
+}
+
+
+/*************************************************************************
+ *
+ * PC DMA stuff
+ *
+ *************************************************************************/
+
+uint8_t at_mb_device::page8_r(offs_t offset)
+{
+ uint8_t data = m_at_pages[offset % 0x10];
+
+ switch(offset % 8)
+ {
+ case 1:
+ data = m_dma_offset[BIT(offset, 3)][2];
+ break;
+ case 2:
+ data = m_dma_offset[BIT(offset, 3)][3];
+ break;
+ case 3:
+ data = m_dma_offset[BIT(offset, 3)][1];
+ break;
+ case 7:
+ data = m_dma_offset[BIT(offset, 3)][0];
+ break;
+ }
+ return data;
+}
+
+
+void at_mb_device::page8_w(offs_t offset, uint8_t data)
+{
+ m_at_pages[offset % 0x10] = data;
+
+ if (LOG_PORT80 && (offset == 0))
+ {
+ logerror(" at_page8_w(): Port 80h <== 0x%02x (PC=0x%08x)\n", data,
+ (unsigned) m_maincpu->pc());
+ }
+
+ switch(offset % 8)
+ {
+ case 1:
+ m_dma_offset[BIT(offset, 3)][2] = data;
+ break;
+ case 2:
+ m_dma_offset[BIT(offset, 3)][3] = data;
+ break;
+ case 3:
+ m_dma_offset[BIT(offset, 3)][1] = data;
+ break;
+ case 7:
+ m_dma_offset[BIT(offset, 3)][0] = data;
+ break;
+ }
+}
+
+
+WRITE_LINE_MEMBER( at_mb_device::dma_hrq_changed )
+{
+ m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
+
+ /* Assert HLDA */
+ m_dma8237_2->hack_w(state);
+}
+
+uint8_t at_mb_device::dma_read_byte(offs_t offset)
+{
+ address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return 0xff;
+ uint8_t result;
+ offs_t page_offset = ((offs_t) m_dma_offset[0][m_dma_channel]) << 16;
+
+ result = prog_space.read_byte(page_offset + offset);
+ return result;
+}
+
+
+void at_mb_device::dma_write_byte(offs_t offset, uint8_t data)
+{
+ address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return;
+ offs_t page_offset = ((offs_t) m_dma_offset[0][m_dma_channel]) << 16;
+
+ prog_space.write_byte(page_offset + offset, data);
+}
+
+
+uint8_t at_mb_device::dma_read_word(offs_t offset)
+{
+ address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return 0xff;
+ uint16_t result;
+ offs_t page_offset = ((offs_t) m_dma_offset[1][m_dma_channel & 3]) << 16;
+
+ result = prog_space.read_word((page_offset & 0xfe0000) | (offset << 1));
+ m_dma_high_byte = result & 0xff00;
+
+ return result & 0xff;
+}
+
+
+void at_mb_device::dma_write_word(offs_t offset, uint8_t data)
+{
+ address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return;
+ offs_t page_offset = ((offs_t) m_dma_offset[1][m_dma_channel & 3]) << 16;
+
+ prog_space.write_word((page_offset & 0xfe0000) | (offset << 1), m_dma_high_byte | data);
+}
+
+uint8_t at_mb_device::dma8237_0_dack_r() { return m_isabus->dack_r(0); }
+uint8_t at_mb_device::dma8237_1_dack_r() { return m_isabus->dack_r(1); }
+uint8_t at_mb_device::dma8237_2_dack_r() { return m_isabus->dack_r(2); }
+uint8_t at_mb_device::dma8237_3_dack_r() { return m_isabus->dack_r(3); }
+uint8_t at_mb_device::dma8237_5_dack_r() { uint16_t ret = m_isabus->dack16_r(5); m_dma_high_byte = ret & 0xff00; return ret; }
+uint8_t at_mb_device::dma8237_6_dack_r() { uint16_t ret = m_isabus->dack16_r(6); m_dma_high_byte = ret & 0xff00; return ret; }
+uint8_t at_mb_device::dma8237_7_dack_r() { uint16_t ret = m_isabus->dack16_r(7); m_dma_high_byte = ret & 0xff00; return ret; }
+
+
+void at_mb_device::dma8237_0_dack_w(uint8_t data) { m_isabus->dack_w(0, data); }
+void at_mb_device::dma8237_1_dack_w(uint8_t data) { m_isabus->dack_w(1, data); }
+void at_mb_device::dma8237_2_dack_w(uint8_t data) { m_isabus->dack_w(2, data); }
+void at_mb_device::dma8237_3_dack_w(uint8_t data) { m_isabus->dack_w(3, data); }
+void at_mb_device::dma8237_5_dack_w(uint8_t data) { m_isabus->dack16_w(5, m_dma_high_byte | data); }
+void at_mb_device::dma8237_6_dack_w(uint8_t data) { m_isabus->dack16_w(6, m_dma_high_byte | data); }
+void at_mb_device::dma8237_7_dack_w(uint8_t data) { m_isabus->dack16_w(7, m_dma_high_byte | data); }
+
+WRITE_LINE_MEMBER( at_mb_device::dma8237_out_eop )
+{
+ m_cur_eop = state == ASSERT_LINE;
+ if(m_dma_channel != -1)
+ m_isabus->eop_w(m_dma_channel, m_cur_eop ? ASSERT_LINE : CLEAR_LINE );
+}
+
+WRITE_LINE_MEMBER( at_mb_device::dma8237_2_out_eop )
+{
+ m_cur_eop2 = state == ASSERT_LINE;
+ if(m_dma_channel != -1)
+ m_isabus->eop_w(m_dma_channel, m_cur_eop2 ? ASSERT_LINE : CLEAR_LINE );
+}
+
+void at_mb_device::set_dma_channel(int channel, int state)
+{
+ if(!state) {
+ m_dma_channel = channel;
+ if(m_cur_eop)
+ m_isabus->eop_w(channel, ASSERT_LINE );
+
+ } else if(m_dma_channel == channel) {
+ m_dma_channel = -1;
+ if(m_cur_eop)
+ m_isabus->eop_w(channel, CLEAR_LINE );
+ }
+}
+
+void at_mb_device::write_rtc(offs_t offset, uint8_t data)
+{
+ if (offset==0) {
+ m_nmi_enabled = BIT(data,7);
+ if (!m_nmi_enabled)
+ m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+ m_mc146818->write(0,data);
+ }
+ else {
+ m_mc146818->write(offset,data);
+ }
+}
+
+uint32_t at_mb_device::a20_286(bool state)
+{
+ return (state ? 0xffffff : 0xefffff);
+}
+
+WRITE_LINE_MEMBER( at_mb_device::shutdown )
+{
+ if(state)
+ m_maincpu->reset();
+}
+WRITE_LINE_MEMBER( at_mb_device::dack0_w ) { set_dma_channel(0, state); }
+WRITE_LINE_MEMBER( at_mb_device::dack1_w ) { set_dma_channel(1, state); }
+WRITE_LINE_MEMBER( at_mb_device::dack2_w ) { set_dma_channel(2, state); }
+WRITE_LINE_MEMBER( at_mb_device::dack3_w ) { set_dma_channel(3, state); }
+WRITE_LINE_MEMBER( at_mb_device::dack4_w ) { m_dma8237_1->hack_w(state ? 0 : 1); } // it's inverted
+WRITE_LINE_MEMBER( at_mb_device::dack5_w ) { set_dma_channel(5, state); }
+WRITE_LINE_MEMBER( at_mb_device::dack6_w ) { set_dma_channel(6, state); }
+WRITE_LINE_MEMBER( at_mb_device::dack7_w ) { set_dma_channel(7, state); }
+
+WRITE_LINE_MEMBER( at_mb_device::kbd_clk_w ) { m_keybc->kbd_clk_w(state); }
+WRITE_LINE_MEMBER( at_mb_device::kbd_data_w ) { m_keybc->kbd_data_w(state); }
+
+uint8_t at_mb_device::portb_r()
+{
+ uint8_t data = m_at_speaker;
+ data &= ~0xd0; /* AT BIOS don't likes this being set */
+
+ /* 0x10 is the dram refresh line bit, 15.085us. */
+ data |= (machine().time().as_ticks(110000) & 1) ? 0x10 : 0;
+
+ if (m_pit_out2)
+ data |= 0x20;
+ else
+ data &= ~0x20; /* ps2m30 wants this */
+
+ return data;
+}
+
+void at_mb_device::portb_w(uint8_t data)
+{
+ m_at_speaker = data;
+ m_pit8254->write_gate2(BIT(data, 0));
+ speaker_set_spkrdata( BIT(data, 1));
+ m_channel_check = BIT(data, 3);
+ if (m_channel_check)
+ m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+}
+
+WRITE_LINE_MEMBER( at_mb_device::iochck_w )
+{
+ if (!state && m_nmi_enabled && !m_channel_check)
+ m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
+}
diff --git a/src/devices/machine/at.h b/src/devices/machine/at.h
new file mode 100644
index 00000000000..346721ffbbd
--- /dev/null
+++ b/src/devices/machine/at.h
@@ -0,0 +1,107 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol, Miodrag Milanovic, Carl
+#ifndef MAME_MACHINE_AT_H
+#define MAME_MACHINE_AT_H
+
+#include "machine/mc146818.h"
+#include "machine/pic8259.h"
+#include "machine/pit8253.h"
+#include "machine/am9517a.h"
+#include "machine/at_keybc.h"
+#include "bus/isa/isa.h"
+#include "sound/spkrdev.h"
+#include "softlist.h"
+
+class at_mb_device : public device_t
+{
+public:
+ at_mb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ void map(address_map &map);
+
+ auto kbd_clk() { return subdevice<at_keyboard_controller_device>("keybc")->kbd_clk(); }
+ auto kbd_data() { return subdevice<at_keyboard_controller_device>("keybc")->kbd_data(); }
+
+ uint8_t page8_r(offs_t offset);
+ void page8_w(offs_t offset, uint8_t data);
+ DECLARE_WRITE_LINE_MEMBER(kbd_clk_w);
+ DECLARE_WRITE_LINE_MEMBER(kbd_data_w);
+ uint8_t portb_r();
+ void portb_w(uint8_t data);
+ void write_rtc(offs_t offset, uint8_t data);
+ DECLARE_WRITE_LINE_MEMBER(iochck_w);
+
+ DECLARE_WRITE_LINE_MEMBER(shutdown);
+
+ uint32_t a20_286(bool state);
+
+ void at_softlists(machine_config &config);
+protected:
+ void device_start() override;
+ void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+private:
+ void set_dma_channel(int channel, int state);
+ void speaker_set_spkrdata(uint8_t data);
+
+ required_device<cpu_device> m_maincpu;
+ required_device<isa16_device> m_isabus;
+ required_device<pic8259_device> m_pic8259_slave;
+ required_device<am9517a_device> m_dma8237_1;
+ required_device<am9517a_device> m_dma8237_2;
+ required_device<pit8254_device> m_pit8254;
+ required_device<speaker_sound_device> m_speaker;
+ required_device<mc146818_device> m_mc146818;
+ optional_device<at_keyboard_controller_device> m_keybc; // removed in mtouchxl.cpp and vis.cpp
+
+ uint8_t m_at_spkrdata = 0;
+ uint8_t m_pit_out2 = 0;
+ int m_dma_channel = 0;
+ bool m_cur_eop = false, m_cur_eop2 = false;
+ uint8_t m_dma_offset[2][4]{};
+ uint8_t m_at_pages[0x10]{};
+ uint16_t m_dma_high_byte = 0;
+ uint8_t m_at_speaker = 0;
+ uint8_t m_channel_check = 0;
+ uint8_t m_nmi_enabled = 0;
+
+ DECLARE_WRITE_LINE_MEMBER(pit8254_out2_changed);
+
+ DECLARE_WRITE_LINE_MEMBER(dma8237_out_eop);
+ DECLARE_WRITE_LINE_MEMBER(dma8237_2_out_eop);
+ uint8_t dma8237_0_dack_r();
+ uint8_t dma8237_1_dack_r();
+ uint8_t dma8237_2_dack_r();
+ uint8_t dma8237_3_dack_r();
+ uint8_t dma8237_5_dack_r();
+ uint8_t dma8237_6_dack_r();
+ uint8_t dma8237_7_dack_r();
+ void dma8237_0_dack_w(uint8_t data);
+ void dma8237_1_dack_w(uint8_t data);
+ void dma8237_2_dack_w(uint8_t data);
+ void dma8237_3_dack_w(uint8_t data);
+ void dma8237_5_dack_w(uint8_t data);
+ void dma8237_6_dack_w(uint8_t data);
+ void dma8237_7_dack_w(uint8_t data);
+ DECLARE_WRITE_LINE_MEMBER(dack0_w);
+ DECLARE_WRITE_LINE_MEMBER(dack1_w);
+ DECLARE_WRITE_LINE_MEMBER(dack2_w);
+ DECLARE_WRITE_LINE_MEMBER(dack3_w);
+ DECLARE_WRITE_LINE_MEMBER(dack4_w);
+ DECLARE_WRITE_LINE_MEMBER(dack5_w);
+ DECLARE_WRITE_LINE_MEMBER(dack6_w);
+ DECLARE_WRITE_LINE_MEMBER(dack7_w);
+ uint8_t get_slave_ack(offs_t offset);
+ DECLARE_WRITE_LINE_MEMBER(dma_hrq_changed);
+
+ uint8_t dma_read_byte(offs_t offset);
+ void dma_write_byte(offs_t offset, uint8_t data);
+ uint8_t dma_read_word(offs_t offset);
+ void dma_write_word(offs_t offset, uint8_t data);
+};
+
+DECLARE_DEVICE_TYPE(AT_MB, at_mb_device)
+
+
+#endif // MAME_MACHINE_AT_H
diff --git a/src/devices/machine/cammu.cpp b/src/devices/machine/cammu.cpp
new file mode 100644
index 00000000000..dabc637f085
--- /dev/null
+++ b/src/devices/machine/cammu.cpp
@@ -0,0 +1,814 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * An implementation of the Fairchild/Intergraph Cache and Memory Management
+ * Unit (CAMMU) designed for use with the CLIPPER CPU family.
+ *
+ * The C100 and C300 designs used a pair of identical CAMMU devices, each
+ * containing a cache, TLB and dynamic translation unit. One device was
+ * configured and used for instruction memory, the other for data. It is
+ * possible to write to multiple CAMMU devices sharing a common system bus by
+ * using "global" register addresses.
+ *
+ * C400 designs initially implemented the memory management and cache functions
+ * using discrete logic, later using a more highly integrated memory management
+ * implementation, but still using discrete cache memory. In these systems, the
+ * mmu is consolidated into a single logical unit handling both instruction and
+ * data memory, with distinctly different program-visible architectures on the
+ * C4I and C4E/T devices. Almost no documentation for these has been located.
+ *
+ * Primary reference: http://bitsavers.org/pdf/fairchild/clipper/CLIPPER%20C300%2032-Bit%20Compute%20Engine.pdf
+ * Another reference: http://www.eecs.berkeley.edu/Pubs/TechRpts/1986/CSD-86-289.pdf
+ *
+ * TODO
+ * - c4 variants
+ * - fault register values
+ * - cache
+ * - bus errors
+ */
+
+#include "emu.h"
+#include "cammu.h"
+
+#include <algorithm>
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_ACCESS (1U << 1)
+#define LOG_DTU (1U << 2)
+#define LOG_TLB (1U << 3)
+
+//#define VERBOSE (LOG_GENERAL | LOG_ACCESS | LOG_DTU)
+#include "logmacro.h"
+
+// each variant of the cammu has different registers and a different addressing map
+void cammu_c4t_device::map(address_map &map)
+{
+ map(0x008, 0x00b).rw(FUNC(cammu_c4t_device::ram_line_r), FUNC(cammu_c4t_device::ram_line_w));
+ map(0x010, 0x013).rw(FUNC(cammu_c4t_device::s_pdo_r), FUNC(cammu_c4t_device::s_pdo_w));
+ map(0x018, 0x01b).rw(FUNC(cammu_c4t_device::u_pdo_r), FUNC(cammu_c4t_device::u_pdo_w));
+ map(0x020, 0x023).rw(FUNC(cammu_c4t_device::htlb_offset_r), FUNC(cammu_c4t_device::htlb_offset_w));
+ map(0x028, 0x02b).rw(FUNC(cammu_c4t_device::i_fault_r), FUNC(cammu_c4t_device::i_fault_w));
+ map(0x030, 0x033).rw(FUNC(cammu_c4t_device::fault_address_1_r), FUNC(cammu_c4t_device::fault_address_1_w));
+ map(0x038, 0x03b).rw(FUNC(cammu_c4t_device::fault_address_2_r), FUNC(cammu_c4t_device::fault_address_2_w));
+ map(0x040, 0x043).rw(FUNC(cammu_c4t_device::fault_data_1_lo_r), FUNC(cammu_c4t_device::fault_data_1_lo_w));
+ map(0x048, 0x04b).rw(FUNC(cammu_c4t_device::fault_data_1_hi_r), FUNC(cammu_c4t_device::fault_data_1_hi_w));
+ map(0x050, 0x053).rw(FUNC(cammu_c4t_device::fault_data_2_lo_r), FUNC(cammu_c4t_device::fault_data_2_lo_w));
+ map(0x058, 0x05b).rw(FUNC(cammu_c4t_device::fault_data_2_hi_r), FUNC(cammu_c4t_device::fault_data_2_hi_w));
+ map(0x060, 0x063).rw(FUNC(cammu_c4t_device::c4_bus_poll_r), FUNC(cammu_c4t_device::c4_bus_poll_w));
+ map(0x068, 0x06b).rw(FUNC(cammu_c4t_device::control_r), FUNC(cammu_c4t_device::control_w));
+ map(0x070, 0x073).rw(FUNC(cammu_c4t_device::bio_control_r), FUNC(cammu_c4t_device::bio_control_w));
+ map(0x078, 0x07b).rw(FUNC(cammu_c4t_device::bio_address_tag_r), FUNC(cammu_c4t_device::bio_address_tag_w));
+
+ map(0x100, 0x103).rw(FUNC(cammu_c4t_device::cache_data_lo_r), FUNC(cammu_c4t_device::cache_data_lo_w));
+ map(0x104, 0x107).rw(FUNC(cammu_c4t_device::cache_data_hi_r), FUNC(cammu_c4t_device::cache_data_hi_w));
+ map(0x108, 0x10b).rw(FUNC(cammu_c4t_device::cache_cpu_tag_r), FUNC(cammu_c4t_device::cache_cpu_tag_w));
+ map(0x10c, 0x10f).rw(FUNC(cammu_c4t_device::cache_system_tag_valid_r), FUNC(cammu_c4t_device::cache_system_tag_valid_w));
+ map(0x110, 0x113).rw(FUNC(cammu_c4t_device::cache_system_tag_r), FUNC(cammu_c4t_device::cache_system_tag_w));
+ map(0x118, 0x11b).rw(FUNC(cammu_c4t_device::tlb_va_line_r), FUNC(cammu_c4t_device::tlb_va_line_w));
+ map(0x11c, 0x11f).rw(FUNC(cammu_c4t_device::tlb_ra_line_r), FUNC(cammu_c4t_device::tlb_ra_line_w));
+}
+
+void cammu_c4i_device::map(address_map &map)
+{
+ map(0x000, 0x003).rw(FUNC(cammu_c4i_device::reset_r), FUNC(cammu_c4i_device::reset_w));
+ map(0x010, 0x013).rw(FUNC(cammu_c4i_device::s_pdo_r), FUNC(cammu_c4i_device::s_pdo_w));
+ map(0x018, 0x01b).rw(FUNC(cammu_c4i_device::u_pdo_r), FUNC(cammu_c4i_device::u_pdo_w));
+ map(0x020, 0x023).rw(FUNC(cammu_c4i_device::clr_s_data_tlb_r), FUNC(cammu_c4i_device::clr_s_data_tlb_w));
+ map(0x028, 0x02b).rw(FUNC(cammu_c4i_device::clr_u_data_tlb_r), FUNC(cammu_c4i_device::clr_u_data_tlb_w));
+ map(0x030, 0x033).rw(FUNC(cammu_c4i_device::clr_s_insn_tlb_r), FUNC(cammu_c4i_device::clr_s_insn_tlb_w));
+ map(0x038, 0x03b).rw(FUNC(cammu_c4i_device::clr_u_insn_tlb_r), FUNC(cammu_c4i_device::clr_u_insn_tlb_w));
+
+ map(0x068, 0x06b).rw(FUNC(cammu_c4i_device::control_r), FUNC(cammu_c4i_device::control_w));
+
+ map(0x080, 0x083).rw(FUNC(cammu_c4i_device::test_data_r), FUNC(cammu_c4i_device::test_data_w));
+ map(0x088, 0x08b).rw(FUNC(cammu_c4i_device::i_fault_r), FUNC(cammu_c4i_device::i_fault_w));
+ map(0x090, 0x093).rw(FUNC(cammu_c4i_device::fault_address_1_r), FUNC(cammu_c4i_device::fault_address_1_w));
+ map(0x098, 0x09b).rw(FUNC(cammu_c4i_device::fault_address_2_r), FUNC(cammu_c4i_device::fault_address_2_w));
+ map(0x0a0, 0x0a3).rw(FUNC(cammu_c4i_device::fault_data_1_lo_r), FUNC(cammu_c4i_device::fault_data_1_lo_w));
+ map(0x0a8, 0x0ab).rw(FUNC(cammu_c4i_device::fault_data_1_hi_r), FUNC(cammu_c4i_device::fault_data_1_hi_w));
+ map(0x0b0, 0x0b3).rw(FUNC(cammu_c4i_device::fault_data_2_lo_r), FUNC(cammu_c4i_device::fault_data_2_lo_w));
+ map(0x0b8, 0x0bb).rw(FUNC(cammu_c4i_device::fault_data_2_hi_r), FUNC(cammu_c4i_device::fault_data_2_hi_w));
+ map(0x0c0, 0x0c3).rw(FUNC(cammu_c4i_device::test_address_r), FUNC(cammu_c4i_device::test_address_w));
+}
+
+DEFINE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device, "c4t", "C4E/C4T CAMMU")
+DEFINE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device, "c4i", "C4I CAMMU")
+DEFINE_DEVICE_TYPE(CAMMU_C3, cammu_c3_device, "c3", "C1/C3 CAMMU")
+
+cammu_c4t_device::cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_c4_device(mconfig, CAMMU_C4T, tag, owner, clock)
+ , m_ram_line(0)
+ , m_htlb_offset(0)
+ , m_c4_bus_poll(0)
+ , m_bio_control(0)
+ , m_bio_address_tag(0)
+ , m_cache_data_lo(0)
+ , m_cache_data_hi(0)
+ , m_cache_cpu_tag(0)
+ , m_cache_system_tag_valid(0)
+ , m_cache_system_tag(0)
+ , m_tlb_va_line(0)
+ , m_tlb_ra_line(0)
+{
+}
+
+cammu_c4i_device::cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_c4_device(mconfig, CAMMU_C4I, tag, owner, clock)
+ , m_reset(0)
+ , m_clr_s_data_tlb(0)
+ , m_clr_u_data_tlb(0)
+ , m_clr_s_insn_tlb(0)
+ , m_clr_u_insn_tlb(0)
+ , m_test_data(0)
+ , m_test_address(0)
+{
+}
+
+cammu_c4_device::cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_device(mconfig, type, tag, owner, clock)
+ , m_s_pdo(0)
+ , m_u_pdo(0)
+ , m_control(0)
+ , m_i_fault(0)
+ , m_fault_address_1(0)
+ , m_fault_address_2(0)
+ , m_fault_data_1_lo(0)
+ , m_fault_data_1_hi(0)
+ , m_fault_data_2_lo(0)
+ , m_fault_data_2_hi(0)
+{
+}
+
+cammu_c3_device::cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : cammu_device(mconfig, CAMMU_C3, tag, owner, clock)
+ , m_linked{ this }
+ , m_s_pdo(0)
+ , m_u_pdo(0)
+ , m_fault(0)
+ , m_control(CID_C3)
+{
+}
+
+cammu_device::cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock)
+ , m_exception_func(*this)
+{
+}
+
+void cammu_device::device_start()
+{
+ m_exception_func.resolve();
+}
+
+void cammu_device::device_reset()
+{
+}
+
+void cammu_c4_device::device_start()
+{
+ cammu_device::device_start();
+
+ save_item(NAME(m_s_pdo));
+ save_item(NAME(m_u_pdo));
+ save_item(NAME(m_control));
+
+ save_item(NAME(m_i_fault));
+ save_item(NAME(m_fault_address_1));
+ save_item(NAME(m_fault_address_2));
+ save_item(NAME(m_fault_data_1_lo));
+ save_item(NAME(m_fault_data_1_hi));
+ save_item(NAME(m_fault_data_2_lo));
+ save_item(NAME(m_fault_data_2_hi));
+}
+
+void cammu_c4i_device::device_start()
+{
+ cammu_c4_device::device_start();
+
+ save_item(NAME(m_reset));
+ save_item(NAME(m_clr_s_data_tlb));
+ save_item(NAME(m_clr_u_data_tlb));
+ save_item(NAME(m_clr_s_insn_tlb));
+ save_item(NAME(m_clr_u_insn_tlb));
+ save_item(NAME(m_test_data));
+ save_item(NAME(m_test_address));
+}
+
+void cammu_c4t_device::device_start()
+{
+ cammu_c4_device::device_start();
+
+ save_item(NAME(m_ram_line));
+ save_item(NAME(m_htlb_offset));
+ save_item(NAME(m_c4_bus_poll));
+ save_item(NAME(m_bio_control));
+ save_item(NAME(m_bio_address_tag));
+
+ save_item(NAME(m_cache_data_lo));
+ save_item(NAME(m_cache_data_hi));
+ save_item(NAME(m_cache_cpu_tag));
+ save_item(NAME(m_cache_system_tag_valid));
+ save_item(NAME(m_cache_system_tag));
+ save_item(NAME(m_tlb_va_line));
+ save_item(NAME(m_tlb_ra_line));
+}
+
+void cammu_c3_device::device_start()
+{
+ cammu_device::device_start();
+
+ save_item(NAME(m_s_pdo));
+ save_item(NAME(m_u_pdo));
+ save_item(NAME(m_fault));
+ save_item(NAME(m_control));
+
+ for (tlb_set_t &tlb_set : m_tlb)
+ {
+ tlb_set.u = false;
+
+ tlb_set.w.ra = tlb_set.w.va = 0;
+ m_memory[ST0].space->cache(tlb_set.w.cache);
+
+ tlb_set.x.ra = tlb_set.x.va = 0;
+ m_memory[ST0].space->cache(tlb_set.x.cache);
+ }
+}
+
+void cammu_c3_device::device_reset()
+{
+ cammu_device::device_reset();
+
+ m_control = (m_control & CNTL_CID) | (CNTL_ATE | UST_3 | CNTL_EWIR | CNTL_EWIW | CNTL_EWCW | CNTL_EP);
+}
+
+void cammu_device::set_spaces(address_space &main_space, address_space &io_space, address_space &boot_space)
+{
+ m_memory[ST0].space = &main_space;
+ m_memory[ST1].space = &main_space;
+ m_memory[ST2].space = &main_space;
+ m_memory[ST3].space = &main_space;
+
+ m_memory[ST4].space = &io_space;
+ m_memory[ST5].space = &boot_space;
+ m_memory[ST6].space = &main_space;
+
+ // FIXME: this tag is probably not used, but if it is, need to figure
+ // out how to implement it properly.
+ m_memory[ST7].space = &main_space;
+
+ for (memory_t &memory : m_memory)
+ memory.space->cache(memory.cache);
+}
+
+bool cammu_device::memory_translate(const u32 ssw, const int spacenum, const int intention, offs_t &address)
+{
+ // translate the address
+ translated_t translated = translate_address(ssw, address, BYTE,
+ (intention & TRANSLATE_TYPE_MASK) == TRANSLATE_READ ? READ :
+ (intention & TRANSLATE_TYPE_MASK) == TRANSLATE_WRITE ? WRITE :
+ EXECUTE);
+
+ // check that the requested space number matches the mapped space
+ if (translated.cache && translated.cache->space().spacenum() == spacenum)
+ {
+ address = translated.address;
+
+ return true;
+ }
+
+ return false;
+}
+
+cammu_device::translated_t cammu_device::translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode)
+{
+ // get effective user/supervisor mode
+ const bool user = (mode == EXECUTE) ? (ssw & SSW_U) : (ssw & (SSW_U | SSW_UU));
+
+ // check for alignment faults
+ if (!machine().side_effects_disabled() && get_alignment())
+ {
+ if ((mode == EXECUTE && (virtual_address & 0x1)) || (mode != EXECUTE && virtual_address & (size - 1)))
+ {
+ set_fault(virtual_address, mode == EXECUTE ? EXCEPTION_I_ALIGNMENT_FAULT : EXCEPTION_D_ALIGNMENT_FAULT);
+
+ return { nullptr, 0 };
+ }
+ }
+
+ // in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
+ if (!user && (virtual_address & ~0x7fff) == 0)
+ {
+ switch (virtual_address & 0x7000)
+ {
+ // pages 0-3: main space pages 0-3
+ case 0x0000: return { &m_memory[ST1].cache, virtual_address & 0x3fff };
+ case 0x1000: return { &m_memory[ST2].cache, virtual_address & 0x3fff };
+ case 0x2000: return { &m_memory[ST3].cache, virtual_address & 0x3fff };
+ case 0x3000: return { &m_memory[ST3].cache, virtual_address & 0x3fff };
+
+ // pages 4-5: i/o space pages 0-1
+ case 0x4000: return { &m_memory[ST4].cache, virtual_address & 0x1fff };
+ case 0x5000: return { &m_memory[ST4].cache, virtual_address & 0x1fff };
+
+ // pages 6-7: boot space pages 0-1
+ case 0x6000: return { &m_memory[ST5].cache, virtual_address & 0x1fff };
+ case 0x7000: return { &m_memory[ST5].cache, virtual_address & 0x1fff };
+ }
+ }
+
+ // if not in mapped mode, use unmapped system tag
+ if ((ssw & SSW_M) == 0)
+ return { &m_memory[get_ust_space()].cache, virtual_address };
+
+ // get the page table entry
+ pte_t pte = get_pte(virtual_address, user);
+
+ // check for page faults
+ if (pte.entry & PTE_F)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ LOG("%s page fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ mode == EXECUTE ? "instruction" : "data",
+ virtual_address, ssw, pte.entry, machine().describe_context());
+
+ set_fault(virtual_address, mode == EXECUTE ? EXCEPTION_I_PAGE_FAULT : EXCEPTION_D_PAGE_FAULT);
+ }
+
+ return { nullptr, 0 };
+ }
+
+ // check for protection level faults
+ if (!machine().side_effects_disabled())
+ {
+ if ((mode == EXECUTE) && !get_access(mode, pte.entry, ssw))
+ {
+ LOGMASKED(LOG_ACCESS, "execute protection fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ virtual_address, ssw, pte.entry, machine().describe_context());
+
+ set_fault(virtual_address, EXCEPTION_I_EXECUTE_PROTECT_FAULT);
+
+ return { nullptr, 0 };
+ }
+
+ if ((mode & READ) && !get_access(READ, pte.entry, ssw))
+ {
+ LOGMASKED(LOG_ACCESS, "read protection fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ virtual_address, ssw, pte.entry, machine().describe_context());
+
+ set_fault(virtual_address, EXCEPTION_D_READ_PROTECT_FAULT);
+
+ return { nullptr, 0 };
+ }
+
+ if ((mode & WRITE) && !get_access(WRITE, pte.entry, ssw))
+ {
+ LOGMASKED(LOG_ACCESS, "write protection fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ virtual_address, ssw, pte.entry, machine().describe_context());
+
+ set_fault(virtual_address, EXCEPTION_D_WRITE_PROTECT_FAULT);
+
+ return { nullptr, 0 };
+ }
+ }
+
+ // set pte referenced and dirty flags
+ if ((mode & WRITE) && !(pte.entry & PTE_D))
+ m_memory[ST0].cache.write_dword(pte.address, pte.entry | PTE_D | PTE_R);
+ else if (!(pte.entry & PTE_R))
+ m_memory[ST0].cache.write_dword(pte.address, pte.entry | PTE_R);
+
+ // translate the address
+ LOGMASKED(LOG_DTU, "%s address translated 0x%08x\n", mode == EXECUTE ? "instruction" : "data",
+ (pte.entry & ~CAMMU_PAGE_MASK) | (virtual_address & CAMMU_PAGE_MASK));
+
+ // return the system tag and translated address
+ return { &m_memory[(pte.entry & PTE_ST) >> ST_SHIFT].cache, (pte.entry & ~CAMMU_PAGE_MASK) | (virtual_address & CAMMU_PAGE_MASK) };
+}
+
+cammu_c3_device::tlb_line_t &cammu_c3_device::tlb_lookup(const bool user, const u32 virtual_address, const access_type mode)
+{
+ const u8 set = (virtual_address >> 12) & 0x3f;
+ tlb_set_t &tlb_set = m_tlb[set];
+
+ // check w compartment
+ if ((tlb_set.w.va & TLB_VA_VA) == (virtual_address & TLB_VA_VA) && (((user && (tlb_set.w.va & TLB_VA_UV)) || (!user && (tlb_set.w.va & TLB_VA_SV)))))
+ {
+ LOGMASKED(LOG_TLB, "tlb_lookup 0x%08x set %2d line W hit 0x%08x\n", virtual_address, set, tlb_set.w.ra);
+
+ // mark x line least recently used
+ tlb_set.u = true;
+
+ return tlb_set.w;
+ }
+
+ // check x compartment
+ if ((tlb_set.x.va & TLB_VA_VA) == (virtual_address & TLB_VA_VA) && (((user && (tlb_set.x.va & TLB_VA_UV))) || (!user && (tlb_set.x.va & TLB_VA_SV))))
+ {
+ LOGMASKED(LOG_TLB, "tlb_lookup 0x%08x set %2d line X hit 0x%08x\n", virtual_address, set, tlb_set.x.ra);
+
+ // mark w line least recently used
+ tlb_set.u = false;
+
+ return tlb_set.x;
+ }
+
+ // return the least recently used line
+ if (tlb_set.u)
+ {
+ LOGMASKED(LOG_TLB, "tlb_lookup 0x%08x set %2d line X miss\n", virtual_address, set);
+
+ tlb_set.u = false;
+ tlb_set.x.ra &= ~TLB_RA_R;
+
+ return tlb_set.x;
+ }
+ else
+ {
+ LOGMASKED(LOG_TLB, "tlb_lookup 0x%08x set %2d line W miss\n", virtual_address, set);
+
+ tlb_set.u = true;
+ tlb_set.w.ra &= ~TLB_RA_R;
+
+ return tlb_set.w;
+ }
+}
+
+cammu_device::translated_t cammu_c3_device::translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode)
+{
+ // get effective user/supervisor mode
+ const bool user = (mode == EXECUTE) ? (ssw & SSW_U) : (ssw & (SSW_U | SSW_UU));
+
+ // check for alignment faults
+ if (!machine().side_effects_disabled() && get_alignment())
+ {
+ if ((mode == EXECUTE && (virtual_address & 0x1)) || (mode != EXECUTE && virtual_address & (size - 1)))
+ {
+ set_fault(virtual_address, mode == EXECUTE ? EXCEPTION_I_ALIGNMENT_FAULT : EXCEPTION_D_ALIGNMENT_FAULT);
+
+ return { nullptr, 0 };
+ }
+ }
+
+ // in supervisor mode, the first 8 pages are always mapped via the hard-wired tlb
+ if (!user && (virtual_address & ~0x7fff) == 0)
+ {
+ switch (virtual_address & 0x7000)
+ {
+ // pages 0-3: main space pages 0-3
+ case 0x0000: return { &m_memory[ST1].cache, virtual_address & 0x3fff };
+ case 0x1000: return { &m_memory[ST2].cache, virtual_address & 0x3fff };
+ case 0x2000: return { &m_memory[ST3].cache, virtual_address & 0x3fff };
+ case 0x3000: return { &m_memory[ST3].cache, virtual_address & 0x3fff };
+
+ // pages 4-5: i/o space pages 0-1
+ case 0x4000: return { &m_memory[ST4].cache, virtual_address & 0x1fff };
+ case 0x5000: return { &m_memory[ST4].cache, virtual_address & 0x1fff };
+
+ // pages 6-7: boot space pages 0-1
+ case 0x6000: return { &m_memory[ST5].cache, virtual_address & 0x1fff };
+ case 0x7000: return { &m_memory[ST5].cache, virtual_address & 0x1fff };
+ }
+ }
+
+ // if not in mapped mode, use unmapped system tag
+ if ((ssw & SSW_M) == 0)
+ return { &m_memory[get_ust_space()].cache, virtual_address };
+
+ // check translation lookaside buffer
+ tlb_line_t &tlbl = tlb_lookup(user, virtual_address, mode);
+
+ pte_t pte = { PTE_F, 0 };
+
+ // handle translation lookaside buffer miss
+ if (!(tlbl.ra & TLB_RA_R))
+ {
+ // get the page table entry
+ pte = get_pte(virtual_address, user);
+
+ // check for page faults
+ if (pte.entry & PTE_F)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ LOG("%s page fault address 0x%08x ssw 0x%08x pte 0x%08x (%s)\n",
+ mode == EXECUTE ? "instruction" : "data",
+ virtual_address, ssw, pte.entry, machine().describe_context());
+
+ set_fault(virtual_address, mode == EXECUTE ? EXCEPTION_I_PAGE_FAULT : EXCEPTION_D_PAGE_FAULT);
+ }
+
+ return { nullptr, 0 };
+ }
+
+ // update tlb line from page table entry
+ // FIXME: not sure if user/supervisor valid follow actual or effective mode?
+ tlbl.va = (virtual_address & TLB_VA_VA) | (user ? TLB_VA_UV : TLB_VA_SV);
+ tlbl.ra = pte.entry;
+ }
+
+ // check protection level
+ if (!machine().side_effects_disabled())
+ {
+ if ((mode == EXECUTE) && !get_access(EXECUTE, tlbl.ra, ssw))
+ {
+ LOGMASKED(LOG_ACCESS, "execute protection fault address 0x%08x ssw 0x%08x (%s)\n",
+ virtual_address, ssw, machine().describe_context());
+
+ set_fault(virtual_address, EXCEPTION_I_EXECUTE_PROTECT_FAULT);
+
+ return { nullptr, 0 };
+ }
+ if ((mode & READ) && !get_access(READ, tlbl.ra, ssw))
+ {
+ LOGMASKED(LOG_ACCESS, "read protection fault address 0x%08x ssw 0x%08x (%s)\n",
+ virtual_address, ssw, machine().describe_context());
+
+ set_fault(virtual_address, EXCEPTION_D_READ_PROTECT_FAULT);
+
+ return { nullptr, 0 };
+ }
+ if ((mode & WRITE) && !get_access(WRITE, tlbl.ra, ssw))
+ {
+ LOGMASKED(LOG_ACCESS, "write protection fault address 0x%08x ssw 0x%08x (%s)\n",
+ virtual_address, ssw, machine().describe_context());
+
+ set_fault(virtual_address, EXCEPTION_D_WRITE_PROTECT_FAULT);
+
+ return { nullptr, 0 };
+ }
+ }
+
+ // update dirty flag
+ if ((mode & WRITE) && !(tlbl.ra & TLB_RA_D))
+ {
+ // fetch the page table entry if needed
+ if (pte.entry & PTE_F)
+ pte = get_pte(virtual_address, user);
+
+ // set page table entry dirty flag
+ if (!(pte.entry & PTE_D))
+ {
+ pte.entry |= PTE_D | PTE_R;
+ m_memory[ST0].cache.write_dword(pte.address, pte.entry);
+ }
+
+ tlbl.ra |= TLB_RA_D | TLB_RA_R;
+ }
+
+ // update referenced flag
+ if (!(tlbl.ra & TLB_RA_R))
+ {
+ // fetch the page table entry if needed
+ if (pte.entry & PTE_F)
+ pte = get_pte(virtual_address, user);
+
+ // set page table entry referenced flag
+ if (!(pte.entry & PTE_R))
+ {
+ pte.entry |= PTE_R;
+ m_memory[ST0].cache.write_dword(pte.address, pte.entry);
+ }
+
+ tlbl.ra |= TLB_RA_R;
+ }
+
+ // return the system tag and translated address
+ LOGMASKED(LOG_DTU, "%s address translated 0x%08x\n", mode == EXECUTE ? "instruction" : "data",
+ (tlbl.ra & TLB_RA_RA) | (virtual_address & CAMMU_PAGE_MASK));
+
+ if (tlbl.ra & 0x800)
+ return { &m_memory[(tlbl.ra & TLB_RA_ST) >> ST_SHIFT].cache, (tlbl.ra & TLB_RA_RA) | (virtual_address & CAMMU_PAGE_MASK) };
+ else
+ return { &tlbl.cache, (tlbl.ra & TLB_RA_RA) | (virtual_address & CAMMU_PAGE_MASK) };
+}
+
+// return the page table entry for a given virtual address
+cammu_device::pte_t cammu_device::get_pte(const u32 va, const bool user)
+{
+ // get page table directory origin from user or supervisor pdo register
+ const u32 pdo = get_pdo(user);
+
+ // get page table directory index from top 12 bits of virtual address
+ const u32 ptdi = (va & VA_PTDI) >> 20;
+
+ // fetch page table directory entry
+ const u32 ptde = m_memory[ST0].cache.read_dword(pdo | ptdi);
+
+ LOGMASKED(LOG_DTU, "get_pte pdo 0x%08x ptdi 0x%08x ptde 0x%08x\n", pdo, ptdi, ptde);
+
+ // check for page table directory entry fault
+ if (ptde & PTDE_F)
+ return { PTE_F, pdo | ptdi };
+
+ // get the page table origin from the page table directory entry
+ const u32 pto = ptde & PTDE_PTO;
+
+ // get the page table index from the middle 12 bits of the virtual address
+ const u32 pti = (va & VA_PTI) >> 10;
+
+ // fetch page table entry
+ pte_t pte = { m_memory[ST0].cache.read_dword(pto | pti), pto | pti };
+
+ LOGMASKED(LOG_DTU, "get_pte pto 0x%08x pti 0x%08x pte 0x%08x\n", pto, pti, pte.entry);
+
+ // check for page table entry fault
+ if (!(pte.entry & PTE_F))
+ LOGMASKED(LOG_DTU, "get_pte address 0x%08x pte 0x%08x (%s)\n", va, pte.entry, machine().describe_context());
+
+ return pte;
+}
+
+bool cammu_c4_device::get_access(const access_type mode, const u32 pte, const u32 ssw) const
+{
+ switch (mode)
+ {
+ case READ: return pte & 0x20;
+ case WRITE: return pte & 0x10;
+ case EXECUTE: return pte & 0x08;
+
+ default: return false;
+ }
+}
+
+bool cammu_c3_device::get_access(const access_type mode, const u32 pte, const u32 ssw) const
+{
+ const u8 pl = (pte & PTE_PL) >> 3;
+
+ // special case for user data mode
+ if ((mode != EXECUTE) && !(ssw & SSW_U) && (ssw & SSW_UU))
+ return protection_matrix[(ssw & SSW_KU) ? 2 : 3][pl] & mode;
+ else
+ return protection_matrix[((ssw ^ SSW_K) & (SSW_U | SSW_K)) >> 29][pl] & mode;
+}
+
+// C100/C300 CAMMU protection level matrix
+const u8 cammu_c3_device::protection_matrix[4][16] =
+{
+ { RW, RW, RW, RW, RW, RW, RW, RWE, RE, R, R, R, N, N, N, N },
+ { N, RW, RW, RW, RW, RW, R, RWE, N, RE, R, R, RE, N, N, N },
+ { N, N, RW, RW, RW, R, R, RWE, N, N, RE, RE, N, RE, N, N },
+ { N, N, N, RW, R, R, R, RWE, N, N, N, RE, RE, N, RE, N }
+};
+
+void cammu_c3_device::reset_w(const u32 data)
+{
+ // translation lookaside buffer reset operations
+ if (data & (RESET_RSV | RESET_RUV | RESET_RD | RESET_RR))
+ {
+ LOGMASKED(LOG_TLB, "reset_w%s%s%s%s (%s)\n",
+ (data & RESET_RSV) ? " RSV" : "",
+ (data & RESET_RUV) ? " RUV" : "",
+ (data & RESET_RD) ? " RD" : "",
+ (data & RESET_RR) ? " RR" : "",
+ machine().describe_context());
+
+ const u32 va_mask = ((data & RESET_RSV) ? TLB_VA_SV : 0) | ((data & RESET_RUV) ? TLB_VA_UV : 0);
+ const u32 ra_mask = ((data & RESET_RD) ? TLB_RA_D : 0) | ((data & RESET_RR) ? TLB_RA_R : 0);
+
+ for (tlb_set_t &tlb_set : m_tlb)
+ {
+ tlb_set.w.va &= ~va_mask;
+ tlb_set.w.ra &= ~ra_mask;
+ tlb_set.x.va &= ~va_mask;
+ tlb_set.x.ra &= ~ra_mask;
+ }
+ }
+}
+
+u32 cammu_c3_device::tlb_r(const u8 address) const
+{
+ const u8 set = address >> 2;
+ u32 result = 0;
+
+ switch (address & 0x3)
+ {
+ case 0x0: result = m_tlb[set].w.ra | (m_tlb[set].u ? TLB_RA_U : 0); break;
+ case 0x1: result = m_tlb[set].w.va; break;
+ case 0x2: result = m_tlb[set].x.ra | (m_tlb[set].u ? TLB_RA_U : 0); break;
+ case 0x3: result = m_tlb[set].x.va; break;
+ }
+
+ LOGMASKED(LOG_TLB, "tlb_r set %2d line %c %s 0x%08x (%s)\n",
+ set, (address & 0x2) ? 'X' : 'W', (address & 0x1) ? "va" : "ra",
+ result, machine().describe_context());
+
+ return result;
+}
+
+void cammu_c3_device::tlb_w(const u8 address, const u32 data)
+{
+ const u32 mem_mask = ~TLB_RA_U;
+ const u8 set = address >> 2;
+
+ LOGMASKED(LOG_TLB, "tlb_w set %2d line %c %s 0x%08x (%s)\n",
+ set, (address & 0x2) ? 'X' : 'W', (address & 0x1) ? "va" : "ra",
+ data, machine().describe_context());
+
+ switch (address & 0x3)
+ {
+ case 0x0: COMBINE_DATA(&m_tlb[set].w.ra); break;
+ case 0x1: COMBINE_DATA(&m_tlb[set].w.va); break;
+ case 0x2: COMBINE_DATA(&m_tlb[set].x.ra); break;
+ case 0x3: COMBINE_DATA(&m_tlb[set].x.va); break;
+ }
+}
+
+u32 cammu_c3_device::cammu_r(const u32 address)
+{
+ switch (address & CAMMU_SELECT)
+ {
+ case CAMMU_D_TLB:
+ return tlb_r(address);
+
+ case CAMMU_D_REG:
+ switch (address & 0xff)
+ {
+ case CAMMU_REG_SPDO: return s_pdo_r();
+ case CAMMU_REG_UPDO: return u_pdo_r();
+ case CAMMU_REG_FAULT: return fault_r();
+ case CAMMU_REG_CONTROL: return control_r();
+ }
+ break;
+
+ case CAMMU_I_TLB:
+ return m_linked[1]->tlb_r(address);
+
+ case CAMMU_I_REG:
+ switch (address & 0xff)
+ {
+ case CAMMU_REG_SPDO: return m_linked[1]->s_pdo_r();
+ case CAMMU_REG_UPDO: return m_linked[1]->u_pdo_r();
+ case CAMMU_REG_FAULT: return m_linked[1]->fault_r();
+ case CAMMU_REG_CONTROL: return m_linked[1]->control_r();
+ }
+ break;
+ }
+
+ LOG("cammu_r unknown address 0x%08x\n", address);
+ return 0;
+}
+
+void cammu_c3_device::cammu_w(const u32 address, const u32 data)
+{
+ switch (address & CAMMU_SELECT)
+ {
+ case CAMMU_D_TLB:
+ tlb_w(address, data);
+ break;
+
+ case CAMMU_D_REG:
+ switch (address & 0xff)
+ {
+ case CAMMU_REG_SPDO: s_pdo_w(data); break;
+ case CAMMU_REG_UPDO: u_pdo_w(data); break;
+ case CAMMU_REG_FAULT: fault_w(data); break;
+ case CAMMU_REG_CONTROL: control_w(data); break;
+ case CAMMU_REG_RESET: reset_w(data); break;
+ default:
+ break;
+ }
+ break;
+
+ case CAMMU_I_TLB:
+ m_linked[1]->tlb_w(address, data);
+ break;
+
+ case CAMMU_I_REG:
+ switch (address & 0xff)
+ {
+ case CAMMU_REG_SPDO: m_linked[1]->s_pdo_w(data); break;
+ case CAMMU_REG_UPDO: m_linked[1]->u_pdo_w(data); break;
+ case CAMMU_REG_FAULT: m_linked[1]->fault_w(data); break;
+ case CAMMU_REG_CONTROL: m_linked[1]->control_w(data); break;
+ case CAMMU_REG_RESET: m_linked[1]->reset_w(data); break;
+ default:
+ break;
+ }
+ break;
+
+ case CAMMU_G_TLB:
+ for (cammu_c3_device *cammu : m_linked)
+ cammu->tlb_w(address, data);
+ break;
+
+ case CAMMU_G_REG:
+ for (cammu_c3_device *cammu : m_linked)
+ switch (address & 0xff)
+ {
+ case CAMMU_REG_SPDO: cammu->s_pdo_w(data); break;
+ case CAMMU_REG_UPDO: cammu->u_pdo_w(data); break;
+ case CAMMU_REG_FAULT: cammu->fault_w(data); break;
+ case CAMMU_REG_CONTROL: cammu->control_w(data); break;
+ case CAMMU_REG_RESET: cammu->reset_w(data); break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ LOG("cammu_w unknown address 0x%08x data 0x%08x\n", address, data);
+ break;
+ }
+}
diff --git a/src/devices/machine/cammu.h b/src/devices/machine/cammu.h
new file mode 100644
index 00000000000..83b44d4b120
--- /dev/null
+++ b/src/devices/machine/cammu.h
@@ -0,0 +1,665 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_MACHINE_CAMMU_H
+#define MAME_MACHINE_CAMMU_H
+
+#pragma once
+
+#include "cpu/clipper/common.h"
+
+class cammu_device : public device_t
+{
+public:
+ auto exception_callback() { return m_exception_func.bind(); }
+
+ static const u32 CAMMU_PAGE_SIZE = 0x1000;
+ static const u32 CAMMU_PAGE_MASK = (CAMMU_PAGE_SIZE - 1);
+
+ enum pdo_mask : u32
+ {
+ PDO_MASK = 0xfffff000
+ };
+
+ enum ptde_mask : u32
+ {
+ PTDE_F = 0x00000001, // page fault
+ PTDE_PTO = 0xfffff000 // page table origin
+ };
+
+ enum pte_mask : u32
+ {
+ PTE_F = 0x00000001, // page fault
+ PTE_R = 0x00000002, // referenced flag
+ PTE_D = 0x00000004, // dirty flag
+ PTE_PL = 0x00000078, // protection level
+ PTE_S = 0x00000180, // system reserved
+ PTE_ST = 0x00000e00, // system tag
+ PTE_RA = 0xfffff000, // real address
+
+ PTE_CW = 0x00000040, // copy on write (c400)
+ PTE_NDREF = 0x00000080, // secondary reference (software) / copy on write (fault)?
+ PTE_LOCK = 0x00000100 // page lock (software)
+ };
+
+ static constexpr int PL_SHIFT = 3;
+ static constexpr int ST_SHIFT = 9;
+
+ enum va_mask : u32
+ {
+ VA_POFS = 0x00000fff, // page offset
+ VA_PTI = 0x003ff000, // page table index
+ VA_PTDI = 0xffc00000 // page table directory index
+ };
+
+ enum system_tag_t : u8
+ {
+ ST0 = 0, // private, write-through, main memory space
+ ST1 = 1, // shared, write-through, main memory space
+ ST2 = 2, // private, copy-back, main memory space
+ ST3 = 3, // noncacheable, main memory space
+ ST4 = 4, // noncacheable, i/o space
+ ST5 = 5, // noncacheable, boot space
+ ST6 = 6, // cache purge
+ ST7 = 7 // slave i/o
+ };
+
+ void set_spaces(address_space &main_space, address_space &io_space, address_space &boot_space);
+
+ // translation lookaside buffer and register access
+ virtual u32 cammu_r(const u32 address) = 0;
+ virtual void cammu_w(const u32 address, const u32 data) = 0;
+
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(const u32 ssw, const u32 address, U &&apply)
+ {
+ // check for cammu access
+ if ((ssw & (SSW_UU | SSW_U)) || ((address & ~0x7ff) != 0x00004800))
+ {
+ translated_t t = translate_address(ssw, address, access_size(sizeof(T)), READ);
+
+ if (!t.cache)
+ return false;
+
+ switch (sizeof(T))
+ {
+ case 1: apply(T(t.cache->read_byte(t.address))); break;
+ case 2: apply(T(t.cache->read_word(t.address))); break;
+ case 4: apply(T(t.cache->read_dword(t.address))); break;
+ case 8: apply(T(t.cache->read_qword(t.address))); break;
+ default:
+ fatalerror("unhandled load 0x%08x size %d (%s)",
+ address, access_size(sizeof(T)), machine().describe_context().c_str());
+ }
+ }
+ else if (sizeof(T) == 4)
+ apply(cammu_r(address));
+ else
+ fatalerror("unhandled cammu load 0x%08x size %d (%s)",
+ address, access_size(sizeof(T)), machine().describe_context().c_str());
+
+ return true;
+ }
+
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(const u32 ssw, const u32 address, U data)
+ {
+ // check for cammu access
+ if ((ssw & (SSW_UU | SSW_U)) || ((address & ~0x7ff) != 0x00004800))
+ {
+ translated_t t = translate_address(ssw, address, access_size(sizeof(T)), WRITE);
+
+ if (!t.cache)
+ return false;
+
+ switch (sizeof(T))
+ {
+ case 1: t.cache->write_byte(t.address, T(data)); break;
+ case 2: t.cache->write_word(t.address, T(data)); break;
+ case 4: t.cache->write_dword(t.address, T(data)); break;
+ case 8: t.cache->write_qword(t.address, T(data)); break;
+ default:
+ fatalerror("unhandled store 0x%08x size %d (%s)",
+ address, access_size(sizeof(T)), machine().describe_context().c_str());
+ }
+ }
+ else if (sizeof(T) == 4)
+ cammu_w(address, data);
+ else
+ fatalerror("unhandled cammu store 0x%08x size %d (%s)",
+ address, access_size(sizeof(T)), machine().describe_context().c_str());
+
+ return true;
+ }
+
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<T(T)>>::value, bool> modify(const u32 ssw, const u32 address, U &&apply)
+ {
+ translated_t t = translate_address(ssw, address, access_size(sizeof(T)), access_type(READ | WRITE));
+
+ if (!t.cache)
+ return false;
+
+ switch (sizeof(T))
+ {
+ case 4: t.cache->write_dword(t.address, apply(T(t.cache->read_dword(t.address)))); break;
+ default:
+ fatalerror("unhandled modify 0x%08x size %d (%s)",
+ address, access_size(sizeof(T)), machine().describe_context().c_str());
+ }
+
+ return true;
+ }
+
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> fetch(const u32 ssw, const u32 address, U &&apply)
+ {
+ translated_t t = translate_address(ssw, address, access_size(sizeof(T)), EXECUTE);
+
+ if (!t.cache)
+ return false;
+
+ switch (sizeof(T))
+ {
+ case 2: apply(T(t.cache->read_word(t.address))); break;
+ case 4:
+ {
+ // check for unaligned access
+ if (address & 0x2)
+ {
+ // check for page span
+ if ((address & CAMMU_PAGE_MASK) == (CAMMU_PAGE_SIZE - 2))
+ {
+ translated_t u = translate_address(ssw, address + 2, access_size(sizeof(u16)), EXECUTE);
+ if (u.cache)
+ {
+ const u16 lsw = t.cache->read_word(t.address);
+ const u16 msw = t.cache->read_word(u.address);
+
+ apply((T(msw) << 16) | lsw);
+ }
+ else
+ return false;
+ }
+ else
+ apply(T(t.cache->read_dword_unaligned(t.address)));
+ }
+ else
+ apply(T(t.cache->read_dword(t.address)));
+ }
+ break;
+ default:
+ fatalerror("unhandled fetch 0x%08x size %d (%s)\n",
+ address, access_size(sizeof(T)), machine().describe_context().c_str());
+ }
+
+ return true;
+ }
+
+ // address translation for debugger
+ bool memory_translate(const u32 ssw, const int spacenum, const int intention, offs_t &address);
+
+protected:
+ cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ enum access_size : u8
+ {
+ BYTE = 1,
+ WORD = 2,
+ DWORD = 4,
+ QWORD = 8
+ };
+
+ enum access_type : u8
+ {
+ READ = 1,
+ WRITE = 2,
+ EXECUTE = 4,
+
+ // matrix abbreviations and combinations
+ N = 0,
+ R = READ,
+ W = WRITE,
+ RW = READ | WRITE,
+ RE = READ | EXECUTE,
+ RWE = READ | WRITE | EXECUTE,
+ };
+
+ struct translated_t
+ {
+ memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache *const cache;
+ const u32 address;
+ };
+
+ struct pte_t
+ {
+ u32 entry;
+ u32 address;
+ };
+
+ struct memory_t
+ {
+ address_space *space = nullptr;
+ memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache cache;
+ };
+
+ // address translation
+ virtual translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode);
+ pte_t get_pte(const u32 va, const bool user);
+
+ // helpers
+ virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const = 0;
+ virtual bool get_alignment() const = 0;
+ virtual u32 get_pdo(const bool user) const = 0;
+ virtual system_tag_t get_ust_space() const = 0;
+ virtual void set_fault(const u32 address, const exception_vector type) = 0;
+
+ // device state
+ devcb_write16 m_exception_func;
+ memory_t m_memory[8];
+};
+
+class cammu_c4_device : public cammu_device
+{
+public:
+ // TODO: translation lookaside buffer and register access
+ virtual void map(address_map &map) = 0;
+ virtual u32 cammu_r(const u32 address) override { return 0; }
+ virtual void cammu_w(const u32 address, const u32 data) override {}
+
+ void set_cammu_id(const u32 cammu_id) { m_control = cammu_id; }
+
+ u32 s_pdo_r() { return m_s_pdo; }
+ void s_pdo_w(offs_t offset, u32 data, u32 mem_mask = ~0) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
+ u32 u_pdo_r() { return m_u_pdo; }
+ void u_pdo_w(offs_t offset, u32 data, u32 mem_mask = ~0) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
+
+ virtual u32 control_r() = 0;
+ virtual void control_w(offs_t offset, u32 data, u32 mem_mask = ~0) = 0;
+
+ u32 i_fault_r() { return m_i_fault; }
+ void i_fault_w(u32 data) { m_i_fault = data; }
+ u32 fault_address_1_r() { return m_fault_address_1; }
+ void fault_address_1_w(u32 data) { m_fault_address_1 = data; }
+ u32 fault_address_2_r() { return m_fault_address_2; }
+ void fault_address_2_w(u32 data) { m_fault_address_2 = data; }
+ u32 fault_data_1_lo_r() { return m_fault_data_1_lo; }
+ void fault_data_1_lo_w(u32 data) { m_fault_data_1_lo = data; }
+ u32 fault_data_1_hi_r() { return m_fault_data_1_hi; }
+ void fault_data_1_hi_w(u32 data) { m_fault_data_1_hi = data; }
+ u32 fault_data_2_lo_r() { return m_fault_data_2_lo; }
+ void fault_data_2_lo_w(u32 data) { m_fault_data_2_lo = data; }
+ u32 fault_data_2_hi_r() { return m_fault_data_2_hi; }
+ void fault_data_2_hi_w(u32 data) { m_fault_data_2_hi = data; }
+
+protected:
+ cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual void device_start() override;
+
+ virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override;
+ virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }
+
+ virtual void set_fault(const u32 address, const exception_vector type) override { m_fault_address_1 = address; m_exception_func(type); }
+
+ u32 m_s_pdo;
+ u32 m_u_pdo;
+ u32 m_control;
+
+ u32 m_i_fault;
+ u32 m_fault_address_1;
+ u32 m_fault_address_2;
+ u32 m_fault_data_1_lo;
+ u32 m_fault_data_1_hi;
+ u32 m_fault_data_2_lo;
+ u32 m_fault_data_2_hi;
+};
+
+class cammu_c4t_device : public cammu_c4_device
+{
+public:
+ cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual void map(address_map &map) override;
+
+ u32 ram_line_r() { return m_ram_line; }
+ void ram_line_w(u32 data) { m_ram_line = data; }
+
+ u32 htlb_offset_r() { return m_htlb_offset; }
+ void htlb_offset_w(u32 data) { m_htlb_offset = data; }
+
+ u32 c4_bus_poll_r() { return m_c4_bus_poll; }
+ void c4_bus_poll_w(u32 data) { m_c4_bus_poll = data; }
+
+ enum control_mask : u32
+ {
+ CNTL_RUV = 0x00000001, // reset user valid
+ CNTL_RSV = 0x00000002, // reset supervisor valid
+ CNTL_DBWR = 0x00000004, // disable bus watch read
+ CNTL_ATD = 0x00000008, // alignment trap disable
+ CNTL_UST = 0x00000030, // unmapped system tag
+ CNTL_IOTS = 0x00000040, // i/o tag select
+ CNTL_UVS = 0x00000080, // user valid status
+ CNTL_PB = 0x00000100, // purge busy
+ CNTL_CICT = 0x00000200, // clear i-side cache tags
+ CNTL_CFR = 0x00000400, // clear trap registers
+ CNTL_HTLBD = 0x00000800, // htlb disable
+ CNTL_CDCT = 0x00001000, // clear d-side cache tags
+ CNTL_CID = 0xff000000 // cammu id
+ };
+
+ enum control_ust_mask : u32
+ {
+ UST_NCA = 0x00, // unmapped system tag, noncacheable
+ UST_PWT = 0x10, // unmapped system tag, write through
+ UST_PCB = 0x20, // unmapped system tag, copy back
+ UST_PGE = 0x30 // unmapped system tag, purge mode
+ };
+
+ enum control_cid_mask : u32
+ {
+ CID_C4T = 0x00000000 // unknown
+ };
+
+ virtual u32 control_r() override { return m_control; }
+ virtual void control_w(offs_t offset, u32 data, u32 mem_mask = ~0) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
+ u32 bio_control_r() { return m_bio_control; }
+ void bio_control_w(u32 data) { m_bio_control = data; }
+ u32 bio_address_tag_r() { return m_bio_address_tag; }
+ void bio_address_tag_w(u32 data) { m_bio_address_tag = data; }
+
+ u32 cache_data_lo_r() { return m_cache_data_lo; }
+ void cache_data_lo_w(u32 data) { m_cache_data_lo = data; }
+ u32 cache_data_hi_r() { return m_cache_data_hi; }
+ void cache_data_hi_w(u32 data) { m_cache_data_hi = data; }
+ u32 cache_cpu_tag_r() { return m_cache_cpu_tag; }
+ void cache_cpu_tag_w(u32 data) { m_cache_cpu_tag = data; }
+ u32 cache_system_tag_valid_r() { return m_cache_system_tag_valid; }
+ void cache_system_tag_valid_w(u32 data) { m_cache_system_tag_valid = data; }
+ u32 cache_system_tag_r() { return m_cache_system_tag; }
+ void cache_system_tag_w(u32 data) { m_cache_system_tag = data; }
+ u32 tlb_va_line_r() { return m_tlb_va_line; }
+ void tlb_va_line_w(u32 data) { m_tlb_va_line = data; }
+ u32 tlb_ra_line_r() { return m_tlb_ra_line; }
+ void tlb_ra_line_w(u32 data) { m_tlb_ra_line = data; }
+
+protected:
+ virtual void device_start() override;
+
+ virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
+ virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & (CNTL_IOTS | CNTL_UST)) >> 4); }
+
+private:
+ u32 m_ram_line;
+ u32 m_htlb_offset;
+ u32 m_c4_bus_poll;
+ u32 m_bio_control;
+ u32 m_bio_address_tag;
+
+ u32 m_cache_data_lo;
+ u32 m_cache_data_hi;
+ u32 m_cache_cpu_tag;
+ u32 m_cache_system_tag_valid;
+ u32 m_cache_system_tag;
+ u32 m_tlb_va_line;
+ u32 m_tlb_ra_line;
+};
+
+class cammu_c4i_device : public cammu_c4_device
+{
+public:
+ cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual void map(address_map &map) override;
+
+ enum control_mask : u32
+ {
+ CNTL_LRAS = 0x00000001, // tlb line replacement
+ CNTL_BWWD = 0x00000002, // buswatch write disable
+ CNTL_BWRD = 0x00000004, // buswatch read disable
+ CNTL_FSR = 0x00000010, // fake system response
+ CNTL_ATD = 0x00000100, // alignment trap disable
+ CNTL_UMM = 0x00003000, // unmapped mode address space select
+ CNTL_POLL = 0x00030000, // poll bus signals
+ CNTL_BM = 0x00040000, // burst mode address space select
+ CNTL_PZBS = 0x00080000, // page 0 boot select
+ CNTL_CRR = 0x00700000, // cache memory refresh rate
+ CNTL_CID = 0xff000000 // cammu identification
+ };
+
+ enum control_umm_mask : u32
+ {
+ UMM_MM = 0x00000000, // mm space, noncacheable
+ UMM_MMRIO = 0x00001000, // mm or i/o space, noncacheable
+ UMM_IO = 0x00002000 // i/o space noncacheable
+ };
+
+ enum control_crr_mask : u32
+ {
+ CRR_GT131 = 0x00000000, // clock rate over 131 MHz
+ CRR_GT66 = 0x00100000, // clock rate over 66 MHz
+ CRR_GT33 = 0x00200000, // clock rate over 33 MHz
+ CRR_GT8 = 0x00300000, // clock rate over 8 MHz
+ CRR_GT2 = 0x00400000, // clock rate over 2 MHz
+ CRR_GT1 = 0x00500000, // clock rate over 1 MHz
+ CRR_GTHALF = 0x00600000, // clock rate over 0.5 MHz
+ CRR_OFF = 0x00700000, // refresh off
+ };
+
+ // c4i cammu identification (rev 2 and rev 3 known to have existed)
+ enum control_cid_mask : u32
+ {
+ CID_C4IR0 = 0x00000000,
+ CID_C4IR2 = 0x02000000
+ };
+
+ virtual u32 control_r() override { return m_control; }
+ virtual void control_w(offs_t offset, u32 data, u32 mem_mask) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
+
+ enum reset_mask : u32
+ {
+ RESET_CDCT = 0x00000001, // clear data cache tags
+ RESET_RDUV = 0x00000100, // reset all d-side uv flags
+ RESET_RDSV = 0x00001000, // reset all d-side sv flags
+ RESET_CICT = 0x00010000, // clear ins. cache tags
+ RESET_RIUV = 0x01000000, // reset all i-side uv flags
+ RESET_RISV = 0x10000000, // reset all i-side sv flags
+ RESET_FLUSH = 0x40000000, // flush out burst io buffer
+ RESET_CFR = 0x80000000 // clear fault registers
+ };
+ u32 reset_r() { return m_reset; }
+ void reset_w(u32 data) { m_reset = data; }
+
+ u32 clr_s_data_tlb_r() { return m_clr_s_data_tlb; }
+ void clr_s_data_tlb_w(u32 data) { m_clr_s_data_tlb = data; }
+ u32 clr_u_data_tlb_r() { return m_clr_u_data_tlb; }
+ void clr_u_data_tlb_w(u32 data) { m_clr_u_data_tlb = data; }
+ u32 clr_s_insn_tlb_r() { return m_clr_s_insn_tlb; }
+ void clr_s_insn_tlb_w(u32 data) { m_clr_s_insn_tlb = data; }
+ u32 clr_u_insn_tlb_r() { return m_clr_u_insn_tlb; }
+ void clr_u_insn_tlb_w(u32 data) { m_clr_u_insn_tlb = data; }
+
+ u32 test_data_r() { return m_test_data; }
+ void test_data_w(u32 data) { m_test_data = data; }
+
+ u32 test_address_r() { return m_test_address; }
+ void test_address_w(u32 data) { m_test_address = data; }
+
+protected:
+ virtual void device_start() override;
+
+ virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
+ // FIXME: don't really know how unmapped mode works on c4i
+ virtual system_tag_t get_ust_space() const override { return (m_control & UMM_IO) ? ST4 : ST3; }
+
+private:
+ u32 m_reset;
+ u32 m_clr_s_data_tlb;
+ u32 m_clr_u_data_tlb;
+ u32 m_clr_s_insn_tlb;
+ u32 m_clr_u_insn_tlb;
+ u32 m_test_data;
+ u32 m_test_address;
+};
+
+class cammu_c3_device : public cammu_device
+{
+public:
+ cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void add_linked(cammu_c3_device *child) { m_linked.push_back(child); }
+
+protected:
+ // device-level overrides
+ virtual void device_reset() override;
+ virtual void device_start() override;
+
+ // translation lookaside buffer and register access
+ virtual u32 cammu_r(const u32 address) override;
+ virtual void cammu_w(const u32 address, const u32 data) override;
+
+ // address translation
+ virtual translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode) override;
+
+private:
+ enum cammu_address_mask : u32
+ {
+ CAMMU_TLB_VA = 0x00000001, // tlb va/ra select
+ CAMMU_TLB_X = 0x00000002, // tlb x/w line select
+ CAMMU_TLB_SET = 0x000000fc, // tlb set select
+ CAMMU_REG = 0x000000ff, // register select
+ CAMMU_SELECT = 0x00000700, // cammu select
+ };
+ enum tlb_ra_mask : u32
+ {
+ TLB_RA_U = 0x00000001, // used flag
+ TLB_RA_R = 0x00000002, // referenced flag
+ TLB_RA_D = 0x00000004, // dirty flag
+ TLB_RA_PL = 0x00000078, // protection level
+ TLB_RA_ST = 0x00000e00, // system tag
+ TLB_RA_RA = 0xfffff000, // real address
+ };
+ enum tlb_va_mask : u32
+ {
+ TLB_VA_UV = 0x00000002, // user valid flag
+ TLB_VA_SV = 0x00000004, // supervisor valid flag
+ TLB_VA_VA = 0xfffc0000, // virtual address tag
+ };
+
+ /*
+ * The C1/C3 CAMMU has 64-entry, two-way set associative TLB, with lines
+ * grouped into W and X compartments. The associated U flag is set to
+ * indicate that the W line of the set was most recently accessed, and
+ * cleared when the X line was most recently accessed. On TLB miss, the
+ * least recently used line as indicated by this flag is replaced.
+ *
+ * Each line consists of a real address field and a virtual address field.
+ * The real address field format is practically identical to the page table
+ * entry format.
+ */
+ struct tlb_line_t
+ {
+ u32 ra; // real address field
+ u32 va; // virtual address field
+
+ memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache cache;
+ };
+ struct tlb_set_t
+ {
+ tlb_line_t w;
+ tlb_line_t x;
+ bool u;
+ };
+
+ enum cammu_select_mask : u32
+ {
+ CAMMU_D_TLB = 0x000, // d-cammu tlb
+ CAMMU_D_REG = 0x100, // d-cammu register
+ CAMMU_I_TLB = 0x200, // i-cammu tlb
+ CAMMU_I_REG = 0x300, // i-cammu register
+ CAMMU_G_TLB = 0x400, // global tlb
+ CAMMU_G_REG = 0x500, // global register
+ };
+ enum cammu_register_mask : u8
+ {
+ CAMMU_REG_SPDO = 0x04, // supervisor pdo register
+ CAMMU_REG_UPDO = 0x08, // user pdo register
+ CAMMU_REG_FAULT = 0x10, // fault register
+ CAMMU_REG_CONTROL = 0x40, // control register
+ CAMMU_REG_RESET = 0x80, // reset register
+ };
+
+ enum control_mask : u32
+ {
+ CNTL_EP = 0x00000001, // enable prefetch
+ CNTL_EWCW = 0x00000002, // enable watch cpu writes
+ CNTL_EWIW = 0x00000004, // enable watch i/o writes
+ CNTL_EWIR = 0x00000008, // enable watch i/o reads
+ CNTL_UST = 0x00000030, // unmapped system tag
+ CNTL_CV = 0x00000100, // clear valid
+ CNTL_ATE = 0x00000200, // alignment trap enable
+ CNTL_CID = 0xff000000 // cammu id
+ };
+ enum control_ust_mask : u32
+ {
+ UST_0 = 0x00000000, // private, write-through, main memory space
+ UST_1 = 0x00000010, // shared, write-through, main memory space
+ UST_2 = 0x00000020, // private, copy-back, main memory space
+ UST_3 = 0x00000030 // noncacheable, main memory space
+ };
+ enum control_cid_mask : u32
+ {
+ CID_C3 = 0x00000000 // unknown
+ };
+
+ enum reset_mask : u32
+ {
+ RESET_RLVW = 0x00000001, // reset all W line LV flags in cache
+ RESET_RLVX = 0x00000002, // reset all X line LV flags in cache
+ RESET_RSV = 0x00000004, // reset all SV flags in tlb
+ RESET_RUV = 0x00000008, // reset all UV flags in tlb
+ RESET_RD = 0x00000010, // reset all D flags in tlb
+ RESET_RR = 0x00000020, // reset all R flags in tlb
+ RESET_RU = 0x00000040, // reset all U flags in cache
+ };
+
+ u32 tlb_r(const u8 address) const;
+ void tlb_w(const u8 address, const u32 data);
+ tlb_line_t &tlb_lookup(const bool user, const u32 virtual_address, const access_type mode);
+
+ u32 s_pdo_r() const { return m_s_pdo; }
+ void s_pdo_w(const u32 data) { m_s_pdo = data & PDO_MASK; }
+ u32 u_pdo_r() const { return m_u_pdo; }
+ void u_pdo_w(const u32 data) { m_u_pdo = data & PDO_MASK; }
+ u32 fault_r() const { return m_fault; }
+ void fault_w(const u32 data) { m_fault = data; }
+ u32 control_r() const { return m_control; }
+ void control_w(const u32 data) { m_control = (m_control & CNTL_CID) | (data & ~CNTL_CID); }
+ void reset_w(const u32 data);
+
+ virtual bool get_alignment() const override { return m_control & CNTL_ATE; }
+ virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & CNTL_UST) >> 4); }
+ virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override;
+ virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }
+
+ virtual void set_fault(const u32 address, const exception_vector type) override { m_fault = address; m_exception_func(type); }
+
+ static const u8 protection_matrix[4][16];
+
+ // device state
+ std::vector<cammu_c3_device *> m_linked;
+
+ u32 m_s_pdo;
+ u32 m_u_pdo;
+ u32 m_fault;
+ u32 m_control;
+
+ tlb_set_t m_tlb[64];
+};
+
+// device type definitions
+DECLARE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device)
+DECLARE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device)
+DECLARE_DEVICE_TYPE(CAMMU_C3, cammu_c3_device)
+
+#endif // MAME_MACHINE_CAMMU_H