1 /* dv-m68hc11.c -- CPU 68HC11&68HC12 as a device.
2 Copyright (C) 1999-2024 Free Software Foundation, Inc.
3 Written by Stephane Carrez (stcarrez@nerim.fr)
4 (From a driver model Contributed by Cygnus Solutions.)
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 /* This must come before any other includes. */
22 #include "defs.h"
23
24 #include "sim-main.h"
25 #include "sim-hw.h"
26 #include "hw-main.h"
27 #include "sim-options.h"
28 #include "hw-base.h"
29 #include <limits.h>
30 #include <stdlib.h>
31
32 #include "m68hc11-sim.h"
33
34 /* DEVICE
35
36 m68hc11cpu - m68hc11 cpu virtual device
37 m68hc12cpu - m68hc12 cpu virtual device
38
39 DESCRIPTION
40
41 Implements the external m68hc11/68hc12 functionality. This includes
42 the delivery of of interrupts generated from other devices and the
43 handling of device specific registers.
44
45
46 PROPERTIES
47
48 reg <base> <size>
49
50 Register base (should be 0x1000 0x03f for C11, 0x0000 0x3ff for HC12).
51
52 clock <hz>
53
54 Frequency of the quartz used by the processor.
55
56 mode [single | expanded | bootstrap | test]
57
58 Cpu operating mode (the MODA and MODB external pins).
59
60
61 PORTS
62
63 reset (input)
64
65 Reset the cpu and generates a cpu-reset event (used to reset
66 other devices).
67
68 nmi (input)
69
70 Deliver a non-maskable interrupt to the processor.
71
72
73 set-port-a (input)
74 set-port-c (input)
75 set-pord-d (input)
76
77 Allow an external device to set the value of port A, C or D inputs.
78
79
80 cpu-reset (output)
81
82 Event generated after the CPU performs a reset.
83
84
85 port-a (output)
86 port-b (output)
87 port-c (output)
88 port-d (output)
89
90 Event generated when the value of the output port A, B, C or D
91 changes.
92
93
94 BUGS
95
96 When delivering an interrupt, this code assumes that there is only
97 one processor (number 0).
98
99 */
100
101 enum
102 {
103 OPTION_OSC_SET = OPTION_START,
104 OPTION_OSC_CLEAR,
105 OPTION_OSC_INFO
106 };
107
108 static DECLARE_OPTION_HANDLER (m68hc11_option_handler);
109
110 static const OPTION m68hc11_options[] =
111 {
112 { {"osc-set", required_argument, NULL, OPTION_OSC_SET },
113 '\0', "BIT,FREQ", "Set the oscillator on input port BIT",
114 m68hc11_option_handler },
115 { {"osc-clear", required_argument, NULL, OPTION_OSC_CLEAR },
116 '\0', "BIT", "Clear oscillator on input port BIT",
117 m68hc11_option_handler },
118 { {"osc-info", no_argument, NULL, OPTION_OSC_INFO },
119 '\0', NULL, "Print information about current input oscillators",
120 m68hc11_option_handler },
121
122 { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL }
123 };
124
125 struct input_osc
126 {
127 int64_t on_time;
128 int64_t off_time;
129 int64_t repeat;
130 struct hw_event *event;
131 const char *name;
132 uint8_t mask;
133 uint8_t value;
134 uint16_t addr;
135 };
136
137 #define NR_PORT_A_OSC (4)
138 #define NR_PORT_B_OSC (0)
139 #define NR_PORT_C_OSC (8)
140 #define NR_PORT_D_OSC (6)
141 #define NR_OSC (NR_PORT_A_OSC + NR_PORT_B_OSC + NR_PORT_C_OSC + NR_PORT_D_OSC)
142 struct m68hc11cpu {
143 /* Pending interrupts for delivery by event handler. */
144 int pending_reset;
145 int pending_nmi;
146 int pending_level;
147 struct hw_event *event;
148 unsigned_word attach_address;
149 unsigned int attach_size;
150 int attach_space;
151 int last_oscillator;
152 struct input_osc oscillators[NR_OSC];
153 };
154
155
156
157 /* input port ID's */
158
159 enum {
160 RESET_PORT,
161 NMI_PORT,
162 IRQ_PORT,
163 CPU_RESET_PORT,
164 SET_PORT_A,
165 SET_PORT_C,
166 SET_PORT_D,
167 CPU_WRITE_PORT,
168 PORT_A,
169 PORT_B,
170 PORT_C,
171 PORT_D,
172 CAPTURE
173 };
174
175
176 static const struct hw_port_descriptor m68hc11cpu_ports[] = {
177
178 /* Interrupt inputs. */
179 { "reset", RESET_PORT, 0, input_port, },
180 { "nmi", NMI_PORT, 0, input_port, },
181 { "irq", IRQ_PORT, 0, input_port, },
182
183 { "set-port-a", SET_PORT_A, 0, input_port, },
184 { "set-port-c", SET_PORT_C, 0, input_port, },
185 { "set-port-d", SET_PORT_D, 0, input_port, },
186
187 { "cpu-write-port", CPU_WRITE_PORT, 0, input_port, },
188
189 /* Events generated for connection to other devices. */
190 { "cpu-reset", CPU_RESET_PORT, 0, output_port, },
191
192 /* Events generated when the corresponding port is
193 changed by the program. */
194 { "port-a", PORT_A, 0, output_port, },
195 { "port-b", PORT_B, 0, output_port, },
196 { "port-c", PORT_C, 0, output_port, },
197 { "port-d", PORT_D, 0, output_port, },
198
199 { "capture", CAPTURE, 0, output_port, },
200
201 { NULL, },
202 };
203
204 static hw_io_read_buffer_method m68hc11cpu_io_read_buffer;
205 static hw_io_write_buffer_method m68hc11cpu_io_write_buffer;
206 static hw_ioctl_method m68hc11_ioctl;
207
208 /* Finish off the partially created hw device. Attach our local
209 callbacks. Wire up our port names etc. */
210
211 static hw_port_event_method m68hc11cpu_port_event;
212
213 static void make_oscillator (struct m68hc11cpu *controller,
214 const char *id, uint16_t addr, uint8_t mask);
215 static struct input_osc *find_oscillator (struct m68hc11cpu *controller,
216 const char *id);
217 static void reset_oscillators (struct hw *me);
218
219 static void
dv_m6811_attach_address_callback(struct hw * me,int level,int space,address_word addr,address_word nr_bytes,struct hw * client)220 dv_m6811_attach_address_callback (struct hw *me,
221 int level,
222 int space,
223 address_word addr,
224 address_word nr_bytes,
225 struct hw *client)
226 {
227 HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, sz=%ld, client=%s",
228 level, space, (unsigned long) addr, (unsigned long) nr_bytes,
229 hw_path (client)));
230
231 if (space != io_map)
232 {
233 sim_core_attach (hw_system (me),
234 NULL, /*cpu*/
235 level,
236 access_read_write_exec,
237 space, addr,
238 nr_bytes,
239 0, /* modulo */
240 client,
241 NULL);
242 }
243 else
244 {
245 /*printf("Attach from sub device: %d\n", (long) addr);*/
246 sim_core_attach (hw_system (me),
247 NULL, /*cpu*/
248 level,
249 access_io,
250 space, addr,
251 nr_bytes,
252 0, /* modulo */
253 client,
254 NULL);
255 }
256 }
257
258 static void
m68hc11_delete(struct hw * me)259 m68hc11_delete (struct hw* me)
260 {
261 struct m68hc11cpu *controller;
262
263 controller = hw_data (me);
264
265 reset_oscillators (me);
266 hw_detach_address (me, M6811_IO_LEVEL,
267 controller->attach_space,
268 controller->attach_address,
269 controller->attach_size, me);
270 }
271
272
273 static void
attach_m68hc11_regs(struct hw * me,struct m68hc11cpu * controller)274 attach_m68hc11_regs (struct hw *me,
275 struct m68hc11cpu *controller)
276 {
277 SIM_DESC sd;
278 sim_cpu *cpu;
279 struct m68hc11_sim_cpu *m68hc11_cpu;
280 reg_property_spec reg;
281 const char *cpu_mode;
282
283 if (hw_find_property (me, "reg") == NULL)
284 hw_abort (me, "Missing \"reg\" property");
285
286 if (!hw_find_reg_array_property (me, "reg", 0, ®))
287 hw_abort (me, "\"reg\" property must contain one addr/size entry");
288
289 hw_unit_address_to_attach_address (hw_parent (me),
290 ®.address,
291 &controller->attach_space,
292 &controller->attach_address,
293 me);
294 hw_unit_size_to_attach_size (hw_parent (me),
295 ®.size,
296 &controller->attach_size, me);
297
298 hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
299 controller->attach_space,
300 controller->attach_address,
301 controller->attach_size,
302 me);
303 set_hw_delete (me, m68hc11_delete);
304
305 /* Get cpu frequency. */
306 sd = hw_system (me);
307 cpu = STATE_CPU (sd, 0);
308 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
309 if (hw_find_property (me, "clock") != NULL)
310 {
311 m68hc11_cpu->cpu_frequency = hw_find_integer_property (me, "clock");
312 }
313 else
314 {
315 m68hc11_cpu->cpu_frequency = 8*1000*1000;
316 }
317
318 if (hw_find_property (me, "use_bank") != NULL)
319 hw_attach_address (hw_parent (me), 0,
320 exec_map,
321 m68hc11_cpu->bank_start,
322 m68hc11_cpu->bank_end - m68hc11_cpu->bank_start,
323 me);
324
325 cpu_mode = "expanded";
326 if (hw_find_property (me, "mode") != NULL)
327 cpu_mode = hw_find_string_property (me, "mode");
328
329 if (strcmp (cpu_mode, "test") == 0)
330 m68hc11_cpu->cpu_mode = M6811_MDA | M6811_SMOD;
331 else if (strcmp (cpu_mode, "bootstrap") == 0)
332 m68hc11_cpu->cpu_mode = M6811_SMOD;
333 else if (strcmp (cpu_mode, "single") == 0)
334 m68hc11_cpu->cpu_mode = 0;
335 else
336 m68hc11_cpu->cpu_mode = M6811_MDA;
337
338 controller->last_oscillator = 0;
339
340 /* Create oscillators for input port A. */
341 make_oscillator (controller, "A7", M6811_PORTA, 0x80);
342 make_oscillator (controller, "A2", M6811_PORTA, 0x04);
343 make_oscillator (controller, "A1", M6811_PORTA, 0x02);
344 make_oscillator (controller, "A0", M6811_PORTA, 0x01);
345
346 /* port B is output only. */
347
348 /* Create oscillators for input port C. */
349 make_oscillator (controller, "C0", M6811_PORTC, 0x01);
350 make_oscillator (controller, "C1", M6811_PORTC, 0x02);
351 make_oscillator (controller, "C2", M6811_PORTC, 0x04);
352 make_oscillator (controller, "C3", M6811_PORTC, 0x08);
353 make_oscillator (controller, "C4", M6811_PORTC, 0x10);
354 make_oscillator (controller, "C5", M6811_PORTC, 0x20);
355 make_oscillator (controller, "C6", M6811_PORTC, 0x40);
356 make_oscillator (controller, "C7", M6811_PORTC, 0x80);
357
358 /* Create oscillators for input port D. */
359 make_oscillator (controller, "D0", M6811_PORTD, 0x01);
360 make_oscillator (controller, "D1", M6811_PORTD, 0x02);
361 make_oscillator (controller, "D2", M6811_PORTD, 0x04);
362 make_oscillator (controller, "D3", M6811_PORTD, 0x08);
363 make_oscillator (controller, "D4", M6811_PORTD, 0x10);
364 make_oscillator (controller, "D5", M6811_PORTD, 0x20);
365
366 /* Add oscillator commands. */
367 sim_add_option_table (sd, 0, m68hc11_options);
368 }
369
370 static void
m68hc11cpu_finish(struct hw * me)371 m68hc11cpu_finish (struct hw *me)
372 {
373 struct m68hc11cpu *controller;
374
375 controller = HW_ZALLOC (me, struct m68hc11cpu);
376 set_hw_data (me, controller);
377 set_hw_io_read_buffer (me, m68hc11cpu_io_read_buffer);
378 set_hw_io_write_buffer (me, m68hc11cpu_io_write_buffer);
379 set_hw_ports (me, m68hc11cpu_ports);
380 set_hw_port_event (me, m68hc11cpu_port_event);
381 set_hw_attach_address (me, dv_m6811_attach_address_callback);
382 #ifdef set_hw_ioctl
383 set_hw_ioctl (me, m68hc11_ioctl);
384 #else
385 me->to_ioctl = m68hc11_ioctl;
386 #endif
387
388 /* Initialize the pending interrupt flags. */
389 controller->pending_level = 0;
390 controller->pending_reset = 0;
391 controller->pending_nmi = 0;
392 controller->event = NULL;
393
394 attach_m68hc11_regs (me, controller);
395 }
396
397 /* An event arrives on an interrupt port. */
398
399 static void
deliver_m68hc11cpu_interrupt(struct hw * me,void * data)400 deliver_m68hc11cpu_interrupt (struct hw *me, void *data)
401 {
402 }
403
404 static void
make_oscillator(struct m68hc11cpu * controller,const char * name,uint16_t addr,uint8_t mask)405 make_oscillator (struct m68hc11cpu *controller, const char *name,
406 uint16_t addr, uint8_t mask)
407 {
408 struct input_osc *osc;
409
410 if (controller->last_oscillator >= NR_OSC)
411 hw_abort (0, "Too many oscillators");
412
413 osc = &controller->oscillators[controller->last_oscillator];
414 osc->name = name;
415 osc->addr = addr;
416 osc->mask = mask;
417 controller->last_oscillator++;
418 }
419
420 /* Find the oscillator given the input port name. */
421 static struct input_osc *
find_oscillator(struct m68hc11cpu * controller,const char * name)422 find_oscillator (struct m68hc11cpu *controller, const char *name)
423 {
424 int i;
425
426 for (i = 0; i < controller->last_oscillator; i++)
427 if (strcasecmp (controller->oscillators[i].name, name) == 0)
428 return &controller->oscillators[i];
429
430 return 0;
431 }
432
433 static void
oscillator_handler(struct hw * me,void * data)434 oscillator_handler (struct hw *me, void *data)
435 {
436 struct input_osc *osc = (struct input_osc*) data;
437 SIM_DESC sd;
438 sim_cpu *cpu;
439 struct m68hc11_sim_cpu *m68hc11_cpu;
440 int64_t dt;
441 uint8_t val;
442
443 sd = hw_system (me);
444 cpu = STATE_CPU (sd, 0);
445 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
446
447 /* Change the input bit. */
448 osc->value ^= osc->mask;
449 val = m68hc11_cpu->ios[osc->addr] & ~osc->mask;
450 val |= osc->value;
451 m68hc11cpu_set_port (me, cpu, osc->addr, val);
452
453 /* Setup event to toggle the bit. */
454 if (osc->value)
455 dt = osc->on_time;
456 else
457 dt = osc->off_time;
458
459 if (dt && --osc->repeat >= 0)
460 {
461 sim_events *events = STATE_EVENTS (sd);
462
463 dt += events->nr_ticks_to_process;
464 osc->event = hw_event_queue_schedule (me, dt, oscillator_handler, osc);
465 }
466 else
467 osc->event = 0;
468 }
469
470 static void
reset_oscillators(struct hw * me)471 reset_oscillators (struct hw *me)
472 {
473 struct m68hc11cpu *controller = hw_data (me);
474 int i;
475
476 for (i = 0; i < controller->last_oscillator; i++)
477 {
478 if (controller->oscillators[i].event)
479 {
480 hw_event_queue_deschedule (me, controller->oscillators[i].event);
481 controller->oscillators[i].event = 0;
482 }
483 }
484 }
485
486 static void
m68hc11cpu_port_event(struct hw * me,int my_port,struct hw * source,int source_port,int level)487 m68hc11cpu_port_event (struct hw *me,
488 int my_port,
489 struct hw *source,
490 int source_port,
491 int level)
492 {
493 struct m68hc11cpu *controller = hw_data (me);
494 SIM_DESC sd;
495 sim_cpu *cpu;
496
497 sd = hw_system (me);
498 cpu = STATE_CPU (sd, 0);
499 switch (my_port)
500 {
501 case RESET_PORT:
502 HW_TRACE ((me, "port-in reset"));
503
504 /* The reset is made in 3 steps:
505 - First, cleanup the current sim_cpu struct.
506 - Reset the devices.
507 - Restart the cpu for the reset (get the CPU mode from the
508 CONFIG register that gets initialized by EEPROM device). */
509 cpu_reset (cpu);
510 reset_oscillators (me);
511 hw_port_event (me, CPU_RESET_PORT, 1);
512 cpu_restart (cpu);
513 break;
514
515 case NMI_PORT:
516 controller->pending_nmi = 1;
517 HW_TRACE ((me, "port-in nmi"));
518 break;
519
520 case IRQ_PORT:
521 /* level == 0 means that the interrupt was cleared. */
522 if(level == 0)
523 controller->pending_level = -1; /* signal end of interrupt */
524 else
525 controller->pending_level = level;
526 HW_TRACE ((me, "port-in level=%d", level));
527 break;
528
529 case SET_PORT_A:
530 m68hc11cpu_set_port (me, cpu, M6811_PORTA, level);
531 break;
532
533 case SET_PORT_C:
534 m68hc11cpu_set_port (me, cpu, M6811_PORTC, level);
535 break;
536
537 case SET_PORT_D:
538 m68hc11cpu_set_port (me, cpu, M6811_PORTD, level);
539 break;
540
541 case CPU_WRITE_PORT:
542 break;
543
544 default:
545 hw_abort (me, "bad switch");
546 break;
547 }
548
549 /* Schedule an event to be delivered immediately after current
550 instruction. */
551 if(controller->event != NULL)
552 hw_event_queue_deschedule(me, controller->event);
553 controller->event =
554 hw_event_queue_schedule (me, 0, deliver_m68hc11cpu_interrupt, NULL);
555 }
556
557
558 io_reg_desc config_desc[] = {
559 { M6811_NOSEC, "NOSEC ", "Security Mode Disable" },
560 { M6811_NOCOP, "NOCOP ", "COP System Disable" },
561 { M6811_ROMON, "ROMON ", "Enable On-chip Rom" },
562 { M6811_EEON, "EEON ", "Enable On-chip EEprom" },
563 { 0, 0, 0 }
564 };
565
566 io_reg_desc hprio_desc[] = {
567 { M6811_RBOOT, "RBOOT ", "Read Bootstrap ROM" },
568 { M6811_SMOD, "SMOD ", "Special Mode" },
569 { M6811_MDA, "MDA ", "Mode Select A" },
570 { M6811_IRV, "IRV ", "Internal Read Visibility" },
571 { 0, 0, 0 }
572 };
573
574 io_reg_desc option_desc[] = {
575 { M6811_ADPU, "ADPU ", "A/D Powerup" },
576 { M6811_CSEL, "CSEL ", "A/D/EE Charge pump clock source select" },
577 { M6811_IRQE, "IRQE ", "IRQ Edge/Level sensitive" },
578 { M6811_DLY, "DLY ", "Stop exit turn on delay" },
579 { M6811_CME, "CME ", "Clock Monitor Enable" },
580 { M6811_CR1, "CR1 ", "COP timer rate select (CR1)" },
581 { M6811_CR0, "CR0 ", "COP timer rate select (CR0)" },
582 { 0, 0, 0 }
583 };
584
585 static void
m68hc11_info(struct hw * me)586 m68hc11_info (struct hw *me)
587 {
588 SIM_DESC sd;
589 uint16_t base = 0;
590 sim_cpu *cpu;
591 struct m68hc11_sim_cpu *m68hc11_cpu;
592 uint8_t val;
593
594 sd = hw_system (me);
595 cpu = STATE_CPU (sd, 0);
596 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
597
598 base = cpu_get_io_base (cpu);
599 sim_io_printf (sd, "M68HC11:\n");
600
601 val = m68hc11_cpu->ios[M6811_HPRIO];
602 print_io_byte (sd, "HPRIO ", hprio_desc, val, base + M6811_HPRIO);
603 switch (m68hc11_cpu->cpu_mode)
604 {
605 case M6811_MDA | M6811_SMOD:
606 sim_io_printf (sd, "[test]\n");
607 break;
608 case M6811_SMOD:
609 sim_io_printf (sd, "[bootstrap]\n");
610 break;
611 case M6811_MDA:
612 sim_io_printf (sd, "[extended]\n");
613 break;
614 default:
615 sim_io_printf (sd, "[single]\n");
616 break;
617 }
618
619 val = m68hc11_cpu->ios[M6811_CONFIG];
620 print_io_byte (sd, "CONFIG", config_desc, val, base + M6811_CONFIG);
621 sim_io_printf (sd, "\n");
622
623 val = m68hc11_cpu->ios[M6811_OPTION];
624 print_io_byte (sd, "OPTION", option_desc, val, base + M6811_OPTION);
625 sim_io_printf (sd, "\n");
626
627 val = m68hc11_cpu->ios[M6811_INIT];
628 print_io_byte (sd, "INIT ", 0, val, base + M6811_INIT);
629 sim_io_printf (sd, "Ram = 0x%04x IO = 0x%04x\n",
630 (((uint16_t) (val & 0xF0)) << 8),
631 (((uint16_t) (val & 0x0F)) << 12));
632
633
634 cpu_info (sd, cpu);
635 interrupts_info (sd, &m68hc11_cpu->cpu_interrupts);
636 }
637
638 static int
m68hc11_ioctl(struct hw * me,hw_ioctl_request request,va_list ap)639 m68hc11_ioctl (struct hw *me,
640 hw_ioctl_request request,
641 va_list ap)
642 {
643 m68hc11_info (me);
644 return 0;
645 }
646
647 /* Setup an oscillator on an input port.
648
649 TON represents the time in seconds that the input port should be set to 1.
650 TOFF is the time in seconds for the input port to be set to 0.
651
652 The oscillator frequency is therefore 1 / (ton + toff).
653
654 REPEAT indicates the number of 1 <-> 0 transitions until the oscillator
655 stops. */
656 int
m68hc11cpu_set_oscillator(SIM_DESC sd,const char * port,double ton,double toff,int64_t repeat)657 m68hc11cpu_set_oscillator (SIM_DESC sd, const char *port,
658 double ton, double toff, int64_t repeat)
659 {
660 sim_cpu *cpu;
661 struct m68hc11_sim_cpu *m68hc11_cpu;
662 struct input_osc *osc;
663 double f;
664
665 cpu = STATE_CPU (sd, 0);
666 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
667
668 /* Find oscillator that corresponds to the input port. */
669 osc = find_oscillator (hw_data (m68hc11_cpu->hw_cpu), port);
670 if (osc == 0)
671 return -1;
672
673 /* Compute the ON time in cpu cycles. */
674 f = (double) (m68hc11_cpu->cpu_frequency) * ton;
675 osc->on_time = (int64_t) (f / 4.0);
676 if (osc->on_time < 1)
677 osc->on_time = 1;
678
679 /* Compute the OFF time in cpu cycles. */
680 f = (double) (m68hc11_cpu->cpu_frequency) * toff;
681 osc->off_time = (int64_t) (f / 4.0);
682 if (osc->off_time < 1)
683 osc->off_time = 1;
684
685 osc->repeat = repeat;
686 if (osc->event)
687 hw_event_queue_deschedule (m68hc11_cpu->hw_cpu, osc->event);
688
689 osc->event = hw_event_queue_schedule (m68hc11_cpu->hw_cpu,
690 osc->value ? osc->on_time
691 : osc->off_time,
692 oscillator_handler, osc);
693 return 0;
694 }
695
696 /* Clear the oscillator. */
697 int
m68hc11cpu_clear_oscillator(SIM_DESC sd,const char * port)698 m68hc11cpu_clear_oscillator (SIM_DESC sd, const char *port)
699 {
700 sim_cpu *cpu;
701 struct m68hc11_sim_cpu *m68hc11_cpu;
702 struct input_osc *osc;
703
704 cpu = STATE_CPU (sd, 0);
705 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
706 osc = find_oscillator (hw_data (m68hc11_cpu->hw_cpu), port);
707 if (osc == 0)
708 return -1;
709
710 if (osc->event)
711 hw_event_queue_deschedule (m68hc11_cpu->hw_cpu, osc->event);
712 osc->event = 0;
713 osc->repeat = 0;
714 return 0;
715 }
716
717 static int
get_frequency(const char * s,double * f)718 get_frequency (const char *s, double *f)
719 {
720 char *p;
721
722 *f = strtod (s, &p);
723 if (s == p)
724 return -1;
725
726 if (*p)
727 {
728 if (strcasecmp (p, "khz") == 0)
729 *f = *f * 1000.0;
730 else if (strcasecmp (p, "mhz") == 0)
731 *f = *f * 1000000.0;
732 else if (strcasecmp (p, "hz") != 0)
733 return -1;
734 }
735 return 0;
736 }
737
738 static SIM_RC
m68hc11_option_handler(SIM_DESC sd,sim_cpu * cpu,int opt,char * arg,int is_command)739 m68hc11_option_handler (SIM_DESC sd, sim_cpu *cpu,
740 int opt, char *arg, int is_command)
741 {
742 struct m68hc11_sim_cpu *m68hc11_cpu;
743 struct m68hc11cpu *controller;
744 double f;
745 char *p;
746 int i;
747 int title_printed = 0;
748
749 if (cpu == 0)
750 cpu = STATE_CPU (sd, 0);
751 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
752
753 controller = hw_data (m68hc11_cpu->hw_cpu);
754 switch (opt)
755 {
756 case OPTION_OSC_SET:
757 p = strchr (arg, ',');
758 if (p)
759 *p++ = 0;
760
761 if (p == 0)
762 sim_io_eprintf (sd, "No frequency specified\n");
763 else if (get_frequency (p, &f) < 0 || f < 1.0e-8)
764 sim_io_eprintf (sd, "Invalid frequency: '%s'\n", p);
765 else if (m68hc11cpu_set_oscillator (sd, arg,
766 1.0 / (f * 2.0),
767 1.0 / (f * 2.0), LONG_MAX))
768 sim_io_eprintf (sd, "Invalid input port: '%s'\n", arg);
769 break;
770
771 case OPTION_OSC_CLEAR:
772 if (m68hc11cpu_clear_oscillator (sd, arg) != 0)
773 sim_io_eprintf (sd, "Invalid input port: '%s'\n", arg);
774 break;
775
776 case OPTION_OSC_INFO:
777 for (i = 0; i < controller->last_oscillator; i++)
778 {
779 int64_t t;
780 struct input_osc *osc;
781
782 osc = &controller->oscillators[i];
783 if (osc->event)
784 {
785 int cur_value;
786 int next_value;
787 char freq[32];
788
789 if (title_printed == 0)
790 {
791 title_printed = 1;
792 sim_io_printf (sd, " PORT Frequency Current"
793 " Next Transition time\n");
794 }
795
796 f = (double) (osc->on_time + osc->off_time);
797 f = (double) (m68hc11_cpu->cpu_frequency / 4) / f;
798 t = hw_event_remain_time (m68hc11_cpu->hw_cpu, osc->event);
799
800 if (f > 10000.0)
801 sprintf (freq, "%6.2f", f / 1000.0);
802 else
803 sprintf (freq, "%6.2f", f);
804 cur_value = osc->value ? 1 : 0;
805 next_value = osc->value ? 0 : 1;
806 if (f > 10000.0)
807 sim_io_printf (sd, " %4.4s %8.8s khz"
808 " %d %d %35.35s\n",
809 osc->name, freq,
810 cur_value, next_value,
811 cycle_to_string (cpu, t,
812 PRINT_TIME | PRINT_CYCLE));
813 else
814 sim_io_printf (sd, " %4.4s %8.8s hz "
815 " %d %d %35.35s\n",
816 osc->name, freq,
817 cur_value, next_value,
818 cycle_to_string (cpu, t,
819 PRINT_TIME | PRINT_CYCLE));
820 }
821 }
822 break;
823 }
824
825 return SIM_RC_OK;
826 }
827
828 /* generic read/write */
829
830 static unsigned
m68hc11cpu_io_read_buffer(struct hw * me,void * dest,int space,unsigned_word base,unsigned nr_bytes)831 m68hc11cpu_io_read_buffer (struct hw *me,
832 void *dest,
833 int space,
834 unsigned_word base,
835 unsigned nr_bytes)
836 {
837 SIM_DESC sd;
838 struct m68hc11cpu *controller = hw_data (me);
839 sim_cpu *cpu;
840 struct m68hc11_sim_cpu *m68hc11_cpu;
841 unsigned byte = 0;
842 int result;
843
844 HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
845
846 sd = hw_system (me);
847 cpu = STATE_CPU (sd, 0);
848 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
849
850 if (base >= m68hc11_cpu->bank_start && base < m68hc11_cpu->bank_end)
851 {
852 address_word virt_addr = phys_to_virt (cpu, base);
853 if (virt_addr != base)
854 return sim_core_read_buffer (sd, cpu, space, dest,
855 virt_addr, nr_bytes);
856 }
857
858 /* Handle reads for the sub-devices. */
859 base -= controller->attach_address;
860 result = sim_core_read_buffer (sd, cpu,
861 io_map, dest, base, nr_bytes);
862 if (result > 0)
863 return result;
864
865 while (nr_bytes)
866 {
867 if (base >= controller->attach_size)
868 break;
869
870 memcpy (dest, &m68hc11_cpu->ios[base], 1);
871 dest = (char*) dest + 1;
872 base++;
873 byte++;
874 nr_bytes--;
875 }
876 return byte;
877 }
878
879 void
m68hc11cpu_set_port(struct hw * me,sim_cpu * cpu,unsigned addr,uint8_t val)880 m68hc11cpu_set_port (struct hw *me, sim_cpu *cpu,
881 unsigned addr, uint8_t val)
882 {
883 struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu);
884 uint8_t mask;
885 uint8_t delta;
886 int check_interrupts = 0;
887 int i;
888
889 switch (addr)
890 {
891 case M6811_PORTA:
892 if (m68hc11_cpu->ios[M6811_PACTL] & M6811_DDRA7)
893 mask = 3;
894 else
895 mask = 0x83;
896
897 val = val & mask;
898 val |= m68hc11_cpu->ios[M6811_PORTA] & ~mask;
899 delta = val ^ m68hc11_cpu->ios[M6811_PORTA];
900 m68hc11_cpu->ios[M6811_PORTA] = val;
901 if (delta & 0x80)
902 {
903 /* Pulse accumulator is enabled. */
904 if ((m68hc11_cpu->ios[M6811_PACTL] & M6811_PAEN)
905 && !(m68hc11_cpu->ios[M6811_PACTL] & M6811_PAMOD))
906 {
907 int inc;
908
909 /* Increment event counter according to rising/falling edge. */
910 if (m68hc11_cpu->ios[M6811_PACTL] & M6811_PEDGE)
911 inc = (val & 0x80) ? 1 : 0;
912 else
913 inc = (val & 0x80) ? 0 : 1;
914
915 m68hc11_cpu->ios[M6811_PACNT] += inc;
916
917 /* Event counter overflowed. */
918 if (inc && m68hc11_cpu->ios[M6811_PACNT] == 0)
919 {
920 m68hc11_cpu->ios[M6811_TFLG2] |= M6811_PAOVI;
921 check_interrupts = 1;
922 }
923 }
924 }
925
926 /* Scan IC3, IC2 and IC1. Bit number is 3 - i. */
927 for (i = 0; i < 3; i++)
928 {
929 mask = (1 << i);
930
931 if (delta & mask)
932 {
933 uint8_t edge;
934 int captured;
935
936 edge = m68hc11_cpu->ios[M6811_TCTL2];
937 edge = (edge >> (2 * i)) & 0x3;
938 switch (edge)
939 {
940 case 0:
941 captured = 0;
942 break;
943 case 1:
944 captured = (val & mask) != 0;
945 break;
946 case 2:
947 captured = (val & mask) == 0;
948 break;
949 default:
950 captured = 1;
951 break;
952 }
953 if (captured)
954 {
955 m68hc11_cpu->ios[M6811_TFLG1] |= (1 << i);
956 hw_port_event (me, CAPTURE, M6811_TIC1 + 3 - i);
957 check_interrupts = 1;
958 }
959 }
960 }
961 break;
962
963 case M6811_PORTC:
964 mask = m68hc11_cpu->ios[M6811_DDRC];
965 val = val & mask;
966 val |= m68hc11_cpu->ios[M6811_PORTC] & ~mask;
967 m68hc11_cpu->ios[M6811_PORTC] = val;
968 break;
969
970 case M6811_PORTD:
971 mask = m68hc11_cpu->ios[M6811_DDRD];
972 val = val & mask;
973 val |= m68hc11_cpu->ios[M6811_PORTD] & ~mask;
974 m68hc11_cpu->ios[M6811_PORTD] = val;
975 break;
976
977 default:
978 break;
979 }
980
981 if (check_interrupts)
982 interrupts_update_pending (&m68hc11_cpu->cpu_interrupts);
983 }
984
985 static void
m68hc11cpu_io_write(struct hw * me,sim_cpu * cpu,unsigned_word addr,uint8_t val)986 m68hc11cpu_io_write (struct hw *me, sim_cpu *cpu,
987 unsigned_word addr, uint8_t val)
988 {
989 struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu);
990
991 switch (addr)
992 {
993 case M6811_PORTA:
994 hw_port_event (me, PORT_A, val);
995 break;
996
997 case M6811_PIOC:
998 break;
999
1000 case M6811_PORTC:
1001 hw_port_event (me, PORT_C, val);
1002 break;
1003
1004 case M6811_PORTB:
1005 hw_port_event (me, PORT_B, val);
1006 break;
1007
1008 case M6811_PORTCL:
1009 break;
1010
1011 case M6811_DDRC:
1012 break;
1013
1014 case M6811_PORTD:
1015 hw_port_event (me, PORT_D, val);
1016 break;
1017
1018 case M6811_DDRD:
1019 break;
1020
1021 case M6811_TMSK2:
1022
1023 break;
1024
1025 /* Change the RAM and I/O mapping. */
1026 case M6811_INIT:
1027 {
1028 uint8_t old_bank = m68hc11_cpu->ios[M6811_INIT];
1029
1030 m68hc11_cpu->ios[M6811_INIT] = val;
1031
1032 /* Update IO mapping. Detach from the old address
1033 and attach to the new one. */
1034 if ((old_bank & 0x0F) != (val & 0x0F))
1035 {
1036 struct m68hc11cpu *controller = hw_data (me);
1037
1038 hw_detach_address (hw_parent (me), M6811_IO_LEVEL,
1039 controller->attach_space,
1040 controller->attach_address,
1041 controller->attach_size,
1042 me);
1043 controller->attach_address = (val & 0x0F0) << 12;
1044 hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
1045 controller->attach_space,
1046 controller->attach_address,
1047 controller->attach_size,
1048 me);
1049 }
1050 if ((old_bank & 0xF0) != (val & 0xF0))
1051 {
1052 ;
1053 }
1054 return;
1055 }
1056
1057 /* Writing the config is similar to programing the eeprom.
1058 The config register value is the last byte of the EEPROM.
1059 This last byte is not mapped in memory (that's why we have
1060 to add '1' to 'end_addr'). */
1061 case M6811_CONFIG:
1062 {
1063 return;
1064 }
1065
1066
1067 /* COP reset. */
1068 case M6811_COPRST:
1069 if (val == 0xAA && m68hc11_cpu->ios[addr] == 0x55)
1070 {
1071 val = 0;
1072 /* COP reset here. */
1073 }
1074 break;
1075
1076 default:
1077 break;
1078
1079 }
1080 m68hc11_cpu->ios[addr] = val;
1081 }
1082
1083 static unsigned
m68hc11cpu_io_write_buffer(struct hw * me,const void * source,int space,unsigned_word base,unsigned nr_bytes)1084 m68hc11cpu_io_write_buffer (struct hw *me,
1085 const void *source,
1086 int space,
1087 unsigned_word base,
1088 unsigned nr_bytes)
1089 {
1090 SIM_DESC sd;
1091 struct m68hc11cpu *controller = hw_data (me);
1092 unsigned byte;
1093 sim_cpu *cpu;
1094 struct m68hc11_sim_cpu *m68hc11_cpu;
1095 int result;
1096
1097 HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
1098
1099 sd = hw_system (me);
1100 cpu = STATE_CPU (sd, 0);
1101 m68hc11_cpu = M68HC11_SIM_CPU (cpu);
1102
1103 if (base >= m68hc11_cpu->bank_start && base < m68hc11_cpu->bank_end)
1104 {
1105 address_word virt_addr = phys_to_virt (cpu, base);
1106 if (virt_addr != base)
1107 return sim_core_write_buffer (sd, cpu, space, source,
1108 virt_addr, nr_bytes);
1109 }
1110 base -= controller->attach_address;
1111 result = sim_core_write_buffer (sd, cpu,
1112 io_map, source, base, nr_bytes);
1113 if (result > 0)
1114 return result;
1115
1116 byte = 0;
1117 while (nr_bytes)
1118 {
1119 uint8_t val;
1120 if (base >= controller->attach_size)
1121 break;
1122
1123 val = *((uint8_t*) source);
1124 m68hc11cpu_io_write (me, cpu, base, val);
1125 source = (char*) source + 1;
1126 base++;
1127 byte++;
1128 nr_bytes--;
1129 }
1130 return byte;
1131 }
1132
1133 const struct hw_descriptor dv_m68hc11_descriptor[] = {
1134 { "m68hc11", m68hc11cpu_finish },
1135 { "m68hc12", m68hc11cpu_finish },
1136 { NULL },
1137 };
1138
1139