ie15: convert to a device with frontends (standalone driver and rs232 slot device) (take 2)

This commit is contained in:
Sergey Svishchev 2017-02-26 03:08:23 +03:00
parent 5676b415e5
commit dc6b566136
13 changed files with 964 additions and 702 deletions

View File

@ -1764,6 +1764,8 @@ if (BUSES["RS232"]~=null) then
MAME_DIR .. "src/devices/bus/rs232/terminal.h",
MAME_DIR .. "src/devices/bus/rs232/xvd701.cpp",
MAME_DIR .. "src/devices/bus/rs232/xvd701.h",
MAME_DIR .. "src/devices/bus/rs232/ie15.cpp",
MAME_DIR .. "src/devices/bus/rs232/ie15.h",
}
end

View File

@ -1137,6 +1137,20 @@ if (MACHINES["IDE"]~=null) then
}
end
---------------------------------------------------
--
--@src/devices/machine/ie15.h,MACHINES["IE15"] = true
---------------------------------------------------
if (MACHINES["IE15"]~=null) then
files {
MAME_DIR .. "src/devices/machine/ie15.cpp",
MAME_DIR .. "src/devices/machine/ie15.h",
MAME_DIR .. "src/devices/machine/ie15_kbd.cpp",
MAME_DIR .. "src/devices/machine/ie15_kbd.h",
}
end
---------------------------------------------------
--
--@src/devices/machine/im6402.h,MACHINES["IM6402"] = true

View File

@ -426,6 +426,7 @@ MACHINES["I8271"] = true
MACHINES["I8279"] = true
MACHINES["I8355"] = true
MACHINES["IDE"] = true
MACHINES["IE15"] = true
MACHINES["IM6402"] = true
MACHINES["INS8154"] = true
MACHINES["INS8250"] = true
@ -955,6 +956,7 @@ function linkProjects_mame_mess(_target, _subtarget)
"homelab",
"hp",
"ibm6580",
"ie15",
"imp",
"intel",
"interpro",
@ -2067,6 +2069,11 @@ files {
MAME_DIR .. "src/mame/machine/ibm6580_fdc.h",
}
createMESSProjects(_target, _subtarget, "ie15")
files {
MAME_DIR .. "src/mame/drivers/ie15.cpp",
}
createMESSProjects(_target, _subtarget, "intel")
files {
MAME_DIR .. "src/mame/drivers/basic52.cpp",
@ -3371,9 +3378,6 @@ files {
MAME_DIR .. "src/mame/drivers/i7000.cpp",
MAME_DIR .. "src/mame/drivers/ibm3153.cpp",
MAME_DIR .. "src/mame/drivers/icatel.cpp",
MAME_DIR .. "src/mame/drivers/ie15.cpp",
MAME_DIR .. "src/mame/machine/ie15_kbd.cpp",
MAME_DIR .. "src/mame/machine/ie15_kbd.h",
MAME_DIR .. "src/mame/drivers/if800.cpp",
MAME_DIR .. "src/mame/drivers/imsai.cpp",
MAME_DIR .. "src/mame/drivers/indiana.cpp",

View File

@ -0,0 +1,90 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
#include "ie15.h"
ie15_terminal_device::ie15_terminal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: ie15_device(mconfig, SERIAL_TERMINAL_IE15, "IE15 Terminal", tag, owner, clock, "ie15_terminal", __FILE__)
, device_rs232_port_interface(mconfig, *this)
, m_rs232_txbaud(*this, "RS232_TXBAUD")
, m_rs232_rxbaud(*this, "RS232_RXBAUD")
, m_rs232_startbits(*this, "RS232_STARTBITS")
, m_rs232_databits(*this, "RS232_DATABITS")
, m_rs232_parity(*this, "RS232_PARITY")
, m_rs232_stopbits(*this, "RS232_STOPBITS")
{
}
static INPUT_PORTS_START(ie15_terminal)
PORT_INCLUDE(ie15)
MCFG_RS232_BAUD("RS232_TXBAUD", RS232_BAUD_9600, "TX Baud", ie15_terminal_device, update_serial)
MCFG_RS232_BAUD("RS232_RXBAUD", RS232_BAUD_9600, "RX Baud", ie15_terminal_device, update_serial)
MCFG_RS232_STARTBITS("RS232_STARTBITS", RS232_STARTBITS_1, "Start Bits", ie15_terminal_device, update_serial)
MCFG_RS232_DATABITS("RS232_DATABITS", RS232_DATABITS_8, "Data Bits", ie15_terminal_device, update_serial)
MCFG_RS232_PARITY("RS232_PARITY", RS232_PARITY_NONE, "Parity", ie15_terminal_device, update_serial)
MCFG_RS232_STOPBITS("RS232_STOPBITS", RS232_STOPBITS_1, "Stop Bits", ie15_terminal_device, update_serial)
INPUT_PORTS_END
ioport_constructor ie15_terminal_device::device_input_ports() const
{
return INPUT_PORTS_NAME(ie15_terminal);
}
WRITE_LINE_MEMBER(ie15_terminal_device::update_serial)
{
int startbits = convert_startbits(m_rs232_startbits->read());
int databits = convert_databits(m_rs232_databits->read());
parity_t parity = convert_parity(m_rs232_parity->read());
stop_bits_t stopbits = convert_stopbits(m_rs232_stopbits->read());
set_data_frame(startbits, databits, parity, stopbits);
int txbaud = convert_baud(m_rs232_txbaud->read());
set_tra_rate(txbaud);
int rxbaud = convert_baud(m_rs232_rxbaud->read());
set_rcv_rate(rxbaud);
output_rxd(1);
// TODO: make this configurable
output_dcd(0);
output_dsr(0);
output_cts(0);
}
void ie15_terminal_device::tra_callback()
{
output_rxd(transmit_register_get_data_bit());
}
void ie15_terminal_device::tra_complete()
{
ie15_device::tra_complete();
}
void ie15_terminal_device::rcv_complete()
{
receive_register_extract();
term_write(get_received_char());
}
void ie15_terminal_device::device_start()
{
ie15_device::device_start();
}
void ie15_terminal_device::device_reset()
{
update_serial(0);
ie15_device::device_reset();
}
void ie15_terminal_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
ie15_device::device_timer(timer, id, param, ptr);
}
const device_type SERIAL_TERMINAL_IE15 = device_creator<ie15_terminal_device>;

View File

@ -0,0 +1,42 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
#ifndef MAME_BUS_RS232_IE15_H
#define MAME_BUS_RS232_IE15_H
#include "rs232.h"
#include "machine/ie15.h"
class ie15_terminal_device : public ie15_device,
public device_rs232_port_interface
{
public:
ie15_terminal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
virtual DECLARE_WRITE_LINE_MEMBER( input_txd ) override { ie15_device::serial_rx_callback(state); }
DECLARE_WRITE_LINE_MEMBER(update_serial);
protected:
virtual ioport_constructor device_input_ports() const override;
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
virtual void tra_callback() override;
virtual void tra_complete() override;
virtual void rcv_complete() override;
private:
required_ioport m_rs232_txbaud;
required_ioport m_rs232_rxbaud;
required_ioport m_rs232_startbits;
required_ioport m_rs232_databits;
required_ioport m_rs232_parity;
required_ioport m_rs232_stopbits;
};
extern const device_type SERIAL_TERMINAL_IE15;
#endif // MAME_BUS_RS232_IE15_H

View File

@ -116,6 +116,7 @@ device_rs232_port_interface::~device_rs232_port_interface()
#include "pty.h"
#include "sun_kbd.h"
#include "terminal.h"
#include "ie15.h"
SLOT_INTERFACE_START( default_rs232_devices )
SLOT_INTERFACE("keyboard", SERIAL_KEYBOARD)
@ -125,4 +126,5 @@ SLOT_INTERFACE_START( default_rs232_devices )
SLOT_INTERFACE("terminal", SERIAL_TERMINAL)
SLOT_INTERFACE("pty", PSEUDO_TERMINAL)
SLOT_INTERFACE("sunkbd", SUN_KBD_ADAPTOR)
SLOT_INTERFACE("ie15", SERIAL_TERMINAL_IE15)
SLOT_INTERFACE_END

View File

@ -21,17 +21,17 @@
//**************************************************************************
// device type definition
const device_type IE15 = device_creator<ie15_device>;
const device_type IE15_CPU = device_creator<ie15_cpu_device>;
//**************************************************************************
// DEVICE INTERFACE
//**************************************************************************
//-------------------------------------------------
// ie15_device - constructor
// ie15_cpu_device - constructor
//-------------------------------------------------
ie15_device::ie15_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, IE15, "ie15", tag, owner, clock, "ie15_cpu", __FILE__),
ie15_cpu_device::ie15_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, IE15_CPU, "ie15 CPU", tag, owner, clock, "ie15_cpu", __FILE__),
m_program_config("program", ENDIANNESS_LITTLE, 8, 14),
m_io_config("io", ENDIANNESS_LITTLE, 8, 8), m_A(0), m_CF(0), m_ZF(0), m_RF(0), m_flags(0),
m_program(nullptr), m_io(nullptr),
@ -45,7 +45,7 @@ ie15_device::ie15_device(const machine_config &mconfig, const char *tag, device_
// device_start - start up the device
//-------------------------------------------------
void ie15_device::device_start()
void ie15_cpu_device::device_start()
{
// find address spaces
m_program = &space(AS_PROGRAM);
@ -75,7 +75,7 @@ void ie15_device::device_start()
// device_reset - reset the device
//-------------------------------------------------
void ie15_device::device_reset()
void ie15_cpu_device::device_reset()
{
m_CF = m_ZF = m_RF = 0;
m_A = 0;
@ -89,7 +89,7 @@ void ie15_device::device_reset()
// the space doesn't exist
//-------------------------------------------------
const address_space_config *ie15_device::memory_space_config(address_spacenum spacenum) const
const address_space_config *ie15_cpu_device::memory_space_config(address_spacenum spacenum) const
{
return (spacenum == AS_PROGRAM) ? &m_program_config :
(spacenum == AS_IO) ? &m_io_config :
@ -101,7 +101,7 @@ const address_space_config *ie15_device::memory_space_config(address_spacenum sp
// after it has been set
//-------------------------------------------------
void ie15_device::state_import(const device_state_entry &entry)
void ie15_cpu_device::state_import(const device_state_entry &entry)
{
switch (entry.index())
{
@ -118,7 +118,7 @@ void ie15_device::state_import(const device_state_entry &entry)
// to a known location where it can be read
//-------------------------------------------------
void ie15_device::state_export(const device_state_entry &entry)
void ie15_cpu_device::state_export(const device_state_entry &entry)
{
switch (entry.index())
{
@ -135,7 +135,7 @@ void ie15_device::state_export(const device_state_entry &entry)
// for the debugger
//-------------------------------------------------
void ie15_device::state_string_export(const device_state_entry &entry, std::string &str) const
void ie15_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
{
switch (entry.index())
{
@ -153,7 +153,7 @@ void ie15_device::state_string_export(const device_state_entry &entry, std::stri
// of the shortest instruction, in bytes
//-------------------------------------------------
uint32_t ie15_device::disasm_min_opcode_bytes() const
uint32_t ie15_cpu_device::disasm_min_opcode_bytes() const
{
return 1;
}
@ -163,7 +163,7 @@ uint32_t ie15_device::disasm_min_opcode_bytes() const
// of the longest instruction, in bytes
//-------------------------------------------------
uint32_t ie15_device::disasm_max_opcode_bytes() const
uint32_t ie15_cpu_device::disasm_max_opcode_bytes() const
{
return 2;
}
@ -173,7 +173,7 @@ uint32_t ie15_device::disasm_max_opcode_bytes() const
// helper function
//-------------------------------------------------
offs_t ie15_device::disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options)
offs_t ie15_cpu_device::disasm_disassemble(std::ostream &stream, offs_t pc, const uint8_t *oprom, const uint8_t *opram, uint32_t options)
{
extern CPU_DISASSEMBLE( ie15 );
return CPU_DISASSEMBLE_NAME(ie15)(nullptr, stream, pc, oprom, opram, 0);
@ -188,7 +188,7 @@ offs_t ie15_device::disasm_disassemble(std::ostream &stream, offs_t pc, const ui
// cycles it takes for one instruction to execute
//-------------------------------------------------
uint32_t ie15_device::execute_min_cycles() const
uint32_t ie15_cpu_device::execute_min_cycles() const
{
return 1;
}
@ -198,7 +198,7 @@ uint32_t ie15_device::execute_min_cycles() const
// cycles it takes for one instruction to execute
//-------------------------------------------------
uint32_t ie15_device::execute_max_cycles() const
uint32_t ie15_cpu_device::execute_max_cycles() const
{
return 1;
}
@ -207,7 +207,7 @@ uint32_t ie15_device::execute_max_cycles() const
// execute_run - execute until our icount expires
//-------------------------------------------------
void ie15_device::execute_run()
void ie15_cpu_device::execute_run()
{
// Removing the hook entirely is considerably faster than calling it for every instruction if the debugger is disabled entirely
if (machine().debug_flags & DEBUG_FLAG_ENABLED)
@ -227,7 +227,7 @@ void ie15_device::execute_run()
}
}
inline void ie15_device::illegal(uint8_t opcode)
inline void ie15_cpu_device::illegal(uint8_t opcode)
{
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
@ -238,7 +238,7 @@ inline void ie15_device::illegal(uint8_t opcode)
// XXX verify that m_ZF and m_CF are set and handled right
// XXX 'ota' apparently writes the ALU buffer register, not accumulator
// XXX what if ldc was at 0x_ff?
inline void ie15_device::execute_one(int opcode)
inline void ie15_cpu_device::execute_one(int opcode)
{
uint16_t tmp;
@ -425,42 +425,42 @@ inline void ie15_device::execute_one(int opcode)
INLINE FUNCTIONS
***************************************************************************/
inline uint8_t ie15_device::rop()
inline uint8_t ie15_cpu_device::rop()
{
uint8_t retVal = m_direct->read_byte(m_PC.w.l);
m_PC.w.l = (m_PC.w.l + 1) & 0x0fff;
return retVal;
}
inline uint8_t ie15_device::arg()
inline uint8_t ie15_cpu_device::arg()
{
uint8_t retVal = m_direct->read_byte(m_PC.w.l);
return retVal;
}
inline uint8_t ie15_device::get_reg_lo(uint8_t reg)
inline uint8_t ie15_cpu_device::get_reg_lo(uint8_t reg)
{
uint16_t tmp = m_RF ? m_REGS[16 + reg] : m_REGS[reg];
return tmp & 255;
}
inline uint16_t ie15_device::get_reg(uint8_t reg)
inline uint16_t ie15_cpu_device::get_reg(uint8_t reg)
{
return m_RF ? m_REGS[16 + reg] : m_REGS[reg];
}
inline void ie15_device::set_reg(uint8_t reg, uint16_t val)
inline void ie15_cpu_device::set_reg(uint8_t reg, uint16_t val)
{
(m_RF ? m_REGS[16 + reg] : m_REGS[reg]) = val;
}
inline void ie15_device::update_flags(uint8_t val)
inline void ie15_cpu_device::update_flags(uint8_t val)
{
m_ZF = (val == 0xff) ? 1 : 0;
}
inline uint8_t ie15_device::do_condition(uint8_t val)
inline uint8_t ie15_cpu_device::do_condition(uint8_t val)
{
uint8_t v = (val >> 5) & 1;
uint8_t cond = 0;
@ -475,7 +475,7 @@ inline uint8_t ie15_device::do_condition(uint8_t val)
return cond;
}
inline uint16_t ie15_device::get_addr(uint8_t val)
inline uint16_t ie15_cpu_device::get_addr(uint8_t val)
{
uint8_t lo = arg();
return ((val & 0x0f) << 8) + lo + 1;

View File

@ -21,13 +21,13 @@ enum
// TYPE DEFINITIONS
//**************************************************************************
class ie15_device;
class ie15_cpu_device;
class ie15_device : public cpu_device
class ie15_cpu_device : public cpu_device
{
public:
// construction/destruction
ie15_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
ie15_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
// device-level overrides
@ -84,6 +84,6 @@ protected:
};
// device type definition
extern const device_type IE15;
extern const device_type IE15_CPU;
#endif

View File

@ -0,0 +1,631 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************
15IE-00-013 Terminal
A serial (RS232 or current loop) green-screen terminal, mostly VT52
compatible (no Hold Screen mode and no graphics character set).
Alternate character set (selected by SO/SI chars) is Cyrillic.
****************************************************************************/
#include "machine/ie15.h"
#include "ie15.lh"
#define VERBOSE_DBG 1 /* general debug messages */
#define DBG_LOG(N,M,A) \
do { \
if(VERBOSE_DBG>=N) \
{ \
if( M ) \
logerror("%11.6f at %s: %-10s",machine().time().as_double(),machine().describe_context(),(char*)M ); \
logerror A; \
} \
} while (0)
ie15_device::ie15_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source)
: device_t(mconfig, type, name, tag, owner, clock, shortname, source)
, device_serial_interface(mconfig, *this)
, m_maincpu(*this, "maincpu")
, m_p_videoram(*this, "video")
, m_p_chargen(*this, "chargen")
, m_beeper(*this, "beeper")
, m_rs232(*this, "rs232")
, m_screen(*this, "screen")
, m_keyboard(*this, "keyboard")
, m_io_keyboard(*this, "io_keyboard")
{
}
ie15_device::ie15_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: ie15_device(mconfig, IE15, "IE15 Terminal", tag, owner, clock, "ie15", __FILE__)
{
}
READ8_MEMBER(ie15_device::mem_r)
{
uint8_t ret;
ret = m_p_videoram[m_video.ptr1];
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 && m_video.ptr1 >= SCREEN_PAGE)
{
DBG_LOG(2, "memory", ("R @ %03x == %02x\n", m_video.ptr1, ret));
}
m_video.ptr1++;
m_video.ptr1 &= 0xfff;
m_latch = 0;
return ret;
}
WRITE8_MEMBER(ie15_device::mem_w)
{
if ((m_latch ^= 1) == 0)
{
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 && m_video.ptr1 >= SCREEN_PAGE)
{
DBG_LOG(2, "memory", ("W @ %03x <- %02x\n", m_video.ptr1, data));
}
m_p_videoram[m_video.ptr1++] = data;
m_video.ptr1 &= 0xfff;
}
}
WRITE8_MEMBER(ie15_device::mem_addr_inc_w)
{
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2, "memory", ("++ %03x\n", m_video.ptr1));
}
m_video.ptr1++;
m_video.ptr1 &= 0xfff;
if (m_video.enable) m_video.ptr2 = m_video.ptr1;
}
WRITE8_MEMBER(ie15_device::mem_addr_dec_w)
{
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2, "memory", ("-- %03x\n", m_video.ptr1));
}
m_video.ptr1--;
m_video.ptr1 &= 0xfff;
if (m_video.enable) m_video.ptr2 = m_video.ptr1;
}
WRITE8_MEMBER(ie15_device::mem_addr_lo_w)
{
uint16_t tmp = m_video.ptr1;
tmp &= 0xff0;
tmp |= ((data >> 4) & 0xf);
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2, "memory", ("lo %03x <- %02x = %03x\n", m_video.ptr1, data, tmp));
}
m_video.ptr1 = tmp;
if (m_video.enable) m_video.ptr2 = tmp;
}
WRITE8_MEMBER(ie15_device::mem_addr_hi_w)
{
uint16_t tmp = m_video.ptr1;
tmp &= 0xf;
tmp |= (data << 4);
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2, "memory", ("hi %03x <- %02x = %03x\n", m_video.ptr1, data, tmp));
}
m_video.ptr1 = tmp;
if (m_video.enable) m_video.ptr2 = tmp;
}
TIMER_CALLBACK_MEMBER(ie15_device::ie15_beepoff)
{
m_beeper->set_state(0);
}
WRITE8_MEMBER(ie15_device::beep_w)
{
uint16_t length = (m_long_beep & IE_TRUE) ? 150 : 400;
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(1, "beep", ("(%s)\n", m_long_beep ? "short" : "long"));
}
machine().scheduler().timer_set(attotime::from_msec(length), timer_expired_delegate(FUNC(ie15_device::ie15_beepoff),this));
m_beeper->set_state(1);
}
/* keyboard */
// active high
READ8_MEMBER(ie15_device::kb_r)
{
DBG_LOG(2, "keyboard", ("R %02X '%c'\n", m_kb_data, m_kb_data < 0x20 ? ' ' : m_kb_data));
return m_kb_data;
}
// active low
READ8_MEMBER(ie15_device::kb_ready_r)
{
m_kb_flag &= IE_TRUE;
if (m_kb_flag != m_kb_flag0)
{
DBG_LOG(2, "keyboard", ("? %c\n", m_kb_flag ? 'n' : 'y'));
m_kb_flag0 = m_kb_flag;
}
return m_kb_flag;
}
// active low
WRITE8_MEMBER(ie15_device::kb_ready_w)
{
DBG_LOG(2, "keyboard", ("clear ready\n"));
m_kb_flag = IE_TRUE | IE_KB_ACK;
}
// active high; active = interpret controls, inactive = display controls
READ8_MEMBER(ie15_device::kb_s_red_r)
{
return m_io_keyboard->read() & IE_KB_RED ? IE_TRUE : 0;
}
// active high; active = setup mode
READ8_MEMBER(ie15_device::kb_s_sdv_r)
{
return m_kb_control & IE_KB_SDV ? IE_TRUE : 0;
}
// active high; active = keypress detected on aux keypad
READ8_MEMBER(ie15_device::kb_s_dk_r)
{
return m_kb_control & IE_KB_DK ? IE_TRUE : 0;
}
// active low; active = full duplex, inactive = half duplex
READ8_MEMBER(ie15_device::kb_s_dupl_r)
{
return m_io_keyboard->read() & IE_KB_DUP ? IE_TRUE : 0;
}
// active high; active = on-line, inactive = local editing
READ8_MEMBER(ie15_device::kb_s_lin_r)
{
return m_io_keyboard->read() & IE_KB_LIN ? IE_TRUE : 0;
}
/* serial port */
void ie15_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
device_serial_interface::device_timer(timer, id, param, ptr);
switch (id)
{
case TIMER_HBLANK:
if (m_hblank) // Transitioning from in blanking to out of blanking
{
m_hblank = 0;
m_hblank_timer->adjust(m_screen->time_until_pos((m_vpos + 1) % IE15_TOTAL_VERT, 0));
scanline_callback();
}
else // Transitioning from out of blanking to in blanking
{
m_hblank = 1;
m_hblank_timer->adjust(m_screen->time_until_pos(m_vpos, IE15_HORZ_START));
}
break;
}
}
WRITE_LINE_MEMBER(ie15_device::serial_rx_callback)
{
device_serial_interface::rx_w(state);
}
void ie15_device::rcv_complete()
{
receive_register_extract();
m_serial_rx_char = get_received_char();
m_serial_rx_ready = IE_FALSE;
}
void ie15_device::tra_callback()
{
uint8_t bit = transmit_register_get_data_bit();
m_rs232->write_txd(bit);
}
void ie15_device::tra_complete()
{
m_serial_tx_ready = IE_TRUE;
}
// active low
READ8_MEMBER(ie15_device::serial_rx_ready_r)
{
return m_serial_rx_ready;
}
// active high
READ8_MEMBER(ie15_device::serial_tx_ready_r)
{
return m_serial_tx_ready;
}
// not called unless data are ready
READ8_MEMBER(ie15_device::serial_r)
{
m_serial_rx_ready = IE_TRUE;
DBG_LOG(1,"serial",("R %02X '%c'\n", m_serial_rx_char, m_serial_rx_char < 0x20?' ':m_serial_rx_char&127));
return m_serial_rx_char;
}
WRITE8_MEMBER(ie15_device::serial_w)
{
DBG_LOG(1, "serial", ("W %02X '%c'\n", data, data < 0x20 ? ' ' : data & 127));
m_serial_tx_ready = IE_FALSE;
transmit_register_setup(data);
}
WRITE8_MEMBER(ie15_device::serial_speed_w)
{
return;
}
READ8_MEMBER(ie15_device::flag_r)
{
switch (offset)
{
case 0: // hsync pulse (not hblank)
return m_hblank;
case 1: // marker scanline
return m_marker_scanline;
case 2: // vblank
return !m_screen->vblank();
case 4:
return m_kb_ruslat;
default:
break;
}
if (machine().debug_flags & DEBUG_FLAG_ENABLED)
{
DBG_LOG(2, "flag", ("read %d: ?\n", offset));
}
return 0;
}
WRITE8_MEMBER(ie15_device::flag_w)
{
switch (offset)
{
case 0:
m_video.enable = data;
break;
case 1:
m_video.cursor = data;
break;
case 2:
m_long_beep = data;
break;
case 3:
m_video.line25 = data;
break;
case 4:
m_kb_ruslat = data;
break;
default:
break;
}
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 && !offset)
{
DBG_LOG(2, "flag", ("%sset %d\n", data ? "" : "re", offset));
}
}
static ADDRESS_MAP_START( ie15_mem, AS_PROGRAM, 8, ie15_device )
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x0fff) AM_ROM
ADDRESS_MAP_END
static ADDRESS_MAP_START( ie15_io, AS_IO, 8, ie15_device )
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(000, 000) AM_READWRITE(mem_r, mem_w) // 00h W: memory request, R: memory data [6.1.2.2]
AM_RANGE(001, 001) AM_READ(serial_rx_ready_r) AM_WRITENOP // 01h W: memory latch [6.1.2.2]
AM_RANGE(002, 002) AM_WRITE(mem_addr_hi_w) // 02h W: memory address high [6.1.2.2]
AM_RANGE(003, 003) AM_WRITE(mem_addr_lo_w) // 03h W: memory address low [6.1.2.2]
AM_RANGE(004, 004) AM_WRITE(mem_addr_inc_w) // 04h W: memory address counter + [6.1.2.2]
AM_RANGE(005, 005) AM_WRITE(mem_addr_dec_w) // 05h W: memory address counter - [6.1.2.2]
AM_RANGE(006, 006) AM_READWRITE(serial_r, serial_w) // 06h W: serial port data [6.1.5.4]
// port 7 is handled in cpu core
AM_RANGE(010, 010) AM_READWRITE(serial_tx_ready_r, beep_w) // 08h W: speaker control [6.1.5.4]
AM_RANGE(011, 011) AM_READ(kb_r) // 09h R: keyboard data [6.1.5.2]
AM_RANGE(012, 012) AM_READ(kb_s_red_r) // 0Ah I: keyboard mode "RED" [6.1.5.2]
AM_RANGE(013, 013) AM_READ(kb_ready_r) // 0Bh R: keyboard data ready [6.1.5.2]
AM_RANGE(014, 014) AM_READWRITE(kb_s_sdv_r, serial_speed_w) // 0Ch W: serial port speed [6.1.3.1], R: keyboard mode "SDV" [6.1.5.2]
AM_RANGE(015, 015) AM_READWRITE(kb_s_dk_r, kb_ready_w) // 0Dh I: keyboard mode "DK" [6.1.5.2]
AM_RANGE(016, 016) AM_READ(kb_s_dupl_r) // 0Eh I: keyboard mode "DUPL" [6.1.5.2]
AM_RANGE(017, 017) AM_READ(kb_s_lin_r) // 0Fh I: keyboard mode "LIN" [6.1.5.2]
// simulation of flag registers
AM_RANGE(020, 027) AM_READWRITE(flag_r, flag_w)
ADDRESS_MAP_END
/* Input ports */
INPUT_PORTS_START( ie15 )
PORT_START("io_keyboard")
PORT_DIPNAME(IE_KB_RED, IE_KB_RED, "RED (Interpret controls)")
PORT_DIPSETTING(0x00, "Off")
PORT_DIPSETTING(IE_KB_RED, "On")
PORT_DIPNAME(IE_KB_DUP, IE_KB_DUP, "DUP (Full duplex)")
PORT_DIPSETTING(0x00, "Off")
PORT_DIPSETTING(IE_KB_DUP, "On")
PORT_DIPNAME(IE_KB_LIN, IE_KB_LIN, "LIN (Online)")
PORT_DIPSETTING(0x00, "Off")
PORT_DIPSETTING(IE_KB_LIN, "On")
INPUT_PORTS_END
WRITE16_MEMBER( ie15_device::kbd_put )
{
DBG_LOG(2,"keyboard",("W %02X<-%02X '%c' %02X (%c)\n", m_kb_data, data, 'x' /* data < 0x20 ? ' ' : (data & 255) */,
m_kb_flag, m_kb_flag ? 'n' : 'y'));
m_kb_control = (data >> 8) & 255;
// send new key only when firmware has processed previous one
if (m_kb_flag == IE_TRUE)
{
m_kb_data = data & 255;
m_kb_flag = 0;
}
}
void ie15_device::device_start()
{
m_hblank_timer = timer_alloc(TIMER_HBLANK);
m_hblank_timer->adjust(attotime::never);
m_video.ptr1 = m_video.ptr2 = m_latch = 0;
m_tmpbmp = std::make_unique<uint32_t[]>(IE15_TOTAL_HORZ * IE15_TOTAL_VERT);
}
void ie15_device::device_reset()
{
memset(&m_video, 0, sizeof(m_video));
m_kb_ruslat = m_long_beep = m_kb_control = m_kb_data = m_kb_flag0 = 0;
m_kb_flag = IE_TRUE;
m_hblank = 1;
m_hblank_timer->adjust(m_screen->time_until_pos(0, IE15_HORZ_START));
m_vpos = m_screen->vpos();
m_marker_scanline = (m_vpos % 11) > 7;
m_beeper->set_state(0);
m_serial_tx_ready = m_serial_rx_ready = IE_TRUE;
set_data_frame(1 /* start bits */, 8 /* data bits */, PARITY_NONE, STOP_BITS_1);
// device supports rates from 150 to 9600 baud but null_modem has hardcoded 9600
set_rate(9600);
}
/*
Usable raster is 800 x 275 pixels (80 x 25 characters). 24 lines are
available to the user and 25th (topmost) line is the status line.
Status line, if enabled, displays current serial port speed, 16 setup
bits, and clock. There is no NVRAM, so setup bits are always 0 after
reset and clock starts counting at 0 XXX.
No character attributes are available, but in 'display controls' mode
control characters stored in memory are shown as blinking chars.
Character cell is 10 x 11; character generator provides 7 x 8 of that.
3 extra horizontal pixels are always blank. Blinking cursor may be
displayed on 3 extra scan lines.
On each scan line, video board draws 80 characters from any location
in video memory; this is used by firmware to provide instant scroll
and cursor, which is a character with code 0x7F stored in off-screen
memory.
Video board output is controlled by
- control flag 0 "disable video": 0 == disable
- control flag 1 "cursor": 0 == if this scan line is one of extra 3,
enable video every 5 frames.
- control flag 3 "status line": 0 == current scan line is part of status line
- keyboard mode 'RED' ('display controls'): if character code is
less than 0x20 and RED is set, enable video every 5 frames; if RED is
unset, disable video.
*/
void ie15_device::draw_scanline(uint32_t *p, uint16_t offset, uint8_t scanline)
{
static const uint32_t palette[2] = { 0xff000000, 0xff00c000 };
uint8_t ra = scanline % 8;
uint32_t ra_high = 0x200 | ra;
bool blink((m_screen->frame_number() % 10) > 4);
bool red(m_io_keyboard->read() & IE_KB_RED);
bool blink_red_line25 = blink && red && m_video.line25;
bool cursor_blank = scanline > 7 && (!m_video.cursor || blink);
if (cursor_blank)
{
for (uint16_t x = 0; x < 80 * 10; x++)
{
*p++ = palette[0];
}
}
else
{
for (uint16_t x = offset; x < offset + 80; x++)
{
uint16_t chr = m_p_videoram[x] << 3;
uint8_t gfx = m_p_chargen[chr | ra];
if (chr < (0x20 << 3))
{
if (blink_red_line25)
gfx = m_p_chargen[chr | ra_high];
else
gfx = 0;
}
*p++ = palette[BIT(gfx, 7)];
*p++ = palette[BIT(gfx, 6)];
*p++ = palette[BIT(gfx, 5)];
*p++ = palette[BIT(gfx, 4)];
*p++ = palette[BIT(gfx, 3)];
*p++ = palette[BIT(gfx, 2)];
*p++ = palette[BIT(gfx, 1)];
*p++ = palette[0];
*p++ = palette[0];
*p++ = palette[0];
}
}
}
void ie15_device::update_leds()
{
uint8_t data = m_io_keyboard->read();
machine().output().set_value("lat_led", m_kb_ruslat ^ 1);
machine().output().set_value("nr_led", BIT(m_kb_control, IE_KB_NR_BIT) ^ 1);
machine().output().set_value("pch_led", BIT(data, IE_KB_PCH_BIT) ^ 1);
machine().output().set_value("dup_led", BIT(data, IE_KB_DUP_BIT) ^ 1);
machine().output().set_value("lin_led", BIT(data, IE_KB_LIN_BIT) ^ 1);
machine().output().set_value("red_led", BIT(data, IE_KB_RED_BIT) ^ 1);
machine().output().set_value("sdv_led", BIT(m_kb_control, IE_KB_SDV_BIT) ^ 1);
machine().output().set_value("prd_led", 1); // XXX
}
/*
VBlank is active for 3 topmost on-screen rows and 1 at the bottom; however, control flag 3
overrides VBlank,
allowing status line to be switched on and off.
*/
void ie15_device::scanline_callback()
{
int y = m_vpos;
m_vpos++;
m_vpos %= IE15_TOTAL_VERT;
m_marker_scanline = (m_vpos % 11) > 7;
DBG_LOG(3,"scanline_cb",
("addr %03x frame %d x %.4d y %.3d row %.2d e:c:s %d:%d:%d\n",
m_video.ptr2, (int)m_screen->frame_number(), m_screen->hpos(), y,
y%11, m_video.enable, m_video.cursor, m_video.line25));
if (y < IE15_VERT_START) return;
y -= IE15_VERT_START;
if (y >= IE15_DISP_VERT) return;
if (!m_video.enable || (y < IE15_STATUSLINE && m_video.line25))
{
memset(&m_tmpbmp[(y + IE15_VERT_START) * IE15_TOTAL_HORZ], 0, sizeof(uint32_t) * IE15_TOTAL_HORZ);
}
else
{
draw_scanline(&m_tmpbmp[(y + IE15_VERT_START) * IE15_TOTAL_HORZ + IE15_HORZ_START],
m_video.ptr2, y % 11);
}
}
uint32_t ie15_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
update_leds();
memcpy(&bitmap.pix32(0), &m_tmpbmp[0], sizeof(uint32_t) * IE15_TOTAL_HORZ * IE15_TOTAL_VERT);
return 0;
}
/* F4 Character Displayer */
static const gfx_layout ie15_charlayout =
{
7, 8, /* 7x8 pixels in 10x11 cell */
256, /* 256 characters */
1, /* 1 bits per pixel */
{ 0 }, /* no bitplanes */
/* x offsets */
{ 0, 1, 2, 3, 4, 5, 6 },
/* y offsets */
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
8*8 /* every char takes 8 bytes */
};
static GFXDECODE_START( ie15 )
GFXDECODE_ENTRY("chargen", 0x0000, ie15_charlayout, 0, 1)
GFXDECODE_END
static MACHINE_CONFIG_FRAGMENT( ie15core )
/* Basic machine hardware */
MCFG_CPU_ADD("maincpu", IE15_CPU, XTAL_30_8MHz/10)
MCFG_CPU_PROGRAM_MAP(ie15_mem)
MCFG_CPU_IO_MAP(ie15_io)
MCFG_DEFAULT_LAYOUT(layout_ie15)
/* Devices */
MCFG_DEVICE_ADD("keyboard", IE15_KEYBOARD, 0)
MCFG_IE15_KEYBOARD_CB(WRITE16(ie15_device, kbd_put))
MCFG_RS232_PORT_ADD("rs232", default_rs232_devices, "null_modem")
MCFG_RS232_RXD_HANDLER(WRITELINE(ie15_device, serial_rx_callback))
MCFG_SPEAKER_STANDARD_MONO("mono")
MCFG_SOUND_ADD("beeper", BEEP, 2400)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.15)
MACHINE_CONFIG_END
static MACHINE_CONFIG_FRAGMENT( ie15 )
MCFG_FRAGMENT_ADD(ie15core)
MCFG_SCREEN_ADD_MONOCHROME("screen", RASTER, rgb_t::green())
MCFG_SCREEN_UPDATE_DRIVER(ie15_device, screen_update)
MCFG_SCREEN_RAW_PARAMS(XTAL_30_8MHz/2, IE15_TOTAL_HORZ, IE15_HORZ_START,
IE15_HORZ_START+IE15_DISP_HORZ, IE15_TOTAL_VERT, IE15_VERT_START,
IE15_VERT_START+IE15_DISP_VERT);
MCFG_GFXDECODE_ADD("gfxdecode", "palette", ie15)
MCFG_PALETTE_ADD_MONOCHROME("palette")
MACHINE_CONFIG_END
/* ROM definition */
ROM_START( ie15 )
ROM_REGION(0x1000, "maincpu", ROMREGION_ERASE00)
ROM_DEFAULT_BIOS("5chip")
ROM_SYSTEM_BIOS(0, "5chip", "5-chip firmware (newer)")
ROMX_LOAD("dump1.bin", 0x0000, 0x1000, CRC(14b82284) SHA1(5ac4159fbb1c3b81445605e26cd97a713ae12b5f), ROM_BIOS(1))
ROM_SYSTEM_BIOS(1, "6chip", "6-chip firmware (older)")
ROMX_LOAD("dump5.bin", 0x0000, 0x1000, CRC(01f2e065) SHA1(2b72dc0594e38a528400cd25aed0c47e0c432895), ROM_BIOS(2))
ROM_REGION(0x1000, "video", ROMREGION_ERASE00)
ROM_REGION(0x0800, "chargen", ROMREGION_ERASE00)
ROM_LOAD("chargen-15ie.bin", 0x0000, 0x0800, CRC(ed16bf6b) SHA1(6af9fb75f5375943d5c0ce9ed408e0fb4621b17e))
ROM_END
machine_config_constructor ie15_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME(ie15);
}
ioport_constructor ie15_device::device_input_ports() const
{
return INPUT_PORTS_NAME(ie15);
}
const tiny_rom_entry *ie15_device::device_rom_region() const
{
return ROM_NAME(ie15);
}
const device_type IE15 = device_creator<ie15_device>;

133
src/devices/machine/ie15.h Normal file
View File

@ -0,0 +1,133 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
#ifndef MAME_MACHINE_IE15_H_
#define MAME_MACHINE_IE15_H_
#include "emu.h"
#include "bus/rs232/rs232.h"
#include "cpu/ie15/ie15.h"
#include "machine/ie15_kbd.h"
#include "sound/beep.h"
#include "screen.h"
#include "speaker.h"
#define SCREEN_PAGE (80*48)
#define IE_TRUE 0x80
#define IE_FALSE 0
#define IE15_TOTAL_HORZ 1000
#define IE15_DISP_HORZ 800
#define IE15_HORZ_START 200
#define IE15_TOTAL_VERT (28*11)
#define IE15_DISP_VERT (25*11)
#define IE15_VERT_START (2*11)
#define IE15_STATUSLINE 11
INPUT_PORTS_EXTERN(ie15);
class ie15_device : public device_t, public device_serial_interface
{
public:
ie15_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source);
ie15_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_WRITE8_MEMBER(write) { term_write(data); }
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
virtual machine_config_constructor device_mconfig_additions() const override;
virtual ioport_constructor device_input_ports() const override;
virtual const tiny_rom_entry *device_rom_region() const override;
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
virtual void rcv_complete() override;
virtual void tra_callback() override;
virtual void tra_complete() override;
void term_write(uint8_t data) { m_serial_rx_char = data; m_serial_rx_ready = IE_FALSE; }
public:
DECLARE_WRITE16_MEMBER(kbd_put);
DECLARE_WRITE_LINE_MEMBER(serial_rx_callback);
DECLARE_WRITE8_MEMBER(mem_w);
DECLARE_READ8_MEMBER(mem_r);
DECLARE_WRITE8_MEMBER(mem_addr_lo_w);
DECLARE_WRITE8_MEMBER(mem_addr_hi_w);
DECLARE_WRITE8_MEMBER(mem_addr_inc_w);
DECLARE_WRITE8_MEMBER(mem_addr_dec_w);
DECLARE_READ8_MEMBER(flag_r);
DECLARE_WRITE8_MEMBER(flag_w);
DECLARE_WRITE8_MEMBER(beep_w);
DECLARE_READ8_MEMBER(kb_r);
DECLARE_READ8_MEMBER(kb_ready_r);
DECLARE_READ8_MEMBER(kb_s_red_r);
DECLARE_READ8_MEMBER(kb_s_sdv_r);
DECLARE_READ8_MEMBER(kb_s_dk_r);
DECLARE_READ8_MEMBER(kb_s_dupl_r);
DECLARE_READ8_MEMBER(kb_s_lin_r);
DECLARE_WRITE8_MEMBER(kb_ready_w);
DECLARE_READ8_MEMBER(serial_tx_ready_r);
DECLARE_WRITE8_MEMBER(serial_w);
DECLARE_READ8_MEMBER(serial_rx_ready_r);
DECLARE_READ8_MEMBER(serial_r);
DECLARE_WRITE8_MEMBER(serial_speed_w);
TIMER_CALLBACK_MEMBER(ie15_beepoff);
private:
static const device_timer_id TIMER_HBLANK = 0;
void scanline_callback();
void update_leds();
void draw_scanline(uint32_t *p, uint16_t offset, uint8_t scanline);
std::unique_ptr<uint32_t[]> m_tmpbmp;
emu_timer *m_hblank_timer;
uint8_t m_long_beep;
uint8_t m_kb_control;
uint8_t m_kb_data;
uint8_t m_kb_flag0;
uint8_t m_kb_flag;
uint8_t m_kb_ruslat;
uint8_t m_latch;
struct
{
uint8_t cursor;
uint8_t enable;
uint8_t line25;
uint32_t ptr1;
uint32_t ptr2;
} m_video;
uint8_t m_serial_rx_ready;
uint8_t m_serial_rx_char;
uint8_t m_serial_tx_ready;
int m_hblank;
int m_vpos;
int m_marker_scanline;
required_device<cpu_device> m_maincpu;
required_region_ptr<u8> m_p_videoram;
required_region_ptr<u8> m_p_chargen;
required_device<beep_device> m_beeper;
required_device<rs232_port_device> m_rs232;
required_device<screen_device> m_screen;
required_device<ie15_keyboard_device> m_keyboard;
required_ioport m_io_keyboard;
};
extern const device_type IE15;
#endif /* MAME_MACHINE_IE15_H_ */

View File

@ -150,10 +150,10 @@ void ie15_keyboard_device::device_reset()
/*
Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8 Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16 Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
--
;+ 1! 2" 3# 4$ 5% 6& 7' 8( 9) 0 -= 7 8 9 ?????? ???? ?????? ?????? ?????? f1 f2 f3
??J ??C ??U ??K ??E ??N ??G ??[ ??] ??Z ??H :* 4 5 6 ???? ???? ????1 ??1 ????2 f4 f5 f6
??F ??Y ??W ??A ??P ??R ??O ??L ??D ??V ??\ .> ???? 1 2 3 ?????? ?????? ?????? f7 f8 f9
??Q ??^ ??S ??M ??I ??T ??X ??B ??@ ,< /? _ SPC 0 , fA fB fC
;+ 1! 2" 3# 4$ 5% 6& 7' 8( 9) 0 -= 7 8 9 ТАБ ГТ СБР СТР СТС f1 f2 f3
ЙJ ЦC УU КK ЕE НN ГG Ш[ Щ] ЗZ ХH :* 4 5 6 ПС ВК АР1 С1 АР2 f4 f5 f6
ФF ЫY ВW АA ПP РR ОO ЛL ДD ЖV Э\ .> ЗБ 1 2 3 ПРД ПРМ ПРС f7 f8 f9
ЯQ Ч^ СS МM ИI ТT ЬX БB Ю@ ,< /? _ SPC 0 , fA fB fC
rom:

View File

@ -14,684 +14,28 @@
#include "emu.h"
#include "machine/ie15_kbd.h"
#include "bus/rs232/rs232.h"
#include "cpu/ie15/ie15.h"
#include "sound/beep.h"
#include "screen.h"
#include "speaker.h"
#include "ie15.lh"
#include "machine/ie15.h"
#define SCREEN_PAGE (80*48)
#define IE_TRUE 0x80
#define IE_FALSE 0
#define IE15_TOTAL_HORZ 1000
#define IE15_DISP_HORZ 800
#define IE15_HORZ_START 200
#define IE15_TOTAL_VERT (28*11)
#define IE15_DISP_VERT (25*11)
#define IE15_VERT_START (2*11)
#define IE15_STATUSLINE 11
#define VERBOSE_DBG 1 /* general debug messages */
#define DBG_LOG(N,M,A) \
do { \
if(VERBOSE_DBG>=N) \
{ \
if( M ) \
logerror("%11.6f at %s: %-24s",machine().time().as_double(),machine().describe_context(),(char*)M ); \
logerror A; \
} \
} while (0)
class ie15_state : public driver_device,
public device_serial_interface
class ie15_state : public driver_device
{
public:
ie15_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, device_serial_interface(mconfig, *this)
, m_maincpu(*this, "maincpu")
, m_p_videoram(*this, "video")
, m_p_chargen(*this, "chargen")
, m_beeper(*this, "beeper")
, m_rs232(*this, "rs232")
, m_screen(*this, "screen")
, m_io_keyboard(*this, "keyboard")
ie15_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_ie15(*this, "ie15")
{ }
DECLARE_WRITE16_MEMBER( kbd_put );
DECLARE_WRITE_LINE_MEMBER( serial_rx_callback );
DECLARE_WRITE8_MEMBER( mem_w );
DECLARE_READ8_MEMBER( mem_r );
DECLARE_WRITE8_MEMBER( mem_addr_lo_w );
DECLARE_WRITE8_MEMBER( mem_addr_hi_w );
DECLARE_WRITE8_MEMBER( mem_addr_inc_w );
DECLARE_WRITE8_MEMBER( mem_addr_dec_w );
DECLARE_READ8_MEMBER( flag_r );
DECLARE_WRITE8_MEMBER( flag_w );
DECLARE_WRITE8_MEMBER( beep_w );
DECLARE_READ8_MEMBER( kb_r );
DECLARE_READ8_MEMBER( kb_ready_r );
DECLARE_READ8_MEMBER( kb_s_red_r );
DECLARE_READ8_MEMBER( kb_s_sdv_r );
DECLARE_READ8_MEMBER( kb_s_dk_r );
DECLARE_READ8_MEMBER( kb_s_dupl_r );
DECLARE_READ8_MEMBER( kb_s_lin_r );
DECLARE_WRITE8_MEMBER( kb_ready_w );
DECLARE_READ8_MEMBER( serial_tx_ready_r );
DECLARE_WRITE8_MEMBER( serial_w );
DECLARE_READ8_MEMBER( serial_rx_ready_r );
DECLARE_READ8_MEMBER( serial_r );
DECLARE_WRITE8_MEMBER( serial_speed_w );
DECLARE_PALETTE_INIT( ie15 );
TIMER_CALLBACK_MEMBER(ie15_beepoff);
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
private:
static const device_timer_id TIMER_HBLANK = 0;
void scanline_callback();
void update_leds();
void draw_scanline(uint32_t *p, uint16_t offset, uint8_t scanline);
std::unique_ptr<uint32_t[]> m_tmpbmp;
emu_timer *m_hblank_timer;
uint8_t m_long_beep;
uint8_t m_kb_control;
uint8_t m_kb_data;
uint8_t m_kb_flag0;
uint8_t m_kb_flag;
uint8_t m_kb_ruslat;
uint8_t m_latch;
struct {
uint8_t cursor;
uint8_t enable;
uint8_t line25;
uint32_t ptr1;
uint32_t ptr2;
} m_video;
uint8_t m_serial_rx_ready;
uint8_t m_serial_tx_ready;
int m_hblank;
int m_vpos;
int m_marker_scanline;
virtual void machine_start() override;
virtual void machine_reset() override;
virtual void video_start() override;
virtual void rcv_complete() override;
virtual void tra_callback() override;
virtual void tra_complete() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
required_device<cpu_device> m_maincpu;
required_region_ptr<u8> m_p_videoram;
required_region_ptr<u8> m_p_chargen;
required_device<beep_device> m_beeper;
required_device<rs232_port_device> m_rs232;
required_device<screen_device> m_screen;
required_ioport m_io_keyboard;
required_device<ie15_device> m_ie15;
};
READ8_MEMBER( ie15_state::mem_r ) {
uint8_t ret;
ret = m_p_videoram[m_video.ptr1];
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 && m_video.ptr1 >= SCREEN_PAGE)
{
DBG_LOG(2,"memory",("R @ %03x == %02x\n", m_video.ptr1, ret));
}
m_video.ptr1++;
m_video.ptr1 &= 0xfff;
m_latch = 0;
return ret;
}
WRITE8_MEMBER( ie15_state::mem_w ) {
if ((m_latch ^= 1) == 0) {
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 && m_video.ptr1 >= SCREEN_PAGE)
{
DBG_LOG(2,"memory",("W @ %03x <- %02x\n", m_video.ptr1, data));
}
m_p_videoram[m_video.ptr1++] = data;
m_video.ptr1 &= 0xfff;
}
}
WRITE8_MEMBER( ie15_state::mem_addr_inc_w ) {
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2,"memory",("++ %03x\n", m_video.ptr1));
}
m_video.ptr1++;
m_video.ptr1 &= 0xfff;
if (m_video.enable)
m_video.ptr2 = m_video.ptr1;
}
WRITE8_MEMBER( ie15_state::mem_addr_dec_w ) {
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2,"memory",("-- %03x\n", m_video.ptr1));
}
m_video.ptr1--;
m_video.ptr1 &= 0xfff;
if (m_video.enable)
m_video.ptr2 = m_video.ptr1;
}
WRITE8_MEMBER( ie15_state::mem_addr_lo_w ) {
uint16_t tmp = m_video.ptr1;
tmp &= 0xff0;
tmp |= ((data >> 4) & 0xf);
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2,"memory",("lo %03x <- %02x = %03x\n", m_video.ptr1, data, tmp));
}
m_video.ptr1 = tmp;
if (m_video.enable)
m_video.ptr2 = tmp;
}
WRITE8_MEMBER( ie15_state::mem_addr_hi_w ) {
uint16_t tmp = m_video.ptr1;
tmp &= 0xf;
tmp |= (data << 4);
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(2,"memory",("hi %03x <- %02x = %03x\n", m_video.ptr1, data, tmp));
}
m_video.ptr1 = tmp;
if (m_video.enable)
m_video.ptr2 = tmp;
}
TIMER_CALLBACK_MEMBER(ie15_state::ie15_beepoff)
{
m_beeper->set_state(0);
}
WRITE8_MEMBER( ie15_state::beep_w ) {
uint16_t length = (m_long_beep & IE_TRUE) ? 150 : 400;
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
DBG_LOG(1,"beep",("(%s)\n", m_long_beep ? "short" : "long"));
}
machine().scheduler().timer_set(attotime::from_msec(length), timer_expired_delegate(FUNC(ie15_state::ie15_beepoff),this));
m_beeper->set_state(1);
}
/* keyboard */
// active high
READ8_MEMBER( ie15_state::kb_r ) {
DBG_LOG(2,"keyboard",("R %02X '%c'\n", m_kb_data, m_kb_data < 0x20 ? ' ' : m_kb_data));
return m_kb_data;
}
// active low
READ8_MEMBER( ie15_state::kb_ready_r ) {
m_kb_flag &= IE_TRUE;
if (m_kb_flag != m_kb_flag0) {
DBG_LOG(2,"keyboard",("? %c\n", m_kb_flag ? 'n' : 'y'));
m_kb_flag0 = m_kb_flag;
}
return m_kb_flag;
}
// active low
WRITE8_MEMBER( ie15_state::kb_ready_w ) {
DBG_LOG(2,"keyboard",("clear ready\n"));
m_kb_flag = IE_TRUE | IE_KB_ACK;
}
// active high; active = interpret controls, inactive = display controls
READ8_MEMBER( ie15_state::kb_s_red_r ) {
return m_io_keyboard->read() & IE_KB_RED ? IE_TRUE : 0;
}
// active high; active = setup mode
READ8_MEMBER( ie15_state::kb_s_sdv_r ) {
return m_kb_control & IE_KB_SDV ? IE_TRUE : 0;
}
// active high; active = keypress detected on aux keypad
READ8_MEMBER( ie15_state::kb_s_dk_r ) {
return m_kb_control & IE_KB_DK ? IE_TRUE : 0;
}
// active low; active = full duplex, inactive = half duplex
READ8_MEMBER( ie15_state::kb_s_dupl_r ) {
return m_io_keyboard->read() & IE_KB_DUP ? IE_TRUE : 0;
}
// active high; active = on-line, inactive = local editing
READ8_MEMBER( ie15_state::kb_s_lin_r ) {
return m_io_keyboard->read() & IE_KB_LIN ? IE_TRUE : 0;
}
/* serial port */
void ie15_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
device_serial_interface::device_timer(timer, id, param, ptr);
switch(id)
{
case TIMER_HBLANK:
if (m_hblank) // Transitioning from in blanking to out of blanking
{
m_hblank = 0;
m_hblank_timer->adjust(m_screen->time_until_pos((m_vpos+1) % IE15_TOTAL_VERT, 0));
scanline_callback();
}
else // Transitioning from out of blanking to in blanking
{
m_hblank = 1;
m_hblank_timer->adjust(m_screen->time_until_pos(m_vpos, IE15_HORZ_START));
}
break;
}
}
WRITE_LINE_MEMBER( ie15_state::serial_rx_callback )
{
device_serial_interface::rx_w(state);
}
void ie15_state::rcv_complete()
{
receive_register_extract();
m_serial_rx_ready = IE_FALSE;
}
void ie15_state::tra_callback()
{
uint8_t bit = transmit_register_get_data_bit();
m_rs232->write_txd(bit);
}
void ie15_state::tra_complete()
{
m_serial_tx_ready = IE_TRUE;
}
// active low
READ8_MEMBER( ie15_state::serial_rx_ready_r ) {
return m_serial_rx_ready;
}
// active high
READ8_MEMBER( ie15_state::serial_tx_ready_r ) {
return m_serial_tx_ready;
}
// not called unless data are ready
READ8_MEMBER( ie15_state::serial_r ) {
uint8_t data;
data = get_received_char();
m_serial_rx_ready = IE_TRUE;
DBG_LOG(1,"serial",("R %02X '%c'\n", data, data < 0x20?' ':data));
return data;
}
WRITE8_MEMBER( ie15_state::serial_w ) {
DBG_LOG(1,"serial",("W %02X '%c'\n", data, data < 0x20?' ':data));
m_serial_tx_ready = IE_FALSE;
transmit_register_setup(data);
}
WRITE8_MEMBER( ie15_state::serial_speed_w ) {
return;
}
READ8_MEMBER( ie15_state::flag_r ) {
switch (offset)
{
case 0: // hsync pulse (not hblank)
return m_hblank;
case 1: // marker scanline
return m_marker_scanline;
case 2: // vblank
return !m_screen->vblank();
case 4:
return m_kb_ruslat;
default:
break;
}
if (machine().debug_flags & DEBUG_FLAG_ENABLED)
{
DBG_LOG(2,"flag",("read %d: ?\n", offset));
}
return 0;
}
WRITE8_MEMBER( ie15_state::flag_w ) {
switch (offset)
{
case 0:
m_video.enable = data;
break;
case 1:
m_video.cursor = data;
break;
case 2:
m_long_beep = data;
break;
case 3:
m_video.line25 = data;
break;
case 4:
m_kb_ruslat = data;
break;
default:
break;
}
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 && !offset)
{
DBG_LOG(2,"flag",("%sset %d\n", data?"":"re", offset));
}
}
static ADDRESS_MAP_START( ie15_mem, AS_PROGRAM, 8, ie15_state )
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x0fff) AM_ROM
ADDRESS_MAP_END
static ADDRESS_MAP_START( ie15_io, AS_IO, 8, ie15_state )
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(000, 000) AM_READWRITE(mem_r, mem_w) // 00h W: memory request, R: memory data [6.1.2.2]
AM_RANGE(001, 001) AM_READ(serial_rx_ready_r) AM_WRITENOP // 01h W: memory latch [6.1.2.2]
AM_RANGE(002, 002) AM_WRITE(mem_addr_hi_w) // 02h W: memory address high [6.1.2.2]
AM_RANGE(003, 003) AM_WRITE(mem_addr_lo_w) // 03h W: memory address low [6.1.2.2]
AM_RANGE(004, 004) AM_WRITE(mem_addr_inc_w) // 04h W: memory address counter + [6.1.2.2]
AM_RANGE(005, 005) AM_WRITE(mem_addr_dec_w) // 05h W: memory address counter - [6.1.2.2]
AM_RANGE(006, 006) AM_READWRITE(serial_r, serial_w) // 06h W: serial port data [6.1.5.4]
// port 7 is handled in cpu core
AM_RANGE(010, 010) AM_READWRITE(serial_tx_ready_r, beep_w) // 08h W: speaker control [6.1.5.4]
AM_RANGE(011, 011) AM_READ(kb_r) // 09h R: keyboard data [6.1.5.2]
AM_RANGE(012, 012) AM_READ(kb_s_red_r) // 0Ah I: keyboard mode "RED" [6.1.5.2]
AM_RANGE(013, 013) AM_READ(kb_ready_r) // 0Bh R: keyboard data ready [6.1.5.2]
AM_RANGE(014, 014) AM_READWRITE(kb_s_sdv_r, serial_speed_w) // 0Ch W: serial port speed [6.1.3.1], R: keyboard mode "SDV" [6.1.5.2]
AM_RANGE(015, 015) AM_READWRITE(kb_s_dk_r, kb_ready_w) // 0Dh I: keyboard mode "DK" [6.1.5.2]
AM_RANGE(016, 016) AM_READ(kb_s_dupl_r) // 0Eh I: keyboard mode "DUPL" [6.1.5.2]
AM_RANGE(017, 017) AM_READ(kb_s_lin_r) // 0Fh I: keyboard mode "LIN" [6.1.5.2]
// simulation of flag registers
AM_RANGE(020, 027) AM_READWRITE(flag_r, flag_w)
ADDRESS_MAP_END
/* Input ports */
static INPUT_PORTS_START( ie15 )
PORT_START("keyboard")
PORT_DIPNAME(IE_KB_RED, IE_KB_RED, "RED (Interpret controls)")
PORT_DIPSETTING(0x00, "Off")
PORT_DIPSETTING(IE_KB_RED, "On")
PORT_DIPNAME(IE_KB_DUP, IE_KB_DUP, "DUP (Full duplex)")
PORT_DIPSETTING(0x00, "Off")
PORT_DIPSETTING(IE_KB_DUP, "On")
PORT_DIPNAME(IE_KB_LIN, IE_KB_LIN, "LIN (Online)")
PORT_DIPSETTING(0x00, "Off")
PORT_DIPSETTING(IE_KB_LIN, "On")
INPUT_PORTS_END
WRITE16_MEMBER( ie15_state::kbd_put )
{
DBG_LOG(2,"keyboard",("W %02X<-%02X '%c' %02X (%c)\n", m_kb_data, data, 'x' /* data < 0x20 ? ' ' : (data & 255) */,
m_kb_flag, m_kb_flag ? 'n' : 'y'));
m_kb_control = (data >> 8) & 255;
// send new key only when firmware has processed previous one
if (m_kb_flag == IE_TRUE) {
m_kb_data = data & 255;
m_kb_flag = 0;
}
}
void ie15_state::machine_start()
{
m_hblank_timer = timer_alloc(TIMER_HBLANK);
m_hblank_timer->adjust(attotime::never);
}
void ie15_state::machine_reset()
{
memset(&m_video, 0, sizeof(m_video));
m_kb_ruslat = m_long_beep = m_kb_control = m_kb_data = m_kb_flag0 = 0;
m_kb_flag = IE_TRUE;
m_hblank = 1;
m_hblank_timer->adjust(m_screen->time_until_pos(0, IE15_HORZ_START));
m_vpos = m_screen->vpos();
m_marker_scanline = (m_vpos % 11) > 7;
m_beeper->set_state(0);
m_serial_tx_ready = m_serial_rx_ready = IE_TRUE;
set_data_frame(1 /* start bits */, 8 /* data bits */, PARITY_NONE, STOP_BITS_1);
// device supports rates from 150 to 9600 baud but null_modem has hardcoded 9600
set_rate(9600);
}
void ie15_state::video_start()
{
m_video.ptr1 = m_video.ptr2 = m_latch = 0;
m_tmpbmp = std::make_unique<uint32_t[]>(IE15_TOTAL_HORZ * IE15_TOTAL_VERT);
}
/*
Usable raster is 800 x 275 pixels (80 x 25 characters). 24 lines are
available to the user and 25th (topmost) line is the status line.
Status line, if enabled, displays current serial port speed, 16 setup
bits, and clock. There is no NVRAM, so setup bits are always 0 after
reset and clock starts counting at 0 XXX.
No character attributes are available, but in 'display controls' mode
control characters stored in memory are shown as blinking chars.
Character cell is 10 x 11; character generator provides 7 x 8 of that.
3 extra horizontal pixels are always blank. Blinking cursor may be
displayed on 3 extra scan lines.
On each scan line, video board draws 80 characters from any location
in video memory; this is used by firmware to provide instant scroll
and cursor, which is a character with code 0x7F stored in XXX.
Video board output is controlled by
- control flag 0 "disable video": 0 == disable
- control flag 1 "cursor": 0 == if this scan line is one of extra 3,
enable video every 5 frames.
- control flag 3 "status line": 0 == current scan line is part of status line
- keyboard mode 'RED' ('display controls'): if character code is
less than 0x20 and RED is set, enable video every 5 frames; if RED is
unset, disable video.
XXX 'dotted look' is caused by strobe at 2x pixel clock -- use HLSL for this.
*/
void ie15_state::draw_scanline(uint32_t *p, uint16_t offset, uint8_t scanline)
{
static const uint32_t palette[2] = { 0xff000000, 0xff00c000 };
uint8_t ra = scanline % 8;
uint32_t ra_high = 0x200 | ra;
bool blink((m_screen->frame_number() % 10) > 4);
bool red(m_io_keyboard->read() & IE_KB_RED);
bool blink_red_line25 = blink && red && m_video.line25;
bool cursor_blank = scanline > 7 && (!m_video.cursor || blink);
if (cursor_blank)
{
for (uint16_t x = 0; x < 80*10; x++)
{
*p++ = palette[0];
}
}
else
{
for (uint16_t x = offset; x < offset + 80; x++)
{
uint16_t chr = m_p_videoram[x] << 3;
uint8_t gfx = m_p_chargen[chr | ra];
if (chr < (0x20<<3))
{
if (blink_red_line25)
gfx = m_p_chargen[chr | ra_high];
else
gfx = 0;
}
*p++ = palette[BIT(gfx, 7)];
*p++ = palette[BIT(gfx, 6)];
*p++ = palette[BIT(gfx, 5)];
*p++ = palette[BIT(gfx, 4)];
*p++ = palette[BIT(gfx, 3)];
*p++ = palette[BIT(gfx, 2)];
*p++ = palette[BIT(gfx, 1)];
*p++ = palette[0];
*p++ = palette[0];
*p++ = palette[0];
}
}
}
void ie15_state::update_leds()
{
uint8_t data = m_io_keyboard->read();
output().set_value("lat_led", m_kb_ruslat ^ 1);
output().set_value("nr_led", BIT(m_kb_control, IE_KB_NR_BIT) ^ 1);
output().set_value("pch_led", BIT(data, IE_KB_PCH_BIT) ^ 1);
output().set_value("dup_led", BIT(data, IE_KB_DUP_BIT) ^ 1);
output().set_value("lin_led", BIT(data, IE_KB_LIN_BIT) ^ 1);
output().set_value("red_led", BIT(data, IE_KB_RED_BIT) ^ 1);
output().set_value("sdv_led", BIT(m_kb_control, IE_KB_SDV_BIT) ^ 1);
output().set_value("prd_led", 1); // XXX
}
/*
VBlank is active for 3 topmost on-screen rows and 1 at the bottom; however, control flag 3 overrides VBlank,
allowing status line to be switched on and off.
*/
void ie15_state::scanline_callback()
{
int y = m_vpos;
m_vpos++;
m_vpos %= IE15_TOTAL_VERT;
m_marker_scanline = (m_vpos % 11) > 7;
DBG_LOG(3,"scanline_cb",
("addr %03x frame %d x %.4d y %.3d row %.2d e:c:s %d:%d:%d\n",
m_video.ptr2, (int)m_screen->frame_number(), m_screen->hpos(), y,
y%11, m_video.enable, m_video.cursor, m_video.line25));
if (y < IE15_VERT_START) return;
y -= IE15_VERT_START;
if (y >= IE15_DISP_VERT) return;
if (!m_video.enable || (y < IE15_STATUSLINE && m_video.line25)) {
memset(&m_tmpbmp[(y + IE15_VERT_START) * IE15_TOTAL_HORZ], 0, sizeof(uint32_t) * IE15_TOTAL_HORZ);
} else {
draw_scanline(&m_tmpbmp[(y + IE15_VERT_START) * IE15_TOTAL_HORZ + IE15_HORZ_START], m_video.ptr2, y % 11);
}
}
uint32_t ie15_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
update_leds();
memcpy(&bitmap.pix32(0), &m_tmpbmp[0], sizeof(uint32_t) * IE15_TOTAL_HORZ * IE15_TOTAL_VERT);
return 0;
}
/* F4 Character Displayer */
static const gfx_layout ie15_charlayout =
{
7, 8, /* 7x8 pixels in 10x11 cell */
256, /* 256 characters */
1, /* 1 bits per pixel */
{ 0 }, /* no bitplanes */
/* x offsets */
{ 0, 1, 2, 3, 4, 5, 6 },
/* y offsets */
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
8*8 /* every char takes 8 bytes */
};
static GFXDECODE_START( ie15 )
GFXDECODE_ENTRY("chargen", 0x0000, ie15_charlayout, 0, 1)
GFXDECODE_END
PALETTE_INIT_MEMBER( ie15_state, ie15 )
{
palette.set_pen_color(0, rgb_t::black()); // black
palette.set_pen_color(1, 0x00, 0xc0, 0x00); // green
}
static MACHINE_CONFIG_START( ie15, ie15_state )
/* Basic machine hardware */
MCFG_CPU_ADD("maincpu", IE15, XTAL_30_8MHz/10)
MCFG_CPU_PROGRAM_MAP(ie15_mem)
MCFG_CPU_IO_MAP(ie15_io)
/* Video hardware */
MCFG_SCREEN_ADD_MONOCHROME("screen", RASTER, rgb_t::green())
MCFG_SCREEN_UPDATE_DRIVER(ie15_state, screen_update)
MCFG_SCREEN_RAW_PARAMS(XTAL_30_8MHz/2, IE15_TOTAL_HORZ, IE15_HORZ_START,
IE15_HORZ_START+IE15_DISP_HORZ, IE15_TOTAL_VERT, IE15_VERT_START,
IE15_VERT_START+IE15_DISP_VERT);
MCFG_DEFAULT_LAYOUT(layout_ie15)
MCFG_GFXDECODE_ADD("gfxdecode", "palette", ie15)
MCFG_PALETTE_ADD_MONOCHROME("palette")
/* Devices */
MCFG_DEVICE_ADD("keyboard", IE15_KEYBOARD, 0)
MCFG_IE15_KEYBOARD_CB(WRITE16(ie15_state, kbd_put))
MCFG_RS232_PORT_ADD("rs232", default_rs232_devices, "null_modem")
MCFG_RS232_RXD_HANDLER(WRITELINE(ie15_state, serial_rx_callback))
MCFG_SPEAKER_STANDARD_MONO("mono")
MCFG_SOUND_ADD("beeper", BEEP, 2400)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.15)
MCFG_DEVICE_ADD("ie15", IE15, 0)
MACHINE_CONFIG_END
/* ROM definition */
ROM_START( ie15 )
ROM_REGION(0x1000, "maincpu", ROMREGION_ERASE00)
ROM_DEFAULT_BIOS("5chip")
ROM_SYSTEM_BIOS(0, "5chip", "5-chip firmware (newer)")
ROMX_LOAD("dump1.bin", 0x0000, 0x1000, CRC(14b82284) SHA1(5ac4159fbb1c3b81445605e26cd97a713ae12b5f), ROM_BIOS(1))
ROM_SYSTEM_BIOS(1, "6chip", "6-chip firmware (older)")
ROMX_LOAD("dump5.bin", 0x0000, 0x1000, CRC(01f2e065) SHA1(2b72dc0594e38a528400cd25aed0c47e0c432895), ROM_BIOS(2))
ROM_REGION(0x1000, "video", ROMREGION_ERASE00)
ROM_REGION(0x0800, "chargen", ROMREGION_ERASE00)
ROM_LOAD("chargen-15ie.bin", 0x0000, 0x0800, CRC(ed16bf6b) SHA1(6af9fb75f5375943d5c0ce9ed408e0fb4621b17e))
ROM_START(ie15)
ROM_END
/* Driver */
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */
COMP( 1980, ie15, 0, 0, ie15, ie15, driver_device, 0, "USSR", "15IE-00-013", 0)
COMP( 1980, ie15, 0, 0, ie15, 0, driver_device, 0, "USSR", "15IE-00-013", 0)