summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/ericsson/eispc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/ericsson/eispc.cpp')
-rw-r--r--src/mame/ericsson/eispc.cpp1074
1 files changed, 1074 insertions, 0 deletions
diff --git a/src/mame/ericsson/eispc.cpp b/src/mame/ericsson/eispc.cpp
new file mode 100644
index 00000000000..4b254ebc314
--- /dev/null
+++ b/src/mame/ericsson/eispc.cpp
@@ -0,0 +1,1074 @@
+// license:BSD-3-Clause
+// copyright-holders:Joakim Larsson Edström
+/***************************************************************************************************
+ *
+ * Ericsson Information Systems PC "compatibles"
+ *
+ * The Ericsson PC was the first original Ericsson design for the office PC market replacing the
+ * Step/One which was an OEM:ed clone of the Matsushita Mybrain 3000 (see myb3k.cpp driver).
+ *
+ **************************************************************
+ * Ericsson PC
+ *------------
+ * Links: https://youtu.be/6uilOdMJc24
+ * Form Factor: Desktop
+ * CPU: 8088 @ 4.77MHz
+ * RAM: 256K
+ * Bus: 6x ISA
+ * Video: Monchrome or Color 80x25 character mode. 320x200 and 640x400 grahics modes
+ * Display: Orange Gas Plasma (GP) display
+ * Mass storage: 2 x 5.25" 360K or 1 20Mb HDD
+ * On board ports: Beeper,
+ * Ports: serial, parallel
+ * Internal Options: Up to 640K RAM through add-on RAM card
+ * Misc: The hardware was not 100% PC compatible so non BIOS based software would not always run. 50.000+ units sold
+ *
+ * TODO
+ * - Complete the Ericsson 1070 MDA ISA board and test all the graphics modes including 640x400 (aka HR)
+ * - Add the Ericsson 1065 HDC and boot from a hard drive
+ * - Add softlist
+ * - Pass the diagnostics software system test at EPC2.IMD, it currently hangs the keyboard.
+ * A later version of the test on EPC5.IMD works though so need to verify EPC2.IMD on real hardware first.
+ *
+ * CREDITS The driver code is inspired from m24.cpp, myb3k.cpp and genpc.cpp. Information about the EPC has
+ * been contributed by many, mainly the people at Dalby Computer museum http://www.datormuseum.se/
+ * A dead pcb was donated by rfka01 and rom dumps by ZnaxQue@sweclockers.com
+ *
+ ************************************************************************************************************/
+/*
+ Links
+ -----
+
+ */
+
+#include "emu.h"
+
+#include "eispc_kb.h"
+#include "epc.lh"
+
+// Devices
+#include "cpu/i86/i86.h"
+#include "machine/am9517a.h"
+#include "machine/i8087.h"
+#include "machine/i8251.h"
+#include "machine/i8255.h"
+#include "machine/pit8253.h"
+#include "machine/pic8259.h"
+#include "machine/upd765.h"
+#include "machine/ins8250.h"
+
+// Expansion cards
+//#include "bus/isa/isa.h"
+//#include "bus/isa/isa_cards.h"
+#include "bus/isa/ega.h"
+#include "bus/isa/eis_hgb107x.h"
+#include "bus/isa/eis_twib.h"
+#include "machine/pc_lpt.h"
+
+#include "machine/ram.h"
+#include "machine/timer.h"
+#include "sound/spkrdev.h"
+#include "speaker.h"
+#include "imagedev/floppy.h"
+#include "bus/rs232/rs232.h"
+
+#define LOG_PPI (1U << 1)
+#define LOG_PIT (1U << 2)
+#define LOG_PIC (1U << 3)
+#define LOG_KBD (1U << 4)
+#define LOG_DMA (1U << 5)
+#define LOG_IRQ (1U << 6)
+#define LOG_FDC (1U << 7)
+#define LOG_LPT (1U << 8)
+#define LOG_NMI (1U << 9)
+#define LOG_BITS (1U << 10)
+#define LOG_FPU (1U << 11)
+#define LOG_COM (1U << 12)
+
+//#define VERBOSE (LOG_LPT)
+//#define LOG_OUTPUT_STREAM std::cout
+
+#include "logmacro.h"
+
+#define LOGPPI(...) LOGMASKED(LOG_PPI, __VA_ARGS__)
+#define LOGPIT(...) LOGMASKED(LOG_PIT, __VA_ARGS__)
+#define LOGPIC(...) LOGMASKED(LOG_PIC, __VA_ARGS__)
+#define LOGKBD(...) LOGMASKED(LOG_KBD, __VA_ARGS__)
+#define LOGDMA(...) LOGMASKED(LOG_DMA, __VA_ARGS__)
+#define LOGIRQ(...) LOGMASKED(LOG_IRQ, __VA_ARGS__)
+#define LOGFDC(...) LOGMASKED(LOG_FDC, __VA_ARGS__)
+#define LOGLPT(...) LOGMASKED(LOG_LPT, __VA_ARGS__)
+#define LOGNMI(...) LOGMASKED(LOG_NMI, __VA_ARGS__)
+#define LOGBITS(...) LOGMASKED(LOG_BITS, __VA_ARGS__)
+#define LOGFPU(...) LOGMASKED(LOG_FPU, __VA_ARGS__)
+#define LOGCOM(...) LOGMASKED(LOG_COM, __VA_ARGS__)
+
+
+namespace {
+
+class epc_state : public driver_device
+{
+public:
+ epc_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_ram(*this, RAM_TAG)
+ , m_isabus(*this, "isabus")
+ , m_dma8237a(*this, "dma8237")
+ , m_ppi8255(*this, "ppi8255")
+ , m_io_dsw(*this, "DSW")
+ , m_io_j10(*this, "J10")
+ , m_io_s21(*this, "S21")
+ , m_lpt(*this, "lpt")
+ , m_kbd8251(*this, "kbd8251")
+ , m_keyboard(*this, "keyboard")
+ , m_leds(*this, "kbled%u")
+ , m_pic8259(*this, "pic8259")
+ , m_pit8253(*this, "pit8253")
+ , m_speaker(*this, "speaker")
+ , m_fdc(*this, "fdc")
+ , m_floppy_connectors(*this, "fdc:%u", 0)
+ , m_uart(*this, "uart")
+ { }
+
+ void epc(machine_config &config);
+ void init_epc();
+
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+ virtual void machine_reset() override ATTR_COLD;
+
+private:
+ required_device<i8086_cpu_device> m_maincpu;
+ required_device<ram_device> m_ram;
+ required_device<isa8_device> m_isabus;
+
+ // DMA
+ void dma_tc_w(int state);
+ void dreq0_ck_w(int state);
+ void epc_dma_hrq_changed(int state);
+ void epc_dma8237_out_eop(int state);
+ uint8_t epc_dma_read_byte(offs_t offset);
+ void epc_dma_write_byte(offs_t offset, uint8_t data);
+ template <int Channel> uint8_t epc_dma8237_io_r(offs_t offset);
+ template <int Channel> void epc_dma8237_io_w(offs_t offset, uint8_t data);
+ template <int Channel> void epc_dack_w(int state);
+ required_device<am9517a_device> m_dma8237a;
+ uint8_t m_dma_segment[4];
+ uint8_t m_dma_active;
+ bool m_tc;
+ bool m_txd;
+ bool m_rxrdy;
+ bool m_int;
+ bool m_dreq0_ck;
+
+ // PPI
+ required_device<i8255_device> m_ppi8255;
+ void ppi_portb_w(uint8_t data);
+ uint8_t ppi_portc_r();
+ uint8_t m_ppi_portb;
+ required_ioport m_io_dsw;
+ required_ioport m_io_j10;
+ required_ioport m_io_s21;
+
+ // Printer port
+ optional_device<pc_lpt_device> m_lpt;
+
+ // Keyboard Controller/USART
+ required_device<i8251_device> m_kbd8251;
+ required_device<eispc_keyboard_device> m_keyboard;
+ emu_timer *m_kbdclk_timer;
+ TIMER_CALLBACK_MEMBER(rxtxclk_w);
+ bool m_8251rxtx_clk_state;
+ bool m_kbdclk_state;
+ bool m_8251dtr_state;
+ int m_kbdclk;
+ output_finder<3> m_leds;
+
+ // Interrupt Controller
+ required_device<pic8259_device> m_pic8259;
+ void int_w(int state);
+ uint8_t m_nmi_enabled;
+ uint8_t m_8087_int = 0;
+ uint8_t m_parer_int = 0;
+ uint8_t m_iochck_int = 0;
+ void update_nmi();
+
+ // Timer
+ required_device<pit8253_device> m_pit8253;
+
+ // Speaker
+ void speaker_ck_w(int state);
+ required_device<speaker_sound_device> m_speaker;
+ bool m_pc4;
+ bool m_pc5;
+
+ void epc_map(address_map &map) ATTR_COLD;
+ void epc_io(address_map &map) ATTR_COLD;
+
+ // FDC
+ void check_fdc_irq();
+ void check_fdc_drq();
+ required_device<i8272a_device> m_fdc;
+ uint8_t m_ocr;
+ bool m_irq; // System signal after glue logic
+ bool m_drq; // System signal after glue logic
+ bool m_fdc_irq; // FDC output pin
+ bool m_fdc_drq; // FDC output pin
+
+ optional_device_array<floppy_connector, 4> m_floppy_connectors;
+
+ // UART
+ required_device<ins8250_device> m_uart;
+};
+
+void epc_state::check_fdc_irq()
+{
+ bool pirq = m_irq;
+ m_irq = m_fdc_irq && (m_ocr & 4) && (m_ocr & 8); // IRQ enabled and not in reset?
+ if(m_irq != pirq) // has the state changed?
+ {
+ LOGIRQ("FDC: IRQ6 request: %d\n", m_irq);
+ m_pic8259->ir6_w(m_irq);
+ }
+}
+
+void epc_state::check_fdc_drq()
+{
+ bool pdrq = m_drq;
+ m_drq = m_fdc_drq && (m_ocr & 4) && (m_ocr & 8); // DREQ enabled and not in reset?
+ if(m_drq != pdrq) // has the state changed?
+ {
+ LOGDMA("FDC: DMA channel 2 request: %d\n", m_drq);
+ m_dma8237a->dreq2_w(m_drq);
+ }
+}
+
+void epc_state::epc_map(address_map &map)
+{
+ map.unmap_value_high();
+ map(0x20000, 0x9ffff).noprw(); // Base RAM - mapped to avoid unmaped errors when BIOS is probing RAM size
+ // 0xa0000-0xaffff is reserved
+ map(0xb0000, 0xb7fff).noprw(); // Monochrome RAM - mapped to avoid unaped errors when BIOS is probing RAM size
+ map(0xb0000, 0xb7fff).noprw(); // Monochrome RAM - mapped to avoid unaped errors when BIOS is probing RAM size
+ map(0xb8000, 0xbffff).noprw(); // Color/Graphics RAM - mapped to avoid unaped errors when BIOS is probing RAM size
+ map(0xc0000, 0xeffff).noprw(); // Expansion ROM area - Hard Disk BIOS etc
+ map(0xf0000, 0xfffff).rom().region("bios", 0);
+}
+
+void epc_state::epc_io(address_map &map)
+{
+ map(0x0000, 0x000f).mirror(0x10).lrw8(
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t data = m_dma8237a->read(offset);
+ LOGDMA("dma8237_r %04x\n", offset);
+ return data;
+ },
+ "dma8237_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGDMA("dma8237_w %04x: %02x\n", offset, data);
+ m_dma8237a->write(offset, data);
+ },
+ "dma8237_w"
+ );
+
+ map(0x0020, 0x0021).mirror(0x1e).lrw8(
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t data = m_pic8259->read(offset);
+ LOGPIC("pic8259_r %04x: %02x\n", offset, data);
+ return data;
+ },
+ "pic8259_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGPIC("pic8259_w %04x: %02x\n", offset, data);
+ m_pic8259->write(offset, data);
+ },
+ "pic8259_w"
+ );
+
+ map(0x0040, 0x0043).mirror(0x1c).lrw8(
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t data = m_pit8253->read(offset);
+ LOGPIT("pit8253_r %04x\n", offset);
+ return data;
+ },
+ "pit8253_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGPIT("pit8253_w %04x: %02x\n", offset, data);
+ m_pit8253->write(offset, data);
+ },
+ "pit8253_w"
+ );
+
+ map(0x0060, 0x0060).mirror(0x1c).lrw8(
+ [this]() -> uint8_t
+ {
+ uint8_t data = m_kbd8251->data_r();
+ LOGKBD("kbd8251_r %02x\n", data);
+ return data;
+ },
+ "kbd_8251_data_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGKBD("kbd8251_w 0x60 %02x\n", data);
+ m_kbd8251->data_w(data);
+ },
+ "kbd_8251_data_w"
+ );
+ // NOTE: PPI Port A is not mapped
+ map(0x0061, 0x0061).mirror(0x1c).lrw8( // PPI Port B
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t data = m_ppi8255->read(1);
+ LOGPPI("ppi8255_r Port B: %02x\n", data);
+ return data;
+ },
+ "ppi8255_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGPPI("ppi8255_w Port B: %02x\n", data);
+ m_ppi8255->write(1, data);
+ },
+ "ppi8255_w"
+ );
+
+ map(0x0062, 0x0062).mirror(0x1c).lrw8( // PPI Port C
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t data = m_ppi8255->read(2);
+ LOGPPI("ppi8255_r Port C: %02x\n", data);
+ return data;
+ },
+ "ppi8255_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGPPI("ppi8255_w Port C: %02x\n", data);
+ m_ppi8255->write(2, data);
+ },
+ "ppi8255_w"
+ );
+
+ map(0x0063, 0x0063).lrw8( // PPI Control register
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t data = m_ppi8255->read(3);
+ LOGPPI("ppi8255_r Control: %02x\n", data);
+ return data;
+ },
+ "ppi8255_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGPPI("ppi8255_w Control: %02x\n", data);
+ m_ppi8255->write(3, data);
+ },
+ "ppi8255_w"
+ );
+
+ map(0x0070, 0x0070).mirror(0x0e).lw8(
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGKBD("kbd8251_w 0x70: %02x\n", data);
+ m_kbd8251->data_w(data);
+ },
+ "i8251_data_w"
+ );
+
+ map(0x0071, 0x0071).mirror(0x0e).lrw8(
+ [this](offs_t offset) -> uint8_t
+ {
+ uint8_t stat = m_kbd8251->status_r();
+ //LOGKBD("kbd8251_status_r %02x\n", stat);
+ return stat;
+ },
+ "kbd_8251_stat_ctrl_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGKBD("kbd8251_control_w 0x71: %02x\n", data);
+ m_kbd8251->control_w(data);
+ },
+ "kbd_8251_stat_ctrl_w"
+ );
+
+ map(0x0080, 0x0083).mirror(0xc).lw8(
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGDMA("dma_segment_w %04x: %02x\n", offset, data);
+ m_dma_segment[offset] = data & 0x0f;
+ },
+ "dma_segement_w"
+ );
+
+ map(0x00a0, 0x00a1).mirror(0xe).lw8(
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGNMI("nmi_enable_w %04x: %02x\n", offset, data);
+ m_nmi_enabled = BIT(data,7);
+ update_nmi();
+ },
+ "nmi_enable_w"
+ );
+
+ // FDC Output Control Register (same as PC XT DOR)
+ map(0x03f2, 0x03f3).lw8( // B0-B1 Drive select 0-3
+ [this](offs_t offset, uint8_t data) // B2 FDC Reset line
+ { // B3 Enable FDC DMA/IRQ
+ LOGFDC("FDC OCR: %02x\n", data);// B4-B7 Motor on for selected drive
+ uint8_t pocr = m_ocr;
+ uint8_t fid = m_ocr & 3;
+ m_ocr = data;
+ if ((m_ocr & 4) && m_floppy_connectors[fid]) // Not in reset and there is a floppy drive attached
+ {
+ floppy_image_device *floppy = m_floppy_connectors[fid]->get_device(); // try to retrieve the floppy
+ if (floppy)
+ {
+ LOGFDC(" - Motor %s for drive %d\n", (m_ocr & (0x10 << fid)) ? "ON" : "OFF", fid);
+ floppy->mon_w(!(m_ocr & (0x10 << fid)));
+ LOGFDC(" - Setting a floppy for drive %d\n", fid);
+ m_fdc->set_floppy((m_ocr & (0x10 << fid)) ? floppy : nullptr);
+ }
+ }
+ if (((pocr ^ m_ocr) & 4) && (m_ocr & 4) == 0) // If FDC reset state bit has changed to low then reset the FDC
+ m_fdc->reset();
+ check_fdc_irq();
+ check_fdc_drq();
+ },
+ "ocr_w"
+ );
+
+ map(0x03f4, 0x03f5).m(m_fdc, FUNC(i8272a_device::map));
+
+ map(0x03bc, 0x03be).lrw8(
+ [this](offs_t offset, uint8_t mem_mask) -> uint8_t
+ {
+ uint8_t data = m_lpt->read(offset);
+ LOGLPT("LPT read offset %02x: %02x\n", offset, data);
+ return data;
+ },
+ "lpt_r",
+ [this](offs_t offset, uint8_t data)
+ {
+ LOGLPT("LPT write offset %02x: %02x\n", offset, data);
+ m_lpt->write(offset, data);
+ },
+ "lpt_w"
+ );
+
+ map(0x03f8, 0x03ff).rw(m_uart, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w));
+}
+
+void epc_state::machine_start()
+{
+ m_maincpu->space(AS_PROGRAM).install_ram(0, m_ram->size() - 1, m_ram->pointer());
+
+ std::fill_n(&m_dma_segment[0], 4, 0);
+
+ save_item(NAME(m_dma_segment));
+ save_item(NAME(m_dma_active));
+ save_item(NAME(m_tc));
+ save_item(NAME(m_txd));
+ save_item(NAME(m_rxrdy));
+ save_item(NAME(m_int));
+ save_item(NAME(m_dreq0_ck));
+ save_item(NAME(m_ppi_portb));
+ save_item(NAME(m_8251rxtx_clk_state));
+ save_item(NAME(m_kbdclk_state));
+ save_item(NAME(m_kbdclk));
+ save_item(NAME(m_8251dtr_state));
+ save_item(NAME(m_nmi_enabled));
+ save_item(NAME(m_8087_int));
+ save_item(NAME(m_parer_int));
+ save_item(NAME(m_iochck_int));
+ save_item(NAME(m_pc4));
+ save_item(NAME(m_pc5));
+ save_item(NAME(m_ocr));
+ save_item(NAME(m_irq));
+ save_item(NAME(m_drq));
+ save_item(NAME(m_fdc_irq));
+ save_item(NAME(m_fdc_drq));
+
+ m_leds.resolve();
+}
+
+void epc_state::machine_reset()
+{
+ m_dma_active = 0;
+ m_tc = false;
+ m_txd = false;
+ m_rxrdy = false;
+ m_int = 1;
+ m_dreq0_ck = true;
+ m_ppi_portb = 0;
+ m_8251rxtx_clk_state = 0;
+ m_kbdclk_state = 0;
+ m_kbdclk = 0;
+ m_8251dtr_state = 1;
+ m_nmi_enabled = 0;
+ m_8087_int = 0;
+ m_parer_int = 0;
+ m_iochck_int = 0;
+ m_pc4 = 0;
+ m_pc5 = 0;
+ m_ocr = 0;
+ m_irq = 0;
+ m_drq = 0;
+ m_fdc_irq = 0;
+ m_fdc_drq = 0;
+
+ m_keyboard->rst_line_w(ASSERT_LINE);
+ m_kbd8251->write_cts(0); // Tied to GND
+}
+
+void epc_state::init_epc()
+{
+ /* Keyboard UART Rxc/Txc is 19.2 kHz from x960 divider */
+ m_kbdclk_timer = timer_alloc(FUNC(epc_state::rxtxclk_w), this);
+ m_kbdclk_timer->adjust(attotime::from_hz(XTAL(18'432'000) / 960) / 2);
+}
+
+TIMER_CALLBACK_MEMBER(epc_state::rxtxclk_w)
+{
+ m_kbd8251->write_rxc(m_8251rxtx_clk_state);
+ m_kbd8251->write_txc(m_8251rxtx_clk_state);
+
+ // The EPC PCB has an option to support a custom receive clock for the INS8250 apart from the TX clock through a mux controlled
+ // by the DTR pin of the I8251. The ins8250 device doesn't support RCLK as it is considerd implicitly as the same as BAUDOUT
+ // First attempt to support this in INS8250 by lifting out the BRG from deserial was reverted due to lots of regressions.
+ // We probably need to remove diserial dependencies completely from ins8250 or implement BRG hooks in diserial.cpp.
+ // if (!m_8251dtr_state) m_uart->rclk_w(m_8251rxtx_clk_state); // TODO: fix RCLK support in INS8250
+
+ m_8251rxtx_clk_state = !m_8251rxtx_clk_state;
+
+ // If CLK signal is jumpered in instead of reset signal for the keyboard
+ if ((m_io_s21->read() & 0x01) == 0x01)
+ {
+ if (m_kbdclk++ >= 4) // Frequncy is taken out of the same divider as the rxtx clock but 2 steps later
+ {
+ m_keyboard->rst_line_w(m_kbdclk_state);
+ m_kbdclk = 0;
+ m_kbdclk_state = !m_kbdclk_state;
+ }
+ }
+
+ /* Keyboard UART Rxc/Txc is 19.2 kHz from x960 divider ( 15 (74ls161) * 4 (74ls393.1) * 16 (74ls393) ) */
+ m_kbdclk_timer->adjust(attotime::from_hz(XTAL(18'432'000) / 960) / 2);
+}
+
+template <int Channel>
+uint8_t epc_state::epc_dma8237_io_r(offs_t offset)
+{
+ LOGDMA("epc_dma8237_io_r: %d\n", Channel);
+ if (Channel == 2)
+ return m_fdc->dma_r();
+ else
+ return m_isabus->dack_r(Channel);
+}
+
+template <int Channel>
+void epc_state::epc_dma8237_io_w(offs_t offset, uint8_t data)
+{
+ LOGDMA("epc_dma8237_io_w: %d - %02x\n", Channel, data);
+ if (Channel == 2)
+ m_fdc->dma_w(data);
+ else
+ m_isabus->dack_w(Channel, data);
+}
+
+template <int Channel>
+void epc_state::epc_dack_w(int state)
+{
+ LOGDMA("epc_dack_w: %d - %d\n", Channel, state);
+
+ m_isabus->dack_line_w(Channel, state);
+
+ if (!state)
+ {
+ m_dma_active |= 1 << Channel;
+ if (Channel == 0)
+ m_dma8237a->dreq0_w(0);
+ if (m_tc)
+ m_isabus->eop_w(Channel, ASSERT_LINE);
+ }
+ else
+ {
+ m_dma_active &= ~(1 << Channel);
+ if (m_tc)
+ m_isabus->eop_w(Channel, CLEAR_LINE);
+ }
+}
+
+void epc_state::dma_tc_w(int state)
+{
+ m_tc = (state == ASSERT_LINE);
+ for (int channel = 0; channel < 4; channel++)
+ {
+ if (BIT(m_dma_active, channel))
+ {
+ LOGDMA("dma_tc_w ch %d: %d\n", channel, state);
+ m_isabus->eop_w(channel, state);
+ }
+ }
+
+ // Special treatment for on board FDC
+ if (BIT(m_dma_active, 2))
+ {
+ m_fdc->tc_w(0);
+ }
+ else
+ {
+ m_fdc->tc_w(1);
+ }
+}
+
+void epc_state::dreq0_ck_w(int state)
+{
+ if (state && !m_dreq0_ck && !BIT(m_dma_active, 0))
+ m_dma8237a->dreq0_w(1);
+
+ m_dreq0_ck = state;
+}
+
+void epc_state::speaker_ck_w(int state)
+{
+ m_pc5 = state;
+ m_pc4 = (m_ppi_portb & 0x02) && state ? 1 : 0;
+ m_speaker->level_w(m_pc4);
+}
+
+/**********************************************************
+ *
+ * PPI8255 interface
+ *
+ *
+ * PORT A (not used)
+ *
+ * Reads of port A is shadowed by UART8251A's read register
+ * gaining some compatibility with PC software. The UART8251
+ * communicates with the serial keyboard and extends it with
+ * write capability enabling keyboard led control as with a
+ * PC AT keyboard.
+ *
+ * PORT B (output)
+ * 0 - PB0 - - Control signal for the sound generator (short beeps)
+ * 1 - PB1 - - Control signal for the sound generator
+ * 2 - PB2 - - Unused
+ * 3 - PB3 - - Data select for the configuration switches 0=SW1-4 1=SW5-8
+ * 4 - PB4 - * - Enable ram parity check
+ * 5 - PB5 - * - Enable expansion I/O check
+ * 6 - PB6 - * - Keyboard reset
+ * 7 - PB7 - - Reset keyboard interrupt
+ *
+ * PORT C
+ * 0 - PC0 - - Dipswitch SW 1/5 PB3=0/PB3=1
+ * 1 - PC1 - - Dipswitch SW 2/6 PB3=0/PB3=1
+ * 2 - PC2 - - Dipswitch SW 3/7 PB3=0/PB3=1
+ * 3 - PC3 - - Dipswitch SW 4/8 PB3=0/PB3=1
+ * 4 - PC4 - SPK - Speaker/cassette data (spare in PC XT spec)
+ * 5 - PC5 - OUT2 - OUT2 from 8253 (ibmpcjr compatible)
+ * 6 - PC6 -
+ * 7 - PC7 -
+ *
+ * Ericsson PC SW:
+ * 1 - Not used. Must be set to OFF
+ * 2 - OFF - 8087 present
+ * ON - No 8087 present *)
+ * 3 - Not Used. Don't care but OFF *)
+ * 4 - Not Used. Must be set to ON
+ * 5+6 - Used to select display
+ * OFF OFF - Monochrome HR graphics monitor 3111 installed + 1020 color secondary monitor
+ * ON OFF - Monochrome HR graphics monitor 3111 installed + optional 1020 color main monitor *)
+ * OFF ON - Not used
+ * ON ON - Not used
+ * 7+8 - Used to select number of disk drives
+ * OFF OFF - Not used
+ * ON OFF - Not used
+ * OFF ON - two disk drives, system units 1030-1 and 1030-2
+ * ON ON - one disk drive, system units 1030-3, 1030-4, 1031-1 and 1031-2
+ *
+ * *) - Factory settings
+ *
+ **********************************************************/
+
+uint8_t epc_state::ppi_portc_r()
+{
+ uint8_t data;
+
+ // Read 4 configurations dip switches depending on PB3
+ data = (m_io_dsw->read() >> ((m_ppi_portb & 0x08) ? 4 : 0) & 0x0f);
+
+ data |= (m_pc4 ? 1U << 4 : 0); // Feedback from gated speaker beep
+ data |= (m_pc5 ? 1U << 5 : 0); // Feedback from timer source for speaker beep
+
+ LOGPPI("PPI Port C read: %02x\n", data);
+
+ return data;
+}
+
+void epc_state::ppi_portb_w(uint8_t data)
+{
+ LOGPPI("PPI Port B write: %02x\n", data);
+ LOGPPI(" PB0 - Enable beeper : %d\n", (data & 0x01) ? 1 : 0);
+ LOGPPI(" PB1 - Beeper data : %d\n", (data & 0x02) ? 1 : 0);
+ LOGPPI(" PB2 - Unused : %d\n", (data & 0x04) ? 1 : 0);
+ LOGPPI(" PB3 - Port C dip switch select : %d\n", (data & 0x08) ? 1 : 0);
+ LOGPPI(" PB4 - RAM parity enable : %d\n", (data & 0x10) ? 1 : 0);
+ LOGPPI(" PB5 - ISA error checking enable : %d\n", (data & 0x20) ? 1 : 0);
+ LOGPPI(" PB6 - Reset keyboard : %d\n", (data & 0x40) ? 1 : 0);
+ LOGPPI(" PB7 - Reset keyboard interrupt : %d\n", (data & 0x80) ? 1 : 0);
+
+ uint8_t changed = m_ppi_portb ^ data;
+
+ m_ppi_portb = data;
+
+ if (changed & 0x40)
+ {
+ if ((m_io_s21->read() & 0x01) == 0x00)
+ {
+ if (m_ppi_portb & 0x40)
+ {
+ LOGKBD("PB6 set, clearing Keyboard RESET\n");
+ m_keyboard->rst_line_w(CLEAR_LINE);
+ }
+ else
+ {
+ LOGKBD("PB6 cleared, asserting Keyboard RESET\n");
+ m_keyboard->rst_line_w(ASSERT_LINE);
+ }
+ }
+ }
+
+ if (changed & m_ppi_portb & 0x80)
+ {
+ LOGIRQ("PB7 set, clearing IRQ1 and releasing HOLD\n");
+ m_pic8259->ir1_w(CLEAR_LINE);
+ m_keyboard->hold_w(ASSERT_LINE);
+ }
+}
+
+void epc_state::int_w(int state)
+{
+ if (m_int != state)
+ {
+ LOGIRQ("int_w: %d\n", state);
+ m_int = state;
+ m_maincpu->set_input_line(0, m_int);
+ }
+}
+
+static void epc_isa8_cards(device_slot_interface &device)
+{
+ device.option_add("epc_mda", ISA8_EPC_MDA);
+ device.option_add("ega", ISA8_EGA);
+ device.option_add("epc_twib", ISA8_EIS_TWIB);
+ // device.option_add("epc_hdc1065", ISA8_EPC_HDC1065);
+ // device.option_add("epc_mb1080", ISA8_EPC_MB1080);
+}
+
+static void epc_sd_floppies(device_slot_interface &device)
+{
+ device.option_add("525sd", FLOPPY_525_SD);
+}
+
+void epc_state::epc(machine_config &config)
+{
+ config.set_default_layout(layout_epc);
+
+ // CPU
+ I8088(config, m_maincpu, XTAL(14'318'181) / 3.0); // TWE crystal marked X1 verified divided through a 82874
+ m_maincpu->set_addrmap(AS_PROGRAM, &epc_state::epc_map);
+ m_maincpu->set_addrmap(AS_IO, &epc_state::epc_io);
+ m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb));
+ m_maincpu->esc_opcode_handler().set("fpu8087", FUNC(i8087_device::insn_w));
+ m_maincpu->esc_data_handler().set("fpu8087", FUNC(i8087_device::addr_w));
+
+ i8087_device &i8087(I8087(config, "fpu8087", XTAL(14'318'181) / 3.0));
+ i8087.set_space_88(m_maincpu, AS_PROGRAM);
+ i8087.irq().set([this](bool state)
+ {
+ LOGFPU("8087 INT: %d\n", state);
+ m_8087_int = state;
+ update_nmi();
+ });
+ i8087.busy().set_inputline(m_maincpu, INPUT_LINE_TEST);
+
+ // DMA
+ AM9517A(config, m_dma8237a, XTAL(14'318'181) / 3.0); // TWE crystal marked X1 verified
+ m_dma8237a->out_hreq_callback().set(FUNC(epc_state::epc_dma_hrq_changed));
+ m_dma8237a->out_eop_callback().set(FUNC(epc_state::dma_tc_w));
+ m_dma8237a->in_memr_callback().set(FUNC(epc_state::epc_dma_read_byte));
+ m_dma8237a->out_memw_callback().set(FUNC(epc_state::epc_dma_write_byte));
+ m_dma8237a->in_ior_callback<1>().set(FUNC(epc_state::epc_dma8237_io_r<1>));
+ m_dma8237a->in_ior_callback<2>().set(FUNC(epc_state::epc_dma8237_io_r<2>));
+ m_dma8237a->in_ior_callback<3>().set(FUNC(epc_state::epc_dma8237_io_r<3>));
+ m_dma8237a->out_iow_callback<0>().set(FUNC(epc_state::epc_dma8237_io_w<0>));
+ m_dma8237a->out_iow_callback<1>().set(FUNC(epc_state::epc_dma8237_io_w<1>));
+ m_dma8237a->out_iow_callback<2>().set(FUNC(epc_state::epc_dma8237_io_w<2>));
+ m_dma8237a->out_iow_callback<3>().set(FUNC(epc_state::epc_dma8237_io_w<3>));
+ m_dma8237a->out_dack_callback<0>().set(FUNC(epc_state::epc_dack_w<0>));
+ m_dma8237a->out_dack_callback<1>().set(FUNC(epc_state::epc_dack_w<1>));
+ m_dma8237a->out_dack_callback<2>().set(FUNC(epc_state::epc_dack_w<2>));
+ m_dma8237a->out_dack_callback<3>().set(FUNC(epc_state::epc_dack_w<3>));
+
+ // TTL-level serial keyboard callback
+ EISPC_KB(config, m_keyboard);
+ m_keyboard->txd_cb().set([this](bool state)
+ {
+ LOGBITS("KBD->EPC: %d\n", state);
+ m_kbd8251->write_rxd(state);
+ });
+ m_keyboard->caps_cb().set( [this](bool state){ m_leds[0] = state; });
+ m_keyboard->num_cb().set( [this](bool state){ m_leds[1] = state; });
+ m_keyboard->scroll_cb().set([this](bool state){ m_leds[2] = state; });
+
+ // Keyboard USART
+ I8251( config, m_kbd8251, XTAL(14'318'181) / 6.0 ); // TWE crystal marked X1 verified divided through a 82874
+
+ m_kbd8251->txd_handler().set([this](bool state)
+ {
+ if (m_txd != state)
+ {
+ LOGBITS("EPC->KBD: %d\n", state);
+ m_txd = state;
+ m_keyboard->rxd_w(m_txd);
+ }
+ });
+
+ m_kbd8251->rxrdy_handler().set([this](bool state)
+ {
+ m_rxrdy = state;
+ LOGKBD("KBD RxRdy: %d HOLD: %d\n", m_rxrdy ? 1 : 0, m_rxrdy ? 0 : 1);
+ m_keyboard->hold_w(!m_rxrdy);
+ if (m_rxrdy)
+ {
+ LOGIRQ("RxRdy set, asserting IRQ1\n");
+ m_pic8259->ir1_w(ASSERT_LINE); // Cleared by setting PB7
+ }
+ });
+ m_kbd8251->dtr_handler().set([this](bool state) // Controls RCLK for INS8250, either 19.2KHz or INS8250 BAUDOUT
+ {
+ LOGCOM("KBD DTR: %d\n", state ? 1 : 0);
+ m_8251dtr_state = state;
+ });
+
+ // Interrupt Controller
+ PIC8259(config, m_pic8259);
+ m_pic8259->out_int_callback().set(FUNC(epc_state::int_w));
+
+ // Parallel port
+ I8255A(config, m_ppi8255);
+ m_ppi8255->out_pa_callback().set([this] (uint8_t data) { LOGPPI("PPI: write %02x to unused Port A\n", data); } ); // Port A is not used
+ m_ppi8255->out_pb_callback().set(FUNC(epc_state::ppi_portb_w));
+ m_ppi8255->in_pc_callback().set(FUNC(epc_state::ppi_portc_r));
+
+ // system board Parallel port
+ PC_LPT(config, m_lpt);
+ m_lpt->irq_handler().set([this](int state)
+ { // Jumper field J10 decides what IRQ to pull
+ if ((m_io_j10->read() & 0x03) == 0x01) { LOGIRQ("LPT IRQ2: %d\n", state); m_pic8259->ir2_w(state); }
+ if ((m_io_j10->read() & 0x0c) == 0x04) { LOGIRQ("LPT IRQ3: %d\n", state); m_pic8259->ir3_w(state); }
+ if ((m_io_j10->read() & 0x30) == 0x10) { LOGIRQ("LPT IRQ4: %d\n", state); m_pic8259->ir4_w(state); }
+ if ((m_io_j10->read() & 0xc0) == 0x40) { LOGIRQ("LPT IRQ7: %d\n", state); m_pic8259->ir7_w(state); } // Factory setting
+ });
+
+ // Timer
+ PIT8253(config, m_pit8253);
+ m_pit8253->set_clk<0>((XTAL(14'318'181) / 3.0) / 2.0 );
+ m_pit8253->set_clk<1>((XTAL(14'318'181) / 3.0) / 2.0 );
+ m_pit8253->set_clk<2>((XTAL(14'318'181) / 3.0) / 2.0 );
+ m_pit8253->out_handler<0>().set(m_pic8259, FUNC(pic8259_device::ir0_w));
+ m_pit8253->out_handler<1>().set(FUNC(epc_state::dreq0_ck_w));
+ m_pit8253->out_handler<2>().set(FUNC(epc_state::speaker_ck_w));
+
+ // Speaker
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 1.00);
+
+ // ISA bus
+ ISA8(config, m_isabus, XTAL(14'318'181) / 3.0); // TEW crystal marked X1 verified
+ m_isabus->set_memspace(m_maincpu, AS_PROGRAM);
+ m_isabus->set_iospace(m_maincpu, AS_IO);
+ m_isabus->irq2_callback().set(m_pic8259, FUNC(pic8259_device::ir2_w)); // Reserved in service manual
+ m_isabus->irq3_callback().set(m_pic8259, FUNC(pic8259_device::ir3_w));
+ m_isabus->irq4_callback().set(m_pic8259, FUNC(pic8259_device::ir4_w));
+ m_isabus->irq5_callback().set(m_pic8259, FUNC(pic8259_device::ir5_w));
+ m_isabus->irq6_callback().set(m_pic8259, FUNC(pic8259_device::ir6_w));
+ m_isabus->irq7_callback().set(m_pic8259, FUNC(pic8259_device::ir7_w));
+ m_isabus->drq1_callback().set(m_dma8237a, FUNC(am9517a_device::dreq1_w));
+ m_isabus->drq2_callback().set(m_dma8237a, FUNC(am9517a_device::dreq2_w));
+ m_isabus->drq3_callback().set(m_dma8237a, FUNC(am9517a_device::dreq3_w));
+ m_isabus->iochck_callback().set([this] (int state)
+ {
+ if (m_nmi_enabled && !state && 0)
+ {
+ LOGNMI("IOCHCK: NMI Requested\n");
+ update_nmi();
+ }
+ });
+
+ ISA8_SLOT(config, "isa1", 0, m_isabus, epc_isa8_cards, "epc_mda", false);
+ ISA8_SLOT(config, "isa2", 0, m_isabus, epc_isa8_cards, nullptr, false);
+ ISA8_SLOT(config, "isa3", 0, m_isabus, epc_isa8_cards, nullptr, false);
+ ISA8_SLOT(config, "isa4", 0, m_isabus, epc_isa8_cards, nullptr, false);
+ ISA8_SLOT(config, "isa5", 0, m_isabus, epc_isa8_cards, nullptr, false);
+ ISA8_SLOT(config, "isa6", 0, m_isabus, epc_isa8_cards, nullptr, false);
+
+ // System board has 128kB memory with parity, expansion can be achieved through the
+ // 128kB Memory Expansion Board 1090 and/or the 128kB Multifunction Board MB1080-001
+ // and/or the 384kB MB1080-002. The MB1080 DRAM might need to be dynamically added as
+ // base address and also a video memory hole is configurable.
+ RAM(config, m_ram).set_default_size("128K").set_extra_options("256K, 384K, 512K, 640K");
+
+ // FDC
+ I8272A(config, m_fdc, XTAL(16'000'000) / 2, false); // TEW crystal marked X3 verified
+ m_fdc->intrq_wr_callback().set([this] (int state){ m_fdc_irq = state; check_fdc_irq(); });
+ m_fdc->drq_wr_callback().set([this] (int state){ m_fdc_drq = state; check_fdc_drq(); });
+ FLOPPY_CONNECTOR(config, m_floppy_connectors[0], epc_sd_floppies, "525sd", floppy_image_device::default_pc_floppy_formats);
+ FLOPPY_CONNECTOR(config, m_floppy_connectors[1], epc_sd_floppies, "525sd", floppy_image_device::default_pc_floppy_formats);
+ //SOFTWARE_LIST(config, "epc_flop_list").set_original("epc_flop");
+
+ // system board UART
+ INS8250(config, m_uart, XTAL(18'432'000) / 10); // TEW crystal marked X2 verified. TODO: Let 8051 DTR control RCLK (see above)
+ m_uart->out_tx_callback().set("com1", FUNC(rs232_port_device::write_txd));
+ m_uart->out_dtr_callback().set("com1", FUNC(rs232_port_device::write_dtr));
+ m_uart->out_rts_callback().set("com1", FUNC(rs232_port_device::write_rts));
+ m_uart->out_int_callback().set([this](int state)
+ { // Jumper field J10 decides what IRQ to pull
+ if ((m_io_j10->read() & 0x03) == 0x02) { LOGCOM("UART IRQ2: %d\n", state); m_pic8259->ir2_w(state); }
+ if ((m_io_j10->read() & 0x0c) == 0x08) { LOGCOM("UART IRQ3: %d\n", state); m_pic8259->ir3_w(state); }
+ if ((m_io_j10->read() & 0x30) == 0x20) { LOGCOM("UART IRQ4: %d\n", state); m_pic8259->ir4_w(state); } // Factory setting
+ if ((m_io_j10->read() & 0xc0) == 0x80) { LOGCOM("UART IRQ7: %d\n", state); m_pic8259->ir7_w(state); }
+ });
+ // m_uart->out_baudout_callback().set([this](int state){ if (m_8251dtr_state) m_uart->rclk_w(state); }); // TODO: Fix INS8250 BAUDOUT pin support
+
+ rs232_port_device &rs232(RS232_PORT(config, "com1", default_rs232_devices, nullptr));
+ rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
+ rs232.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
+ rs232.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
+ rs232.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
+ rs232.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));
+}
+
+void epc_state::update_nmi()
+{
+ if (m_nmi_enabled &&
+ ((m_8087_int && (m_io_dsw->read() & 0x02)) || // FPU int only if FPU is enabled by DSW2
+ (m_parer_int != 0) || // Parity error is always false as it is an emulator, at least for now
+ (m_iochck_int != 0))) // Same goes for ISA board errors
+ {
+ LOGNMI(" NMI Asserted\n");
+ m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
+ }
+ else
+ {
+ LOGNMI(" NMI Cleared\n");
+ m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+ }
+}
+
+void epc_state::epc_dma_hrq_changed(int state)
+{
+ LOGDMA("epc_dma_hrq_changed %d\n", state);
+
+ m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
+
+ /* Assert HLDA */
+ m_dma8237a->hack_w(state);
+}
+
+
+uint8_t epc_state::epc_dma_read_byte(offs_t offset)
+{
+ if ((m_dma_active & 0x0f) == 0)
+ {
+ LOGDMA("epc_dma_read_byte failed\n");
+ return 0xff;
+ }
+
+ const int seg = (BIT(m_dma_active, 2) ? 0 : 2) | (BIT(m_dma_active, 3) ? 0 : 1);
+ return m_maincpu->space(AS_PROGRAM).read_byte(offset | u32(m_dma_segment[seg]) << 16);
+}
+
+void epc_state::epc_dma_write_byte(offs_t offset, uint8_t data)
+{
+ if ((m_dma_active & 0x0f) == 0)
+ {
+ LOGDMA("epc_dma_write_byte failed\n");
+ return;
+ }
+
+ const int seg = (BIT(m_dma_active, 2) ? 0 : 2) | (BIT(m_dma_active, 3) ? 0 : 1);
+ m_maincpu->space(AS_PROGRAM).write_byte(offset | u32(m_dma_segment[seg]) << 16, data);
+}
+
+
+static INPUT_PORTS_START( epc_ports )
+ PORT_START("DSW")
+ PORT_DIPNAME( 0x01, 0x01, "Not used")
+ PORT_DIPSETTING( 0x00, "ON - Don't use")
+ PORT_DIPSETTING( 0x01, "OFF - Factory Setting")
+ PORT_DIPNAME( 0x02, 0x00, "8087 installed")
+ PORT_DIPSETTING( 0x00, DEF_STR(No) )
+ PORT_DIPSETTING( 0x02, DEF_STR(Yes) )
+ PORT_DIPNAME( 0x04, 0x04, "Not used")
+ PORT_DIPSETTING( 0x00, "ON - Don't care")
+ PORT_DIPSETTING( 0x04, "OFF - Factory Setting")
+ PORT_DIPNAME( 0x08, 0x00, "Not used")
+ PORT_DIPSETTING( 0x00, "ON - Factory Setting")
+ PORT_DIPSETTING( 0x08, "OFF - Don't use")
+ PORT_DIPNAME( 0x30, 0x30, "Main monitor")
+ PORT_DIPSETTING( 0x00, "Not used" )
+ PORT_DIPSETTING( 0x10, "Optional 1020 color" )
+ PORT_DIPSETTING( 0x20, "Not used" )
+ PORT_DIPSETTING( 0x30, "3111 HR Monochrome" )
+ PORT_DIPNAME( 0xc0, 0x40, "Number of floppy drives")
+ PORT_DIPSETTING( 0x00, "1" )
+ PORT_DIPSETTING( 0x40, "2" )
+ PORT_DIPSETTING( 0x80, "Not used" )
+ PORT_DIPSETTING( 0xc0, "Not used" )
+
+ PORT_START("J10") // Jumper area, field 0=no jumper 1=LPT 2=COM 3=n/a
+ PORT_DIPNAME(0x03, 0x00, "IRQ2")
+ PORT_DIPSETTING(0x00, "no jumper")
+ PORT_DIPSETTING(0x01, "LPT")
+ PORT_DIPSETTING(0x02, "COM")
+ PORT_DIPNAME(0x0c, 0x00, "IRQ3")
+ PORT_DIPSETTING(0x00, "no jumper")
+ PORT_DIPSETTING(0x04, "LPT")
+ PORT_DIPSETTING(0x08, "COM")
+ PORT_DIPNAME(0x30, 0x20, "IRQ4")
+ PORT_DIPSETTING(0x00, "no jumper")
+ PORT_DIPSETTING(0x10, "LPT")
+ PORT_DIPSETTING(0x20, "COM")
+ PORT_DIPNAME(0xc0, 0x40, "IRQ7")
+ PORT_DIPSETTING(0x00, "no jumper")
+ PORT_DIPSETTING(0x40, "LPT")
+ PORT_DIPSETTING(0x80, "COM")
+
+ PORT_START("S21") // Jumper 0=PB6 reset, 1=KBCLK 4.8kHz - what to send to keyboard pin 3
+ PORT_DIPNAME(0x01, 0x00, "Keyboard Clock/Reset pin")
+ PORT_DIPSETTING(0x00, "PB6")
+ PORT_DIPSETTING(0x01, "4.8kHz") // This setting is apparantly for another keyboard, currently unknown
+INPUT_PORTS_END
+
+ROM_START( epc )
+ ROM_REGION(0x10000,"bios", 0)
+ ROM_DEFAULT_BIOS("p860110")
+ ROM_SYSTEM_BIOS(0, "p840705", "P840705")
+ ROMX_LOAD("ericsson_8088.bin", 0xe000, 0x2000, CRC(3953c38d) SHA1(2bfc1f1d11d0da5664c3114994fc7aa3d6dd010d), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "p860110", "P860110")
+ ROMX_LOAD("epcbios1.bin", 0xe000, 0x02000, CRC(79a83706) SHA1(33528c46a24d7f65ef5a860fbed05afcf797fc55), ROM_BIOS(1))
+ ROMX_LOAD("epcbios2.bin", 0xa000, 0x02000, CRC(3ca764ca) SHA1(02232fedef22d31a641f4b65933b9e269afce19e), ROM_BIOS(1))
+ ROMX_LOAD("epcbios3.bin", 0xc000, 0x02000, CRC(70483280) SHA1(b44b09da94d77b0269fc48f07d130b2d74c4bb8f), ROM_BIOS(1))
+ROM_END
+
+} // anonymous namespace
+
+
+COMP( 1985, epc, 0, 0, epc, epc_ports, epc_state, init_epc, "Ericsson Information System", "Ericsson PC" , 0)
+//COMP( 1985, eppc, ibm5150, 0, pccga, pccga, pc_state, empty_init, "Ericsson Information System", "Ericsson Portable PC", MACHINE_NOT_WORKING )