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