summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/vs9209.cpp
blob: e6bd7288be4a1447a5cf0f3aa55ca964d2e14d48 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// license:BSD-3-Clause
// copyright-holders:AJR
/**********************************************************************

    VS9209 (4L01F1429) custom QFP80 I/O

    This chip, which appears on various Video System PCBs from 1992
    to 1995, provides a programmable interface for up to eight ports.

    There are at least four configuration registers that control the
    directions of individual bits on some of the ports (low for input
    and high for output). However, the game programs always write
    zero to the first register, except for Super Slams which doesn't
    write to it at all.

    No program attempts to write to Ports A, B, C or D, with the
    dubious exception of Tao Taido writing to the Port C offset on
    its second VS9209 (whose inputs are mostly unused) at
    initialization time. It seems possible that only the latter four
    ports may be configured for output.

    Much like CXD1095, the last port is apparently only half width.

**********************************************************************/

#include "emu.h"
#include "machine/vs9209.h"

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

DEFINE_DEVICE_TYPE(VS9209, vs9209_device, "vs9209", "VS9209 I/O")

//**************************************************************************
//  DEVICE DEFINITION
//**************************************************************************

//-------------------------------------------------
//  vs9209_device - constructor
//-------------------------------------------------

vs9209_device::vs9209_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, VS9209, tag, owner, clock)
	, m_input_cb(*this)
	, m_output_cb(*this)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void vs9209_device::device_start()
{
	// resolve callbacks
	m_input_cb.resolve_all();
	m_output_cb.resolve_all();

	std::fill(std::begin(m_data_latch), std::end(m_data_latch), 0);

	// save state
	save_item(NAME(m_data_latch));
	save_item(NAME(m_data_dir));
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void vs9209_device::device_reset()
{
	std::fill(std::begin(m_data_dir), std::end(m_data_dir), 0);
}

//-------------------------------------------------
//  read - read from an input port
//-------------------------------------------------

u8 vs9209_device::read(address_space &space, offs_t offset)
{
	int port = offset & 7;

	if ((offset & 8) == 0)
	{
		u8 input_data = 0;
		u8 input_mask = ~m_data_dir[port];
		if (port == 7)
			input_mask &= 0x0f;

		// read through callback if port not configured entirely for output
		if (input_mask != 0 && !m_input_cb[port].isnull())
			input_data = m_input_cb[port](0, input_mask) & input_mask;
		else if (m_data_dir[port] == 0)
			logerror("%s: Read from undefined input port %c\n", machine().describe_context(), 'A' + port);

		// combine live inputs with latched data
		return input_data | (m_data_latch[port] & m_data_dir[port]);
	}

	//logerror("%s: Read from write-only/nonexistent register %d\n", machine().describe_context(), offset);
	return space.unmap();
}

//-------------------------------------------------
//  write - write to an output port or one of two
//  control registers
//-------------------------------------------------

void vs9209_device::write(offs_t offset, u8 data)
{
	// port H is probably only 4 bits wide
	int port = offset & 7;
	if (port == 7 && (data & 0xf0) != 0)
	{
		logerror("%s: Attempt to write %02X to port H%s", machine().describe_context(), data, (offset & 8) != 0 ? " direction register" : "");
		data &= 0x0f;
	}

	if ((offset & 8) == 0)
	{
		// update our latched data
		m_data_latch[port] = data;

		if (m_data_dir[port] != 0)
		{
			u8 dataout = data & m_data_dir[port];

			// send output through callback
			if (!m_output_cb[port].isnull())
				m_output_cb[port](0, dataout, m_data_dir[port]);
			else
				logerror("%s: Writing %02X to undefined output port %c\n", machine().describe_context(), dataout, 'A' + port);
		}
		else if (m_output_cb[port].isnull())
			logerror("%s: Writing %02X to input-only port %c\n", machine().describe_context(), data, 'A' + port);
	}
	else
	{
		u8 old_data_dir = m_data_dir[port];
		m_data_dir[port] = data;

		u8 all_port_bits = (port == 7) ? 0x0f : 0xff;
		if (data != all_port_bits)
			logerror("Port %c & %02X configured for input\n", 'A' + port, all_port_bits ^ data);
		if (data != 0)
		{
			logerror("Port %c & %02X configured for output\n", 'A' + port, data);

			// if direction changed to output, begin output from latch
			if ((data & ~old_data_dir) != 0 && !m_output_cb[port].isnull())
				m_output_cb[port](0, m_data_latch[port], data);
		}
	}
}