/[gxemul]/trunk/src/cpus/cpu_sh.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/cpus/cpu_sh.c

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

revision 37 by dpavlin, Mon Oct 8 16:21:34 2007 UTC revision 38 by dpavlin, Mon Oct 8 16:21:53 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: cpu_sh.c,v 1.62 2007/03/08 19:04:09 debug Exp $   *  $Id: cpu_sh.c,v 1.66 2007/04/13 07:06:31 debug Exp $
29   *   *
30   *  Hitachi SuperH ("SH") CPU emulation.   *  Hitachi SuperH ("SH") CPU emulation.
31   *   *
# Line 185  int sh_cpu_new(struct cpu *cpu, struct m Line 185  int sh_cpu_new(struct cpu *cpu, struct m
185          }          }
186    
187          /*  SH4-specific memory mapped registers, TLBs, caches, etc:  */          /*  SH4-specific memory mapped registers, TLBs, caches, etc:  */
188          if (cpu->cd.sh.cpu_type.arch == 4)          if (cpu->cd.sh.cpu_type.arch == 4) {
189                  device_add(machine, "sh4");                  device_add(machine, "sh4");
190    
191                    /*
192                     *  Interrupt Controller initial values, according to the
193                     *  SH7760 manual:
194                     */
195                    cpu->cd.sh.intc_iprd = 0xda74;
196                    cpu->cd.sh.intc_intmsk00 = 0xf3ff7fff;
197                    cpu->cd.sh.intc_intmsk04 = 0x00ffffff;
198                    /*  All others are zero.  */
199    
200                    /*  TODO: Initial priorities?  */
201                    cpu->cd.sh.intc_intpri00 = 0x33333333;
202                    cpu->cd.sh.intc_intpri04 = 0x33333333;
203                    cpu->cd.sh.intc_intpri08 = 0x33333333;
204                    cpu->cd.sh.intc_intpri0c = 0x33333333;
205            }
206    
207            sh_update_interrupt_priorities(cpu);
208    
209          return 1;          return 1;
210  }  }
211    
212    
213  /*  /*
214     *  sh_update_interrupt_priorities():
215     *
216     *  SH interrupts are a bit complicated; there are several intc registers
217     *  controlling priorities for various peripherals:
218     *
219     *  Register:  Bits 15..12  11..8  7..4      3..0
220     *  ---------  -----------  -----  ----      ----
221     *  ipra       TMU0         TMU1   TMU2      Reserved
222     *  iprb       WDT          REF    Reserved  Reserved
223     *  iprc       GPIO         DMAC   Reserved  H-UDI
224     *  iprd       IRL0         IRL1   IRL2      IRL3
225     *
226     *  Register:  31..28  27..24  23..20  19..16  15..12  11..8   7..4   3..0
227     *  ---------  ------  ------  ------  ------  ------  -----   ----   ----
228     *  intpri00   IRQ4    IRQ5    IRQ6    IRQ7    Rsrvd.  Rsrvd.  Rsrvd. Reserved
229     *  intpri04   HCAN2,0 HCAN2,1 SSI(0)  SSI(1)  HAC(0)  HAC(1)  I2C(0) I2C(1)
230     *  intpri08   USB     LCDC    DMABRG  SCIF(0) SCIF(1) SCIF(2) SIM    HSPI
231     *  intpri0c   Reserv. Reserv. MMCIF   Reserv. MFI     Rsrvd.  ADC    CMT
232     */
233    void sh_update_interrupt_priorities(struct cpu *cpu)
234    {
235            int i;
236    
237            /*
238             *  Set priorities of known interrupts, without affecting the
239             *  SH_INT_ASSERTED bit:
240             */
241    
242            for (i=SH4_INTEVT_IRQ0; i<=SH4_INTEVT_IRQ14; i+=0x20) {
243                    cpu->cd.sh.int_prio_and_pending[i/0x20] &= ~SH_INT_PRIO_MASK;
244                    cpu->cd.sh.int_prio_and_pending[i/0x20] |= (15 - ((i -
245                        SH4_INTEVT_IRQ0) / 0x20));
246            }
247    
248            cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU0_TUNI0 / 0x20] &=
249                ~SH_INT_PRIO_MASK;
250            cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU0_TUNI0 / 0x20] |=
251                (cpu->cd.sh.intc_ipra >> 12) & 0xf;
252    
253            cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU1_TUNI1 / 0x20] &=
254                ~SH_INT_PRIO_MASK;
255            cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU1_TUNI1 / 0x20] |=
256                (cpu->cd.sh.intc_ipra >> 8) & 0xf;
257    
258            cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU2_TUNI2 / 0x20] &=
259                ~SH_INT_PRIO_MASK;
260            cpu->cd.sh.int_prio_and_pending[SH_INTEVT_TMU2_TUNI2 / 0x20] |=
261                (cpu->cd.sh.intc_ipra >> 4) & 0xf;
262    
263            for (i=SH4_INTEVT_SCIF_ERI; i<=SH4_INTEVT_SCIF_TXI; i+=0x20) {
264                    cpu->cd.sh.int_prio_and_pending[i/0x20] &= ~SH_INT_PRIO_MASK;
265                    cpu->cd.sh.int_prio_and_pending[i/0x20] |=
266                        ((cpu->cd.sh.intc_intpri08 >> 16) & 0xf);
267            }
268    }
269    
270    
271    /*
272   *  sh_cpu_interrupt_assert():   *  sh_cpu_interrupt_assert():
273   */   */
274  void sh_cpu_interrupt_assert(struct interrupt *interrupt)  void sh_cpu_interrupt_assert(struct interrupt *interrupt)
275  {  {
276          struct cpu *cpu = interrupt->extra;          struct cpu *cpu = interrupt->extra;
277          int irq_nr = interrupt->line;          int irq_nr = interrupt->line;
278          int word_index, bit_index;          int index = irq_nr / 0x20;
279            int prio;
280    
281          /*          /*  Assert the interrupt, and check its priority level:  */
282           *  Note: This gives higher interrupt priority to lower number          cpu->cd.sh.int_prio_and_pending[index] |= SH_INT_ASSERTED;
283           *  interrupts. Hopefully this is correct.          prio = cpu->cd.sh.int_prio_and_pending[index] & SH_INT_PRIO_MASK;
284           */  
285            if (prio == 0) {
286                    /*  Interrupt not implemented? Hm.  */
287                    fatal("[ SH interrupt 0x%x, prio 0 (?), aborting ]\n", irq_nr);
288                    exit(1);
289            }
290    
291          if (cpu->cd.sh.int_to_assert == 0 || irq_nr < cpu->cd.sh.int_to_assert)          if (cpu->cd.sh.int_to_assert == 0 || prio > cpu->cd.sh.int_level) {
292                  cpu->cd.sh.int_to_assert = irq_nr;                  cpu->cd.sh.int_to_assert = irq_nr;
293                    cpu->cd.sh.int_level = prio;
294          /*          }
          *  TODO: Keep track of all pending interrupts at multiple levels...  
          *  
          *  This is just a quick hack:  
          */  
         cpu->cd.sh.int_level = 1;  
         if (irq_nr == SH_INTEVT_TMU0_TUNI0)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf;  
         if (irq_nr == SH_INTEVT_TMU1_TUNI1)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf;  
         if (irq_nr == SH_INTEVT_TMU2_TUNI2)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf;  
         if (irq_nr >= SH4_INTEVT_SCIF_ERI &&  
             irq_nr <= SH4_INTEVT_SCIF_TXI)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf;  
   
         irq_nr /= 0x20;  
         word_index = irq_nr / (sizeof(uint32_t)*8);  
         bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1);  
   
         cpu->cd.sh.int_pending[word_index] |= (1 << bit_index);  
295  }  }
296    
297    
# Line 240  void sh_cpu_interrupt_deassert(struct in Line 302  void sh_cpu_interrupt_deassert(struct in
302  {  {
303          struct cpu *cpu = interrupt->extra;          struct cpu *cpu = interrupt->extra;
304          int irq_nr = interrupt->line;          int irq_nr = interrupt->line;
305          int word_index, bit_index;          int index = irq_nr / 0x20;
306    
307          if (cpu->cd.sh.int_to_assert == irq_nr) {          /*  Deassert the interrupt:  */
308                  /*          if (cpu->cd.sh.int_prio_and_pending[index] & SH_INT_ASSERTED) {
309                   *  Rescan all interrupts to see if any are still asserted.                  cpu->cd.sh.int_prio_and_pending[index] &= ~SH_INT_ASSERTED;
                  *  
                  *  Note: The scan only has to go from irq_nr + 0x20 to the max  
                  *        index, since any lower interrupt cannot be asserted  
                  *        at this time.  
                  */  
                 int i, max = 0x1000;  
                 cpu->cd.sh.int_to_assert = 0;  
   
                 for (i=irq_nr+0x20; i<max; i+=0x20) {  
                         int j = i / 0x20;  
                         int word_index = j / (sizeof(uint32_t)*8);  
                         int bit_index = j & ((sizeof(uint32_t)*8) - 1);  
   
                         /*  Skip entire word if no bits are set:  */  
                         if (bit_index == 0 &&  
                             cpu->cd.sh.int_pending[word_index] == 0)  
                                 i += (sizeof(uint32_t)*8 - 1) * 0x20;  
                         else if (cpu->cd.sh.int_pending[word_index]  
                             & (1 << bit_index)) {  
                                 cpu->cd.sh.int_to_assert = i;  
   
   
 /*  Hack. TODO: Fix.  */  
         cpu->cd.sh.int_level = 1;  
         if (i == SH_INTEVT_TMU0_TUNI0)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf;  
         if (i == SH_INTEVT_TMU1_TUNI1)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf;  
         if (i == SH_INTEVT_TMU2_TUNI2)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf;  
         if (i >= SH4_INTEVT_SCIF_ERI &&  
             i <= SH4_INTEVT_SCIF_TXI)  
                 cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf;  
310    
311                    /*  Calculate new interrupt assertion:  */
312                    cpu->cd.sh.int_to_assert = 0;
313                    cpu->cd.sh.int_level = 0;
314    
315                                  break;                  /*  NOTE/TODO: This is slow, but should hopefully work:  */
316                    for (index=0; index<0x1000/0x20; index++) {
317                            uint8_t x = cpu->cd.sh.int_prio_and_pending[index];
318                            uint8_t prio = x & SH_INT_PRIO_MASK;
319                            if (x & SH_INT_ASSERTED &&
320                                prio > cpu->cd.sh.int_level) {
321                                    cpu->cd.sh.int_to_assert = index * 0x20;
322                                    cpu->cd.sh.int_level = prio;
323                          }                          }
324                  }                  }
325          }          }
   
         irq_nr /= 0x20;  
         word_index = irq_nr / (sizeof(uint32_t)*8);  
         bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1);  
   
         cpu->cd.sh.int_pending[word_index] &= ~(1 << bit_index);  
326  }  }
327    
328    
# Line 508  void sh_cpu_tlbdump(struct machine *m, i Line 541  void sh_cpu_tlbdump(struct machine *m, i
541    
542    
543  /*  /*
  *  sh_cpu_gdb_stub():  
  *  
  *  Execute a "remote GDB" command. Returns a newly allocated response string  
  *  on success, NULL on failure.  
  */  
 char *sh_cpu_gdb_stub(struct cpu *cpu, char *cmd)  
 {  
         fatal("sh_cpu_gdb_stub(): TODO\n");  
         return NULL;  
 }  
   
   
 /*  
544   *  sh_update_sr():   *  sh_update_sr():
545   *   *
546   *  Writes a new value to the status register.   *  Writes a new value to the status register.
# Line 569  void sh_update_fpscr(struct cpu *cpu, ui Line 589  void sh_update_fpscr(struct cpu *cpu, ui
589   *   *
590   *  Causes a transfer of control to an exception or interrupt handler.   *  Causes a transfer of control to an exception or interrupt handler.
591   *  If intevt > 0, then it is an interrupt, otherwise an exception.   *  If intevt > 0, then it is an interrupt, otherwise an exception.
592     *
593     *  vaddr contains the faulting address, on TLB exceptions.
594   */   */
595  void sh_exception(struct cpu *cpu, int expevt, int intevt, uint32_t vaddr)  void sh_exception(struct cpu *cpu, int expevt, int intevt, uint32_t vaddr)
596  {  {
# Line 580  void sh_exception(struct cpu *cpu, int e Line 602  void sh_exception(struct cpu *cpu, int e
602                  else                  else
603                          debug("[ exception 0x%03x", expevt);                          debug("[ exception 0x%03x", expevt);
604    
605                  debug(", pc=0x%08"PRIx32" ", (uint32_t)vaddr);                  debug(", pc=0x%08"PRIx32" ", (uint32_t)cpu->pc);
606                  if (intevt == 0)                  if (intevt == 0)
607                          debug("vaddr=0x%08"PRIx32" ", vaddr);                          debug("vaddr=0x%08"PRIx32" ", vaddr);
608    
# Line 588  void sh_exception(struct cpu *cpu, int e Line 610  void sh_exception(struct cpu *cpu, int e
610          }          }
611    
612          if (cpu->cd.sh.sr & SH_SR_BL) {          if (cpu->cd.sh.sr & SH_SR_BL) {
613                  fatal("sh_exception(): BL bit already set. TODO\n");                  fatal("[ sh_exception(): BL bit already set. ]\n");
614    
615                  /*  This is actually OK in two cases: a User Break,                  /*  This is actually OK in two cases: a User Break,
616                      or on NMI interrupts if a special flag is set?  */                      or on NMI interrupts if a special flag is set?  */
# Line 613  void sh_exception(struct cpu *cpu, int e Line 635  void sh_exception(struct cpu *cpu, int e
635                  cpu->pc -= sizeof(uint16_t);                  cpu->pc -= sizeof(uint16_t);
636          }          }
637    
638          /*  Stuff common to all exceptions:  */  
639            /*
640             *  Stuff common to all exceptions:
641             */
642    
643          cpu->cd.sh.spc = cpu->pc;          cpu->cd.sh.spc = cpu->pc;
644          cpu->cd.sh.ssr = cpu->cd.sh.sr;          cpu->cd.sh.ssr = cpu->cd.sh.sr;
645          cpu->cd.sh.sgr = cpu->cd.sh.r[15];          cpu->cd.sh.sgr = cpu->cd.sh.r[15];
646    
647          if (intevt > 0) {          if (intevt > 0) {
648                  cpu->cd.sh.intevt = intevt;                  cpu->cd.sh.intevt = intevt;
649                  expevt = -1;                  expevt = -1;
650          } else          } else {
651                  cpu->cd.sh.expevt = expevt;                  cpu->cd.sh.expevt = expevt;
652            }
653    
654          sh_update_sr(cpu, cpu->cd.sh.sr | SH_SR_MD | SH_SR_RB | SH_SR_BL);          sh_update_sr(cpu, cpu->cd.sh.sr | SH_SR_MD | SH_SR_RB | SH_SR_BL);
655    
656          /*  Most exceptions set PC to VBR + 0x100.  */          /*  Most exceptions set PC to VBR + 0x100.  */
657          cpu->pc = vbr + 0x100;          cpu->pc = vbr + 0x100;
658    
659    
660          /*  Specific cases:  */          /*  Specific cases:  */
661          switch (expevt) {          switch (expevt) {
662    
# Line 653  void sh_exception(struct cpu *cpu, int e Line 683  void sh_exception(struct cpu *cpu, int e
683                  break;                  break;
684    
685          case EXPEVT_TRAPA:          case EXPEVT_TRAPA:
686                  /*  Note: The TRA register is already set by the                  /*
687                      implementation of the trapa instruction. See                   *  Note: The TRA register is already set by the implementation
688                      cpu_sh_instr.c.  */                   *  of the trapa instruction. See cpu_sh_instr.c for details.
689                     *  Here, spc is incremented, so that a return from the trap
690                     *  handler transfers control to the instruction _following_
691                     *  the trapa.
692                     */
693                  cpu->cd.sh.spc += sizeof(uint16_t);                  cpu->cd.sh.spc += sizeof(uint16_t);
694                  break;                  break;
695    

Legend:
Removed from v.37  
changed lines
  Added in v.38

  ViewVC Help
Powered by ViewVC 1.1.26