1 /*  dv-m68hc11eepr.c -- Simulation of the 68HC11 Internal EEPROM.
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 "hw-main.h"
26 #include "sim-assert.h"
27 #include "sim-events.h"
28 #include "sim-signal.h"
29 
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 
34 #include "m68hc11-sim.h"
35 
36 /* DEVICE
37 
38         m68hc11eepr - m68hc11 EEPROM
39 
40 
41    DESCRIPTION
42 
43         Implements the 68HC11 eeprom device described in the m68hc11
44         user guide (Chapter 4 in the pink book).
45 
46 
47    PROPERTIES
48 
49    reg <base> <length>
50 
51         Base of eeprom and its length.
52 
53    file <path>
54 
55         Path of the EEPROM file.  The default is 'm6811.eeprom'.
56 
57 
58    PORTS
59 
60         None
61 
62    */
63 
64 
65 
66 /* static functions */
67 
68 
69 /* port ID's */
70 
71 enum
72 {
73   RESET_PORT
74 };
75 
76 
77 static const struct hw_port_descriptor m68hc11eepr_ports[] =
78 {
79   { "reset", RESET_PORT, 0, input_port, },
80   { NULL, },
81 };
82 
83 
84 
85 /* The timer/counter register internal state.  Note that we store
86    state using the control register images, in host endian order.  */
87 
88 struct m68hc11eepr
89 {
90   address_word  base_address; /* control register base */
91   int           attach_space;
92   unsigned      size;
93   int           mapped;
94 
95   /* Current state of the eeprom programing:
96      - eeprom_wmode indicates whether the EEPROM address and byte have
97        been latched.
98      - eeprom_waddr indicates the EEPROM address that was latched
99        and eeprom_wbyte is the byte that was latched.
100      - eeprom_wcycle indicates the CPU absolute cycle type when
101        the high voltage was applied (successfully) on the EEPROM.
102 
103      These data members are setup only when we detect good EEPROM programing
104      conditions (see Motorola EEPROM Programming and PPROG register usage).
105      When the high voltage is switched off, we look at the CPU absolute
106      cycle time to see if the EEPROM command must succeeds or not.
107      The EEPROM content is updated and saved only at that time.
108      (EEPROM command is: byte zero bits program, byte erase, row erase
109      and bulk erase).
110 
111      The CONFIG register is programmed in the same way.  It is physically
112      located at the end of the EEPROM (eeprom size + 1).  It is not mapped
113      in memory but it's saved in the EEPROM file.  */
114   unsigned long               eeprom_wcycle;
115   uint16_t                    eeprom_waddr;
116   uint8_t                     eeprom_wbyte;
117   uint8_t                     eeprom_wmode;
118 
119   uint8_t*                    eeprom;
120 
121   /* Minimum time in CPU cycles for programming the EEPROM.  */
122   unsigned long         eeprom_min_cycles;
123 
124   const char*           file_name;
125 };
126 
127 
128 
129 /* Finish off the partially created hw device.  Attach our local
130    callbacks.  Wire up our port names etc.  */
131 
132 static hw_io_read_buffer_method m68hc11eepr_io_read_buffer;
133 static hw_io_write_buffer_method m68hc11eepr_io_write_buffer;
134 static hw_ioctl_method m68hc11eepr_ioctl;
135 
136 /* Read or write the memory bank content from/to a file.
137    Returns 0 if the operation succeeded and -1 if it failed.  */
138 static int
m6811eepr_memory_rw(struct m68hc11eepr * controller,int mode)139 m6811eepr_memory_rw (struct m68hc11eepr *controller, int mode)
140 {
141   const char *name = controller->file_name;
142   int fd;
143   size_t size;
144 
145   size = controller->size;
146   fd = open (name, mode, 0644);
147   if (fd < 0)
148     {
149       if (mode == O_RDONLY)
150         {
151           memset (controller->eeprom, 0xFF, size);
152           /* Default value for CONFIG register (0xFF should be ok):
153              controller->eeprom[size - 1] = M6811_NOSEC | M6811_NOCOP
154                                             | M6811_ROMON | M6811_EEON;  */
155           return 0;
156         }
157       return -1;
158     }
159 
160   if (mode == O_RDONLY)
161     {
162       if (read (fd, controller->eeprom, size) != size)
163           {
164             close (fd);
165             return -1;
166           }
167     }
168   else
169     {
170       if (write (fd, controller->eeprom, size) != size)
171           {
172             close (fd);
173             return -1;
174           }
175     }
176   close (fd);
177 
178   return 0;
179 }
180 
181 
182 
183 
184 static void
attach_m68hc11eepr_regs(struct hw * me,struct m68hc11eepr * controller)185 attach_m68hc11eepr_regs (struct hw *me,
186                          struct m68hc11eepr *controller)
187 {
188   unsigned_word attach_address;
189   int attach_space;
190   unsigned attach_size;
191   reg_property_spec reg;
192 
193   if (hw_find_property (me, "reg") == NULL)
194     hw_abort (me, "Missing \"reg\" property");
195 
196   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
197     hw_abort (me, "\"reg\" property must contain one addr/size entry");
198 
199   hw_unit_address_to_attach_address (hw_parent (me),
200                                              &reg.address,
201                                              &attach_space,
202                                              &attach_address,
203                                              me);
204   hw_unit_size_to_attach_size (hw_parent (me),
205                                      &reg.size,
206                                      &attach_size, me);
207 
208   /* Attach the two IO registers that control the EEPROM.
209      The EEPROM is only attached at reset time because it may
210      be enabled/disabled by the EEON bit in the CONFIG register.  */
211   hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
212                      io_map, M6811_PPROG, 1, me);
213   hw_attach_address (hw_parent (me), M6811_IO_LEVEL,
214                      io_map, M6811_CONFIG, 1, me);
215 
216   if (hw_find_property (me, "file") == NULL)
217     controller->file_name = "m6811.eeprom";
218   else
219     controller->file_name = hw_find_string_property (me, "file");
220 
221   controller->attach_space = attach_space;
222   controller->base_address = attach_address;
223   controller->eeprom = hw_malloc (me, attach_size + 1);
224   controller->eeprom_min_cycles = 10000;
225   controller->size = attach_size + 1;
226   controller->mapped = 0;
227 
228   m6811eepr_memory_rw (controller, O_RDONLY);
229 }
230 
231 
232 /* An event arrives on an interrupt port.  */
233 
234 static void
m68hc11eepr_port_event(struct hw * me,int my_port,struct hw * source,int source_port,int level)235 m68hc11eepr_port_event (struct hw *me,
236                         int my_port,
237                         struct hw *source,
238                         int source_port,
239                         int level)
240 {
241   SIM_DESC sd;
242   struct m68hc11eepr *controller;
243   sim_cpu *cpu;
244   struct m68hc11_sim_cpu *m68hc11_cpu;
245 
246   controller = hw_data (me);
247   sd         = hw_system (me);
248   cpu        = STATE_CPU (sd, 0);
249   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
250   switch (my_port)
251     {
252     case RESET_PORT:
253       {
254           HW_TRACE ((me, "EEPROM reset"));
255 
256         /* Re-read the EEPROM from the file.  This gives the chance
257            to users to erase this file before doing a reset and have
258            a fresh EEPROM taken into account.  */
259         m6811eepr_memory_rw (controller, O_RDONLY);
260 
261         /* Reset the state of EEPROM programmer.  The CONFIG register
262            is also initialized from the EEPROM/file content.  */
263         m68hc11_cpu->ios[M6811_PPROG]    = 0;
264         if (m68hc11_cpu->cpu_use_local_config)
265           m68hc11_cpu->ios[M6811_CONFIG] = m68hc11_cpu->cpu_config;
266         else
267           m68hc11_cpu->ios[M6811_CONFIG]   = controller->eeprom[controller->size-1];
268         controller->eeprom_wmode = 0;
269         controller->eeprom_waddr = 0;
270         controller->eeprom_wbyte = 0;
271 
272         /* Attach or detach to the bus depending on the EEPROM enable bit.
273            The EEPROM CONFIG register is still enabled and can be programmed
274            for a next configuration (taken into account only after a reset,
275            see Motorola spec).  */
276         if (!(m68hc11_cpu->ios[M6811_CONFIG] & M6811_EEON))
277           {
278             if (controller->mapped)
279               hw_detach_address (hw_parent (me), M6811_EEPROM_LEVEL,
280                                  controller->attach_space,
281                                  controller->base_address,
282                                  controller->size - 1,
283                                  me);
284             controller->mapped = 0;
285           }
286         else
287           {
288             if (!controller->mapped)
289               hw_attach_address (hw_parent (me), M6811_EEPROM_LEVEL,
290                                  controller->attach_space,
291                                  controller->base_address,
292                                  controller->size - 1,
293                                  me);
294             controller->mapped = 1;
295           }
296         break;
297       }
298 
299     default:
300       hw_abort (me, "Event on unknown port %d", my_port);
301       break;
302     }
303 }
304 
305 
306 static void
m68hc11eepr_finish(struct hw * me)307 m68hc11eepr_finish (struct hw *me)
308 {
309   struct m68hc11eepr *controller;
310 
311   controller = HW_ZALLOC (me, struct m68hc11eepr);
312   set_hw_data (me, controller);
313   set_hw_io_read_buffer (me, m68hc11eepr_io_read_buffer);
314   set_hw_io_write_buffer (me, m68hc11eepr_io_write_buffer);
315   set_hw_ports (me, m68hc11eepr_ports);
316   set_hw_port_event (me, m68hc11eepr_port_event);
317 #ifdef set_hw_ioctl
318   set_hw_ioctl (me, m68hc11eepr_ioctl);
319 #else
320   me->to_ioctl = m68hc11eepr_ioctl;
321 #endif
322 
323   attach_m68hc11eepr_regs (me, controller);
324 }
325 
326 
327 
328 static io_reg_desc pprog_desc[] = {
329   { M6811_BYTE,  "BYTE  ", "Byte Program Mode" },
330   { M6811_ROW,   "ROW   ", "Row Program Mode" },
331   { M6811_ERASE, "ERASE ", "Erase Mode" },
332   { M6811_EELAT, "EELAT ", "EEProm Latch Control" },
333   { M6811_EEPGM, "EEPGM ", "EEProm Programming Voltable Enable" },
334   { 0,  0, 0 }
335 };
336 extern io_reg_desc config_desc[];
337 
338 
339 /* Describe the state of the EEPROM device.  */
340 static void
m68hc11eepr_info(struct hw * me)341 m68hc11eepr_info (struct hw *me)
342 {
343   SIM_DESC sd;
344   uint16_t base = 0;
345   sim_cpu *cpu;
346   struct m68hc11_sim_cpu *m68hc11_cpu;
347   struct m68hc11eepr *controller;
348   uint8_t val;
349 
350   sd         = hw_system (me);
351   cpu        = STATE_CPU (sd, 0);
352   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
353   controller = hw_data (me);
354   base       = cpu_get_io_base (cpu);
355 
356   sim_io_printf (sd, "M68HC11 EEprom:\n");
357 
358   val = m68hc11_cpu->ios[M6811_PPROG];
359   print_io_byte (sd, "PPROG  ", pprog_desc, val, base + M6811_PPROG);
360   sim_io_printf (sd, "\n");
361 
362   val = m68hc11_cpu->ios[M6811_CONFIG];
363   print_io_byte (sd, "CONFIG ", config_desc, val, base + M6811_CONFIG);
364   sim_io_printf (sd, "\n");
365 
366   val = controller->eeprom[controller->size - 1];
367   print_io_byte (sd, "(*NEXT*) ", config_desc, val, base + M6811_CONFIG);
368   sim_io_printf (sd, "\n");
369 
370   /* Describe internal state of EEPROM.  */
371   if (controller->eeprom_wmode)
372     {
373       if (controller->eeprom_waddr == controller->size - 1)
374         sim_io_printf (sd, "  Programming CONFIG register ");
375       else
376         sim_io_printf (sd, "  Programming: 0x%04x ",
377                        controller->eeprom_waddr + controller->base_address);
378 
379       sim_io_printf (sd, "with 0x%02x\n",
380                          controller->eeprom_wbyte);
381     }
382 
383   sim_io_printf (sd, "  EEProm file: %s\n",
384                  controller->file_name);
385 }
386 
387 static int
m68hc11eepr_ioctl(struct hw * me,hw_ioctl_request request,va_list ap)388 m68hc11eepr_ioctl (struct hw *me,
389                        hw_ioctl_request request,
390                        va_list ap)
391 {
392   m68hc11eepr_info (me);
393   return 0;
394 }
395 
396 /* generic read/write */
397 
398 static unsigned
m68hc11eepr_io_read_buffer(struct hw * me,void * dest,int space,unsigned_word base,unsigned nr_bytes)399 m68hc11eepr_io_read_buffer (struct hw *me,
400                                   void *dest,
401                                   int space,
402                                   unsigned_word base,
403                                   unsigned nr_bytes)
404 {
405   SIM_DESC sd;
406   struct m68hc11eepr *controller;
407   sim_cpu *cpu;
408   struct m68hc11_sim_cpu *m68hc11_cpu;
409 
410   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
411 
412   sd         = hw_system (me);
413   controller = hw_data (me);
414   cpu        = STATE_CPU (sd, 0);
415   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
416 
417   if (space == io_map)
418     {
419       unsigned cnt = 0;
420 
421       while (nr_bytes != 0)
422         {
423           switch (base)
424             {
425             case M6811_PPROG:
426             case M6811_CONFIG:
427               *((uint8_t*) dest) = m68hc11_cpu->ios[base];
428               break;
429 
430             default:
431               hw_abort (me, "reading wrong register 0x%04x", base);
432             }
433           dest = (uint8_t*) (dest) + 1;
434           base++;
435           nr_bytes--;
436           cnt++;
437         }
438       return cnt;
439     }
440 
441   /* In theory, we can't read the EEPROM when it's being programmed.  */
442   if ((m68hc11_cpu->ios[M6811_PPROG] & M6811_EELAT) != 0
443       && cpu_is_running (cpu))
444     {
445       sim_memory_error (cpu, SIM_SIGBUS, base,
446                               "EEprom not configured for reading");
447     }
448 
449   base = base - controller->base_address;
450   memcpy (dest, &controller->eeprom[base], nr_bytes);
451   return nr_bytes;
452 }
453 
454 
455 static unsigned
m68hc11eepr_io_write_buffer(struct hw * me,const void * source,int space,unsigned_word base,unsigned nr_bytes)456 m68hc11eepr_io_write_buffer (struct hw *me,
457                                    const void *source,
458                                    int space,
459                                    unsigned_word base,
460                                    unsigned nr_bytes)
461 {
462   SIM_DESC sd;
463   struct m68hc11eepr *controller;
464   sim_cpu *cpu;
465   struct m68hc11_sim_cpu *m68hc11_cpu;
466   uint8_t val;
467 
468   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
469 
470   sd         = hw_system (me);
471   controller = hw_data (me);
472   cpu        = STATE_CPU (sd, 0);
473   m68hc11_cpu  = M68HC11_SIM_CPU (cpu);
474 
475   /* Programming several bytes at a time is not possible.  */
476   if (space != io_map && nr_bytes != 1)
477     {
478       sim_memory_error (cpu, SIM_SIGBUS, base,
479                               "EEprom write error (only 1 byte can be programmed)");
480       return 0;
481     }
482 
483   if (nr_bytes != 1)
484     hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");
485 
486   val = *((const uint8_t*) source);
487 
488   /* Write to the EEPROM control register.  */
489   if (space == io_map && base == M6811_PPROG)
490     {
491       uint8_t wrong_bits;
492       uint16_t addr;
493 
494       addr = base + cpu_get_io_base (cpu);
495 
496       /* Setting EELAT and EEPGM at the same time is an error.
497          Clearing them both is ok.  */
498       wrong_bits = (m68hc11_cpu->ios[M6811_PPROG] ^ val) & val;
499       wrong_bits &= (M6811_EELAT | M6811_EEPGM);
500 
501       if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
502           {
503             sim_memory_error (cpu, SIM_SIGBUS, addr,
504                                   "Wrong eeprom programing value");
505             return 0;
506           }
507 
508       if ((val & M6811_EELAT) == 0)
509           {
510             val = 0;
511           }
512       if ((val & M6811_EEPGM) && !(m68hc11_cpu->ios[M6811_PPROG] & M6811_EELAT))
513           {
514             sim_memory_error (cpu, SIM_SIGBUS, addr,
515                                   "EEProm high voltage applied after EELAT");
516           }
517       if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
518           {
519             sim_memory_error (cpu, SIM_SIGSEGV, addr,
520                                   "EEProm high voltage applied without address");
521           }
522       if (val & M6811_EEPGM)
523           {
524             controller->eeprom_wcycle = cpu_current_cycle (cpu);
525           }
526       else if (m68hc11_cpu->ios[M6811_PPROG] & M6811_PPROG)
527           {
528             int i;
529             unsigned long t = cpu_current_cycle (cpu);
530 
531             t -= controller->eeprom_wcycle;
532             if (t < controller->eeprom_min_cycles)
533               {
534                 sim_memory_error (cpu, SIM_SIGILL, addr,
535                                         "EEprom programmed only for %lu cycles",
536                                         t);
537               }
538 
539             /* Program the byte by clearing some bits.  */
540             if (!(m68hc11_cpu->ios[M6811_PPROG] & M6811_ERASE))
541               {
542                 controller->eeprom[controller->eeprom_waddr]
543                     &= controller->eeprom_wbyte;
544               }
545 
546             /* Erase a byte, row or the complete eeprom.  Erased value is 0xFF.
547              Ignore row or complete eeprom erase when we are programming the
548              CONFIG register (last EEPROM byte).  */
549             else if ((m68hc11_cpu->ios[M6811_PPROG] & M6811_BYTE)
550                    || controller->eeprom_waddr == controller->size - 1)
551               {
552                 controller->eeprom[controller->eeprom_waddr] = 0xff;
553               }
554             else if (m68hc11_cpu->ios[M6811_BYTE] & M6811_ROW)
555               {
556               size_t max_size;
557 
558               /* Size of EEPROM (-1 because the last byte is the
559                  CONFIG register.  */
560               max_size = controller->size;
561                 controller->eeprom_waddr &= 0xFFF0;
562                 for (i = 0; i < 16
563                      && controller->eeprom_waddr < max_size; i++)
564                     {
565                       controller->eeprom[controller->eeprom_waddr] = 0xff;
566                       controller->eeprom_waddr ++;
567                     }
568               }
569             else
570               {
571               size_t max_size;
572 
573               max_size = controller->size;
574                 for (i = 0; i < max_size; i++)
575                     {
576                       controller->eeprom[i] = 0xff;
577                     }
578               }
579 
580             /* Save the eeprom in a file.  We have to save after each
581                change because the simulator can be stopped or crash...  */
582             if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
583               {
584                 sim_memory_error (cpu, SIM_SIGABRT, addr,
585                                         "EEPROM programing failed: errno=%d", errno);
586               }
587             controller->eeprom_wmode = 0;
588           }
589       m68hc11_cpu->ios[M6811_PPROG] = val;
590       return 1;
591     }
592 
593   /* The CONFIG IO register is mapped at end of EEPROM.
594      It's not visible.  */
595   if (space == io_map && base == M6811_CONFIG)
596     {
597       base = controller->size - 1;
598     }
599   else
600     {
601       base = base - controller->base_address;
602     }
603 
604   /* Writing the memory is allowed for the Debugger or simulator
605      (cpu not running).  */
606   if (cpu_is_running (cpu))
607     {
608       if ((m68hc11_cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
609           {
610             sim_memory_error (cpu, SIM_SIGSEGV, base,
611                                   "EEprom not configured for writing");
612             return 0;
613           }
614       if (controller->eeprom_wmode != 0)
615           {
616             sim_memory_error (cpu, SIM_SIGSEGV, base,
617                                   "EEprom write error");
618             return 0;
619           }
620       controller->eeprom_wmode = 1;
621       controller->eeprom_waddr = base;
622       controller->eeprom_wbyte = val;
623     }
624   else
625     {
626       controller->eeprom[base] = val;
627       m6811eepr_memory_rw (controller, O_WRONLY);
628     }
629 
630   return 1;
631 }
632 
633 const struct hw_descriptor dv_m68hc11eepr_descriptor[] = {
634   { "m68hc11eepr", m68hc11eepr_finish },
635   { "m68hc12eepr", m68hc11eepr_finish },
636   { NULL },
637 };
638 
639