32x, tentative kludge for blackthorne
authorkub <derkub@gmail.com>
Mon, 21 Nov 2022 21:08:39 +0000 (21:08 +0000)
committerkub <derkub@gmail.com>
Mon, 21 Nov 2022 21:15:51 +0000 (21:15 +0000)
cpu/sh2/compiler.c
cpu/sh2/sh2.h
pico/32x/32x.c
pico/32x/memory.c
pico/32x/pwm.c
pico/32x/sh2soc.c

index 360fcd0..0766a6e 100644 (file)
@@ -3836,8 +3836,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
           u = FETCH32(opd->imm);
         else
           u = (s16)FETCH_OP(opd->imm);
-        // tweak for Blackthorne: avoid stack overwriting
-        if (GET_Rn() == SHR_SP && u == 0x0603f800) u = 0x0603f900;
         gconst_new(GET_Rn(), u);
       }
       else
index bb8debe..db729bb 100644 (file)
@@ -52,6 +52,7 @@ typedef struct SH2_
 #define SH2_STATE_RPOLL (1 << 4)       // polling address in SDRAM\r
 #define SH2_TIMER_RUN   (1 << 6)       // SOC WDT timer is running\r
 #define SH2_IN_DRC      (1 << 7)       // DRC in use\r
+#define SH2_PWM_IRQ     (1 << 8)       // entering IRQ\r
        unsigned int    state;\r
        uint32_t        poll_addr;\r
        int             poll_cycles;\r
index f765436..6b54ea4 100644 (file)
@@ -19,6 +19,11 @@ static int REGPARM(2) sh2_irq_cb(SH2 *sh2, int level)
   if (sh2->pending_irl > sh2->pending_int_irq) {
     elprintf_sh2(sh2, EL_32X, "ack/irl %d @ %08x",
       level, sh2_pc(sh2));
+    // tweak for Blackthorne, part 1: master SH2 overwrites stack of slave SH2 being
+    // in pwm interrupt. On real hardware, nothing happens since slave fetches the
+    // values it has written from its cache, but picodrive doesn't emulate caching.
+    if ((1<<sh2->pending_irl/2) == P32XI_PWM && sh2->is_slave)
+      sh2->state |= SH2_PWM_IRQ;
     return 64 + sh2->pending_irl / 2;
   } else {
     elprintf_sh2(sh2, EL_32X, "ack/int %d/%d @ %08x",
@@ -55,15 +60,13 @@ void p32x_update_irls(SH2 *active_sh2, unsigned int m68k_cycles)
   mrun = sh2_irl_irq(&msh2, mlvl, msh2.state & SH2_STATE_RUN);
   if (mrun) {
     p32x_sh2_poll_event(&msh2, SH2_IDLE_STATES & ~SH2_STATE_SLEEP, m68k_cycles);
-    if (msh2.state & SH2_STATE_RUN)
-      sh2_end_run(&msh2, 0);
+    p32x_sync_other_sh2(&msh2, m68k_cycles);
   }
 
   srun = sh2_irl_irq(&ssh2, slvl, ssh2.state & SH2_STATE_RUN);
   if (srun) {
     p32x_sh2_poll_event(&ssh2, SH2_IDLE_STATES & ~SH2_STATE_SLEEP, m68k_cycles);
-    if (ssh2.state & SH2_STATE_RUN)
-      sh2_end_run(&ssh2, 0);
+    p32x_sync_other_sh2(&ssh2, m68k_cycles);
   }
 
   elprintf(EL_32X, "update_irls: m %d/%d, s %d/%d", mlvl, mrun, slvl, srun);
@@ -386,10 +389,18 @@ static void p32x_run_events(unsigned int until)
 static void run_sh2(SH2 *sh2, unsigned int m68k_cycles)
 {
   unsigned int cycles, done;
+  unsigned int cpwm = 1400, m68k_cpwm = C_SH2_TO_M68K(sh2, cpwm);
 
   pevt_log_sh2_o(sh2, EVT_RUN_START);
   sh2->state |= SH2_STATE_RUN;
   cycles = C_M68K_TO_SH2(sh2, m68k_cycles);
+  // tweak for Blackthorne, part 2: try to ensure that the PWM irq completes before
+  // switching to the other SH2. This irq can run up to about 1400 cycles.
+  // NB, may delay hint/vint interrupts on the same SH2, might cause sync problems.
+  if (unlikely(sh2->state & SH2_PWM_IRQ) && cycles < cpwm &&
+      CYCLES_GT(p32x_event_times[P32X_EVENT_PWM], sh2->m68krcycles_done+m68k_cpwm))
+    cycles = 1400;
+  sh2->state &= ~SH2_PWM_IRQ;
   elprintf_sh2(sh2, EL_32X, "+run %u %d @%08x",
     sh2->m68krcycles_done, cycles, sh2->pc);
 
index 7a61e89..c1c35e2 100644 (file)
@@ -816,7 +816,7 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2)
       Pico32x.sh2_regs[0] &= ~0x80;
       Pico32x.sh2_regs[0] |= d & 0x80;
 
-      if ((d ^ old) & 1)
+      if ((old ^ d) & 1)
         p32x_pwm_schedule_sh2(sh2);
       if ((old ^ d) & 2)
         p32x_update_cmd_irq(sh2, 0);
index ed8d761..1b10103 100644 (file)
@@ -41,13 +41,13 @@ void p32x_pwm_ctl_changed(void)
   pwm.irq_reload = pwm.irq_timer;
   pwm.irq_state = pwm_irq_opt ? PWM_IRQ_STOPPED: PWM_IRQ_LOCKED;
 
-  if (Pico32x.pwm_irq_cnt == 0)
+  if (Pico32x.pwm_irq_cnt <= 0)
     Pico32x.pwm_irq_cnt = pwm.irq_reload;
 }
 
 static void do_pwm_irq(SH2 *sh2, unsigned int m68k_cycles)
 {
-  p32x_trigger_irq(sh2, m68k_cycles, P32XI_PWM);
+  p32x_trigger_irq(NULL, m68k_cycles, P32XI_PWM);
 
   if (Pico32x.regs[0x30 / 2] & P32XP_RTP) {
     p32x_event_schedule(m68k_cycles, P32X_EVENT_PWM, pwm.cycles / 3 + 1);
@@ -110,7 +110,7 @@ static void consume_fifo_do(SH2 *sh2, unsigned int m68k_cycles,
     mem->pwm[pwm.ptr * 2 + 1] = pwm.current[1];
     pwm.ptr = (pwm.ptr + 1) & (PWM_BUFF_LEN - 1);
 
-    if (--Pico32x.pwm_irq_cnt == 0) {
+    if (--Pico32x.pwm_irq_cnt <= 0) {
       Pico32x.pwm_irq_cnt = pwm.irq_reload;
       do_pwm_irq(sh2, m68k_cycles);
     } else if (Pico32x.pwm_p[1] == 0 && pwm.irq_state >= PWM_IRQ_LOW) {
index f8bb4e7..f652472 100644 (file)
@@ -270,6 +270,7 @@ u32 REGPARM(2) sh2_peripheral_read8(u32 a, SH2 *sh2)
   u8 *r = (void *)sh2->peri_regs;
   u32 d;
 
+  DRC_SAVE_SR(sh2);
   a &= 0x1ff;
   d = PREG8(r, a);
 
@@ -277,10 +278,9 @@ u32 REGPARM(2) sh2_peripheral_read8(u32 a, SH2 *sh2)
     a | ~0x1ff, d, sh2_pc(sh2));
   if ((a & 0x1c0) == 0x140) {
     // abused as comm area
-    DRC_SAVE_SR(sh2);
     p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 3);
-    DRC_RESTORE_SR(sh2);
   }
+  DRC_RESTORE_SR(sh2);
   return d;
 }
 
@@ -289,6 +289,7 @@ u32 REGPARM(2) sh2_peripheral_read16(u32 a, SH2 *sh2)
   u16 *r = (void *)sh2->peri_regs;
   u32 d;
 
+  DRC_SAVE_SR(sh2);
   a &= 0x1fe;
   d = r[MEM_BE2(a / 2)];
 
@@ -296,10 +297,9 @@ u32 REGPARM(2) sh2_peripheral_read16(u32 a, SH2 *sh2)
     a | ~0x1ff, d, sh2_pc(sh2));
   if ((a & 0x1c0) == 0x140) {
     // abused as comm area
-    DRC_SAVE_SR(sh2);
     p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 3);
-    DRC_RESTORE_SR(sh2);
   }
+  DRC_RESTORE_SR(sh2);
   return d;
 }
 
@@ -307,6 +307,7 @@ u32 REGPARM(2) sh2_peripheral_read32(u32 a, SH2 *sh2)
 {
   u32 d;
 
+  DRC_SAVE_SR(sh2);
   a &= 0x1fc;
   d = sh2->peri_regs[a / 4];
 
@@ -317,10 +318,9 @@ u32 REGPARM(2) sh2_peripheral_read32(u32 a, SH2 *sh2)
     sh2->poll_cnt = 0;
   else if ((a & 0x1c0) == 0x140) {
     // abused as comm area
-    DRC_SAVE_SR(sh2);
     p32x_sh2_poll_detect(a, sh2, SH2_STATE_CPOLL, 3);
-    DRC_RESTORE_SR(sh2);
   }
+  DRC_RESTORE_SR(sh2);
   return d;
 }
 
@@ -364,18 +364,18 @@ void REGPARM(3) sh2_peripheral_write8(u32 a, u32 d, SH2 *sh2)
   u8 *r = (void *)sh2->peri_regs;
   u8 old;
 
+  DRC_SAVE_SR(sh2);
   elprintf_sh2(sh2, EL_32XP, "peri w8  [%08x]       %02x @%06x",
     a, d, sh2_pc(sh2));
 
   a &= 0x1ff;
   old = PREG8(r, a);
+  PREG8(r, a) = d;
 
   switch (a) {
   case 0x002: // SCR - serial control
-    if (!(PREG8(r, a) & 0x20) && (d & 0x20)) { // TE being set
-      PREG8(r, a) = d;
+    if (!(old & 0x20) && (d & 0x20)) // TE being set
       sci_trigger(sh2, r);
-    }
     break;
   case 0x003: // TDR - transmit data
     break;
@@ -383,27 +383,31 @@ void REGPARM(3) sh2_peripheral_write8(u32 a, u32 d, SH2 *sh2)
     d = (old & (d | 0x06)) | (d & 1);
     PREG8(r, a) = d;
     sci_trigger(sh2, r);
-    return;
+    break;
   case 0x005: // RDR - receive data
     break;
   case 0x010: // TIER
     if (d & 0x8e)
       elprintf(EL_32XP|EL_ANOMALY, "TIER: %02x", d);
     d = (d & 0x8e) | 1;
+    PREG8(r, a) = d;
     break;
   case 0x017: // TOCR
     d |= 0xe0;
+    PREG8(r, a) = d;
     break;
+  default:
+    if ((a & 0x1c0) == 0x140)
+      p32x_sh2_poll_event(sh2, SH2_STATE_CPOLL, SekCyclesDone());
   }
-  PREG8(r, a) = d;
-
-  if ((a & 0x1c0) == 0x140)
-    p32x_sh2_poll_event(sh2, SH2_STATE_CPOLL, SekCyclesDone());
+  DRC_RESTORE_SR(sh2);
 }
 
 void REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, SH2 *sh2)
 {
   u16 *r = (void *)sh2->peri_regs;
+
+  DRC_SAVE_SR(sh2);
   elprintf_sh2(sh2, EL_32XP, "peri w16 [%08x]     %04x @%06x",
     a, d, sh2_pc(sh2));
 
@@ -417,12 +421,12 @@ void REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, SH2 *sh2)
     }
     if ((d & 0xff00) == 0x5a00) // WTCNT
       PREG8(r, 0x81) = d;
-    return;
+  } else {
+    r[MEM_BE2(a / 2)] = d;
+    if ((a & 0x1c0) == 0x140)
+      p32x_sh2_poll_event(sh2, SH2_STATE_CPOLL, SekCyclesDone());
   }
-
-  r[MEM_BE2(a / 2)] = d;
-  if ((a & 0x1c0) == 0x140)
-    p32x_sh2_poll_event(sh2, SH2_STATE_CPOLL, SekCyclesDone());
+  DRC_RESTORE_SR(sh2);
 }
 
 void REGPARM(3) sh2_peripheral_write32(u32 a, u32 d, SH2 *sh2)
@@ -431,6 +435,7 @@ void REGPARM(3) sh2_peripheral_write32(u32 a, u32 d, SH2 *sh2)
   u32 old;
   struct dmac *dmac;
 
+  DRC_SAVE_SR(sh2);
   elprintf_sh2(sh2, EL_32XP, "peri w32 [%08x] %08x @%06x",
     a, d, sh2_pc(sh2));
 
@@ -480,17 +485,17 @@ void REGPARM(3) sh2_peripheral_write32(u32 a, u32 d, SH2 *sh2)
       if (!(dmac->dmaor & DMA_DME))
         return;
 
-      DRC_SAVE_SR(sh2);
       if ((dmac->chan[0].chcr & (DMA_TE|DMA_DE)) == DMA_DE)
         dmac_trigger(sh2, &dmac->chan[0]);
       if ((dmac->chan[1].chcr & (DMA_TE|DMA_DE)) == DMA_DE)
         dmac_trigger(sh2, &dmac->chan[1]);
-      DRC_RESTORE_SR(sh2);
       break;
+    default:
+      if ((a & 0x1c0) == 0x140)
+        p32x_sh2_poll_event(sh2, SH2_STATE_CPOLL, SekCyclesDone());
   }
 
-  if ((a & 0x1c0) == 0x140)
-    p32x_sh2_poll_event(sh2, SH2_STATE_CPOLL, SekCyclesDone());
+  DRC_RESTORE_SR(sh2);
 }
 
 /* 32X specific */