1 // license:BSD-3-Clause
2 // copyright-holders:F. Ulivi
3 
4 // **************************
5 // Driver for HP 9825 systems
6 // **************************
7 //
8 // **** Temporary header, will hopefully evolve into proper doc ****
9 //
10 // What's in:
11 // - Emulation of 9825B and 9825T systems
12 // - 12 kw (9825B) or 31kw (9825T) of RAM
13 // - 12 kw of system ROM
14 // - Keyboard (SHIFT LOCK & RESET not implemented)
15 // - Display & run light
16 // - DC100 tape drive
17 // - Printer
18 // - Beeper
19 // - Internal expansion ROMs
20 // - I/O expansion slots: 98032, 98034 & 98035 modules can be connected
21 // - For 9825T: the so-called SKOAL mechanism that transparently overlays RAM & ROM
22 //   in the same address space
23 // - External expansion ROMs
24 // What's not yet in:
25 // - Configurable RAM size
26 //
27 // Thanks to Dyke Shaffer for publishing (on https://groups.io/g/VintHPcom)
28 // the source code of 98217 mass memory ROM. The 98217.bin image was reconstructed
29 // by re-assembling the source code (this is the reason why it's marked as
30 // a BAD_DUMP). And thanks to Ansgar Kueckes for adapting his assembler to
31 // handle HP9825 source files.
32 // For what regards the 9825T, I'd like to thank again Dyke Shaffer for
33 // publishing a lot of internal HP docs about the SKOAL card. I recovered the
34 // content of SKOAL ROM from its printed & scanned dump.
35 // I'd also like to thank Paul Berger for providing the images of the optional
36 // mass storage ROMs (see http://www.hpmuseum.net).
37 //
38 // 9825A can also be emulated. At the moment I haven't all the necessary
39 // ROM dumps, though.
40 
41 #include "emu.h"
42 #include "cpu/hphybrid/hphybrid.h"
43 #include "machine/timer.h"
44 #include "machine/hp9825_tape.h"
45 #include "machine/hp98x5_io_sys.h"
46 #include "machine/hp9825_optrom.h"
47 #include "bus/hp9845_io/hp9845_io.h"
48 #include "imagedev/bitbngr.h"
49 #include "speaker.h"
50 #include "sound/beep.h"
51 #include "hp9825.lh"
52 #include "softlist.h"
53 
54 // Debugging
55 #define VERBOSE 0
56 #include "logmacro.h"
57 
58 // CPU clock (generated by a trimmered RC oscillator)
59 constexpr unsigned MAIN_CLOCK = 6000000;
60 
61 // KDP chip clock
62 constexpr unsigned KDP_CLOCK = MAIN_CLOCK / 4;
63 
64 // Peripheral Addresses (PA)
65 constexpr uint8_t KDP_PA = 0;
66 constexpr uint8_t TAPE_PA = 1;
67 constexpr uint8_t IO_SLOT_FIRST_PA = 2;
68 constexpr uint8_t IO_SLOT_LAST_PA = 15;
69 
70 // KDP clocks to print 1 line of dots (~33 ms)
71 // This value is semi-guessed.
72 constexpr unsigned KDP_CLOCKS_PER_LINE = 50000;
73 
74 // Beeper constants
75 // Values come from R/C values on schematics
76 constexpr unsigned BEEPER_FREQ = 900;
77 constexpr unsigned BEEPER_MS = 40;
78 
79 // Bit manipulation
80 namespace {
BIT_MASK(unsigned n)81 	template<typename T> constexpr T BIT_MASK(unsigned n)
82 	{
83 		return (T)1U << n;
84 	}
85 
BIT_CLR(T & w,unsigned n)86 	template<typename T> void BIT_CLR(T& w , unsigned n)
87 	{
88 		w &= ~BIT_MASK<T>(n);
89 	}
90 
BIT_SET(T & w,unsigned n)91 	template<typename T> void BIT_SET(T& w , unsigned n)
92 	{
93 		w |= BIT_MASK<T>(n);
94 	}
95 }
96 
97 // +--------------+
98 // | hp9825_state |
99 // +--------------+
100 class hp9825_state : public driver_device
101 {
102 public:
hp9825_state(const machine_config & mconfig,device_type type,const char * tag)103 	hp9825_state(const machine_config &mconfig, device_type type, const char *tag)
104 		: driver_device(mconfig, type, tag)
105 		, m_cpu(*this , "cpu")
106 		, m_rom_drawers(*this , "drawer%u" , 0U)
107 		, m_io_sys(*this , "io_sys")
108 		, m_cursor_timer(*this , "cursor_timer")
109 		, m_tape(*this , "tape")
110 		, m_io_key(*this , "KEY%u" , 0)
111 		, m_shift_key(*this , "KEY_SHIFT")
112 		, m_prt_alpha_out(*this , "prt_alpha")
113 		, m_prt_graph_out(*this , "prt_graph")
114 		, m_prt_timer(*this , "prt_timer")
115 		, m_beeper(*this , "beeper")
116 		, m_beep_timer(*this , "beep_timer")
117 		, m_io_slot(*this, "slot%u", 0U)
118 		, m_display(*this , "char_%u_%u" , 0U , 0U)
119 		, m_run_light(*this , "run_light")
120 	{
121 	}
122 
123 	void hp9825_base(machine_config &config);
124 
125 protected:
126 	virtual void machine_start() override;
127 	virtual void device_reset() override;
128 	virtual void machine_reset() override;
129 
130 	required_device<hp_09825_67907_cpu_device> m_cpu;
131 	required_device_array<hp9825_optrom_device , 4> m_rom_drawers;
132 
133 private:
134 	required_device<hp98x5_io_sys_device> m_io_sys;
135 	required_device<timer_device> m_cursor_timer;
136 	required_device<hp9825_tape_device> m_tape;
137 	required_ioport_array<4> m_io_key;
138 	required_ioport m_shift_key;
139 	required_device<bitbanger_device> m_prt_alpha_out;
140 	required_device<bitbanger_device> m_prt_graph_out;
141 	required_device<timer_device> m_prt_timer;
142 	required_device<beep_device> m_beeper;
143 	required_device<timer_device> m_beep_timer;
144 	required_device_array<hp9845_io_slot_device , 3> m_io_slot;
145 	output_finder<32 , 7> m_display;
146 	output_finder<> m_run_light;
147 
148 	bool m_display_on;
149 	uint8_t m_display_mem[ 32 ];
150 	uint8_t m_display_idx;
151 	bool m_rpl_cursor;
152 	bool m_cursor_blink;
153 	bool m_any_cursor;
154 	uint8_t m_scancode;
155 	bool m_key_pressed;
156 	bool m_autorepeating;
157 	unsigned m_autorepeat_cnt;
158 	// Printer
159 	uint8_t m_printer_mem[ 16 ];
160 	uint8_t m_printer_idx;
161 	unsigned m_printer_line;    // 0: printer idle, 1..10: line being printed
162 	// SC of slots
163 	int m_slot_sc[ 3 ];
164 
165 	void cpu_io_map(address_map &map);
166 
167 	uint16_t kb_scancode_r();
168 	void disp_w(uint16_t data);
169 	uint16_t kdp_status_r();
170 	void kdp_control_w(uint16_t data);
171 	void printer_w(uint16_t data);
172 
173 	void update_display();
174 	TIMER_DEVICE_CALLBACK_MEMBER(cursor_blink);
175 	void kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx);
176 	TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);
177 
178 	TIMER_DEVICE_CALLBACK_MEMBER(prt_timer);
179 
180 	TIMER_DEVICE_CALLBACK_MEMBER(beep_timer);
181 
182 	// Slot handling
183 	void set_irq_slot(unsigned slot , int state);
184 	void set_sts_slot(unsigned slot , int state);
185 	void set_flg_slot(unsigned slot , int state);
186 	void set_dmar_slot(unsigned slot , int state);
187 };
188 
machine_start()189 void hp9825_state::machine_start()
190 {
191 	m_display.resolve();
192 	m_run_light.resolve();
193 
194 	save_item(NAME(m_display_on));
195 	save_item(NAME(m_display_mem));
196 	save_item(NAME(m_display_idx));
197 	save_item(NAME(m_scancode));
198 }
199 
device_reset()200 void hp9825_state::device_reset()
201 {
202 	// First, unmap every r/w handler in 1..12 select codes
203 	for (unsigned sc = IO_SLOT_FIRST_PA; sc < (IO_SLOT_LAST_PA + 1); sc++) {
204 		m_cpu->space(AS_IO).unmap_readwrite(sc * 4 , sc * 4 + 3);
205 	}
206 
207 	// Then, set r/w handlers of all installed I/O cards
208 	int sc;
209 	read16m_delegate rhandler(*this);
210 	write16m_delegate whandler(*this);
211 	for (unsigned i = 0; i < 3; i++) {
212 		if ((sc = m_io_slot[ i ]->get_rw_handlers(rhandler , whandler)) >= 0) {
213 			logerror("Install R/W handlers for slot %u @ SC = %d\n", i, sc);
214 			m_cpu->space(AS_IO).install_readwrite_handler(sc * 4 , sc * 4 + 3 , rhandler , whandler);
215 		}
216 		m_slot_sc[ i ] = sc;
217 	}
218 }
219 
machine_reset()220 void hp9825_state::machine_reset()
221 {
222 	m_display_on = false;
223 	m_display_idx = 0;
224 	m_rpl_cursor = false;
225 	m_cursor_timer->reset();
226 	m_cursor_blink = true;
227 	update_display();
228 	m_scancode = 0;
229 	m_key_pressed = false;
230 	m_autorepeating = false;
231 	m_autorepeat_cnt = 0;
232 	m_printer_idx = 0;
233 	m_printer_line = 0;
234 	m_prt_timer->reset();
235 	m_beeper->set_state(0);
236 	m_beep_timer->reset();
237 }
238 
cpu_io_map(address_map & map)239 void hp9825_state::cpu_io_map(address_map &map)
240 {
241 	map.unmap_value_low();
242 	map(HP_MAKE_IOADDR(KDP_PA , 0) , HP_MAKE_IOADDR(KDP_PA , 0)).rw(FUNC(hp9825_state::kb_scancode_r) , FUNC(hp9825_state::disp_w));
243 	map(HP_MAKE_IOADDR(KDP_PA , 1) , HP_MAKE_IOADDR(KDP_PA , 1)).rw(FUNC(hp9825_state::kdp_status_r) , FUNC(hp9825_state::kdp_control_w));
244 	map(HP_MAKE_IOADDR(KDP_PA , 2) , HP_MAKE_IOADDR(KDP_PA , 2)).w(FUNC(hp9825_state::printer_w));
245 	map(HP_MAKE_IOADDR(TAPE_PA , 0) , HP_MAKE_IOADDR(TAPE_PA , 3)).rw(m_tape , FUNC(hp9825_tape_device::tape_r) , FUNC(hp9825_tape_device::tape_w));
246 	// TODO:
247 }
248 
kb_scancode_r()249 uint16_t hp9825_state::kb_scancode_r()
250 {
251 	uint8_t res = m_scancode;
252 	if (m_shift_key->read()) {
253 		BIT_SET(res , 7);
254 	}
255 	m_io_sys->set_irq(KDP_PA , false);
256 	return res;
257 }
258 
disp_w(uint16_t data)259 void hp9825_state::disp_w(uint16_t data)
260 {
261 	if (m_display_on) {
262 		m_display_on = false;
263 		m_cursor_timer->reset();
264 		m_cursor_blink = true;
265 		m_display_idx = 0;
266 		update_display();
267 	}
268 	m_display_mem[ m_display_idx++ ] = uint8_t(data);
269 }
270 
kdp_status_r()271 uint16_t hp9825_state::kdp_status_r()
272 {
273 	uint16_t res = 8;
274 	if (m_io_sys->is_irq_pending(KDP_PA)) {
275 		BIT_SET(res , 4);
276 	}
277 	if (m_printer_line) {
278 		BIT_SET(res , 2);
279 	}
280 
281 	return res;
282 }
283 
kdp_control_w(uint16_t data)284 void hp9825_state::kdp_control_w(uint16_t data)
285 {
286 	bool regen_display = false;
287 	if (BIT(data , 1) && !m_display_on) {
288 		m_display_on = true;
289 		// Cursor should blink at 2^-19 the KDP clock
290 		attotime cursor_half_period{ attotime::from_ticks(262144 , KDP_CLOCK) };
291 		m_cursor_timer->adjust(cursor_half_period , 0 , cursor_half_period);
292 		regen_display = true;
293 	}
294 	if (BIT(data , 6) && !m_rpl_cursor) {
295 		m_rpl_cursor = true;
296 		regen_display = true;
297 	}
298 	if (BIT(data , 5) && m_rpl_cursor) {
299 		m_rpl_cursor = false;
300 		regen_display = true;
301 	}
302 	if (BIT(data , 4)) {
303 		if (BIT(data , 3)) {
304 			m_run_light = !m_run_light;
305 		} else {
306 			m_run_light = false;
307 		}
308 	} else if (BIT(data , 3)) {
309 		m_run_light = true;
310 	}
311 	if (BIT(data , 0) && m_printer_line == 0) {
312 		// Start printing
313 		// Dump text line to alpha bitbanger
314 		for (auto c : m_printer_mem) {
315 			m_prt_alpha_out->output(c);
316 		}
317 		m_prt_alpha_out->output('\n');
318 		m_printer_idx = 0;
319 		m_printer_line++;
320 		m_prt_timer->adjust(attotime::from_ticks(KDP_CLOCKS_PER_LINE , KDP_CLOCK));
321 	}
322 	if (BIT(data , 2)) {
323 		// Start beeper
324 		m_beeper->set_state(1);
325 		m_beep_timer->adjust(attotime::from_msec(BEEPER_MS));
326 	}
327 	if (regen_display) {
328 		update_display();
329 	}
330 }
331 
printer_w(uint16_t data)332 void hp9825_state::printer_w(uint16_t data)
333 {
334 	m_printer_mem[ m_printer_idx ] = uint8_t(data);
335 	m_printer_idx = (m_printer_idx + 1) & 0xf;
336 }
337 
338 // The character generator was reverse engineered from images of printer & display test patterns.
339 // It is not guaranteed to be pixel-accurate though it looks quite close to the original.
340 static const uint8_t chargen[ 128 ][ 5 ] = {
341 	{ 0x08,0x1c,0x3e,0x7f,0x00 }, // 00
342 	{ 0x30,0x48,0x45,0x40,0x30 }, // 01
343 	{ 0x45,0x29,0x11,0x29,0x45 }, // 02
344 	{ 0x7d,0x09,0x11,0x21,0x7d }, // 03
345 	{ 0x38,0x44,0x44,0x38,0x44 }, // 04
346 	{ 0x7c,0x2a,0x4a,0x4a,0x34 }, // 05
347 	{ 0x7f,0x01,0x01,0x01,0x03 }, // 06
348 	{ 0x7d,0x09,0x05,0x05,0x79 }, // 07
349 	{ 0x60,0x58,0x46,0x58,0x60 }, // 08
350 	{ 0x38,0x44,0x44,0x3c,0x04 }, // 09
351 	{ 0x10,0x20,0x7f,0x20,0x10 }, // 0a
352 	{ 0x62,0x14,0x08,0x10,0x60 }, // 0b
353 	{ 0x40,0x3c,0x20,0x20,0x1c }, // 0c
354 	{ 0x08,0x1c,0x2a,0x08,0x08 }, // 0d
355 	{ 0x10,0x08,0x78,0x08,0x04 }, // 0e
356 	{ 0x08,0x55,0x7f,0x55,0x08 }, // 0f
357 	{ 0x3e,0x49,0x49,0x49,0x3e }, // 10
358 	{ 0x5e,0x61,0x01,0x61,0x5e }, // 11
359 	{ 0x30,0x4a,0x4d,0x49,0x30 }, // 12
360 	{ 0x78,0x14,0x15,0x14,0x78 }, // 13
361 	{ 0x38,0x44,0x45,0x3c,0x40 }, // 14
362 	{ 0x78,0x15,0x14,0x15,0x78 }, // 15
363 	{ 0x38,0x45,0x44,0x3d,0x40 }, // 16
364 	{ 0x3c,0x43,0x42,0x43,0x3c }, // 17
365 	{ 0x38,0x45,0x44,0x45,0x38 }, // 18
366 	{ 0x3e,0x41,0x40,0x41,0x3e }, // 19
367 	{ 0x3c,0x41,0x40,0x41,0x3c }, // 1a
368 	{ 0x7e,0x09,0x7f,0x49,0x49 }, // 1b
369 	{ 0x38,0x44,0x38,0x54,0x58 }, // 1c
370 	{ 0x12,0x19,0x15,0x12,0x00 }, // 1d
371 	{ 0x48,0x7e,0x49,0x41,0x42 }, // 1e
372 	{ 0x55,0x2a,0x55,0x2a,0x55 }, // 1f
373 	{ 0x00,0x00,0x00,0x00,0x00 }, // 20
374 	{ 0x00,0x5f,0x00,0x00,0x00 }, // 21
375 	{ 0x00,0x03,0x00,0x03,0x00 }, // 22
376 	{ 0x14,0x7f,0x14,0x7f,0x14 }, // 23
377 	{ 0x24,0x2a,0x7f,0x2a,0x12 }, // 24
378 	{ 0x23,0x13,0x08,0x64,0x62 }, // 25
379 	{ 0x36,0x49,0x56,0x20,0x50 }, // 26
380 	{ 0x00,0x0b,0x07,0x00,0x00 }, // 27
381 	{ 0x00,0x00,0x3e,0x41,0x00 }, // 28
382 	{ 0x00,0x41,0x3e,0x00,0x00 }, // 29
383 	{ 0x08,0x2a,0x1c,0x2a,0x08 }, // 2a
384 	{ 0x08,0x08,0x3e,0x08,0x08 }, // 2b
385 	{ 0x00,0x58,0x38,0x00,0x00 }, // 2c
386 	{ 0x08,0x08,0x08,0x08,0x08 }, // 2d
387 	{ 0x00,0x60,0x60,0x00,0x00 }, // 2e
388 	{ 0x20,0x10,0x08,0x04,0x02 }, // 2f
389 	{ 0x3e,0x51,0x49,0x45,0x3e }, // 30
390 	{ 0x00,0x42,0x7f,0x40,0x00 }, // 31
391 	{ 0x62,0x51,0x49,0x49,0x46 }, // 32
392 	{ 0x22,0x41,0x49,0x49,0x36 }, // 33
393 	{ 0x18,0x14,0x12,0x7f,0x10 }, // 34
394 	{ 0x27,0x45,0x45,0x45,0x39 }, // 35
395 	{ 0x3c,0x4a,0x49,0x49,0x30 }, // 36
396 	{ 0x01,0x71,0x09,0x05,0x03 }, // 37
397 	{ 0x36,0x49,0x49,0x49,0x36 }, // 38
398 	{ 0x06,0x49,0x49,0x29,0x1e }, // 39
399 	{ 0x00,0x36,0x36,0x00,0x00 }, // 3a
400 	{ 0x00,0x5b,0x3b,0x00,0x00 }, // 3b
401 	{ 0x00,0x08,0x14,0x22,0x41 }, // 3c
402 	{ 0x14,0x14,0x14,0x14,0x14 }, // 3d
403 	{ 0x41,0x22,0x14,0x08,0x00 }, // 3e
404 	{ 0x06,0x01,0x51,0x09,0x06 }, // 3f
405 	{ 0x3e,0x41,0x5d,0x55,0x1e }, // 40
406 	{ 0x7e,0x09,0x09,0x09,0x7e }, // 41
407 	{ 0x7f,0x49,0x49,0x49,0x36 }, // 42
408 	{ 0x3e,0x41,0x41,0x41,0x22 }, // 43
409 	{ 0x7f,0x41,0x41,0x41,0x3e }, // 44
410 	{ 0x7f,0x49,0x49,0x49,0x41 }, // 45
411 	{ 0x7f,0x09,0x09,0x09,0x01 }, // 46
412 	{ 0x3e,0x41,0x41,0x51,0x72 }, // 47
413 	{ 0x7f,0x08,0x08,0x08,0x7f }, // 48
414 	{ 0x00,0x41,0x7f,0x41,0x00 }, // 49
415 	{ 0x20,0x40,0x40,0x40,0x3f }, // 4a
416 	{ 0x7f,0x08,0x14,0x22,0x41 }, // 4b
417 	{ 0x7f,0x40,0x40,0x40,0x40 }, // 4c
418 	{ 0x7f,0x02,0x0c,0x02,0x7f }, // 4d
419 	{ 0x7f,0x04,0x08,0x10,0x7f }, // 4e
420 	{ 0x3e,0x41,0x41,0x41,0x3e }, // 4f
421 	{ 0x7f,0x09,0x09,0x09,0x06 }, // 50
422 	{ 0x3e,0x41,0x51,0x21,0x5e }, // 51
423 	{ 0x7f,0x09,0x19,0x29,0x46 }, // 52
424 	{ 0x26,0x49,0x49,0x49,0x32 }, // 53
425 	{ 0x01,0x01,0x7f,0x01,0x01 }, // 54
426 	{ 0x3f,0x40,0x40,0x40,0x3f }, // 55
427 	{ 0x07,0x18,0x60,0x18,0x07 }, // 56
428 	{ 0x7f,0x20,0x10,0x20,0x7f }, // 57
429 	{ 0x63,0x14,0x08,0x14,0x63 }, // 58
430 	{ 0x03,0x04,0x78,0x04,0x03 }, // 59
431 	{ 0x61,0x51,0x49,0x45,0x43 }, // 5a
432 	{ 0x00,0x00,0x7f,0x41,0x41 }, // 5b
433 	{ 0x20,0x7f,0x01,0x01,0x01 }, // 5c
434 	{ 0x41,0x41,0x7f,0x00,0x00 }, // 5d
435 	{ 0x04,0x02,0x7f,0x02,0x04 }, // 5e
436 	{ 0x40,0x40,0x40,0x40,0x40 }, // 5f
437 	{ 0x00,0x07,0x0b,0x00,0x00 }, // 60
438 	{ 0x38,0x44,0x44,0x3c,0x40 }, // 61
439 	{ 0x7f,0x48,0x44,0x44,0x38 }, // 62
440 	{ 0x38,0x44,0x44,0x44,0x20 }, // 63
441 	{ 0x38,0x44,0x44,0x48,0x7f }, // 64
442 	{ 0x38,0x54,0x54,0x54,0x08 }, // 65
443 	{ 0x08,0x7e,0x09,0x02,0x00 }, // 66
444 	{ 0x08,0x14,0x54,0x54,0x3c }, // 67
445 	{ 0x7f,0x08,0x04,0x04,0x78 }, // 68
446 	{ 0x00,0x44,0x7d,0x40,0x00 }, // 69
447 	{ 0x20,0x40,0x44,0x3d,0x00 }, // 6a
448 	{ 0x7f,0x10,0x28,0x44,0x00 }, // 6b
449 	{ 0x00,0x41,0x7f,0x40,0x00 }, // 6c
450 	{ 0x78,0x04,0x18,0x04,0x78 }, // 6d
451 	{ 0x7c,0x08,0x04,0x04,0x78 }, // 6e
452 	{ 0x38,0x44,0x44,0x44,0x38 }, // 6f
453 	{ 0x7c,0x14,0x24,0x24,0x18 }, // 70
454 	{ 0x18,0x24,0x14,0x7c,0x40 }, // 71
455 	{ 0x7c,0x08,0x04,0x04,0x00 }, // 72
456 	{ 0x48,0x54,0x54,0x54,0x20 }, // 73
457 	{ 0x04,0x3e,0x44,0x20,0x00 }, // 74
458 	{ 0x3c,0x40,0x40,0x20,0x7c }, // 75
459 	{ 0x1c,0x20,0x40,0x20,0x1c }, // 76
460 	{ 0x3c,0x40,0x30,0x40,0x3c }, // 77
461 	{ 0x44,0x28,0x10,0x28,0x44 }, // 78
462 	{ 0x04,0x48,0x30,0x08,0x04 }, // 79
463 	{ 0x44,0x64,0x54,0x4c,0x44 }, // 7a
464 	{ 0x08,0x7c,0x04,0x7c,0x02 }, // 7b
465 	{ 0x00,0x00,0x7f,0x00,0x00 }, // 7c
466 	{ 0x08,0x08,0x2a,0x1c,0x08 }, // 7d
467 	{ 0x41,0x63,0x55,0x49,0x63 }, // 7e
468 	{ 0x7f,0x08,0x08,0x08,0x08 }  // 7f
469 };
470 
update_display()471 void hp9825_state::update_display()
472 {
473 	m_any_cursor = false;
474 	for (unsigned i = 0; i < 32; ++i) {
475 		bool cursor_here = BIT(m_display_mem[ i ] , 7);
476 		if (cursor_here) {
477 			m_any_cursor = true;
478 		}
479 		bool show_cursor = m_cursor_blink && cursor_here;
480 		uint8_t char_code = m_display_mem[ i ] & 0x7f;
481 		for (unsigned j = 0; j < 7; ++j) {
482 			uint8_t five_dots = 0;
483 			if (m_display_on) {
484 				for (unsigned col = 0; col < 5; col++) {
485 					uint8_t char_col;
486 					if (show_cursor) {
487 						if (m_rpl_cursor) {
488 							// Replace cursor: all pixels lit
489 							char_col = ~0;
490 						} else {
491 							// Insert cursor: character code 0
492 							char_col = chargen[ 0 ][ col ];
493 						}
494 					} else {
495 						char_col = chargen[ char_code ][ col ];
496 					}
497 					if (BIT(char_col , j)) {
498 						BIT_SET(five_dots , col);
499 					}
500 				}
501 			}
502 			m_display[ i ][ j ] = five_dots;
503 		}
504 	}
505 }
506 
TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::cursor_blink)507 TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::cursor_blink)
508 {
509 	m_cursor_blink = !m_cursor_blink;
510 	if (m_any_cursor) {
511 		update_display();
512 	}
513 }
514 
TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::kb_scan)515 TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::kb_scan)
516 {
517 	ioport_value input[ 4 ]
518 	{ m_io_key[ 0 ]->read(),
519 			m_io_key[ 1 ]->read(),
520 			m_io_key[ 2 ]->read(),
521 			m_io_key[ 3 ]->read()
522 			};
523 
524 	if (m_key_pressed) {
525 		// Still pressed ?
526 		m_key_pressed = BIT(input[ m_scancode / 32 ] , m_scancode % 32);
527 	} else {
528 		int max_seq_len = 0;
529 		unsigned max_seq_idx = 0;
530 		for (unsigned i = 0; i < 4; i++) {
531 			kb_scan_ioport(input[ i ] , *m_io_key[ i ] , i << 5 , max_seq_len , max_seq_idx);
532 		}
533 		if (max_seq_len) {
534 			m_scancode = max_seq_idx;
535 			m_key_pressed = true;
536 			m_io_sys->set_irq(KDP_PA , true);
537 		}
538 	}
539 
540 	if (m_key_pressed) {
541 		auto prev_cnt = m_autorepeat_cnt;
542 		m_autorepeat_cnt++;
543 		// Auto-repeat initial delay & frequency are entirely guessed..
544 		if (BIT(m_autorepeat_cnt , 5)) {
545 			// Initial delay passed
546 			m_autorepeating = true;
547 		}
548 		if (m_autorepeating && BIT(~prev_cnt & m_autorepeat_cnt , 3)) {
549 			// Repeat key every time bit 3 of autorepeat counter goes 0->1
550 			m_io_sys->set_irq(KDP_PA , true);
551 		}
552 	} else {
553 		m_autorepeating = false;
554 		m_autorepeat_cnt = 0;
555 	}
556 }
557 
kb_scan_ioport(ioport_value pressed,ioport_port & port,unsigned idx_base,int & max_seq_len,unsigned & max_seq_idx)558 void hp9825_state::kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx)
559 {
560 	while (pressed) {
561 		unsigned bit_no = 31 - count_leading_zeros(pressed);
562 		ioport_value mask = BIT_MASK<ioport_value>(bit_no);
563 		int seq_len = port.field(mask)->seq().length();
564 		if (seq_len > max_seq_len) {
565 			max_seq_len = seq_len;
566 			max_seq_idx = bit_no + idx_base;
567 		}
568 		pressed &= ~mask;
569 	}
570 }
571 
TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::prt_timer)572 TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::prt_timer)
573 {
574 	if (m_printer_line == 1 || m_printer_line == 9 || m_printer_line == 10) {
575 		// Empty lines
576 		for (unsigned i = 0; i < 110; i++) {
577 			m_prt_graph_out->output(' ');
578 		}
579 	} else {
580 		for (unsigned i = 0; i < 16; i++) {
581 			for (unsigned col = 0; col < 5; col++) {
582 				uint8_t pixels = chargen[ m_printer_mem[ i ] & 0x7f ][ col ];
583 				m_prt_graph_out->output(BIT(pixels , m_printer_line - 2) ? '*' : ' ');
584 			}
585 			m_prt_graph_out->output(' ');
586 			m_prt_graph_out->output(' ');
587 		}
588 	}
589 	m_prt_graph_out->output('\n');
590 	m_printer_line++;
591 	if (m_printer_line <= 10) {
592 		m_prt_timer->adjust(attotime::from_ticks(KDP_CLOCKS_PER_LINE , KDP_CLOCK));
593 	} else {
594 		m_printer_line = 0;
595 	}
596 }
597 
TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::beep_timer)598 TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::beep_timer)
599 {
600 	m_beeper->set_state(0);
601 }
602 
set_irq_slot(unsigned slot,int state)603 void hp9825_state::set_irq_slot(unsigned slot , int state)
604 {
605 	int sc = m_slot_sc[ slot ];
606 	assert(sc >= 0);
607 	m_io_sys->set_irq(uint8_t(sc) , state);
608 }
609 
set_sts_slot(unsigned slot,int state)610 void hp9825_state::set_sts_slot(unsigned slot , int state)
611 {
612 	int sc = m_slot_sc[ slot ];
613 	assert(sc >= 0);
614 	m_io_sys->set_sts(uint8_t(sc) , state);
615 }
616 
set_flg_slot(unsigned slot,int state)617 void hp9825_state::set_flg_slot(unsigned slot , int state)
618 {
619 	int sc = m_slot_sc[ slot ];
620 	assert(sc >= 0);
621 	m_io_sys->set_flg(uint8_t(sc) , state);
622 }
623 
set_dmar_slot(unsigned slot,int state)624 void hp9825_state::set_dmar_slot(unsigned slot , int state)
625 {
626 	int sc = m_slot_sc[ slot ];
627 	assert(sc >= 0);
628 	m_io_sys->set_dmar(uint8_t(sc) , state);
629 }
630 
hp9825_base(machine_config & config)631 void hp9825_state::hp9825_base(machine_config &config)
632 {
633 	HP_09825_67907(config , m_cpu , MAIN_CLOCK);
634 	// Just guessing... settings borrowed from hp9845
635 	m_cpu->set_rw_cycles(6 , 6);
636 	m_cpu->set_relative_mode(false);
637 	m_cpu->set_addrmap(AS_IO , &hp9825_state::cpu_io_map);
638 	m_cpu->int_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::int_r));
639 	m_cpu->pa_changed_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::pa_w));
640 
641 	// Needed when 98035 RTC module is connected or time advances at about 1/4 the correct speed (NP misses a lot of 1kHz interrupts)
642 	config.set_maximum_quantum(attotime::from_hz(5000));
643 
644 	HP98X5_IO_SYS(config , m_io_sys , 0);
645 	m_io_sys->irl().set_inputline(m_cpu, HPHYBRID_IRL);
646 	m_io_sys->irh().set_inputline(m_cpu, HPHYBRID_IRH);
647 	m_io_sys->sts().set(m_cpu , FUNC(hp_09825_67907_cpu_device::status_w));
648 	m_io_sys->flg().set(m_cpu , FUNC(hp_09825_67907_cpu_device::flag_w));
649 	m_io_sys->dmar().set(m_cpu , FUNC(hp_09825_67907_cpu_device::dmar_w));
650 
651 	TIMER(config , m_cursor_timer , 0).configure_generic(FUNC(hp9825_state::cursor_blink));
652 
653 	// Keyboard scan timer. A scan of the whole keyboard should take 2^14 KDP clocks.
654 	TIMER(config , "kb_timer" , 0).configure_periodic(FUNC(hp9825_state::kb_scan), attotime::from_ticks(16384 , KDP_CLOCK));
655 
656 	// Tape drive
657 	HP9825_TAPE(config , m_tape , 0);
658 	m_tape->flg().set([this](int state) { m_io_sys->set_flg(TAPE_PA , state); });
659 	m_tape->sts().set([this](int state) { m_io_sys->set_sts(TAPE_PA , state); });
660 	m_tape->dmar().set([this](int state) { m_io_sys->set_dmar(TAPE_PA , state); });
661 
662 	// Printer
663 	BITBANGER(config , m_prt_alpha_out , 0);
664 	BITBANGER(config , m_prt_graph_out , 0);
665 	TIMER(config , m_prt_timer , 0).configure_generic(FUNC(hp9825_state::prt_timer));
666 
667 	// Beeper
668 	SPEAKER(config, "mono").front_center();
669 	BEEP(config, m_beeper, BEEPER_FREQ).add_route(ALL_OUTPUTS, "mono", 1.00);
670 	TIMER(config , m_beep_timer , 0).configure_generic(FUNC(hp9825_state::beep_timer));
671 
672 	// I/O slots
673 	for (unsigned slot = 0; slot < 3; slot++) {
674 		auto& finder = m_io_slot[ slot ];
675 		hp9845_io_slot_device& tmp( HP9845_IO_SLOT(config , finder , 0) );
676 		tmp.irq().set([this , slot](int state) { set_irq_slot(slot , state); });
677 		tmp.sts().set([this , slot](int state) { set_sts_slot(slot , state); });
678 		tmp.flg().set([this , slot](int state) { set_flg_slot(slot , state); });
679 		tmp.dmar().set([this , slot](int state) { set_dmar_slot(slot , state); });
680 	}
681 
682 	// Optional ROM slots
683 	for (auto& finder : m_rom_drawers) {
684 		HP9825_OPTROM(config , finder);
685 	}
686 
687 	config.set_default_layout(layout_hp9825);
688 }
689 
690 #define IOP_MASK(x) BIT_MASK<ioport_value>((x))
691 
692 static INPUT_PORTS_START(hp9825)
693 	// Keyboard is arranged in a 8 x 16 matrix. Of the 128 possible positions, 102 are used.
694 	// Keys are mapped on bit b of KEYn
695 	// where b = (row & 1) << 4 + column, n = row >> 1
696 	// column = [0..15]
697 	// row = [0..7]
698 	// 4 more keys are not in the matrix: 2 SHIFTs, 1 SHIFT LOCK and RESET key.
699 	// Fun fact: alphanumeric keys are arranged in the matrix so that their scancode (row/column number)
700 	// equals the lower case ASCII code. The person in charge of routing the keyboard PCB
701 	// must have loved this arrangement...
702 	PORT_START("KEY0")
703 	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,0: N/U
704 	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Stop")                    // 0,1: Stop
705 	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Rewind")                                                                       // 0,2: Rewind
706 	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,3: N/U
707 	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,4: N/U
708 	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,5: N/U
709 	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 0,6: N/U
710 	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Result")                                                                       // 0,7: Result
711 	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line insert")                                                                  // 0,8: Line insert
712 	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line delete")                                                                  // 0,9: Line delete
713 	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Execute")    // 0,10: Execute
714 	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line recall")                                                                 // 0,11: Line recall
715 	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Run")                                                                         // 0,12: Run
716 	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Store")                                                                       // 0,13: Store
717 	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Display left")                                                                // 0,14: Display left
718 	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Display right")                                                               // 0,15: Display right
719 	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Display down")         // 1,0: Display down
720 	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Display up")               // 1,1: Display up
721 	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Clear")                                               // 1,2: Clear
722 	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Print all")                                                                   // 1,3: Print all
723 	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_NAME("Char back")                                           // 1,4: Char back
724 	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_NAME("Char forward")                                       // 1,5: Char forward
725 	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_NAME("Char ins/rpl")                                      // 1,6: Char ins/rpl
726 	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_NAME("Char delete")                                          // 1,7: Char delete
727 	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Step")                                                                        // 1,8: Step
728 	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_NAME("Continue") PORT_CHAR(13)                             // 1,9: Continue
729 	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 1,10: N/U
730 	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("List")                                                                        // 1,11: List
731 	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Line fetch")                                                                  // 1,12: Line fetch
732 	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Erase")                                                                       // 1,13: Erase
733 	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Record")                                                                      // 1,14: Record
734 	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Load")                                                                        // 1,15: Load
735 
736 	PORT_START("KEY1")
737 	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')                                                   // 2,0: Space
738 	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,1: N/U
739 	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,2: N/U
740 	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,3: N/U
741 	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,4: N/U
742 	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,5: N/U
743 	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,6: N/U
744 	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 2,7: N/U
745 	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_NAME("Keypad (")                         // 2,8: KP (
746 	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_NAME("Keypad )")                        // 2,9: KP )
747 	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))                           // 2,10: KP *
748 	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))                           // 2,11: KP +
749 	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')                                   // 2,12: ,
750 	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD))                         // 2,13: KP -
751 	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')                                    // 2,14: .
752 	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD))                         // 2,15: KP /
753 	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('\'')                                      // 3,0: 0
754 	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')                                       // 3,1: 1
755 	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')                                       // 3,2: 2
756 	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')                                       // 3,3: 3
757 	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')                                       // 3,4: 4
758 	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')                                       // 3,5: 5
759 	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')                                       // 3,6: 6
760 	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('@')                                       // 3,7: 7
761 	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('[')                                       // 3,8: 8
762 	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(']')                                       // 3,9: 9
763 	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 3,10: N/U
764 	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';')                                                  // 3,11: ;
765 	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 3,12: N/U
766 	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD))                       // 3,13: =
767 	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 3,14: N/U
768 	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('?') PORT_CHAR(':')                                   // 3,15: ?
769 
770 	PORT_START("KEY2")
771 	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                             // 4,0: N/U
772 	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("f0")                                                                           // 4,1: f0
773 	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("f1")                        // 4,2: f1
774 	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("f2")                        // 4,3: f2
775 	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("f3")                        // 4,4: f3
776 	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("f4")                        // 4,5: f4
777 	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("f5")                        // 4,6: f5
778 	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("f6")                        // 4,7: f6
779 	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("f7")                        // 4,8: f7
780 	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("f8")                        // 4,9: f8
781 	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("f9")                       // 4,10: f9
782 	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("f10")                    // 4,11: f10
783 	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("f11")                    // 4,12: f11
784 	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 4,13: N/U
785 	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))                                 // 4,14: KP 0
786 	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))                                 // 4,15: KP 1
787 	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))                                 // 5,0: KP 2
788 	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))                                 // 5,1: KP 3
789 	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))                                 // 5,2: KP 4
790 	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))                                 // 5,3: KP 5
791 	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))                                 // 5,4: KP 6
792 	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))                                 // 5,5: KP 7
793 	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))                                 // 5,6: KP 8
794 	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))                                 // 5,7: KP 9
795 	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))                             // 5,8: KP .
796 	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))                         // 5,9: KP ,
797 	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,10: N/U
798 	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,11: N/U
799 	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,12: N/U
800 	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,13: N/U
801 	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_NAME(u8"\u2191 \u221A")                                    // 5,14: ^ (↑ √)
802 	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 5,15: N/U
803 
804 	PORT_START("KEY3")
805 	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Enter exp _")                                                                  // 6,0: Enter exp
806 	PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')                                        // 6,1: A
807 	PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')                                        // 6,2: B
808 	PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')                                        // 6,3: C
809 	PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')                                        // 6,4: D
810 	PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')                                        // 6,5: E
811 	PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')                                        // 6,6: F
812 	PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')                                        // 6,7: G
813 	PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')                                        // 6,8: H
814 	PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')                                        // 6,9: I
815 	PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')                                       // 6,10: J
816 	PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')                                       // 6,11: K
817 	PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')                                       // 6,12: L
818 	PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')                                       // 6,13: M
819 	PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')                                       // 6,14: N
820 	PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')                                       // 6,15: O
821 	PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')                                       // 7,0: P
822 	PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')                                       // 7,1: Q
823 	PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')                                       // 7,2: R
824 	PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')                                       // 7,3: S
825 	PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')                                       // 7,4: T
826 	PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')                                       // 7,5: U
827 	PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')                                       // 7,6: V
828 	PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')                                       // 7,7: W
829 	PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')                                       // 7,8: X
830 	PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')                                       // 7,9: Y
831 	PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')                                       // 7,10: Z
832 	PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(u8"π |")                                                                       // 7,11: Pi
833 	PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 7,12: N/U
834 	PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_NAME(u8"\u2192")                                             // 7,13: Gazinta (→)
835 	PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 7,14: N/U
836 	PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED)                                                                                            // 7,15: N/U
837 
838 	PORT_START("KEY_SHIFT")
839 	PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)    // Shift
840 INPUT_PORTS_END
841 
842 // +---------------+
843 // | hp9825b_state |
844 // +---------------+
845 class hp9825b_state : public hp9825_state
846 {
847 public:
hp9825b_state(const machine_config & mconfig,device_type type,const char * tag)848 	hp9825b_state(const machine_config &mconfig, device_type type, const char *tag)
849 		: hp9825_state(mconfig , type , tag)
850 	{
851 	}
852 
853 	void hp9825b(machine_config &config);
854 
855 protected:
856 	virtual void device_reset() override;
857 
858 private:
859 	void cpu_mem_map(address_map &map);
860 };
861 
hp9825b(machine_config & config)862 void hp9825b_state::hp9825b(machine_config &config)
863 {
864 	hp9825_base(config);
865 	m_cpu->set_addrmap(AS_PROGRAM , &hp9825b_state::cpu_mem_map);
866 
867 	for (auto& finder : m_rom_drawers) {
868 		finder->set_rom_limit(0x5000);
869 	}
870 
871 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9825b_rom");
872 }
873 
device_reset()874 void hp9825b_state::device_reset()
875 {
876 	hp9825_state::device_reset();
877 
878 	auto space = &m_cpu->space(AS_PROGRAM);
879 
880 	for (auto& finder : m_rom_drawers) {
881 		finder->install_rw_handlers(space , nullptr);
882 	}
883 }
884 
cpu_mem_map(address_map & map)885 void hp9825b_state::cpu_mem_map(address_map &map)
886 {
887 	map.unmap_value_low();
888 	map(0x0000 , 0x2fff).rom();
889 	map(0x3400 , 0x3bff).rom();
890 	map(0x4000 , 0x4fff).rom();
891 	map(0x5000 , 0x7fff).ram();
892 }
893 
894 // +---------------+
895 // | hp9825t_state |
896 // +---------------+
897 class hp9825t_state : public hp9825_state,
898 					  public device_memory_interface
899 {
900 public:
hp9825t_state(const machine_config & mconfig,device_type type,const char * tag)901 	hp9825t_state(const machine_config &mconfig, device_type type, const char *tag)
902 		: hp9825_state(mconfig , type , tag)
903 		, device_memory_interface(mconfig , *this)
904 		, m_skoalrom(*this , "skoalrom")
905 		, m_ram_space_config("ram" , ENDIANNESS_BIG , 16 , 15 , -1)
906 		, m_rom_space_config("rom" , ENDIANNESS_BIG , 16 , 15 , -1)
907 	{
908 	}
909 
910 	void hp9825t(machine_config &config);
911 
912 protected:
913 	virtual void machine_start() override;
914 	virtual void device_reset() override;
915 
916 	// device_memory_interface overrides
917 	virtual space_config_vector memory_space_config() const override;
918 
919 private:
920 	required_memory_region m_skoalrom;
921 	address_space_config m_ram_space_config;
922 	address_space *m_ram_space;
923 	address_space_config m_rom_space_config;
924 	address_space *m_rom_space;
925 
926 	uint8_t m_cycle_type;
927 
928 	// SKOAL state
929 	bool m_skoalbit;    // U53
930 	bool m_second_access;   // U57-3
931 	bool m_mref;            // U57-4
932 	bool m_ifetch_4400;     // U57-5
933 	uint8_t m_special_opt;  // U42
934 	uint16_t m_fetch_addr;
935 
936 	void cpu_mem_map(address_map &map);
937 	void ram_mem_map(address_map &map);
938 	void rom_mem_map(address_map &map);
939 	uint16_t cpu_mem_r(offs_t offset, uint16_t mem_mask = ~0);
940 	void cpu_mem_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
941 	void stm(uint8_t cycle_type);
942 	void on_cycle_end();
943 	void opcode_fetch(uint16_t opcode);
944 	uint8_t get_skoalrom(uint16_t addr);
945 	bool is_rom(uint16_t addr , uint8_t cycle_type) const;
946 };
947 
hp9825t(machine_config & config)948 void hp9825t_state::hp9825t(machine_config &config)
949 {
950 	hp9825_base(config);
951 	m_cpu->set_addrmap(AS_PROGRAM , &hp9825t_state::cpu_mem_map);
952 	m_cpu->stm_cb().set(FUNC(hp9825t_state::stm));
953 	m_cpu->opcode_cb().set(FUNC(hp9825t_state::opcode_fetch));
954 	set_addrmap(0 , &hp9825t_state::ram_mem_map);
955 	set_addrmap(1 , &hp9825t_state::rom_mem_map);
956 
957 	for (auto& finder : m_rom_drawers) {
958 		finder->set_rom_limit(0x6000);
959 	}
960 
961 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9825b_rom");
962 }
963 
machine_start()964 void hp9825t_state::machine_start()
965 {
966 	hp9825_state::machine_start();
967 
968 	m_ram_space = &space(0);
969 	m_rom_space = &space(1);
970 }
971 
device_reset()972 void hp9825t_state::device_reset()
973 {
974 	hp9825_state::device_reset();
975 
976 	for (auto& finder : m_rom_drawers) {
977 		finder->install_rw_handlers(m_rom_space , m_ram_space);
978 	}
979 
980 	// This has to be done before CPU reset or first instruction won't be fetched correctly
981 	m_cycle_type = 0;
982 	m_special_opt = 0xf;
983 }
984 
memory_space_config() const985 device_memory_interface::space_config_vector hp9825t_state::memory_space_config() const
986 {
987 	return space_config_vector {
988 		std::make_pair(0 , &m_ram_space_config),
989 		std::make_pair(1 , &m_rom_space_config)
990 	};
991 }
992 
cpu_mem_map(address_map & map)993 void hp9825t_state::cpu_mem_map(address_map &map)
994 {
995 	map.unmap_value_low();
996 	map(0x0000 , 0x7fff).rw(FUNC(hp9825t_state::cpu_mem_r) , FUNC(hp9825t_state::cpu_mem_w));
997 }
998 
ram_mem_map(address_map & map)999 void hp9825t_state::ram_mem_map(address_map &map)
1000 {
1001 	// 32 kw of RAM covering the whole address space (1st kw not accessible)
1002 	map(0x0000 , 0x7fff).ram();
1003 }
1004 
rom_mem_map(address_map & map)1005 void hp9825t_state::rom_mem_map(address_map &map)
1006 {
1007 	map.unmap_value_low();
1008 	map(0x0000 , 0x2fff).rom().region(":rom" , 0);
1009 	map(0x3400 , 0x3bff).rom().region(":rom" , 0x6800);
1010 	map(0x4000 , 0x53ff).rom().region(":rom" , 0x8000);
1011 }
1012 
cpu_mem_r(offs_t offset,uint16_t mem_mask)1013 uint16_t hp9825t_state::cpu_mem_r(offs_t offset, uint16_t mem_mask)
1014 {
1015 	bool from_rom;
1016 
1017 	if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_RD_MASK) {
1018 		if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) {
1019 			m_fetch_addr = offset;
1020 		}
1021 		from_rom = is_rom(offset , m_cycle_type);
1022 		LOG("rd @%04x CYC=%x %d%d%d%d ROM=%d\n" , offset , m_cycle_type , m_skoalbit , m_second_access , m_mref , m_ifetch_4400 , from_rom);
1023 		if (!(m_cycle_type & (hp_hybrid_cpu_device::CYCLE_IFETCH_MASK | hp_hybrid_cpu_device::CYCLE_DMA_MASK))) {
1024 			on_cycle_end();
1025 		}
1026 		m_cycle_type = 0;
1027 		// TODO: diagnostic read
1028 	} else {
1029 		// Read coming from debugger and not from CPU: fake an ifetch
1030 		from_rom = is_rom(offset , hp_hybrid_cpu_device::CYCLE_IFETCH_MASK);
1031 	}
1032 
1033 	return from_rom ? m_rom_space->read_word(offset , mem_mask) : m_ram_space->read_word(offset , mem_mask);
1034 }
1035 
cpu_mem_w(offs_t offset,uint16_t data,uint16_t mem_mask)1036 void hp9825t_state::cpu_mem_w(offs_t offset, uint16_t data, uint16_t mem_mask)
1037 {
1038 	if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_WR_MASK) {
1039 		if (!(m_cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK)) {
1040 			on_cycle_end();
1041 		}
1042 		m_cycle_type = 0;
1043 	}
1044 	// All write cycles go to RAM
1045 	m_ram_space->write_word(offset , data , mem_mask);
1046 }
1047 
stm(uint8_t cycle_type)1048 void hp9825t_state::stm(uint8_t cycle_type)
1049 {
1050 	LOG("stm %x\n" , cycle_type);
1051 	m_cycle_type = cycle_type;
1052 	if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) {
1053 		m_second_access = false;
1054 		m_mref = false;
1055 		m_ifetch_4400 = false;
1056 		// In case of ifetch from register area this is kept at 0 (because cpu_mem_r is not called)
1057 		// In case of ifetch from RAM/ROM this is set by cpu_mem_r to the fetch address
1058 		m_fetch_addr = 0;
1059 	} else if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_RAL_MASK) {
1060 		if (!(m_cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK)) {
1061 			on_cycle_end();
1062 		}
1063 		m_cycle_type = 0;
1064 	}
1065 }
1066 
on_cycle_end()1067 void hp9825t_state::on_cycle_end()
1068 {
1069 	m_second_access = false;
1070 }
1071 
opcode_fetch(uint16_t opcode)1072 void hp9825t_state::opcode_fetch(uint16_t opcode)
1073 {
1074 	LOG("oc %04x\n" , opcode);
1075 	m_cycle_type = 0;
1076 	// memory referencing instructions
1077 	m_mref = (opcode & 0x7000) != 0x7000;
1078 	m_second_access = true;
1079 	m_ifetch_4400 = (m_fetch_addr & 0x7f00) == 0x0900;
1080 	if (BIT(m_special_opt , 3) && BIT(m_special_opt , 2)) {
1081 		// Set SKOAL bit
1082 		if (m_fetch_addr < 0x20) {
1083 			// Fetch from registers -> SKOAL bit = 0
1084 			m_skoalbit = false;
1085 		} else if ((m_fetch_addr & 0x6000) == 0x6000) {
1086 			// Fetch in [6000..7fff] range -> SKOAL bit = 0
1087 			m_skoalbit = false;
1088 		} else {
1089 			uint8_t tmp = get_skoalrom(m_fetch_addr);
1090 			m_skoalbit = (tmp >> ((~m_fetch_addr >> 12) & 7)) & 1;
1091 		}
1092 	}
1093 	// Decode SKOAL instructions. They are ignored by the hybrid processor
1094 	// as they are not recognized.
1095 	if ((opcode & 0xffc0) == 0x7040) {
1096 		m_special_opt = opcode & 0xf;
1097 		if (!BIT(m_special_opt , 3)) {
1098 			// RAM/ == 0
1099 			m_skoalbit = false;
1100 		} else if (!BIT(m_special_opt , 2)) {
1101 			// ROM/ == 0
1102 			m_skoalbit = true;
1103 		}
1104 	}
1105 }
1106 
get_skoalrom(uint16_t addr)1107 uint8_t hp9825t_state::get_skoalrom(uint16_t addr)
1108 {
1109 	return m_skoalrom->as_u8(~addr & 0x0fff);
1110 }
1111 
is_rom(uint16_t addr,uint8_t cycle_type) const1112 bool hp9825t_state::is_rom(uint16_t addr , uint8_t cycle_type) const
1113 {
1114 	if ((addr & 0x6000) == 0x6000) {
1115 		// [6000..7fff] -> always RAM
1116 		return false;
1117 	} else if ((cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK) != 0 ||
1118 			   !BIT(m_special_opt , 1)) {
1119 		// DMA cycle or BIT/ == 0 -> RAM
1120 		return false;
1121 	} else if (addr >= 0x400 && !BIT(m_special_opt , 0)) {
1122 		// [0400..5fff] and BIN/ == 0 -> RAM
1123 		return false;
1124 	} else {
1125 		bool addr_0800_7fff = (addr & 0x7800) != 0;
1126 		bool addr_0400_07ff = !addr_0800_7fff && BIT(addr , 10);
1127 		bool addr_0000_03ff = !addr_0800_7fff && !BIT(addr , 10);
1128 
1129 		// U58-6
1130 		bool force_rom;
1131 
1132 		// ROM when one or more of these is true:
1133 		// - addr in [0000..03ff]
1134 		// - Ifetch cycle and addr in [0800..5fff]
1135 		// - 2nd access of a memory-referencing instruction not in [0400..07ff] range
1136 		// - skoalbit = 1 and instruction fetched in [0900..09ff] range
1137 		force_rom =
1138 			addr_0000_03ff ||
1139 			((cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) != 0 && addr_0800_7fff) ||
1140 			(m_second_access && m_mref && (!BIT(m_special_opt , 2) || !addr_0400_07ff)) ||
1141 			(m_skoalbit && m_ifetch_4400);
1142 
1143 		if (force_rom) {
1144 			return true;
1145 		} else if (addr_0400_07ff && BIT(m_special_opt , 2)) {
1146 			return false;
1147 		} else {
1148 			return m_skoalbit;
1149 		}
1150 	}
1151 }
1152 
1153 ROM_START(hp9825b)
1154 	ROM_REGION(0xa000 , "cpu" , ROMREGION_16BIT | ROMREGION_BE)
1155 	ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
1156 	ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
1157 	ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
1158 	ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
1159 	ROM_LOAD("plot72.bin"  , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
1160 	ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))
1161 	ROM_LOAD("extio_t.bin" , 0x8800 , 0x1000 , CRC(a708b978) SHA1(baf53c8a2b24d059f95252baf1452188eaf6e4be))
1162 	ROM_LOAD("strings_t.bin",0x9800 , 0x0800 , CRC(b5ca5da5) SHA1(af13abb3c15836c566863c656e1659f7e6f96d04))
1163 ROM_END
1164 
1165 ROM_START(hp9825t)
1166 	ROM_REGION(0xa800 , ":rom" , ROMREGION_16BIT | ROMREGION_BE | ROMREGION_ERASE | ROMREGION_ERASE00)
1167 	ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
1168 	ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
1169 	ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
1170 	ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
1171 	ROM_LOAD("plot72.bin"  , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
1172 	ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))
1173 	ROM_LOAD("extio_t.bin" , 0x8800 , 0x1000 , CRC(a708b978) SHA1(baf53c8a2b24d059f95252baf1452188eaf6e4be))
1174 	ROM_LOAD("strings_t.bin",0x9800 , 0x0800 , CRC(b5ca5da5) SHA1(af13abb3c15836c566863c656e1659f7e6f96d04))
1175 	ROM_LOAD("syspgm.bin"  , 0xa000 , 0x0800 , CRC(8915588f) SHA1(037f497b5ecc3216fb6b8356767cc361fb0b2945))
1176 
1177 	ROM_REGION(0x1000 , "skoalrom" , 0)
1178 	ROM_LOAD("skoalrom.bin" , 0 , 0x1000 , CRC(5e8124d5) SHA1(dedf7f8a10c62b444f04213956083089e97bf219))
1179 ROM_END
1180 
1181 //   YEAR  NAME     PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY            FULLNAME    FLAGS
1182 COMP(1980, hp9825b, 0,      0,      hp9825b, hp9825, hp9825b_state,empty_init, "Hewlett-Packard", "HP 9825B", 0)
1183 COMP(1980, hp9825t, 0,      0,      hp9825t, hp9825, hp9825t_state,empty_init, "Hewlett-Packard", "HP 9825T", 0)
1184