eispc.cpp: Split out of the Ericsson PC (epc) from pc.cpp and added a serial keyboard

This commit is contained in:
Joakim Larsson Edstrom 2019-09-26 14:06:08 +02:00
parent dd737543f0
commit dda15d1b6d
7 changed files with 1465 additions and 53 deletions

View File

@ -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",

962
src/mame/drivers/eispc.cpp Normal file
View File

@ -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<i8086_cpu_device> m_maincpu;
required_device<ram_device> m_ram;
required_device<isa8_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 <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> DECLARE_WRITE_LINE_MEMBER(epc_dack_w);
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;
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<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);
int m_rxtx_clk_state;
// Interrupt Controller
required_device<pic8259_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<pit8253_device> m_pit8253;
// Speaker
DECLARE_WRITE_LINE_MEMBER(speaker_ck_w);
required_device<speaker_sound_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<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;
DECLARE_FLOPPY_FORMATS( epc_floppy_formats );
// 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("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 <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>
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 )

View File

@ -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<isa8_slot_device>("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 )

View File

@ -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 *)(&param));
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 *)(&param));
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 );
}

View File

@ -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<m6801_cpu_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

View File

@ -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 //

View File

@ -220,6 +220,7 @@ ec184x.cpp
ec65.cpp
ec7915.cpp
einstein.cpp
eispc.cpp
elan_eu3a05.cpp
elan_eu3a14.cpp
electron.cpp