diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index f60a7dc253e..5e30b516a2e 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -1119,6 +1119,7 @@ function linkProjects_mame_mess(_target, _subtarget) "entex", "epoch", "epson", + "ericsson", "exidy", "fairch", "fairlight", @@ -2182,6 +2183,13 @@ files { MAME_DIR .. "src/mame/machine/qx10kbd.h", } +createMESSProjects(_target, _subtarget, "ericsson") +files { + MAME_DIR .. "src/mame/drivers/eispc.cpp", + MAME_DIR .. "src/mame/machine/eispc_kb.cpp", + MAME_DIR .. "src/mame/machine/eispc_kb.h", +} + createMESSProjects(_target, _subtarget, "exidy") files { MAME_DIR .. "src/mame/machine/sorcerer.cpp", diff --git a/src/mame/drivers/eispc.cpp b/src/mame/drivers/eispc.cpp new file mode 100644 index 00000000000..f8e051badc4 --- /dev/null +++ b/src/mame/drivers/eispc.cpp @@ -0,0 +1,962 @@ +// license:BSD-3-Clause +// copyright-holders:Joakim Larsson Edström +/*************************************************************************************************** + * + * Ericsson Information Systems PC "compatibles" + * + * The Ericsson PC was the 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 run. 50.000+ units sold + * + * TODO: + * - Add keyboard, first HLE as in pc.cpp and then LLE when the keyboard controller is dumped + * - Add the on-board FDC and boot DOS 1.xx + * - Complete the Ericsson 1070 MDA ISA board and test all the graphics modes inclusing 640x400 (aka HR) + * - Add the Ericsson 1065 HDC and boot from a hard drive + * + * 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 "machine/eispc_kb.h" + +// Devices +#include "cpu/i86/i86.h" +#include "machine/am9517a.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/mda.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 "formats/imd_dsk.h" +#include "formats/pc_dsk.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 VERBOSE (LOG_BITS|LOG_KBD|LOG_IRQ) +//#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__) + +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_lpt(*this, "lpt") + , m_kbd8251(*this, "kbd8251") + , m_keyboard(*this, "keyboard") + , 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, "uart8250") + { } + + void epc(machine_config &config); + void init_epc(); + + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + required_device m_maincpu; + required_device m_ram; + required_device m_isabus; + + // DMA + DECLARE_WRITE_LINE_MEMBER(dma_tc_w); + DECLARE_WRITE_LINE_MEMBER(dreq0_ck_w); + DECLARE_WRITE_LINE_MEMBER( epc_dma_hrq_changed ); + DECLARE_WRITE_LINE_MEMBER( epc_dma8237_out_eop ); + DECLARE_READ8_MEMBER( epc_dma_read_byte ); + DECLARE_WRITE8_MEMBER( epc_dma_write_byte ); + template uint8_t epc_dma8237_io_r(offs_t offset); + template void epc_dma8237_io_w(offs_t offset, uint8_t data); + template DECLARE_WRITE_LINE_MEMBER(epc_dack_w); + required_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 m_ppi8255; + DECLARE_WRITE8_MEMBER(ppi_portb_w); + DECLARE_READ8_MEMBER(ppi_portc_r); + uint8_t m_ppi_portb; + required_ioport m_io_dsw; + required_ioport m_io_j10; + + // Printer port + optional_device m_lpt; + + // Keyboard Controller/USART + required_device m_kbd8251; + required_device m_keyboard; + emu_timer *m_kbdclk_timer; + TIMER_CALLBACK_MEMBER(rxtxclk_w); + int m_rxtx_clk_state; + + // Interrupt Controller + required_device m_pic8259; + DECLARE_WRITE_LINE_MEMBER(int_w); + uint8_t m_nmi_enabled; + uint8_t m_8087_int = 0; + const uint8_t m_parer_int = 0; + const uint8_t m_iochck_int = 0; + void update_nmi(); + + // Timer + required_device m_pit8253; + + // Speaker + DECLARE_WRITE_LINE_MEMBER(speaker_ck_w); + required_device m_speaker; + + void epc_map(address_map &map); + void epc_io(address_map &map); + + // FDC + void check_fdc_irq(); + void check_fdc_drq(); + required_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 m_floppy_connectors; + DECLARE_FLOPPY_FORMATS( epc_floppy_formats ); + + // UART + required_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("dma8237_rw", + [this](offs_t offset) -> uint8_t + { + uint8_t data = m_dma8237a->read(offset); + LOGDMA("dma8237_r %04x\n", offset); + return data; + }, + [this](offs_t offset, uint8_t data) + { + LOGDMA("dma8237_w %04x: %02x\n", offset, data); + m_dma8237a->write(offset, data); + } + ); + + map(0x0020, 0x0021).mirror(0x1e).lrw8("pic8259_rw", + [this](offs_t offset) -> uint8_t + { + uint8_t data = m_pic8259->read(offset); + LOGPIC("pic8259_r %04x: %02x\n", offset, data); + return data; + }, + [this](offs_t offset, uint8_t data) + { + LOGPIC("pic8259_w %04x: %02x\n", offset, data); + m_pic8259->write(offset, data); + } + ); + + map(0x0040, 0x0043).mirror(0x1c).lrw8("pit8253_rw", + [this](offs_t offset) -> uint8_t + { + uint8_t data = m_pit8253->read(offset); + LOGPIT("pit8253_r %04x\n", offset); + return data; + }, + [this](offs_t offset, uint8_t data) + { + LOGPIT("pit8253_w %04x: %02x\n", offset, data); + m_pit8253->write(offset, data); + } + ); + + map(0x0060, 0x0060).mirror(0x1c).lrw8("kbd_8251_data_rw", + [this]() -> uint8_t + { + uint8_t data = m_kbd8251->data_r(); + LOGKBD("kbd8251_r %02x\n", data); + return data; + }, + [this](offs_t offset, uint8_t data) + { + LOGKBD("kbd8251_w 0x60 %02x\n", data); + m_kbd8251->data_w(data); + } + ); + // NOTE: PPI Port A is not mapped + map(0x0061, 0x0061).mirror(0x1c).lrw8("ppi8255_rw", // 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; + }, + [this](offs_t offset, uint8_t data) + { + LOGPPI("ppi8255_w Port B: %02x\n", data); + m_ppi8255->write(1, data); + } + ); + + map(0x0062, 0x0062).mirror(0x1c).lrw8("ppi8255_rw", // 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; + }, + [this](offs_t offset, uint8_t data) + { + LOGPPI("ppi8255_w Port C: %02x\n", data); + m_ppi8255->write(2, data); + } + ); + + map(0x0063, 0x0063).lrw8("ppi8255_rw", // 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; + }, + [this](offs_t offset, uint8_t data) + { + LOGPPI("ppi8255_w Control: %02x\n", data); + m_ppi8255->write(3, data); + } + ); + + map(0x0070, 0x0070).mirror(0x0e).lw8("i8251_data_w", + [this](offs_t offset, uint8_t data) + { + LOGKBD("kbd8251_w 0x70: %02x\n", data); + m_kbd8251->data_w(data); + } + ); + + map(0x0071, 0x0071).mirror(0x0e).lrw8("kbd_8251_stat_ctrl_rw", + [this](offs_t offset) -> uint8_t + { + uint8_t stat = m_kbd8251->status_r(); + //LOGKBD("kbd8251_status_r %02x\n", stat); + return stat; + }, + [this](offs_t offset, uint8_t data) + { + LOGKBD("kbd8251_control_w 0x71: %02x\n", data); + m_kbd8251->control_w(data); + } + ); + + map(0x0080, 0x0083).mirror(0xc).lw8("dma_segement_w", + [this](offs_t offset, uint8_t data) + { + LOGDMA("dma_segment_w %04x: %02x\n", offset, data); + m_dma_segment[offset] = data & 0x0f; + } + ); + + map(0x00a0, 0x00a1).mirror(0xe).lw8("nmi_enable_w", + [this](offs_t offset, uint8_t data) + { + LOGNMI("nmi_enable_w %04x: %02x\n", offset, data); + m_nmi_enabled = BIT(data,7); + update_nmi(); + } + ); + + // FDC Output Control Register (same as PC XT DOR) + map(0x03f2, 0x03f3).lw8("ocr_w", // 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(); + } + ); + + map(0x03f4, 0x03f5).m(m_fdc, FUNC(i8272a_device::map)); + + map(0x03bc, 0x03be).lrw8("lpt_rw", + [this](address_space &space, offs_t offset, uint8_t mem_mask) -> uint8_t + { + uint8_t data = m_lpt->read(space, offset); + LOGLPT("LPT read offset %02x: %02x\n", offset, data); + return data; + }, + [this](address_space &space, offs_t offset, uint8_t data) + { + LOGLPT("LPT write offset %02x: %02x\n", offset, data); + m_lpt->write(space, offset, data); + } + ); + + 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); + m_dma_active = 0; + m_tc = false; + m_int = 1; + m_txd = false; + m_rxrdy = false; + m_dreq0_ck = true; + m_rxtx_clk_state = 0; + + save_item(NAME(m_dma_segment)); + save_item(NAME(m_dma_active)); + save_item(NAME(m_tc)); + save_item(NAME(m_int)); + save_item(NAME(m_txd)); + save_item(NAME(m_rxrdy)); + save_item(NAME(m_ocr)); + m_ocr = 0x00; + save_item(NAME(m_ppi_portb)); + save_item(NAME(m_dreq0_ck)); + save_item(NAME(m_rxtx_clk_state)); +} + +void epc_state::machine_reset() +{ + m_ppi_portb = 0; + m_keyboard->rst_line_w(ASSERT_LINE); + m_nmi_enabled = 0; + m_kbd8251->write_cts(0); // Held low always +} + +void epc_state::init_epc() +{ + /* Keyboard UART Rxc/Txc is 19.2 kHz from x96 divider */ + m_kbdclk_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(epc_state::rxtxclk_w), this)); + m_kbdclk_timer->adjust(attotime::from_hz((XTAL(18'432'000) / 96) / 5)); +} + +TIMER_CALLBACK_MEMBER(epc_state::rxtxclk_w) +{ + m_kbd8251->write_rxc(m_rxtx_clk_state); + m_kbd8251->write_txc(m_rxtx_clk_state); + m_rxtx_clk_state ^= 0x01; + m_kbdclk_timer->adjust(attotime::from_hz((XTAL(18'432'000) / 96) / 5)); +} + + +template +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 +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 +WRITE_LINE_MEMBER(epc_state::epc_dack_w) +{ + 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); + } +} + +WRITE_LINE_MEMBER(epc_state::dma_tc_w) +{ + 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); + } +} + +WRITE_LINE_MEMBER(epc_state::dreq0_ck_w) +{ + if (state && !m_dreq0_ck && !BIT(m_dma_active, 0)) + m_dma8237a->dreq0_w(1); + + m_dreq0_ck = state; +} + +WRITE_LINE_MEMBER(epc_state::speaker_ck_w) +{ + m_speaker->level_w((m_ppi_portb & 0x02) && state ? 1 : 0); +} + +/********************************************************** + * + * 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 + * + **********************************************************/ + +READ8_MEMBER( 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); + + // TODO: verify what PC4-PC7 is used for, if anything + + LOGPPI("PPI Port C read: %02x\n", data); + + return data; +} + +WRITE8_MEMBER( epc_state::ppi_portb_w ) +{ + 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_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); + } +} + +WRITE_LINE_MEMBER(epc_state::int_w) +{ + 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_hdc1065", ISA8_EPC_HDC1065); + // device.option_add("epc_mb1080", ISA8_EPC_MB1080); +} + +FLOPPY_FORMATS_MEMBER( epc_state::epc_floppy_formats ) + FLOPPY_PC_FORMAT, + FLOPPY_IMD_FORMAT +FLOPPY_FORMATS_END + +static void epc_sd_floppies(device_slot_interface &device) +{ + device.option_add("525sd", FLOPPY_525_SD); +} + +void epc_state::epc(machine_config &config) +{ + 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)); + + // 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, "keyboard").txd_cb().set([this](bool state) + { + LOGBITS("KBD->EPC: %d\n", state); + m_kbd8251->write_rxd(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 + { + LOGKBD("KBD DTR: %d\n", state ? 1 : 0); // TODO: Implement clock selection mux, need to check what state does what + }); + + // 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(FUNC(epc_state::chck_w)); // TODO: Check schematics + 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 configuarable. + 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", epc_floppy_formats); + FLOPPY_CONNECTOR(config, m_floppy_connectors[1], epc_sd_floppies, "525sd", epc_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("uart", FUNC(rs232_port_device::write_txd)); + m_uart->out_dtr_callback().set("uart", FUNC(rs232_port_device::write_dtr)); + m_uart->out_rts_callback().set("uart", 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) { LOGIRQ("UART IRQ2: %d\n", state); m_pic8259->ir2_w(state); } + if ((m_io_j10->read() & 0x0c) == 0x08) { LOGIRQ("UART IRQ3: %d\n", state); m_pic8259->ir3_w(state); } + if ((m_io_j10->read() & 0x30) == 0x20) { LOGIRQ("UART IRQ4: %d\n", state); m_pic8259->ir4_w(state); } // Factory setting + if ((m_io_j10->read() & 0xc0) == 0x80) { LOGIRQ("UART IRQ7: %d\n", state); m_pic8259->ir7_w(state); } + }); + + rs232_port_device &rs232(RS232_PORT(config, "uart", 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); + } +} + +WRITE_LINE_MEMBER( epc_state::epc_dma_hrq_changed ) +{ + 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); +} + + +READ8_MEMBER( epc_state::epc_dma_read_byte ) +{ + 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); +} + +WRITE8_MEMBER( epc_state::epc_dma_write_byte ) +{ + 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") +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 + + +COMP( 1985, epc, 0, 0, epc, epc_ports, epc_state, init_epc, "Ericsson Information System", "Ericsson PC" , MACHINE_NOT_WORKING ) +//COMP( 1985, eppc, ibm5150, 0, pccga, pccga, pc_state, empty_init, "Ericsson Information System", "Ericsson Portable PC", MACHINE_NOT_WORKING ) diff --git a/src/mame/drivers/pc.cpp b/src/mame/drivers/pc.cpp index 5402f35320a..0461f45a095 100644 --- a/src/mame/drivers/pc.cpp +++ b/src/mame/drivers/pc.cpp @@ -46,7 +46,6 @@ public: void ncrpc4i(machine_config &config); void kaypro16(machine_config &config); void kaypropc(machine_config &config); - void epc(machine_config &config); void m15(machine_config &config); void bondwell(machine_config &config); void siemens(machine_config &config); @@ -86,7 +85,6 @@ private: static void cfg_single_360K(device_t *device); static void cfg_single_720K(device_t *device); - void epc_io(address_map &map); void ibm5550_io(address_map &map); void pc16_io(address_map &map); void pc16_map(address_map &map); @@ -533,55 +531,6 @@ ROM_START( mc1702 ) ROM_END -/************************************************************** 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 (CGA?) 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 run. 50.000+ units sold - -******************************************************************************/ - -void pc_state::epc_io(address_map &map) -{ - map.unmap_value_high(); - map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map)); - map(0x0070, 0x0071).rw("i8251", FUNC(i8251_device::read), FUNC(i8251_device::write)); -} - -void pc_state::epc(machine_config &config) -{ - pccga(config); - - i8088_cpu_device &maincpu(I8088(config.replace(), "maincpu", 4772720)); - maincpu.set_addrmap(AS_PROGRAM, &pc_state::pc8_map); - maincpu.set_addrmap(AS_IO, &pc_state::epc_io); - maincpu.set_irq_acknowledge_callback("mb:pic8259", FUNC(pic8259_device::inta_cb)); - - subdevice("isa1")->set_default_option("ega"); - I8251(config, "i8251", 0); // clock? -} - -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 - - /************************************************ Ericsson Portable PC - EPPC *** Links: https://youtu.be/Qmke4L4Jls8 , https://youtu.be/yXK01gBQE6Q @@ -1358,7 +1307,6 @@ ROM_END // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS COMP( 1984, dgone, ibm5150, 0, dgone, pccga, pc_state, empty_init, "Data General", "Data General/One" , MACHINE_NOT_WORKING ) -COMP( 1985, epc, ibm5150, 0, epc, pccga, pc_state, empty_init, "Ericsson Information System", "Ericsson PC" , MACHINE_NOT_WORKING ) COMP( 1985, eppc, ibm5150, 0, pccga, pccga, pc_state, empty_init, "Ericsson Information System", "Ericsson Portable PC", MACHINE_NOT_WORKING ) COMP( 1985, bw230, ibm5150, 0, bondwell, bondwell, pc_state, init_bondwell, "Bondwell Holding", "BW230 (PRO28 Series)", 0 ) COMP( 1992, iskr3104, ibm5150, 0, iskr3104, pccga, pc_state, empty_init, "Schetmash", "Iskra 3104", MACHINE_NOT_WORKING ) diff --git a/src/mame/machine/eispc_kb.cpp b/src/mame/machine/eispc_kb.cpp new file mode 100644 index 00000000000..cac16464e82 --- /dev/null +++ b/src/mame/machine/eispc_kb.cpp @@ -0,0 +1,446 @@ +// license:BSD-3-Clause +// copyright-holders: Joakim Larsson Edström +/********************************************************************** + + Ericsson PC keyboard emulation + + TTL-level bi-directional serial matrix keyboard + + The mc6801 contains an internal ROM that handles scanning of the keyboard, + controlling the 2 or 3 LEDs and also the programming of the scan code for + a single programmable key. + + There are two known variants of the keyboard. The first had the Ericsson + internal name "Sgt Pepper" where the hardware was OEMed/manufactured by + FACIT and had two LEDs while the second variant called "Roger Moore" had + three LEDs and was manufactured by Ericsson. + + Both keyboard hooks up directly to the port of a 6801 MCU. There are + 16 column lines driven by Port 3 and Port 4 that goes low one at a time + during the scan process and when a key is pressed one of the six corresponding + row lines goes low and fed to through a 74HC04 inverter into port 1, where a + high bit means a key was pressed. + + The connector has TX/Hold, Rx and Reset. Reset is connected directly to the + MCU so the host CPU can keep it in RESET until it needs the keyboard. + Rx is connected to the RX line of the SCI in the MCU, P23 - bit 3 of port 2. + Tx/Hold is bidirectional, connected to the TX line of the SCI in the MCU, P24 + bit 4 of port 2, but can also be kept low by the host CPU to temporarily inhibit + the keyboard from sending more scan codes. This is sensed by P16 through a + 74HC04 inverter. + + The data is exchanged in both direction asynchronously at 1200 baud, 8 databits, + 1 start and 1 stop bit. At startup the host CPU sends a $00 (zero) byte to the + keyboard simultaneously with the MCU sending a $A5 to the CPU to ensure full + duplex operation. If the $A5 byte is not received EPC will display a "Keyboard + Error" message on the screen. + + P17 and P20 are connected to LEDs on Caps Lock and Num Lock keys. The latter + keyboard variant Roger Moore also had a LED on Scroll Lock connected to P22. + P20, P21 and P22 are pulled high to bring the MCU into MODE 7 at RESET. NMI + and IRQ are pulled high and not connected to anything externally. + + +--+--+--+--+--+-6x10K--o +5v + +-------+ | | | | | | + | P30|<------x--x--x--x--x--x--- COLUMNS x = 1N4448 diod towards P3/P4 + | P31|<------x--x--x--x--x--x--- x 16 in serie with key button + | P32|<------x--x--x--x--x--x--- + | P33|<------x--x--x--x--x--x--- A pressed button pulls a P1 row + | P34|<------x--x--x--x--x--x--- low when its P3/P4 column is + | P35|<------x--x--x--x--x--x--- being scanned + | P36|<------x--x--x--x--x--x--- + | P37|<------x--x--x--x--x--x--- + | P40|<------x--x--x--x--x--x--- + | P41|<------x--x--x--x--x--x--- + | P42|<------x--x--x--x--x--x--- + | P43|<------x--x--x--x--x--x--- + | P44|<------x--x--x--x--x--x--- + | P45|<------x--x--x--x--x--x--- + | P46|<------x--x--x--x--x--x--- + | P47|<------x--x--x--x--x--x--- + | | | | | | | | + | M6801 | | | | | | | + | | 6 x 74HC04 hex inverter + |P10-P15|<------+--+--+--+--+--+ ROWS x 6 + +-------+ + + Credits + ------- + The internal ROM was dumped in collaboration with Dalby Datormuseum, whom + also provided documentation and schematics of the keyboard + + https://sites.google.com/site/dalbydatormuseum/home + https://github.com/MattisLind/6801reader + + **********************************************************************/ + +#include "emu.h" +#include "eispc_kb.h" +#include "cpu/m6800/m6801.h" + +//************************************************************************** +// CONFIGURABLE LOGGING +//************************************************************************** +#define LOG_PORTS (1U << 1) +#define LOG_RESET (1U << 2) +#define LOG_BITS (1U << 3) +#define LOG_UI (1U << 4) + +//#define VERBOSE (LOG_UI) +//#define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGPORTS(...) LOGMASKED(LOG_PORTS, __VA_ARGS__) +#define LOGRST(...) LOGMASKED(LOG_RESET, __VA_ARGS__) +#define LOGBITS(...) LOGMASKED(LOG_BITS, __VA_ARGS__) +#define LOGUI(...) LOGMASKED(LOG_UI, __VA_ARGS__) + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define M6801_TAG "mcu" + +#define PCM(handler, parameter) PORT_CHANGED_MEMBER(DEVICE_SELF, eispc_keyboard_device, handler, parameter) + +namespace { + +INPUT_PORTS_START(eispc_kb) + PORT_START("P15") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 6") PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR('6') PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PCM(key, 0) // 77 + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP +") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('+') PCM(key, 0) // 78 + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PCM(key, 0) // 76 + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("* PRINT") PORT_CODE(KEYCODE_TILDE) PORT_CHAR('*') PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) PCM(key, 0) // 55 + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("R Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PCM(key, 0) // 54 + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') PCM(key, 0) // 53 + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(". :") PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':') PCM(key, 0) // 52 + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PCM(key, 0) // 63 + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PCM(key, 0) // 64 + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 0) // no scancode is sent + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PCM(key, 0) // 29 + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(", ;") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';') PCM(key, 0) // 51 + PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d') PCM(key, 0) // 32 + PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x') PCM(key, 0) // 45 + PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c') PCM(key, 0) // 46 + PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j') PCM(key, 0) // 36 + + PORT_START("P14") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 1) // 00 - keyboard error + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_CODE(KEYCODE_PAUSE) PORT_CHAR(UCHAR_MAMEKEY(PAUSE)) PCM(key, 1) // 70 + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_CHAR(UCHAR_MAMEKEY(HOME)) PCM(key, 1) // 71 + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 1) // ff - keyboard error + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('^') PORT_CHAR('~') PORT_CHAR(']') PCM(key, 1) // 27 + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00e5) PORT_CHAR(0x00c5) PORT_CHAR('[') PCM(key, 1) // 26 å Å + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p') PCM(key, 1) // 25 + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PCM(key, 1) // 59 + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PCM(key, 1) // 60 + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') PCM(key, 1) // 17 + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') PCM(key, 1) // 18 + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PCM(key, 1) // 24 + PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PCM(key, 1) // 19 + PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PCM(key, 1) // 20 + PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') PCM(key, 1) // 21 + PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') PCM(key, 1) // 23 + + PORT_START("P13") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_NUMLOCK) PORT_CHAR(UCHAR_MAMEKEY(NUMLOCK)) PCM(key, 2) // 69 + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 2) // ff - keyboard error + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BS DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(DEL)) PCM(key, 2) // 14 + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') PCM(key, 2) // 13 + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') PCM(key, 2) // 12 + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') PCM(key, 2) // 11 + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') PCM(key, 2) // 10 + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') PCM(key, 2) // 02 + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PCM(key, 2) // 01 + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@') PCM(key, 2) // 03 + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') PCM(key, 2) // 04 + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') PCM(key, 2) // 09 + PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') PCM(key, 2) // 05 + PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') PCM(key, 2) // 06 + PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') PCM(key, 2) // 07 + PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') PCM(key, 2) // 08 + + PORT_START("P12") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9') PORT_CHAR(UCHAR_MAMEKEY(PGUP)) PCM(key, 3) // 73 + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP -") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PCM(key, 3) // 74 + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 8") PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_UP) PORT_CHAR('8') PORT_CHAR(UCHAR_MAMEKEY(UP)) PCM(key, 3) // 72 + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~') PCM(key, 3) // 41 + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') PCM(key, 3) // 40 + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') PCM(key, 3) // 39 + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PCM(key, 3) // 38 + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PCM(key, 3) // 61 + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PCM(key, 3) // 62 + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') PCM(key, 3) // 16 + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("TAB") PORT_CODE(KEYCODE_TAB) PORT_CHAR(9) PCM(key, 3) // 15 + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PCM(key, 3) // 37 + PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') PCM(key, 3) // 33 + PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') PCM(key, 3) // 34 + PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PCM(key, 3) // 35 + PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') PCM(key, 3) // 22 + + PORT_START("P11") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PCM(key, 4) // 83 + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PCM(key, 4) // 28 + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHAR(UCHAR_MAMEKEY(INSERT)) PCM(key, 4) // 82 + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 4) // 89 - no key + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 4) // 86 - no key + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 4) // 87 - no key + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 4) // 88 - no key + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PCM(key, 4) // 67 + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PCM(key, 4) // 68 + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 4) // scan code ff - keyboard error + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PCM(key, 4) // 43 + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CAPS LOCK") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PCM(key, 4) // 58 + PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT LOCK") PORT_CODE(KEYCODE_LALT) PCM(key, 4) // 56 + PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 4) // 85 - no key + PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PCM(key, 4) // 47 + PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PCM(key, 4) // 57 + + PORT_START("P10") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 3") PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_PGDN) PCM(key, 5) // 81 + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 5) // ff - keyboard error + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 2") PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PCM(key, 5) // 80 + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("NEW LINE") PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PCM(key, 5) // 84 (programmable, default is 28) + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PCM(key, 5) // 79 + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("KP 4") PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR('4') PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PCM(key, 5) // 75 + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_UNUSED ) PCM(key, 5) // ff - keyboard error + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PCM(key, 5) // 65 + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PCM(key, 5) // 66 + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PCM(key, 5) // 42 + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PCM(key, 5) // 44 + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PCM(key, 5) // 50 + PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') PCM(key, 5) // 30 + PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') PCM(key, 5) // 31 + PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PCM(key, 5) // 48 + PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PCM(key, 5) // 49 +INPUT_PORTS_END + + +//------------------------------------------------- +// ROM( eispc_kb ) +//------------------------------------------------- + +ROM_START( eispc_kb ) + ROM_REGION( 0x800, M6801_TAG, 0 ) + ROM_LOAD( "sgtpepper-1.2.bin", 0x000, 0x800, CRC(7107b841) SHA1(a939dd50622575c31fea9c7adb7a7db5403a7aca) ) +ROM_END + +} // anonymous namespace + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(EISPC_KB, eispc_keyboard_device, "eispc_kb", "Ericsson PC keyboard") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// eispc_keyboard_device - constructor +//------------------------------------------------- + +eispc_keyboard_device::eispc_keyboard_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + uint32_t clock) + : device_t(mconfig, EISPC_KB, tag, owner, clock) + , m_mcu(*this, M6801_TAG) + , m_rows(*this, "P1%u", 0) + , m_txd_cb(*this) + , m_rxd_high(true) + , m_txd_high(true) + , m_hold(true) + , m_col_select(0) +{ +} + +INPUT_CHANGED_MEMBER( eispc_keyboard_device::key ) +{ + if (oldval && !newval) + { + LOGUI("Key Pressed - name: %s field: %04x param: %04x oldval: %04x newval: %04x\n", field.name(), field.defvalue(), param, oldval, newval); + int idx = *((int *)(¶m)); + if (idx >= sizeof(keys)) + logerror("Out of bounds access in keys array\n"); + else + keys[idx] |= (uint16_t) field.defvalue(); + } + else if (newval && !oldval) + { + LOGUI("Key Released - name: %s field: %04x param: %04x oldval: %04x newval: %04x\n", field.name(), field.defvalue(), param, oldval, newval); + int idx = *((int *)(¶m)); + if (idx >= sizeof(keys)) + logerror("Out of bounds access in keys array\n"); + else + keys[idx] &= ~(uint16_t)field.defvalue(); + } + for (int i = 0; i < 6; i++) LOGUI("%04x ", keys[i]); LOGUI("\n"); +} + +WRITE_LINE_MEMBER(eispc_keyboard_device::rxd_w) +{ + LOGBITS("KBD bit presented: %d\n", state); + m_rxd_high = CLEAR_LINE != state; +} + +WRITE_LINE_MEMBER(eispc_keyboard_device::hold_w) +{ + m_hold = CLEAR_LINE == state; +} + +WRITE_LINE_MEMBER(eispc_keyboard_device::rst_line_w) +{ + if (state == CLEAR_LINE) + { + m_mcu->resume(SUSPEND_REASON_RESET); + LOGRST("KBD: Keyboard mcu reset line is cleared\n"); + } + else + { + m_mcu->suspend(SUSPEND_REASON_RESET, 0); + LOGRST("KBD: Keyboard mcu reset line is asserted\n"); + } + +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void eispc_keyboard_device::device_reset() +{ + LOGRST("KBD: Keyboard is in reset until host computer explicitly releases the reset line\n"); + m_mcu->suspend(SUSPEND_REASON_RESET, 0); + for (auto & elem : keys) elem = 0; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void eispc_keyboard_device::device_start() +{ + m_txd_cb.resolve_safe(); + + save_item(NAME(m_rxd_high)); + save_item(NAME(m_txd_high)); + save_item(NAME(m_col_select)); + + m_rxd_high = true; + m_txd_high = true; + m_col_select = 0; +} + + + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void eispc_keyboard_device::device_add_mconfig(machine_config &config) +{ + M6801(config, m_mcu, XTAL(4'915'200)); // Crystal verified from schematics and visual inspection + m_mcu->set_addrmap(AS_PROGRAM, &eispc_keyboard_device::eispc_kb_mem); + + m_mcu->in_p1_cb().set([this] + { + uint8_t data = 0; // Indicate what keys are pressed in selected column + + for (int i = 0; i < 6; i++) data |= (keys[i] & m_col_select ? 1 << i : 0); + + // Update txd bit + data &= 0x3f; + data |= ((!m_hold || !m_txd_high) ? 0 : 0x40); + + if ((data & 0x3f) != 0 && data != m_p1) + { + LOGUI("Reading port 1: %02x m_col_select:%04x\n", data, m_col_select); + m_p1 = data; + } + return data; + }); + + m_mcu->out_p1_cb().set([this](uint8_t data) + { + LOGPORTS("Writing %02x PORT 1\n", data); + }); + + m_mcu->in_p2_cb().set([this] + { + uint8_t data = M6801_MODE_7 | (m_rxd_high ? (1 << 3) : 0); + LOGPORTS("Reading port 2: %02x\n", data); + //LOGBITS("KBD: Reading rxd_high: %02x\n", m_rxd_high); + return data; + }); + + m_mcu->out_p2_cb().set([this](uint8_t data) + { + LOGPORTS("Writing port 2: %02x\n", data); + LOGBITS("KBD: writing bit: %02x\n", BIT(data, 4)); + }); + + m_mcu->out_ser_tx_cb().set([this](bool state) + { + m_txd_high = CLEAR_LINE != state; + LOGBITS("KBD: writing bit: %02x\n", m_txd_high); + m_txd_cb(state); + }); + + m_mcu->in_p3_cb().set([this] + { + LOGPORTS("Reading Port 3\n"); + return 0x00; + }); + + m_mcu->out_p3_cb().set([this](uint8_t data) + { + m_col_select &= 0xff00; + m_col_select |= ~data; + }); + + m_mcu->in_p4_cb().set([this] + { + LOGPORTS("Reading Port 4\n"); + return 0x00; + }); + + m_mcu->out_p4_cb().set([this](uint8_t data) + { + m_col_select &= 0x00ff; + m_col_select |= (~data << 8); + }); +} + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor eispc_keyboard_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( eispc_kb ); +} + +//------------------------------------------------- +// ADDRESS_MAP( eispc_kb_mem ) +//------------------------------------------------- + +void eispc_keyboard_device::eispc_kb_mem(address_map &map) +{ + map(0x0000, 0x001f).rw(M6801_TAG, FUNC(m6801_cpu_device::m6801_io_r), FUNC(m6801_cpu_device::m6801_io_w)); + map(0x0080, 0x00ff).ram(); + map(0xf800, 0xffff).rom().region(M6801_TAG, 0); +} + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const tiny_rom_entry *eispc_keyboard_device::device_rom_region() const +{ + return ROM_NAME( eispc_kb ); +} diff --git a/src/mame/machine/eispc_kb.h b/src/mame/machine/eispc_kb.h new file mode 100644 index 00000000000..66547409a47 --- /dev/null +++ b/src/mame/machine/eispc_kb.h @@ -0,0 +1,45 @@ +// license:BSD-3-Clause +// copyright-holders:Joakim Larsson Edström +#ifndef MAME_MACHINE_EISPC_KB_H +#define MAME_MACHINE_EISPC_KB_H + +#pragma once + +#include "cpu/m6800/m6801.h" + +DECLARE_DEVICE_TYPE(EISPC_KB, eispc_keyboard_device) + +class eispc_keyboard_device : public device_t +{ +public: + auto txd_cb() { return m_txd_cb.bind(); } + + eispc_keyboard_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock = 0); + + DECLARE_INPUT_CHANGED_MEMBER(key); + DECLARE_WRITE_LINE_MEMBER(rxd_w); + DECLARE_WRITE_LINE_MEMBER(hold_w); + DECLARE_WRITE_LINE_MEMBER(rst_line_w); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + virtual ioport_constructor device_input_ports() const override; + virtual tiny_rom_entry const *device_rom_region() const override; + + required_device m_mcu; + required_ioport_array<6> m_rows; + devcb_write_line m_txd_cb; // Callback for KBD-> EPC + + bool m_rxd_high; // state of Rx input line + bool m_txd_high; // state of Tx output line + bool m_hold; + uint16_t m_col_select; + uint16_t keys[6]; + uint8_t m_p1; + + void eispc_kb_mem(address_map &map); +}; + +#endif // MAME_MACHINE_EISPC_KB_H diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 6007b355e35..8310c0e9866 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -12715,6 +12715,9 @@ splndrbt2 // (c) 1985 Alpha Denshi Co. splndrbta // (c) 1985 Alpha Denshi Co. splndrbtb // (c) 1985 Alpha Denshi Co. +@source:eispc.cpp +epc // 1984 Ericsson PC + @source:ertictac.cpp ertictac // (c) 1992 Sisteme ertictaca // (c) 1992 Sisteme @@ -31659,7 +31662,6 @@ comport // Compaq Portable dgone // 1984 Data General/One eagle1600 // eaglespirit // Eagle PC Spirit -epc // 1984 Ericsson PC eppc // 1985 Ericsson Portable PC hyo88t // Hyosung Topstar 88T ibm5550 // diff --git a/src/mame/mess.flt b/src/mame/mess.flt index 91f9f9f0af8..079b8b3921b 100644 --- a/src/mame/mess.flt +++ b/src/mame/mess.flt @@ -220,6 +220,7 @@ ec184x.cpp ec65.cpp ec7915.cpp einstein.cpp +eispc.cpp elan_eu3a05.cpp elan_eu3a14.cpp electron.cpp