+ HW_GPU_STATUS = SWAP32(0x14802000);
+ psxHwReadGpuSRptr = Config.hacks.gpu_busy_hack
+ ? psxHwReadGpuSRbusyHack : psxHwReadGpuSR;
+}
+
+void psxHwWriteIstat(u32 value)
+{
+ u32 stat = psxHu16(0x1070) & value;
+ psxHu16ref(0x1070) = SWAPu16(stat);
+
+ psxRegs.CP0.n.Cause &= ~0x400;
+ if (stat & psxHu16(0x1074))
+ psxRegs.CP0.n.Cause |= 0x400;
+}
+
+void psxHwWriteImask(u32 value)
+{
+ u32 stat = psxHu16(0x1070);
+ psxHu16ref(0x1074) = SWAPu16(value);
+ if (stat & value) {
+ //if ((psxRegs.CP0.n.SR & 0x401) == 0x401)
+ // log_unhandled("irq on unmask @%08x\n", psxRegs.pc);
+ new_dyna_set_event(PSXINT_NEWDRC_CHECK, 1);
+ }
+ psxRegs.CP0.n.Cause &= ~0x400;
+ if (stat & value)
+ psxRegs.CP0.n.Cause |= 0x400;
+}
+
+void psxHwWriteDmaIcr32(u32 value)
+{
+ u32 tmp = value & 0x00ff803f;
+ tmp |= (SWAPu32(HW_DMA_ICR) & ~value) & 0x7f000000;
+ if ((tmp & HW_DMA_ICR_GLOBAL_ENABLE && tmp & 0x7f000000)
+ || tmp & HW_DMA_ICR_BUS_ERROR) {
+ if (!(SWAPu32(HW_DMA_ICR) & HW_DMA_ICR_IRQ_SENT))
+ psxHu32ref(0x1070) |= SWAP32(8);
+ tmp |= HW_DMA_ICR_IRQ_SENT;
+ }
+ HW_DMA_ICR = SWAPu32(tmp);
+}
+
+void psxHwWriteGpuSR(u32 value)
+{
+ GPU_writeStatus(value);
+ gpuSyncPluginSR();
+}
+
+u32 psxHwReadGpuSR(void)
+{
+ u32 v;
+
+ // meh2, syncing for img bit, might want to avoid it..
+ gpuSyncPluginSR();
+ v = HW_GPU_STATUS;
+
+ // XXX: because of large timeslices can't use hSyncCount, using rough
+ // approximization instead. Perhaps better use hcounter code here or something.
+ if (hSyncCount < 240 && (HW_GPU_STATUS & PSXGPU_ILACE_BITS) != PSXGPU_ILACE_BITS)
+ v |= PSXGPU_LCF & (psxRegs.cycle << 20);
+ return v;
+}
+
+// a hack due to poor timing of gpu idle bit
+// to get rid of this, GPU draw times, DMAs, cpu timing has to fall within
+// certain timing window or else games like "ToHeart" softlock
+u32 psxHwReadGpuSRbusyHack(void)
+{
+ u32 v = psxHwReadGpuSR();
+ static u32 hack;
+ if (!(hack++ & 3))
+ v &= ~PSXGPU_nBUSY;
+ return v;