#include "dolphin/OSRtcPriv.h"
#include "dolphin/os.h"
#define RTC_CMD_READ 0x20000000
#define RTC_CMD_WRITE 0xa0000000
#define RTC_SRAM_ADDR 0x00000100
#define RTC_SRAM_SIZE 64
#define RTC_CHAN 0
#define RTC_DEV 1
#define RTC_FREQ 3
typedef struct SramControlBlock {
u8 sram[RTC_SRAM_SIZE];
u32 offset;
BOOL enabled;
BOOL locked;
BOOL sync;
void (*callback)(void);
} SramControlBlock;
static SramControlBlock Scb ATTRIBUTE_ALIGN(32);
static BOOL GetRTC(u32* rtc) {
BOOL err;
u32 cmd;
if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
return FALSE;
}
if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
EXIUnlock(RTC_CHAN);
return FALSE;
}
cmd = RTC_CMD_READ;
err = FALSE;
err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIImm(RTC_CHAN, &cmd, 4, 0, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIDeselect(RTC_CHAN);
EXIUnlock(RTC_CHAN);
*rtc = cmd;
return !err;
}
BOOL __OSGetRTC(u32* rtc) {
BOOL err;
u32 t0;
u32 t1;
int i;
for (i = 0; i < 16; i++) {
err = FALSE;
err |= !GetRTC(&t0);
err |= !GetRTC(&t1);
if (err) {
break;
}
if (t0 == t1) {
*rtc = t0;
return TRUE;
}
}
return FALSE;
}
BOOL __OSSetRTC(u32 rtc) {
BOOL err;
u32 cmd;
if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
return FALSE;
}
if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
EXIUnlock(RTC_CHAN);
return FALSE;
}
cmd = RTC_CMD_WRITE;
err = FALSE;
err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIImm(RTC_CHAN, &rtc, 4, 1, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIDeselect(RTC_CHAN);
EXIUnlock(RTC_CHAN);
return !err;
}
static BOOL ReadSram(void* buffer) {
BOOL err;
u32 cmd;
DCInvalidateRange(buffer, RTC_SRAM_SIZE);
if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
return FALSE;
}
if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
EXIUnlock(RTC_CHAN);
return FALSE;
}
cmd = RTC_CMD_READ | RTC_SRAM_ADDR;
err = FALSE;
err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIDma(RTC_CHAN, buffer, RTC_SRAM_SIZE, 0, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIDeselect(RTC_CHAN);
EXIUnlock(RTC_CHAN);
return !err;
}
BOOL WriteSram(void* buffer, u32 offset, u32 size);
static void WriteSramCallback(s32 chan, OSContext* context) {
Scb.sync = WriteSram(Scb.sram + Scb.offset, Scb.offset, RTC_SRAM_SIZE - Scb.offset);
if (Scb.sync) {
Scb.offset = RTC_SRAM_SIZE;
}
}
BOOL WriteSram(void* buffer, u32 offset, u32 size) {
BOOL err;
u32 cmd;
if (!EXILock(RTC_CHAN, RTC_DEV, WriteSramCallback)) {
return FALSE;
}
if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
EXIUnlock(RTC_CHAN);
return FALSE;
}
offset <<= 6;
cmd = RTC_CMD_WRITE | RTC_SRAM_ADDR + offset;
err = FALSE;
err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIImmEx(RTC_CHAN, buffer, (s32)size, 1);
err |= !EXIDeselect(RTC_CHAN);
EXIUnlock(RTC_CHAN);
return !err;
}
void __OSInitSram() {
Scb.locked = Scb.enabled = FALSE;
Scb.sync = ReadSram(Scb.sram);
Scb.offset = RTC_SRAM_SIZE;
}
static void* LockSram(u32 offset) {
BOOL enabled;
enabled = OSDisableInterrupts();
if (Scb.locked != FALSE) {
OSRestoreInterrupts(enabled);
return NULL;
}
Scb.enabled = enabled;
Scb.locked = TRUE;
return Scb.sram + offset;
}
#ifdef FULL_FRANK
OSSram* __OSLockSram() { return LockSram(0); }
#else
#pragma push
#pragma optimization_level 0
#pragma optimizewithasm off
asm OSSram* __OSLockSram() {
nofralloc
mflr r0
lis r3, Scb@ha
stw r0, 4(r1)
stwu r1, -0x10(r1)
stw r31, 0xc(r1)
addi r31, r3, Scb@l
bl OSDisableInterrupts
lwz r0, 0x48(r31)
addi r4, r31, 0x48
cmpwi r0, 0
beq lbl_80383B0C
bl OSRestoreInterrupts
li r31, 0
b lbl_80383B18
lbl_80383B0C:
stw r3, 0x44(r31)
li r0, 1
stw r0, 0(r4)
lbl_80383B18:
mr r3, r31
lwz r0, 0x14(r1)
lwz r31, 0xc(r1)
addi r1, r1, 0x10
mtlr r0
blr
}
#pragma pop
#endif
OSSramEx* __OSLockSramEx() { return LockSram(sizeof(OSSram)); }
static BOOL UnlockSram(BOOL commit, u32 offset) {
u16* p;
if (commit) {
if (offset == 0) {
OSSram* sram = (OSSram*)Scb.sram;
if (2u < (sram->flags & 3)) {
sram->flags &= ~3;
}
sram->checkSum = sram->checkSumInv = 0;
for (p = (u16*)&sram->counterBias; p < (u16*)(Scb.sram + sizeof(OSSram)); p++) {
sram->checkSum += *p;
sram->checkSumInv += ~*p;
}
}
if (offset < Scb.offset) {
Scb.offset = offset;
}
Scb.sync = WriteSram(Scb.sram + Scb.offset, Scb.offset, RTC_SRAM_SIZE - Scb.offset);
if (Scb.sync) {
Scb.offset = RTC_SRAM_SIZE;
}
}
Scb.locked = FALSE;
OSRestoreInterrupts(Scb.enabled);
return Scb.sync;
}
BOOL __OSUnlockSram(BOOL commit) { return UnlockSram(commit, 0); }
BOOL __OSUnlockSramEx(BOOL commit) { return UnlockSram(commit, sizeof(OSSram)); }
BOOL __OSSyncSram() { return Scb.sync; }
BOOL __OSReadROM(void* buffer, s32 length, s32 offset) {
BOOL err;
u32 cmd;
DCInvalidateRange(buffer, (u32)length);
if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
return FALSE;
}
if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
EXIUnlock(RTC_CHAN);
return FALSE;
}
cmd = (u32)(offset << 6);
err = FALSE;
err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIDma(RTC_CHAN, buffer, length, 0, NULL);
err |= !EXISync(RTC_CHAN);
err |= !EXIDeselect(RTC_CHAN);
EXIUnlock(RTC_CHAN);
return !err;
}
inline OSSram* __OSLockSramHACK() { return LockSram(0); }
#ifdef FULL_FRANK
u32 OSGetSoundMode() {
OSSram* sram;
u32 mode;
sram = __OSLockSramHACK();
mode = (sram->flags & 0x4) ? OS_SOUND_MODE_STEREO : OS_SOUND_MODE_MONO;
__OSUnlockSram(FALSE);
return mode;
}
#else
#pragma push
#pragma optimization_level
#pragma optimizewithasm off
asm u32 OSGetSoundMode() {
nofralloc
mflr r0
lis r3, Scb@ha
stw r0, 4(r1)
stwu r1, -0x20(r1)
stw r31, 0x1c(r1)
addi r31, r3, Scb@l
bl OSDisableInterrupts
lwz r0, 0x48(r31)
addi r4, r31, 0x48
cmpwi r0, 0
beq lbl_80384048
bl OSRestoreInterrupts
li r31, 0
b lbl_80384054
lbl_80384048:
stw r3, 0x44(r31)
li r0, 1
stw r0, 0(r4)
lbl_80384054:
lbz r0, 0x13(r31)
rlwinm. r0, r0, 0, 0x1d, 0x1d
beq lbl_80384068
li r31, 1
b lbl_8038406C
lbl_80384068:
li r31, 0
lbl_8038406C:
li r3, 0
li r4, 0
bl UnlockSram
mr r3, r31
lwz r0, 0x24(r1)
lwz r31, 0x1c(r1)
addi r1, r1, 0x20
mtlr r0
blr
}
#pragma pop
#endif
void OSSetSoundMode(u32 mode) {
OSSram* sram;
mode <<= 2;
mode &= 4;
sram = __OSLockSramHACK();
if (mode == (sram->flags & 4)) {
__OSUnlockSram(FALSE);
return;
}
sram->flags &= ~4;
sram->flags |= mode;
__OSUnlockSram(TRUE);
}
#ifdef FULL_FRANK
u32 OSGetProgressiveMode() {
OSSram* sram;
u32 mode;
sram = __OSLockSramHACK();
mode = (sram->flags & 0x80) >> 7;
__OSUnlockSram(FALSE);
return mode;
}
#else
#pragma push
#pragma optimization_level
#pragma optimizewithasm off
asm u32 OSGetProgressiveMode() {
nofralloc
mflr r0
lis r3, Scb@ha
stw r0, 4(r1)
stwu r1, -0x18(r1)
stw r31, 0x14(r1)
addi r31, r3, Scb@l
bl OSDisableInterrupts
lwz r0, 0x48(r31)
addi r4, r31, 0x48
cmpwi r0, 0
beq lbl_8038416C
bl OSRestoreInterrupts
li r31, 0
b lbl_80384178
lbl_8038416C:
stw r3, 0x44(r31)
li r0, 1
stw r0, 0(r4)
lbl_80384178:
lbz r0, 0x13(r31)
li r3, 0
li r4, 0
rlwinm r31, r0, 0x19, 0x1f, 0x1f
bl UnlockSram
mr r3, r31
lwz r0, 0x1c(r1)
lwz r31, 0x14(r1)
addi r1, r1, 0x18
mtlr r0
blr
}
#pragma pop
#endif
void OSSetProgressiveMode(u32 mode) {
OSSram* sram;
mode <<= 7;
mode &= 0x80;
sram = __OSLockSramHACK();
if (mode == (sram->flags & 0x80)) {
__OSUnlockSram(FALSE);
return;
}
sram->flags &= ~0x80;
sram->flags |= mode;
__OSUnlockSram(TRUE);
}
#ifdef FULL_FRANK
u8 OSGetLanguage() {
OSSram* sram;
u8 language;
sram = __OSLockSramHACK();
language = sram->language;
__OSUnlockSram(FALSE);
return language;
}
#else
#pragma push
#pragma optimization_level
#pragma optimizewithasm off
asm u8 OSGetLanguage() {
nofralloc
mflr r0
lis r3, Scb@ha
stw r0, 4(r1)
stwu r1, -0x18(r1)
stw r31, 0x14(r1)
addi r31, r3, Scb@l
bl OSDisableInterrupts
lwz r0, 0x48(r31)
addi r4, r31, 0x48
cmpwi r0, 0
beq lbl_80384280
bl OSRestoreInterrupts
li r31, 0
b lbl_8038428C
lbl_80384280:
stw r3, 0x44(r31)
li r0, 1
stw r0, 0(r4)
lbl_8038428C:
lbz r31, 0x12(r31)
li r3, 0
li r4, 0
bl UnlockSram
mr r3, r31
lwz r0, 0x1c(r1)
lwz r31, 0x14(r1)
addi r1, r1, 0x18
mtlr r0
blr
}
#pragma pop
#endif
#ifdef FULL_FRANK
u16 OSGetWirelessID(s32 channel) {
OSSramEx* sram;
u16 id;
sram = __OSLockSramEx();
id = sram->wirelessPadID[channel];
__OSUnlockSramEx(FALSE);
return id;
}
#else
#pragma push
#pragma optimization_level
#pragma optimizewithasm off
asm u16 OSGetWirelessID(s32 channel) {
nofralloc
mflr r0
lis r4, Scb@ha
stw r0, 4(r1)
stwu r1, -0x20(r1)
stw r31, 0x1c(r1)
addi r31, r4, Scb@l
stw r30, 0x18(r1)
addi r30, r3, 0
bl OSDisableInterrupts
lwz r0, 0x48(r31)
addi r4, r31, 0x48
cmpwi r0, 0
beq lbl_803842F4
bl OSRestoreInterrupts
li r3, 0
b lbl_80384304
lbl_803842F4:
stw r3, 0x44(r31)
li r0, 1
addi r3, r31, 0x14
stw r0, 0(r4)
lbl_80384304:
slwi r0, r30, 1
add r3, r3, r0
lhz r31, 0x1c(r3)
li r3, 0
li r4, 0x14
bl UnlockSram
mr r3, r31
lwz r0, 0x24(r1)
lwz r31, 0x1c(r1)
lwz r30, 0x18(r1)
addi r1, r1, 0x20
mtlr r0
blr
}
#pragma pop
#endif
void OSSetWirelessID(s32 channel, u16 id) {
OSSramEx* sram;
sram = __OSLockSramEx();
if (sram->wirelessPadID[channel] != id) {
sram->wirelessPadID[channel] = id;
__OSUnlockSramEx(TRUE);
return;
}
__OSUnlockSramEx(FALSE);
}