1 // license:BSD-3-Clause
2 // copyright-holders:Robbbert,Vas Crabb
3 /***************************************************************************
4 
5 Univac Terminals
6 
7 2009-05-25 Skeleton driver
8 
9 The terminals are models UTS10, UTS20, UTS30, UTS40, UTS50 and SVT1120.
10 
11 There were other terminals (Uniscope 100/200/300/400) and UTS60, but
12 they had different hardware. Uniscope models are believed to use the i8080,
13 and the UTS60 was a colour graphics terminal with a MC68000 and 2 floppy drives.
14 
15 The terminal has 2 screens selectable by the operator with the Fn + 1-2
16 buttons. Thus the user can have two sessions open at once, to different
17 mainframes or applications. The keyboard connected to the terminal with
18 a coiled cord and a 9-pin D-connector.
19 
20 Sound is a beeper.
21 
22 This driver is all guesswork; Unisys never released technical info
23 to customers. All parts on the PCBs have internal Unisys part numbers
24 instead of the manufacturer's numbers.
25 
26 Notes:
27 * Port $C6 probably controls serial loopback
28   - at a guess, bit 0 enables loopback on both channels
29 * The NVRAM is 4 bits wide on the LSBs, but (0x81) & 0x10 does something
30   - NVRAM nybbles are read/written on the LSBs of 64 ports 0x80 to 0xb4
31   - Nybbles are packed/unpacked into 32 bytes starting at 0xd7d7
32   - On boot it reads (0x81) & 0x10, and if set preserves 0xd831 to 0xd863
33   - This has to be some kind of warm boot detection, but how does it work?
34 
35 You can use a debug trick to get UTS10 to boot:
36 - When it loops at @0B33, pc = B35 and g
37 
38 How to create a FCC (field control code):
39 - Move the cursor to where you want the FCC to be
40 - Press FCC GEN
41 - Now you enter a sequence of 4 bytes
42 - 1. Video Attribute
43 - - Spacebar or N: Normal
44 - - L: Low intensity
45 - - O: Off
46 - - B: Blink (low-half)
47 - - 1: Rev-video/Normal
48 - - 2: Rev-video/half-intensity
49 - - 3: Rev-video/blink: normal-half
50 - - 4: Rev-video/blink: low
51 - 2. Tab-stop
52 - - Spacebar or S: No tab-stop
53 - - T: Tab-stop
54 - - 6: Tab-stop protected
55 - - 7: No tab protected
56 - 3. Data-entry control
57 - - Spacebar or U: Unprotected
58 - - P: Protected
59 - - A: Alpha only
60 - - N: Numeric only
61 - 4. Justified
62 - - Spacebar: Normal
63 - - R: Right-justified
64 - Press Spacebar to enable the new FCC and exit back to normal.
65 
66 Control-page parameters. These vary depending on the terminal and feature set. Press FCTN and CTRL PAGE keys together.
67 You get a protected area covering the first 2 lines where you can configure the terminal. Settings are saved in the NVRAM.
68 Depending on the setting, it may take effect immediately (after exiting the control page), or after a reboot.
69 Entries may be in upper or lower case.
70 UC/NO : Upper and lower case can be entered
71 UC/YS : Lower case is automatically folded to upper case.
72 AB/LI : Alternate brightness is Low Intensity
73 AB/RV : Alternate brightness is Reverse Video
74 AB/NI : Alternate brightness is Normal Intensity
75 IL/RV : Indicator Line is Reverse Video
76 IL/NI : Indicator Line is Normal Intensity
77 KK/ON : Keyclick on
78 KK/OF : Keyclick off
79 SP/NS : Non-destructive spacebar (works like right-arrow)
80 SP/DS : Destructive spacebar
81 VO/01 : Video off after 1 minute (a blank screen saver)
82 VO/04 : Video off after 4 minutes
83 VO/16 : Video off after 16 minutes
84 VO/64 : Video off after 64 minutes
85 CC/ON : Control characters show
86 CC/OF : Control characters off (look like a space)
87 CS/LO : Cursor repeat slow
88 CS/HI : Cursor repeat fast
89 RI/xx : Set the RID (generally 21-2F)
90 SI/xx : Set the SID (generally 51-7F)
91 After entering the characters, press FCTN and CTRL PAGE keys again to save the setting.
92 
93 
94 ****************************************************************************/
95 
96 #include "emu.h"
97 #include "bus/rs232/rs232.h"
98 #include "bus/uts_kbd/uts_kbd.h"
99 #include "cpu/z80/z80.h"
100 #include "machine/74259.h"
101 #include "machine/z80daisy.h"
102 #include "machine/clock.h"
103 #include "machine/nvram.h"
104 #include "machine/z80ctc.h"
105 #include "machine/z80sio.h"
106 #include "sound/spkrdev.h"
107 #include "video/dp8350.h"
108 #include "emupal.h"
109 #include "screen.h"
110 #include "speaker.h"
111 
112 #define LOG_GENERAL (1U << 0)
113 #define LOG_PARITY  (1U << 1)
114 #define LOG_NVRAM   (1U << 2)
115 
116 //#define VERBOSE (LOG_GENERAL | LOG_PARITY | LOG_NVRAM)
117 #include "logmacro.h"
118 
119 #define LOGPARITY(...)  LOGMASKED(LOG_PARITY, __VA_ARGS__)
120 #define LOGNVRAM(...)   LOGMASKED(LOG_NVRAM, __VA_ARGS__)
121 
122 
123 class univac_state : public driver_device
124 {
125 public:
univac_state(const machine_config & mconfig,device_type type,const char * tag)126 	univac_state(const machine_config &mconfig, device_type type, const char *tag)
127 		: driver_device(mconfig, type, tag)
128 		, m_maincpu(*this, "maincpu")
129 		, m_nvram(*this, "nvram")
130 		, m_ctc(*this, "ctc")
131 		, m_keybclk(*this, "keybclk")
132 		, m_sio(*this, "sio")
133 		, m_alarm(*this, "alarm")
134 		, m_screen(*this, "screen")
135 		, m_palette(*this, "palette")
136 		, m_keyboard(*this, "keyboard")
137 		, m_printer(*this, "printer")
138 		, m_p_chargen(*this, "chargen")
139 		, m_p_videoram(*this, "videoram")
140 		, m_p_nvram(*this, "nvram")
141 		, m_bank_mask(0)
142 		, m_parity_poison(false)
143 		, m_display_enable(false)
144 		, m_framecnt(0)
145 		, m_nvram_protect(false)
146 		, m_alarm_enable(false)
147 		, m_alarm_toggle(false)
148 		, m_loopback_control(false)
149 		, m_comm_rxd(true)
150 		, m_sio_txda(true)
151 		, m_aux_rxd(true)
152 		, m_sio_txdb(true)
153 		, m_sio_rtsb(true)
154 		, m_aux_dsr(true)
155 		, m_sio_wrdyb(true)
156 	{ }
157 
158 	void uts10(machine_config &config);
159 	void uts20(machine_config &config);
160 
161 private:
162 	u8 ram_r(offs_t offset);
163 	u8 bank_r(offs_t offset);
164 	void ram_w(offs_t offset, u8 data);
165 	void bank_w(offs_t offset, u8 data);
166 	void nvram_w(offs_t offset, u8 data);
167 
168 	DECLARE_WRITE_LINE_MEMBER(nvram_protect_w);
169 	DECLARE_WRITE_LINE_MEMBER(select_disp_w);
170 	DECLARE_WRITE_LINE_MEMBER(ram_control_w);
171 	DECLARE_WRITE_LINE_MEMBER(parity_poison_w);
172 	DECLARE_WRITE_LINE_MEMBER(display_enable_w);
173 	DECLARE_WRITE_LINE_MEMBER(alarm_enable_w);
174 	DECLARE_WRITE_LINE_MEMBER(sio_loopback_w);
175 	DECLARE_WRITE_LINE_MEMBER(sio_txda_w);
176 	DECLARE_WRITE_LINE_MEMBER(sio_txdb_w);
177 	DECLARE_WRITE_LINE_MEMBER(aux_rxd_w);
178 	DECLARE_WRITE_LINE_MEMBER(sio_rtsb_w);
179 	DECLARE_WRITE_LINE_MEMBER(sio_wrdyb_w);
180 	DECLARE_WRITE_LINE_MEMBER(aux_dsr_w);
181 	DECLARE_WRITE_LINE_MEMBER(loopback_rxcb_w);
182 	DECLARE_WRITE_LINE_MEMBER(porte6_w);
183 
184 	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
185 
186 	void io_map(address_map &map);
187 	void mem_map(address_map &map);
188 	void uts10_io_map(address_map &map);
189 	void uts10_map(address_map &map);
190 	virtual void machine_start() override;
191 
192 	required_device<z80_device>     m_maincpu;
193 	required_device<nvram_device>   m_nvram;
194 	required_device<z80ctc_device>  m_ctc;
195 	optional_device<clock_device>   m_keybclk;
196 	required_device<z80sio_device>  m_sio;
197 	required_device<speaker_sound_device> m_alarm;
198 	required_device<screen_device>  m_screen;
199 	required_device<palette_device> m_palette;
200 
201 	required_device<uts_keyboard_port_device> m_keyboard;
202 	required_device<rs232_port_device> m_printer;
203 
204 	required_region_ptr<u8> m_p_chargen;
205 	required_shared_ptr<u8> m_p_videoram;
206 	required_shared_ptr<u8> m_p_nvram;
207 	std::unique_ptr<u8 []>  m_p_parity;
208 
209 	u16 m_disp_mask;
210 	u16 m_bank_mask;
211 	bool m_parity_poison;
212 	bool m_display_enable;
213 	u8  m_framecnt;
214 	bool m_nvram_protect;
215 
216 	bool m_alarm_enable;
217 	bool m_alarm_toggle;
218 
219 	bool m_loopback_control;
220 	bool m_comm_rxd;
221 	bool m_sio_txda;
222 	bool m_aux_rxd;
223 	bool m_sio_txdb;
224 	bool m_sio_rtsb;
225 	bool m_aux_dsr;
226 	bool m_sio_wrdyb;
227 };
228 
229 
230 
ram_r(offs_t offset)231 u8 univac_state::ram_r(offs_t offset)
232 {
233 	if (BIT(m_p_parity[offset >> 3], offset & 0x07) && !machine().side_effects_disabled())
234 	{
235 		LOGPARITY("parity check failed offset = %04X\n", offset);
236 		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
237 	}
238 	return m_p_videoram[offset];
239 }
240 
bank_r(offs_t offset)241 u8 univac_state::bank_r(offs_t offset)
242 {
243 	return ram_r(offset ^ m_bank_mask);
244 }
245 
ram_w(offs_t offset,u8 data)246 void univac_state::ram_w(offs_t offset, u8 data)
247 {
248 	if (m_parity_poison)
249 	{
250 		LOGPARITY("poison parity offset = %04X\n", offset);
251 		m_p_parity[offset >> 3] |= u8(1) << (offset & 0x07);
252 	}
253 	else
254 	{
255 		m_p_parity[offset >> 3] &= ~(u8(1) << (offset & 0x07));
256 	}
257 	m_p_videoram[offset] = data;
258 }
259 
bank_w(offs_t offset,u8 data)260 void univac_state::bank_w(offs_t offset, u8 data)
261 {
262 	ram_w(offset ^ m_bank_mask, data);
263 }
264 
nvram_w(offs_t offset,u8 data)265 void univac_state::nvram_w(offs_t offset, u8 data)
266 {
267 	// NVRAM is four bits wide, accessed in the low nybble
268 	// It's simplest to hack it when writing to make the upper bits read back high on the open bus
269 	// (But is it all open bus? Bit 4 is specifically tested in a few places...)
270 	if (m_nvram_protect)
271 		LOGNVRAM("%s: NVRAM write suppressed (address %02X, data %02X)\n", machine().describe_context(), offset + 0x80, data);
272 	else
273 		m_p_nvram[offset] = data | 0xf0;
274 }
275 
WRITE_LINE_MEMBER(univac_state::nvram_protect_w)276 WRITE_LINE_MEMBER(univac_state::nvram_protect_w)
277 {
278 	// There seems to be some timing-based write protection related to the CTC's TRG0 input.
279 	// The present implementation is a crude approximation of a wild guess.
280 	if (state)
281 	{
282 		m_nvram_protect = m_screen->vpos() < 10;
283 
284 		if (m_alarm_enable)
285 		{
286 			m_alarm_toggle = !m_alarm_toggle;
287 			m_alarm->level_w(m_alarm_toggle);
288 		}
289 	}
290 }
291 
WRITE_LINE_MEMBER(univac_state::select_disp_w)292 WRITE_LINE_MEMBER(univac_state::select_disp_w)
293 {
294 	m_disp_mask = state ? 0x2000 : 0x0000;
295 }
296 
WRITE_LINE_MEMBER(univac_state::ram_control_w)297 WRITE_LINE_MEMBER(univac_state::ram_control_w)
298 {
299 	m_bank_mask = state ? 0x2000 : 0x0000;
300 }
301 
WRITE_LINE_MEMBER(univac_state::parity_poison_w)302 WRITE_LINE_MEMBER(univac_state::parity_poison_w)
303 {
304 	m_parity_poison = state;
305 }
306 
WRITE_LINE_MEMBER(univac_state::display_enable_w)307 WRITE_LINE_MEMBER(univac_state::display_enable_w)
308 {
309 	m_display_enable = state;
310 }
311 
WRITE_LINE_MEMBER(univac_state::alarm_enable_w)312 WRITE_LINE_MEMBER(univac_state::alarm_enable_w)
313 {
314 	m_alarm_enable = state;
315 	if (!state)
316 	{
317 		m_alarm_toggle = false;
318 		m_alarm->level_w(0);
319 	}
320 }
321 
WRITE_LINE_MEMBER(univac_state::sio_loopback_w)322 WRITE_LINE_MEMBER(univac_state::sio_loopback_w)
323 {
324 	if (state)
325 	{
326 		m_sio->rxa_w(m_sio_txda);
327 		m_sio->rxb_w(m_sio_txdb);
328 		m_sio->dcdb_w(m_sio_wrdyb);
329 		m_sio->ctsb_w(m_sio_wrdyb);
330 		m_sio->syncb_w(!m_sio_rtsb);
331 		m_printer->write_txd(1);
332 		m_printer->write_rts(1);
333 		m_keyboard->ready_w(0);
334 		if (m_keybclk.found())
335 			m_keybclk->set_clock_scale(0.0);
336 	}
337 	else
338 	{
339 		m_sio->rxa_w(m_comm_rxd);
340 		m_sio->rxb_w(m_aux_rxd);
341 		m_sio->dcdb_w(m_aux_dsr);
342 		m_sio->ctsb_w(m_aux_dsr); // likely ignored
343 		m_sio->syncb_w(1);
344 		m_printer->write_txd(m_sio_txdb);
345 		m_printer->write_rts(m_sio_rtsb);
346 		m_keyboard->ready_w(m_sio_wrdyb);
347 		if (m_keybclk.found())
348 			m_keybclk->set_clock_scale(1.0);
349 	}
350 
351 	m_loopback_control = state;
352 }
353 
WRITE_LINE_MEMBER(univac_state::sio_txda_w)354 WRITE_LINE_MEMBER(univac_state::sio_txda_w)
355 {
356 	m_sio_txda = state;
357 	if (m_loopback_control)
358 		m_sio->rxa_w(state);
359 }
360 
WRITE_LINE_MEMBER(univac_state::sio_txdb_w)361 WRITE_LINE_MEMBER(univac_state::sio_txdb_w)
362 {
363 	m_sio_txdb = state;
364 	if (m_loopback_control)
365 		m_sio->rxb_w(state);
366 	else
367 		m_printer->write_txd(state);
368 }
369 
WRITE_LINE_MEMBER(univac_state::aux_rxd_w)370 WRITE_LINE_MEMBER(univac_state::aux_rxd_w)
371 {
372 	m_aux_rxd = state;
373 	if (!m_loopback_control)
374 		m_sio->rxb_w(state);
375 }
376 
WRITE_LINE_MEMBER(univac_state::sio_rtsb_w)377 WRITE_LINE_MEMBER(univac_state::sio_rtsb_w)
378 {
379 	m_sio_rtsb = state;
380 	if (m_loopback_control)
381 		m_sio->syncb_w(!state);
382 	else
383 		m_printer->write_rts(state);
384 }
385 
WRITE_LINE_MEMBER(univac_state::sio_wrdyb_w)386 WRITE_LINE_MEMBER(univac_state::sio_wrdyb_w)
387 {
388 	m_sio_wrdyb = state;
389 	if (m_loopback_control)
390 	{
391 		m_sio->dcdb_w(state);
392 		m_sio->ctsb_w(state);
393 	}
394 	else
395 		m_keyboard->ready_w(state);
396 }
397 
WRITE_LINE_MEMBER(univac_state::aux_dsr_w)398 WRITE_LINE_MEMBER(univac_state::aux_dsr_w)
399 {
400 	m_aux_dsr = state;
401 	if (!m_loopback_control)
402 	{
403 		m_sio->dcdb_w(state);
404 		m_sio->ctsb_w(state);
405 	}
406 }
407 
WRITE_LINE_MEMBER(univac_state::loopback_rxcb_w)408 WRITE_LINE_MEMBER(univac_state::loopback_rxcb_w)
409 {
410 	if (m_loopback_control)
411 		m_sio->rxcb_w(state);
412 }
413 
WRITE_LINE_MEMBER(univac_state::porte6_w)414 WRITE_LINE_MEMBER(univac_state::porte6_w)
415 {
416 	//m_beep->set_state(state); // not sure what belongs here, but it isn't the beeper
417 }
418 
419 
mem_map(address_map & map)420 void univac_state::mem_map(address_map &map)
421 {
422 	map.unmap_value_high();
423 	map(0x0000, 0x4fff).rom().region("roms", 0);
424 	map(0x8000, 0xbfff).rw(FUNC(univac_state::bank_r), FUNC(univac_state::bank_w));
425 	map(0xc000, 0xffff).rw(FUNC(univac_state::ram_r), FUNC(univac_state::ram_w)).share("videoram");
426 }
427 
uts10_map(address_map & map)428 void univac_state::uts10_map(address_map &map)
429 {
430 	map.unmap_value_high();
431 	map(0x0000, 0x4fff).rom().region("roms", 0);
432 	map(0x8000, 0x9fff).mirror(0x2000).rw(FUNC(univac_state::bank_r), FUNC(univac_state::bank_w));
433 	map(0xc000, 0xffff).rw(FUNC(univac_state::ram_r), FUNC(univac_state::ram_w)).share("videoram");
434 }
435 
uts10_io_map(address_map & map)436 void univac_state::uts10_io_map(address_map &map)
437 {
438 	map.global_mask(0xff);
439 	map.unmap_value_high();
440 	map(0x00, 0x03).rw(m_sio, FUNC(z80sio_device::cd_ba_r), FUNC(z80sio_device::cd_ba_w));
441 	map(0x20, 0x23).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
442 	map(0x60, 0x60).nopw(); // values written here may or may not matter
443 	map(0x80, 0xbf).ram().w(FUNC(univac_state::nvram_w)).share("nvram");
444 	map(0xc0, 0xc7).w("latch_c0", FUNC(ls259_device::write_d0));
445 	map(0xe0, 0xe7).w("latch_e0", FUNC(ls259_device::write_d0));
446 }
447 
io_map(address_map & map)448 void univac_state::io_map(address_map &map)
449 {
450 	uts10_io_map(map);
451 	map(0x40, 0x40).nopr(); // read only once, during self-test; result is discarded
452 	map(0x40, 0x47).w("latch_40", FUNC(ls259_device::write_d0));
453 }
454 
455 /* Input ports */
INPUT_PORTS_START(uts20)456 static INPUT_PORTS_START( uts20 )
457 INPUT_PORTS_END
458 
459 
460 void univac_state::machine_start()
461 {
462 	// D7DC and D7DD are checked for valid RID and SID (usually 21 and 51) if not valid then NVRAM gets initialised.
463 
464 	std::size_t const parity_bytes = (m_p_videoram.bytes() + 7) / 8;
465 	m_p_parity.reset(new u8[parity_bytes]);
466 	std::fill_n(m_p_parity.get(), parity_bytes, 0);
467 
468 	save_pointer(NAME(m_p_parity), parity_bytes);
469 	save_item(NAME(m_bank_mask));
470 	save_item(NAME(m_parity_poison));
471 	save_item(NAME(m_display_enable));
472 	save_item(NAME(m_framecnt));
473 	save_item(NAME(m_nvram_protect));
474 	save_item(NAME(m_alarm_enable));
475 	save_item(NAME(m_alarm_toggle));
476 	save_item(NAME(m_loopback_control));
477 	save_item(NAME(m_comm_rxd));
478 	save_item(NAME(m_sio_txda));
479 	save_item(NAME(m_aux_rxd));
480 	save_item(NAME(m_sio_txdb));
481 	save_item(NAME(m_sio_rtsb));
482 	save_item(NAME(m_aux_dsr));
483 	save_item(NAME(m_sio_wrdyb));
484 	save_item(NAME(m_disp_mask));
485 }
486 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)487 uint32_t univac_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
488 {
489 	if (!m_display_enable)
490 	{
491 		bitmap.fill(0, cliprect);
492 		return 0;
493 	}
494 
495 	const pen_t *pen = m_palette->pens();
496 
497 	uint16_t sy=0,ma=0;
498 
499 	m_framecnt++;
500 
501 	for (u8 y = 0; y < 25; y++)
502 	{
503 		for (u8 ra = 0; ra < 14; ra++)
504 		{
505 			uint32_t *p = &bitmap.pix(sy++);
506 
507 			for (uint16_t x = ma; x < ma + 80; x++)
508 			{
509 				u8 chr = ram_r(x ^ m_disp_mask);    // bit 7 = rv attribute (or dim, depending on control-page setting)
510 
511 				uint16_t gfx = m_p_chargen[((chr & 0x7f)<<4) | ra];
512 
513 				// chars 1C, 1D, 1F need special handling
514 				if ((chr >= 0x1c) && (chr <= 0x1f) && BIT(gfx, 7))
515 				{
516 					gfx &= 0x7f;
517 					if (m_framecnt & 16) // They also blink
518 						gfx = 0;
519 				}
520 
521 				// reverse-video attribute
522 				if (BIT(chr, 7))
523 					gfx = ~gfx;
524 
525 				/* Display a scanline of a character */
526 				for (int bit = 8; bit >= 0; bit--)
527 				{
528 					*p++ = pen[BIT(gfx, bit)];
529 				}
530 			}
531 		}
532 		ma += 80;
533 	}
534 	return 0;
535 }
536 
537 /* F4 Character Displayer */
538 static const gfx_layout charlayout =
539 {
540 	8, 14,                   /* 8 x 14 characters */
541 	128,                    /* 128 characters */
542 	1,                  /* 1 bits per pixel */
543 	{ 0 },                  /* no bitplanes */
544 	/* x offsets */
545 	{ 0, 1, 2, 3, 4, 5, 6, 7 },
546 	/* y offsets */
547 	{ 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 },
548 	8*16                    /* every char takes 16 bytes */
549 };
550 
551 static GFXDECODE_START( gfx_uts )
552 	GFXDECODE_ENTRY( "chargen", 0x0000, charlayout, 0, 1 )
553 GFXDECODE_END
554 
555 static const z80_daisy_config daisy_chain[] =
556 {
557 	{ "sio" },
558 	{ "ctc" },
559 	{ nullptr }
560 };
561 
562 // All frequencies confirmed
uts20(machine_config & config)563 void univac_state::uts20(machine_config &config)
564 {
565 	/* basic machine hardware */
566 	Z80(config, m_maincpu, 18.432_MHz_XTAL / 6); // 3.072 MHz
567 	m_maincpu->set_addrmap(AS_PROGRAM, &univac_state::mem_map);
568 	m_maincpu->set_addrmap(AS_IO, &univac_state::io_map);
569 	m_maincpu->set_daisy_config(daisy_chain);
570 
571 	ls259_device &latch_40(LS259(config, "latch_40")); // actual type and location unknown
572 	latch_40.q_out_cb<1>().set(FUNC(univac_state::select_disp_w));
573 	latch_40.q_out_cb<3>().set(FUNC(univac_state::ram_control_w));
574 
575 	ls259_device &latch_c0(LS259(config, "latch_c0")); // actual type and location unknown
576 	latch_c0.q_out_cb<0>().set(FUNC(univac_state::alarm_enable_w));
577 	latch_c0.q_out_cb<3>().set(FUNC(univac_state::display_enable_w));
578 	latch_c0.q_out_cb<4>().set(FUNC(univac_state::parity_poison_w));
579 	latch_c0.q_out_cb<6>().set(FUNC(univac_state::sio_loopback_w));
580 
581 	ls259_device &latch_e0(LS259(config, "latch_e0")); // actual type and location unknown
582 	//latch_e0.q_out_cb<2>().set(FUNC(univac_state::reverse_video_w));
583 	latch_e0.q_out_cb<5>().set("crtc", FUNC(dp835x_device::refresh_control)).invert();
584 	latch_e0.q_out_cb<6>().set(FUNC(univac_state::porte6_w));
585 
586 	/* video hardware */
587 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green());
588 	m_screen->set_screen_update(FUNC(univac_state::screen_update));
589 	PALETTE(config, m_palette, palette_device::MONOCHROME);
590 	GFXDECODE(config, "gfxdecode", m_palette, gfx_uts);
591 
592 	dp835x_device &crtc(DP835X_A(config, "crtc", 19'980'000));
593 	crtc.set_screen("screen");
594 	crtc.vblank_callback().set(m_ctc, FUNC(z80ctc_device::trg0));
595 	crtc.vblank_callback().append(m_ctc, FUNC(z80ctc_device::trg3));
596 
597 	NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
598 
599 	Z80CTC(config, m_ctc, 18.432_MHz_XTAL / 6);
600 	m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
601 	m_ctc->set_clk<1>(18.432_MHz_XTAL / 12);
602 	m_ctc->set_clk<2>(18.432_MHz_XTAL / 12);
603 	m_ctc->zc_callback<0>().set(FUNC(univac_state::nvram_protect_w));
604 	m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::txca_w));
605 	m_ctc->zc_callback<1>().append(m_sio, FUNC(z80sio_device::rxca_w));
606 	m_ctc->zc_callback<2>().set(m_sio, FUNC(z80sio_device::txcb_w));
607 	m_ctc->zc_callback<2>().append(FUNC(univac_state::loopback_rxcb_w));
608 
609 	CLOCK(config, m_keybclk, 18.432_MHz_XTAL / 60);
610 	m_keybclk->signal_handler().set(m_sio, FUNC(z80sio_device::rxcb_w));
611 
612 	Z80SIO(config, m_sio, 18.432_MHz_XTAL / 6);
613 	m_sio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
614 	m_sio->out_txda_callback().set(FUNC(univac_state::sio_txda_w));
615 	m_sio->out_txdb_callback().set(FUNC(univac_state::sio_txdb_w));
616 	m_sio->out_rtsb_callback().set(FUNC(univac_state::sio_rtsb_w));
617 	m_sio->out_wrdyb_callback().set(FUNC(univac_state::sio_wrdyb_w));
618 
619 	/* Sound */
620 	SPEAKER(config, "mono").front_center();
621 	SPEAKER_SOUND(config, m_alarm).add_route(ALL_OUTPUTS, "mono", 0.05);
622 
623 	UTS_KEYBOARD(config, m_keyboard, uts20_keyboards, "extw");
624 	m_keyboard->rxd_callback().set(FUNC(univac_state::aux_rxd_w));
625 
626 	RS232_PORT(config, m_printer, default_rs232_devices, nullptr);
627 	m_printer->dcd_handler().set(FUNC(univac_state::aux_dsr_w));
628 }
629 
uts10(machine_config & config)630 void univac_state::uts10(machine_config &config)
631 {
632 	uts20(config);
633 	m_maincpu->set_addrmap(AS_PROGRAM, &univac_state::uts10_map);
634 	m_maincpu->set_addrmap(AS_IO, &univac_state::uts10_io_map);
635 
636 	config.device_remove("keybclk");
637 	m_ctc->zc_callback<2>().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
638 
639 	config.device_remove("latch_40");
640 	subdevice<ls259_device>("latch_c0")->q_out_cb<6>().set_nop();
641 	subdevice<ls259_device>("latch_e0")->q_out_cb<7>().set(FUNC(univac_state::sio_loopback_w)).invert();
642 
643 	UTS_KEYBOARD(config.replace(), m_keyboard, uts10_keyboards, "extw");
644 	m_keyboard->rxd_callback().set(FUNC(univac_state::aux_rxd_w));
645 }
646 
647 
648 /* ROM definition */
649 ROM_START( uts10 )
650 	ROM_REGION( 0x5000, "roms", ROMREGION_ERASEFF )
651 	ROM_LOAD( "f3577_1.bin",  0x0000, 0x0800, CRC(f7d47484) SHA1(84c01d054df19e8da44c242a67d97f643bdabc4c) )
652 	ROM_LOAD( "f3577_2.bin",  0x0800, 0x0800, CRC(7c1045f0) SHA1(732e8c111a346476c59bcfda73f0f826cdcd7eb3) )
653 	ROM_LOAD( "f3577_3.bin",  0x1000, 0x0800, CRC(10f47af2) SHA1(a61b693af264bfa6565c43b4fe473833f8aba046) )
654 	ROM_LOAD( "f3577_4.bin",  0x1800, 0x0800, CRC(bed8924c) SHA1(1fe3e118cc1c17f4c8b9c0025257822b99fcde38) )
655 	ROM_LOAD( "f3577_5.bin",  0x2000, 0x0800, CRC(38d671b5) SHA1(3fb3feaaddb08af5ba50a9c08511cbb3949a7985) )
656 	ROM_LOAD( "f3577_6.bin",  0x2800, 0x0800, CRC(6dbe9c4a) SHA1(11bc4b7c99811bd26423a15b33d02a86fa0bfd17) )
657 
658 	ROM_REGION( 0x0800, "chargen", 0 ) // possibly some bitrot, see h,m,n in F4 displayer
659 	ROM_LOAD( "chr_5565.bin", 0x0000, 0x0800, CRC(7d99744f) SHA1(2db330ca94a91f7b2ac2ac088ae9255f5bb0a7b4) )
660 ROM_END
661 
662 ROM_START( uts20 )
663 	ROM_REGION( 0x5000, "roms", ROMREGION_ERASEFF )
664 	ROM_LOAD( "uts20a.rom", 0x0000, 0x1000, CRC(1a7b4b4e) SHA1(c3732e25b4b7c7a80172e3fe55c77b923cf511eb) )
665 	ROM_LOAD( "uts20b.rom", 0x1000, 0x1000, CRC(7f8de87b) SHA1(a85f404ad9d560df831cc3e651a4b45e4ed30130) )
666 	ROM_LOAD( "uts20c.rom", 0x2000, 0x1000, CRC(4e334705) SHA1(ff1a730551b42f29d20af8ecc4495fd30567d35b) )
667 	ROM_LOAD( "uts20d.rom", 0x3000, 0x1000, CRC(76757cf7) SHA1(b0509d9a35366b21955f83ec3685163844c4dbf1) )
668 	ROM_LOAD( "uts20e.rom", 0x4000, 0x1000, CRC(0dfc8062) SHA1(cd681020bfb4829d4cebaf1b5bf618e67b55bda3) )
669 
670 	// character generator not dumped, using the one from 'UTS10' for now
671 	ROM_REGION( 0x0800, "chargen", 0 )
672 	ROM_LOAD( "chr_5565.bin", 0x0000, 0x0800, BAD_DUMP CRC(7d99744f) SHA1(2db330ca94a91f7b2ac2ac088ae9255f5bb0a7b4) )
673 ROM_END
674 
675 /* Driver */
676 
677 //    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS         INIT        COMPANY          FULLNAME  FLAGS
678 COMP( 1981, uts10, uts20,  0,      uts10,   uts20, univac_state, empty_init, "Sperry Univac", "UTS-10", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
679 COMP( 1980, uts20, 0,      0,      uts20,   uts20, univac_state, empty_init, "Sperry Univac", "UTS-20", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
680