1 // license:BSD-3-Clause
2 // copyright-holders:Carl
3 
4 #include "emu.h"
5 #include "pcd.h"
6 
7 #include "cpu/mcs48/mcs48.h"
8 #include "cpu/mcs51/mcs51.h"
9 #include "screen.h"
10 
11 
12 DEFINE_DEVICE_TYPE(PCD_VIDEO, pcd_video_device, "pcd_video", "Siemens PC-D Video")
13 DEFINE_DEVICE_TYPE(PCX_VIDEO, pcx_video_device, "pcx_video", "Siemens PC-X Video")
14 
pcdx_video_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)15 pcdx_video_device::pcdx_video_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
16 	device_t(mconfig, type, tag, owner, clock),
17 	device_gfx_interface(mconfig, *this, nullptr, "palette"),
18 	m_maincpu(*this, ":maincpu"),
19 	m_mcu(*this, "graphics"),
20 	m_pic2(*this, ":pic2")
21 {
22 }
23 
pcd_video_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)24 pcd_video_device::pcd_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
25 	pcdx_video_device(mconfig, PCD_VIDEO, tag, owner, clock),
26 	m_crtc(*this, "crtc"),
27 	m_mouse_btn(*this, "MOUSE"),
28 	m_mouse_x(*this, "MOUSEX"),
29 	m_mouse_y(*this, "MOUSEY"),
30 	m_vram(32*1024),
31 	m_charram(8*1024)
32 {
33 }
34 
pcx_video_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)35 pcx_video_device::pcx_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
36 	pcdx_video_device(mconfig, PCX_VIDEO, tag, owner, clock),
37 	device_serial_interface(mconfig, *this),
38 	m_crtc(*this, "crtc"),
39 	m_charrom(*this, "char"),
40 	m_txd_handler(*this)
41 {
42 }
43 
44 ROM_START( pcd_video )
45 	ROM_REGION(0x400, "graphics", 0)
46 	ROM_LOAD("s36361-d321-v1.bin", 0x000, 0x400, CRC(69baeb2a) SHA1(98b9cd0f38c51b4988a3aed0efcf004bedd115ff))
47 ROM_END
48 
device_rom_region() const49 const tiny_rom_entry *pcd_video_device::device_rom_region() const
50 {
51 	return ROM_NAME( pcd_video );
52 }
53 
54 ROM_START( pcx_video )
55 	ROM_REGION(0x2000, "char", 0)
CRC(e4933c16)56 	ROM_LOAD("d12-graka.bin", 0x0000, 0x2000, CRC(e4933c16) SHA1(932ae1f0cd2b029b7f5fc3d2d1679e70b25c0828))
57 
58 	ROM_REGION(0x6000, "graphics", 0)
59 	ROM_LOAD("d40-graka.bin", 0x0000, 0x2000, CRC(dce48252) SHA1(0d9a575b2d001168a36864d7bd5db1c3aca5fb8d))
60 	ROM_LOAD("d39-graka.bin", 0x4000, 0x2000, CRC(02920e25) SHA1(145a6648d75c1dc4788f9bc7790281ef7e8f8426))
61 ROM_END
62 
63 const tiny_rom_entry *pcx_video_device::device_rom_region() const
64 {
65 	return ROM_NAME( pcx_video );
66 }
67 
68 static const gfx_layout pcd_charlayout =
69 {
70 	8, 14,                   /* 8 x 14 characters */
71 	512,                    /* 512 characters */
72 	1,                  /* 1 bits per pixel */
73 	{ 0 },                  /* no bitplanes */
74 	/* x offsets */
75 	{ 0, 1, 2, 3, 4, 5, 6, 7 },
76 	/* y offsets */
77 	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8 },
78 	8*16
79 };
80 
81 static GFXDECODE_START( gfx_pcx )
82 	GFXDECODE_DEVICE( "char", 0x0000, pcd_charlayout, 0, 1 )
83 GFXDECODE_END
84 
INPUT_PORTS_START(pcd_mouse)85 static INPUT_PORTS_START( pcd_mouse )
86 	PORT_START("MOUSE")
87 	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Right Mouse Button")   PORT_CODE(MOUSECODE_BUTTON1)
88 	PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Left Mouse Button")    PORT_CODE(MOUSECODE_BUTTON2)
89 
90 	PORT_START("MOUSEX")
91 	PORT_BIT( 0xfff, 0x000, IPT_MOUSE_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0)
92 
93 	PORT_START("MOUSEY")
94 	PORT_BIT( 0xfff, 0x000, IPT_MOUSE_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0)
95 INPUT_PORTS_END
96 
97 ioport_constructor pcd_video_device::device_input_ports() const
98 {
99 	return INPUT_PORTS_NAME(pcd_mouse);
100 }
101 
device_add_mconfig(machine_config & config)102 void pcd_video_device::device_add_mconfig(machine_config &config)
103 {
104 	i8741a_device &mcu(I8741A(config, "graphics", 16_MHz_XTAL / 2)); // NEC D8741AD
105 	mcu.p1_in_cb().set(FUNC(pcd_video_device::p1_r));
106 	mcu.p2_out_cb().set(FUNC(pcd_video_device::p2_w));
107 	mcu.t1_in_cb().set(FUNC(pcd_video_device::t1_r));
108 
109 	// video hardware
110 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
111 	screen.set_raw(16_MHz_XTAL, 832, 0, 640, 381, 0, 350);
112 	screen.set_screen_update("crtc", FUNC(scn2674_device::screen_update));
113 
114 	PALETTE(config, "palette", FUNC(pcd_video_device::pcdx_palette), 3);
115 
116 	SCN2674(config, m_crtc, 16_MHz_XTAL / 16);
117 	m_crtc->set_character_width(16);
118 	m_crtc->set_display_callback(FUNC(pcd_video_device::display_pixels));
119 	m_crtc->set_screen("screen");
120 
121 	TIMER(config, "mouse_timer").configure_periodic(FUNC(pcd_video_device::mouse_timer), attotime::from_hz(15000)); // guess
122 }
123 
pcx_vid_map(address_map & map)124 void pcx_video_device::pcx_vid_map(address_map &map)
125 {
126 	map(0x0000, 0x5fff).rom().region("graphics", 0);
127 }
128 
pcx_vid_io(address_map & map)129 void pcx_video_device::pcx_vid_io(address_map &map)
130 {
131 	map(0x8000, 0x8007).rw(m_crtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write));
132 	map(0x8008, 0x8008).r(FUNC(pcx_video_device::unk_r));
133 	map(0xa000, 0xa000).rw(m_crtc, FUNC(scn2672_device::buffer_r), FUNC(scn2672_device::buffer_w));
134 	map(0xa001, 0xa001).rw(m_crtc, FUNC(scn2672_device::attr_buffer_r), FUNC(scn2672_device::attr_buffer_w));
135 	map(0xa002, 0xa003).rw(FUNC(pcx_video_device::term_mcu_r), FUNC(pcx_video_device::term_mcu_w));
136 	map(0xc000, 0xc7ff).ram();
137 }
138 
pcx_char_ram(address_map & map)139 void pcx_video_device::pcx_char_ram(address_map &map)
140 {
141 	map(0x0000, 0x07ff).ram();
142 }
143 
pcx_attr_ram(address_map & map)144 void pcx_video_device::pcx_attr_ram(address_map &map)
145 {
146 	map(0x0000, 0x07ff).ram();
147 }
148 
device_add_mconfig(machine_config & config)149 void pcx_video_device::device_add_mconfig(machine_config &config)
150 {
151 	i8031_device &gfx(I8031(config, "graphics", 24_MHz_XTAL / 2));
152 	gfx.set_addrmap(AS_PROGRAM, &pcx_video_device::pcx_vid_map);
153 	gfx.set_addrmap(AS_IO, &pcx_video_device::pcx_vid_io);
154 	gfx.port_out_cb<1>().set(FUNC(pcx_video_device::p1_w));
155 	gfx.serial_tx_cb().set(FUNC(pcx_video_device::tx_callback));
156 	gfx.serial_rx_cb().set(FUNC(pcx_video_device::rx_callback));
157 
158 	// video hardware
159 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
160 	screen.set_raw(24_MHz_XTAL, 1272, 0, 960, 381, 0, 350);
161 	screen.set_screen_update("crtc", FUNC(scn2672_device::screen_update));
162 
163 	PALETTE(config, "palette", palette_device::MONOCHROME);
164 
165 	SCN2672(config, m_crtc, 24_MHz_XTAL / 12); // used with SCB2673B
166 	m_crtc->intr_callback().set_inputline("graphics", MCS51_INT0_LINE);
167 	m_crtc->set_character_width(8);
168 	m_crtc->set_display_callback(FUNC(pcx_video_device::display_pixels));
169 	m_crtc->set_screen("screen");
170 	m_crtc->set_addrmap(0, &pcx_video_device::pcx_char_ram);
171 	m_crtc->set_addrmap(1, &pcx_video_device::pcx_attr_ram);
172 }
173 
174 
SCN2674_DRAW_CHARACTER_MEMBER(pcd_video_device::display_pixels)175 SCN2674_DRAW_CHARACTER_MEMBER(pcd_video_device::display_pixels)
176 {
177 	address <<= 1;
178 	if(lg)
179 	{
180 		uint16_t data = m_vram[address + 1] | (m_vram[address] << 8);
181 		if(m_p2 & 8)
182 			data = ~data;
183 		for(int i = 0; i < 16; i++)
184 		{
185 			bitmap.pix(y, x++) = palette().pen(BIT(data, 15) ? 1 : 0);
186 			data <<= 1;
187 		}
188 	}
189 	else
190 	{
191 		uint8_t data, attr;
192 		int bgnd = 0, fgnd = 1;
193 		data = m_charram[m_vram[address] * 16 + linecount];
194 		attr = m_vram[address + 1];
195 		if(cursor)
196 			data = 0xff;
197 		if(ul && (attr & 0x20))
198 			data = 0xff;
199 
200 		if(attr & 0x10)
201 			data = ~data;
202 		if(m_p2 & 8)
203 		{
204 			fgnd = 0;
205 			bgnd = (attr & 8) ? 2 : 1;
206 		}
207 		else if(attr & 8)
208 			bgnd = 2;
209 		for(int i = 0; i < 8; i++)
210 		{
211 			rgb_t pix = palette().pen(BIT(data, 7) ? fgnd : bgnd);
212 			bitmap.pix(y, x++) = pix;
213 			bitmap.pix(y, x++) = pix;
214 			data <<= 1;
215 		}
216 	}
217 }
218 
SCN2672_DRAW_CHARACTER_MEMBER(pcx_video_device::display_pixels)219 SCN2672_DRAW_CHARACTER_MEMBER(pcx_video_device::display_pixels)
220 {
221 	uint16_t data = m_charrom[charcode * 16 + linecount + (attrcode & 0x20 ? 4096 : 0)];
222 
223 	if (cursor)
224 		data = 0xff;
225 
226 	if (BIT(m_p1, 5))
227 		data ^= 0xff;
228 
229 	for (int i = 0; i < 8; i++)
230 	{
231 		rgb_t pix = palette().pen(BIT(data, 7) ? 1 : 0);
232 		bitmap.pix(y, x++) = pix;
233 		data <<= 1;
234 	}
235 }
236 
pcdx_palette(palette_device & palette) const237 void pcdx_video_device::pcdx_palette(palette_device &palette) const
238 {
239 	palette.set_pen_color(0, rgb_t::black());
240 	palette.set_pen_color(1, rgb_t::white());
241 	palette.set_pen_color(2, rgb_t(128, 128, 128));
242 }
243 
TIMER_DEVICE_CALLBACK_MEMBER(pcd_video_device::mouse_timer)244 TIMER_DEVICE_CALLBACK_MEMBER(pcd_video_device::mouse_timer)
245 {
246 	m_t1 = (m_t1 == CLEAR_LINE) ? ASSERT_LINE : CLEAR_LINE;
247 	if(m_t1)
248 	{
249 		switch(m_mouse.phase)
250 		{
251 		case 0:
252 			m_mouse.xa = m_mouse.x > m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE;
253 			m_mouse.xb = m_mouse.x < m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE;
254 			m_mouse.ya = m_mouse.y > m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE;
255 			m_mouse.yb = m_mouse.y < m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE;
256 			break;
257 		case 1:
258 			m_mouse.xa = m_mouse.xb = m_mouse.x != m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE;
259 			m_mouse.ya = m_mouse.yb = m_mouse.y != m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE;
260 			break;
261 		case 2:
262 			m_mouse.xa = m_mouse.x < m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE;
263 			m_mouse.xb = m_mouse.x > m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE;
264 			m_mouse.ya = m_mouse.y < m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE;
265 			m_mouse.yb = m_mouse.y > m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE;
266 			break;
267 		case 3:
268 			m_mouse.xa = m_mouse.xb = ASSERT_LINE;
269 			m_mouse.ya = m_mouse.yb = ASSERT_LINE;
270 			m_mouse.prev_x = m_mouse.x;
271 			m_mouse.prev_y = m_mouse.y;
272 			m_mouse.x = m_mouse_x->read();
273 			m_mouse.y = m_mouse_y->read();
274 			break;
275 		}
276 
277 		m_mouse.phase = (m_mouse.phase + 1) & 3;
278 	}
279 }
280 
vram_w(offs_t offset,uint8_t data)281 void pcd_video_device::vram_w(offs_t offset, uint8_t data)
282 {
283 	if(m_vram_sw)
284 		m_vram[offset] = data;
285 	else if(!(offset & 1))
286 	{
287 		offset >>= 1;
288 		m_charram[offset & 0x1fff] = data;
289 		gfx(0)->mark_dirty(offset/16);
290 	}
291 }
292 
vram_r(offs_t offset)293 uint8_t pcd_video_device::vram_r(offs_t offset)
294 {
295 	return m_vram[offset];
296 }
297 
vram_sw_w(uint8_t data)298 void pcd_video_device::vram_sw_w(uint8_t data)
299 {
300 	m_vram_sw = data & 1;
301 }
302 
t1_r()303 uint8_t pcd_video_device::t1_r()
304 {
305 	return m_t1;
306 }
307 
p1_r()308 uint8_t pcd_video_device::p1_r()
309 {
310 	uint8_t data = (m_mouse_btn->read() & 0x30) | 0x80; // char ram/rom jumper?
311 	data |= (m_mouse.xa != CLEAR_LINE ? 0 : 1);
312 	data |= (m_mouse.xb != CLEAR_LINE ? 0 : 2);
313 	data |= (m_mouse.ya != CLEAR_LINE ? 0 : 4);
314 	data |= (m_mouse.yb != CLEAR_LINE ? 0 : 8);
315 
316 	return data;
317 }
318 
p2_w(uint8_t data)319 void pcd_video_device::p2_w(uint8_t data)
320 {
321 	m_p2 = data;
322 	m_pic2->ir7_w((data & 0x80) ? CLEAR_LINE : ASSERT_LINE);
323 }
324 
term_r(offs_t offset)325 uint8_t pcx_video_device::term_r(offs_t offset)
326 {
327 	switch(offset)
328 	{
329 		case 0:
330 			m_pic2->ir0_w(CLEAR_LINE);
331 			m_term_stat &= ~2;
332 			return m_term_key;
333 		case 1:
334 			m_pic2->ir0_w(CLEAR_LINE);
335 			return m_term_stat >> 1;
336 	}
337 	return 0xff;
338 }
339 
term_w(offs_t offset,uint8_t data)340 void pcx_video_device::term_w(offs_t offset, uint8_t data)
341 {
342 	if(!offset)
343 	{
344 		m_mcu->set_input_line(MCS51_INT1_LINE, ASSERT_LINE);
345 		m_term_char = data;
346 		m_term_stat |= 4;
347 	}
348 }
349 
term_mcu_r(offs_t offset)350 uint8_t pcx_video_device::term_mcu_r(offs_t offset)
351 {
352 	switch(offset)
353 	{
354 		case 0:
355 			m_mcu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE);
356 			m_pic2->ir0_w(ASSERT_LINE);
357 			m_term_stat &= ~4;
358 			return m_term_char;
359 		case 1:
360 			return m_term_stat;
361 	}
362 	return 0;
363 }
364 
term_mcu_w(offs_t offset,uint8_t data)365 void pcx_video_device::term_mcu_w(offs_t offset, uint8_t data)
366 {
367 	if(!offset)
368 	{
369 		m_term_key = data;
370 		m_pic2->ir0_w(ASSERT_LINE);
371 		m_term_stat |= 2;
372 	}
373 }
374 
detect_r()375 uint8_t pcdx_video_device::detect_r()
376 {
377 	return 0;
378 }
379 
detect_w(uint8_t data)380 void pcdx_video_device::detect_w(uint8_t data)
381 {
382 }
383 
unk_r()384 uint8_t pcx_video_device::unk_r()
385 {
386 	return 0x80;
387 }
388 
p1_w(uint8_t data)389 void pcx_video_device::p1_w(uint8_t data)
390 {
391 	m_p1 = data;
392 }
393 
device_start()394 void pcd_video_device::device_start()
395 {
396 	m_maincpu->space(AS_IO).install_readwrite_handler(0xfb00, 0xfb01, read8smo_delegate(*this, FUNC(pcdx_video_device::detect_r)), write8smo_delegate(*this, FUNC(pcdx_video_device::detect_w)), 0xff00);
397 	m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xf0000, 0xf7fff, read8sm_delegate(*this, FUNC(pcd_video_device::vram_r)), write8sm_delegate(*this, FUNC(pcd_video_device::vram_w)), 0xffff);
398 	set_gfx(0, std::make_unique<gfx_element>(&palette(), pcd_charlayout, &m_charram[0], 0, 1, 0));
399 }
400 
device_reset()401 void pcd_video_device::device_reset()
402 {
403 	m_mouse.phase = 0;
404 	m_mouse.xa = m_mouse.xb = ASSERT_LINE;
405 	m_mouse.ya = m_mouse.yb = ASSERT_LINE;
406 	m_mouse.x = m_mouse.y = 0;
407 	m_mouse.prev_x = m_mouse.prev_y = 0;
408 	m_vram_sw = 1;
409 	m_t1 = CLEAR_LINE;
410 }
411 
map(address_map & map)412 void pcd_video_device::map(address_map &map)
413 {
414 	map(0x00, 0x0f).w(m_crtc, FUNC(scn2674_device::write)).umask16(0x00ff);
415 	map(0x00, 0x0f).r(m_crtc, FUNC(scn2674_device::read)).umask16(0xff00);
416 	map(0x20, 0x20).w(FUNC(pcd_video_device::vram_sw_w));
417 	map(0x30, 0x33).rw("graphics", FUNC(i8741a_device::upi41_master_r), FUNC(i8741a_device::upi41_master_w)).umask16(0x00ff);
418 }
419 
device_start()420 void pcx_video_device::device_start()
421 {
422 	m_maincpu->space(AS_IO).install_readwrite_handler(0xfb00, 0xfb01, read8smo_delegate(*this, FUNC(pcdx_video_device::detect_r)), write8smo_delegate(*this, FUNC(pcdx_video_device::detect_w)), 0x00ff);
423 	m_txd_handler.resolve_safe();
424 
425 	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
426 	set_rate(600);
427 
428 	decode_gfx(gfx_pcx);
429 }
430 
device_reset()431 void pcx_video_device::device_reset()
432 {
433 	m_term_key = 0;
434 	m_term_stat = 0;
435 	transmit_register_reset();
436 	receive_register_reset();
437 
438 	m_txd_handler(1);
439 }
440 
map(address_map & map)441 void pcx_video_device::map(address_map &map)
442 {
443 	map(0x0, 0xf).rw(FUNC(pcx_video_device::term_r), FUNC(pcx_video_device::term_w));
444 }
445 
rx_callback()446 uint8_t pcx_video_device::rx_callback()
447 {
448 	return get_received_char();
449 }
450 
tx_callback(uint8_t data)451 void pcx_video_device::tx_callback(uint8_t data)
452 {
453 	transmit_register_setup(data);
454 }
455 
tra_callback()456 void pcx_video_device::tra_callback()
457 {
458 	m_txd_handler(transmit_register_get_data_bit());
459 }
460 
rcv_complete()461 void pcx_video_device::rcv_complete()
462 {
463 	receive_register_extract();
464 	m_mcu->set_input_line(MCS51_RX_LINE, ASSERT_LINE);
465 	m_mcu->set_input_line(MCS51_RX_LINE, CLEAR_LINE);
466 }
467