mirror of
https://github.com/holub/mame
synced 2025-07-04 17:38:08 +03:00
i8212: Device overhaul (nw)
- Change the mode setting from a line write to an input callback. This is based on the observation that MD is nearly always tied to either Vcc or GND on actual hardware. - Make the mode a scoped enumeration. - Add strobed write handler for input mode. This allows the device to be hooked up in supstarf in place of generic_latch_8_device. - Add interrupt acknowledge callback for future use. - Add extensive introductory comments.
This commit is contained in:
parent
51dfc76af8
commit
2ad6b11191
@ -1,8 +1,40 @@
|
|||||||
// license:BSD-3-Clause
|
// license:BSD-3-Clause
|
||||||
// copyright-holders:Curt Coder
|
// copyright-holders:Curt Coder,AJR
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Intel 8212 8-Bit Input/Output Port emulation
|
Intel 8212/3212 8-Bit Input/Output Port (Multi-Mode Latch Buffer)
|
||||||
|
|
||||||
|
The Intel 8212 was one of the first in a line of bipolar Schottky
|
||||||
|
peripherals released early on for the 8080. Many of these were
|
||||||
|
assigned alternate part numbers in the 3200 series to identify
|
||||||
|
them with Intel's 3001/3002 bipolar bit-slice processing elements.
|
||||||
|
|
||||||
|
The 8212's MD pin is typically tied to either GND or Vcc to fix
|
||||||
|
the chip in one of its two operating modes. In the input mode
|
||||||
|
(MD = L), data is latched on the falling edge of STB, and the
|
||||||
|
three-state outputs are enabled by a combination of two chip
|
||||||
|
select inputs of opposite polarities. In the output mode (MD = H),
|
||||||
|
data is latched on the falling edge of chip selection, and outputs
|
||||||
|
are always enabled. The service request flip-flop is clocked on
|
||||||
|
the falling edge of STB to produce the INT output, and is reset by
|
||||||
|
either chip selection or the active-low CLR input, the latter
|
||||||
|
also resetting the latched data to zero.
|
||||||
|
|
||||||
|
The 8212 in output mode was often used with the 8080 to latch the
|
||||||
|
status word and with the 8085 to latch the lower address bits.
|
||||||
|
|
||||||
|
RCA's CDP1852 is an almost pin-for-pin CMOS counterpart to the
|
||||||
|
8212. The control lines of the CDP1852, however, work slightly
|
||||||
|
differently, especially in output mode.
|
||||||
|
|
||||||
|
When TI second-sourced the 8080A, they cloned the 8212 as the
|
||||||
|
SN74S412 (and numbered their versions of the 8224, 8228 and 8338
|
||||||
|
similarly). While simpler octal latches from the 7400 series such
|
||||||
|
as 74LS273, 74LS373 and 74LS374 became far more common and widely
|
||||||
|
used, even when a separate service request flip-flop needed to be
|
||||||
|
coupled, the Fairchild Advanced Schottky TTL (FAST) evolution of
|
||||||
|
the 7400 series had both inverting (74F432) and non-inverting
|
||||||
|
(74F412) versions of this device.
|
||||||
|
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
@ -19,7 +51,7 @@
|
|||||||
//**************************************************************************
|
//**************************************************************************
|
||||||
|
|
||||||
// device type definition
|
// device type definition
|
||||||
DEFINE_DEVICE_TYPE(I8212, i8212_device, "i8212", "Intel 8212 I/O")
|
DEFINE_DEVICE_TYPE(I8212, i8212_device, "i8212", "Intel 8212 I/O Port")
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// i8212_device - constructor
|
// i8212_device - constructor
|
||||||
@ -30,8 +62,8 @@ i8212_device::i8212_device(const machine_config &mconfig, const char *tag, devic
|
|||||||
m_write_int(*this),
|
m_write_int(*this),
|
||||||
m_read_di(*this),
|
m_read_di(*this),
|
||||||
m_write_do(*this),
|
m_write_do(*this),
|
||||||
m_md(MODE_INPUT),
|
m_read_md(*this),
|
||||||
m_stb(0), m_data(0)
|
m_stb(1), m_data(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,23 +78,37 @@ void i8212_device::device_start()
|
|||||||
m_write_int.resolve_safe();
|
m_write_int.resolve_safe();
|
||||||
m_read_di.resolve_safe(0);
|
m_read_di.resolve_safe(0);
|
||||||
m_write_do.resolve_safe();
|
m_write_do.resolve_safe();
|
||||||
|
m_read_md.resolve_safe(0);
|
||||||
|
|
||||||
// register for state saving
|
// register for state saving
|
||||||
save_item(NAME(m_md));
|
|
||||||
save_item(NAME(m_stb));
|
save_item(NAME(m_stb));
|
||||||
save_item(NAME(m_data));
|
save_item(NAME(m_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------
|
||||||
|
// get_mode - resolve device mode
|
||||||
|
//-------------------------------------------------
|
||||||
|
|
||||||
|
i8212_device::mode i8212_device::get_mode()
|
||||||
|
{
|
||||||
|
return m_read_md() ? mode::OUTPUT : mode::INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// device_reset - device-specific reset
|
// device_reset - device-specific reset
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
void i8212_device::device_reset()
|
void i8212_device::device_reset()
|
||||||
{
|
{
|
||||||
|
// clear interrupt line
|
||||||
|
m_write_int(CLEAR_LINE);
|
||||||
|
|
||||||
|
// clear latched data
|
||||||
m_data = 0;
|
m_data = 0;
|
||||||
|
|
||||||
if (m_md == MODE_OUTPUT)
|
if (get_mode() == mode::OUTPUT)
|
||||||
{
|
{
|
||||||
// output data
|
// output data
|
||||||
m_write_do((offs_t)0, m_data);
|
m_write_do((offs_t)0, m_data);
|
||||||
@ -74,13 +120,28 @@ void i8212_device::device_reset()
|
|||||||
// read - data latch read
|
// read - data latch read
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
READ8_MEMBER( i8212_device::read )
|
READ8_MEMBER(i8212_device::read)
|
||||||
|
{
|
||||||
|
if (!machine().side_effect_disabled())
|
||||||
|
{
|
||||||
|
// clear interrupt line
|
||||||
|
m_write_int(CLEAR_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------
|
||||||
|
// inta_cb - data latch read (INTA triggered)
|
||||||
|
//-------------------------------------------------
|
||||||
|
|
||||||
|
IRQ_CALLBACK_MEMBER(i8212_device::inta_cb)
|
||||||
{
|
{
|
||||||
// clear interrupt line
|
// clear interrupt line
|
||||||
m_write_int(CLEAR_LINE);
|
m_write_int(CLEAR_LINE);
|
||||||
|
|
||||||
LOG("I8212 INT: %u\n", CLEAR_LINE);
|
// read latched data as interrupt vector
|
||||||
|
|
||||||
return m_data;
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,25 +150,37 @@ READ8_MEMBER( i8212_device::read )
|
|||||||
// write - data latch write
|
// write - data latch write
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
WRITE8_MEMBER( i8212_device::write )
|
WRITE8_MEMBER(i8212_device::write)
|
||||||
{
|
{
|
||||||
|
// clear interrupt line
|
||||||
|
m_write_int(CLEAR_LINE);
|
||||||
|
|
||||||
|
if (get_mode() == mode::OUTPUT)
|
||||||
|
{
|
||||||
// latch data
|
// latch data
|
||||||
m_data = data;
|
m_data = data;
|
||||||
|
LOG("I8212: Writing %02X into latch (output mode)\n", data);
|
||||||
|
|
||||||
// output data
|
// output data
|
||||||
m_write_do((offs_t)0, m_data);
|
m_write_do((offs_t)0, m_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// md_w - mode write
|
// strobe - data input strobe
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
WRITE_LINE_MEMBER( i8212_device::md_w )
|
WRITE8_MEMBER(i8212_device::strobe)
|
||||||
{
|
{
|
||||||
LOG("I8212 Mode: %s\n", state ? "output" : "input");
|
if (get_mode() == mode::INPUT)
|
||||||
|
{
|
||||||
|
m_data = data;
|
||||||
|
LOG("I8212: Writing %02X into latch (input mode)\n", data);
|
||||||
|
}
|
||||||
|
|
||||||
m_md = state;
|
// assert interrupt line
|
||||||
|
m_write_int(ASSERT_LINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,22 +188,20 @@ WRITE_LINE_MEMBER( i8212_device::md_w )
|
|||||||
// stb_w - data strobe write
|
// stb_w - data strobe write
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
WRITE_LINE_MEMBER( i8212_device::stb_w )
|
WRITE_LINE_MEMBER(i8212_device::stb_w)
|
||||||
{
|
{
|
||||||
LOG("I8212 STB: %u\n", state);
|
// active on falling edge
|
||||||
|
|
||||||
if (m_md == MODE_INPUT)
|
|
||||||
{
|
|
||||||
if (m_stb && !state)
|
if (m_stb && !state)
|
||||||
|
{
|
||||||
|
if (get_mode() == mode::INPUT)
|
||||||
{
|
{
|
||||||
// input data
|
// input data
|
||||||
m_data = m_read_di(0);
|
m_data = m_read_di(0);
|
||||||
|
LOG("I8212: Reading %02X into latch (input mode)\n", m_data);
|
||||||
|
}
|
||||||
|
|
||||||
// assert interrupt line
|
// assert interrupt line
|
||||||
m_write_int(ASSERT_LINE);
|
m_write_int(ASSERT_LINE);
|
||||||
|
|
||||||
LOG("I8212 INT: %u\n", ASSERT_LINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stb = state;
|
m_stb = state;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// license:BSD-3-Clause
|
// license:BSD-3-Clause
|
||||||
// copyright-holders:Curt Coder
|
// copyright-holders:Curt Coder,AJR
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Intel 8212 8-Bit Input/Output Port emulation
|
Intel 8212/3212 8-Bit Input/Output Port (Multi-Mode Latch Buffer)
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
_____ _____
|
_____ _____
|
||||||
@ -12,7 +12,7 @@
|
|||||||
DO1 4 | | 21 DO8
|
DO1 4 | | 21 DO8
|
||||||
DI2 5 | | 20 DI7
|
DI2 5 | | 20 DI7
|
||||||
DO2 6 | 8212 | 19 DO7
|
DO2 6 | 8212 | 19 DO7
|
||||||
DI3 7 | | 18 DI6
|
DI3 7 | 3212 | 18 DI6
|
||||||
DO3 8 | | 17 DO6
|
DO3 8 | | 17 DO6
|
||||||
DI4 9 | | 16 DI5
|
DI4 9 | | 16 DI5
|
||||||
DO4 10 | | 15 DO5
|
DO4 10 | | 15 DO5
|
||||||
@ -42,6 +42,9 @@
|
|||||||
#define MCFG_I8212_DO_CALLBACK(_write) \
|
#define MCFG_I8212_DO_CALLBACK(_write) \
|
||||||
devcb = &i8212_device::set_do_wr_callback(*device, DEVCB_##_write);
|
devcb = &i8212_device::set_do_wr_callback(*device, DEVCB_##_write);
|
||||||
|
|
||||||
|
#define MCFG_I8212_MD_CALLBACK(_read) \
|
||||||
|
devcb = &i8212_device::set_md_rd_callback(*device, DEVCB_##_read);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///*************************************************************************
|
///*************************************************************************
|
||||||
@ -52,6 +55,12 @@
|
|||||||
|
|
||||||
class i8212_device : public device_t
|
class i8212_device : public device_t
|
||||||
{
|
{
|
||||||
|
enum class mode : u8
|
||||||
|
{
|
||||||
|
INPUT,
|
||||||
|
OUTPUT
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// construction/destruction
|
// construction/destruction
|
||||||
i8212_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
i8212_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||||
@ -59,12 +68,19 @@ public:
|
|||||||
template <class Object> static devcb_base &set_int_wr_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_write_int.set_callback(std::forward<Object>(cb)); }
|
template <class Object> static devcb_base &set_int_wr_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_write_int.set_callback(std::forward<Object>(cb)); }
|
||||||
template <class Object> static devcb_base &set_di_rd_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_read_di.set_callback(std::forward<Object>(cb)); }
|
template <class Object> static devcb_base &set_di_rd_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_read_di.set_callback(std::forward<Object>(cb)); }
|
||||||
template <class Object> static devcb_base &set_do_wr_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_write_do.set_callback(std::forward<Object>(cb)); }
|
template <class Object> static devcb_base &set_do_wr_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_write_do.set_callback(std::forward<Object>(cb)); }
|
||||||
|
template <class Object> static devcb_base &set_md_rd_callback(device_t &device, Object &&cb) { return downcast<i8212_device &>(device).m_read_md.set_callback(std::forward<Object>(cb)); }
|
||||||
|
|
||||||
DECLARE_READ8_MEMBER( read );
|
// data read handlers
|
||||||
DECLARE_WRITE8_MEMBER( write );
|
DECLARE_READ8_MEMBER(read);
|
||||||
|
IRQ_CALLBACK_MEMBER(inta_cb);
|
||||||
|
|
||||||
DECLARE_WRITE_LINE_MEMBER( md_w );
|
// data write handlers
|
||||||
DECLARE_WRITE_LINE_MEMBER( stb_w );
|
DECLARE_WRITE8_MEMBER(write);
|
||||||
|
DECLARE_WRITE8_MEMBER(strobe);
|
||||||
|
|
||||||
|
// line write handlers
|
||||||
|
DECLARE_WRITE_LINE_MEMBER(md_w);
|
||||||
|
DECLARE_WRITE_LINE_MEMBER(stb_w);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// device-level overrides
|
// device-level overrides
|
||||||
@ -72,17 +88,14 @@ protected:
|
|||||||
virtual void device_reset() override;
|
virtual void device_reset() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum
|
// helpers
|
||||||
{
|
mode get_mode();
|
||||||
MODE_INPUT = 0,
|
|
||||||
MODE_OUTPUT
|
|
||||||
};
|
|
||||||
|
|
||||||
devcb_write_line m_write_int;
|
devcb_write_line m_write_int;
|
||||||
devcb_read8 m_read_di;
|
devcb_read8 m_read_di;
|
||||||
devcb_write8 m_write_do;
|
devcb_write8 m_write_do;
|
||||||
|
devcb_read_line m_read_md;
|
||||||
|
|
||||||
int m_md; // mode
|
|
||||||
int m_stb; // strobe
|
int m_stb; // strobe
|
||||||
uint8_t m_data; // data latch
|
uint8_t m_data; // data latch
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
#include "cpu/i8085/i8085.h"
|
#include "cpu/i8085/i8085.h"
|
||||||
#include "cpu/mcs48/mcs48.h"
|
#include "cpu/mcs48/mcs48.h"
|
||||||
#include "machine/gen_latch.h"
|
#include "machine/i8212.h"
|
||||||
#include "sound/ay8910.h"
|
#include "sound/ay8910.h"
|
||||||
#include "speaker.h"
|
#include "speaker.h"
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ private:
|
|||||||
required_device<cpu_device> m_maincpu;
|
required_device<cpu_device> m_maincpu;
|
||||||
required_device<cpu_device> m_soundcpu;
|
required_device<cpu_device> m_soundcpu;
|
||||||
required_device_array<ay8910_device, 2> m_psg;
|
required_device_array<ay8910_device, 2> m_psg;
|
||||||
required_device_array<generic_latch_8_device, 2> m_soundlatch;
|
required_device_array<i8212_device, 2> m_soundlatch;
|
||||||
|
|
||||||
u8 m_port1_data;
|
u8 m_port1_data;
|
||||||
bool m_pcs[2];
|
bool m_pcs[2];
|
||||||
@ -54,7 +54,7 @@ private:
|
|||||||
|
|
||||||
static ADDRESS_MAP_START(main_map, AS_PROGRAM, 8, supstarf_state)
|
static ADDRESS_MAP_START(main_map, AS_PROGRAM, 8, supstarf_state)
|
||||||
AM_RANGE(0x0000, 0x3fff) AM_ROM
|
AM_RANGE(0x0000, 0x3fff) AM_ROM
|
||||||
AM_RANGE(0x8000, 0x8000) AM_DEVREAD("soundlatch1", generic_latch_8_device, read) AM_DEVWRITE("soundlatch2", generic_latch_8_device, write)
|
AM_RANGE(0x8000, 0x8000) AM_DEVREAD("soundlatch1", i8212_device, read) AM_DEVWRITE("soundlatch2", i8212_device, strobe)
|
||||||
AM_RANGE(0xc000, 0xc7ff) AM_RAM // 5517 (2Kx8) at IC11
|
AM_RANGE(0xc000, 0xc7ff) AM_RAM // 5517 (2Kx8) at IC11
|
||||||
ADDRESS_MAP_END
|
ADDRESS_MAP_END
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ WRITE8_MEMBER(supstarf_state::psg_latch_w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_latch_select)
|
if (m_latch_select)
|
||||||
m_soundlatch[0]->write(space, 0, data);
|
m_soundlatch[0]->strobe(space, 0, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE8_MEMBER(supstarf_state::port1_w)
|
WRITE8_MEMBER(supstarf_state::port1_w)
|
||||||
@ -177,11 +177,13 @@ static MACHINE_CONFIG_START(supstarf)
|
|||||||
MCFG_MCS48_PORT_P2_OUT_CB(WRITE8(supstarf_state, port2_w))
|
MCFG_MCS48_PORT_P2_OUT_CB(WRITE8(supstarf_state, port2_w))
|
||||||
MCFG_MCS48_PORT_T1_IN_CB(READLINE(supstarf_state, phase_detect_r))
|
MCFG_MCS48_PORT_T1_IN_CB(READLINE(supstarf_state, phase_detect_r))
|
||||||
|
|
||||||
MCFG_GENERIC_LATCH_8_ADD("soundlatch1")
|
MCFG_DEVICE_ADD("soundlatch1", I8212, 0)
|
||||||
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("maincpu", I8085_RST55_LINE))
|
MCFG_I8212_MD_CALLBACK(GND)
|
||||||
|
MCFG_I8212_INT_CALLBACK(INPUTLINE("maincpu", I8085_RST55_LINE))
|
||||||
|
|
||||||
MCFG_GENERIC_LATCH_8_ADD("soundlatch2")
|
MCFG_DEVICE_ADD("soundlatch2", I8212, 0)
|
||||||
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("soundcpu", MCS48_INPUT_IRQ))
|
MCFG_I8212_MD_CALLBACK(GND)
|
||||||
|
MCFG_I8212_INT_CALLBACK(INPUTLINE("soundcpu", MCS48_INPUT_IRQ))
|
||||||
//MCFG_DEVCB_CHAIN_OUTPUT(INPUTLINE("maincpu", I8085_READY_LINE))
|
//MCFG_DEVCB_CHAIN_OUTPUT(INPUTLINE("maincpu", I8085_READY_LINE))
|
||||||
|
|
||||||
MCFG_SPEAKER_STANDARD_MONO("mono")
|
MCFG_SPEAKER_STANDARD_MONO("mono")
|
||||||
|
Loading…
Reference in New Issue
Block a user