1 // license:BSD-3-Clause
2 // copyright-holders:AJR
3 /**********************************************************************
4 
5     VS9209 (4L01F1429) custom QFP80 I/O
6 
7     This chip, which appears on various Video System PCBs from 1992
8     to 1995, provides a programmable interface for up to eight ports.
9 
10     There are at least four configuration registers that control the
11     directions of individual bits on some of the ports (low for input
12     and high for output). However, the game programs always write
13     zero to the first register, except for Super Slams which doesn't
14     write to it at all.
15 
16     No program attempts to write to Ports A, B, C or D, with the
17     dubious exception of Tao Taido writing to the Port C offset on
18     its second VS9209 (whose inputs are mostly unused) at
19     initialization time. It seems possible that only the latter four
20     ports may be configured for output.
21 
22     Much like CXD1095, the last port is apparently only half width.
23 
24 **********************************************************************/
25 
26 #include "emu.h"
27 #include "machine/vs9209.h"
28 
29 //**************************************************************************
30 //  GLOBAL VARIABLES
31 //**************************************************************************
32 
33 DEFINE_DEVICE_TYPE(VS9209, vs9209_device, "vs9209", "VS9209 I/O")
34 
35 //**************************************************************************
36 //  DEVICE DEFINITION
37 //**************************************************************************
38 
39 //-------------------------------------------------
40 //  vs9209_device - constructor
41 //-------------------------------------------------
42 
vs9209_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)43 vs9209_device::vs9209_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
44 	: device_t(mconfig, VS9209, tag, owner, clock)
45 	, m_input_cb(*this)
46 	, m_output_cb(*this)
47 {
48 }
49 
50 //-------------------------------------------------
51 //  device_start - device-specific startup
52 //-------------------------------------------------
53 
device_start()54 void vs9209_device::device_start()
55 {
56 	// resolve callbacks
57 	m_input_cb.resolve_all();
58 	m_output_cb.resolve_all();
59 
60 	std::fill(std::begin(m_data_latch), std::end(m_data_latch), 0);
61 
62 	// save state
63 	save_item(NAME(m_data_latch));
64 	save_item(NAME(m_data_dir));
65 }
66 
67 //-------------------------------------------------
68 //  device_reset - device-specific reset
69 //-------------------------------------------------
70 
device_reset()71 void vs9209_device::device_reset()
72 {
73 	std::fill(std::begin(m_data_dir), std::end(m_data_dir), 0);
74 }
75 
76 //-------------------------------------------------
77 //  read - read from an input port
78 //-------------------------------------------------
79 
read(address_space & space,offs_t offset)80 u8 vs9209_device::read(address_space &space, offs_t offset)
81 {
82 	int port = offset & 7;
83 
84 	if ((offset & 8) == 0)
85 	{
86 		u8 input_data = 0;
87 		u8 input_mask = ~m_data_dir[port];
88 		if (port == 7)
89 			input_mask &= 0x0f;
90 
91 		// read through callback if port not configured entirely for output
92 		if (input_mask != 0 && !m_input_cb[port].isnull())
93 			input_data = m_input_cb[port](0, input_mask) & input_mask;
94 		else if (m_data_dir[port] == 0)
95 			logerror("%s: Read from undefined input port %c\n", machine().describe_context(), 'A' + port);
96 
97 		// combine live inputs with latched data
98 		return input_data | (m_data_latch[port] & m_data_dir[port]);
99 	}
100 
101 	//logerror("%s: Read from write-only/nonexistent register %d\n", machine().describe_context(), offset);
102 	return space.unmap();
103 }
104 
105 //-------------------------------------------------
106 //  write - write to an output port or one of two
107 //  control registers
108 //-------------------------------------------------
109 
write(offs_t offset,u8 data)110 void vs9209_device::write(offs_t offset, u8 data)
111 {
112 	// port H is probably only 4 bits wide
113 	int port = offset & 7;
114 	if (port == 7 && (data & 0xf0) != 0)
115 	{
116 		logerror("%s: Attempt to write %02X to port H%s", machine().describe_context(), data, (offset & 8) != 0 ? " direction register" : "");
117 		data &= 0x0f;
118 	}
119 
120 	if ((offset & 8) == 0)
121 	{
122 		// update our latched data
123 		m_data_latch[port] = data;
124 
125 		if (m_data_dir[port] != 0)
126 		{
127 			u8 dataout = data & m_data_dir[port];
128 
129 			// send output through callback
130 			if (!m_output_cb[port].isnull())
131 				m_output_cb[port](0, dataout, m_data_dir[port]);
132 			else
133 				logerror("%s: Writing %02X to undefined output port %c\n", machine().describe_context(), dataout, 'A' + port);
134 		}
135 		else if (m_output_cb[port].isnull())
136 			logerror("%s: Writing %02X to input-only port %c\n", machine().describe_context(), data, 'A' + port);
137 	}
138 	else
139 	{
140 		u8 old_data_dir = m_data_dir[port];
141 		m_data_dir[port] = data;
142 
143 		u8 all_port_bits = (port == 7) ? 0x0f : 0xff;
144 		if (data != all_port_bits)
145 			logerror("Port %c & %02X configured for input\n", 'A' + port, all_port_bits ^ data);
146 		if (data != 0)
147 		{
148 			logerror("Port %c & %02X configured for output\n", 'A' + port, data);
149 
150 			// if direction changed to output, begin output from latch
151 			if ((data & ~old_data_dir) != 0 && !m_output_cb[port].isnull())
152 				m_output_cb[port](0, m_data_latch[port], data);
153 		}
154 	}
155 }
156