1 // license:BSD-3-Clause
2 // copyright-holders:AJR
3 /*******************************************************************************
4 
5     Skeleton driver for Wyse WY-100 video terminal.
6 
7     The WY-100 was Wyse Technology's first product.
8 
9     Of the two 8276 CRTCs, one is used solely to keep track of which characters
10     are protected, which is the only transparent attribute supported.
11 
12     Known emulation bugs:
13     - Frequent screen glitches when writing to the display
14     - No dimming of protected characters
15 
16 *******************************************************************************/
17 
18 #include "emu.h"
19 #include "bus/rs232/rs232.h"
20 #include "cpu/mcs48/mcs48.h"
21 #include "machine/bankdev.h"
22 #include "machine/input_merger.h"
23 #include "machine/scn_pci.h"
24 #include "machine/wy50kb.h"
25 #include "sound/spkrdev.h"
26 #include "video/i8275.h"
27 #include "screen.h"
28 #include "speaker.h"
29 
30 class wy100_state : public driver_device
31 {
32 public:
wy100_state(const machine_config & mconfig,device_type type,const char * tag)33 	wy100_state(const machine_config &mconfig, device_type type, const char *tag)
34 		: driver_device(mconfig, type, tag)
35 		, m_maincpu(*this, "maincpu")
36 		, m_rambank(*this, "rambank")
37 		, m_crtc(*this, "crtc%u", 1U)
38 		, m_pci(*this, "pci")
39 		, m_modem(*this, "modem")
40 		, m_printer(*this, "aux")
41 		, m_chargen(*this, "chargen")
42 	{
43 	}
44 
45 	void wy100(machine_config &config);
46 
47 protected:
48 	virtual void machine_start() override;
49 
50 private:
51 	I8275_DRAW_CHARACTER_MEMBER(draw_character);
52 
53 	DECLARE_WRITE_LINE_MEMBER(brdy_w);
54 	DECLARE_WRITE_LINE_MEMBER(txd_w);
55 	void p2_w(u8 data);
56 	u8 memory_r(offs_t offset);
57 	void memory_w(offs_t offset, u8 data);
58 
59 	void prg_map(address_map &map);
60 	void io_map(address_map &map);
61 	void bank_map(address_map &map);
62 
63 	required_device<mcs48_cpu_device> m_maincpu;
64 	required_device<address_map_bank_device> m_rambank;
65 	required_device_array<i8276_device, 2> m_crtc;
66 	required_device<scn2651_device> m_pci;
67 	required_device<rs232_port_device> m_modem;
68 	required_device<rs232_port_device> m_printer;
69 
70 	required_region_ptr<u8> m_chargen;
71 
72 	bool m_brdy;
73 	bool m_bs_enable;
74 	bool m_txd;
75 	bool m_printer_select;
76 };
77 
machine_start()78 void wy100_state::machine_start()
79 {
80 	m_brdy = false;
81 	m_bs_enable = false;
82 	m_txd = true;
83 	m_printer_select = false;
84 
85 	save_item(NAME(m_brdy));
86 	save_item(NAME(m_bs_enable));
87 	save_item(NAME(m_txd));
88 	save_item(NAME(m_printer_select));
89 }
90 
WRITE_LINE_MEMBER(wy100_state::brdy_w)91 WRITE_LINE_MEMBER(wy100_state::brdy_w)
92 {
93 	m_brdy = state;
94 }
95 
I8275_DRAW_CHARACTER_MEMBER(wy100_state::draw_character)96 I8275_DRAW_CHARACTER_MEMBER(wy100_state::draw_character)
97 {
98 	// LTEN attribute output is not used (GPA1 generates underline instead)
99 	u8 dots = 0;
100 	if (!vsp)
101 	{
102 		if (BIT(gpa, 1) && (linecount & 0xb) == 0xa)
103 			dots = 0xff;
104 		else if (!BIT(gpa, 0))
105 			dots = m_chargen[(charcode << 4) | linecount];
106 	}
107 	if (rvv)
108 		dots ^= 0xff;
109 
110 	// TODO: dim protected characters
111 	const rgb_t fg = rgb_t::white();
112 	const rgb_t bg = rgb_t::black();
113 	for (int i = 0; i < 10; i++)
114 		bitmap.pix(y, x + i) = BIT(dots, i < 1 || i > 8 ? 7 : 8 - i) ? fg : bg;
115 }
116 
WRITE_LINE_MEMBER(wy100_state::txd_w)117 WRITE_LINE_MEMBER(wy100_state::txd_w)
118 {
119 	m_txd = state;
120 	if (m_printer_select)
121 		m_printer->write_txd(state);
122 	else
123 		m_modem->write_txd(state);
124 }
125 
p2_w(u8 data)126 void wy100_state::p2_w(u8 data)
127 {
128 	m_rambank->set_bank(data & 0x1f);
129 	if (!BIT(data, 6))
130 		m_bs_enable = false;
131 	if (BIT(data, 7) && !m_printer_select)
132 	{
133 		m_printer_select = true;
134 		m_printer->write_txd(m_txd);
135 		m_modem->write_txd(1);
136 	}
137 	else if (!BIT(data, 7) && m_printer_select)
138 	{
139 		m_printer_select = false;
140 		m_modem->write_txd(m_txd);
141 		m_printer->write_txd(1);
142 	}
143 }
144 
memory_r(offs_t offset)145 u8 wy100_state::memory_r(offs_t offset)
146 {
147 	u8 p2 = m_maincpu->p2_r();
148 	u8 data = BIT(p2, 5) ? m_pci->read(p2 & 3) : m_rambank->read8(offset);
149 	if (m_bs_enable && !machine().side_effects_disabled())
150 	{
151 		u8 chardata = (data & 0xe0) == 0x80 ? data : data & 0x7f;
152 		m_crtc[0]->dack_w(chardata);
153 		m_crtc[1]->dack_w((chardata & 0xfe) | (BIT(data, 7) ? 0x00 : 0x01));
154 	}
155 	return data;
156 }
157 
memory_w(offs_t offset,u8 data)158 void wy100_state::memory_w(offs_t offset, u8 data)
159 {
160 	u8 p2 = m_maincpu->p2_r();
161 
162 	// CRTC access is write-only
163 	if (!BIT(p2, 6))
164 	{
165 		m_crtc[0]->write(p2 & 1, data);
166 		m_crtc[1]->write(p2 & 1, data);
167 	}
168 	else if (m_brdy)
169 		m_bs_enable = true;
170 
171 	if (BIT(p2, 5))
172 		m_pci->write(p2 & 3, data);
173 
174 	m_rambank->write8(offset, data);
175 }
176 
prg_map(address_map & map)177 void wy100_state::prg_map(address_map &map)
178 {
179 	map(0x000, 0xfff).rom().region("maincpu", 0);
180 }
181 
io_map(address_map & map)182 void wy100_state::io_map(address_map &map)
183 {
184 	map(0x00, 0xff).rw(FUNC(wy100_state::memory_r), FUNC(wy100_state::memory_w));
185 }
186 
bank_map(address_map & map)187 void wy100_state::bank_map(address_map &map)
188 {
189 	map(0x0000, 0x0bff).nopw();
190 	map(0x0c00, 0x0fff).ram(); // buffer RAM (P2114A-6 at 4-5A)
191 	map(0x1000, 0x1fff).ram(); // display RAM (P2114A-6 at 6-9A, optionally also at 10-13A)
192 }
193 
194 
INPUT_PORTS_START(wy100)195 static INPUT_PORTS_START(wy100)
196 INPUT_PORTS_END
197 
198 
199 void wy100_state::wy100(machine_config &config)
200 {
201 	I8039(config, m_maincpu, 10.1376_MHz_XTAL); // INS8039N-11
202 	m_maincpu->set_addrmap(AS_PROGRAM, &wy100_state::prg_map);
203 	m_maincpu->set_addrmap(AS_IO, &wy100_state::io_map);
204 	m_maincpu->p1_out_cb().set("keyboard", FUNC(wy100_keyboard_device::scan_w)).mask(0x7f).invert();
205 	m_maincpu->p1_out_cb().append("spkrgate", FUNC(input_merger_device::in_w<0>)).bit(7);
206 	m_maincpu->p2_out_cb().set(FUNC(wy100_state::p2_w));
207 	m_maincpu->t0_in_cb().set("keyboard", FUNC(wy100_keyboard_device::sense_r)).invert();
208 	m_maincpu->t1_in_cb().set(m_pci, FUNC(scn2651_device::rxrdy_r));
209 
210 	WY100_KEYBOARD(config, "keyboard");
211 
212 	ADDRESS_MAP_BANK(config, m_rambank);
213 	m_rambank->set_addrmap(0, &wy100_state::bank_map);
214 	m_rambank->set_data_width(8);
215 	m_rambank->set_addr_width(13);
216 	m_rambank->set_stride(0x100);
217 
218 	SCN2651(config, m_pci, 10.1376_MHz_XTAL / 2); // INS2651N
219 	m_pci->rts_handler().set(m_modem, FUNC(rs232_port_device::write_rts));
220 	m_pci->dtr_handler().set(m_modem, FUNC(rs232_port_device::write_dtr));
221 	m_pci->txd_handler().set(FUNC(wy100_state::txd_w));
222 
223 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
224 	screen.set_raw(18.48_MHz_XTAL, 1000, 0, 800, 308, 0, 286);
225 	//screen.set_raw(18.48_MHz_XTAL, 1100, 0, 800, 336, 0, 312);
226 	screen.set_color(rgb_t::green());
227 	screen.set_screen_update("crtc1", FUNC(i8276_device::screen_update));
228 
229 	for (auto &crtc : m_crtc)
230 	{
231 		I8276(config, crtc, 18.48_MHz_XTAL / 10);
232 		crtc->set_screen("screen");
233 		crtc->set_character_width(10);
234 	}
235 	m_crtc[0]->set_display_callback(FUNC(wy100_state::draw_character));
236 	m_crtc[0]->drq_wr_callback().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
237 	m_crtc[0]->drq_wr_callback().append(FUNC(wy100_state::brdy_w));
238 	m_crtc[0]->lc_wr_callback().set("spkrgate", FUNC(input_merger_device::in_w<1>)).bit(3);
239 
240 	SPEAKER(config, "mono").front_center();
241 	SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.5);
242 	input_merger_device &spkrgate(INPUT_MERGER_ALL_HIGH(config, "spkrgate"));
243 	spkrgate.output_handler().set("speaker", FUNC(speaker_sound_device::level_w));
244 
245 	RS232_PORT(config, m_modem, default_rs232_devices, "loopback");
246 	m_modem->dcd_handler().set(m_pci, FUNC(scn2651_device::dcd_w));
247 	m_modem->cts_handler().set(m_pci, FUNC(scn2651_device::cts_w));
248 	m_modem->rxd_handler().set(m_pci, FUNC(scn2651_device::rxd_w));
249 
250 	RS232_PORT(config, m_printer, default_rs232_devices, nullptr);
251 	m_printer->dsr_handler().set(m_pci, FUNC(scn2651_device::dsr_w));
252 }
253 
254 
255 ROM_START(wy100)
256 	ROM_REGION(0x1000, "maincpu", 0)
257 	ROM_LOAD("wy100_00401f.bin", 0x0000, 0x1000, CRC(1f71de8f) SHA1(2bd9f712aba8b44823ce0b3e111da7b472a1ab38))
258 
259 	ROM_REGION(0x0800, "chargen", 0)
260 	ROM_LOAD("wy100_23-002-01c.bin", 0x0000, 0x0800, CRC(93c31537) SHA1(085e5ad110a76bee83e819a718a7d4cbfb8e07e7))
261 ROM_END
262 
263 
264 COMP(1981, wy100, 0, 0, wy100, wy100, wy100_state, empty_init, "Wyse Technology", "WY-100", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS)
265