]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/m68hc11/dv-m68hc11.c
New simulator.
[thirdparty/binutils-gdb.git] / sim / m68hc11 / dv-m68hc11.c
1 /* dv-m68hc11.c -- CPU 68HC11 as a device.
2 Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3 Written by Stephane Carrez (stcarrez@worldnet.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 2 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22
23 #include "sim-main.h"
24 #include "hw-main.h"
25
26 /* DEVICE
27
28 m68hc11cpu - m68hc11 cpu virtual device
29
30
31 DESCRIPTION
32
33 Implements the external m68hc11 functionality. This includes the
34 delivery of of interrupts generated from other devices and the
35 handling of device specific registers.
36
37
38 PROPERTIES
39
40 reg <base> <size>
41
42 Register base (should be 0x1000 0x03f).
43
44 clock <hz>
45
46 Frequency of the quartz used by the processor.
47
48 mode [single | expanded | bootstrap | test]
49
50 Cpu operating mode (the MODA and MODB external pins).
51
52
53 PORTS
54
55 reset (input)
56
57 Reset the cpu and generates a cpu-reset event (used to reset
58 other devices).
59
60 nmi (input)
61
62 Deliver a non-maskable interrupt to the processor.
63
64
65 cpu-reset (output)
66
67 Event generated after the CPU performs a reset.
68
69
70 BUGS
71
72 When delivering an interrupt, this code assumes that there is only
73 one processor (number 0).
74
75 */
76
77
78
79 struct m68hc11cpu {
80 /* Pending interrupts for delivery by event handler. */
81 int pending_reset;
82 int pending_nmi;
83 int pending_level;
84 struct hw_event *event;
85 unsigned_word attach_address;
86 int attach_size;
87 int attach_space;
88 };
89
90
91
92 /* input port ID's */
93
94 enum {
95 RESET_PORT,
96 NMI_PORT,
97 IRQ_PORT,
98 CPU_RESET_PORT
99 };
100
101
102 static const struct hw_port_descriptor m68hc11cpu_ports[] = {
103
104 /* Interrupt inputs. */
105 { "reset", RESET_PORT, 0, input_port, },
106 { "nmi", NMI_PORT, 0, input_port, },
107 { "irq", IRQ_PORT, 0, input_port, },
108
109 /* Events generated for connection to other devices. */
110 { "cpu-reset", CPU_RESET_PORT, 0, output_port, },
111
112 { NULL, },
113 };
114
115 static hw_io_read_buffer_method m68hc11cpu_io_read_buffer;
116 static hw_io_write_buffer_method m68hc11cpu_io_write_buffer;
117 static hw_ioctl_method m68hc11_ioctl;
118
119 /* Finish off the partially created hw device. Attach our local
120 callbacks. Wire up our port names etc. */
121
122 static hw_port_event_method m68hc11cpu_port_event;
123
124
125 static void
126 dv_m6811_attach_address_callback (struct hw *me,
127 int level,
128 int space,
129 address_word addr,
130 address_word nr_bytes,
131 struct hw *client)
132 {
133 HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, sz=%ld, client=%s",
134 level, space, (unsigned long) addr, (unsigned long) nr_bytes,
135 hw_path (client)));
136
137 if (space != io_map)
138 {
139 sim_core_attach (hw_system (me),
140 NULL, /*cpu*/
141 level,
142 access_read_write_exec,
143 space, addr,
144 nr_bytes,
145 0, /* modulo */
146 client,
147 NULL);
148 }
149 else
150 {
151 /*printf("Attach from sub device: %d\n", (long) addr);*/
152 sim_core_attach (hw_system (me),
153 NULL, /*cpu*/
154 level,
155 access_io,
156 space, addr,
157 nr_bytes,
158 0, /* modulo */
159 client,
160 NULL);
161 }
162 }
163
164 static void
165 dv_m6811_detach_address_callback (struct hw *me,
166 int level,
167 int space,
168 address_word addr,
169 address_word nr_bytes,
170 struct hw *client)
171 {
172 sim_core_detach (hw_system (me), NULL, /*cpu*/
173 level, space, addr);
174 }
175
176
177 static void
178 attach_m68hc11_regs (struct hw *me,
179 struct m68hc11cpu *controller)
180 {
181 SIM_DESC sd;
182 sim_cpu *cpu;
183 reg_property_spec reg;
184 const char *cpu_mode;
185
186 if (hw_find_property (me, "reg") == NULL)
187 hw_abort (me, "Missing \"reg\" property");
188
189 if (!hw_find_reg_array_property (me, "reg", 0, &reg))
190 hw_abort (me, "\"reg\" property must contain one addr/size entry");
191
192 hw_unit_address_to_attach_address (hw_parent (me),
193 &reg.address,
194 &controller->attach_space,
195 &controller->attach_address,
196 me);
197 hw_unit_size_to_attach_size (hw_parent (me),
198 &reg.size,
199 &controller->attach_size, me);
200
201 hw_attach_address (hw_parent (me), 0,
202 controller->attach_space,
203 controller->attach_address,
204 controller->attach_size,
205 me);
206
207
208 /* Get cpu frequency. */
209 sd = hw_system (me);
210 cpu = STATE_CPU (sd, 0);
211 if (hw_find_property (me, "clock") != NULL)
212 {
213 cpu->cpu_frequency = hw_find_integer_property (me, "clock");
214 }
215 else
216 {
217 cpu->cpu_frequency = 8*1000*1000;
218 }
219
220 cpu_mode = "expanded";
221 if (hw_find_property (me, "mode") != NULL)
222 cpu_mode = hw_find_string_property (me, "mode");
223
224 if (strcmp (cpu_mode, "test") == 0)
225 cpu->cpu_mode = M6811_MDA | M6811_SMOD;
226 else if (strcmp (cpu_mode, "bootstrap") == 0)
227 cpu->cpu_mode = M6811_SMOD;
228 else if (strcmp (cpu_mode, "single") == 0)
229 cpu->cpu_mode = 0;
230 else
231 cpu->cpu_mode = M6811_MDA;
232 }
233
234 static void
235 m68hc11cpu_finish (struct hw *me)
236 {
237 struct m68hc11cpu *controller;
238
239 controller = HW_ZALLOC (me, struct m68hc11cpu);
240 me->overlap_mode_hw = 1;
241 set_hw_data (me, controller);
242 set_hw_io_read_buffer (me, m68hc11cpu_io_read_buffer);
243 set_hw_io_write_buffer (me, m68hc11cpu_io_write_buffer);
244 set_hw_ports (me, m68hc11cpu_ports);
245 set_hw_port_event (me, m68hc11cpu_port_event);
246 set_hw_attach_address (me, dv_m6811_attach_address_callback);
247 set_hw_detach_address (me, dv_m6811_detach_address_callback);
248 #ifdef set_hw_ioctl
249 set_hw_ioctl (me, m68hc11_ioctl);
250 #else
251 me->to_ioctl = m68hc11_ioctl;
252 #endif
253
254 /* Initialize the pending interrupt flags. */
255 controller->pending_level = 0;
256 controller->pending_reset = 0;
257 controller->pending_nmi = 0;
258 controller->event = NULL;
259
260 attach_m68hc11_regs (me, controller);
261 }
262
263
264
265 /* An event arrives on an interrupt port. */
266
267 static void
268 deliver_m68hc11cpu_interrupt (struct hw *me, void *data)
269 {
270 }
271
272
273 static void
274 m68hc11cpu_port_event (struct hw *me,
275 int my_port,
276 struct hw *source,
277 int source_port,
278 int level)
279 {
280 struct m68hc11cpu *controller = hw_data (me);
281 SIM_DESC sd;
282 sim_cpu* cpu;
283
284 sd = hw_system (me);
285 cpu = STATE_CPU (sd, 0);
286 switch (my_port)
287 {
288 case RESET_PORT:
289 HW_TRACE ((me, "port-in reset"));
290
291 /* The reset is made in 3 steps:
292 - First, cleanup the current sim_cpu struct.
293 - Reset the devices.
294 - Restart the cpu for the reset (get the CPU mode from the
295 CONFIG register that gets initialized by EEPROM device). */
296 cpu_reset (cpu);
297 hw_port_event (me, CPU_RESET_PORT, 1);
298 cpu_restart (cpu);
299 break;
300
301 case NMI_PORT:
302 controller->pending_nmi = 1;
303 HW_TRACE ((me, "port-in nmi"));
304 break;
305
306 case IRQ_PORT:
307 /* level == 0 means that the interrupt was cleared. */
308 if(level == 0)
309 controller->pending_level = -1; /* signal end of interrupt */
310 else
311 controller->pending_level = level;
312 HW_TRACE ((me, "port-in level=%d", level));
313 break;
314
315 default:
316 hw_abort (me, "bad switch");
317 break;
318 }
319
320 /* Schedule an event to be delivered immediately after current
321 instruction. */
322 if(controller->event != NULL)
323 hw_event_queue_deschedule(me, controller->event);
324 controller->event =
325 hw_event_queue_schedule (me, 0, deliver_m68hc11cpu_interrupt, NULL);
326 }
327
328
329 io_reg_desc config_desc[] = {
330 { M6811_NOSEC, "NOSEC ", "Security Mode Disable" },
331 { M6811_NOCOP, "NOCOP ", "COP System Disable" },
332 { M6811_ROMON, "ROMON ", "Enable On-chip Rom" },
333 { M6811_EEON, "EEON ", "Enable On-chip EEprom" },
334 { 0, 0, 0 }
335 };
336
337 io_reg_desc hprio_desc[] = {
338 { M6811_RBOOT, "RBOOT ", "Read Bootstrap ROM" },
339 { M6811_SMOD, "SMOD ", "Special Mode" },
340 { M6811_MDA, "MDA ", "Mode Select A" },
341 { M6811_IRV, "IRV ", "Internal Read Visibility" },
342 { 0, 0, 0 }
343 };
344
345 io_reg_desc option_desc[] = {
346 { M6811_ADPU, "ADPU ", "A/D Powerup" },
347 { M6811_CSEL, "CSEL ", "A/D/EE Charge pump clock source select" },
348 { M6811_IRQE, "IRQE ", "IRQ Edge/Level sensitive" },
349 { M6811_DLY, "DLY ", "Stop exit turn on delay" },
350 { M6811_CME, "CME ", "Clock Monitor Enable" },
351 { M6811_CR1, "CR1 ", "COP timer rate select (CR1)" },
352 { M6811_CR0, "CR0 ", "COP timer rate select (CR0)" },
353 { 0, 0, 0 }
354 };
355
356 static void
357 m68hc11_info (struct hw *me)
358 {
359 SIM_DESC sd;
360 uint16 base = 0;
361 sim_cpu *cpu;
362 struct m68hc11sio *controller;
363 uint8 val;
364
365 sd = hw_system (me);
366 cpu = STATE_CPU (sd, 0);
367 controller = hw_data (me);
368
369 base = cpu_get_io_base (cpu);
370 sim_io_printf (sd, "M68HC11:\n");
371
372 val = cpu->ios[M6811_HPRIO];
373 print_io_byte (sd, "HPRIO ", hprio_desc, val, base + M6811_HPRIO);
374 sim_io_printf (sd, "\n");
375
376 val = cpu->ios[M6811_CONFIG];
377 print_io_byte (sd, "CONFIG", config_desc, val, base + M6811_CONFIG);
378 sim_io_printf (sd, "\n");
379
380 val = cpu->ios[M6811_OPTION];
381 print_io_byte (sd, "OPTION", option_desc, val, base + M6811_OPTION);
382 sim_io_printf (sd, "\n");
383
384 val = cpu->ios[M6811_INIT];
385 print_io_byte (sd, "INIT ", 0, val, base + M6811_INIT);
386 sim_io_printf (sd, "Ram = 0x%04x IO = 0x%04x\n",
387 (((uint16) (val & 0xF0)) << 8),
388 (((uint16) (val & 0x0F)) << 12));
389
390
391 cpu_info (sd, cpu);
392 interrupts_info (sd, &cpu->cpu_interrupts);
393 }
394
395 static int
396 m68hc11_ioctl (struct hw *me,
397 hw_ioctl_request request,
398 va_list ap)
399 {
400 m68hc11_info (me);
401 return 0;
402 }
403
404 /* generic read/write */
405
406 static unsigned
407 m68hc11cpu_io_read_buffer (struct hw *me,
408 void *dest,
409 int space,
410 unsigned_word base,
411 unsigned nr_bytes)
412 {
413 SIM_DESC sd;
414 struct m68hc11cpu *controller = hw_data (me);
415 sim_cpu *cpu;
416 unsigned byte = 0;
417 int result;
418
419 HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
420
421 sd = hw_system (me);
422 cpu = STATE_CPU (sd, 0);
423
424 /* Handle reads for the sub-devices. */
425 base -= controller->attach_address;
426 result = sim_core_read_buffer (sd, cpu,
427 io_map, dest, base, nr_bytes);
428 if (result > 0)
429 return result;
430
431 while (nr_bytes)
432 {
433 if (base >= 0x3F)
434 break;
435
436 memcpy (dest, &cpu->ios[base], 1);
437 dest++;
438 base++;
439 byte++;
440 nr_bytes--;
441 }
442 return byte;
443 }
444
445
446 static void
447 m68hc11cpu_io_write (struct hw *me, sim_cpu *cpu,
448 unsigned_word addr, uint8 val)
449 {
450 switch (addr)
451 {
452 case M6811_PORTA:
453 break;
454
455 case M6811_PIOC:
456 break;
457
458 case M6811_PORTC:
459 break;
460
461 case M6811_PORTB:
462 break;
463
464 case M6811_PORTCL:
465 break;
466
467 case M6811_DDRC:
468 break;
469
470 case M6811_PORTD:
471 break;
472
473 case M6811_DDRD:
474 break;
475
476 case M6811_TMSK2:
477
478 break;
479
480 /* Change the RAM and I/O mapping. */
481 case M6811_INIT:
482 {
483 uint8 old_bank = cpu->ios[M6811_INIT];
484
485 cpu->ios[M6811_INIT] = val;
486
487 /* Update IO mapping. Detach from the old address
488 and attach to the new one. */
489 if ((old_bank & 0xF0) != (val & 0xF0))
490 {
491 struct m68hc11cpu *controller = hw_data (me);
492
493 hw_detach_address (hw_parent (me), 0,
494 controller->attach_space,
495 controller->attach_address,
496 controller->attach_size,
497 me);
498 controller->attach_address = (val & 0x0F0) << 12;
499 hw_attach_address (hw_parent (me), 0,
500 controller->attach_space,
501 controller->attach_address,
502 controller->attach_size,
503 me);
504 }
505 if ((old_bank & 0x0F) != (val & 0x0F))
506 {
507 ;
508 }
509 return;
510 }
511
512 /* Writing the config is similar to programing the eeprom.
513 The config register value is the last byte of the EEPROM.
514 This last byte is not mapped in memory (that's why we have
515 to add '1' to 'end_addr'). */
516 case M6811_CONFIG:
517 {
518 return;
519 }
520
521
522 /* COP reset. */
523 case M6811_COPRST:
524 if (val == 0xAA && cpu->ios[addr] == 0x55)
525 {
526 val = 0;
527 /* COP reset here. */
528 }
529 break;
530
531 default:
532 break;
533
534 }
535 cpu->ios[addr] = val;
536 }
537
538 static unsigned
539 m68hc11cpu_io_write_buffer (struct hw *me,
540 const void *source,
541 int space,
542 unsigned_word base,
543 unsigned nr_bytes)
544 {
545 SIM_DESC sd;
546 struct m68hc11cpu *controller = hw_data (me);
547 unsigned byte;
548 sim_cpu *cpu;
549 int result;
550
551 HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
552
553 sd = hw_system (me);
554 cpu = STATE_CPU (sd, 0);
555 base -= controller->attach_address;
556 result = sim_core_write_buffer (sd, cpu,
557 io_map, source, base, nr_bytes);
558 if (result > 0)
559 return result;
560
561 byte = 0;
562 while (nr_bytes)
563 {
564 uint8 val;
565 if (base >= 0x3F)
566 break;
567
568 val = *((uint8*) source);
569 m68hc11cpu_io_write (me, cpu, base, val);
570 source++;
571 base++;
572 byte++;
573 nr_bytes--;
574 }
575 return byte;
576 }
577
578 const struct hw_descriptor dv_m68hc11_descriptor[] = {
579 { "m68hc11", m68hc11cpu_finish, },
580 { NULL },
581 };
582