diff --git a/src/NDS.cpp b/src/NDS.cpp index 6d62376b..3f0a84bb 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1198,6 +1198,25 @@ void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 para Reschedule(evt->Timestamp); } +void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param) +{ + if (SchedListMask & (1<Timestamp = timestamp; + evt->Func = func; + evt->Param = param; + + SchedListMask |= (1<Timestamp); +} + void CancelEvent(u32 id) { SchedListMask &= ~(1<> 8; return Wifi::Read(addr) & 0xFF; } @@ -2460,6 +2480,7 @@ u16 ARM7Read16(u32 addr) case 0x04800000: if (addr < 0x04810000) { + if (!(PowerControl7 & (1<<1))) return 0; return Wifi::Read(addr); } break; @@ -2523,6 +2544,7 @@ u32 ARM7Read32(u32 addr) case 0x04800000: if (addr < 0x04810000) { + if (!(PowerControl7 & (1<<1))) return 0; return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16); } break; @@ -2663,6 +2685,7 @@ void ARM7Write16(u32 addr, u16 val) case 0x04800000: if (addr < 0x04810000) { + if (!(PowerControl7 & (1<<1))) return; Wifi::Write(addr, val); return; } @@ -2740,6 +2763,7 @@ void ARM7Write32(u32 addr, u32 val) case 0x04800000: if (addr < 0x04810000) { + if (!(PowerControl7 & (1<<1))) return; Wifi::Write(addr, val & 0xFFFF); Wifi::Write(addr+2, val >> 16); return; @@ -3751,6 +3775,7 @@ u8 ARM7IORead8(u32 addr) case 0x04000241: return WRAMCnt; case 0x04000300: return PostFlag7; + case 0x04000304: return PowerControl7; } if (addr >= 0x04000400 && addr < 0x04000520) @@ -3833,7 +3858,9 @@ u16 ARM7IORead16(u32 addr) case 0x040001C2: return SPI::ReadData(); case 0x04000204: return ExMemCnt[1]; - case 0x04000206: return WifiWaitCnt; + case 0x04000206: + if (!(PowerControl7 & (1<<1))) return 0; + return WifiWaitCnt; case 0x04000208: return IME[1]; case 0x04000210: return IE[1] & 0xFFFF; @@ -3915,6 +3942,7 @@ u32 ARM7IORead32(u32 addr) case 0x04000210: return IE[1]; case 0x04000214: return IF[1]; + case 0x04000304: return PowerControl7; case 0x04000308: return ARM7BIOSProt; case 0x04100000: @@ -4143,6 +4171,7 @@ void ARM7IOWrite16(u32 addr, u16 val) return; } case 0x04000206: + if (!(PowerControl7 & (1<<1))) return; SetWifiWaitCnt(val); return; @@ -4158,7 +4187,11 @@ void ARM7IOWrite16(u32 addr, u16 val) PostFlag7 = val & 0x01; return; - case 0x04000304: PowerControl7 = val; return; + case 0x04000304: + PowerControl7 = val & 0x0003; + SPU::SetPowerCnt(val & 0x0001); + Wifi::SetPowerCnt(val & 0x0002); + return; case 0x04000308: if (ARM7BIOSProt == 0) @@ -4280,7 +4313,11 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x04000210: IE[1] = val; UpdateIRQ(1); return; case 0x04000214: IF[1] &= ~val; UpdateIRQ(1); return; - case 0x04000304: PowerControl7 = val & 0xFFFF; return; + case 0x04000304: + PowerControl7 = val & 0x0003; + SPU::SetPowerCnt(val & 0x0001); + Wifi::SetPowerCnt(val & 0x0002); + return; case 0x04000308: if (ARM7BIOSProt == 0) diff --git a/src/NDS.h b/src/NDS.h index 80a1c6d7..9e3ed342 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -52,6 +52,8 @@ enum Event_DSi_CamTransfer, Event_DSi_DSP, + Event_WifiLegacy, + Event_MAX }; @@ -263,6 +265,7 @@ void SetLidClosed(bool closed); void MicInputFrame(s16* data, int samples); void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param); +void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param); void CancelEvent(u32 id); void debug(u32 p); diff --git a/src/SPU.cpp b/src/SPU.cpp index cba05586..9f245a20 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -184,6 +184,12 @@ void DoSavestate(Savestate* file) } +void SetPowerCnt(u32 val) +{ + // TODO +} + + void SetInterpolation(int type) { InterpType = type; diff --git a/src/SPU.h b/src/SPU.h index 1e20c117..1f28c2f8 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -31,6 +31,8 @@ void Stop(); void DoSavestate(Savestate* file); +void SetPowerCnt(u32 val); + // 0=none 1=linear 2=cosine 3=cubic void SetInterpolation(int type); diff --git a/src/Wifi.cpp b/src/Wifi.cpp index b98f5658..0d008d66 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -46,11 +46,20 @@ const u8 MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00}; const u8 MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10}; const u8 MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03}; +bool Enabled; +bool PowerOn; + u16 Random; // general, always-on microsecond counter u64 USTimestamp; +u64 SchedTimestamp; +SchedEvent SchedList[Event_MAX]; +u32 SchedListMask; + +const u32 kRXCheckPeriod = 512; + u64 USCounter; u64 USCompare; bool BlockBeaconIRQ14; @@ -137,21 +146,35 @@ const u64 kMaxRunahead = 4096; // 4 = switching from TX to RX // 5 = MP host data sent, waiting for replies (RFPINS=0x0084) // 6 = RX -// 7 = ?? +// 7 = switching from RX reply to TX ack // 8 = MP client sending reply, MP host sending ack (RFPINS=0x0046) // 9 = idle // wifi TODO: -// * power saving -// * RXSTAT, multiplay reply errors +// * RXSTAT // * TX errors (if applicable) +void UpdateTimers(); +void Reschedule(); +void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param); +void CancelEvent(u32 id); + +void StartTX_Beacon(); +void PeriodicRXCheck(u32 time); + + bool Init() { - MPInited = false; - LANInited = false; + //MPInited = false; + //LANInited = false; + + Platform::MP_Init(); + MPInited = true; + + Platform::LAN_Init(); + LANInited = true; WifiAP::Init(); @@ -173,6 +196,9 @@ void Reset() memset(RAM, 0, 0x2000); memset(IO, 0, 0x1000); + Enabled = false; + PowerOn = false; + Random = 1; memset(BBRegs, 0, 0x100); @@ -231,6 +257,10 @@ void Reset() USTimestamp = 0; RXTimestamp = 0; + SchedTimestamp = 0; + memset(SchedList, 0, sizeof(SchedList)); + SchedListMask = 0; + USCounter = 0; USCompare = 0; BlockBeaconIRQ14 = false; @@ -290,6 +320,178 @@ void DoSavestate(Savestate* file) } +void UpdatePowerOn(); + +void SetPowerCnt(u32 val) +{ + Enabled = val & (1<<1); + UpdatePowerOn(); +} + + +// wifi scheduling +// NOTE: this is technically inaccurate +// in the DS, the wifi system is driven by its own 22MHz clock, not by the system clock +// but in melonDS, we do need to drive it from the system scheduler, so it stays in sync +// with the rest of the system + +inline u64 USToSysCycles(u64 us) +{ + return (us * 33513982) / 1000000; +} + +inline u64 SysCyclesToUS(u64 sys) +{ + return ((sys + 16) * 1000000) / 33513982; +} + +void UpdateTimers() +{ + u64 oldts = SchedTimestamp; + SchedTimestamp = SysCyclesToUS(NDS::GetSysClockCycles(0)); + u64 diff = SchedTimestamp - oldts; + if (diff == 0) return; + + USTimestamp += diff; + + if (IOPORT(W_USCountCnt)) + { + USCounter += diff; + } +} + +void UpdatePowerOn() +{ + bool on = Enabled; + + if (NDS::ConsoleType == 1) + { + // TODO for DSi: + // * W_POWER_US doesn't work (atleast on DWM-W024) + // * other registers like GPIO_WIFI may also control wifi power/clock + // * turning wifi off via POWCNT2 while sending breaks further attempts at sending frames + } + else + { + on = on && ((IOPORT(W_PowerUS) & 0x1) == 0); + } + + if (on == PowerOn) + return; + + PowerOn = on; + if (on) + { + printf("WIFI: ON\n"); + + u64 oldts = SchedTimestamp; + SchedTimestamp = SysCyclesToUS(NDS::GetSysClockCycles(0)); + u64 diff = SchedTimestamp - oldts; + + for (int i = 0; i < Event_MAX; i++) + { + if (!(SchedListMask & (1<= oldts) + { + SchedList[i].Timestamp += diff; + } + } + + ScheduleEvent(Event_RXCheck, false, kRXCheckPeriod, PeriodicRXCheck, 0); + + Platform::MP_Begin(); + } + else + { + printf("WIFI: OFF\n"); + + CancelEvent(Event_RXCheck); + + UpdateTimers(); + NDS::CancelEvent(NDS::Event_Wifi); + + Platform::MP_End(); + } +} + +void RunEvents(u32 param) +{ + UpdateTimers(); +//printf("[%016llX] RUN EVENTS, MASK=%08X\n", SchedTimestamp, SchedListMask); + u32 mask = SchedListMask; + for (int i = 0; i < Event_MAX; i++) + { + if (!mask) break; + if (mask & 0x1) + { + if (SchedList[i].Timestamp <= SchedTimestamp) + { + SchedListMask &= ~(1<>= 1; + } +} + +void Reschedule() +{ + u64 next = UINT64_MAX; + + u32 mask = SchedListMask; + for (int i = 0; i < Event_MAX; i++) + { + if (!mask) break; + if (mask & 0x1) + { + if (SchedList[i].Timestamp < next) + next = SchedList[i].Timestamp; + } + + mask >>= 1; + } +//printf("[%016llX] RESCHEDULE: NEXT TIMESTAMP = %016llX\n", SchedTimestamp, next); + NDS::CancelEvent(NDS::Event_Wifi); + NDS::ScheduleEvent(NDS::Event_Wifi, USToSysCycles(next), RunEvents, 0); +} + +void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param) +{ + if (SchedListMask & (1<Timestamp += delay; + } + else + { + UpdateTimers(); + evt->Timestamp = SchedTimestamp + delay; + } + + evt->Func = func; + evt->Param = param; + + SchedListMask |= (1<Timestamp); + Reschedule(); +} + +void CancelEvent(u32 id) +{ + SchedListMask &= ~(1<> 10) + 1)) + { + s32 delay = (IOPORT(W_BeaconCount1) << 10) - IOPORT(W_PreBeacon); + CancelEvent(Event_IRQ15); + ScheduleEvent(Event_IRQ15, false, delay, SetIRQ15, 0); + } if (IOPORT(W_BeaconCount1) == 0) { SetIRQ14(1); @@ -1365,11 +1584,18 @@ void MSTimer() IOPORT(W_BeaconCount2)--; if (IOPORT(W_BeaconCount2) == 0) SetIRQ13(); } +printf("MSTimer %016llX %016llX\n", SchedTimestamp, USCounter); + ScheduleEvent(Event_MSTimer, true, 1024, MSTimer, 0); } void USTimer(u32 param) { - USTimestamp++; + /*USTimestamp++; + + u64 sys = NDS::GetSysClockCycles(0); + u64 ts = SysCyclesToUS(sys); + ts = USTimestamp - ts; + printf("diff=%016llX\n", ts); if (IsMPClient) { @@ -1386,10 +1612,10 @@ void USTimer(u32 param) CheckRX(2); } } - } + }*/ - WifiAP::USTimer(); + //WifiAP::USTimer(); bool switchOffPowerSaving = false; if (USUntilPowerOn < 0) @@ -1406,7 +1632,7 @@ void USTimer(u32 param) SetIRQ(11); } - if (IOPORT(W_USCountCnt)) + /*if (IOPORT(W_USCountCnt)) { USCounter++; u32 uspart = (USCounter & 0x3FF); @@ -1418,7 +1644,7 @@ void USTimer(u32 param) } if (!uspart) MSTimer(); - } + }*/ if (IOPORT(W_CmdCountCnt) & 0x0001) { @@ -1590,8 +1816,10 @@ void USTimer(u32 param) // TODO: make it more accurate, eventually // in the DS, the wifi system has its own 22MHz clock and doesn't use the system clock + // measurement: 16715us per frame + // calculation: 16715.113113088143330744761992174us per frame //NDS::ScheduleEvent(NDS::Event_Wifi, true, 33, USTimer, 0); - NDS::ScheduleEvent(NDS::Event_Wifi, true, 34, USTimer, 0); + NDS::ScheduleEvent(NDS::Event_WifiLegacy, true, 34, USTimer, 0); } @@ -1654,10 +1882,10 @@ u16 Read(u32 addr) case W_Preamble: return IOPORT(W_Preamble) & 0x0003; - case W_USCount0: return (u16)(USCounter & 0xFFFF); - case W_USCount1: return (u16)((USCounter >> 16) & 0xFFFF); - case W_USCount2: return (u16)((USCounter >> 32) & 0xFFFF); - case W_USCount3: return (u16)(USCounter >> 48); + case W_USCount0: UpdateTimers(); return (u16)(USCounter & 0xFFFF); + case W_USCount1: UpdateTimers(); return (u16)((USCounter >> 16) & 0xFFFF); + case W_USCount2: UpdateTimers(); return (u16)((USCounter >> 32) & 0xFFFF); + case W_USCount3: UpdateTimers(); return (u16)(USCounter >> 48); case W_USCompare0: return (u16)(USCompare & 0xFFFF); case W_USCompare1: return (u16)((USCompare >> 16) & 0xFFFF); @@ -1894,31 +2122,9 @@ void Write(u32 addr, u16 val) } break; case W_PowerUS: - // schedule timer event when the clock is enabled - // TODO: check whether this resets USCOUNT (and also which other events can reset it) - if ((IOPORT(W_PowerUS) & 0x0001) && !(val & 0x0001)) - { - printf("WIFI ON\n"); - NDS::ScheduleEvent(NDS::Event_Wifi, false, 33, USTimer, 0); - if (!MPInited) - { - Platform::MP_Init(); - MPInited = true; - } - if (!LANInited) - { - Platform::LAN_Init(); - LANInited = true; - } - Platform::MP_Begin(); - } - else if (!(IOPORT(W_PowerUS) & 0x0001) && (val & 0x0001)) - { - printf("WIFI OFF\n"); - NDS::CancelEvent(NDS::Event_Wifi); - Platform::MP_End(); - } - break; + IOPORT(W_PowerUS) = val & 0x0003; + UpdatePowerOn(); + return; case W_PowerUnk: val &= 0x0003; //printf("writing power unk %x\n", val); @@ -1928,16 +2134,29 @@ void Write(u32 addr, u16 val) val = 3; break; - case W_USCountCnt: val &= 0x0001; break; + case W_USCountCnt: + val &= 0x0001; + if (val && (!IOPORT(W_USCountCnt))) + {printf("[%016llX][%016llX] ON -> NEXT=%016llX\n", SchedTimestamp, USCounter, USCounter + 1024 - (USCounter & 0x3FF)); + ScheduleEvent(Event_MSTimer, false, 1024 - (USCounter & 0x3FF), MSTimer, 0); + IOPORT(W_USCountCnt) = val; + } + else if ((!val) && IOPORT(W_USCountCnt)) + { + UpdateTimers();printf("[%016llX][%016llX] OFF\n", SchedTimestamp, USCounter); + IOPORT(W_USCountCnt) = val; + CancelEvent(Event_MSTimer); + } + return; case W_USCompareCnt: if (val & 0x0002) SetIRQ14(2); val &= 0x0001; break; - case W_USCount0: USCounter = (USCounter & 0xFFFFFFFFFFFF0000) | (u64)val; return; - case W_USCount1: USCounter = (USCounter & 0xFFFFFFFF0000FFFF) | ((u64)val << 16); return; - case W_USCount2: USCounter = (USCounter & 0xFFFF0000FFFFFFFF) | ((u64)val << 32); return; - case W_USCount3: USCounter = (USCounter & 0x0000FFFFFFFFFFFF) | ((u64)val << 48); return; + case W_USCount0: UpdateTimers(); USCounter = (USCounter & 0xFFFFFFFFFFFF0000) | (u64)val; return; + case W_USCount1: UpdateTimers(); USCounter = (USCounter & 0xFFFFFFFF0000FFFF) | ((u64)val << 16); return; + case W_USCount2: UpdateTimers(); USCounter = (USCounter & 0xFFFF0000FFFFFFFF) | ((u64)val << 32); return; + case W_USCount3: UpdateTimers(); USCounter = (USCounter & 0x0000FFFFFFFFFFFF) | ((u64)val << 48); return; case W_USCompare0: USCompare = (USCompare & 0xFFFFFFFFFFFF0000) | (u64)(val & 0xFC00); diff --git a/src/Wifi.h b/src/Wifi.h index 831362ac..dee525cb 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -151,6 +151,27 @@ enum W_RXTXAddr = 0x268, }; +enum +{ + Event_RXCheck = 0, + Event_IRQ15, + Event_MSTimer, + Event_PowerOn, + Event_MPClientSync, + Event_TRX, + Event_RF, + Event_BB, + + Event_MAX +}; + +struct SchedEvent +{ + void (*Func)(u32 param); + u64 Timestamp; + u32 Param; +}; + extern bool MPInited; @@ -160,7 +181,7 @@ void DeInit(); void Reset(); void DoSavestate(Savestate* file); -void StartTX_Beacon(); +void SetPowerCnt(u32 val); void USTimer(u32 param); diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index e083c74d..eaa221fa 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -115,6 +115,7 @@ bool MACIsBroadcast(u8* a) } +// REMOVE ME!! void USTimer() { USCounter++;