Add structs for kernel/shared pages + related refactor, see details:
- Added osKernelConfig_s/osSharedConfig_s structs - Added OS_KernelConfig/OS_SharedConfig macros for accessing the pages - Replaced 0x1FF8nnnn hardcoded addresses with struct accesses - Added osIsHeadsetConnected function - Refactored and rewritten RTC time support with the correct algorithm (many thanks to @TuxSH for reverse engineering the PTM sysmodule): - Fully added and documented the osTimeRef_s structure - This replaces the previous internal datetime_t struct type - Added osGetTimeRef function: - This replaces the previous internal getSysTime function - Added missing barrier to the lock free reading algorithm - osGetTime rewritten to implement the drift-correction algorithm - **This introduces a dependency to __aeabi_ldivmod** - __libctru_gtod rewritten to reuse osGetTime
This commit is contained in:
parent
53cbfc231f
commit
4e387679f7
@ -52,10 +52,73 @@
|
||||
#define OS_DSPRAM_PADDR 0x1FF00000 ///< DSP memory physical address
|
||||
#define OS_DSPRAM_SIZE 0x80000 ///< DSP memory size (512 KiB)
|
||||
|
||||
#define OS_KERNELCFG_VADDR 0x1FF80000 ///< Kernel configuration page virtual address
|
||||
#define OS_SHAREDCFG_VADDR 0x1FF81000 ///< Shared system configuration page virtual address
|
||||
|
||||
#define OS_FCRAM_VADDR 0x30000000 ///< Linear FCRAM mapping virtual address
|
||||
#define OS_FCRAM_PADDR 0x20000000 ///< Linear FCRAM mapping physical address
|
||||
#define OS_FCRAM_SIZE 0x10000000 ///< Linear FCRAM mapping size (256 MiB)
|
||||
|
||||
#define OS_KernelConfig ((osKernelConfig_s const*)OS_KERNELCFG_VADDR) ///< Pointer to the kernel configuration page (see \ref osKernelConfig_s)
|
||||
#define OS_SharedConfig ((osSharedConfig_s*)OS_SHAREDCFG_VADDR) ///< Pointer to the shared system configuration page (see \ref osSharedConfig_s)
|
||||
|
||||
/// Kernel configuration page (read-only).
|
||||
typedef struct
|
||||
{
|
||||
u32 kernel_ver;
|
||||
u32 update_flag;
|
||||
u64 ns_tid;
|
||||
u32 kernel_syscore_ver;
|
||||
u8 env_info;
|
||||
u8 unit_info;
|
||||
u8 boot_env;
|
||||
u8 unk_0x17;
|
||||
u32 kernel_ctrsdk_ver;
|
||||
u32 unk_0x1c;
|
||||
u32 firmlaunch_flags;
|
||||
u8 unk_0x24[0xc];
|
||||
u32 app_memtype;
|
||||
u8 unk_0x34[0x10];
|
||||
u32 memregion_sz[3];
|
||||
u8 unk_0x4c[0x14];
|
||||
u32 firm_ver;
|
||||
u32 firm_syscore_ver;
|
||||
u32 firm_ctrsdk_ver;
|
||||
} osKernelConfig_s;
|
||||
|
||||
/// Time reference information struct (filled in by PTM).
|
||||
typedef struct
|
||||
{
|
||||
u64 value_ms; ///< Milliseconds elapsed since January 1900 when this structure was last updated
|
||||
u64 value_tick; ///< System ticks elapsed since boot when this structure was last updated
|
||||
s64 sysclock_hz;///< System clock frequency in Hz adjusted using RTC measurements (usually around \ref SYSCLOCK_ARM11)
|
||||
s64 drift_ms; ///< Measured time drift of the system clock (according to the RTC) in milliseconds since the last update
|
||||
} osTimeRef_s;
|
||||
|
||||
/// Shared system configuration page structure (read-only or read-write depending on exheader).
|
||||
typedef struct
|
||||
{
|
||||
vu32 timeref_cnt;
|
||||
u8 running_hw;
|
||||
u8 mcu_hwinfo;
|
||||
u8 unk_0x06[0x1A];
|
||||
volatile osTimeRef_s timeref[2];
|
||||
u8 wifi_macaddr[6];
|
||||
vu8 wifi_strength;
|
||||
vu8 network_state;
|
||||
u8 unk_0x68[0x18];
|
||||
volatile float slider_3d;
|
||||
vu8 led_3d;
|
||||
vu8 led_battery;
|
||||
vu8 unk_flag;
|
||||
u8 unk_0x87;
|
||||
u8 unk_0x88[0x18];
|
||||
vu64 menu_tid;
|
||||
vu64 cur_menu_tid;
|
||||
u8 unk_0xB0[0x10];
|
||||
vu8 headset_connected;
|
||||
} osSharedConfig_s;
|
||||
|
||||
/// Tick counter.
|
||||
typedef struct
|
||||
{
|
||||
@ -106,7 +169,7 @@ const char* osStrError(Result error);
|
||||
*/
|
||||
static inline u32 osGetFirmVersion(void)
|
||||
{
|
||||
return (*(vu32*)0x1FF80060) & ~0xFF;
|
||||
return OS_KernelConfig->firm_ver &~ 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,19 +184,19 @@ static inline u32 osGetFirmVersion(void)
|
||||
*/
|
||||
static inline u32 osGetKernelVersion(void)
|
||||
{
|
||||
return (*(vu32*)0x1FF80000) & ~0xFF;
|
||||
return OS_KernelConfig->kernel_ver &~ 0xFF;
|
||||
}
|
||||
|
||||
/// Gets the system's "core version" (2 on NATIVE_FIRM, 3 on SAFE_FIRM, etc.)
|
||||
static inline u32 osGetSystemCoreVersion(void)
|
||||
{
|
||||
return *(vu32*)0x1FF80010;
|
||||
return OS_KernelConfig->kernel_syscore_ver;
|
||||
}
|
||||
|
||||
/// Gets the system's memory layout ID (0-5 on Old 3DS, 6-8 on New 3DS)
|
||||
static inline u32 osGetApplicationMemType(void)
|
||||
{
|
||||
return *(vu32*)0x1FF80030;
|
||||
return OS_KernelConfig->app_memtype;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +209,7 @@ static inline u32 osGetMemRegionSize(MemRegion region)
|
||||
if(region == MEMREGION_ALL) {
|
||||
return osGetMemRegionSize(MEMREGION_APPLICATION) + osGetMemRegionSize(MEMREGION_SYSTEM) + osGetMemRegionSize(MEMREGION_BASE);
|
||||
} else {
|
||||
return *(vu32*) (0x1FF80040 + (region - 1) * 0x4);
|
||||
return OS_KernelConfig->memregion_sz[region-1];
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +235,12 @@ static inline u32 osGetMemRegionFree(MemRegion region)
|
||||
return osGetMemRegionSize(region) - osGetMemRegionUsed(region);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the latest reference timepoint published by PTM.
|
||||
* @return Structure (see \ref osTimeRef_s).
|
||||
*/
|
||||
osTimeRef_s osGetTimeRef(void);
|
||||
|
||||
/**
|
||||
* @brief Gets the current time.
|
||||
* @return The number of milliseconds since 1st Jan 1900 00:00.
|
||||
@ -222,7 +291,7 @@ double osTickCounterRead(const TickCounter* cnt);
|
||||
*/
|
||||
static inline u8 osGetWifiStrength(void)
|
||||
{
|
||||
return *(vu8*)0x1FF81066;
|
||||
return OS_SharedConfig->wifi_strength;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,7 +300,16 @@ static inline u8 osGetWifiStrength(void)
|
||||
*/
|
||||
static inline float osGet3DSliderState(void)
|
||||
{
|
||||
return *(volatile float*)0x1FF81080;
|
||||
return OS_SharedConfig->slider_3d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a headset is connected.
|
||||
* @return true or false.
|
||||
*/
|
||||
static inline bool osIsHeadsetConnected(void)
|
||||
{
|
||||
return OS_SharedConfig->headset_connected != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,7 +142,7 @@ static void ndspUpdateMaster(void)
|
||||
u32 flags = m->flags, mflags = ndspMaster.flags;
|
||||
int i;
|
||||
|
||||
m->headsetConnected = *(vu8*)0x1FF810C0;
|
||||
m->headsetConnected = osIsHeadsetConnected();
|
||||
flags |= 0x10000000;
|
||||
|
||||
if (mflags & MFLAG_MASTERVOL)
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/os.h>
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/synchronization.h>
|
||||
#include <3ds/services/ptmsysm.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
@ -13,16 +14,6 @@ static inline double u64_to_double(u64 value) {
|
||||
return (((double)(u32)(value >> 32))*0x100000000ULL+(u32)value);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u64 date_time;
|
||||
u64 update_tick;
|
||||
//...
|
||||
} datetime_t;
|
||||
|
||||
#define __datetime_selector (*(vu32*)0x1FF81000)
|
||||
#define __datetime0 (*(volatile datetime_t*)0x1FF81020)
|
||||
#define __datetime1 (*(volatile datetime_t*)0x1FF81040)
|
||||
|
||||
__attribute__((weak)) bool __ctru_speedup = false;
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
@ -56,60 +47,66 @@ void* osConvertOldLINEARMemToNew(const void* addr) {
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
static datetime_t getSysTime(void) {
|
||||
osTimeRef_s osGetTimeRef(void) {
|
||||
//---------------------------------------------------------------------------------
|
||||
u32 s1, s2 = __datetime_selector & 1;
|
||||
datetime_t dt;
|
||||
osTimeRef_s tr;
|
||||
u32 next = OS_SharedConfig->timeref_cnt;
|
||||
u32 cur;
|
||||
|
||||
do {
|
||||
s1 = s2;
|
||||
if(!s1)
|
||||
dt = __datetime0;
|
||||
else
|
||||
dt = __datetime1;
|
||||
s2 = __datetime_selector & 1;
|
||||
} while(s2 != s1);
|
||||
cur = next;
|
||||
tr = OS_SharedConfig->timeref[cur&1];
|
||||
__dmb();
|
||||
next = OS_SharedConfig->timeref_cnt;
|
||||
} while (cur != next);
|
||||
|
||||
return dt;
|
||||
return tr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
u64 osGetTime(void) {
|
||||
//---------------------------------------------------------------------------------
|
||||
// Read the latest time reference point published by PTM
|
||||
osTimeRef_s tr = osGetTimeRef();
|
||||
|
||||
// Calculate the time elapsed since the reference point using the system clock
|
||||
s64 elapsed_tick = svcGetSystemTick() - tr.value_tick;
|
||||
s64 elapsed_ms = elapsed_tick * 1000 / tr.sysclock_hz;
|
||||
|
||||
// Apply the drift adjustment if present:
|
||||
// Every time PTM publishes a new reference point it measures by how long the
|
||||
// system clock has drifted with respect to the RTC. It also recalculates the
|
||||
// system clock frequency using RTC data in order to minimize future drift.
|
||||
// The idea behind the following logic is to reapply the inaccuracy to the
|
||||
// calculated timestamp, but gradually reducing it to zero over the course of
|
||||
// an hour. This ensures the monotonic growth of the returned time value.
|
||||
const s64 hour_in_ms = 60*60*1000; // milliseconds in an hour
|
||||
if (tr.drift_ms != 0 && elapsed_ms < hour_in_ms)
|
||||
elapsed_ms += tr.drift_ms * (hour_in_ms - elapsed_ms) / hour_in_ms;
|
||||
|
||||
// Return the final timestamp
|
||||
return tr.value_ms + elapsed_ms;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
int __libctru_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz) {
|
||||
//---------------------------------------------------------------------------------
|
||||
if (tp != NULL) {
|
||||
// Retrieve current time, adjusting epoch from 1900 to 1970
|
||||
s64 now = osGetTime() - 2208988800000ULL;
|
||||
|
||||
datetime_t dt = getSysTime();
|
||||
|
||||
u64 delta = svcGetSystemTick() - dt.update_tick;
|
||||
|
||||
u32 offset = (u32)(u64_to_double(delta)/CPU_TICKS_PER_USEC);
|
||||
|
||||
// adjust from 1900 to 1970
|
||||
u64 now = ((dt.date_time - 2208988800000ULL) * 1000) + offset;
|
||||
|
||||
tp->tv_sec = u64_to_double(now)/1000000.0;
|
||||
tp->tv_usec = now - ((tp->tv_sec) * 1000000);
|
||||
|
||||
// Convert to struct timeval
|
||||
tp->tv_sec = now / 1000;
|
||||
tp->tv_usec = (now - 1000*tp->tv_sec) * 1000;
|
||||
}
|
||||
|
||||
if (tz != NULL) {
|
||||
// Provide dummy information, as the 3DS does not have the concept of timezones
|
||||
tz->tz_minuteswest = 0;
|
||||
tz->tz_dsttime = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// Returns number of milliseconds since 1st Jan 1900 00:00.
|
||||
//---------------------------------------------------------------------------------
|
||||
u64 osGetTime(void) {
|
||||
//---------------------------------------------------------------------------------
|
||||
datetime_t dt = getSysTime();
|
||||
|
||||
u64 delta = svcGetSystemTick() - dt.update_tick;
|
||||
|
||||
return dt.date_time + (u32)(u64_to_double(delta)/CPU_TICKS_PER_MSEC);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user