summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/a2bus/suprterminal.cpp
blob: 796937bebcb02db08b7da5fbe5a5558cc8e71599 (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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*********************************************************************

    suprterminal.cpp
    M&R Enterprises SUP'R'TERMINAL 80-column card.

    This card only works in Slot 3.  The firmware will crash in any other slot.
    Emulation tested and works with DOS 3.3, ProDOS, and CP/M.

    $C0B2: Select VRAM at $C800 and disable ROM at $C800
    $C0B4: Select font RAM at $C800 and disable ROM at $C800
    $C0B6: Select bank 2 of (VRAM? font RAM?).  This is unused on this card,
           and there are not enough DRAMs (or places for them!) to make it work.
    $C0B8: 6845 address
    $C0B9: 6845 data

    The $C800 handling on this card is unusual.
    Disabling the card's $C800 space by accessing $CFFF resets the card to ROM
    at $C800.  Enabling either VRAM or font RAM at $C800 disables ROM until
    $CFFF is accessed.

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

#include "emu.h"
#include "suprterminal.h"

#include "video/mc6845.h"

#include "screen.h"


namespace {

ROM_START(a2suprterm)
	ROM_REGION(0x800, "s3firmware", ROMREGION_ERASEFF)
	ROM_LOAD("suprterminal.bin", 0x000000, 0x000800, CRC(4c49b0f5) SHA1(66a7411c46c04a8089a7ddfb5ffd9809dd08a21f))
ROM_END

//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

class a2bus_suprterminal_device:
	public device_t,
	public device_a2bus_card_interface
{
public:
	// construction/destruction
	a2bus_suprterminal_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);

protected:
	a2bus_suprterminal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);

	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_add_mconfig(machine_config &config) override;
	virtual const tiny_rom_entry *device_rom_region() const override;

	// overrides of standard a2bus slot functions
	virtual u8 read_cnxx(u8 offset) override;
	virtual u8 read_c0nx(u8 offset) override;
	virtual void write_c0nx(u8 offset, u8 data) override;
	virtual u8 read_c800(u16 offset) override;
	virtual void write_c800(u16 offset, u8 data) override;
	virtual bool take_c800() override { return true; }

private:
	required_device<mc6845_device> m_crtc;
	required_region_ptr<u8> m_rom;

	MC6845_UPDATE_ROW(crtc_update_row);

	std::unique_ptr<u8[]> m_vram;
	std::unique_ptr<u8[]> m_fontram;
//  u8 m_fontram[0x400];
	bool m_bRasterRAM, m_bCharBank1, m_bC800IsRAM;
};

//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

void a2bus_suprterminal_device::device_add_mconfig(machine_config &config)
{
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_raw(17.43_MHz_XTAL, 1116, 0, 720, 260, 0, 216);
	screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));

	HD6845S(config, m_crtc, 17.43_MHz_XTAL / 9);
	m_crtc->set_screen("screen");
	m_crtc->set_show_border_area(false);
	m_crtc->set_char_width(9);
	m_crtc->set_update_row_callback(FUNC(a2bus_suprterminal_device::crtc_update_row));
}

const tiny_rom_entry *a2bus_suprterminal_device::device_rom_region() const
{
	return ROM_NAME(a2suprterm);
}

//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

a2bus_suprterminal_device::a2bus_suprterminal_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
	a2bus_suprterminal_device(mconfig, A2BUS_SUPRTERMINAL, tag, owner, clock)
{
}

a2bus_suprterminal_device::a2bus_suprterminal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
	device_t(mconfig, type, tag, owner, clock),
	device_a2bus_card_interface(mconfig, *this),
	m_crtc(*this, "crtc"),
	m_rom(*this, "s3firmware")
{
}

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

void a2bus_suprterminal_device::device_start()
{
	m_vram = std::make_unique<u8[]>(0x800); // 4 2114 DRAMs
	m_fontram = std::make_unique<u8[]>(0x400); // 2 2114 DRAMs

	m_bRasterRAM = true;
	m_bCharBank1 = false;

	save_item(NAME(m_bRasterRAM));
	save_item(NAME(m_bCharBank1));
	save_item(NAME(m_bC800IsRAM));
	save_pointer(NAME(m_vram), 0x800);
	save_pointer(NAME(m_fontram), 0x400);
}

void a2bus_suprterminal_device::device_reset()
{
}

u8 a2bus_suprterminal_device::read_cnxx(u8 offset)
{
	return m_rom[offset+0x300];
}

u8 a2bus_suprterminal_device::read_c0nx(u8 offset)
{
	switch (offset)
	{
		case 9:
			return m_crtc->register_r();
	}

	return 0xff;
}

void a2bus_suprterminal_device::write_c0nx(u8 offset, u8 data)
{
	switch (offset)
	{
		case 2:
			m_bRasterRAM = true;
			m_bC800IsRAM = true;
			break;

		case 4:
			m_bRasterRAM = false;
			m_bC800IsRAM = true;
			break;

		case 6:
			m_bCharBank1 ^= 1;
			break;

		case 8:
			m_crtc->address_w(data);
			break;

		case 9:
			m_crtc->register_w(data);
			break;
	}
}

u8 a2bus_suprterminal_device::read_c800(u16 offset)
{
	if (!machine().side_effects_disabled())
	{
		if (offset == 0x7ff)
		{
			m_bC800IsRAM = false;
		}
	}

	if (m_bC800IsRAM)
	{
		if (!m_bRasterRAM)
		{
			return m_vram[offset];
		}
		else
		{
			return m_fontram[offset&0x3ff];
		}
	}

	return m_rom[offset];
}

/*-------------------------------------------------
    write_c800 - called for writes to this card's c800 space
-------------------------------------------------*/
void a2bus_suprterminal_device::write_c800(u16 offset, u8 data)
{
	if (offset == 0x7ff)
	{
		m_bC800IsRAM = false;
	}

	if (!m_bRasterRAM)
	{
		m_vram[offset] = data;
	}
	else
	{
		m_fontram[offset&0x3ff] = data;
	}
}

MC6845_UPDATE_ROW(a2bus_suprterminal_device::crtc_update_row)
{
	u32 *p = &bitmap.pix(y);
	u16 chr_base = ra;

	for (int i = 0; i < x_count; i++)
	{
		u16 offset = (ma + i) & 0x7ff;
		u8 chr = m_vram[offset] & 0x7f;
		u8 data = m_fontram[chr_base + chr * 8];
		rgb_t fg = rgb_t::white();
		rgb_t bg = rgb_t::black();

		if (i == cursor_x)
		{
			std::swap(fg, bg);
		}

		for (int j = 9; j > 0; j--)
		{
			*p++ = BIT(data, 7) ? fg : bg;
			data <<= 1;
		}
	}
}

} // anonymous namespace


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

DEFINE_DEVICE_TYPE_PRIVATE(A2BUS_SUPRTERMINAL, device_a2bus_card_interface, a2bus_suprterminal_device, "a2suprterm", "M&R Enterprises SUP'R'TERMINAL")
span class="nb">false), m_irq_b2(false), m_irq_b_state(0), m_in_a_pushed(false), m_out_a_needs_pulled(false), m_in_ca1_pushed(false), m_in_ca2_pushed(false), m_out_ca2_needs_pulled(false), m_in_b_pushed(false), m_out_b_needs_pulled(false), m_in_cb1_pushed(false), m_in_cb2_pushed(false), m_out_cb2_needs_pulled(false), m_logged_port_a_not_connected(false), m_logged_port_b_not_connected(false), m_logged_ca1_not_connected(false), m_logged_ca2_not_connected(false), m_logged_cb1_not_connected(false), m_logged_cb2_not_connected(false) { } //------------------------------------------------- // device_resolve_objects - resolve objects that // may be needed for other devices to set // initial conditions at start time //------------------------------------------------- void pia6821_device::device_resolve_objects() { // resolve callbacks m_in_a_handler.resolve(); m_in_b_handler.resolve(); m_in_ca1_handler.resolve(); m_in_cb1_handler.resolve(); m_in_ca2_handler.resolve(); m_out_a_handler.resolve(); m_out_b_handler.resolve(); m_ts_b_handler.resolve(); m_ca2_handler.resolve(); m_cb2_handler.resolve(); m_irqa_handler.resolve_safe(); m_irqb_handler.resolve_safe(); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void pia6821_device::device_start() { m_in_a = 0xff; m_in_b = 0; m_in_a_pushed = false; m_out_a_needs_pulled = false; m_out_ca2_needs_pulled = false; m_in_b_pushed = false; m_out_b_needs_pulled = false; m_out_cb2_needs_pulled = false; m_logged_port_a_not_connected = false; m_logged_port_b_not_connected = false; m_logged_ca1_not_connected = false; m_logged_ca2_not_connected = false; m_logged_cb1_not_connected = false; m_logged_cb2_not_connected = false; save_item(NAME(m_in_a)); save_item(NAME(m_in_ca1)); save_item(NAME(m_in_ca2)); save_item(NAME(m_out_a)); save_item(NAME(m_out_ca2)); save_item(NAME(m_ddr_a)); save_item(NAME(m_ctl_a)); save_item(NAME(m_irq_a1)); save_item(NAME(m_irq_a2)); save_item(NAME(m_irq_a_state)); save_item(NAME(m_in_b)); save_item(NAME(m_in_cb1)); save_item(NAME(m_in_cb2)); save_item(NAME(m_out_b)); save_item(NAME(m_out_cb2)); save_item(NAME(m_last_out_cb2_z)); save_item(NAME(m_ddr_b)); save_item(NAME(m_ctl_b)); save_item(NAME(m_irq_b1)); save_item(NAME(m_irq_b2)); save_item(NAME(m_irq_b_state)); save_item(NAME(m_in_a_pushed)); save_item(NAME(m_out_a_needs_pulled)); save_item(NAME(m_in_ca1_pushed)); save_item(NAME(m_in_ca2_pushed)); save_item(NAME(m_out_ca2_needs_pulled)); save_item(NAME(m_in_b_pushed)); save_item(NAME(m_out_b_needs_pulled)); save_item(NAME(m_in_cb1_pushed)); save_item(NAME(m_in_cb2_pushed)); save_item(NAME(m_out_cb2_needs_pulled)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void pia6821_device::device_reset() { // // set default read values. // // ports A,CA1,CA2 default to 1 // ports B,CB1,CB2 are three-state and undefined (set to 0) // m_out_a = 0; m_out_ca2 = 0; m_ddr_a = 0; m_ctl_a = 0; m_irq_a1 = false; m_irq_a2 = false; m_irq_a_state = 0; m_out_b = 0; m_out_cb2 = 0; m_last_out_cb2_z = 0; m_ddr_b = 0; m_ctl_b = 0; m_irq_b1 = false; m_irq_b2 = false; m_irq_b_state = 0; // clear the IRQs m_irqa_handler(CLEAR_LINE); m_irqb_handler(CLEAR_LINE); // reset port A to internal pullups if (!m_out_a_handler.isnull()) m_out_a_handler(0xff); if (!m_ca2_handler.isnull()) m_ca2_handler(1); // reset port B to three-state outputs if (!m_out_b_handler.isnull() && !m_ts_b_handler.isnull()) m_out_b_handler(offs_t(0), m_ts_b_handler(), 0); } //------------------------------------------------- // update_interrupts //------------------------------------------------- void pia6821_device::update_interrupts() { // start with IRQ A int new_state = (m_irq_a1 && irq1_enabled(m_ctl_a)) || (m_irq_a2 && irq2_enabled(m_ctl_a)); if (new_state != m_irq_a_state) { m_irq_a_state = new_state; m_irqa_handler(m_irq_a_state ? ASSERT_LINE : CLEAR_LINE); } // then do IRQ B new_state = (m_irq_b1 && irq1_enabled(m_ctl_b)) || (m_irq_b2 && irq2_enabled(m_ctl_b)); if (new_state != m_irq_b_state) { m_irq_b_state = new_state; m_irqb_handler(m_irq_b_state ? ASSERT_LINE : CLEAR_LINE); } } //------------------------------------------------- // get_in_a_value //------------------------------------------------- uint8_t pia6821_device::get_in_a_value() { uint8_t port_a_data = 0; uint8_t ret; // update the input if (!m_in_a_handler.isnull()) { port_a_data = m_in_a_handler(0); } else { if (m_in_a_pushed) { port_a_data = m_in_a; } else { // assume pins are disconnected and simulate the internal pullups. port_a_data = 0xff; if (!m_logged_port_a_not_connected && (m_ddr_a != 0xff) && !machine().side_effects_disabled()) { logerror("Warning! No port A read handler. Assuming pins 0x%02X not connected\n", m_ddr_a ^ 0xff); m_logged_port_a_not_connected = true; } } } // For port A, when the port is in output mode other devices can drive the // pins too. If the device pulls the voltage on the lines enough,a read of // the output register will show the external device value on the driven pins. ret = (~m_ddr_a & port_a_data) // input pins | (m_ddr_a & m_out_a & ~m_a_input_overrides_output_mask) // normal output pins | (m_ddr_a & port_a_data & m_a_input_overrides_output_mask); // overridden output pins return ret; } //------------------------------------------------- // get_in_b_value //------------------------------------------------- uint8_t pia6821_device::get_in_b_value() { uint8_t ret; if (m_ddr_b == 0xff) { // all output, just return buffer ret = m_out_b; } else { uint8_t port_b_data; // update the input if (!m_in_b_handler.isnull()) { port_b_data = m_in_b_handler(0); } else { if (m_in_b_pushed) { port_b_data = m_in_b; } else { if (!m_logged_port_b_not_connected && (m_ddr_b != 0xff) && !machine().side_effects_disabled()) { logerror("Error! No port B read handler. Three-state pins 0x%02X are undefined\n", m_ddr_b ^ 0xff); m_logged_port_b_not_connected = true; } // undefined -- need to return something port_b_data = 0x00; } } // the DDR determines if the pin or the output buffer is read ret = (m_out_b & m_ddr_b) | (port_b_data & ~m_ddr_b); } return ret; } //------------------------------------------------- // get_out_a_value //------------------------------------------------- uint8_t pia6821_device::get_out_a_value() { uint8_t ret; if (m_ddr_a == 0xff) { // all output ret = m_out_a; } else { // input pins don't change ret = (m_out_a & m_ddr_a) | (get_in_a_value() & ~m_ddr_a); } return ret; } //------------------------------------------------- // get_out_b_value //------------------------------------------------- uint8_t pia6821_device::get_out_b_value() { uint8_t ret = m_out_b & m_ddr_b; // input pins are high-impedance - send them as zeros for backwards compatibility if (m_ddr_b != 0xff && !m_ts_b_handler.isnull()) ret |= m_ts_b_handler() & ~m_ddr_b; return ret; } //------------------------------------------------- // set_out_ca2 //------------------------------------------------- void pia6821_device::set_out_ca2(int data) { m_out_ca2 = data; // send to output function if (!m_ca2_handler.isnull()) { m_ca2_handler(m_out_ca2); } else { if (m_out_ca2_needs_pulled) logerror("Warning! No port CA2 write handler. Previous value has been lost!\n"); m_out_ca2_needs_pulled = true; } } //------------------------------------------------- // set_out_cb2 //------------------------------------------------- void pia6821_device::set_out_cb2(int data) { int z = cb2_output_z(); if ((data != m_out_cb2) || (z != m_last_out_cb2_z)) { m_out_cb2 = data; m_last_out_cb2_z = z; // send to output function if (!m_cb2_handler.isnull()) { m_cb2_handler(m_out_cb2); } else { if (m_out_cb2_needs_pulled) logerror("Warning! No port CB2 write handler. Previous value has been lost!\n"); m_out_cb2_needs_pulled = true; } } } //------------------------------------------------- // port_a_r //------------------------------------------------- uint8_t pia6821_device::port_a_r() { uint8_t ret = get_in_a_value(); if (!machine().side_effects_disabled()) { // IRQ flags implicitly cleared by a read m_irq_a1 = false; m_irq_a2 = false; update_interrupts(); // CA2 is configured as output and in read strobe mode if (c2_output(m_ctl_a) && c2_strobe_mode(m_ctl_a)) { // this will cause a transition low if (m_out_ca2) set_out_ca2(false); // if the CA2 strobe is cleared by the E, reset it right away if (strobe_e_reset(m_ctl_a)) set_out_ca2(true); } LOG("PIA port A read = %02X\n", ret); } return ret; } //------------------------------------------------- // ddr_a_r //------------------------------------------------- uint8_t pia6821_device::ddr_a_r() { const uint8_t ret = m_ddr_a; LOG("PIA DDR A read = %02X\n", ret); return ret; } //------------------------------------------------- // port_b_r //------------------------------------------------- uint8_t pia6821_device::port_b_r() { const uint8_t ret = get_in_b_value(); if (!machine().side_effects_disabled()) { // This read will implicitly clear the IRQ B1 flag. If CB2 is // in write-strobe mode with CB1 restore, and a CB1 active // transition set the flag, clearing it will cause CB2 to go // high again. Note that this is different from what happens // with port A. if (m_irq_b1 && c2_strobe_mode(m_ctl_b) && strobe_c1_reset(m_ctl_b)) set_out_cb2(true); // IRQ flags implicitly cleared by a read m_irq_b1 = false; m_irq_b2 = false; update_interrupts(); LOG("PIA port B read = %02X\n", ret); } return ret; } //------------------------------------------------- // ddr_b_r //------------------------------------------------- uint8_t pia6821_device::ddr_b_r() { const uint8_t ret = m_ddr_b; LOG("PIA DDR B read = %02X\n", ret); return ret; } //------------------------------------------------- // control_a_r //------------------------------------------------- uint8_t pia6821_device::control_a_r() { uint8_t ret; if (!machine().side_effects_disabled()) { // update CA1 & CA2 if callback exists, these in turn may update IRQ's if (!m_in_ca1_handler.isnull()) { ca1_w(m_in_ca1_handler()); } else if(!m_logged_ca1_not_connected && (!m_in_ca1_pushed)) { logerror("Warning! No CA1 read handler. Assuming pin not connected\n"); m_logged_ca1_not_connected = true; } if (!m_in_ca2_handler.isnull()) { ca2_w(m_in_ca2_handler()); } else if ( !m_logged_ca2_not_connected && c2_input(m_ctl_a) && !m_in_ca2_pushed) { logerror("Warning! No CA2 read handler. Assuming pin not connected\n"); m_logged_ca2_not_connected = true; } } // read control register ret = m_ctl_a; // set the IRQ flags if we have pending IRQs if (m_irq_a1) ret |= PIA_IRQ1; if (m_irq_a2 && c2_input(m_ctl_a)) ret |= PIA_IRQ2; LOG("PIA control A read = %02X\n", ret); return ret; } //------------------------------------------------- // control_b_r //------------------------------------------------- uint8_t pia6821_device::control_b_r() { uint8_t ret; if (!machine().side_effects_disabled()) { // update CB1 & CB2 if callback exists, these in turn may update IRQ's if(!m_in_cb1_handler.isnull()) { cb1_w(m_in_cb1_handler()); } else if(!m_logged_cb1_not_connected && !m_in_cb1_pushed) { logerror("Error! no CB1 read handler. Three-state pin is undefined\n"); m_logged_cb1_not_connected = true; } if(!m_logged_cb2_not_connected && c2_input(m_ctl_b) && !m_in_cb2_pushed) { logerror("Error! Three-state pin is undefined\n"); m_logged_cb2_not_connected = true; } } // read control register ret = m_ctl_b; // set the IRQ flags if we have pending IRQs if (m_irq_b1) ret |= PIA_IRQ1; if (m_irq_b2 && c2_input(m_ctl_b)) ret |= PIA_IRQ2; LOG("PIA control B read = %02X\n", ret); return ret; } //------------------------------------------------- // read //------------------------------------------------- uint8_t pia6821_device::read(offs_t offset) { uint8_t ret; switch (offset & 0x03) { default: // impossible case 0x00: ret = output_selected(m_ctl_a) ? port_a_r() : ddr_a_r(); break; case 0x01: ret = control_a_r(); break; case 0x02: ret = output_selected(m_ctl_b) ? port_b_r() : ddr_b_r(); break; case 0x03: ret = control_b_r(); break; } return ret; } //------------------------------------------------- // send_to_out_a_func //------------------------------------------------- void pia6821_device::send_to_out_a_func(const char* message) { // input pins are pulled high const uint8_t data = get_out_a_value(); LOG("PIA %s = %02X DDRA=%02x\n", message, data, m_ddr_a); if (!m_out_a_handler.isnull()) { m_out_a_handler(offs_t(0), data); } else { if (m_out_a_needs_pulled) logerror("Warning! No port A write handler. Previous value has been lost!\n"); m_out_a_needs_pulled = true; } } //------------------------------------------------- // send_to_out_b_func //------------------------------------------------- void pia6821_device::send_to_out_b_func(const char* message) { const uint8_t data = get_out_b_value(); LOG("PIA %s = %02X DDRB=%02x\n", message, data, m_ddr_b); if (!m_out_b_handler.isnull()) { m_out_b_handler(offs_t(0), data, m_ddr_b); } else { if (m_out_b_needs_pulled) logerror("Warning! No port B write handler. Previous value has been lost!\n"); m_out_b_needs_pulled = true; } } //------------------------------------------------- // port_a_w //------------------------------------------------- void pia6821_device::port_a_w(uint8_t data) { // buffer the output value m_out_a = data; LOGSETUP("PIA "); send_to_out_a_func("port A write"); } //------------------------------------------------- // ddr_a_w //------------------------------------------------- void pia6821_device::ddr_a_w(uint8_t data) { LOGSETUP("PIA DDR A write = %02X (%s mode)\n", data, (0x00 == data) ? "input" : (0xff == data) ? "output" : "mixed"); if (m_ddr_a != data) { // DDR changed, call the callback again m_ddr_a = data; m_logged_port_a_not_connected = false; send_to_out_a_func("port A write due to DDR change"); } } //------------------------------------------------- // port_b_w //------------------------------------------------- void pia6821_device::port_b_w(uint8_t data) { // buffer the output value m_out_b = data; send_to_out_b_func("port B write"); // CB2 in write strobe mode if (c2_strobe_mode(m_ctl_b)) { // this will cause a transition low set_out_cb2(false); // if the CB2 strobe is cleared by the E, reset it right away if (strobe_e_reset(m_ctl_b)) set_out_cb2(true); } } //------------------------------------------------- // ddr_b_w //------------------------------------------------- void pia6821_device::ddr_b_w(uint8_t data) { LOGSETUP("PIA DDR B write = %02X (%s mode)\n", data, (0x00 == data) ? "input" : (0xff == data) ? "output" : "mixed"); if (m_ddr_b != data) { // DDR changed, call the callback again m_ddr_b = data; m_logged_port_b_not_connected = false; send_to_out_b_func("port B write due to DDR change"); } } //------------------------------------------------- // control_a_w //------------------------------------------------- void pia6821_device::control_a_w(uint8_t data) { // bit 7 and 6 are read only data &= 0x3f; LOGSETUP("PIA control A write = %02X\n", data); LOGSETUP(" - CA1 interrupts %s\n", (data & 0x01) ? "enabled" : "disabled"); LOGSETUP(" - CA1 interrupts active on %s transition\n", (data & 0x02) ? "low-to-high" : "high-to-low"); LOGSETUP(" - Port A %s register selected\n", (data & 0x04) ? "Data" : "DDR"); // update the control register bool ca2_was_output = c2_output(m_ctl_a); m_ctl_a = data; // CA2 is configured as output if (c2_output(m_ctl_a)) { if (c2_set_mode(m_ctl_a)) { bool set = c2_set(m_ctl_a); // set/reset mode - bit value determines the new output LOGSETUP(" - CA2 %sset output\n", set ? "" : "re"); if (!ca2_was_output || m_out_ca2 != set) set_out_ca2(set); } else { LOGSETUP(" - CA2 strobe output mode\n"); if (!ca2_was_output || !m_out_ca2) set_out_ca2(true); // strobe mode - output is always high unless strobed } } else if (ca2_was_output) { LOGSETUP(" - CA2 pulled up as input\n"); if (!m_ca2_handler.isnull()) m_ca2_handler(1); } // update externals update_interrupts(); } //------------------------------------------------- // control_b_w //------------------------------------------------- void pia6821_device::control_b_w(uint8_t data) { // bit 7 and 6 are read only data &= 0x3f; LOGSETUP("PIA control B write = %02X\n", data); LOGSETUP(" - CB1 interrupts %s\n", (data & 0x01) ? "enabled" : "disabled"); LOGSETUP(" - CB1 interrupts active on %s transition\n", (data & 0x02) ? "low-to-high" : "high-to-low"); LOGSETUP(" - Port B %s register selected\n", (data & 0x04) ? "Data" : "DDR"); // update the control register m_ctl_b = data; bool temp; if (c2_set_mode(m_ctl_b)) { temp = c2_set(m_ctl_b); // set/reset mode - bit value determines the new output LOGSETUP(" - CB2 %sset output\n", temp ? "" : "re"); } else { LOGSETUP(" - CB2 strobe output mode\n"); temp = true; // strobe mode - output is always high unless strobed } set_out_cb2(temp); // update externals update_interrupts(); } //------------------------------------------------- // write //------------------------------------------------- void pia6821_device::write(offs_t offset, uint8_t data) { switch (offset & 0x03) { default: // impossible case 0x00: if (output_selected(m_ctl_a)) port_a_w(data); else ddr_a_w(data); break; case 0x01: control_a_w(data); break; case 0x02: if (output_selected(m_ctl_b)) port_b_w(data); else ddr_b_w(data); break; case 0x03: control_b_w(data); break; } } //------------------------------------------------- // set_a_input //------------------------------------------------- void pia6821_device::set_a_input(uint8_t data) { if (!m_in_a_handler.isnull()) throw emu_fatalerror("pia6821_device::set_a_input() called when m_in_a_handler set"); LOG("Set PIA input port A = %02X\n", data); m_in_a = data; m_in_a_pushed = true; } //------------------------------------------------- // porta_w //------------------------------------------------- void pia6821_device::porta_w(uint8_t data) { set_a_input(data); } //------------------------------------------------- // write_porta_line //------------------------------------------------- void pia6821_device::write_porta_line(int line, bool state) { const uint8_t mask = 1 << line; if (state) set_a_input(m_in_a | mask); else set_a_input(m_in_a & ~mask); } //------------------------------------------------- // a_output //------------------------------------------------- uint8_t pia6821_device::a_output() { m_out_a_needs_pulled = false; return get_out_a_value(); } //------------------------------------------------- // ca1_w //------------------------------------------------- WRITE_LINE_MEMBER( pia6821_device::ca1_w ) { LOGCA1("Set PIA input CA1 = %d\n", state); // the new state has caused a transition if ((m_in_ca1 != state) && ((state && c1_low_to_high(m_ctl_a)) || (!state && c1_high_to_low(m_ctl_a)))) { LOGCA1("CA1 triggering\n"); // mark the IRQ m_irq_a1 = true; // update externals update_interrupts(); // CA2 is configured as output and in read strobe mode and cleared by a CA1 transition if (c2_output(m_ctl_a) && c2_strobe_mode(m_ctl_a) && strobe_c1_reset(m_ctl_a) && !m_out_ca2) set_out_ca2(true); } // set the new value for CA1 m_in_ca1 = state; m_in_ca1_pushed = true; } //------------------------------------------------- // ca2_w //------------------------------------------------- WRITE_LINE_MEMBER( pia6821_device::ca2_w ) { LOG("Set PIA input CA2 = %d\n", state); // if input mode and the new state has caused a transition if (c2_input(m_ctl_a) && (m_in_ca2 != state) && ((state && c2_low_to_high(m_ctl_a)) || (!state && c2_high_to_low(m_ctl_a)))) { LOG("CA2 triggering\n"); // mark the IRQ m_irq_a2 = true; // update externals update_interrupts(); } // set the new value for CA2 m_in_ca2 = state; m_in_ca2_pushed = true; } //------------------------------------------------- // ca2_output //------------------------------------------------- bool pia6821_device::ca2_output() { m_out_ca2_needs_pulled = false; return m_out_ca2; } //------------------------------------------------- // ca2_output_z - version of ca2_output which // takes account of internal pullup resistor //------------------------------------------------- bool pia6821_device::ca2_output_z() { m_out_ca2_needs_pulled = false; // If it's an output, output the bit, if it's an input, it's pulled up return m_out_ca2 | c2_input(m_ctl_a); } //------------------------------------------------- // portb_w //------------------------------------------------- void pia6821_device::portb_w(uint8_t data) { if (!m_in_b_handler.isnull()) throw emu_fatalerror("pia6821_device::portb_w() called when in_b_func implemented"); LOG("Set PIA input port B = %02X\n", data); m_in_b = data; m_in_b_pushed = true; } //------------------------------------------------- // write_portb_line //------------------------------------------------- void pia6821_device::write_portb_line(int line, bool state) { const uint8_t mask = 1 << line; if (state) portb_w(m_in_b | mask); else portb_w(m_in_b & ~mask); } //------------------------------------------------- // b_output //------------------------------------------------- uint8_t pia6821_device::b_output() { m_out_b_needs_pulled = false; return get_out_b_value(); } //------------------------------------------------- // cb1_w //------------------------------------------------- WRITE_LINE_MEMBER( pia6821_device::cb1_w ) { LOG("Set PIA input CB1 = %d\n", state); // the new state has caused a transition if ((m_in_cb1 != state) && ((state && c1_low_to_high(m_ctl_b)) || (!state && c1_high_to_low(m_ctl_b)))) { LOG("CB1 triggering\n"); // mark the IRQ m_irq_b1 = true; // update externals update_interrupts(); // If CB2 is configured as a write-strobe output which is reset by a CB1 // transition, this reset will only happen when a read from port B implicitly // clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this // is different from what happens with port A. } // set the new value for CB1 m_in_cb1 = state; m_in_cb1_pushed = true; } //------------------------------------------------- // cb2_w //------------------------------------------------- WRITE_LINE_MEMBER( pia6821_device::cb2_w ) { LOG("Set PIA input CB2 = %d\n", state); // if input mode and the new state has caused a transition if (c2_input(m_ctl_b) && (m_in_cb2 != state) && ((state && c2_low_to_high(m_ctl_b)) || (!state && c2_high_to_low(m_ctl_b)))) { LOG("CB2 triggering\n"); // mark the IRQ m_irq_b2 = true; // update externals update_interrupts(); } // set the new value for CA2 m_in_cb2 = state; m_in_cb2_pushed = true; } //------------------------------------------------- // output_cb2 //------------------------------------------------- bool pia6821_device::cb2_output() { m_out_cb2_needs_pulled = false; return m_out_cb2; } //------------------------------------------------- // cb2_output_z //------------------------------------------------- bool pia6821_device::cb2_output_z() { return !c2_output(m_ctl_b); } //------------------------------------------------- // control byte wrappers //------------------------------------------------- inline bool pia6821_device::irq1_enabled(uint8_t c) { return bool(BIT(c, 0)); } inline bool pia6821_device::c1_low_to_high(uint8_t c) { return bool(BIT(c, 1)); } inline bool pia6821_device::c1_high_to_low(uint8_t c) { return !bool(BIT(c, 1)); } inline bool pia6821_device::output_selected(uint8_t c) { return bool(BIT(c, 2)); } inline bool pia6821_device::irq2_enabled(uint8_t c) { return bool(BIT(c, 3)); } inline bool pia6821_device::strobe_e_reset(uint8_t c) { return bool(BIT(c, 3)); } inline bool pia6821_device::strobe_c1_reset(uint8_t c) { return !bool(BIT(c, 3)); } inline bool pia6821_device::c2_set(uint8_t c) { return bool(BIT(c, 3)); } inline bool pia6821_device::c2_low_to_high(uint8_t c) { return bool(BIT(c, 4)); } inline bool pia6821_device::c2_high_to_low(uint8_t c) { return !bool(BIT(c, 4)); } inline bool pia6821_device::c2_set_mode(uint8_t c) { return bool(BIT(c, 4)); } inline bool pia6821_device::c2_strobe_mode(uint8_t c) { return !bool(BIT(c, 4)); } inline bool pia6821_device::c2_output(uint8_t c) { return bool(BIT(c, 5)); } inline bool pia6821_device::c2_input(uint8_t c) { return !bool(BIT(c, 5)); }