Difference between revisions of "ARM9 OS"

From DSiBrew
Jump to navigation Jump to search
(added a stub section on memory allocation)
(→‎Threads: kernelSp is a system stack used for thread cleanup and SVCs)
 
(11 intermediate revisions by the same user not shown)
Line 2: Line 2:
  
 
== Threads ==
 
== Threads ==
 +
The scheduler only runs the thread with the highest priority, and does not switch between threads if two threads have the same priority. Because of this, rescheduling only happens when a thread-related function is called and the thread with the highest priority can no longer run.
 
<pre>
 
<pre>
struct OSFpContext {
+
struct OSMathContext {
f64 REG_DIV_NUMER; // 0x0
+
u64 REG_DIV_NUMER; // 0x0
f64 REG_DIV_DENOM; // 0x8
+
u64 REG_DIV_DENOM; // 0x8
f64 SQRT_PARAM; // 0x10
+
u64 SQRT_PARAM; // 0x10
 
u16 REG_DIVCNT; // 0x18
 
u16 REG_DIVCNT; // 0x18
 
u16 REG_SQRTCNT; // 0x1a
 
u16 REG_SQRTCNT; // 0x1a
Line 29: Line 30:
 
void *lr; // 0x3c
 
void *lr; // 0x3c
 
void *pc; // 0x40
 
void *pc; // 0x40
void *kernelSp; // 0x44
+
void *systemStack; // 0x44
struct OSFpContext fp; // 0x48
+
struct OSMathContext math; // 0x48
 
}
 
}
  
Line 42: Line 43:
 
struct OSThreadQueue *queue; // 0x78
 
struct OSThreadQueue *queue; // 0x78
 
struct OSThreadLink linkQueue; // 0x7c
 
struct OSThreadLink linkQueue; // 0x7c
u32 unknown2; // 0x80
+
struct OSMutex *mutex; // 0x84 - set to the mutex the thread is currently trying to lock
struct OSMutexQueue *queueMutex; // 0x84
+
struct OSMutexQueue queueMutex; // 0x88
// more unknown fields - the total length is not known
+
void *stackLo; // 0x90
 +
void *stackHi; // 0x94
 +
void *unknown2; // 0x98
 +
struct OSThreadQueue queueJoin; // 0x9c
 +
u32 unknown3[3]; // 0xa4
 +
struct OSAlarm *timedSleepAlarm; // 0xb0
 +
void (*cleanupFunc)(u32 res); // 0xb4
 +
u32 unknown4[2];
 
}
 
}
  
Line 66: Line 74:
  
 
struct OSMutexQueue {
 
struct OSMutexQueue {
struct OSMutex *waiting;
 
 
struct OSMutex *head;
 
struct OSMutex *head;
 
struct OSMutex *tail;
 
struct OSMutex *tail;
Line 86: Line 93:
 
== Memory allocation ==
 
== Memory allocation ==
 
3 types of heaps exist: EXPH (exponential heap), FRMH (frame heap), and UNTH (unit heap).
 
3 types of heaps exist: EXPH (exponential heap), FRMH (frame heap), and UNTH (unit heap).
 +
 +
== Time ==
 +
Timer 0 is used to keep track of global time by manually incrementing a global, while timer 1 is used to generate an interrupt for alarms. The OS orders the alarms by alert time, so that it only needs to keep track of the frontmost alarm.
 +
 +
<pre>
 +
struct OSAlarm {
 +
void (*handler)(void *userData);
 +
void *userData;
 +
u32 unknown;
 +
u64 alertTime;
 +
struct OSAlarm *prev;
 +
struct OSAlarm *next;
 +
u64 repeatInterval; // 0 for non-repeating alarms
 +
u64 repeatStart; // undefined value for non-repeating alarms
 +
}
 +
 +
struct OSAlarmQueue {
 +
u32 unknown;
 +
struct OSAlarm *head;
 +
struct OSAlarm *tail;
 +
}
 +
</pre>
 +
 +
== Interrupts ==
 +
The interrupt handler called by the [[ARM9 BIOS]] calls a handler in a table mapping the raw IRQ IDs to handlers. Each handler is either a no-op (not listed in the table below) or a stub that calls <code>__OSDispatchInterrupt</code> with an OS interrupt ID. <code>__OSDispatchInterrupt</code> then calls the handler registered with <code>__OSSetInterruptHandler</code>.
 +
 +
{| class="wikitable sortable"
 +
! Hardware ID
 +
! Translated ID
 +
! Name
 +
|-
 +
| 3
 +
| 8
 +
| Timer 0 overflow
 +
|-
 +
| 4
 +
| 9
 +
| Timer 1 overflow
 +
|-
 +
| 5
 +
| 10
 +
| Timer 2 overflow
 +
|-
 +
| 6
 +
| 11
 +
| Timer 3 overflow
 +
|-
 +
| 8
 +
| 0
 +
| DMA 0
 +
|-
 +
| 9
 +
| 1
 +
| DMA 1
 +
|-
 +
| 10
 +
| 2
 +
| DMA 2
 +
|-
 +
| 11
 +
| 3
 +
| DMA 3
 +
|-
 +
| 28
 +
| 4
 +
| New DMA 0
 +
|-
 +
| 29
 +
| 5
 +
| New DMA 1
 +
|-
 +
| 30
 +
| 6
 +
| New DMA 2
 +
|-
 +
| 31
 +
| 7
 +
| New DMA 3
 +
|}

Latest revision as of 22:47, 9 October 2022

The ARM9 SDK contains an OS library that handles various functions. It does not have many debugging strings, so most names listed on this page are taken from the Wii.

Threads

The scheduler only runs the thread with the highest priority, and does not switch between threads if two threads have the same priority. Because of this, rescheduling only happens when a thread-related function is called and the thread with the highest priority can no longer run.

struct OSMathContext {
	u64 REG_DIV_NUMER; // 0x0
	u64 REG_DIV_DENOM; // 0x8
	u64 SQRT_PARAM; // 0x10
	u16 REG_DIVCNT; // 0x18
	u16 REG_SQRTCNT; // 0x1a
}

struct OSContext {
	u32 psr; // 0x0
	u32 r0; // 0x4
	u32 r1; // 0x8
	u32 r2; // 0xc
	u32 r3; // 0x10
	u32 r4; // 0x14
	u32 r5; // 0x18
	u32 r6; // 0x1c
	u32 r7; // 0x20
	u32 r8; // 0x24
	u32 r9; // 0x28
	u32 r10; // 0x2c
	u32 r11; // 0x30
	u32 r12; // 0x34
	void *sp; // 0x38
	void *lr; // 0x3c
	void *pc; // 0x40
	void *systemStack; // 0x44
	struct OSMathContext math; // 0x48
}

struct OSThread {
	struct OSContext ctx; // 0x0
	u32 state; // 0x64
	struct OSThread *nextRunning; // 0x68
	u32 threadId; // 0x6c
	u32 priority; // 0x70
	u32 unknown; // 0x74
	struct OSThreadQueue *queue; // 0x78
	struct OSThreadLink linkQueue; // 0x7c
	struct OSMutex *mutex; // 0x84 - set to the mutex the thread is currently trying to lock
	struct OSMutexQueue queueMutex; // 0x88
	void *stackLo; // 0x90
	void *stackHi; // 0x94
	void *unknown2; // 0x98
	struct OSThreadQueue queueJoin; // 0x9c
	u32 unknown3[3]; // 0xa4
	struct OSAlarm *timedSleepAlarm; // 0xb0
	void (*cleanupFunc)(u32 res); // 0xb4
	u32 unknown4[2];
}

struct OSThreadQueue {
	struct OSThread *head;
	struct OSThread *tail;
}

struct OSThreadLink {
	struct OSThread *prev;
	struct OSThread *next;
}

struct OSMutex {
	struct OSThreadQueue waitingQueue;
	struct OSThread *holder;
	u32 timesLocked;
	struct OSMutex *next;
	struct OSMutex *prev;
}

struct OSMutexQueue {
	struct OSMutex *head;
	struct OSMutex *tail;
}

Message queues

struct OSMessageQueue {
	struct OSThreadQueue waitForReceive;
	struct OSThreadQueue waitForSend;
	u32 *buf;
	u32 capacity;
	u32 rotation;
	u32 messagesEnqueued;
}

Memory allocation

3 types of heaps exist: EXPH (exponential heap), FRMH (frame heap), and UNTH (unit heap).

Time

Timer 0 is used to keep track of global time by manually incrementing a global, while timer 1 is used to generate an interrupt for alarms. The OS orders the alarms by alert time, so that it only needs to keep track of the frontmost alarm.

struct OSAlarm {
	void (*handler)(void *userData);
	void *userData;
	u32 unknown;
	u64 alertTime;
	struct OSAlarm *prev;
	struct OSAlarm *next;
	u64 repeatInterval; // 0 for non-repeating alarms
	u64 repeatStart; // undefined value for non-repeating alarms
}

struct OSAlarmQueue {
	u32 unknown;
	struct OSAlarm *head;
	struct OSAlarm *tail;
}

Interrupts

The interrupt handler called by the ARM9 BIOS calls a handler in a table mapping the raw IRQ IDs to handlers. Each handler is either a no-op (not listed in the table below) or a stub that calls __OSDispatchInterrupt with an OS interrupt ID. __OSDispatchInterrupt then calls the handler registered with __OSSetInterruptHandler.

Hardware ID Translated ID Name
3 8 Timer 0 overflow
4 9 Timer 1 overflow
5 10 Timer 2 overflow
6 11 Timer 3 overflow
8 0 DMA 0
9 1 DMA 1
10 2 DMA 2
11 3 DMA 3
28 4 New DMA 0
29 5 New DMA 1
30 6 New DMA 2
31 7 New DMA 3