diff --git a/desmume/src/FIFO.cpp b/desmume/src/FIFO.cpp index 6daa87378..e0a5c6e9d 100644 --- a/desmume/src/FIFO.cpp +++ b/desmume/src/FIFO.cpp @@ -75,7 +75,7 @@ void IPC_FIFOsend(u8 proc, u32 val) T1WriteWord(MMU.MMU_MEM[proc][0x40], 0x184, cnt_l); T1WriteWord(MMU.MMU_MEM[proc_remote][0x40], 0x184, cnt_r); - setIF(proc_remote, ((cnt_r & 0x0400)<<8)); // IRQ18: recv not empty + NDS_Reschedule(); } u32 IPC_FIFOrecv(u8 proc) @@ -116,38 +116,56 @@ u32 IPC_FIFOrecv(u8 proc) T1WriteWord(MMU.MMU_MEM[proc][0x40], 0x184, cnt_l); T1WriteWord(MMU.MMU_MEM[proc_remote][0x40], 0x184, cnt_r); - setIF(proc_remote, ((cnt_r & 0x0004)<<15)); // IRQ17: send empty + NDS_Reschedule(); return (val); } +#define IPCFIFOCNT_SENDEMPTY 0x0001 +#define IPCFIFOCNT_SENDFULL 0x0002 +#define IPCFIFOCNT_SENDIRQEN 0x0004 +#define IPCFIFOCNT_SENDCLEAR 0x0008 +#define IPCFIFOCNT_RECVEMPTY 0x0100 +#define IPCFIFOCNT_RECVFULL 0x0200 +#define IPCFIFOCNT_RECVIRQEN 0x0400 +#define IPCFIFOCNT_FIFOERROR 0x4000 +#define IPCFIFOCNT_FIFOENABLE 0x8000 +#define IPCFIFOCNT_WRITEABLE (IPCFIFOCNT_SENDIRQEN | IPCFIFOCNT_RECVIRQEN | IPCFIFOCNT_FIFOENABLE) + void IPC_FIFOcnt(u8 proc, u16 val) { u16 cnt_l = T1ReadWord(MMU.MMU_MEM[proc][0x40], 0x184); u16 cnt_r = T1ReadWord(MMU.MMU_MEM[proc^1][0x40], 0x184); - //LOG("IPC%s FIFO context 0x%X (local 0x%04X, remote 0x%04X)\n", proc?"7":"9", val, cnt_l, cnt_r); - if (val & 0x4008) + if (val & IPCFIFOCNT_FIFOERROR) { - ipc_fifo[proc].head = 0; ipc_fifo[proc].tail = 0; ipc_fifo[proc].size = 0; - T1WriteWord(MMU.MMU_MEM[proc][0x40], 0x184, (cnt_l & 0x0301) | (val & 0x8404) | 1); - T1WriteWord(MMU.MMU_MEM[proc^1][0x40], 0x184, (cnt_r & 0x8407) | 0x100); - //MMU.reg_IF[proc^1] |= ((val & 0x0004) << 15); - setIF(proc^1, ((cnt_r & 0x0004)<<15)); - return; + //at least SPP uses this, maybe every retail game + cnt_l &= ~IPCFIFOCNT_FIFOERROR; } - T1WriteWord(MMU.MMU_MEM[proc][0x40], 0x184, val); + if (val & IPCFIFOCNT_SENDCLEAR) + { + ipc_fifo[proc].head = 0; ipc_fifo[proc].tail = 0; ipc_fifo[proc].size = 0; + + cnt_l |= IPCFIFOCNT_SENDEMPTY; + cnt_l &= ~IPCFIFOCNT_SENDFULL; + cnt_r |= IPCFIFOCNT_RECVEMPTY; + cnt_r &= ~IPCFIFOCNT_RECVFULL; + } + + cnt_l &= ~IPCFIFOCNT_WRITEABLE; + cnt_l |= val & IPCFIFOCNT_WRITEABLE; + + T1WriteWord(MMU.MMU_MEM[proc][0x40], 0x184, cnt_l); + T1WriteWord(MMU.MMU_MEM[proc^1][0x40], 0x184, cnt_r); + + NDS_Reschedule(); } // ========================================================= GFX FIFO GFX_PIPE gxPIPE; GFX_FIFO gxFIFO; - - - - void GFX_PIPEclear() { gxPIPE.head = 0; diff --git a/desmume/src/MMU.cpp b/desmume/src/MMU.cpp index 5edf19e8c..d933562e5 100644 --- a/desmume/src/MMU.cpp +++ b/desmume/src/MMU.cpp @@ -919,7 +919,7 @@ void MMU_Reset() memset(MMU.reg_IME, 0, sizeof(u32) * 2); memset(MMU.reg_IE, 0, sizeof(u32) * 2); - memset(MMU.reg_IF, 0, sizeof(u32) * 2); + memset(MMU.reg_IF_bits, 0, sizeof(u32) * 2); memset(MMU.dscard, 0, sizeof(nds_dscard) * 2); @@ -1220,17 +1220,96 @@ u32 MMU_readFromGC() //does some validation on the game's choice of IF value, correcting it if necessary static void validateIF_arm9() { - //according to gbatek, these flags are forced on until the condition is removed. - //no proof of this though... - if(MMU_new.gxstat.gxfifo_irq == 1) - if(gxFIFO.size <= 127) - MMU.reg_IF[ARMCPU_ARM9] |= (1<<21); - else MMU.reg_IF[ARMCPU_ARM9] &= ~(1<<21); - else if(MMU_new.gxstat.gxfifo_irq == 2) - if(gxFIFO.size == 0) - MMU.reg_IF[ARMCPU_ARM9] |= (1<<21); - else MMU.reg_IF[ARMCPU_ARM9] &= ~(1<<21); - else if(MMU_new.gxstat.gxfifo_irq == 0) MMU.reg_IF[ARMCPU_ARM9] &= ~(1<<21); +} + +template static void REG_IF_WriteByte(u32 addr, u8 val) +{ + //the following bits are generated from logic and should not be affected here + //Bit 17 IPC Send FIFO Empty + //Bit 18 IPC Recv FIFO Not Empty + //Bit 21 NDS9 only: Geometry Command FIFO + //arm9: IF &= ~0x00260000; + //arm7: IF &= ~0x00060000; + if(addr==2) + if(PROCNUM==ARMCPU_ARM9) + val &= ~0x26; + else + val &= ~0x06; + + MMU.reg_IF_bits[PROCNUM] &= (~(((u32)val)<<(addr<<3))); + NDS_Reschedule(); +} + +template static void REG_IF_WriteWord(u32 addr,u16 val) +{ + REG_IF_WriteByte(addr,val&0xFF); + REG_IF_WriteByte(addr+1,(val>>8)&0xFF); +} + +template static void REG_IF_WriteLong(u32 val) +{ + REG_IF_WriteByte(0,val&0xFF); + REG_IF_WriteByte(1,(val>>8)&0xFF); + REG_IF_WriteByte(2,(val>>16)&0xFF); + REG_IF_WriteByte(3,(val>>24)&0xFF); +} + +template +u32 MMU_struct::gen_IF() +{ + //TODO - analyze setIF behaviour in GXF_FIFO_handleEvents + + u32 IF = reg_IF_bits[PROCNUM]; + + if(PROCNUM==ARMCPU_ARM9) + { + //according to gbatek, these flags are forced on until the condition is removed. + //no proof of this though... + switch(MMU_new.gxstat.gxfifo_irq) + { + case 0: //never + break; + case 1: //less than half full + if(gxFIFO.size <= 127) + IF |= (1<<21); + break; + case 2: //empty + if(gxFIFO.size == 0) + IF |= (1<<21); + break; + case 3: //reserved/unknown + break; + } + } + + //generate IPC IF states from the ipc registers + u16 ipc = T1ReadWord(MMU.MMU_MEM[PROCNUM][0x40], 0x184); + if(ipc&0x8000) + { + if(ipc&0x0004) if(ipc&0x0001) + IF |= (1<<17); //IPC Send FIFO Empty + if(ipc&0x0400) if(!(ipc&0x0100)) + IF |= (1<<18); //IPC Recv FIFO Not Empty + } + + //4000184h - NDS9/NDS7 - IPCFIFOCNT - IPC Fifo Control Register (R/W) + // + // Bit Dir Expl. + // 0 R Send Fifo Empty Status (0=Not Empty, 1=Empty) + // 1 R Send Fifo Full Status (0=Not Full, 1=Full) + // 2 R/W Send Fifo Empty IRQ (0=Disable, 1=Enable) + // 3 W Send Fifo Clear (0=Nothing, 1=Flush Send Fifo) + // 4-7 - Not used + // 8 R Receive Fifo Empty (0=Not Empty, 1=Empty) + // 9 R Receive Fifo Full (0=Not Full, 1=Full) + // 10 R/W Receive Fifo Not Empty IRQ (0=Disable, 1=Enable) + // 11-13 - Not used + // 14 R/W Error, Read Empty/Send Full (0=No Error, 1=Error/Acknowledge) + // 15 R/W Enable Send/Receive Fifo (0=Disable, 1=Enable) + // 16-31 - Not used + + + return IF; } static u32 readreg_POWCNT1(const int size, const u32 adr) { @@ -2549,14 +2628,10 @@ void FASTCALL _MMU_ARM9_write16(u32 adr, u16 val) MMU.reg_IE[ARMCPU_ARM9] = (MMU.reg_IE[ARMCPU_ARM9]&0xFFFF) | (((u32)val)<<16); return; case REG_IF : - NDS_Reschedule(); - MMU.reg_IF[ARMCPU_ARM9] &= (~((u32)val)); - validateIF_arm9(); + REG_IF_WriteWord(0,val); return; case REG_IF + 2 : - NDS_Reschedule(); - MMU.reg_IF[ARMCPU_ARM9] &= (~(((u32)val)<<16)); - validateIF_arm9(); + REG_IF_WriteWord(2,val); return; case REG_IPCSYNC : @@ -2968,9 +3043,7 @@ void FASTCALL _MMU_ARM9_write32(u32 adr, u32 val) return; case REG_IF : - NDS_Reschedule(); - MMU.reg_IF[ARMCPU_ARM9] &= (~val); - validateIF_arm9(); + REG_IF_WriteLong(val); return; case REG_TM0CNTL: @@ -3101,6 +3174,11 @@ u8 FASTCALL _MMU_ARM9_read08(u32 adr) switch(adr) { + case REG_IF: return MMU.gen_IF(); + case REG_IF+1: return (MMU.gen_IF()>>8); + case REG_IF+2: return (MMU.gen_IF()>>16); + case REG_IF+3: return (MMU.gen_IF()>>24); + case REG_DISPA_DISPSTAT: break; case REG_DISPA_DISPSTAT+1: @@ -3210,10 +3288,8 @@ u16 FASTCALL _MMU_ARM9_read16(u32 adr) case REG_IE + 2 : return (u16)(MMU.reg_IE[ARMCPU_ARM9]>>16); - case REG_IF : - return (u16)MMU.reg_IF[ARMCPU_ARM9]; - case REG_IF + 2 : - return (u16)(MMU.reg_IF[ARMCPU_ARM9]>>16); + case REG_IF: return MMU.gen_IF(); + case REG_IF+2: return MMU.gen_IF()>>16; case REG_TM0CNTL : case REG_TM1CNTL : @@ -3342,8 +3418,9 @@ u32 FASTCALL _MMU_ARM9_read32(u32 adr) return MMU.reg_IME[ARMCPU_ARM9]; case REG_IE : return MMU.reg_IE[ARMCPU_ARM9]; - case REG_IF : - return MMU.reg_IF[ARMCPU_ARM9]; + + case REG_IF: return MMU.gen_IF(); + case REG_IPCFIFORECV : return IPC_FIFOrecv(ARMCPU_ARM9); case REG_TM0CNTL : @@ -3714,23 +3791,20 @@ void FASTCALL _MMU_ARM7_write16(u32 adr, u16 val) MMU.reg_IE[ARMCPU_ARM7] = (MMU.reg_IE[ARMCPU_ARM7]&0xFFFF) | (((u32)val)<<16); return; - case REG_IF : - NDS_Reschedule(); - //emu_halt(); - MMU.reg_IF[ARMCPU_ARM7] &= (~((u32)val)); + case REG_IF: + REG_IF_WriteWord(0,val); return; - case REG_IF + 2 : - NDS_Reschedule(); - //emu_halt(); - MMU.reg_IF[ARMCPU_ARM7] &= (~(((u32)val)<<16)); + + case REG_IF+2: + REG_IF_WriteWord(2,val); return; case REG_IPCSYNC : - MMU_IPCSync(ARMCPU_ARM7, val); + MMU_IPCSync(ARMCPU_ARM7, val); return; case REG_IPCFIFOCNT : - IPC_FIFOcnt(ARMCPU_ARM7, val); + IPC_FIFOcnt(ARMCPU_ARM7, val); return; case REG_TM0CNTL : case REG_TM1CNTL : @@ -3812,8 +3886,7 @@ void FASTCALL _MMU_ARM7_write32(u32 adr, u32 val) return; case REG_IF : - NDS_Reschedule(); - MMU.reg_IF[ARMCPU_ARM7] &= (~val); + REG_IF_WriteLong(val); return; case REG_TM0CNTL: @@ -3962,10 +4035,8 @@ u16 FASTCALL _MMU_ARM7_read16(u32 adr) case REG_IE + 2 : return (u16)(MMU.reg_IE[ARMCPU_ARM7]>>16); - case REG_IF : - return (u16)MMU.reg_IF[ARMCPU_ARM7]; - case REG_IF + 2 : - return (u16)(MMU.reg_IF[ARMCPU_ARM7]>>16); + case REG_IF: return MMU.gen_IF(); + case REG_IF+2: return MMU.gen_IF()>>16; case REG_TM0CNTL : case REG_TM1CNTL : @@ -4043,8 +4114,7 @@ u32 FASTCALL _MMU_ARM7_read32(u32 adr) return MMU.reg_IME[ARMCPU_ARM7]; case REG_IE : return MMU.reg_IE[ARMCPU_ARM7]; - case REG_IF : - return MMU.reg_IF[ARMCPU_ARM7]; + case REG_IF: return MMU.gen_IF(); case REG_IPCFIFORECV : return IPC_FIFOrecv(ARMCPU_ARM7); case REG_TM0CNTL : diff --git a/desmume/src/MMU.h b/desmume/src/MMU.h index 1f33e8d55..387e0a738 100644 --- a/desmume/src/MMU.h +++ b/desmume/src/MMU.h @@ -384,7 +384,11 @@ struct MMU_struct u32 reg_IME[2]; u32 reg_IE[2]; - u32 reg_IF[2]; + + //these are the user-controlled IF bits. some IF bits are generated as necessary from hardware conditions + u32 reg_IF_bits[2]; + + template u32 gen_IF(); BOOL divRunning; s64 divResult; diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 4c6fb5e3d..1a705cf68 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -46,7 +46,7 @@ //#define LOG_ARM9 //#define LOG_ARM7 -//bool dolog = true; +//bool dolog = false; //#define LOG_TO_FILE //#define LOG_TO_FILE_REGS @@ -1769,6 +1769,7 @@ bool nds_loadstate(EMUFILE* is, int size) FORCEINLINE void arm9log() { + //if(currFrameCounter>19) dolog=true; #ifdef LOG_ARM9 if(dolog) { @@ -1951,7 +1952,7 @@ void NDS_exec(s32 nb) //speculative code: if ANY irq happens, wake up the arm7. //I think the arm7 program analyzes the system and may decide not to wake up //if it is dissatisfied with the conditions - if((MMU.reg_IE[1] & MMU.reg_IF[1])) + if((MMU.reg_IE[1] & MMU.gen_IF<1>())) { nds.sleeping = FALSE; } @@ -2062,7 +2063,7 @@ void NDS_exec(s32 nb) void execHardware_interrupts() { - if((MMU.reg_IF[0]&MMU.reg_IE[0]) && (MMU.reg_IME[0])) + if((MMU.reg_IME[0]) && (MMU.gen_IF<0>()&MMU.reg_IE[0])) { //TODO - remove GDB specific code //#ifdef GDB_STUB @@ -2076,7 +2077,7 @@ void execHardware_interrupts() } } //TODO - remove GDB specific code - if((MMU.reg_IF[1]&MMU.reg_IE[1]) && (MMU.reg_IME[1])) + if((MMU.reg_IME[1]) && (MMU.gen_IF<1>()&MMU.reg_IE[1])) { //#ifdef GDB_STUB // if ( armcpu_flagIrq( &NDS_ARM7)) diff --git a/desmume/src/armcpu.h b/desmume/src/armcpu.h index 2a7d3df6a..c25301353 100644 --- a/desmume/src/armcpu.h +++ b/desmume/src/armcpu.h @@ -261,19 +261,23 @@ extern armcpu_t NDS_ARM9; static INLINE void setIF(int PROCNUM, u32 flag) { - MMU.reg_IF[PROCNUM] |= flag; + //don't set generated bits!!! + assert(!(flag&0x00260000)); + + MMU.reg_IF_bits[PROCNUM] |= flag; extern void NDS_Reschedule(); NDS_Reschedule(); + //TODO SEP - was this necessary??? //generate the interrupt if enabled - if ((MMU.reg_IE[PROCNUM] & (flag)) && MMU.reg_IME[PROCNUM]) - { - if(PROCNUM==0) - NDS_ARM9.waitIRQ = FALSE; - else - NDS_ARM7.waitIRQ = FALSE; - } + //if ((MMU.reg_IE[PROCNUM] & (flag)) && MMU.reg_IME[PROCNUM]) + //{ + // if(PROCNUM==0) + // NDS_ARM9.waitIRQ = FALSE; + // else + // NDS_ARM7.waitIRQ = FALSE; + //} } static INLINE void NDS_makeARM9Int(u32 num) diff --git a/desmume/src/saves.cpp b/desmume/src/saves.cpp index 59a7d8a8a..7eb1c7e37 100644 --- a/desmume/src/saves.cpp +++ b/desmume/src/saves.cpp @@ -202,7 +202,7 @@ SFORMAT SF_MMU[]={ { "MTRL", 2, 8, MMU.timerReload}, { "MIME", 4, 2, MMU.reg_IME}, { "MIE_", 4, 2, MMU.reg_IE}, - { "MIF_", 4, 2, MMU.reg_IF}, + { "MIF_", 4, 2, MMU.reg_IF_bits}, { "MGXC", 8, 1, &MMU.gfx3dCycles}, @@ -449,6 +449,10 @@ static bool mmu_loadstate(EMUFILE* is, int size) ok &= MMU_new.sqrt.loadstate(is,version); ok &= MMU_new.div.loadstate(is,version); + //to prevent old savestates from confusing IF bits, mask out ones which had been stored but should have been generated + MMU.reg_IF_bits[0] &= ~0x00260000; + MMU.reg_IF_bits[1] &= ~0x00060000; + return ok; }