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:
AJR 2017-09-25 19:29:14 -04:00
parent 51dfc76af8
commit 2ad6b11191
3 changed files with 137 additions and 51 deletions

View File

@ -1,8 +1,40 @@
// 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
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
@ -30,8 +62,8 @@ i8212_device::i8212_device(const machine_config &mconfig, const char *tag, devic
m_write_int(*this),
m_read_di(*this),
m_write_do(*this),
m_md(MODE_INPUT),
m_stb(0), m_data(0)
m_read_md(*this),
m_stb(1), m_data(0)
{
}
@ -46,23 +78,37 @@ void i8212_device::device_start()
m_write_int.resolve_safe();
m_read_di.resolve_safe(0);
m_write_do.resolve_safe();
m_read_md.resolve_safe(0);
// register for state saving
save_item(NAME(m_md));
save_item(NAME(m_stb));
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
//-------------------------------------------------
void i8212_device::device_reset()
{
// clear interrupt line
m_write_int(CLEAR_LINE);
// clear latched data
m_data = 0;
if (m_md == MODE_OUTPUT)
if (get_mode() == mode::OUTPUT)
{
// output data
m_write_do((offs_t)0, m_data);
@ -74,13 +120,28 @@ void i8212_device::device_reset()
// 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
m_write_int(CLEAR_LINE);
LOG("I8212 INT: %u\n", CLEAR_LINE);
// read latched data as interrupt vector
return m_data;
}
@ -89,25 +150,37 @@ READ8_MEMBER( i8212_device::read )
// write - data latch write
//-------------------------------------------------
WRITE8_MEMBER( i8212_device::write )
WRITE8_MEMBER(i8212_device::write)
{
// latch data
m_data = data;
// clear interrupt line
m_write_int(CLEAR_LINE);
// output data
m_write_do((offs_t)0, m_data);
if (get_mode() == mode::OUTPUT)
{
// latch data
m_data = data;
LOG("I8212: Writing %02X into latch (output mode)\n", data);
// output 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
//-------------------------------------------------
WRITE_LINE_MEMBER( i8212_device::stb_w )
WRITE_LINE_MEMBER(i8212_device::stb_w)
{
LOG("I8212 STB: %u\n", state);
if (m_md == MODE_INPUT)
// active on falling edge
if (m_stb && !state)
{
if (m_stb && !state)
if (get_mode() == mode::INPUT)
{
// input data
m_data = m_read_di(0);
// assert interrupt line
m_write_int(ASSERT_LINE);
LOG("I8212 INT: %u\n", ASSERT_LINE);
LOG("I8212: Reading %02X into latch (input mode)\n", m_data);
}
// assert interrupt line
m_write_int(ASSERT_LINE);
}
m_stb = state;

View File

@ -1,8 +1,8 @@
// 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
DI2 5 | | 20 DI7
DO2 6 | 8212 | 19 DO7
DI3 7 | | 18 DI6
DI3 7 | 3212 | 18 DI6
DO3 8 | | 17 DO6
DI4 9 | | 16 DI5
DO4 10 | | 15 DO5
@ -42,6 +42,9 @@
#define MCFG_I8212_DO_CALLBACK(_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
{
enum class mode : u8
{
INPUT,
OUTPUT
};
public:
// construction/destruction
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_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_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 );
DECLARE_WRITE8_MEMBER( write );
// data read handlers
DECLARE_READ8_MEMBER(read);
IRQ_CALLBACK_MEMBER(inta_cb);
DECLARE_WRITE_LINE_MEMBER( md_w );
DECLARE_WRITE_LINE_MEMBER( stb_w );
// data write handlers
DECLARE_WRITE8_MEMBER(write);
DECLARE_WRITE8_MEMBER(strobe);
// line write handlers
DECLARE_WRITE_LINE_MEMBER(md_w);
DECLARE_WRITE_LINE_MEMBER(stb_w);
protected:
// device-level overrides
@ -72,19 +88,16 @@ protected:
virtual void device_reset() override;
private:
enum
{
MODE_INPUT = 0,
MODE_OUTPUT
};
// helpers
mode get_mode();
devcb_write_line m_write_int;
devcb_read8 m_read_di;
devcb_write8 m_write_do;
devcb_read_line m_read_md;
int m_md; // mode
int m_stb; // strobe
uint8_t m_data; // data latch
uint8_t m_data; // data latch
};

View File

@ -11,7 +11,7 @@
#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/gen_latch.h"
#include "machine/i8212.h"
#include "sound/ay8910.h"
#include "speaker.h"
@ -45,7 +45,7 @@ private:
required_device<cpu_device> m_maincpu;
required_device<cpu_device> m_soundcpu;
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;
bool m_pcs[2];
@ -54,7 +54,7 @@ private:
static ADDRESS_MAP_START(main_map, AS_PROGRAM, 8, supstarf_state)
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
ADDRESS_MAP_END
@ -102,7 +102,7 @@ WRITE8_MEMBER(supstarf_state::psg_latch_w)
}
if (m_latch_select)
m_soundlatch[0]->write(space, 0, data);
m_soundlatch[0]->strobe(space, 0, data);
}
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_T1_IN_CB(READLINE(supstarf_state, phase_detect_r))
MCFG_GENERIC_LATCH_8_ADD("soundlatch1")
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("maincpu", I8085_RST55_LINE))
MCFG_DEVICE_ADD("soundlatch1", I8212, 0)
MCFG_I8212_MD_CALLBACK(GND)
MCFG_I8212_INT_CALLBACK(INPUTLINE("maincpu", I8085_RST55_LINE))
MCFG_GENERIC_LATCH_8_ADD("soundlatch2")
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("soundcpu", MCS48_INPUT_IRQ))
MCFG_DEVICE_ADD("soundlatch2", I8212, 0)
MCFG_I8212_MD_CALLBACK(GND)
MCFG_I8212_INT_CALLBACK(INPUTLINE("soundcpu", MCS48_INPUT_IRQ))
//MCFG_DEVCB_CHAIN_OUTPUT(INPUTLINE("maincpu", I8085_READY_LINE))
MCFG_SPEAKER_STANDARD_MONO("mono")