1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 /***************************************************************************
4
5 i8x9x.h
6
7 MCS96, 8x9x branch, the original version
8
9 ***************************************************************************/
10
11 #include "emu.h"
12 #include "i8x9x.h"
13 #include "i8x9xd.h"
14
i8x9x_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock,int data_width)15 i8x9x_device::i8x9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int data_width) :
16 mcs96_device(mconfig, type, tag, owner, clock, data_width, address_map_constructor(FUNC(i8x9x_device::internal_regs), this)),
17 m_ach_cb(*this),
18 m_hso_cb(*this),
19 m_serial_tx_cb(*this),
20 m_in_p0_cb(*this),
21 m_out_p1_cb(*this), m_in_p1_cb(*this),
22 m_out_p2_cb(*this), m_in_p2_cb(*this),
23 base_timer2(0), ad_done(0), hsi_mode(0), hsi_status(0), hso_command(0), ad_command(0), hso_active(0), hso_time(0), ad_result(0), pwm_control(0),
24 port1(0), port2(0),
25 ios0(0), ios1(0), ioc0(0), ioc1(0), extint(false),
26 sbuf(0), sp_con(0), sp_stat(0), serial_send_buf(0), serial_send_timer(0), baud_reg(0), brh(false)
27 {
28 for (auto &hso : hso_info)
29 {
30 hso.command = 0;
31 hso.time = 0;
32 }
33 hso_cam_hold.command = 0;
34 hso_cam_hold.time = 0;
35 }
36
create_disassembler()37 std::unique_ptr<util::disasm_interface> i8x9x_device::create_disassembler()
38 {
39 return std::make_unique<i8x9x_disassembler>();
40 }
41
device_resolve_objects()42 void i8x9x_device::device_resolve_objects()
43 {
44 m_ach_cb.resolve_all();
45 m_hso_cb.resolve_safe();
46 m_serial_tx_cb.resolve_safe();
47 m_in_p0_cb.resolve_safe(0);
48 m_out_p1_cb.resolve_safe();
49 m_in_p1_cb.resolve_safe(0xff);
50 m_out_p2_cb.resolve_safe();
51 m_in_p2_cb.resolve_safe(0xc2);
52 }
53
device_start()54 void i8x9x_device::device_start()
55 {
56 mcs96_device::device_start();
57 cycles_scaling = 3;
58
59 state_add(I8X9X_HSI_MODE, "HSI_MODE", hsi_mode);
60 state_add<u8>(I8X9X_HSI_STATUS, "HSI_STATUS",
61 [this]() -> u8 { return hsi_status; },
62 [this](u8 data) { hsi_status = (data & 0x55) | (hsi_status & 0xaa); });
63 state_add(I8X9X_HSO_TIME, "HSO_TIME", hso_time);
64 state_add(I8X9X_HSO_COMMAND, "HSO_COMMAND", hso_command);
65 state_add(I8X9X_AD_COMMAND, "AD_COMMAND", ad_command).mask(0xf);
66 state_add(I8X9X_AD_RESULT, "AD_RESULT", ad_result);
67 state_add(I8X9X_PWM_CONTROL, "PWM_CONTROL", pwm_control);
68 state_add(I8X9X_SBUF_RX, "SBUF_RX", sbuf);
69 state_add(I8X9X_SBUF_TX, "SBUF_TX", serial_send_buf);
70 state_add(I8X9X_SP_CON, "SP_CON", sp_con).mask(0x1f);
71 state_add(I8X9X_SP_STAT, "SP_STAT", sp_stat).mask(0xe0);
72 state_add(I8X9X_BAUD_RATE, "BAUD_RATE", baud_reg);
73 if (i8x9x_has_p1())
74 state_add<u8>(I8X9X_PORT1, "PORT1", [this]() -> u8 { return port1; }, [this](u8 data) { port1_w(data); });
75 state_add<u8>(I8X9X_PORT2, "PORT2", [this]() -> u8 { return port2; }, [this](u8 data) { port2_w(data); }).mask(i8x9x_p2_mask());
76 state_add(I8X9X_IOC0, "IOC0", ioc0).mask(0xfd);
77 state_add(I8X9X_IOC1, "IOC1", ioc1);
78 state_add<u8>(I8X9X_IOS0, "IOS0", [this]() -> u8 { return ios0; }, [this](u8 data) { ios0_w(data); });
79 state_add(I8X9X_IOS1, "IOS1", ios1);
80
81 save_item(STRUCT_MEMBER(hso_info, command));
82 save_item(STRUCT_MEMBER(hso_info, time));
83 save_item(NAME(hso_cam_hold.command));
84 save_item(NAME(hso_cam_hold.time));
85
86 save_item(NAME(base_timer2));
87 save_item(NAME(ad_done));
88 save_item(NAME(hsi_mode));
89 save_item(NAME(hsi_status));
90 save_item(NAME(hso_command));
91 save_item(NAME(ad_command));
92 save_item(NAME(hso_active));
93 save_item(NAME(hso_time));
94 save_item(NAME(ad_result));
95 save_item(NAME(port1));
96 save_item(NAME(port2));
97 save_item(NAME(pwm_control));
98 save_item(NAME(ios0));
99 save_item(NAME(ios1));
100 save_item(NAME(ioc0));
101 save_item(NAME(ioc1));
102 save_item(NAME(extint));
103 save_item(NAME(sbuf));
104 save_item(NAME(sp_con));
105 save_item(NAME(sp_stat));
106 save_item(NAME(serial_send_buf));
107 save_item(NAME(serial_send_timer));
108 save_item(NAME(baud_reg));
109 save_item(NAME(brh));
110 }
111
device_reset()112 void i8x9x_device::device_reset()
113 {
114 mcs96_device::device_reset();
115 hso_active = 0;
116 hso_command = 0;
117 hso_time = 0;
118 timer2_reset(total_cycles());
119 port1 = 0xff;
120 port2 = 0xc1 & i8x9x_p2_mask(); // P2.5 is cleared
121 ios0 = ios1 = 0x00;
122 ioc0 &= 0xaa;
123 ioc1 = (ioc1 & 0xae) | 0x01;
124 ad_result = 0;
125 ad_done = 0;
126 pwm_control = 0x00;
127 sp_con &= 0x17;
128 sp_stat &= 0x80;
129 serial_send_timer = 0;
130 brh = false;
131 m_out_p1_cb(0xff);
132 m_out_p2_cb(0xc1);
133 m_hso_cb(0);
134 }
135
commit_hso_cam()136 void i8x9x_device::commit_hso_cam()
137 {
138 for(int i=0; i<8; i++)
139 if(!BIT(hso_active, i)) {
140 //logerror("hso cam %02x %04x in slot %d (%04x)\n", hso_command, hso_time, i, PPC);
141 hso_active |= 1 << i;
142 if(hso_active == 0xff)
143 ios0 |= 0x40;
144 hso_info[i].command = hso_command;
145 hso_info[i].time = hso_time;
146 internal_update(total_cycles());
147 return;
148 }
149 ios0 |= 0xc0;
150 hso_cam_hold.command = hso_command;
151 hso_cam_hold.time = hso_time;
152 }
153
ad_start(u64 current_time)154 void i8x9x_device::ad_start(u64 current_time)
155 {
156 ad_result = 8 | (ad_command & 7);
157 if (!BIT(i8x9x_p0_mask(), ad_command & 7))
158 logerror("Analog input on ACH%d does not exist on this device\n", ad_command & 7);
159 else if (m_ach_cb[ad_command & 7].isnull())
160 logerror("Analog input on ACH%d not configured\n", ad_command & 7);
161 else
162 ad_result |= m_ach_cb[ad_command & 7]() << 6;
163 ad_done = current_time + 88;
164 internal_update(current_time);
165 }
166
serial_send(u8 data)167 void i8x9x_device::serial_send(u8 data)
168 {
169 serial_send_buf = data;
170 serial_send_timer = total_cycles() + 9600;
171 }
172
serial_send_done()173 void i8x9x_device::serial_send_done()
174 {
175 serial_send_timer = 0;
176 m_serial_tx_cb(serial_send_buf);
177 pending_irq |= IRQ_SERIAL;
178 sp_stat |= 0x20;
179 check_irq();
180 }
181
internal_regs(address_map & map)182 void i8x9x_device::internal_regs(address_map &map)
183 {
184 map(0x00, 0x01).lr16([] () -> u16 { return 0; }, "r0").nopw();
185 map(0x02, 0x03).r(FUNC(i8x9x_device::ad_result_r)); // 8-bit access
186 map(0x02, 0x02).w(FUNC(i8x9x_device::ad_command_w));
187 map(0x03, 0x03).w(FUNC(i8x9x_device::hsi_mode_w));
188 map(0x04, 0x05).rw(FUNC(i8x9x_device::hsi_time_r), FUNC(i8x9x_device::hso_time_w)); // 16-bit access
189 map(0x06, 0x06).rw(FUNC(i8x9x_device::hsi_status_r), FUNC(i8x9x_device::hso_command_w));
190 map(0x07, 0x07).rw(FUNC(i8x9x_device::sbuf_r), FUNC(i8x9x_device::sbuf_w));
191 map(0x08, 0x08).rw(FUNC(i8x9x_device::int_mask_r), FUNC(i8x9x_device::int_mask_w));
192 map(0x09, 0x09).rw(FUNC(i8x9x_device::int_pending_r), FUNC(i8x9x_device::int_pending_w));
193 map(0x0a, 0x0b).r(FUNC(i8x9x_device::timer1_r)); // 16-bit access
194 map(0x0c, 0x0d).r(FUNC(i8x9x_device::timer2_r)); // 16-bit access
195 map(0x0a, 0x0a).w(FUNC(i8x9x_device::watchdog_w));
196 map(0x0e, 0x0e).rw(FUNC(i8x9x_device::port0_r), FUNC(i8x9x_device::baud_rate_w));
197 map(0x0f, 0x0f).rw(FUNC(i8x9x_device::port1_r), FUNC(i8x9x_device::port1_w));
198 map(0x10, 0x10).rw(FUNC(i8x9x_device::port2_r), FUNC(i8x9x_device::port2_w));
199 map(0x11, 0x11).rw(FUNC(i8x9x_device::sp_stat_r), FUNC(i8x9x_device::sp_con_w));
200 map(0x15, 0x15).rw(FUNC(i8x9x_device::ios0_r), FUNC(i8x9x_device::ioc0_w));
201 map(0x16, 0x16).rw(FUNC(i8x9x_device::ios1_r), FUNC(i8x9x_device::ioc1_w));
202 map(0x17, 0x17).w(FUNC(i8x9x_device::pwm_control_w));
203 map(0x18, 0xff).ram().share("register_file");
204 }
205
ad_command_w(u8 data)206 void i8x9x_device::ad_command_w(u8 data)
207 {
208 ad_command = data & 0xf;
209 if (ad_command & 8)
210 ad_start(total_cycles());
211 }
212
ad_result_r(offs_t offset)213 u8 i8x9x_device::ad_result_r(offs_t offset)
214 {
215 return ad_result >> (offset ? 8 : 0);
216 }
217
hsi_mode_w(u8 data)218 void i8x9x_device::hsi_mode_w(u8 data)
219 {
220 hsi_mode = data;
221 logerror("hsi_mode %02x (%04x)\n", data, PPC);
222 }
223
hso_time_w(u16 data)224 void i8x9x_device::hso_time_w(u16 data)
225 {
226 hso_time = data;
227 commit_hso_cam();
228 }
229
hsi_time_r()230 u16 i8x9x_device::hsi_time_r()
231 {
232 if (!machine().side_effects_disabled())
233 logerror("read hsi time (%04x)\n", PPC);
234 return 0x0000;
235 }
236
hso_command_w(u8 data)237 void i8x9x_device::hso_command_w(u8 data)
238 {
239 hso_command = data;
240 }
241
hsi_status_r()242 u8 i8x9x_device::hsi_status_r()
243 {
244 return hsi_status;
245 }
246
sbuf_w(u8 data)247 void i8x9x_device::sbuf_w(u8 data)
248 {
249 logerror("sbuf %02x (%04x)\n", data, PPC);
250 serial_send(data);
251 }
252
sbuf_r()253 u8 i8x9x_device::sbuf_r()
254 {
255 if (!machine().side_effects_disabled())
256 logerror("read sbuf %02x (%04x)\n", sbuf, PPC);
257 return sbuf;
258 }
259
watchdog_w(u8 data)260 void i8x9x_device::watchdog_w(u8 data)
261 {
262 logerror("watchdog %02x (%04x)\n", data, PPC);
263 }
264
timer1_r()265 u16 i8x9x_device::timer1_r()
266 {
267 u16 data = timer_value(1, total_cycles());
268 if (0 && !machine().side_effects_disabled())
269 logerror("read timer1 %04x (%04x)\n", data, PPC);
270 return data;
271 }
272
timer2_r()273 u16 i8x9x_device::timer2_r()
274 {
275 u16 data = timer_value(2, total_cycles());
276 if (!machine().side_effects_disabled())
277 logerror("read timer2 %04x (%04x)\n", data, PPC);
278 return data;
279 }
280
baud_rate_w(u8 data)281 void i8x9x_device::baud_rate_w(u8 data)
282 {
283 if (brh)
284 baud_reg = (baud_reg & 0x00ff) | u16(data) << 8;
285 else
286 baud_reg = (baud_reg & 0xff00) | data;
287 if (!machine().side_effects_disabled())
288 brh = !brh;
289 }
290
port0_r()291 u8 i8x9x_device::port0_r()
292 {
293 static int last = -1;
294 if (!machine().side_effects_disabled() && m_in_p0_cb() != last)
295 {
296 last = m_in_p0_cb();
297 logerror("read p0 %02x\n", last);
298 }
299 return m_in_p0_cb() & i8x9x_p0_mask();
300 }
301
port1_w(u8 data)302 void i8x9x_device::port1_w(u8 data)
303 {
304 if (!i8x9x_has_p1())
305 {
306 logerror("%s: Write %02x to nonexistent port 1\n", machine().describe_context(), data);
307 return;
308 }
309
310 port1 = data;
311 m_out_p1_cb(data);
312 }
313
port1_r()314 u8 i8x9x_device::port1_r()
315 {
316 if (!i8x9x_has_p1())
317 return 0xff;
318
319 return m_in_p1_cb() & port1;
320 }
321
port2_w(u8 data)322 void i8x9x_device::port2_w(u8 data)
323 {
324 data &= 0xe1 & i8x9x_p2_mask();
325 port2 = data;
326 m_out_p2_cb(data);
327 }
328
port2_r()329 u8 i8x9x_device::port2_r()
330 {
331 // P2.0 and P2.5 are for output only (but can be read back despite what Intel claims?)
332 return (m_in_p2_cb() | 0x25 | ~i8x9x_p2_mask()) & (port2 | (extint ? 0x1e : 0x1a));
333 }
334
sp_con_w(u8 data)335 void i8x9x_device::sp_con_w(u8 data)
336 {
337 sp_con = data & 0x1f;
338 }
339
sp_stat_r()340 u8 i8x9x_device::sp_stat_r()
341 {
342 u8 res = sp_stat;
343 if (!machine().side_effects_disabled())
344 {
345 sp_stat &= 0x80;
346 logerror("read sp stat %02x (%04x)\n", res, PPC);
347 }
348 return res;
349 }
350
ioc0_w(u8 data)351 void i8x9x_device::ioc0_w(u8 data)
352 {
353 ioc0 = data & 0xfd;
354 if (BIT(data, 1))
355 timer2_reset(total_cycles());
356 }
357
ios0_r()358 u8 i8x9x_device::ios0_r()
359 {
360 return ios0;
361 }
362
ios0_w(u8 data)363 void i8x9x_device::ios0_w(u8 data)
364 {
365 u8 mask = (data ^ ios0) & 0x3f;
366 ios0 = (data & 0x3f) | (ios0 & 0xc0);
367 if (mask != 0)
368 m_hso_cb(0, data & 0x3f, mask);
369 }
370
ioc1_w(u8 data)371 void i8x9x_device::ioc1_w(u8 data)
372 {
373 ioc1 = data;
374 }
375
ios1_r()376 u8 i8x9x_device::ios1_r()
377 {
378 u8 res = ios1;
379 if (!machine().side_effects_disabled())
380 ios1 = ios1 & 0xc0;
381 return res;
382 }
383
pwm_control_w(u8 data)384 void i8x9x_device::pwm_control_w(u8 data)
385 {
386 pwm_control = data;
387 }
388
do_exec_partial()389 void i8x9x_device::do_exec_partial()
390 {
391 }
392
serial_w(u8 val)393 void i8x9x_device::serial_w(u8 val)
394 {
395 sbuf = val;
396 sp_stat |= 0x40;
397 pending_irq |= IRQ_SERIAL;
398 check_irq();
399 }
400
timer_value(int timer,u64 current_time) const401 u16 i8x9x_device::timer_value(int timer, u64 current_time) const
402 {
403 if(timer == 2)
404 current_time -= base_timer2;
405 return current_time >> 3;
406 }
407
timer_time_until(int timer,u64 current_time,u16 timer_value) const408 u64 i8x9x_device::timer_time_until(int timer, u64 current_time, u16 timer_value) const
409 {
410 u64 timer_base = timer == 2 ? base_timer2 : 0;
411 u64 delta = (current_time - timer_base) >> 3;
412 u32 tdelta = u16(timer_value - delta);
413 if(!tdelta)
414 tdelta = 0x10000;
415 return timer_base + ((delta + tdelta) << 3);
416 }
417
timer2_reset(u64 current_time)418 void i8x9x_device::timer2_reset(u64 current_time)
419 {
420 base_timer2 = current_time;
421 }
422
set_hsi_state(int pin,bool state)423 void i8x9x_device::set_hsi_state(int pin, bool state)
424 {
425 if(pin == 0 && !BIT(hsi_status, 1) && state) {
426 if(BIT(ioc1, 1)) {
427 pending_irq |= IRQ_HSI0;
428 check_irq();
429 }
430 if((ioc0 & 0x28) == 0x28)
431 timer2_reset(total_cycles());
432 }
433
434 if(state)
435 hsi_status |= 2 << (pin * 2);
436 else
437 hsi_status &= ~(2 << (pin * 2));
438 }
439
trigger_cam(int id,u64 current_time)440 void i8x9x_device::trigger_cam(int id, u64 current_time)
441 {
442 hso_cam_entry &cam = hso_info[id];
443 if(hso_active == 0xff && !BIT(ios0, 7))
444 ios0 &= 0xbf;
445 hso_active &= ~(1 << id);
446 switch(cam.command & 0x0f) {
447 case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5:
448 set_hso(1 << (cam.command & 7), BIT(cam.command, 5));
449 break;
450
451 case 0x6:
452 set_hso(0x03, BIT(cam.command, 5));
453 break;
454
455 case 0x7:
456 set_hso(0x0c, BIT(cam.command, 5));
457 break;
458
459 case 0x8: case 0x9: case 0xa: case 0xb:
460 ios1 |= 1 << (cam.command & 3);
461 break;
462
463 case 0xe:
464 timer2_reset(current_time);
465 break;
466
467 case 0xf:
468 ad_start(current_time);
469 break;
470
471 default:
472 logerror("HSO action %x undefined\n", cam.command & 0x0f);
473 break;
474 }
475
476 if(BIT(cam.command, 4))
477 {
478 pending_irq |= BIT(cam.command, 3) ? IRQ_SOFT : IRQ_HSO;
479 check_irq();
480 }
481 }
482
set_hso(u8 mask,bool state)483 void i8x9x_device::set_hso(u8 mask, bool state)
484 {
485 if(state)
486 ios0 |= mask;
487 else
488 ios0 &= ~mask;
489 m_hso_cb(0, ios0 & 0x3f, mask);
490 }
491
internal_update(u64 current_time)492 void i8x9x_device::internal_update(u64 current_time)
493 {
494 u16 current_timer1 = timer_value(1, current_time);
495 u16 current_timer2 = timer_value(2, current_time);
496
497 for(int i=0; i<8; i++)
498 if(BIT(hso_active, i)) {
499 u8 cmd = hso_info[i].command;
500 u16 t = hso_info[i].time;
501 if(((cmd & 0x40) && t == current_timer2) ||
502 (!(cmd & 0x40) && t == current_timer1)) {
503 //logerror("hso cam %02x %04x in slot %d triggered\n", cmd, t, i);
504 trigger_cam(i, current_time);
505 }
506 }
507
508 if(ad_done && current_time >= ad_done) {
509 // A/D conversion complete
510 ad_done = 0;
511 ad_result &= ~8;
512 pending_irq |= IRQ_AD;
513 check_irq();
514 }
515
516 if(current_time == serial_send_timer)
517 serial_send_done();
518
519 u64 event_time = 0;
520 for(int i=0; i<8; i++) {
521 if(!BIT(hso_active, i) && BIT(ios0, 7)) {
522 hso_info[i] = hso_cam_hold;
523 hso_active |= 1 << i;
524 ios0 &= 0x7f;
525 if(hso_active == 0xff)
526 ios0 |= 0x40;
527 logerror("hso cam %02x %04x in slot %d from hold\n", hso_cam_hold.command, hso_cam_hold.time, i);
528 }
529 if(BIT(hso_active, i)) {
530 u64 new_time = timer_time_until(hso_info[i].command & 0x40 ? 2 : 1, current_time, hso_info[i].time);
531 if(!event_time || new_time < event_time)
532 event_time = new_time;
533 }
534 }
535
536 if(ad_done && ad_done < event_time)
537 event_time = ad_done;
538
539 if(serial_send_timer && serial_send_timer < event_time)
540 event_time = serial_send_timer;
541
542 recompute_bcount(event_time);
543 }
544
execute_set_input(int linenum,int state)545 void i8x9x_device::execute_set_input(int linenum, int state)
546 {
547 switch(linenum) {
548 case EXTINT_LINE:
549 if(!extint && state && !BIT(ioc1, 1)) {
550 pending_irq |= IRQ_EXTINT;
551 check_irq();
552 }
553 extint = state;
554 break;
555
556 case HSI0_LINE:
557 set_hsi_state(0, state);
558 break;
559
560 case HSI1_LINE:
561 set_hsi_state(1, state);
562 break;
563
564 case HSI2_LINE:
565 set_hsi_state(2, state);
566 break;
567
568 case HSI3_LINE:
569 set_hsi_state(3, state);
570 break;
571 }
572 }
573
c8095_90_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)574 c8095_90_device::c8095_90_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
575 i8x9x_device(mconfig, C8095_90, tag, owner, clock, 16)
576 {
577 }
578
n8097bh_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)579 n8097bh_device::n8097bh_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
580 i8x9x_device(mconfig, N8097BH, tag, owner, clock, 16)
581 {
582 }
583
p8098_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)584 p8098_device::p8098_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
585 i8x9x_device(mconfig, P8098, tag, owner, clock, 8)
586 {
587 }
588
p8798_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)589 p8798_device::p8798_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
590 i8x9x_device(mconfig, P8798, tag, owner, clock, 8)
591 {
592 }
593
594 DEFINE_DEVICE_TYPE(C8095_90, c8095_90_device, "c8095_90", "Intel C8095-90")
595 DEFINE_DEVICE_TYPE(N8097BH, n8097bh_device, "n8097bh", "Intel N8097BH")
596 DEFINE_DEVICE_TYPE(P8098, p8098_device, "p8098", "Intel P8098")
597 DEFINE_DEVICE_TYPE(P8798, p8798_device, "p8798", "Intel P8798")
598
599 #include "cpu/mcs96/i8x9x.hxx"
600