/[gxemul]/trunk/src/devices/dev_mc146818.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/src/devices/dev_mc146818.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 19 by dpavlin, Mon Oct 8 16:19:01 2007 UTC revision 20 by dpavlin, Mon Oct 8 16:19:23 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *     *  
27   *   *
28   *  $Id: dev_mc146818.c,v 1.75 2005/10/09 22:21:31 debug Exp $   *  $Id: dev_mc146818.c,v 1.80 2005/11/20 11:28:45 debug Exp $
29   *     *  
30   *  MC146818 real-time clock, used by many different machines types.   *  MC146818 real-time clock, used by many different machines types.
31   *  (DS1687 as used in some other machines is also similar to the MC146818.)   *  (DS1687 as used in some other machines is also similar to the MC146818.)
# Line 53  Line 53 
53    
54    
55  #define to_bcd(x)       ( ((x)/10) * 16 + ((x)%10) )  #define to_bcd(x)       ( ((x)/10) * 16 + ((x)%10) )
56    #define from_bcd(x)     ( ((x)>>4) * 10 + ((x)&15) )
57    
58  /*  #define MC146818_DEBUG  */  /*  #define MC146818_DEBUG  */
59    
# Line 76  struct mc_data { Line 77  struct mc_data {
77          int     irq_nr;          int     irq_nr;
78    
79          int     previous_second;          int     previous_second;
80            int     n_seconds_elapsed;
81            int     uip_threshold;
82    
83          int     interrupt_every_x_cycles;          int     interrupt_every_x_cycles;
84          int     cycles_left_until_interrupt;          int     cycles_left_until_interrupt;
85    
86            int     ugly_netbsd_prep_hack_done;
87            int     ugly_netbsd_prep_hack_sec;
88  };  };
89    
90    
91  /*  /*
92     *  Ugly hack to fool NetBSD/prep to accept the clock.  (See mcclock_isa_match
93     *  in NetBSD's arch/prep/isa/mcclock_isa.c for details.)
94     */
95    #define NETBSD_HACK_INIT                0
96    #define NETBSD_HACK_FIRST_1             1
97    #define NETBSD_HACK_FIRST_2             2
98    #define NETBSD_HACK_SECOND_1            3
99    #define NETBSD_HACK_SECOND_2            4
100    #define NETBSD_HACK_DONE                5
101    
102    
103    /*
104   *  recalc_interrupt_cycle():   *  recalc_interrupt_cycle():
105   *   *
106   *  If automatic_clock_adjustment is turned on, then emulated_hz is modified   *  If automatic_clock_adjustment is turned on, then emulated_hz is modified
# Line 133  void dev_mc146818_tick(struct cpu *cpu, Line 151  void dev_mc146818_tick(struct cpu *cpu,
151  {  {
152          struct mc_data *d = extra;          struct mc_data *d = extra;
153    
         if (d == NULL)  
                 return;  
   
154          recalc_interrupt_cycle(cpu, d);          recalc_interrupt_cycle(cpu, d);
155    
156          if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) &&          if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) &&
# Line 226  static void mc146818_update_time(struct Line 241  static void mc146818_update_time(struct
241           *  Special hacks for emulating the behaviour of various machines:           *  Special hacks for emulating the behaviour of various machines:
242           */           */
243          switch (d->access_style) {          switch (d->access_style) {
244            case MC146818_ALGOR:
245                    d->reg[4 * MC_YEAR] += 80;
246                    break;
247          case MC146818_ARC_NEC:          case MC146818_ARC_NEC:
248                  d->reg[4 * MC_YEAR] += (0x18 - 104);                  d->reg[4 * MC_YEAR] += (0x18 - 104);
249                  break;                  break;
# Line 307  int dev_mc146818_access(struct cpu *cpu, Line 325  int dev_mc146818_access(struct cpu *cpu,
325    
326          /*  Different ways of accessing the registers:  */          /*  Different ways of accessing the registers:  */
327          switch (d->access_style) {          switch (d->access_style) {
328            case MC146818_ALGOR:
329          case MC146818_CATS:          case MC146818_CATS:
330          case MC146818_PC_CMOS:          case MC146818_PC_CMOS:
331                  if ((relative_addr & 1) == 0x00) {                  if ((relative_addr & 1) == 0x00) {
# Line 351  int dev_mc146818_access(struct cpu *cpu, Line 370  int dev_mc146818_access(struct cpu *cpu,
370                   *  should be ignored. It works _almost_ as DEC, if offsets are                   *  should be ignored. It works _almost_ as DEC, if offsets are
371                   *  divided by 0x40.                   *  divided by 0x40.
372                   */                   */
373                    break;
374            case MC146818_PMPPC:
375                    relative_addr *= 4;
376                    break;
377          default:          default:
378                  ;                  ;
379          }          }
# Line 360  int dev_mc146818_access(struct cpu *cpu, Line 383  int dev_mc146818_access(struct cpu *cpu,
383                  fatal("[ mc146818: write to addr=0x%04x (len %i): ",                  fatal("[ mc146818: write to addr=0x%04x (len %i): ",
384                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
385                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
386                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
387                  fatal("]\n");                  fatal("]\n");
388          }          }
389  #endif  #endif
# Line 373  int dev_mc146818_access(struct cpu *cpu, Line 396  int dev_mc146818_access(struct cpu *cpu,
396           *  Linux on at least sgimips and evbmips (Malta) wants the UIP bit           *  Linux on at least sgimips and evbmips (Malta) wants the UIP bit
397           *  in REGA to be updated once a second.           *  in REGA to be updated once a second.
398           */           */
399          timet = time(NULL);          if (relative_addr == MC_REGA*4 || relative_addr == MC_REGC*4) {
400          tmp = gmtime(&timet);                  timet = time(NULL);
401          d->reg[MC_REGC * 4] &= ~MC_REGC_UF;                  tmp = gmtime(&timet);
402                    d->reg[MC_REGC * 4] &= ~MC_REGC_UF;
403                    if (tmp->tm_sec != d->previous_second) {
404                            d->n_seconds_elapsed ++;
405                            d->previous_second = tmp->tm_sec;
406                    }
407                    if (d->n_seconds_elapsed > d->uip_threshold) {
408                            d->n_seconds_elapsed = 0;
409    
410          if (tmp->tm_sec != d->previous_second) {                          d->reg[MC_REGA * 4] |= MC_REGA_UIP;
                 d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;  
411    
412                  d->reg[MC_REGC * 4] |= MC_REGC_UF;                          d->reg[MC_REGC * 4] |= MC_REGC_UF;
413                  d->reg[MC_REGC * 4] |= MC_REGC_IRQF;                          d->reg[MC_REGC * 4] |= MC_REGC_IRQF;
                 d->previous_second = tmp->tm_sec;  
414    
415                  /*  For some reason, some Linux/DECstation KN04 kernels want                          /*  For some reason, some Linux/DECstation KN04
416                      the PF (periodic flag) bit set, even though interrupts                              kernels want the PF (periodic flag) bit set,
417                      are not enabled?  */                              even though interrupts are not enabled?  */
418                  d->reg[MC_REGC * 4] |= MC_REGC_PF;                          d->reg[MC_REGC * 4] |= MC_REGC_PF;
419          } else                  } else
420                  d->reg[MC_REGA * 4] |= MC_REGA_UIP;                          d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;
421            }
422    
423          /*  RTC data is in either BCD format or binary:  */          /*  RTC data is in either BCD format or binary:  */
424          if (d->use_bcd)          if (d->use_bcd)
# Line 439  int dev_mc146818_access(struct cpu *cpu, Line 468  int dev_mc146818_access(struct cpu *cpu,
468                          case MC_RATE_8_Hz:      d->interrupt_hz = 8;    break;                          case MC_RATE_8_Hz:      d->interrupt_hz = 8;    break;
469                          case MC_RATE_4_Hz:      d->interrupt_hz = 4;    break;                          case MC_RATE_4_Hz:      d->interrupt_hz = 4;    break;
470                          case MC_RATE_2_Hz:      d->interrupt_hz = 2;    break;                          case MC_RATE_2_Hz:      d->interrupt_hz = 2;    break;
471                          default:                          default:/*  debug("[ mc146818: unimplemented "
                                 /*  debug("[ mc146818: unimplemented "  
472                                      "MC_REGA RS: %i ]\n",                                      "MC_REGA RS: %i ]\n",
473                                      data[0] & MC_REGA_RSMASK);  */                                      data[0] & MC_REGA_RSMASK);  */
474                                  ;                                  ;
# Line 506  int dev_mc146818_access(struct cpu *cpu, Line 534  int dev_mc146818_access(struct cpu *cpu,
534                  case 0x15:                  case 0x15:
535                          break;                          break;
536                  case 4 * MC_SEC:                  case 4 * MC_SEC:
537                            if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) {
538                                    d->ugly_netbsd_prep_hack_done ++;
539                                    switch (d->ugly_netbsd_prep_hack_done) {
540                                    case NETBSD_HACK_FIRST_1:
541                                            d->ugly_netbsd_prep_hack_sec =
542                                                from_bcd(d->reg[relative_addr]);
543                                            break;
544                                    case NETBSD_HACK_FIRST_2:
545                                            d->reg[relative_addr] = to_bcd(
546                                                d->ugly_netbsd_prep_hack_sec);
547                                            break;
548                                    case NETBSD_HACK_SECOND_1:
549                                    case NETBSD_HACK_SECOND_2:
550                                            d->reg[relative_addr] = to_bcd((1 +
551                                                d->ugly_netbsd_prep_hack_sec) % 60);
552                                            break;
553                                    }
554                            }
555                  case 4 * MC_MIN:                  case 4 * MC_MIN:
556                  case 4 * MC_HOUR:                  case 4 * MC_HOUR:
557                  case 4 * MC_DOW:                  case 4 * MC_DOW:
# Line 522  int dev_mc146818_access(struct cpu *cpu, Line 568  int dev_mc146818_access(struct cpu *cpu,
568                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)
569                                  break;                                  break;
570    
571                          mc146818_update_time(d);                          if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE)
572                                    mc146818_update_time(d);
573                            break;
574                    case 4 * MC_REGA:
575                          break;                          break;
576                  case 4 * MC_REGC:       /*  Interrupt ack.  */                  case 4 * MC_REGC:       /*  Interrupt ack.  */
577                          /*  NOTE: Acking is done below, _after_ the                          /*  NOTE: Acking is done below, _after_ the
578                              register has been read.  */                              register has been read.  */
579                          break;                          break;
580                  default:                  default:debug("[ mc146818: read from relative_addr = "
                         debug("[ mc146818: read from relative_addr = "  
581                              "%04x ]\n", (int)relative_addr);                              "%04x ]\n", (int)relative_addr);
                         ;  
582                  }                  }
583    
584                  data[0] = d->reg[relative_addr];                  data[0] = d->reg[relative_addr];
# Line 549  int dev_mc146818_access(struct cpu *cpu, Line 596  int dev_mc146818_access(struct cpu *cpu,
596                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",
597                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
598                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
599                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
600                  fatal("]\n");                  fatal("]\n");
601          }          }
602  #endif  #endif
# Line 582  void dev_mc146818_init(struct machine *m Line 629  void dev_mc146818_init(struct machine *m
629          d->access_style  = access_style;          d->access_style  = access_style;
630          d->addrdiv       = addrdiv;          d->addrdiv       = addrdiv;
631    
         /*  Only SGIs and PCs use BCD format (?)  */  
632          d->use_bcd = 0;          d->use_bcd = 0;
633          if (access_style == MC146818_SGI || access_style == MC146818_PC_CMOS)          switch (access_style) {
634            case MC146818_SGI:
635            case MC146818_PC_CMOS:
636            case MC146818_PMPPC:
637                  d->use_bcd = 1;                  d->use_bcd = 1;
638            }
639    
640            if (machine->machine_type != MACHINE_PREP) {
641                    /*  NetBSD/prep has a really ugly clock detection code;
642                        all other machines/OSes don't need this.  */
643                    d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE;
644            }
645    
646          if (access_style == MC146818_DEC) {          if (access_style == MC146818_DEC) {
647          /*  Station Ethernet Address, on DECstation 3100:  */          /*  Station Ethernet Address, on DECstation 3100:  */
# Line 625  void dev_mc146818_init(struct machine *m Line 681  void dev_mc146818_init(struct machine *m
681                  d->reg[0xf8] = 1;                  d->reg[0xf8] = 1;
682          }          }
683    
684            /*
685             *  uip_threshold should ideally be 1, but when Linux polls the UIP bit
686             *  it looses speed. This hack gives Linux the impression that the cpu
687             *  is uip_threshold times faster than the slow clock it would
688             *  otherwise detect.
689             *
690             *  TODO:  Find out if this messes up Sprite emulation; if so, then
691             *         this hack has to be removed.
692             */
693            d->uip_threshold = 5;
694    
695          if (access_style == MC146818_ARC_JAZZ)          if (access_style == MC146818_ARC_JAZZ)
696                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,
697                      1, dev_mc146818_jazz_access, d, MEM_DEFAULT, NULL);                      1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL);
698    
699          dev_len = DEV_MC146818_LENGTH;          dev_len = DEV_MC146818_LENGTH;
700          switch (access_style) {          switch (access_style) {
# Line 641  void dev_mc146818_init(struct machine *m Line 708  void dev_mc146818_init(struct machine *m
708    
709          memory_device_register(mem, "mc146818", baseaddr,          memory_device_register(mem, "mc146818", baseaddr,
710              dev_len * addrdiv, dev_mc146818_access,              dev_len * addrdiv, dev_mc146818_access,
711              d, MEM_DEFAULT, NULL);              d, DM_DEFAULT, NULL);
712    
713          mc146818_update_time(d);          mc146818_update_time(d);
714    

Legend:
Removed from v.19  
changed lines
  Added in v.20

  ViewVC Help
Powered by ViewVC 1.1.26