1 // license:BSD-3-Clause
2 // copyright-holders:R. Belmont, Carl
3 /***************************************************************************
4 
5   TeleVideo 990/995 terminal
6 
7   Driver by Carl and R. Belmont
8   Thanks to Al Kossow.
9 
10   H/W:
11   68000-P16 CPU (clock unknown, above 10 MHz it outruns the AT keyboard controller)
12   16C452 dual 16450 (PC/AT standard) UART + PC-compatible Centronics (integrated into
13          ASIC on 995)
14   AMI MEGA-KBD-H-Q PS/2 keyboard interface on 990, PS/2 8042 on 995
15   Televideo ASIC marked "134446-00 TVI1111-0 427"
16   3x AS7C256 (32K x 8 SRAM)
17 
18   IRQs:
19   2 = PS/2 keyboard
20   3 = Centronics
21   4 = UART 1
22   5 = UART 0
23   6 = VBL (9003b is status, write 3 to 9003b to reset
24 
25   Video modes include 80 or 132 wide by 24, 25, 42, 43, 48, or 49 lines high plus an
26                       optional status bar
27   Modes include TeleVideo 990, 950, and 955, Wyse WY-60, WY-150/120/50+/50, ANSI,
28                       DEC VT320/220, VT100/52, SCO Console, and PC TERM.
29 
30 ****************************************************************************/
31 
32 #include "emu.h"
33 #include "bus/rs232/rs232.h"
34 #include "cpu/m68000/m68000.h"
35 #include "machine/8042kbdc.h"
36 #include "machine/ins8250.h"
37 #include "machine/nvram.h"
38 #include "machine/pc_lpt.h"
39 #include "sound/beep.h"
40 #include "emupal.h"
41 #include "screen.h"
42 #include "speaker.h"
43 
44 
45 #define RS232A_TAG      "rs232a"
46 #define RS232B_TAG      "rs232b"
47 #define LPT_TAG         "lpt"
48 
49 class tv990_state : public driver_device
50 {
51 public:
tv990_state(const machine_config & mconfig,device_type type,const char * tag)52 	tv990_state(const machine_config &mconfig, device_type type, const char *tag) :
53 		driver_device(mconfig, type, tag),
54 		m_maincpu(*this, "maincpu"),
55 		m_vram(*this, "vram"),
56 		m_fontram(*this, "fontram"),
57 		m_uart(*this, "ns16450_%u", 0U),
58 		m_screen(*this, "screen"),
59 		m_kbdc(*this, "pc_kbdc"),
60 		m_palette(*this, "palette"),
61 		m_beep(*this, "beep")
62 	{
63 	}
64 
65 	void tv990(machine_config &config);
66 
67 	DECLARE_INPUT_CHANGED_MEMBER(color);
68 
69 private:
70 	required_device<m68000_device> m_maincpu;
71 	required_shared_ptr<uint16_t> m_vram;
72 	required_shared_ptr<uint16_t> m_fontram;
73 	required_device_array<ns16450_device, 2> m_uart;
74 	required_device<screen_device> m_screen;
75 	required_device<kbdc8042_device> m_kbdc;
76 	required_device<palette_device> m_palette;
77 	required_device<beep_device> m_beep;
78 
79 	virtual void machine_reset() override;
80 	virtual void machine_start() override;
81 	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
82 	virtual void device_post_load() override;
83 
84 	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
85 
86 	uint16_t tvi1111_r(offs_t offset);
87 	void tvi1111_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
88 	uint8_t kbdc_r(offs_t offset);
89 	void kbdc_w(offs_t offset, uint8_t data);
90 
91 	DECLARE_WRITE_LINE_MEMBER(uart0_irq);
92 	DECLARE_WRITE_LINE_MEMBER(uart1_irq);
93 	DECLARE_WRITE_LINE_MEMBER(lpt_irq);
94 	DECLARE_WRITE_LINE_MEMBER(vblank_irq);
95 
96 	void tv990_mem(address_map &map);
97 
98 	uint16_t tvi1111_regs[(0x100/2)+2];
99 	emu_timer *m_rowtimer;
100 	int m_rowh, m_width, m_height;
101 };
102 
WRITE_LINE_MEMBER(tv990_state::vblank_irq)103 WRITE_LINE_MEMBER(tv990_state::vblank_irq)
104 {
105 	if (state)
106 	{
107 		m_rowtimer->adjust(m_screen->time_until_pos(m_rowh));
108 		m_maincpu->set_input_line(M68K_IRQ_6, ASSERT_LINE);
109 		tvi1111_regs[0x1d] |= 4;
110 	}
111 }
112 
machine_start()113 void tv990_state::machine_start()
114 {
115 	m_rowtimer = timer_alloc();
116 
117 	save_item(NAME(tvi1111_regs));
118 	save_item(NAME(m_rowh));
119 	save_item(NAME(m_width));
120 	save_item(NAME(m_height));
121 }
122 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)123 void tv990_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
124 {
125 	m_rowtimer->adjust(m_screen->time_until_pos(m_screen->vpos() + m_rowh));
126 	m_maincpu->set_input_line(M68K_IRQ_6, ASSERT_LINE);
127 	m_screen->update_now();
128 }
129 
WRITE_LINE_MEMBER(tv990_state::uart0_irq)130 WRITE_LINE_MEMBER(tv990_state::uart0_irq)
131 {
132 	m_maincpu->set_input_line(M68K_IRQ_5, state);
133 }
134 
WRITE_LINE_MEMBER(tv990_state::uart1_irq)135 WRITE_LINE_MEMBER(tv990_state::uart1_irq)
136 {
137 	m_maincpu->set_input_line(M68K_IRQ_4, state);
138 }
139 
WRITE_LINE_MEMBER(tv990_state::lpt_irq)140 WRITE_LINE_MEMBER(tv990_state::lpt_irq)
141 {
142 	m_maincpu->set_input_line(M68K_IRQ_3, state);
143 }
144 
tvi1111_r(offs_t offset)145 uint16_t tv990_state::tvi1111_r(offs_t offset)
146 {
147 	if (offset == (0x32/2))
148 	{
149 		tvi1111_regs[offset] |= 8;  // loop at 109ca wants this set
150 	}
151 	else if(offset == 0x1d)
152 	{
153 		m_maincpu->set_input_line(M68K_IRQ_6, CLEAR_LINE);
154 	}
155 
156 	return tvi1111_regs[offset];
157 }
158 
tvi1111_w(offs_t offset,uint16_t data,uint16_t mem_mask)159 void tv990_state::tvi1111_w(offs_t offset, uint16_t data, uint16_t mem_mask)
160 {
161 #if 0
162 	//if ((offset != 0x50) && (offset != 0x68) && (offset != 0x1d) && (offset != 0x1e) && (offset != 0x17) && (offset != 0x1c))
163 	{
164 		if (mem_mask == 0x00ff)
165 		{
166 			printf("%x (%d) to ASIC @ %x (mask %04x)\n", data & 0xff, data & 0xff, offset, mem_mask);
167 		}
168 		else if (mem_mask == 0xff00)
169 		{
170 			printf("%x (%d) to ASIC @ %x (mask %04x)\n", data & 0xff, data & 0xff, offset, mem_mask);
171 		}
172 		else
173 		{
174 			printf("%x (%d) to ASIC @ %x (mask %04x)\n", data, data, offset, mem_mask);
175 		}
176 	}
177 #endif
178 	COMBINE_DATA(&tvi1111_regs[offset]);
179 	if((offset == 0x1c) || (offset == 0x10) || (offset == 0x9) || (offset == 0xa))
180 	{
181 		m_width = BIT(tvi1111_regs[0x1c], 11) ? 132 : 80;
182 		m_rowh = (tvi1111_regs[0x10] & 0xff) + 1;
183 		if(!m_rowh)
184 			m_rowh = 16;
185 		m_height = (tvi1111_regs[0xa] - tvi1111_regs[0x9]) / m_rowh;
186 		// m_height can be 0 or -1 while machine is starting, leading to a crash on a debug build, so we sanitise it.
187 		if(m_height < 8 || m_height > 99)
188 			m_height = 0x1a;
189 		m_screen->set_visible_area(0, m_width * 16 - 1, 0, m_height * m_rowh - 1);
190 	}
191 	if(offset == 0x17)
192 		m_beep->set_state(tvi1111_regs[0x17] & 4 ? ASSERT_LINE : CLEAR_LINE);
193 }
194 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)195 uint32_t tv990_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
196 {
197 	uint16_t const *const vram = (uint16_t *)m_vram.target();
198 	uint8_t const *const fontram = (uint8_t *)m_fontram.target();
199 	int const miny = cliprect.min_y / m_rowh;
200 	int const maxy = cliprect.max_y / m_rowh;
201 
202 	bitmap.fill(0, cliprect);
203 
204 	for (int y = miny; y <= maxy; y++)
205 	{
206 		for(int i = 7; i >= 0; i--)
207 		{
208 			if(!BIT(tvi1111_regs[0x1f], i))
209 				continue;
210 
211 			int const starty = tvi1111_regs[i + 0x40] >> 8;
212 			int const endy = tvi1111_regs[i + 0x40] & 0xff;
213 			if((y < starty) || (y >= endy))
214 				continue;
215 
216 			uint16_t const row_offset = tvi1111_regs[i + 0x50];
217 			uint16_t const *curchar = &vram[row_offset];
218 			int minx = tvi1111_regs[i + 0x30] >> 8;
219 			int maxx = tvi1111_regs[i + 0x30] & 0xff;
220 
221 			if(maxx > m_width)
222 				maxx = m_width;
223 
224 			uint16_t const cursor_x = tvi1111_regs[0x16] - row_offset;
225 
226 			for (int x = minx; x < maxx; x++)
227 			{
228 				uint8_t chr = curchar[x - minx] >> 8;
229 				uint8_t attr = curchar[x - minx] & 0xff;
230 				if((attr & 2) && (m_screen->frame_number() & 32)) // blink rate?
231 					continue;
232 
233 				uint8_t const *fontptr = &fontram[(chr + (attr & 0x40 ? 256 : 0)) * 64];
234 
235 				if (BIT(tvi1111_regs[0x1b], 0) && x == cursor_x)
236 				{
237 					uint8_t attrchg;
238 					if(tvi1111_regs[0x15] & 0xff00) // what does this really mean? it looks like a mask but that doesn't work in 8line char mode
239 						attrchg = 8;
240 					else
241 						attrchg = 4;
242 					if(!BIT(tvi1111_regs[0x1b], 1))
243 						attr ^= attrchg;
244 					else if(m_screen->frame_number() & 32)
245 						attr ^= attrchg;
246 				}
247 
248 				uint32_t palette[2];
249 				if (attr & 0x4) // inverse video?
250 				{
251 					palette[1] = m_palette->pen(0);
252 					palette[0] = (attr & 0x10) ? m_palette->pen(1) : m_palette->pen(2);
253 				}
254 				else
255 				{
256 					palette[0] = m_palette->pen(0);
257 					palette[1] = (attr & 0x10) ? m_palette->pen(1) : m_palette->pen(2);
258 				}
259 
260 				for (int chary = 0; chary < m_rowh; chary++)
261 				{
262 					uint32_t *scanline = &bitmap.pix((y*m_rowh)+chary, (x*16));
263 
264 					uint8_t pixels = *fontptr++;
265 					uint8_t pixels2 = *fontptr++;
266 					if((attr & 0x8) && (chary == m_rowh - 1))
267 					{
268 						pixels = 0xff;
269 						pixels2 = 0xff;
270 					}
271 
272 					*scanline++ = palette[BIT(pixels, 7)];
273 					*scanline++ = palette[BIT(pixels2, 7)];
274 					*scanline++ = palette[BIT(pixels, 6)];
275 					*scanline++ = palette[BIT(pixels2, 6)];
276 					*scanline++ = palette[BIT(pixels, 5)];
277 					*scanline++ = palette[BIT(pixels2, 5)];
278 					*scanline++ = palette[BIT(pixels, 4)];
279 					*scanline++ = palette[BIT(pixels2, 4)];
280 					*scanline++ = palette[BIT(pixels, 3)];
281 					*scanline++ = palette[BIT(pixels2, 3)];
282 					*scanline++ = palette[BIT(pixels, 2)];
283 					*scanline++ = palette[BIT(pixels2, 2)];
284 					*scanline++ = palette[BIT(pixels, 1)];
285 					*scanline++ = palette[BIT(pixels2, 1)];
286 					*scanline++ = palette[BIT(pixels, 0)];
287 					*scanline++ = palette[BIT(pixels2, 0)];
288 				}
289 			}
290 		}
291 	}
292 
293 	return 0;
294 }
295 
kbdc_r(offs_t offset)296 uint8_t tv990_state::kbdc_r(offs_t offset)
297 {
298 	if(offset)
299 		return m_kbdc->data_r(4);
300 	else
301 		return m_kbdc->data_r(0);
302 }
303 
kbdc_w(offs_t offset,uint8_t data)304 void tv990_state::kbdc_w(offs_t offset, uint8_t data)
305 {
306 	if(offset)
307 		m_kbdc->data_w(4, data);
308 	else
309 		m_kbdc->data_w(0, data);
310 }
311 
tv990_mem(address_map & map)312 void tv990_state::tv990_mem(address_map &map)
313 {
314 	map(0x000000, 0x03ffff).rom().region("maincpu", 0);
315 	map(0x060000, 0x06ffff).ram().share("vram"); // character/attribute RAM
316 	map(0x080000, 0x087fff).ram().share("fontram"); // font RAM
317 	map(0x090000, 0x0900ff).rw(FUNC(tv990_state::tvi1111_r), FUNC(tv990_state::tvi1111_w));
318 	map(0x0a0000, 0x0a000f).rw(m_uart[0], FUNC(ns16450_device::ins8250_r), FUNC(ns16450_device::ins8250_w)).umask16(0x00ff);
319 	map(0x0a0010, 0x0a001f).rw(m_uart[1], FUNC(ns16450_device::ins8250_r), FUNC(ns16450_device::ins8250_w)).umask16(0x00ff);
320 	map(0x0a0028, 0x0a002d).rw(LPT_TAG, FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write)).umask16(0x00ff);
321 	map(0x0b0000, 0x0b0003).rw(FUNC(tv990_state::kbdc_r), FUNC(tv990_state::kbdc_w)).umask16(0x00ff);
322 	map(0x0c0000, 0x0c7fff).ram().share("nvram");// work RAM
323 }
324 
325 /* Input ports */
326 static INPUT_PORTS_START( tv990 )
327 	PORT_START("Screen")
328 	PORT_CONFNAME( 0x30, 0x00, "Color") PORT_CHANGED_MEMBER(DEVICE_SELF, tv990_state, color, 0)
329 	PORT_CONFSETTING(    0x00, "Green")
330 	PORT_CONFSETTING(    0x10, "Amber")
331 	PORT_CONFSETTING(    0x20, "White")
332 INPUT_PORTS_END
333 
INPUT_CHANGED_MEMBER(tv990_state::color)334 INPUT_CHANGED_MEMBER(tv990_state::color)
335 {
336 	rgb_t color;
337 	if(newval == oldval)
338 		return;
339 
340 	switch(newval)
341 	{
342 		case 0:
343 		default:
344 			color = rgb_t::green();
345 			break;
346 		case 1:
347 			color = rgb_t::amber();
348 			break;
349 		case 2:
350 			color = rgb_t::white();
351 			break;
352 	}
353 	m_screen->set_color(color);
354 }
355 
machine_reset()356 void tv990_state::machine_reset()
357 {
358 	m_rowtimer->adjust(m_screen->time_until_pos(0));
359 
360 	memset(tvi1111_regs, 0, sizeof(tvi1111_regs));
361 	m_rowh = 16;
362 	m_width = 80;
363 	m_height = 50;
364 }
365 
device_post_load()366 void tv990_state::device_post_load()
367 {
368 	m_screen->set_visible_area(0, m_width * 16 - 1, 0, m_height * m_rowh - 1);
369 }
370 
tv990(machine_config & config)371 void tv990_state::tv990(machine_config &config)
372 {
373 	/* basic machine hardware */
374 	M68000(config, m_maincpu, 14967500);   // verified (59.86992/4)
375 	m_maincpu->set_addrmap(AS_PROGRAM, &tv990_state::tv990_mem);
376 
377 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
378 	m_screen->set_color(rgb_t::green());
379 	m_screen->set_screen_update(FUNC(tv990_state::screen_update));
380 	m_screen->set_size(132*16, 50*16);
381 	m_screen->set_visarea(0, (80*16)-1, 0, (50*16)-1);
382 	m_screen->set_refresh_hz(60);
383 	m_screen->screen_vblank().set(FUNC(tv990_state::vblank_irq));
384 
385 	PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
386 
387 	NS16450(config, m_uart[0], 3.6864_MHz_XTAL);
388 	m_uart[0]->out_dtr_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_dtr));
389 	m_uart[0]->out_rts_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_rts));
390 	m_uart[0]->out_tx_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd));
391 	m_uart[0]->out_int_callback().set(FUNC(tv990_state::uart0_irq));
392 
393 	NS16450(config, m_uart[1], 3.6864_MHz_XTAL);
394 	m_uart[1]->out_dtr_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_dtr));
395 	m_uart[1]->out_rts_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_rts));
396 	m_uart[1]->out_tx_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd));
397 	m_uart[1]->out_int_callback().set(FUNC(tv990_state::uart1_irq));
398 
399 	pc_lpt_device &lpt(PC_LPT(config, LPT_TAG));
400 	lpt.irq_handler().set(FUNC(tv990_state::lpt_irq));
401 
402 	rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr));
403 	rs232a.rxd_handler().set(m_uart[0], FUNC(ns16450_device::rx_w));
404 	rs232a.dcd_handler().set(m_uart[0], FUNC(ns16450_device::dcd_w));
405 	rs232a.cts_handler().set(m_uart[0], FUNC(ns16450_device::cts_w));
406 
407 	rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr));
408 	rs232b.rxd_handler().set(m_uart[1], FUNC(ns16450_device::rx_w));
409 	rs232b.dcd_handler().set(m_uart[1], FUNC(ns16450_device::dcd_w));
410 	rs232b.cts_handler().set(m_uart[1], FUNC(ns16450_device::cts_w));
411 
412 	KBDC8042(config, m_kbdc);
413 	m_kbdc->set_keyboard_type(kbdc8042_device::KBDC8042_STANDARD);
414 	m_kbdc->input_buffer_full_callback().set_inputline("maincpu", M68K_IRQ_2);
415 
416 	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
417 
418 	SPEAKER(config, "mono").front_center();
419 	BEEP(config, "beep", 1000).add_route(ALL_OUTPUTS, "mono", 1.0); //whats the freq?
420 }
421 
422 /* ROM definition */
423 ROM_START( tv990 )
424 	ROM_REGION( 0x40000, "maincpu", 0 )
425 	ROM_LOAD16_BYTE( "180003-89_u3.bin", 0x000000, 0x010000, CRC(0465fc55) SHA1(b8874ce54bf2bf4f77664194d2f23c0e4e6ccbe9) )
426 	ROM_LOAD16_BYTE( "180003-90_u4.bin", 0x000001, 0x010000, CRC(fad7d77d) SHA1(f1114a4a07c8b4ffa0323a2e7ce03d82a386f7d3) )
427 ROM_END
428 
429 ROM_START( tv995 )
430 	ROM_REGION( 0x40000, "maincpu", 0 )
431 	ROM_LOAD16_BYTE( "995-65_u3.bin", 0x000000, 0x020000, CRC(2d71b6fe) SHA1(a2a3406c19308eb9232db319ea8f151949b2ac74) )
432 	ROM_LOAD16_BYTE( "995-65_u4.bin", 0x000001, 0x020000, CRC(dc002af2) SHA1(9608e7a729c5ac0fc58f673eaf441d2f4f591ec6) )
433 ROM_END
434 
435 /* Driver */
436 COMP( 1992, tv990, 0, 0, tv990, tv990, tv990_state, empty_init, "TeleVideo", "TeleVideo 990",    MACHINE_SUPPORTS_SAVE )
437 COMP( 1994, tv995, 0, 0, tv990, tv990, tv990_state, empty_init, "TeleVideo", "TeleVideo 995-65", MACHINE_SUPPORTS_SAVE )
438