]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/m68hc11/dv-m68hc11.c
Register a delete handler for 68HC11 core device node
[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 static void
177 m68hc11_delete (struct hw* me)
178 {
179 struct m68hc11cpu *controller;
180
181 controller = hw_data (me);
182
183 hw_detach_address (me, M6811_IO_LEVEL,
184 controller->attach_space,
185 controller->attach_address,
186 controller->attach_size, me);
187 }
188
189
190 static void
191 attach_m68hc11_regs (struct hw *me,
192 struct m68hc11cpu *controller)
193 {
194 SIM_DESC sd;
195 sim_cpu *cpu;
196 reg_property_spec reg;
197 const char *cpu_mode;
198
199 if (hw_find_property (me, "reg") == NULL)
200 hw_abort (me, "Missing \"reg\" property");
201
202 if (!hw_find_reg_array_property (me, "reg", 0, &reg))
203 hw_abort (me, "\"reg\" property must contain one addr/size entry");
204
205 hw_unit_address_to_attach_address (hw_parent (me),
206 &reg.address,
207 &controller->attach_space,
208 &controller->attach_address,
209 me);
210 hw_unit_size_to_attach_size (hw_parent (me),
211 &reg.size,
212 &controller->attach_size, me);
213
214 hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
215 controller->attach_space,
216 controller->attach_address,
217 controller->attach_size,
218 me);
219 set_hw_delete (me, m68hc11_delete);
220
221 /* Get cpu frequency. */
222 sd = hw_system (me);
223 cpu = STATE_CPU (sd, 0);
224 if (hw_find_property (me, "clock") != NULL)
225 {
226 cpu->cpu_frequency = hw_find_integer_property (me, "clock");
227 }
228 else
229 {
230 cpu->cpu_frequency = 8*1000*1000;
231 }
232
233 cpu_mode = "expanded";
234 if (hw_find_property (me, "mode") != NULL)
235 cpu_mode = hw_find_string_property (me, "mode");
236
237 if (strcmp (cpu_mode, "test") == 0)
238 cpu->cpu_mode = M6811_MDA | M6811_SMOD;
239 else if (strcmp (cpu_mode, "bootstrap") == 0)
240 cpu->cpu_mode = M6811_SMOD;
241 else if (strcmp (cpu_mode, "single") == 0)
242 cpu->cpu_mode = 0;
243 else
244 cpu->cpu_mode = M6811_MDA;
245 }
246
247 static void
248 m68hc11cpu_finish (struct hw *me)
249 {
250 struct m68hc11cpu *controller;
251
252 controller = HW_ZALLOC (me, struct m68hc11cpu);
253 set_hw_data (me, controller);
254 set_hw_io_read_buffer (me, m68hc11cpu_io_read_buffer);
255 set_hw_io_write_buffer (me, m68hc11cpu_io_write_buffer);
256 set_hw_ports (me, m68hc11cpu_ports);
257 set_hw_port_event (me, m68hc11cpu_port_event);
258 set_hw_attach_address (me, dv_m6811_attach_address_callback);
259 set_hw_detach_address (me, dv_m6811_detach_address_callback);
260 #ifdef set_hw_ioctl
261 set_hw_ioctl (me, m68hc11_ioctl);
262 #else
263 me->to_ioctl = m68hc11_ioctl;
264 #endif
265
266 /* Initialize the pending interrupt flags. */
267 controller->pending_level = 0;
268 controller->pending_reset = 0;
269 controller->pending_nmi = 0;
270 controller->event = NULL;
271
272 attach_m68hc11_regs (me, controller);
273 }
274
275
276
277 /* An event arrives on an interrupt port. */
278
279 static void
280 deliver_m68hc11cpu_interrupt (struct hw *me, void *data)
281 {
282 }
283
284
285 static void
286 m68hc11cpu_port_event (struct hw *me,
287 int my_port,
288 struct hw *source,
289 int source_port,
290 int level)
291 {
292 struct m68hc11cpu *controller = hw_data (me);
293 SIM_DESC sd;
294 sim_cpu* cpu;
295
296 sd = hw_system (me);
297 cpu = STATE_CPU (sd, 0);
298 switch (my_port)
299 {
300 case RESET_PORT:
301 HW_TRACE ((me, "port-in reset"));
302
303 /* The reset is made in 3 steps:
304 - First, cleanup the current sim_cpu struct.
305 - Reset the devices.
306 - Restart the cpu for the reset (get the CPU mode from the
307 CONFIG register that gets initialized by EEPROM device). */
308 cpu_reset (cpu);
309 hw_port_event (me, CPU_RESET_PORT, 1);
310 cpu_restart (cpu);
311 break;
312
313 case NMI_PORT:
314 controller->pending_nmi = 1;
315 HW_TRACE ((me, "port-in nmi"));
316 break;
317
318 case IRQ_PORT:
319 /* level == 0 means that the interrupt was cleared. */
320 if(level == 0)
321 controller->pending_level = -1; /* signal end of interrupt */
322 else
323 controller->pending_level = level;
324 HW_TRACE ((me, "port-in level=%d", level));
325 break;
326
327 default:
328 hw_abort (me, "bad switch");
329 break;
330 }
331
332 /* Schedule an event to be delivered immediately after current
333 instruction. */
334 if(controller->event != NULL)
335 hw_event_queue_deschedule(me, controller->event);
336 controller->event =
337 hw_event_queue_schedule (me, 0, deliver_m68hc11cpu_interrupt, NULL);
338 }
339
340
341 io_reg_desc config_desc[] = {
342 { M6811_NOSEC, "NOSEC ", "Security Mode Disable" },
343 { M6811_NOCOP, "NOCOP ", "COP System Disable" },
344 { M6811_ROMON, "ROMON ", "Enable On-chip Rom" },
345 { M6811_EEON, "EEON ", "Enable On-chip EEprom" },
346 { 0, 0, 0 }
347 };
348
349 io_reg_desc hprio_desc[] = {
350 { M6811_RBOOT, "RBOOT ", "Read Bootstrap ROM" },
351 { M6811_SMOD, "SMOD ", "Special Mode" },
352 { M6811_MDA, "MDA ", "Mode Select A" },
353 { M6811_IRV, "IRV ", "Internal Read Visibility" },
354 { 0, 0, 0 }
355 };
356
357 io_reg_desc option_desc[] = {
358 { M6811_ADPU, "ADPU ", "A/D Powerup" },
359 { M6811_CSEL, "CSEL ", "A/D/EE Charge pump clock source select" },
360 { M6811_IRQE, "IRQE ", "IRQ Edge/Level sensitive" },
361 { M6811_DLY, "DLY ", "Stop exit turn on delay" },
362 { M6811_CME, "CME ", "Clock Monitor Enable" },
363 { M6811_CR1, "CR1 ", "COP timer rate select (CR1)" },
364 { M6811_CR0, "CR0 ", "COP timer rate select (CR0)" },
365 { 0, 0, 0 }
366 };
367
368 static void
369 m68hc11_info (struct hw *me)
370 {
371 SIM_DESC sd;
372 uint16 base = 0;
373 sim_cpu *cpu;
374 struct m68hc11sio *controller;
375 uint8 val;
376
377 sd = hw_system (me);
378 cpu = STATE_CPU (sd, 0);
379 controller = hw_data (me);
380
381 base = cpu_get_io_base (cpu);
382 sim_io_printf (sd, "M68HC11:\n");
383
384 val = cpu->ios[M6811_HPRIO];
385 print_io_byte (sd, "HPRIO ", hprio_desc, val, base + M6811_HPRIO);
386 sim_io_printf (sd, "\n");
387
388 val = cpu->ios[M6811_CONFIG];
389 print_io_byte (sd, "CONFIG", config_desc, val, base + M6811_CONFIG);
390 sim_io_printf (sd, "\n");
391
392 val = cpu->ios[M6811_OPTION];
393 print_io_byte (sd, "OPTION", option_desc, val, base + M6811_OPTION);
394 sim_io_printf (sd, "\n");
395
396 val = cpu->ios[M6811_INIT];
397 print_io_byte (sd, "INIT ", 0, val, base + M6811_INIT);
398 sim_io_printf (sd, "Ram = 0x%04x IO = 0x%04x\n",
399 (((uint16) (val & 0xF0)) << 8),
400 (((uint16) (val & 0x0F)) << 12));
401
402
403 cpu_info (sd, cpu);
404 interrupts_info (sd, &cpu->cpu_interrupts);
405 }
406
407 static int
408 m68hc11_ioctl (struct hw *me,
409 hw_ioctl_request request,
410 va_list ap)
411 {
412 m68hc11_info (me);
413 return 0;
414 }
415
416 /* generic read/write */
417
418 static unsigned
419 m68hc11cpu_io_read_buffer (struct hw *me,
420 void *dest,
421 int space,
422 unsigned_word base,
423 unsigned nr_bytes)
424 {
425 SIM_DESC sd;
426 struct m68hc11cpu *controller = hw_data (me);
427 sim_cpu *cpu;
428 unsigned byte = 0;
429 int result;
430
431 HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
432
433 sd = hw_system (me);
434 cpu = STATE_CPU (sd, 0);
435
436 /* Handle reads for the sub-devices. */
437 base -= controller->attach_address;
438 result = sim_core_read_buffer (sd, cpu,
439 io_map, dest, base, nr_bytes);
440 if (result > 0)
441 return result;
442
443 while (nr_bytes)
444 {
445 if (base >= 0x3F)
446 break;
447
448 memcpy (dest, &cpu->ios[base], 1);
449 dest++;
450 base++;
451 byte++;
452 nr_bytes--;
453 }
454 return byte;
455 }
456
457
458 static void
459 m68hc11cpu_io_write (struct hw *me, sim_cpu *cpu,
460 unsigned_word addr, uint8 val)
461 {
462 switch (addr)
463 {
464 case M6811_PORTA:
465 break;
466
467 case M6811_PIOC:
468 break;
469
470 case M6811_PORTC:
471 break;
472
473 case M6811_PORTB:
474 break;
475
476 case M6811_PORTCL:
477 break;
478
479 case M6811_DDRC:
480 break;
481
482 case M6811_PORTD:
483 break;
484
485 case M6811_DDRD:
486 break;
487
488 case M6811_TMSK2:
489
490 break;
491
492 /* Change the RAM and I/O mapping. */
493 case M6811_INIT:
494 {
495 uint8 old_bank = cpu->ios[M6811_INIT];
496
497 cpu->ios[M6811_INIT] = val;
498
499 /* Update IO mapping. Detach from the old address
500 and attach to the new one. */
501 if ((old_bank & 0xF0) != (val & 0xF0))
502 {
503 struct m68hc11cpu *controller = hw_data (me);
504
505 hw_detach_address (hw_parent (me), M6811_IO_LEVEL,
506 controller->attach_space,
507 controller->attach_address,
508 controller->attach_size,
509 me);
510 controller->attach_address = (val & 0x0F0) << 12;
511 hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
512 controller->attach_space,
513 controller->attach_address,
514 controller->attach_size,
515 me);
516 }
517 if ((old_bank & 0x0F) != (val & 0x0F))
518 {
519 ;
520 }
521 return;
522 }
523
524 /* Writing the config is similar to programing the eeprom.
525 The config register value is the last byte of the EEPROM.
526 This last byte is not mapped in memory (that's why we have
527 to add '1' to 'end_addr'). */
528 case M6811_CONFIG:
529 {
530 return;
531 }
532
533
534 /* COP reset. */
535 case M6811_COPRST:
536 if (val == 0xAA && cpu->ios[addr] == 0x55)
537 {
538 val = 0;
539 /* COP reset here. */
540 }
541 break;
542
543 default:
544 break;
545
546 }
547 cpu->ios[addr] = val;
548 }
549
550 static unsigned
551 m68hc11cpu_io_write_buffer (struct hw *me,
552 const void *source,
553 int space,
554 unsigned_word base,
555 unsigned nr_bytes)
556 {
557 SIM_DESC sd;
558 struct m68hc11cpu *controller = hw_data (me);
559 unsigned byte;
560 sim_cpu *cpu;
561 int result;
562
563 HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
564
565 sd = hw_system (me);
566 cpu = STATE_CPU (sd, 0);
567 base -= controller->attach_address;
568 result = sim_core_write_buffer (sd, cpu,
569 io_map, source, base, nr_bytes);
570 if (result > 0)
571 return result;
572
573 byte = 0;
574 while (nr_bytes)
575 {
576 uint8 val;
577 if (base >= 0x3F)
578 break;
579
580 val = *((uint8*) source);
581 m68hc11cpu_io_write (me, cpu, base, val);
582 source++;
583 base++;
584 byte++;
585 nr_bytes--;
586 }
587 return byte;
588 }
589
590 const struct hw_descriptor dv_m68hc11_descriptor[] = {
591 { "m68hc11", m68hc11cpu_finish, },
592 { NULL },
593 };
594