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