summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/arm_interface.h10
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp19
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h7
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp582
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp6
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h4
-rw-r--r--src/core/arm/interpreter/armsupp.cpp16
-rw-r--r--src/core/arm/skyeye_common/armdefs.h1
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp2
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp66
-rw-r--r--src/core/core.cpp20
-rw-r--r--src/core/core.h20
-rw-r--r--src/core/core_timing.cpp711
-rw-r--r--src/core/core_timing.h77
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp18
-rw-r--r--src/core/file_sys/archive_extsavedata.h2
-rw-r--r--src/core/file_sys/archive_savedata.cpp18
-rw-r--r--src/core/file_sys/archive_savedatacheck.cpp15
-rw-r--r--src/core/file_sys/archive_sdmc.cpp4
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp7
-rw-r--r--src/core/file_sys/archive_systemsavedata.h2
-rw-r--r--src/core/hle/function_wrappers.h8
-rw-r--r--src/core/hle/hle.cpp4
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp33
-rw-r--r--src/core/hle/kernel/address_arbiter.h2
-rw-r--r--src/core/hle/kernel/event.cpp16
-rw-r--r--src/core/hle/kernel/kernel.cpp34
-rw-r--r--src/core/hle/kernel/kernel.h26
-rw-r--r--src/core/hle/kernel/mutex.cpp23
-rw-r--r--src/core/hle/kernel/semaphore.cpp10
-rw-r--r--src/core/hle/kernel/shared_memory.cpp4
-rw-r--r--src/core/hle/kernel/thread.cpp425
-rw-r--r--src/core/hle/kernel/thread.h118
-rw-r--r--src/core/hle/kernel/timer.cpp144
-rw-r--r--src/core/hle/kernel/timer.h47
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/apt_a.cpp2
-rw-r--r--src/core/hle/service/apt_s.cpp122
-rw-r--r--src/core/hle/service/apt_s.h30
-rw-r--r--src/core/hle/service/apt_u.cpp32
-rw-r--r--src/core/hle/service/cfg/cfg.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg.h2
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp98
-rw-r--r--src/core/hle/service/cfg/cfg_s.h23
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/fs/archive.cpp26
-rw-r--r--src/core/hle/service/fs/archive.h7
-rw-r--r--src/core/hle/service/fs/fs_user.cpp10
-rw-r--r--src/core/hle/service/gsp_gpu.cpp35
-rw-r--r--src/core/hle/service/gsp_gpu.h25
-rw-r--r--src/core/hle/service/hid_user.cpp3
-rw-r--r--src/core/hle/service/ptm_sysm.cpp56
-rw-r--r--src/core/hle/service/ptm_sysm.h23
-rw-r--r--src/core/hle/service/ptm_u.cpp8
-rw-r--r--src/core/hle/service/service.cpp23
-rw-r--r--src/core/hle/service/service.h46
-rw-r--r--src/core/hle/service/soc_u.cpp13
-rw-r--r--src/core/hle/service/srv.cpp4
-rw-r--r--src/core/hle/service/y2r_u.cpp17
-rw-r--r--src/core/hle/shared_page.cpp80
-rw-r--r--src/core/hle/shared_page.h26
-rw-r--r--src/core/hle/svc.cpp122
-rw-r--r--src/core/hle/svc.h15
-rw-r--r--src/core/hw/gpu.cpp131
-rw-r--r--src/core/hw/gpu.h3
-rw-r--r--src/core/hw/hw.cpp1
-rw-r--r--src/core/loader/3dsx.cpp109
-rw-r--r--src/core/loader/3dsx.h14
-rw-r--r--src/core/loader/elf.cpp116
-rw-r--r--src/core/loader/elf.h14
-rw-r--r--src/core/loader/loader.cpp110
-rw-r--r--src/core/loader/loader.h18
-rw-r--r--src/core/loader/ncch.cpp291
-rw-r--r--src/core/loader/ncch.h24
-rw-r--r--src/core/mem_map_funcs.cpp5
-rw-r--r--src/core/system.cpp4
78 files changed, 2524 insertions, 1692 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b67226d8d..0fc8bf318 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -31,17 +31,20 @@ set(SRCS
hle/kernel/mutex.cpp
hle/kernel/semaphore.cpp
hle/kernel/shared_memory.cpp
+ hle/kernel/timer.cpp
hle/kernel/thread.cpp
hle/service/ac_u.cpp
hle/service/act_u.cpp
hle/service/am_app.cpp
hle/service/am_net.cpp
hle/service/apt_a.cpp
+ hle/service/apt_s.cpp
hle/service/apt_u.cpp
hle/service/boss_u.cpp
hle/service/cecd_u.cpp
hle/service/cfg/cfg.cpp
hle/service/cfg/cfg_i.cpp
+ hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
hle/service/dsp_dsp.cpp
@@ -62,6 +65,7 @@ set(SRCS
hle/service/nwm_uds.cpp
hle/service/pm_app.cpp
hle/service/ptm_u.cpp
+ hle/service/ptm_sysm.cpp
hle/service/service.cpp
hle/service/soc_u.cpp
hle/service/srv.cpp
@@ -69,6 +73,7 @@ set(SRCS
hle/service/y2r_u.cpp
hle/config_mem.cpp
hle/hle.cpp
+ hle/shared_page.cpp
hle/svc.cpp
hw/gpu.cpp
hw/hw.cpp
@@ -123,17 +128,20 @@ set(HEADERS
hle/kernel/semaphore.h
hle/kernel/session.h
hle/kernel/shared_memory.h
+ hle/kernel/timer.h
hle/kernel/thread.h
hle/service/ac_u.h
hle/service/act_u.h
hle/service/am_app.h
hle/service/am_net.h
hle/service/apt_a.h
+ hle/service/apt_s.h
hle/service/apt_u.h
hle/service/boss_u.h
hle/service/cecd_u.h
hle/service/cfg/cfg.h
hle/service/cfg/cfg_i.h
+ hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
hle/service/dsp_dsp.h
@@ -154,6 +162,7 @@ set(HEADERS
hle/service/nwm_uds.h
hle/service/pm_app.h
hle/service/ptm_u.h
+ hle/service/ptm_sysm.h
hle/service/service.h
hle/service/soc_u.h
hle/service/srv.h
@@ -163,6 +172,7 @@ set(HEADERS
hle/result.h
hle/function_wrappers.h
hle/hle.h
+ hle/shared_page.h
hle/svc.h
hw/gpu.h
hw/hw.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 3b7209418..e612f7439 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,7 +7,9 @@
#include "common/common.h"
#include "common/common_types.h"
-#include "core/hle/svc.h"
+namespace Core {
+ struct ThreadContext;
+}
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
@@ -87,13 +89,13 @@ public:
* Saves the current CPU context
* @param ctx Thread context to save
*/
- virtual void SaveContext(ThreadContext& ctx) = 0;
+ virtual void SaveContext(Core::ThreadContext& ctx) = 0;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
- virtual void LoadContext(const ThreadContext& ctx) = 0;
+ virtual void LoadContext(const Core::ThreadContext& ctx) = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
@@ -103,6 +105,8 @@ public:
return num_instructions;
}
+ s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
+
protected:
/**
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index a838fd25a..9c4cc90f2 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -9,11 +9,14 @@
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+
const static cpu_config_t s_arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
-ARM_DynCom::ARM_DynCom() : ticks(0) {
+ARM_DynCom::ARM_DynCom() {
state = std::unique_ptr<ARMul_State>(new ARMul_State);
ARMul_EmulateInit();
@@ -72,11 +75,14 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
}
u64 ARM_DynCom::GetTicks() const {
- return ticks;
+ // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone
+ return CoreTiming::GetTicks();
}
void ARM_DynCom::AddTicks(u64 ticks) {
- this->ticks += ticks;
+ down_count -= ticks;
+ if (down_count < 0)
+ CoreTiming::Advance();
}
void ARM_DynCom::ExecuteInstructions(int num_instructions) {
@@ -85,10 +91,11 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
// Dyncom only breaks on instruction dispatch. This only happens on every instruction when
// executing one instruction at a time. Otherwise, if a block is being executed, more
// instructions may actually be executed than specified.
- ticks += InterpreterMainLoop(state.get());
+ unsigned ticks_executed = InterpreterMainLoop(state.get());
+ AddTicks(ticks_executed);
}
-void ARM_DynCom::SaveContext(ThreadContext& ctx) {
+void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@@ -104,7 +111,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
ctx.mode = state->NextInstr;
}
-void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
+void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 7284dcd07..f16fb070c 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -71,13 +71,13 @@ public:
* Saves the current CPU context
* @param ctx Thread context to save
*/
- void SaveContext(ThreadContext& ctx) override;
+ void SaveContext(Core::ThreadContext& ctx) override;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
- void LoadContext(const ThreadContext& ctx) override;
+ void LoadContext(const Core::ThreadContext& ctx) override;
/// Prepare core for thread reschedule (if needed to correctly handle state)
void PrepareReschedule() override;
@@ -89,8 +89,5 @@ public:
void ExecuteInstructions(int num_instructions) override;
private:
-
std::unique_ptr<ARMul_State> state;
- u64 ticks;
-
};
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 9b291862c..fce8d8e4f 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -176,13 +176,11 @@ unsigned int DPO(ArithmeticShiftRightByImmediate)(arm_processor *cpu, unsigned i
unsigned int shifter_operand;
int shift_imm = BITS(sht_oper, 7, 11);
if (shift_imm == 0) {
- if (BIT(rm, 31)) {
+ if (BIT(rm, 31) == 0)
shifter_operand = 0;
- cpu->shifter_carry_out = BIT(rm, 31);
- } else {
+ else
shifter_operand = 0xFFFFFFFF;
- cpu->shifter_carry_out = BIT(rm, 31);
- }
+ cpu->shifter_carry_out = BIT(rm, 31);
} else {
shifter_operand = static_cast<int>(rm) >> shift_imm;
cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
@@ -412,10 +410,21 @@ void LnSWoUB(ScaledRegisterPreIndexed)(arm_processor *cpu, unsigned int inst, un
}
break;
case 2:
- DEBUG_MSG;
+ if (shift_imm == 0) { // ASR #32
+ if (BIT(rm, 31) == 1)
+ index = 0xFFFFFFFF;
+ else
+ index = 0;
+ } else {
+ index = static_cast<int>(rm) >> shift_imm;
+ }
break;
case 3:
- DEBUG_MSG;
+ if (shift_imm == 0) {
+ index = (cpu->CFlag << 31) | (rm >> 1);
+ } else {
+ index = ROTATE_RIGHT_32(rm, shift_imm);
+ }
break;
}
@@ -451,10 +460,21 @@ void LnSWoUB(ScaledRegisterPostIndexed)(arm_processor *cpu, unsigned int inst, u
}
break;
case 2:
- DEBUG_MSG;
+ if (shift_imm == 0) { // ASR #32
+ if (BIT(rm, 31) == 1)
+ index = 0xFFFFFFFF;
+ else
+ index = 0;
+ } else {
+ index = static_cast<int>(rm) >> shift_imm;
+ }
break;
case 3:
- DEBUG_MSG;
+ if (shift_imm == 0) {
+ index = (cpu->CFlag << 31) | (rm >> 1);
+ } else {
+ index = ROTATE_RIGHT_32(rm, shift_imm);
+ }
break;
}
@@ -656,8 +676,8 @@ void LnSWoUB(ScaledRegisterOffset)(arm_processor *cpu, unsigned int inst, unsign
}
break;
case 2:
- if (shift_imm == 0){ // ASR #32
- if (rm >> 31)
+ if (shift_imm == 0) { // ASR #32
+ if (BIT(rm, 31) == 1)
index = 0xFFFFFFFF;
else
index = 0;
@@ -666,7 +686,11 @@ void LnSWoUB(ScaledRegisterOffset)(arm_processor *cpu, unsigned int inst, unsign
}
break;
case 3:
- DEBUG_MSG;
+ if (shift_imm == 0) {
+ index = (cpu->CFlag << 31) | (rm >> 1);
+ } else {
+ index = ROTATE_RIGHT_32(rm, shift_imm);
+ }
break;
}
@@ -947,6 +971,15 @@ typedef struct _smla_inst {
unsigned int Rn;
} smla_inst;
+typedef struct smlalxy_inst {
+ unsigned int x;
+ unsigned int y;
+ unsigned int RdLo;
+ unsigned int RdHi;
+ unsigned int Rm;
+ unsigned int Rn;
+} smlalxy_inst;
+
typedef struct ssat_inst {
unsigned int Rn;
unsigned int Rd;
@@ -1719,25 +1752,21 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
- if (I_BIT == 0) {
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
DEBUG_MSG;
}
- #if 0
- inst_cream->get_addr = get_calc_addr_op(inst);
- if(inst == 0x54f13001) {
- DEBUG_LOG(ARM11, "get_calc_addr_op:%llx\n", inst_cream->get_addr);
- }
- #endif
if (BITS(inst, 12, 15) == 15) {
inst_base->br = INDIRECT_BRANCH;
@@ -1837,17 +1866,24 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
- if (I_BIT == 0) {
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
+ // Reaching this would indicate the thumb version
+ // of this instruction, however the 3DS CPU doesn't
+ // support this variant (the 3DS CPU is only ARMv6K,
+ // while this variant is added in ARMv6T2).
+ // So it's sufficient for citra to not implement this.
DEBUG_MSG;
}
@@ -2403,7 +2439,25 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlalxy_inst));
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->x = BIT(inst, 5);
+ inst_cream->y = BIT(inst, 6);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->Rn = BITS(inst, 0, 4);
+ inst_cream->Rm = BITS(inst, 8, 11);
+
+ return inst_base;
+}
ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index)
{
@@ -2687,17 +2741,19 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
-// inst_cream->get_addr = get_calc_addr_op(inst);
- if (I_BIT == 0) {
+
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
DEBUG_MSG;
}
@@ -2769,17 +2825,24 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
- if (I_BIT == 0) {
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
+ // Reaching this would indicate the thumb version
+ // of this instruction, however the 3DS CPU doesn't
+ // support this variant (the 3DS CPU is only ARMv6K,
+ // while this variant is added in ARMv6T2).
+ // So it's sufficient for citra to not implement this.
DEBUG_MSG;
}
@@ -3878,21 +3941,9 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
#endif
- #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0)
- #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1)
-
- #define UPDATE_CFLAG(dst, lop, rop) (cpu->CFlag = ((dst < lop) || (dst < rop)))
- #define UPDATE_CFLAG_CARRY_FROM_ADD(lop, rop, flag) (cpu->CFlag = (((uint64_t) lop + (uint64_t) rop + (uint64_t) flag) > 0xffffffff) )
- #define UPDATE_CFLAG_NOT_BORROW_FROM_FLAG(lop, rop, flag) (cpu->CFlag = ((uint64_t) lop >= ((uint64_t) rop + (uint64_t) flag)))
- #define UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop) (cpu->CFlag = (lop >= rop))
- #define UPDATE_CFLAG_WITH_NOT(dst, lop, rop) (cpu->CFlag = !(dst < lop))
- #define UPDATE_CFLAG_WITH_SC (cpu->CFlag = cpu->shifter_carry_out)
-
- #define UPDATE_VFLAG(dst, lop, rop) (cpu->VFlag = (((lop < 0) && (rop < 0) && (dst >= 0)) || \
- ((lop >= 0) && (rop) >= 0 && (dst < 0))))
- #define UPDATE_VFLAG_WITH_NOT(dst, lop, rop) (cpu->VFlag = !(((lop < 0) && (rop < 0) && (dst >= 0)) || \
- ((lop >= 0) && (rop) >= 0 && (dst < 0))))
- #define UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop) (cpu->VFlag = (((lop ^ rop) & (lop ^ dst)) >> 31))
+ #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0)
+ #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1)
+ #define UPDATE_CFLAG_WITH_SC (cpu->CFlag = cpu->shifter_carry_out)
#define SAVE_NZCVT cpu->Cpsr = (cpu->Cpsr & 0x0fffffdf) | \
(cpu->NFlag << 31) | \
@@ -3940,16 +3991,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
&&INIT_INST_LENGTH,&&END
};
#endif
- arm_inst * inst_base;
- unsigned int lop, rop, dst;
+ arm_inst* inst_base;
unsigned int addr;
unsigned int phys_addr;
- unsigned int last_pc = 0;
unsigned int num_instrs = 0;
- static unsigned int last_physical_base = 0, last_logical_base = 0;
int ptr;
- bool single_step = (cpu->NumInstrsToExecute == 1);
LOAD_NZCVT;
DISPATCH:
@@ -3976,12 +4023,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
ADC_INST:
{
- adc_inst *inst_cream = (adc_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- unsigned int sht_op = SHIFTER_OPERAND;
- rop = SHIFTER_OPERAND + cpu->CFlag;
- RD = dst = lop + rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ adc_inst* const inst_cream = (adc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(RN, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -3989,10 +4037,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_CARRY_FROM_ADD(lop, sht_op, cpu->CFlag);
- UPDATE_VFLAG((int)dst, (int)lop, (int)rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(adc_inst));
@@ -4006,14 +4054,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
ADD_INST:
{
- add_inst *inst_cream = (add_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- if (inst_cream->Rn == 15) {
- lop += 2 * GET_INST_SIZE(cpu);
- }
- rop = SHIFTER_OPERAND;
- RD = dst = lop + rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ add_inst* const inst_cream = (add_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4021,10 +4072,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG(dst, lop, rop);
- UPDATE_VFLAG((int)dst, (int)lop, (int)rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(add_inst));
@@ -4040,9 +4091,9 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
and_inst *inst_cream = (and_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- RD = dst = lop & rop;
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop & rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4050,8 +4101,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4083,12 +4134,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
bic_inst *inst_cream = (bic_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
+ u32 lop = RN;
if (inst_cream->Rn == 15) {
lop += 2 * GET_INST_SIZE(cpu);
}
- rop = SHIFTER_OPERAND;
- RD = dst = lop & (~rop);
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop & (~rop);
if ((inst_cream->S) && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4096,8 +4147,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4126,7 +4177,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
cpu->Reg[14] = (cpu->Reg[15] + GET_INST_SIZE(cpu));
cpu->TFlag = 0x1;
int signed_int = inst_cream->val.signed_immed_24;
- signed_int = (signed_int) & 0x800000 ? (0x3F000000 | signed_int) : signed_int;
+ signed_int = (signed_int & 0x800000) ? (0x3F000000 | signed_int) : signed_int;
signed_int = signed_int << 2;
cpu->Reg[15] = cpu->Reg[15] + 8 + signed_int + (BIT(inst, 24) << 1);
}
@@ -4203,15 +4254,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
CMN_INST:
{
- cmn_inst *inst_cream = (cmn_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- dst = lop + rop;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG(dst, lop, rop);
- UPDATE_VFLAG((int)dst, (int)lop, (int)rop);
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ cmn_inst* const inst_cream = (cmn_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ u32 result = AddWithCarry(RN, SHIFTER_OPERAND, 0, &carry, &overflow);
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(cmn_inst));
@@ -4220,19 +4273,21 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
CMP_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- cmp_inst *inst_cream = (cmp_inst *)inst_base->component;
- lop = RN;
- if (inst_cream->Rn == 15) {
- lop += 2 * GET_INST_SIZE(cpu);
- }
- rop = SHIFTER_OPERAND;
- dst = lop - rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ cmp_inst* const inst_cream = (cmp_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ u32 result = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop);
- UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop);
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(cmp_inst));
@@ -4290,12 +4345,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
eor_inst *inst_cream = (eor_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
+ u32 lop = RN;
if (inst_cream->Rn == 15) {
lop += 2 * GET_INST_SIZE(cpu);
}
- rop = SHIFTER_OPERAND;
- RD = dst = lop ^ rop;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop ^ rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4303,8 +4358,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4821,10 +4876,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOG_ERROR(Core_ARM11, "invalid operands for MLA");
CITRA_IGNORE_EXIT(-1);
}
- RD = dst = static_cast<uint32_t>((rm * rs + rn) & 0xffffffff);
+ RD = static_cast<uint32_t>((rm * rs + rn) & 0xffffffff);
if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mla_inst));
@@ -4840,7 +4895,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
mov_inst *inst_cream = (mov_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- RD = dst = SHIFTER_OPERAND;
+ RD = SHIFTER_OPERAND;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4848,8 +4903,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4937,39 +4992,41 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
MSR_INST:
{
- msr_inst *inst_cream = (msr_inst *)inst_base->component;
- const uint32_t UnallocMask = 0x06f0fc00, UserMask = 0xf80f0200, PrivMask = 0x000001df, StateMask = 0x01000020;
- unsigned int inst = inst_cream->inst;
- unsigned int operand;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ msr_inst *inst_cream = (msr_inst *)inst_base->component;
+ const uint32_t UnallocMask = 0x06f0fc00, UserMask = 0xf80f0200, PrivMask = 0x000001df, StateMask = 0x01000020;
+ unsigned int inst = inst_cream->inst;
+ unsigned int operand;
- if (BIT(inst, 25)) {
- int rot_imm = BITS(inst, 8, 11) * 2;
- operand = ROTATE_RIGHT_32(BITS(inst, 0, 7), rot_imm);
- } else {
- operand = cpu->Reg[BITS(inst, 0, 3)];
- }
- uint32_t byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0)
- | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
- uint32_t mask;
- if (!inst_cream->R) {
- if (InAPrivilegedMode(cpu)) {
- if ((operand & StateMask) != 0) {
- /// UNPREDICTABLE
- DEBUG_MSG;
- } else
- mask = byte_mask & (UserMask | PrivMask);
+ if (BIT(inst, 25)) {
+ int rot_imm = BITS(inst, 8, 11) * 2;
+ operand = ROTATE_RIGHT_32(BITS(inst, 0, 7), rot_imm);
} else {
- mask = byte_mask & UserMask;
+ operand = cpu->Reg[BITS(inst, 0, 3)];
}
- SAVE_NZCVT;
+ uint32_t byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0)
+ | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
+ uint32_t mask;
+ if (!inst_cream->R) {
+ if (InAPrivilegedMode(cpu)) {
+ if ((operand & StateMask) != 0) {
+ /// UNPREDICTABLE
+ DEBUG_MSG;
+ } else
+ mask = byte_mask & (UserMask | PrivMask);
+ } else {
+ mask = byte_mask & UserMask;
+ }
+ SAVE_NZCVT;
- cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask);
- switch_mode(cpu, cpu->Cpsr & 0x1f);
- LOAD_NZCVT;
- } else {
- if (CurrentModeHasSPSR) {
- mask = byte_mask & (UserMask | PrivMask | StateMask);
- cpu->Spsr_copy = (cpu->Spsr_copy & ~mask) | (operand & mask);
+ cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask);
+ switch_mode(cpu, cpu->Cpsr & 0x1f);
+ LOAD_NZCVT;
+ } else {
+ if (CurrentModeHasSPSR) {
+ mask = byte_mask & (UserMask | PrivMask | StateMask);
+ cpu->Spsr_copy = (cpu->Spsr_copy & ~mask) | (operand & mask);
+ }
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4983,10 +5040,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
uint64_t rm = RM;
uint64_t rs = RS;
- RD = dst = static_cast<uint32_t>((rm * rs) & 0xffffffff);
+ RD = static_cast<uint32_t>((rm * rs) & 0xffffffff);
if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mul_inst));
@@ -5000,9 +5057,11 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
MVN_INST:
{
- mvn_inst *inst_cream = (mvn_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- RD = dst = ~SHIFTER_OPERAND;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mvn_inst* const inst_cream = (mvn_inst*)inst_base->component;
+
+ RD = ~SHIFTER_OPERAND;
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5010,8 +5069,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -5026,11 +5085,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
ORR_INST:
{
- orr_inst *inst_cream = (orr_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- RD = dst = lop | rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ orr_inst* const inst_cream = (orr_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop | rop;
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5038,8 +5099,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -5259,14 +5320,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
RFE_INST:
RSB_INST:
{
- rsb_inst *inst_cream = (rsb_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- rop = RN;
- lop = SHIFTER_OPERAND;
- if (inst_cream->Rn == 15) {
- rop += 2 * GET_INST_SIZE(cpu);;
- }
- RD = dst = lop - rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rsb_inst* const inst_cream = (rsb_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(~rn_val, SHIFTER_OPERAND, 1, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5274,10 +5338,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop);
- UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsb_inst));
@@ -5291,11 +5355,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
RSC_INST:
{
- rsc_inst *inst_cream = (rsc_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- RD = dst = rop - lop - !cpu->CFlag;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rsc_inst* const inst_cream = (rsc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(~RN, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5303,10 +5369,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM_FLAG(rop, lop, !cpu->CFlag);
- UPDATE_VFLAG_OVERFLOW_FROM((int)dst, (int)rop, (int)lop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsc_inst));
@@ -5429,11 +5495,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
SBC_INST:
{
- sbc_inst *inst_cream = (sbc_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = SHIFTER_OPERAND + !cpu->CFlag;
- rop = RN;
- RD = dst = rop - lop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sbc_inst* const inst_cream = (sbc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(RN, ~SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5441,15 +5509,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
-
- if(rop >= !cpu->CFlag)
- UPDATE_CFLAG_NOT_BORROW_FROM(rop - !cpu->CFlag, SHIFTER_OPERAND);
- else
- UPDATE_CFLAG_NOT_BORROW_FROM(rop, !cpu->CFlag);
-
- UPDATE_VFLAG_OVERFLOW_FROM(dst, rop, lop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sbc_inst));
@@ -5686,6 +5749,34 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SMLALXY_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ u64 operand1 = RN;
+ u64 operand2 = RM;
+
+ if (inst_cream->x != 0)
+ operand1 >>= 16;
+ if (inst_cream->y != 0)
+ operand2 >>= 16;
+ operand1 &= 0xFFFF;
+ if (operand1 & 0x8000)
+ operand1 -= 65536;
+ operand2 &= 0xFFFF;
+ if (operand2 & 0x8000)
+ operand2 -= 65536;
+
+ u64 dest = ((u64)RDHI << 32 | RDLO) + (operand1 * operand2);
+ RDLO = (dest & 0xFFFFFFFF);
+ RDHI = ((dest >> 32) & 0xFFFFFFFF);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlalxy_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
SMLAW_INST:
{
@@ -5836,16 +5927,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
SMULW_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- smlad_inst *inst_cream = (smlad_inst *)inst_base->component;
- int64_t rm = RM;
- int64_t rn = RN;
- if (inst_cream->m)
- rm = BITS(rm, 16, 31);
- else
- rm = BITS(rm, 0, 15);
- int64_t rst = rm * rn;
- RD = BITS(rst, 16, 47);
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);
+
+ s64 result = (s64)rm * (s64)(s32)RN;
+ RD = BITS(result, 16, 47);
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(smlad_inst));
@@ -6202,14 +6290,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SUB_INST:
{
- sub_inst *inst_cream = (sub_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- if (inst_cream->Rn == 15) {
- lop += 8;
- }
- rop = SHIFTER_OPERAND;
- RD = dst = lop - rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sub_inst* const inst_cream = (sub_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 8;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -6217,10 +6308,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop);
- UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sub_inst));
@@ -6267,6 +6358,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
addr = RN;
unsigned int value = Memory::Read8(addr);
Memory::Write8(addr, (RM & 0xFF));
+ RD = value;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(swp_inst));
@@ -6347,18 +6439,19 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
TEQ_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- teq_inst *inst_cream = (teq_inst *)inst_base->component;
- lop = RN;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ teq_inst* const inst_cream = (teq_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += GET_INST_SIZE(cpu) * 2;
- rop = SHIFTER_OPERAND;
- dst = lop ^ rop;
+ u32 result = lop ^ rop;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
UPDATE_CFLAG_WITH_SC;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6368,18 +6461,19 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
TST_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- tst_inst *inst_cream = (tst_inst *)inst_base->component;
- lop = RN;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ tst_inst* const inst_cream = (tst_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += GET_INST_SIZE(cpu) * 2;
- rop = SHIFTER_OPERAND;
- dst = lop & rop;
+ u32 result = lop & rop;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
UPDATE_CFLAG_WITH_SC;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6643,10 +6737,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
- const u32 rm = RM;
- const u32 rn = RN;
- const u32 rd_lo = RDLO;
- const u32 rd_hi = RDHI;
+ const u64 rm = RM;
+ const u64 rn = RN;
+ const u64 rd_lo = RDLO;
+ const u64 rd_hi = RDHI;
const u64 result = (rm * rn) + rd_lo + rd_hi;
RDLO = (result & 0xFFFFFFFF);
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index 80ebc359e..c76d371a2 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -4,6 +4,8 @@
#include "core/arm/interpreter/arm_interpreter.h"
+#include "core/core.h"
+
const static cpu_config_t arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
@@ -75,7 +77,7 @@ void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
ARMul_Emulate32(state);
}
-void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
+void ARM_Interpreter::SaveContext(Core::ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@@ -91,7 +93,7 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
ctx.mode = state->NextInstr;
}
-void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
+void ARM_Interpreter::LoadContext(const Core::ThreadContext& ctx) {
memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index 019dad5df..e5ecc69c2 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -70,13 +70,13 @@ public:
* Saves the current CPU context
* @param ctx Thread context to save
*/
- void SaveContext(ThreadContext& ctx) override;
+ void SaveContext(Core::ThreadContext& ctx) override;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
- void LoadContext(const ThreadContext& ctx) override;
+ void LoadContext(const Core::ThreadContext& ctx) override;
/// Prepare core for thread reschedule (if needed to correctly handle state)
void PrepareReschedule() override;
diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp
index 68ac2a0ce..e2626eefb 100644
--- a/src/core/arm/interpreter/armsupp.cpp
+++ b/src/core/arm/interpreter/armsupp.cpp
@@ -418,6 +418,22 @@ ARMul_NegZero (ARMul_State * state, ARMword result)
}
}
+// Add with carry, indicates if a carry-out or signed overflow occurred.
+u32 AddWithCarry(u32 left, u32 right, u32 carry_in, bool* carry_out_occurred, bool* overflow_occurred)
+{
+ u64 unsigned_sum = (u64)left + (u64)right + (u64)carry_in;
+ s64 signed_sum = (s64)(s32)left + (s64)(s32)right + (s64)carry_in;
+ u64 result = (unsigned_sum & 0xFFFFFFFF);
+
+ if (carry_out_occurred)
+ *carry_out_occurred = (result != unsigned_sum);
+
+ if (overflow_occurred)
+ *overflow_occurred = ((s64)(s32)result != signed_sum);
+
+ return (u32)result;
+}
+
// Compute whether an addition of A and B, giving RESULT, overflowed.
bool AddOverflow(ARMword a, ARMword b, ARMword result)
{
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 1b2cef451..560b51a9f 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -795,6 +795,7 @@ extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
+extern u32 AddWithCarry(u32, u32, u32, bool*, bool*);
extern bool ARMul_AddOverflowQ(ARMword, ARMword);
extern u8 ARMul_SignedSaturatedAdd8(u8, u8);
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
index a9df490ba..272ca99f8 100644
--- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
@@ -1400,7 +1400,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
u32 except;
char type;
- type = fop->flags & OP_SD ? 's' : 'd';
+ type = (fop->flags & OP_SD) ? 's' : 'd';
if (op == FOP_EXT)
pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n",
vecitr >> FPSCR_LENGTH_BIT,
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index 08d0d719f..b7872bdc4 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -959,70 +959,34 @@ vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_s
static u32
vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func)
{
-
- {
- struct vfp_single vsd, vsp, vsn, vsm;
- u32 exceptions;
- s32 v;
-
-
-
- v = vfp_get_float(state, sn);
- pr_debug("VFP: s%u = %08x\n", sn, v);
- vfp_single_unpack(&vsn, v);
- if (vsn.exponent == 0 && vsn.significand)
- vfp_single_normalise_denormal(&vsn);
-
- vfp_single_unpack(&vsm, m);
- if (vsm.exponent == 0 && vsm.significand)
- vfp_single_normalise_denormal(&vsm);
-
- exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
-
- if (negate & NEG_MULTIPLY)
- vsp.sign = vfp_sign_negate(vsp.sign);
-
- v = vfp_get_float(state, sd);
- pr_debug("VFP: s%u = %08x\n", sd, v);
- vfp_single_unpack(&vsn, v);
- if (negate & NEG_SUBTRACT)
- vsn.sign = vfp_sign_negate(vsn.sign);
-
- exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
-
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
- }
-
- struct vfp_double vsd, vsp, vsn, vsm;
+ vfp_single vsd, vsp, vsn, vsm;
u32 exceptions;
s32 v;
- s64 vd;
- s64 md;
v = vfp_get_float(state, sn);
- vd = vfp_single_to_doubleintern(state, v, fpscr);
- vfp_double_unpack(&vsn, vd);
+ pr_debug("VFP: s%u = %08x\n", sn, v);
+ vfp_single_unpack(&vsn, v);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
- md = vfp_single_to_doubleintern(state, m, fpscr);
- vfp_double_unpack(&vsm, md);
+ exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
- exceptions = vfp_double_multiply(&vsp, &vsn, &vsm, fpscr);
if (negate & NEG_MULTIPLY)
vsp.sign = vfp_sign_negate(vsp.sign);
v = vfp_get_float(state, sd);
- vd = vfp_single_to_doubleintern(state, v, fpscr);
- vfp_double_unpack(&vsn, vd);
-
+ pr_debug("VFP: s%u = %08x\n", sd, v);
+ vfp_single_unpack(&vsn, v);
if (negate & NEG_SUBTRACT)
vsn.sign = vfp_sign_negate(vsn.sign);
- exceptions |= vfp_double_add(&vsd, &vsn, &vsp, fpscr);
-
- s64 debug = vfp_double_pack(&vsd);
-
- return vfp_double_fcvtsinterncutting(state, sd, &vsd, fpscr);
+ exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
}
/*
@@ -1326,7 +1290,7 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
u32 except;
char type;
- type = fop->flags & OP_DD ? 'd' : 's';
+ type = (fop->flags & OP_DD) ? 'd' : 's';
if (op == FOP_EXT)
pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n",
vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8ac4481cc..e9e5c35cc 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -5,8 +5,10 @@
#include "common/common_types.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/settings.h"
+#include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h"
#include "core/arm/interpreter/arm_interpreter.h"
#include "core/arm/dyncom/arm_dyncom.h"
@@ -16,14 +18,22 @@
namespace Core {
-static u64 last_ticks = 0; ///< Last CPU ticks
-static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
- g_app_core->Run(tight_loop);
+ // If the current thread is an idle thread, then don't execute instructions,
+ // instead advance to the next event and try to yield to the next thread
+ if (Kernel::GetCurrentThread()->IsIdle()) {
+ LOG_TRACE(Core_ARM11, "Idling");
+ CoreTiming::Idle();
+ CoreTiming::Advance();
+ HLE::Reschedule(__func__);
+ } else {
+ g_app_core->Run(tight_loop);
+ }
+
HW::Update();
if (HLE::g_reschedule) {
Kernel::Reschedule();
@@ -49,7 +59,6 @@ void Stop() {
int Init() {
LOG_DEBUG(Core, "initialized OK");
- disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
switch (Settings::values.cpu_core) {
@@ -62,13 +71,10 @@ int Init() {
break;
}
- last_ticks = Core::g_app_core->GetTicks();
-
return 0;
}
void Shutdown() {
- delete disasm;
delete g_app_core;
delete g_sys_core;
diff --git a/src/core/core.h b/src/core/core.h
index ecd58a73a..2f5e8bc6b 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,8 +4,9 @@
#pragma once
-#include "core/arm/arm_interface.h"
-#include "core/arm/skyeye_common/armdefs.h"
+#include "common/common_types.h"
+
+class ARM_Interface;
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -16,6 +17,21 @@ enum CPUCore {
CPU_OldInterpreter,
};
+struct ThreadContext {
+ u32 cpu_registers[13];
+ u32 sp;
+ u32 lr;
+ u32 pc;
+ u32 cpsr;
+ u32 fpu_registers[32];
+ u32 fpscr;
+ u32 fpexc;
+
+ // These are not part of native ThreadContext, but needed by emu
+ u32 reg_15;
+ u32 mode;
+};
+
extern ARM_Interface* g_app_core; ///< ARM11 application core
extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 321648b37..3aebd7e9d 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,16 +1,16 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <vector>
-#include <cstdio>
#include <atomic>
+#include <cstdio>
#include <mutex>
+#include <vector>
#include "common/chunk_file.h"
-#include "common/msg_handler.h"
-#include "common/string_util.h"
+#include "common/log.h"
+#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -22,281 +22,265 @@ int g_clock_rate_arm11 = 268123480;
namespace CoreTiming
{
-
struct EventType
{
EventType() {}
- EventType(TimedCallback cb, const char *n)
+ EventType(TimedCallback cb, const char* n)
: callback(cb), name(n) {}
TimedCallback callback;
- const char *name;
+ const char* name;
};
-std::vector<EventType> event_types;
+static std::vector<EventType> event_types;
struct BaseEvent
{
s64 time;
u64 userdata;
int type;
- // Event *next;
};
typedef LinkedListItem<BaseEvent> Event;
-Event *first;
-Event *tsFirst;
-Event *tsLast;
+static Event* first;
+static Event* ts_first;
+static Event* ts_last;
// event pools
-Event *eventPool = 0;
-Event *eventTsPool = 0;
-int allocatedTsEvents = 0;
+static Event* event_pool = nullptr;
+static Event* event_ts_pool = nullptr;
+static int allocated_ts_events = 0;
// Optimization to skip MoveEvents when possible.
-std::atomic<u32> hasTsEvents;
+static std::atomic<bool> has_ts_events(false);
-// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block
-// as we can already reach that structure through a register.
-int slicelength;
+int g_slice_length;
-MEMORY_ALIGNED16(s64) globalTimer;
-s64 idledCycles;
+static s64 global_timer;
+static s64 idled_cycles;
+static s64 last_global_time_ticks;
+static s64 last_global_time_us;
-static std::recursive_mutex externalEventSection;
+static std::recursive_mutex external_event_section;
// Warning: not included in save state.
-void(*advanceCallback)(int cyclesExecuted) = nullptr;
+using AdvanceCallback = void(int cycles_executed);
+static AdvanceCallback* advance_callback = nullptr;
+static std::vector<MHzChangeCallback> mhz_change_callbacks;
-void SetClockFrequencyMHz(int cpuMhz)
-{
- g_clock_rate_arm11 = cpuMhz * 1000000;
+void FireMhzChange() {
+ for (auto callback : mhz_change_callbacks)
+ callback();
+}
+
+void SetClockFrequencyMHz(int cpu_mhz) {
+ // When the mhz changes, we keep track of what "time" it was before hand.
+ // This way, time always moves forward, even if mhz is changed.
+ last_global_time_us = GetGlobalTimeUs();
+ last_global_time_ticks = GetTicks();
+
+ g_clock_rate_arm11 = cpu_mhz * 1000000;
// TODO: Rescale times of scheduled events?
+
+ FireMhzChange();
}
-int GetClockFrequencyMHz()
-{
+int GetClockFrequencyMHz() {
return g_clock_rate_arm11 / 1000000;
}
+u64 GetGlobalTimeUs() {
+ s64 ticks_since_last = GetTicks() - last_global_time_ticks;
+ int freq = GetClockFrequencyMHz();
+ s64 us_since_last = ticks_since_last / freq;
+ return last_global_time_us + us_since_last;
+}
-Event* GetNewEvent()
-{
- if (!eventPool)
+Event* GetNewEvent() {
+ if (!event_pool)
return new Event;
- Event* ev = eventPool;
- eventPool = ev->next;
- return ev;
+ Event* event = event_pool;
+ event_pool = event->next;
+ return event;
}
-Event* GetNewTsEvent()
-{
- allocatedTsEvents++;
+Event* GetNewTsEvent() {
+ allocated_ts_events++;
- if (!eventTsPool)
+ if (!event_ts_pool)
return new Event;
- Event* ev = eventTsPool;
- eventTsPool = ev->next;
- return ev;
+ Event* event = event_ts_pool;
+ event_ts_pool = event->next;
+ return event;
}
-void FreeEvent(Event* ev)
-{
- ev->next = eventPool;
- eventPool = ev;
+void FreeEvent(Event* event) {
+ event->next = event_pool;
+ event_pool = event;
}
-void FreeTsEvent(Event* ev)
-{
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
+void FreeTsEvent(Event* event) {
+ event->next = event_ts_pool;
+ event_ts_pool = event;
+ allocated_ts_events--;
}
-int RegisterEvent(const char *name, TimedCallback callback)
-{
+int RegisterEvent(const char* name, TimedCallback callback) {
event_types.push_back(EventType(callback, name));
return (int)event_types.size() - 1;
}
-void AntiCrashCallback(u64 userdata, int cyclesLate)
-{
- LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
+void AntiCrashCallback(u64 userdata, int cycles_late) {
+ LOG_CRITICAL(Core_Timing, "Savestate broken: an unregistered event was called.");
Core::Halt("invalid timing events");
}
-void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback)
-{
+void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callback) {
if (event_type >= (int)event_types.size())
event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT"));
event_types[event_type] = EventType(callback, name);
}
-void UnregisterAllEvents()
-{
+void UnregisterAllEvents() {
if (first)
PanicAlert("Cannot unregister events with events pending");
event_types.clear();
}
-void Init()
-{
- //currentMIPS->downcount = INITIAL_SLICE_LENGTH;
- //slicelength = INITIAL_SLICE_LENGTH;
- globalTimer = 0;
- idledCycles = 0;
- hasTsEvents = 0;
+void Init() {
+ Core::g_app_core->down_count = INITIAL_SLICE_LENGTH;
+ g_slice_length = INITIAL_SLICE_LENGTH;
+ global_timer = 0;
+ idled_cycles = 0;
+ last_global_time_ticks = 0;
+ last_global_time_us = 0;
+ has_ts_events = 0;
+ mhz_change_callbacks.clear();
}
-void Shutdown()
-{
+void Shutdown() {
MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
- while (eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- delete ev;
+ while (event_pool) {
+ Event* event = event_pool;
+ event_pool = event->next;
+ delete event;
}
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- while (eventTsPool)
- {
- Event *ev = eventTsPool;
- eventTsPool = ev->next;
- delete ev;
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ while (event_ts_pool) {
+ Event* event = event_ts_pool;
+ event_ts_pool = event->next;
+ delete event;
}
}
-u64 GetTicks()
-{
- LOG_ERROR(Core, "Unimplemented function!");
- return 0;
- //return (u64)globalTimer + slicelength - currentMIPS->downcount;
+u64 GetTicks() {
+ return (u64)global_timer + g_slice_length - Core::g_app_core->down_count;
}
-u64 GetIdleTicks()
-{
- return (u64)idledCycles;
+u64 GetIdleTicks() {
+ return (u64)idled_cycles;
}
// This is to be called when outside threads, such as the graphics thread, wants to
// schedule things to be executed on the main thread.
-void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- Event *ne = GetNewTsEvent();
- ne->time = GetTicks() + cyclesIntoFuture;
- ne->type = event_type;
- ne->next = 0;
- ne->userdata = userdata;
- if (!tsFirst)
- tsFirst = ne;
- if (tsLast)
- tsLast->next = ne;
- tsLast = ne;
-
- hasTsEvents.store(1, std::memory_order_release);
+void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata) {
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ Event* new_event = GetNewTsEvent();
+ new_event->time = GetTicks() + cycles_into_future;
+ new_event->type = event_type;
+ new_event->next = 0;
+ new_event->userdata = userdata;
+ if (!ts_first)
+ ts_first = new_event;
+ if (ts_last)
+ ts_last->next = new_event;
+ ts_last = new_event;
+
+ has_ts_events = true;
}
// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread
// in which case the event will get handled immediately, before returning.
-void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata)
-{
+void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) {
if (false) //Core::IsCPUThread())
{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
event_types[event_type].callback(userdata, 0);
}
else
ScheduleEvent_Threadsafe(0, event_type, userdata);
}
-void ClearPendingEvents()
-{
- while (first)
- {
- Event *e = first->next;
+void ClearPendingEvents() {
+ while (first) {
+ Event* event = first->next;
FreeEvent(first);
- first = e;
+ first = event;
}
}
-void AddEventToQueue(Event* ne)
-{
- Event* prev = nullptr;
- Event** pNext = &first;
- for (;;)
- {
- Event*& next = *pNext;
- if (!next || ne->time < next->time)
- {
- ne->next = next;
- next = ne;
+void AddEventToQueue(Event* new_event) {
+ Event* prev_event = nullptr;
+ Event** next_event = &first;
+ for (;;) {
+ Event*& next = *next_event;
+ if (!next || new_event->time < next->time) {
+ new_event->next = next;
+ next = new_event;
break;
}
- prev = next;
- pNext = &prev->next;
+ prev_event = next;
+ next_event = &prev_event->next;
}
}
-// This must be run ONLY from within the cpu thread
-// cyclesIntoFuture may be VERY inaccurate if called from anything else
-// than Advance
-void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
-{
- Event *ne = GetNewEvent();
- ne->userdata = userdata;
- ne->type = event_type;
- ne->time = GetTicks() + cyclesIntoFuture;
- AddEventToQueue(ne);
+void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata) {
+ Event* new_event = GetNewEvent();
+ new_event->userdata = userdata;
+ new_event->type = event_type;
+ new_event->time = GetTicks() + cycles_into_future;
+ AddEventToQueue(new_event);
}
-// Returns cycles left in timer.
-s64 UnscheduleEvent(int event_type, u64 userdata)
-{
+s64 UnscheduleEvent(int event_type, u64 userdata) {
s64 result = 0;
if (!first)
return result;
- while (first)
- {
- if (first->type == event_type && first->userdata == userdata)
- {
- result = first->time - globalTimer;
+ while (first) {
+ if (first->type == event_type && first->userdata == userdata) {
+ result = first->time - GetTicks();
- Event *next = first->next;
+ Event* next = first->next;
FreeEvent(first);
first = next;
- }
- else
- {
+ } else {
break;
}
}
if (!first)
return result;
- Event *prev = first;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type && ptr->userdata == userdata)
- {
- result = ptr->time - globalTimer;
- prev->next = ptr->next;
+ Event* prev_event = first;
+ Event* ptr = prev_event->next;
+
+ while (ptr) {
+ if (ptr->type == event_type && ptr->userdata == userdata) {
+ result = ptr->time - GetTicks();
+
+ prev_event->next = ptr->next;
FreeEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
+ ptr = prev_event->next;
+ } else {
+ prev_event = ptr;
ptr = ptr->next;
}
}
@@ -304,51 +288,44 @@ s64 UnscheduleEvent(int event_type, u64 userdata)
return result;
}
-s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
-{
+s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) {
s64 result = 0;
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- if (!tsFirst)
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ if (!ts_first)
return result;
- while (tsFirst)
- {
- if (tsFirst->type == event_type && tsFirst->userdata == userdata)
- {
- result = tsFirst->time - globalTimer;
- Event *next = tsFirst->next;
- FreeTsEvent(tsFirst);
- tsFirst = next;
- }
- else
- {
+ while (ts_first) {
+ if (ts_first->type == event_type && ts_first->userdata == userdata) {
+ result = ts_first->time - GetTicks();
+
+ Event* next = ts_first->next;
+ FreeTsEvent(ts_first);
+ ts_first = next;
+ } else {
break;
}
}
- if (!tsFirst)
+
+ if (!ts_first)
{
- tsLast = nullptr;
+ ts_last = nullptr;
return result;
}
- Event *prev = tsFirst;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type && ptr->userdata == userdata)
- {
- result = ptr->time - globalTimer;
-
- prev->next = ptr->next;
- if (ptr == tsLast)
- tsLast = prev;
- FreeTsEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
+ Event* prev_event = ts_first;
+ Event* next = prev_event->next;
+ while (next) {
+ if (next->type == event_type && next->userdata == userdata) {
+ result = next->time - GetTicks();
+
+ prev_event->next = next->next;
+ if (next == ts_last)
+ ts_last = prev_event;
+ FreeTsEvent(next);
+ next = prev_event->next;
+ } else {
+ prev_event = next;
+ next = next->next;
}
}
@@ -356,271 +333,217 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
}
// Warning: not included in save state.
-void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted))
-{
- advanceCallback = callback;
+void RegisterAdvanceCallback(AdvanceCallback* callback) {
+ advance_callback = callback;
}
-bool IsScheduled(int event_type)
-{
+void RegisterMHzChangeCallback(MHzChangeCallback callback) {
+ mhz_change_callbacks.push_back(callback);
+}
+
+bool IsScheduled(int event_type) {
if (!first)
return false;
- Event *e = first;
- while (e) {
- if (e->type == event_type)
+ Event* event = first;
+ while (event) {
+ if (event->type == event_type)
return true;
- e = e->next;
+ event = event->next;
}
return false;
}
-void RemoveEvent(int event_type)
-{
+void RemoveEvent(int event_type) {
if (!first)
return;
- while (first)
- {
- if (first->type == event_type)
- {
+ while (first) {
+ if (first->type == event_type) {
Event *next = first->next;
FreeEvent(first);
first = next;
- }
- else
- {
+ } else {
break;
}
}
if (!first)
return;
- Event *prev = first;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type)
- {
- prev->next = ptr->next;
- FreeEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
+ Event* prev = first;
+ Event* next = prev->next;
+ while (next) {
+ if (next->type == event_type) {
+ prev->next = next->next;
+ FreeEvent(next);
+ next = prev->next;
+ } else {
+ prev = next;
+ next = next->next;
}
}
}
-void RemoveThreadsafeEvent(int event_type)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- if (!tsFirst)
- {
+void RemoveThreadsafeEvent(int event_type) {
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ if (!ts_first)
return;
- }
- while (tsFirst)
- {
- if (tsFirst->type == event_type)
- {
- Event *next = tsFirst->next;
- FreeTsEvent(tsFirst);
- tsFirst = next;
- }
- else
- {
+
+ while (ts_first) {
+ if (ts_first->type == event_type) {
+ Event* next = ts_first->next;
+ FreeTsEvent(ts_first);
+ ts_first = next;
+ } else {
break;
}
}
- if (!tsFirst)
- {
- tsLast = nullptr;
+
+ if (!ts_first) {
+ ts_last = nullptr;
return;
}
- Event *prev = tsFirst;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type)
- {
- prev->next = ptr->next;
- if (ptr == tsLast)
- tsLast = prev;
- FreeTsEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
+
+ Event* prev = ts_first;
+ Event* next = prev->next;
+ while (next) {
+ if (next->type == event_type) {
+ prev->next = next->next;
+ if (next == ts_last)
+ ts_last = prev;
+ FreeTsEvent(next);
+ next = prev->next;
+ } else {
+ prev = next;
+ next = next->next;
}
}
}
-void RemoveAllEvents(int event_type)
-{
+void RemoveAllEvents(int event_type) {
RemoveThreadsafeEvent(event_type);
RemoveEvent(event_type);
}
-//This raise only the events required while the fifo is processing data
-void ProcessFifoWaitEvents()
-{
- while (first)
- {
- if (first->time <= globalTimer)
- {
- //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
- // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
+// This raise only the events required while the fifo is processing data
+void ProcessFifoWaitEvents() {
+ while (first) {
+ if (first->time <= (s64)GetTicks()) {
Event* evt = first;
first = first->next;
- event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
+ event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time));
FreeEvent(evt);
- }
- else
- {
+ } else {
break;
}
}
}
-void MoveEvents()
-{
- hasTsEvents.store(0, std::memory_order_release);
+void MoveEvents() {
+ has_ts_events = false;
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
// Move events from async queue into main queue
- while (tsFirst)
- {
- Event *next = tsFirst->next;
- AddEventToQueue(tsFirst);
- tsFirst = next;
+ while (ts_first) {
+ Event* next = ts_first->next;
+ AddEventToQueue(ts_first);
+ ts_first = next;
}
- tsLast = nullptr;
+ ts_last = nullptr;
// Move free events to threadsafe pool
- while (allocatedTsEvents > 0 && eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
+ while (allocated_ts_events > 0 && event_pool) {
+ Event* event = event_pool;
+ event_pool = event->next;
+ event->next = event_ts_pool;
+ event_ts_pool = event;
+ allocated_ts_events--;
}
}
-void Advance()
-{
- LOG_ERROR(Core, "Unimplemented function!");
- //int cyclesExecuted = slicelength - currentMIPS->downcount;
- //globalTimer += cyclesExecuted;
- //currentMIPS->downcount = slicelength;
-
- //if (Common::AtomicLoadAcquire(hasTsEvents))
- // MoveEvents();
- //ProcessFifoWaitEvents();
-
- //if (!first)
- //{
- // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
- // currentMIPS->downcount += 10000;
- //}
- //else
- //{
- // slicelength = (int)(first->time - globalTimer);
- // if (slicelength > MAX_SLICE_LENGTH)
- // slicelength = MAX_SLICE_LENGTH;
- // currentMIPS->downcount = slicelength;
- //}
- //if (advanceCallback)
- // advanceCallback(cyclesExecuted);
-}
-
-void LogPendingEvents()
-{
- Event *ptr = first;
- while (ptr)
- {
- //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type);
- ptr = ptr->next;
- }
+void ForceCheck() {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ global_timer += cycles_executed;
+ // This will cause us to check for new events immediately.
+ Core::g_app_core->down_count = 0;
+ // But let's not eat a bunch more time in Advance() because of this.
+ g_slice_length = 0;
}
-void Idle(int maxIdle)
-{
- LOG_ERROR(Core, "Unimplemented function!");
- //int cyclesDown = currentMIPS->downcount;
- //if (maxIdle != 0 && cyclesDown > maxIdle)
- // cyclesDown = maxIdle;
-
- //if (first && cyclesDown > 0)
- //{
- // int cyclesExecuted = slicelength - currentMIPS->downcount;
- // int cyclesNextEvent = (int) (first->time - globalTimer);
-
- // if (cyclesNextEvent < cyclesExecuted + cyclesDown)
- // {
- // cyclesDown = cyclesNextEvent - cyclesExecuted;
- // // Now, now... no time machines, please.
- // if (cyclesDown < 0)
- // cyclesDown = 0;
- // }
- //}
-
- //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f));
-
- //idledCycles += cyclesDown;
- //currentMIPS->downcount -= cyclesDown;
- //if (currentMIPS->downcount == 0)
- // currentMIPS->downcount = -1;
-}
-
-std::string GetScheduledEventsSummary()
-{
- Event *ptr = first;
- std::string text = "Scheduled events\n";
- text.reserve(1000);
- while (ptr)
- {
- unsigned int t = ptr->type;
- if (t >= event_types.size())
- PanicAlert("Invalid event type"); // %i", t);
- const char *name = event_types[ptr->type].name;
- if (!name)
- name = "[unknown]";
+void Advance() {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ global_timer += cycles_executed;
+ Core::g_app_core->down_count = g_slice_length;
- text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)ptr->time,
- (u32)(ptr->userdata >> 32), (u32)(ptr->userdata));
+ if (has_ts_events)
+ MoveEvents();
+ ProcessFifoWaitEvents();
- ptr = ptr->next;
+ if (!first) {
+ if (g_slice_length < 10000) {
+ g_slice_length += 10000;
+ Core::g_app_core->down_count += g_slice_length;
+ }
+ } else {
+ // Note that events can eat cycles as well.
+ int target = (int)(first->time - global_timer);
+ if (target > MAX_SLICE_LENGTH)
+ target = MAX_SLICE_LENGTH;
+
+ const int diff = target - g_slice_length;
+ g_slice_length += diff;
+ Core::g_app_core->down_count += diff;
}
- return text;
+ if (advance_callback)
+ advance_callback(cycles_executed);
}
-void Event_DoState(PointerWrap &p, BaseEvent *ev)
-{
- p.Do(*ev);
+void LogPendingEvents() {
+ Event* event = first;
+ while (event) {
+ //LOG_TRACE(Core_Timing, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, next->time, next->type);
+ event = event->next;
+ }
}
-void DoState(PointerWrap &p)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+void Idle(int max_idle) {
+ int cycles_down = Core::g_app_core->down_count;
+ if (max_idle != 0 && cycles_down > max_idle)
+ cycles_down = max_idle;
- auto s = p.Section("CoreTiming", 1);
- if (!s)
- return;
+ if (first && cycles_down > 0) {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ int cycles_next_event = (int)(first->time - global_timer);
- int n = (int)event_types.size();
- p.Do(n);
- // These (should) be filled in later by the modules.
- event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
+ if (cycles_next_event < cycles_executed + cycles_down) {
+ cycles_down = cycles_next_event - cycles_executed;
+ // Now, now... no time machines, please.
+ if (cycles_down < 0)
+ cycles_down = 0;
+ }
+ }
- p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
- p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
+ LOG_TRACE(Core_Timing, "Idle for %i cycles! (%f ms)", cycles_down, cycles_down / (float)(g_clock_rate_arm11 * 0.001f));
- p.Do(g_clock_rate_arm11);
- p.Do(slicelength);
- p.Do(globalTimer);
- p.Do(idledCycles);
+ idled_cycles += cycles_down;
+ Core::g_app_core->down_count -= cycles_down;
+ if (Core::g_app_core->down_count == 0)
+ Core::g_app_core->down_count = -1;
+}
+
+std::string GetScheduledEventsSummary() {
+ Event* event = first;
+ std::string text = "Scheduled events\n";
+ text.reserve(1000);
+ while (event) {
+ unsigned int t = event->type;
+ if (t >= event_types.size())
+ PanicAlert("Invalid event type"); // %i", t);
+ const char* name = event_types[event->type].name;
+ if (!name)
+ name = "[unknown]";
+ text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)event->time,
+ (u32)(event->userdata >> 32), (u32)(event->userdata));
+ event = event->next;
+ }
+ return text;
}
} // namespace
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 496234538..d62ff3604 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -1,9 +1,11 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
+#include <string>
+
// This is a system to schedule events into the emulated machine's future. Time is measured
// in main CPU clock cycles.
@@ -12,14 +14,14 @@
// See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler.
-// The int cyclesLate that the callbacks get is how many cycles late it was.
+// The int cycles_late that the callbacks get is how many cycles late it was.
// So to schedule a new event on a regular basis:
// inside callback:
-// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
+// ScheduleEvent(periodInCycles - cycles_late, callback, "whatever")
-#include "common/common.h"
+#include <functional>
-class PointerWrap;
+#include "common/common.h"
extern int g_clock_rate_arm11;
@@ -55,55 +57,84 @@ inline s64 cyclesToUs(s64 cycles) {
return cycles / (g_clock_rate_arm11 / 1000000);
}
-namespace CoreTiming {
+inline u64 cyclesToMs(s64 cycles) {
+ return cycles / (g_clock_rate_arm11 / 1000);
+}
+namespace CoreTiming
+{
void Init();
void Shutdown();
-typedef void(*TimedCallback)(u64 userdata, int cyclesLate);
+typedef void(*MHzChangeCallback)();
+typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
u64 GetTicks();
u64 GetIdleTicks();
-
-// Returns the event_type identifier.
-int RegisterEvent(const char *name, TimedCallback callback);
-// For save states.
+u64 GetGlobalTimeUs();
+
+/**
+ * Registers an event type with the specified name and callback
+ * @param name Name of the event type
+ * @param callback Function that will execute when this event fires
+ * @returns An identifier for the event type that was registered
+ */
+int RegisterEvent(const char* name, TimedCallback callback);
+/// For save states.
void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback);
void UnregisterAllEvents();
-// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
-// when we implement state saves.
-void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata = 0);
-void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata = 0);
+/// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
+/// when we implement state saves.
+/**
+ * Schedules an event to run after the specified number of cycles,
+ * with an optional parameter to be passed to the callback handler.
+ * This must be run ONLY from within the cpu thread.
+ * @param cycles_into_future The number of cycles after which this event will be fired
+ * @param event_type The event type to fire, as returned from RegisterEvent
+ * @param userdata Optional parameter to pass to the callback when fired
+ */
+void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0);
+
+void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata = 0);
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0);
+
+/**
+ * Unschedules an event with the specified type and userdata
+ * @param event_type The type of event to unschedule, as returned from RegisterEvent
+ * @param userdata The userdata that identifies this event, as passed to ScheduleEvent
+ * @returns The remaining ticks until the next invocation of the event callback
+ */
s64 UnscheduleEvent(int event_type, u64 userdata);
+
s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata);
void RemoveEvent(int event_type);
void RemoveThreadsafeEvent(int event_type);
void RemoveAllEvents(int event_type);
bool IsScheduled(int event_type);
+/// Runs any pending events and updates downcount for the next slice of cycles
void Advance();
void MoveEvents();
void ProcessFifoWaitEvents();
+void ForceCheck();
-// Pretend that the main CPU has executed enough cycles to reach the next event.
+/// Pretend that the main CPU has executed enough cycles to reach the next event.
void Idle(int maxIdle = 0);
-// Clear all pending events. This should ONLY be done on exit or state load.
+/// Clear all pending events. This should ONLY be done on exit or state load.
void ClearPendingEvents();
void LogPendingEvents();
-// Warning: not included in save states.
-void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted));
+/// Warning: not included in save states.
+void RegisterAdvanceCallback(void(*callback)(int cycles_executed));
+void RegisterMHzChangeCallback(MHzChangeCallback callback);
std::string GetScheduledEventsSummary();
-void DoState(PointerWrap &p);
-
-void SetClockFrequencyMHz(int cpuMhz);
+void SetClockFrequencyMHz(int cpu_mhz);
int GetClockFrequencyMHz();
-extern int slicelength;
+extern int g_slice_length;
} // namespace
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index 4759ef3ae..0805f42ae 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -9,6 +9,7 @@
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -19,15 +20,22 @@ namespace FileSys {
static std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) {
std::vector<u8> vec_data = path.AsBinary();
const u32* data = reinterpret_cast<const u32*>(vec_data.data());
- u32 media_type = data[0];
u32 save_low = data[1];
u32 save_high = data[2];
- return Common::StringFromFormat("%s%s/%08X/%08X/", mount_point.c_str(), media_type == 0 ? "nand" : "sdmc", save_high, save_low);
+ return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_high, save_low);
}
-Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_point)
- : DiskArchive(mount_point), concrete_mount_point(mount_point) {
- LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", this->mount_point.c_str());
+static std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) {
+ if (shared)
+ return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), SYSTEM_ID.c_str());
+
+ return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(),
+ SYSTEM_ID.c_str(), SDCARD_ID.c_str());
+}
+
+Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_location, bool shared)
+ : DiskArchive(GetExtDataContainerPath(mount_location, shared)) {
+ LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
}
bool Archive_ExtSaveData::Initialize() {
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
index a3a144799..fb7f209d2 100644
--- a/src/core/file_sys/archive_extsavedata.h
+++ b/src/core/file_sys/archive_extsavedata.h
@@ -17,7 +17,7 @@ namespace FileSys {
/// File system interface to the ExtSaveData archive
class Archive_ExtSaveData final : public DiskArchive {
public:
- Archive_ExtSaveData(const std::string& mount_point);
+ Archive_ExtSaveData(const std::string& mount_point, bool shared);
/**
* Initialize the archive.
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
index 280d4ff5d..3baee5294 100644
--- a/src/core/file_sys/archive_savedata.cpp
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -9,6 +9,7 @@
#include "core/file_sys/archive_savedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -16,14 +17,25 @@
namespace FileSys {
-Archive_SaveData::Archive_SaveData(const std::string& mount_point)
- : DiskArchive(mount_point) {
+static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
+ return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(),
+ SYSTEM_ID.c_str(), SDCARD_ID.c_str());
+}
+
+static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
+ u32 high = program_id >> 32;
+ u32 low = program_id & 0xFFFFFFFF;
+ return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low);
+}
+
+Archive_SaveData::Archive_SaveData(const std::string& sdmc_directory)
+ : DiskArchive(GetSaveDataContainerPath(sdmc_directory)) {
LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
}
ResultCode Archive_SaveData::Open(const Path& path) {
if (concrete_mount_point.empty())
- concrete_mount_point = Common::StringFromFormat("%s%016X", mount_point.c_str(), Kernel::g_program_id) + DIR_SEP;
+ concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id);
if (!FileUtil::Exists(concrete_mount_point)) {
// When a SaveData archive is created for the first time, it is not yet formatted
// and the save file/directory structure expected by the game has not yet been initialized.
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp
index 233158a0c..a7a507536 100644
--- a/src/core/file_sys/archive_savedatacheck.cpp
+++ b/src/core/file_sys/archive_savedatacheck.cpp
@@ -5,13 +5,24 @@
#include "common/file_util.h"
#include "core/file_sys/archive_savedatacheck.h"
+#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
-Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& mount_loc) : mount_point(mount_loc) {
+static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) {
+ return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str());
+}
+
+static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) {
+ return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs",
+ mount_point.c_str(), high, low);
+}
+
+Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& nand_directory) :
+ mount_point(GetSaveDataCheckContainerPath(nand_directory)) {
}
ResultCode Archive_SaveDataCheck::Open(const Path& path) {
@@ -23,7 +34,7 @@ ResultCode Archive_SaveDataCheck::Open(const Path& path) {
// this archive again with a different path, will corrupt the previously open file.
auto vec = path.AsBinary();
const u32* data = reinterpret_cast<u32*>(vec.data());
- std::string file_path = Common::StringFromFormat("%s%08x%08x.bin", mount_point.c_str(), data[1], data[0]);
+ std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]);
FileUtil::IOFile file(file_path, "rb");
std::fill(raw_data.begin(), raw_data.end(), 0);
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 1c1c170b6..26b03e82f 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -16,8 +16,8 @@
namespace FileSys {
-Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
- LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
+Archive_SDMC::Archive_SDMC(const std::string& sdmc_directory) : DiskArchive(sdmc_directory) {
+ LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
}
bool Archive_SDMC::Initialize() {
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 0da32d510..c2a5d641a 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -9,6 +9,7 @@
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -22,8 +23,12 @@ static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 sav
return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
}
+static std::string GetSystemSaveDataContainerPath(const std::string& mount_point) {
+ return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str());
+}
+
Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id)
- : DiskArchive(GetSystemSaveDataPath(mount_point, save_id)) {
+ : DiskArchive(GetSystemSaveDataPath(GetSystemSaveDataContainerPath(mount_point), save_id)) {
LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str());
}
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
index 55d85193c..c8f5845ca 100644
--- a/src/core/file_sys/archive_systemsavedata.h
+++ b/src/core/file_sys/archive_systemsavedata.h
@@ -15,8 +15,6 @@
namespace FileSys {
/// File system interface to the SystemSaveData archive
-/// TODO(Subv): This archive should point to a location in the NAND,
-/// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High>
class Archive_SystemSaveData final : public DiskArchive {
public:
Archive_SystemSaveData(const std::string& mount_point, u64 save_id);
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0f822f84b..a2f51b41b 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -5,6 +5,8 @@
#pragma once
#include "common/common_types.h"
+
+#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
@@ -135,6 +137,12 @@ template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32, s64, s64)> void Wrap() {
+ s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2);
+ s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1);
+ FuncReturn(func(PARAM(0), param1, param2));
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 33ac12507..11570c8b4 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -4,8 +4,10 @@
#include <vector>
+#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
+#include "core/hle/shared_page.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/fs/archive.h"
@@ -71,6 +73,8 @@ void Init() {
RegisterAllModules();
+ SharedPage::Init();
+
LOG_DEBUG(Kernel, "initialized OK");
}
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index 59b770f02..3f6f9a4b5 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -4,6 +4,8 @@
#pragma once
+#include <string>
+
#include "common/common_types.h"
#include "core/core.h"
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 736bbc36a..b7434aaf2 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -29,35 +29,56 @@ public:
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Arbitrate an address
-ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) {
+ Object* object = Kernel::g_handle_table.GetGeneric(handle).get();
+ if (object == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
switch (type) {
// Signal thread(s) waiting for arbitrate address...
case ArbitrationType::Signal:
// Negative value means resume all threads
if (value < 0) {
- ArbitrateAllThreads(handle, address);
+ ArbitrateAllThreads(object, address);
} else {
// Resume first N threads
for(int i = 0; i < value; i++)
- ArbitrateHighestPriorityThread(handle, address);
+ ArbitrateHighestPriorityThread(object, address);
}
break;
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ HLE::Reschedule(__func__);
+ }
+ break;
+ case ArbitrationType::WaitIfLessThanWithTimeout:
+ if ((s32)Memory::Read32(address) <= value) {
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds);
HLE::Reschedule(__func__);
}
break;
-
case ArbitrationType::DecrementAndWaitIfLessThan:
{
s32 memory_value = Memory::Read32(address) - 1;
Memory::Write32(address, memory_value);
if (memory_value <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ HLE::Reschedule(__func__);
+ }
+ break;
+ }
+ case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout:
+ {
+ s32 memory_value = Memory::Read32(address) - 1;
+ Memory::Write32(address, memory_value);
+ if (memory_value <= value) {
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds);
HLE::Reschedule(__func__);
}
break;
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 030e7ad7b..3ffd465a2 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
};
/// Arbitrate an address
-ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds);
/// Create an address arbiter
Handle CreateAddressArbiter(const std::string& name = "Unknown");
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index e43c3ee4e..271190dbe 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -33,11 +33,11 @@ public:
ResultVal<bool> WaitSynchronization() override {
bool wait = locked;
if (locked) {
- Handle thread = GetCurrentThreadHandle();
+ Handle thread = GetCurrentThread()->GetHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
waiting_threads.push_back(thread);
}
- Kernel::WaitCurrentThread(WAITTYPE_EVENT, GetHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_EVENT, this);
}
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
locked = true;
@@ -53,7 +53,7 @@ public:
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
@@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
@@ -82,13 +82,15 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SignalEvent(const Handle handle) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Resume threads waiting for event to signal
bool event_caught = false;
for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
- ResumeThreadFromWait( evt->waiting_threads[i]);
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get();
+ if (thread != nullptr)
+ thread->ResumeFromWait();
// If any thread is signalled awake by this event, assume the event was "caught" and reset
// the event. This will result in the next thread waiting on the event to block. Otherwise,
@@ -110,7 +112,7 @@ ResultCode SignalEvent(const Handle handle) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode ClearEvent(Handle handle) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e59ed1b57..d3684896f 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -6,13 +6,15 @@
#include "common/common.h"
+#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
namespace Kernel {
-Handle g_main_thread = 0;
+SharedPtr<Thread> g_main_thread = nullptr;
HandleTable g_handle_table;
u64 g_program_id = 0;
@@ -21,7 +23,7 @@ HandleTable::HandleTable() {
Clear();
}
-ResultVal<Handle> HandleTable::Create(Object* obj) {
+ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
_dbg_assert_(Kernel, obj != nullptr);
u16 slot = next_free_slot;
@@ -37,22 +39,23 @@ ResultVal<Handle> HandleTable::Create(Object* obj) {
// CTR-OS doesn't use generation 0, so skip straight to 1.
if (next_generation >= (1 << 15)) next_generation = 1;
+ Handle handle = generation | (slot << 15);
+ if (obj->handle == INVALID_HANDLE)
+ obj->handle = handle;
+
generations[slot] = generation;
- intrusive_ptr_add_ref(obj);
- objects[slot] = obj;
+ objects[slot] = std::move(obj);
- Handle handle = generation | (slot << 15);
- obj->handle = handle;
return MakeResult<Handle>(handle);
}
ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
- Object* object = GetGeneric(handle);
+ SharedPtr<Object> object = GetGeneric(handle);
if (object == nullptr) {
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
return ERR_INVALID_HANDLE;
}
- return Create(object);
+ return Create(std::move(object));
}
ResultCode HandleTable::Close(Handle handle) {
@@ -62,7 +65,6 @@ ResultCode HandleTable::Close(Handle handle) {
size_t slot = GetSlot(handle);
u16 generation = GetGeneration(handle);
- intrusive_ptr_release(objects[slot]);
objects[slot] = nullptr;
generations[generation] = next_free_slot;
@@ -77,10 +79,9 @@ bool HandleTable::IsValid(Handle handle) const {
return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
}
-Object* HandleTable::GetGeneric(Handle handle) const {
+SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
- // TODO(yuriks) Directly return the pointer once this is possible.
- handle = GetCurrentThreadHandle();
+ return GetCurrentThread();
} else if (handle == CurrentProcess) {
LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess);
return nullptr;
@@ -95,8 +96,6 @@ Object* HandleTable::GetGeneric(Handle handle) const {
void HandleTable::Clear() {
for (size_t i = 0; i < MAX_COUNT; ++i) {
generations[i] = i + 1;
- if (objects[i] != nullptr)
- intrusive_ptr_release(objects[i]);
objects[i] = nullptr;
}
next_free_slot = 0;
@@ -105,12 +104,13 @@ void HandleTable::Clear() {
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
+ Kernel::TimersInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
-
+ Kernel::TimersShutdown();
g_handle_table.Clear(); // Free all kernel objects
}
@@ -123,7 +123,9 @@ bool LoadExec(u32 entry_point) {
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far
- g_main_thread = Kernel::SetupMainThread(0x30);
+ g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE);
+ // Setup the idle thread
+ Kernel::SetupIdleThread();
return true;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7f86fd07d..5e5217b78 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,6 +4,8 @@
#pragma once
+#include <boost/intrusive_ptr.hpp>
+
#include <array>
#include <string>
#include "common/common.h"
@@ -16,6 +18,8 @@ const Handle INVALID_HANDLE = 0;
namespace Kernel {
+class Thread;
+
// TODO: Verify code
const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
ErrorSummary::OutOfResource, ErrorLevel::Temporary);
@@ -39,6 +43,7 @@ enum class HandleType : u32 {
Process = 8,
AddressArbiter = 9,
Semaphore = 10,
+ Timer = 11
};
enum {
@@ -49,7 +54,7 @@ class HandleTable;
class Object : NonCopyable {
friend class HandleTable;
- u32 handle;
+ u32 handle = INVALID_HANDLE;
public:
virtual ~Object() {}
Handle GetHandle() const { return handle; }
@@ -73,7 +78,7 @@ private:
unsigned int ref_count = 0;
};
-// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
+// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
@@ -84,6 +89,9 @@ inline void intrusive_ptr_release(Object* object) {
}
}
+template <typename T>
+using SharedPtr = boost::intrusive_ptr<T>;
+
/**
* This class allows the creation of Handles, which are references to objects that can be tested
* for validity and looked up. Here they are used to pass references to kernel objects to/from the
@@ -116,7 +124,7 @@ public:
* @return The created Handle or one of the following errors:
* - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
*/
- ResultVal<Handle> Create(Object* obj);
+ ResultVal<Handle> Create(SharedPtr<Object> obj);
/**
* Returns a new handle that points to the same object as the passed in handle.
@@ -140,7 +148,7 @@ public:
* Looks up a handle.
* @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
*/
- Object* GetGeneric(Handle handle) const;
+ SharedPtr<Object> GetGeneric(Handle handle) const;
/**
* Looks up a handle while verifying its type.
@@ -148,10 +156,10 @@ public:
* type differs from the handle type `T::HANDLE_TYPE`.
*/
template <class T>
- T* Get(Handle handle) const {
- Object* object = GetGeneric(handle);
+ SharedPtr<T> Get(Handle handle) const {
+ SharedPtr<Object> object = GetGeneric(handle);
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
- return static_cast<T*>(object);
+ return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}
@@ -170,7 +178,7 @@ private:
static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
/// Stores the Object referenced by the handle or null if the slot is empty.
- std::array<Object*, MAX_COUNT> objects;
+ std::array<SharedPtr<Object>, MAX_COUNT> objects;
/**
* The value of `next_generation` when the handle was created, used to check for validity. For
@@ -189,7 +197,7 @@ private:
};
extern HandleTable g_handle_table;
-extern Handle g_main_thread;
+extern SharedPtr<Thread> g_main_thread;
/// The ID code of the currently running game
/// TODO(Subv): This variable should not be here,
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 3dfeffc9b..853a5dd74 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -40,14 +40,21 @@ static MutexMap g_mutex_held_locks;
* @param mutex Mutex that is to be acquired
* @param thread Thread that will acquired
*/
-void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
+void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) {
g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
mutex->lock_thread = thread;
}
-bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
- MutexAcquireLock(mutex, thread);
- Kernel::ResumeThreadFromWait(thread);
+bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) {
+ MutexAcquireLock(mutex, thread_handle);
+
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get();
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle);
+ return false;
+ }
+
+ thread->ResumeFromWait();
return true;
}
@@ -87,7 +94,7 @@ void ReleaseThreadMutexes(Handle thread) {
// Release every mutex that the thread holds, and resume execution on the waiting threads
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- Mutex* mutex = g_handle_table.Get<Mutex>(iter->second);
+ Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get();
ResumeWaitingThread(mutex);
}
@@ -115,7 +122,7 @@ bool ReleaseMutex(Mutex* mutex) {
* @param handle Handle to mutex to release
*/
ResultCode ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle);
+ Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get();
if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
@@ -168,8 +175,8 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
ResultVal<bool> Mutex::WaitSynchronization() {
bool wait = locked;
if (locked) {
- waiting_threads.push_back(GetCurrentThreadHandle());
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
+ waiting_threads.push_back(GetCurrentThread()->GetHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this);
} else {
// Lock the mutex when the first thread accesses it
locked = true;
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 6bc8066a6..88ec9a104 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -37,8 +37,8 @@ public:
bool wait = !IsAvailable();
if (wait) {
- Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
- waiting_threads.push(GetCurrentThreadHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_SEMA, this);
+ waiting_threads.push(GetCurrentThread()->GetHandle());
} else {
--available_count;
}
@@ -70,7 +70,7 @@ ResultCode CreateSemaphore(Handle* handle, s32 initial_count,
}
ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
- Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle);
+ Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get();
if (semaphore == nullptr)
return InvalidHandle(ErrorModule::Kernel);
@@ -84,7 +84,9 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
// Notify some of the threads that the semaphore has been released
// stop once the semaphore is full again or there are no more waiting threads
while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
- Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get();
+ if (thread != nullptr)
+ thread->ResumeFromWait();
semaphore->waiting_threads.pop();
--semaphore->available_count;
}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index cea1f6fa1..5368e4728 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -61,7 +61,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
- SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
+ SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get();
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
@@ -72,7 +72,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
}
ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
+ SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get();
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 872df2d14..bc86a7c59 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -10,7 +10,9 @@
#include "common/common.h"
#include "common/thread_queue_list.h"
+#include "core/arm/arm_interface.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
@@ -20,64 +22,25 @@
namespace Kernel {
-class Thread : public Kernel::Object {
-public:
-
- std::string GetName() const override { return name; }
- std::string GetTypeName() const override { return "Thread"; }
-
- static const HandleType HANDLE_TYPE = HandleType::Thread;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
-
- inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
- inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
- inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
- inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
- inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
-
- ResultVal<bool> WaitSynchronization() override {
- const bool wait = status != THREADSTATUS_DORMANT;
- if (wait) {
- Handle thread = GetCurrentThreadHandle();
- if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
- waiting_threads.push_back(thread);
- }
- WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
+ResultVal<bool> Thread::WaitSynchronization() {
+ const bool wait = status != THREADSTATUS_DORMANT;
+ if (wait) {
+ Thread* thread = GetCurrentThread();
+ if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
+ waiting_threads.push_back(thread);
}
-
- return MakeResult<bool>(wait);
+ WaitCurrentThread(WAITTYPE_THREADEND, this);
}
- ThreadContext context;
-
- u32 thread_id;
-
- u32 status;
- u32 entry_point;
- u32 stack_top;
- u32 stack_size;
-
- s32 initial_priority;
- s32 current_priority;
-
- s32 processor_id;
-
- WaitType wait_type;
- Handle wait_handle;
- VAddr wait_address;
-
- std::vector<Handle> waiting_threads;
-
- std::string name;
-};
+ return MakeResult<bool>(wait);
+}
// Lists all thread ids that aren't deleted/etc.
-static std::vector<Handle> thread_queue;
+static std::vector<SharedPtr<Thread>> thread_list;
// Lists only ready thread ids.
-static Common::ThreadQueueList<Handle> thread_ready_queue;
+static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue;
-static Handle current_thread_handle;
static Thread* current_thread;
static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
@@ -87,30 +50,9 @@ Thread* GetCurrentThread() {
return current_thread;
}
-/// Gets the current thread handle
-Handle GetCurrentThreadHandle() {
- return GetCurrentThread()->GetHandle();
-}
-
-/// Sets the current thread
-inline void SetCurrentThread(Thread* t) {
- current_thread = t;
- current_thread_handle = t->GetHandle();
-}
-
-/// Saves the current CPU context
-void SaveContext(ThreadContext& ctx) {
- Core::g_app_core->SaveContext(ctx);
-}
-
-/// Loads a CPU context
-void LoadContext(ThreadContext& ctx) {
- Core::g_app_core->LoadContext(ctx);
-}
-
/// Resets a thread
-void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
- memset(&t->context, 0, sizeof(ThreadContext));
+static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
+ memset(&t->context, 0, sizeof(Core::ThreadContext));
t->context.cpu_registers[0] = arg;
t->context.pc = t->context.reg_15 = t->entry_point;
@@ -126,22 +68,21 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
t->current_priority = t->initial_priority;
}
t->wait_type = WAITTYPE_NONE;
- t->wait_handle = 0;
+ t->wait_object = nullptr;
t->wait_address = 0;
}
/// Change a thread to "ready" state
-void ChangeReadyState(Thread* t, bool ready) {
- Handle handle = t->GetHandle();
+static void ChangeReadyState(Thread* t, bool ready) {
if (t->IsReady()) {
if (!ready) {
- thread_ready_queue.remove(t->current_priority, handle);
+ thread_ready_queue.remove(t->current_priority, t);
}
} else if (ready) {
if (t->IsRunning()) {
- thread_ready_queue.push_front(t->current_priority, handle);
+ thread_ready_queue.push_front(t->current_priority, t);
} else {
- thread_ready_queue.push_back(t->current_priority, handle);
+ thread_ready_queue.push_back(t->current_priority, t);
}
t->status = THREADSTATUS_READY;
}
@@ -153,43 +94,36 @@ static bool CheckWaitType(const Thread* thread, WaitType type) {
}
/// Check if a thread is blocking on a specified wait type with a specified handle
-static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
- return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
+static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) {
+ return CheckWaitType(thread, type) && wait_object == thread->wait_object;
}
/// Check if a thread is blocking on a specified wait type with a specified handle and address
-static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
- return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
+static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) {
+ return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address);
}
/// Stops the current thread
-ResultCode StopThread(Handle handle, const char* reason) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
+void Thread::Stop(const char* reason) {
// Release all the mutexes that this thread holds
- ReleaseThreadMutexes(handle);
+ ReleaseThreadMutexes(GetHandle());
- ChangeReadyState(thread, false);
- thread->status = THREADSTATUS_DORMANT;
- for (Handle waiting_handle : thread->waiting_threads) {
- Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle);
-
- if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
- ResumeThreadFromWait(waiting_handle);
+ ChangeReadyState(this, false);
+ status = THREADSTATUS_DORMANT;
+ for (auto& waiting_thread : waiting_threads) {
+ if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this))
+ waiting_thread->ResumeFromWait();
}
- thread->waiting_threads.clear();
+ waiting_threads.clear();
// Stopped threads are never waiting.
- thread->wait_type = WAITTYPE_NONE;
- thread->wait_handle = 0;
- thread->wait_address = 0;
-
- return RESULT_SUCCESS;
+ wait_type = WAITTYPE_NONE;
+ wait_object = nullptr;
+ wait_address = 0;
}
/// Changes a threads state
-void ChangeThreadState(Thread* t, ThreadStatus new_status) {
+static void ChangeThreadState(Thread* t, ThreadStatus new_status) {
if (!t || t->status == new_status) {
return;
}
@@ -204,46 +138,44 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
/// Arbitrate the highest priority thread that is waiting
-Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
- Handle highest_priority_thread = 0;
+Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) {
+ Thread* highest_priority_thread = nullptr;
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (Handle handle : thread_queue) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
-
- if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
+ for (auto& thread : thread_list) {
+ if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address))
continue;
if (thread == nullptr)
continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
if(thread->current_priority <= priority) {
- highest_priority_thread = handle;
+ highest_priority_thread = thread.get();
priority = thread->current_priority;
}
}
+
// If a thread was arbitrated, resume it
- if (0 != highest_priority_thread)
- ResumeThreadFromWait(highest_priority_thread);
+ if (nullptr != highest_priority_thread) {
+ highest_priority_thread->ResumeFromWait();
+ }
return highest_priority_thread;
}
/// Arbitrate all threads currently waiting
-void ArbitrateAllThreads(u32 arbiter, u32 address) {
+void ArbitrateAllThreads(Object* arbiter, u32 address) {
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (Handle handle : thread_queue) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
-
- if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
- ResumeThreadFromWait(handle);
+ for (auto& thread : thread_list) {
+ if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address))
+ thread->ResumeFromWait();
}
}
/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
-void CallThread(Thread* t) {
+static void CallThread(Thread* t) {
// Stop waiting
if (t->wait_type != WAITTYPE_NONE) {
t->wait_type = WAITTYPE_NONE;
@@ -252,12 +184,12 @@ void CallThread(Thread* t) {
}
/// Switches CPU context to that of the specified thread
-void SwitchContext(Thread* t) {
+static void SwitchContext(Thread* t) {
Thread* cur = GetCurrentThread();
// Save context for current thread
if (cur) {
- SaveContext(cur->context);
+ Core::g_app_core->SaveContext(cur->context);
if (cur->IsRunning()) {
ChangeReadyState(cur, true);
@@ -265,19 +197,19 @@ void SwitchContext(Thread* t) {
}
// Load context of new thread
if (t) {
- SetCurrentThread(t);
+ current_thread = t;
ChangeReadyState(t, false);
t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
t->wait_type = WAITTYPE_NONE;
- LoadContext(t->context);
+ Core::g_app_core->LoadContext(t->context);
} else {
- SetCurrentThread(nullptr);
+ current_thread = nullptr;
}
}
/// Gets the next thread that is ready to be run by priority
-Thread* NextThread() {
- Handle next;
+static Thread* NextThread() {
+ Thread* next;
Thread* cur = GetCurrentThread();
if (cur && cur->IsRunning()) {
@@ -288,63 +220,111 @@ Thread* NextThread() {
if (next == 0) {
return nullptr;
}
- return Kernel::g_handle_table.Get<Thread>(next);
+ return next;
}
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
+void WaitCurrentThread(WaitType wait_type, Object* wait_object) {
Thread* thread = GetCurrentThread();
thread->wait_type = wait_type;
- thread->wait_handle = wait_handle;
+ thread->wait_object = wait_object;
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
- WaitCurrentThread(wait_type, wait_handle);
+void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) {
+ WaitCurrentThread(wait_type, wait_object);
GetCurrentThread()->wait_address = wait_address;
}
+/// Event type for the thread wake up event
+static int ThreadWakeupEventType = -1;
+
+/// Callback that will wake up the thread it was scheduled for
+static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
+ Handle handle = static_cast<Handle>(parameter);
+ SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle);
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ return;
+ }
+
+ thread->ResumeFromWait();
+}
+
+
+void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) {
+ // Don't schedule a wakeup if the thread wants to wait forever
+ if (nanoseconds == -1)
+ return;
+ _dbg_assert_(Kernel, thread != nullptr);
+
+ u64 microseconds = nanoseconds / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle());
+}
+
/// Resumes a thread from waiting by marking it as "ready"
-void ResumeThreadFromWait(Handle handle) {
- Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
- if (thread) {
- thread->status &= ~THREADSTATUS_WAIT;
- thread->wait_handle = 0;
- thread->wait_type = WAITTYPE_NONE;
- if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
- ChangeReadyState(thread, true);
- }
+void Thread::ResumeFromWait() {
+ // Cancel any outstanding wakeup events
+ CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle());
+
+ status &= ~THREADSTATUS_WAIT;
+ wait_object = nullptr;
+ wait_type = WAITTYPE_NONE;
+ if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
+ ChangeReadyState(this, true);
}
}
/// Prints the thread queue for debugging purposes
-void DebugThreadQueue() {
+static void DebugThreadQueue() {
Thread* thread = GetCurrentThread();
if (!thread) {
return;
}
- LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
- for (u32 i = 0; i < thread_queue.size(); i++) {
- Handle handle = thread_queue[i];
- s32 priority = thread_ready_queue.contains(handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle());
+ for (auto& t : thread_list) {
+ s32 priority = thread_ready_queue.contains(t.get());
if (priority != -1) {
- LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle());
}
}
}
-/// Creates a new thread
-Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
- s32 processor_id, u32 stack_top, int stack_size) {
+ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
+ u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) {
+ if (stack_size < 0x200) {
+ LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size);
+ // TODO: Verify error
+ return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ }
+
+ if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
+ s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
+ name.c_str(), priority, new_priority);
+ // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
+ // validity of this
+ priority = new_priority;
+ }
- _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
- "priority=%d, outside of allowable range!", priority)
+ if (!Memory::GetPointer(entry_point)) {
+ LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
+ // TODO: Verify error
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ }
- Thread* thread = new Thread;
+ SharedPtr<Thread> thread(new Thread);
- // TOOD(yuriks): Fix error reporting
- handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE);
+ // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for
+ // the time being. Create a handle here, it will be copied to the handle field in
+ // the object and use by the rest of the code. This should be removed when other
+ // code doesn't rely on the handle anymore.
+ ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread);
+ if (handle.Failed())
+ return handle.Code();
- thread_queue.push_back(handle);
+ thread_list.push_back(thread);
thread_ready_queue.prepare(priority);
thread->thread_id = next_thread_id++;
@@ -355,69 +335,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
thread->initial_priority = thread->current_priority = priority;
thread->processor_id = processor_id;
thread->wait_type = WAITTYPE_NONE;
- thread->wait_handle = 0;
+ thread->wait_object = nullptr;
thread->wait_address = 0;
- thread->name = name;
-
- return thread;
-}
-
-/// Creates a new thread - wrapper for external user
-Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
- u32 stack_top, int stack_size) {
-
- if (name == nullptr) {
- LOG_ERROR(Kernel_SVC, "nullptr name");
- return -1;
- }
- if ((u32)stack_size < 0x200) {
- LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
- stack_size);
- return -1;
- }
- if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
- s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
- name, priority, new_priority);
- // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
- // validity of this
- priority = new_priority;
- }
- if (!Memory::GetPointer(entry_point)) {
- LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
- return -1;
- }
- Handle handle;
- Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
- stack_size);
-
- ResetThread(thread, arg, 0);
- CallThread(thread);
-
- return handle;
-}
+ thread->name = std::move(name);
-/// Get the priority of the thread specified by handle
-ResultVal<u32> GetThreadPriority(const Handle handle) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+ ResetThread(thread.get(), arg, 0);
+ CallThread(thread.get());
- return MakeResult<u32>(thread->current_priority);
+ return MakeResult<SharedPtr<Thread>>(std::move(thread));
}
/// Set the priority of the thread specified by handle
-ResultCode SetThreadPriority(Handle handle, s32 priority) {
- Thread* thread = nullptr;
- if (!handle) {
- thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
- } else {
- thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr) {
- return InvalidHandle(ErrorModule::Kernel);
- }
- }
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
+void Thread::SetPriority(s32 priority) {
// If priority is invalid, clamp to valid range
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
@@ -428,31 +357,39 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
}
// Change thread priority
- s32 old = thread->current_priority;
- thread_ready_queue.remove(old, handle);
- thread->current_priority = priority;
- thread_ready_queue.prepare(thread->current_priority);
+ s32 old = current_priority;
+ thread_ready_queue.remove(old, this);
+ current_priority = priority;
+ thread_ready_queue.prepare(current_priority);
// Change thread status to "ready" and push to ready queue
- if (thread->IsRunning()) {
- thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
+ if (IsRunning()) {
+ status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
- if (thread->IsReady()) {
- thread_ready_queue.push_back(thread->current_priority, handle);
+ if (IsReady()) {
+ thread_ready_queue.push_back(current_priority, this);
}
-
- return RESULT_SUCCESS;
}
-/// Sets up the primary application thread
-Handle SetupMainThread(s32 priority, int stack_size) {
- Handle handle;
+Handle SetupIdleThread() {
+ // We need to pass a few valid values to get around parameter checking in Thread::Create.
+ auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0,
+ THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE);
+ _dbg_assert_(Kernel, thread_res.Succeeded());
+ SharedPtr<Thread> thread = std::move(*thread_res);
- // Initialize new "main" thread
- Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
- THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
+ thread->idle = true;
+ CallThread(thread.get());
+ return thread->GetHandle();
+}
- ResetThread(thread, 0, 0);
+SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) {
+ // Initialize new "main" thread
+ auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0,
+ THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
+ // TODO(yuriks): Propagate error
+ _dbg_assert_(Kernel, thread_res.Succeeded());
+ SharedPtr<Thread> thread = std::move(*thread_res);
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
@@ -461,11 +398,11 @@ Handle SetupMainThread(s32 priority, int stack_size) {
}
// Run new "main" thread
- SetCurrentThread(thread);
+ current_thread = thread.get();
thread->status = THREADSTATUS_RUNNING;
- LoadContext(thread->context);
+ Core::g_app_core->LoadContext(thread->context);
- return handle;
+ return thread;
}
@@ -481,37 +418,19 @@ void Reschedule() {
} else {
LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
- for (Handle handle : thread_queue) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
+ for (auto& thread : thread_list) {
LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
- thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
+ thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type,
+ (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE));
}
}
-
- // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
- // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
- // switch has occurred. This results in the current thread yielding on a sleep once, and then it
- // will immediately be placed back in the queue for execution.
-
- if (CheckWaitType(prev, WAITTYPE_SLEEP))
- ResumeThreadFromWait(prev->GetHandle());
-}
-
-ResultCode GetThreadId(u32* thread_id, Handle handle) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr)
- return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
- ErrorSummary::WrongArgument, ErrorLevel::Permanent);
-
- *thread_id = thread->thread_id;
-
- return RESULT_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
next_thread_id = INITIAL_THREAD_ID;
+ ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 0e1397cd9..8c9f63aa5 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -4,8 +4,12 @@
#pragma once
+#include <string>
+#include <vector>
+
#include "common/common_types.h"
+#include "core/core.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
@@ -43,67 +47,115 @@ enum WaitType {
WAITTYPE_MUTEX,
WAITTYPE_SYNCH,
WAITTYPE_ARB,
+ WAITTYPE_TIMER,
};
namespace Kernel {
-/// Creates a new thread - wrapper for external user
-Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
- u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE);
+class Thread : public Kernel::Object {
+public:
+ static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority,
+ u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size);
-/// Sets up the primary application thread
-Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
+ std::string GetName() const override { return name; }
+ std::string GetTypeName() const override { return "Thread"; }
-/// Reschedules to the next available thread (call after current thread is suspended)
-void Reschedule();
+ static const HandleType HANDLE_TYPE = HandleType::Thread;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
-/// Stops the current thread
-ResultCode StopThread(Handle thread, const char* reason);
+ inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
+ inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
+ inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
+ inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
+ inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+ inline bool IsIdle() const { return idle; }
-/**
- * Retrieves the ID of the specified thread handle
- * @param thread_id Will contain the output thread id
- * @param handle Handle to the thread we want
- * @return Whether the function was successful or not
- */
-ResultCode GetThreadId(u32* thread_id, Handle handle);
+ ResultVal<bool> WaitSynchronization() override;
+
+ s32 GetPriority() const { return current_priority; }
+ void SetPriority(s32 priority);
+
+ u32 GetThreadId() const { return thread_id; }
+
+ void Stop(const char* reason);
+ /// Resumes a thread from waiting by marking it as "ready".
+ void ResumeFromWait();
+
+ Core::ThreadContext context;
+
+ u32 thread_id;
+
+ u32 status;
+ u32 entry_point;
+ u32 stack_top;
+ u32 stack_size;
+
+ s32 initial_priority;
+ s32 current_priority;
+
+ s32 processor_id;
+
+ WaitType wait_type;
+ Object* wait_object;
+ VAddr wait_address;
-/// Resumes a thread from waiting by marking it as "ready"
-void ResumeThreadFromWait(Handle handle);
+ std::vector<SharedPtr<Thread>> waiting_threads;
+
+ std::string name;
+
+ /// Whether this thread is intended to never actually be executed, i.e. always idle
+ bool idle = false;
+
+private:
+ Thread() = default;
+};
+
+/// Sets up the primary application thread
+SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size);
+
+/// Reschedules to the next available thread (call after current thread is suspended)
+void Reschedule();
/// Arbitrate the highest priority thread that is waiting
-Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address);
+Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address);
/// Arbitrate all threads currently waiting...
-void ArbitrateAllThreads(u32 arbiter, u32 address);
+void ArbitrateAllThreads(Object* arbiter, u32 address);
-/// Gets the current thread handle
-Handle GetCurrentThreadHandle();
+/// Gets the current thread
+Thread* GetCurrentThread();
/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
- * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
+ * @param wait_object Kernel object that we are waiting on, defaults to current thread
+ */
+void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread());
+
+/**
+ * Schedules an event to wake up the specified thread after the specified delay.
+ * @param thread The thread to wake after the delay.
+ * @param nanoseconds The time this thread will be allowed to sleep for.
*/
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds);
/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
- * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
+ * @param wait_object Kernel object that we are waiting on
* @param wait_address Arbitration address used to resume from wait
*/
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
+void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address);
-/// Put current thread in a wait state - on WaitSynchronization
-void WaitThread_Synchronization();
-/// Get the priority of the thread specified by handle
-ResultVal<u32> GetThreadPriority(const Handle handle);
-
-/// Set the priority of the thread specified by handle
-ResultCode SetThreadPriority(Handle handle, s32 priority);
+/**
+ * Sets up the idle thread, this is a thread that is intended to never execute instructions,
+ * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue
+ * and will try to yield on every call.
+ * @returns The handle of the idle thread
+ */
+Handle SetupIdleThread();
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
new file mode 100644
index 000000000..3b0452d4d
--- /dev/null
+++ b/src/core/hle/kernel/timer.cpp
@@ -0,0 +1,144 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <set>
+
+#include "common/common.h"
+
+#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/timer.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Timer : public Object {
+public:
+ std::string GetTypeName() const override { return "Timer"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Timer;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ ResetType reset_type; ///< The ResetType of this timer
+
+ bool signaled; ///< Whether the timer has been signaled or not
+ std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer
+ std::string name; ///< Name of timer (optional)
+
+ u64 initial_delay; ///< The delay until the timer fires for the first time
+ u64 interval_delay; ///< The delay until the timer fires after the first time
+
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = !signaled;
+ if (wait) {
+ waiting_threads.insert(GetCurrentThread()->GetHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_TIMER, this);
+ }
+ return MakeResult<bool>(wait);
+ }
+};
+
+/**
+ * Creates a timer.
+ * @param handle Reference to handle for the newly created timer
+ * @param reset_type ResetType describing how to create timer
+ * @param name Optional name of timer
+ * @return Newly created Timer object
+ */
+Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) {
+ Timer* timer = new Timer;
+
+ handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE);
+
+ timer->reset_type = reset_type;
+ timer->signaled = false;
+ timer->name = name;
+ timer->initial_delay = 0;
+ timer->interval_delay = 0;
+ return timer;
+}
+
+ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) {
+ CreateTimer(*handle, reset_type, name);
+ return RESULT_SUCCESS;
+}
+
+ResultCode ClearTimer(Handle handle) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ timer->signaled = false;
+ return RESULT_SUCCESS;
+}
+
+/// The event type of the generic timer callback event
+static int TimerCallbackEventType = -1;
+
+/// The timer callback event, called when a timer is fired
+static void TimerCallback(u64 timer_handle, int cycles_late) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle);
+
+ if (timer == nullptr) {
+ LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle);
+ return;
+ }
+
+ LOG_TRACE(Kernel, "Timer %u fired", timer_handle);
+
+ timer->signaled = true;
+
+ // Resume all waiting threads
+ for (Handle thread_handle : timer->waiting_threads) {
+ if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle))
+ thread->ResumeFromWait();
+ }
+
+ timer->waiting_threads.clear();
+
+ if (timer->reset_type == RESETTYPE_ONESHOT)
+ timer->signaled = false;
+
+ if (timer->interval_delay != 0) {
+ // Reschedule the timer with the interval delay
+ u64 interval_microseconds = timer->interval_delay / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
+ TimerCallbackEventType, timer_handle);
+ }
+}
+
+ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ timer->initial_delay = initial;
+ timer->interval_delay = interval;
+
+ u64 initial_microseconds = initial / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle);
+ return RESULT_SUCCESS;
+}
+
+ResultCode CancelTimer(Handle handle) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle);
+ return RESULT_SUCCESS;
+}
+
+void TimersInit() {
+ TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
+}
+
+void TimersShutdown() {
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
new file mode 100644
index 000000000..8170e82d4
--- /dev/null
+++ b/src/core/hle/kernel/timer.h
@@ -0,0 +1,47 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/svc.h"
+
+namespace Kernel {
+
+/**
+ * Cancels a timer
+ * @param handle Handle of the timer to cancel
+ */
+ResultCode CancelTimer(Handle handle);
+
+/**
+ * Starts a timer with the specified initial delay and interval
+ * @param handle Handle of the timer to start
+ * @param initial Delay until the timer is first fired
+ * @param interval Delay until the timer is fired after the first time
+ */
+ResultCode SetTimer(Handle handle, s64 initial, s64 interval);
+
+/**
+ * Clears a timer
+ * @param handle Handle of the timer to clear
+ */
+ResultCode ClearTimer(Handle handle);
+
+/**
+ * Creates a timer
+ * @param handle Handle to the newly created Timer object
+ * @param reset_type ResetType describing how to create the timer
+ * @param name Optional name of timer
+ * @return ResultCode of the error
+ */
+ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown");
+
+/// Initializes the required variables for timers
+void TimersInit();
+/// Tears down the timer variables
+void TimersShutdown();
+} // namespace
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 0e9c213e0..82dcf5bba 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -369,14 +369,14 @@ private:
StorageType storage;
ResultCode result_code;
-#if _DEBUG
+#ifdef _DEBUG
// The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
// need to cast `storage` to a pointer or pay attention to `result_code`.
const T* debug_ptr;
#endif
void UpdateDebugPtr() {
-#if _DEBUG
+#ifdef _DEBUG
debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
#endif
}
diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp
index 37be4b027..42f2879c1 100644
--- a/src/core/hle/service/apt_a.cpp
+++ b/src/core/hle/service/apt_a.cpp
@@ -11,6 +11,7 @@ namespace APT_U {
extern void GetLockHandle(Service::Interface* self);
extern void ReceiveParameter(Service::Interface* self);
extern void GlanceParameter(Service::Interface* self);
+ extern void GetSharedFont(Service::Interface* self);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,6 +30,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000E0080, APT_U::GlanceParameter, "GlanceParameter?"},
{0x003B0040, nullptr, "CancelLibraryApplet?"},
{0x00430040, nullptr, "NotifyToWait?"},
+ {0x00440000, APT_U::GetSharedFont, "GetSharedFont?"},
{0x004B00C2, nullptr, "AppletUtility?"},
{0x00550040, nullptr, "WriteInputToNsState?"},
};
diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp
new file mode 100644
index 000000000..31e6653ef
--- /dev/null
+++ b/src/core/hle/service/apt_s.cpp
@@ -0,0 +1,122 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/common.h"
+#include "common/file_util.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/service/apt_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace APT_S
+
+namespace APT_U {
+ extern void GetLockHandle(Service::Interface* self);
+ extern void Initialize(Service::Interface* self);
+ extern void Enable(Service::Interface* self);
+ extern void InquireNotification(Service::Interface* self);
+ extern void NotifyToWait(Service::Interface* self);
+ extern void GetSharedFont(Service::Interface* self);
+ extern void AppletUtility(Service::Interface* self);
+ extern void GlanceParameter(Service::Interface* self);
+ extern void ReceiveParameter(Service::Interface* self);
+}
+
+namespace APT_S {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, APT_U::GetLockHandle, "GetLockHandle"},
+ {0x00020080, APT_U::Initialize, "Initialize"},
+ {0x00030040, APT_U::Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, nullptr, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, nullptr, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, APT_U::InquireNotification, "InquireNotification"},
+ {0x000C0104, nullptr, "SendParameter"},
+ {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"},
+ {0x000F0100, nullptr, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, APT_U::NotifyToWait, "NotifyToWait"},
+ {0x00440000, APT_U::GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, APT_U::AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
+ {0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt_s.h
new file mode 100644
index 000000000..f097c9747
--- /dev/null
+++ b/src/core/hle/service/apt_s.h
@@ -0,0 +1,30 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace APT_S
+
+namespace APT_S {
+
+// Application and title launching service. These services handle signaling for home/power button as
+// well. Only one session for either APT service can be open at a time, normally processes close the
+// service handle immediately once finished using the service. The commands for APT:U and APT:S are
+// exactly the same, however certain commands are only accessible with APT:S(NS module will call
+// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
+
+/// Interface to "APT:S" service
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "APT:S";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index d8b261ba7..69a7bcf92 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -10,7 +10,7 @@
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
-#include "apt_u.h"
+#include "core/hle/service/apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace APT_U
@@ -25,10 +25,12 @@ namespace APT_U {
// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
static const VAddr SHARED_FONT_VADDR = 0x18000000;
-// Handle to shared memory region designated to for shared system font
+/// Handle to shared memory region designated to for shared system font
static Handle shared_font_mem = 0;
static Handle lock_handle = 0;
+static Handle notification_event_handle = 0; ///< APT notification event handle
+static Handle pause_event_handle = 0; ///< APT pause event handle
static std::vector<u8> shared_font;
/// Signals used by APT functions
@@ -42,18 +44,28 @@ enum class SignalType : u32 {
void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
- cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
+ notification_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Notification");
+ pause_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause");
- Kernel::SetEventLocked(cmd_buff[3], true);
- Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+ cmd_buff[3] = notification_event_handle;
+ cmd_buff[4] = pause_event_handle;
+
+ Kernel::SetEventLocked(notification_event_handle, true);
+ Kernel::SetEventLocked(pause_event_handle, false); // Fire start event
_assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
Kernel::ReleaseMutex(lock_handle);
cmd_buff[1] = 0; // No error
+}
- LOG_DEBUG(Service_APT, "called");
+void NotifyToWait(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further.
+ Kernel::SignalEvent(pause_event_handle);
+ LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
+ cmd_buff[1] = 0;
}
void GetLockHandle(Service::Interface* self) {
@@ -86,7 +98,7 @@ void Enable(Service::Interface* self) {
void InquireNotification(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[2];
+ u32 app_id = cmd_buff[1];
cmd_buff[1] = 0; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
@@ -194,8 +206,6 @@ void AppletUtility(Service::Interface* self) {
* 4 : Handle to shared font memory
*/
void GetSharedFont(Service::Interface* self) {
- LOG_TRACE(Kernel_SVC, "called");
-
u32* cmd_buff = Kernel::GetCommandBuffer();
if (!shared_font.empty()) {
@@ -281,7 +291,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00400042, nullptr, "SendCaptureBufferInfo"},
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
{0x00420080, nullptr, "SleepSystem"},
- {0x00430040, nullptr, "NotifyToWait"},
+ {0x00430040, NotifyToWait, "NotifyToWait"},
{0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
{0x00460104, nullptr, "Wrap"},
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 161aa8531..8812c49ef 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -161,9 +161,9 @@ ResultCode FormatConfig() {
void CFGInit() {
// TODO(Subv): In the future we should use the FS service to query this archive,
// currently it is not possible because you can only have one open archive of the same type at any time
- std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>(
- syssavedata_directory, CFG_SAVE_ID);
+ nand_directory, CFG_SAVE_ID);
if (!cfg_system_save_data->Initialize()) {
LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service");
return;
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index c74527ca4..e818d7bdc 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -110,7 +110,7 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
* The config savegame file in the filesystem is not updated.
* @param block_id The id of the block we want to create
* @param size The size of the block we want to create
- * @param flag The flags of the new block
+ * @param flags The flags of the new block
* @param data A pointer containing the data we will write to the new block
* @returns ResultCode indicating the result of the operation, 0 on success
*/
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
new file mode 100644
index 000000000..cf4e82152
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -0,0 +1,98 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/cfg/cfg_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_S
+
+namespace CFG_S {
+
+/**
+ * CFG_S::GetConfigInfoBlk2 service function
+ * Inputs:
+ * 0 : 0x00010082
+ * 1 : Size
+ * 2 : Block ID
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void GetConfigInfoBlk2(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 size = cmd_buffer[1];
+ u32 block_id = cmd_buffer[2];
+ u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
+
+ if (data_pointer == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
+}
+
+/**
+ * CFG_S::GetConfigInfoBlk8 service function
+ * Inputs:
+ * 0 : 0x04010082
+ * 1 : Size
+ * 2 : Block ID
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void GetConfigInfoBlk8(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 size = cmd_buffer[1];
+ u32 block_id = cmd_buffer[2];
+ u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
+
+ if (data_pointer == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
+}
+
+/**
+ * CFG_S::UpdateConfigNANDSavegame service function
+ * Inputs:
+ * 0 : 0x04030000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void UpdateConfigNANDSavegame(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
+ {0x00020000, nullptr, "SecureInfoGetRegion"},
+ {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
+ {0x04020082, nullptr, "SetConfigInfoBlk4"},
+ {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
+ {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x04060000, nullptr, "SecureInfoGetRegion"},
+ {0x04070000, nullptr, "SecureInfoGetByte101"},
+ {0x04080042, nullptr, "SecureInfoGetSerialNo"},
+ {0x04090000, nullptr, "UpdateConfigBlk00040003"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h
new file mode 100644
index 000000000..d8b67137f
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_s.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_S
+
+namespace CFG_S {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "cfg:s";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index d4affdfbf..d5e39ea4b 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -23,11 +23,8 @@ void SignalInterrupt() {
// that check the DSP interrupt signal event to run. We should figure out the different types of
// DSP interrupts, and trigger them at the appropriate times.
- if (interrupt_event == 0) {
- LOG_WARNING(Service_DSP, "cannot signal interrupt until DSP event has been created!");
- return;
- }
- Kernel::SignalEvent(interrupt_event);
+ if (interrupt_event != 0)
+ Kernel::SignalEvent(interrupt_event);
}
/**
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index f761c6ab9..958dd9344 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -36,6 +36,10 @@ namespace std {
};
}
+/// TODO(Subv): Confirm length of these strings
+const std::string SYSTEM_ID = "00000000000000000000000000000000";
+const std::string SDCARD_ID = "00000000000000000000000000000000";
+
namespace Service {
namespace FS {
@@ -432,11 +436,11 @@ ResultCode FormatSaveData() {
void ArchiveInit() {
next_handle = 1;
- // TODO(Link Mauve): Add the other archive types (see here for the known types:
- // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
- // archive type is SDMC, so it is the only one getting exposed.
+ // TODO(Subv): Add the other archive types (see here for the known types:
+ // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
if (sdmc_archive->Initialize())
CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
@@ -444,28 +448,24 @@ void ArchiveInit() {
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
// Create the SaveData archive
- std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
- auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory);
+ auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(sdmc_directory);
CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
- std::string extsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA);
- auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(extsavedata_directory);
+ auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sdmc_directory, false);
if (extsavedata_archive->Initialize())
CreateArchive(std::move(extsavedata_archive), ArchiveIdCode::ExtSaveData);
else
- LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_directory.c_str());
+ LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_archive->GetMountPoint().c_str());
- std::string sharedextsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA);
- auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sharedextsavedata_directory);
+ auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true);
if (sharedextsavedata_archive->Initialize())
CreateArchive(std::move(sharedextsavedata_archive), ArchiveIdCode::SharedExtSaveData);
else
LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s",
- sharedextsavedata_directory.c_str());
+ sharedextsavedata_archive->GetMountPoint().c_str());
// Create the SaveDataCheck archive, basically a small variation of the RomFS archive
- std::string savedatacheck_directory = FileUtil::GetUserPath(D_SAVEDATACHECK_IDX);
- auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(savedatacheck_directory);
+ auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(nand_directory);
CreateArchive(std::move(savedatacheck_archive), ArchiveIdCode::SaveDataCheck);
}
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 9e9efa019..2cdfaa7e9 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -10,6 +10,11 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
+/// The unique system identifier hash, also known as ID0
+extern const std::string SYSTEM_ID;
+/// The scrambled SD card CID, also known as ID1
+extern const std::string SDCARD_ID;
+
namespace Service {
namespace FS {
@@ -37,7 +42,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
/**
* Closes an archive
- * @param id_code IdCode of the archive to open
+ * @param handle Handle to the archive to close
*/
ResultCode CloseArchive(ArchiveHandle handle);
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 7eb32146d..56f3117f4 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -27,8 +27,6 @@ static void Initialize(Service::Interface* self) {
// TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
// http://3dbrew.org/wiki/FS:Initialize#Request
cmd_buff[1] = RESULT_SUCCESS.raw;
-
- LOG_DEBUG(Service_FS, "called");
}
/**
@@ -104,8 +102,8 @@ static void OpenFileDirectly(Service::Interface* self) {
FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
FileSys::Path file_path(filename_type, filename_size, filename_ptr);
- LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d",
- archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
+ LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s file_path=%s, mode=%u attributes=%d",
+ archive_id, archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path);
if (archive_handle.Failed()) {
@@ -367,7 +365,7 @@ static void OpenArchive(Service::Interface* self) {
u32 archivename_ptr = cmd_buff[5];
FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
- LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
+ LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s", archive_id, archive_path.DebugStr().c_str());
ResultVal<ArchiveHandle> handle = OpenArchive(archive_id, archive_path);
cmd_buff[1] = handle.Code().raw;
@@ -408,8 +406,6 @@ static void IsSdmcDetected(Service::Interface* self) {
cmd_buff[1] = 0;
cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
-
- LOG_DEBUG(Service_FS, "called");
}
/**
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 0127d4ee5..4ca2b9bd0 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -210,14 +210,27 @@ void SignalInterrupt(InterruptId interrupt_id) {
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id);
- interrupt_relay_queue->number_interrupts = interrupt_relay_queue->number_interrupts + 1;
-
u8 next = interrupt_relay_queue->index;
next += interrupt_relay_queue->number_interrupts;
next = next % 0x34; // 0x34 is the number of interrupt slots
+ interrupt_relay_queue->number_interrupts += 1;
+
interrupt_relay_queue->slot[next] = interrupt_id;
interrupt_relay_queue->error_code = 0x0; // No error
+
+ // Update framebuffer information if requested
+ // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
+ // executing any GSP commands, only waiting on the event.
+ for (int screen_id = 0; screen_id < 2; ++screen_id) {
+ FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
+
+ if (info->is_dirty) {
+ SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+ }
+
+ info->is_dirty = false;
+ }
}
Kernel::SignalEvent(g_interrupt_event);
}
@@ -269,8 +282,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
-
- SignalInterrupt(InterruptId::PSC0);
break;
}
@@ -283,19 +294,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
-
- // TODO(bunnei): Determine if these interrupts should be signalled here.
- SignalInterrupt(InterruptId::PSC1);
- SignalInterrupt(InterruptId::PPF);
-
- // Update framebuffer information if requested
- for (int screen_id = 0; screen_id < 2; ++screen_id) {
- FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
- if (info->is_dirty)
- SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
-
- info->is_dirty = false;
- }
break;
}
@@ -328,9 +326,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
/// This triggers handling of the GX command written to the command buffer in shared memory.
static void TriggerCmdReqQueue(Service::Interface* self) {
-
- LOG_TRACE(Service_GSP, "called");
-
// Iterate through each thread's command queue...
for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id);
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 932b6170f..65abb194a 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -45,21 +45,16 @@ enum class CommandId : u32 {
/// GSP thread interrupt relay queue
struct InterruptRelayQueue {
- union {
- u32 hex;
-
- // Index of last interrupt in the queue
- BitField<0,8,u32> index;
-
- // Number of interrupts remaining to be processed by the userland code
- BitField<8,8,u32> number_interrupts;
-
- // Error code - zero on success, otherwise an error has occurred
- BitField<16,8,u32> error_code;
- };
-
- u32 unk0;
- u32 unk1;
+ // Index of last interrupt in the queue
+ u8 index;
+ // Number of interrupts remaining to be processed by the userland code
+ u8 number_interrupts;
+ // Error code - zero on success, otherwise an error has occurred
+ u8 error_code;
+ u8 padding1;
+
+ u32 missed_PDC0;
+ u32 missed_PDC1;
InterruptId slot[0x34]; ///< Interrupt ID slots
};
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index 99b0ea5a0..1403b1de9 100644
--- a/src/core/hle/service/hid_user.cpp
+++ b/src/core/hle/service/hid_user.cpp
@@ -4,6 +4,7 @@
#include "common/log.h"
+#include "core/arm/arm_interface.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
@@ -162,8 +163,6 @@ static void GetIPCHandles(Service::Interface* self) {
cmd_buff[6] = event_accelerometer;
cmd_buff[7] = event_gyroscope;
cmd_buff[8] = event_debug_pad;
-
- LOG_TRACE(Service_HID, "called");
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm_sysm.cpp
new file mode 100644
index 000000000..4b5f86a47
--- /dev/null
+++ b/src/core/hle/service/ptm_sysm.cpp
@@ -0,0 +1,56 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "common/make_unique.h"
+#include "core/file_sys/archive_extsavedata.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ptm_sysm.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_SYSM
+
+namespace PTM_SYSM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x040100C0, nullptr, "SetRtcAlarmEx"},
+ {0x04020042, nullptr, "ReplySleepQuery"},
+ {0x04030042, nullptr, "NotifySleepPreparationComplete"},
+ {0x04040102, nullptr, "SetWakeupTrigger"},
+ {0x04050000, nullptr, "GetAwakeReason"},
+ {0x04060000, nullptr, "RequestSleep"},
+ {0x040700C0, nullptr, "ShutdownAsync"},
+ {0x04080000, nullptr, "Awake"},
+ {0x04090080, nullptr, "RebootAsync"},
+ {0x040A0000, nullptr, "CheckNew3DS"},
+ {0x08010640, nullptr, "SetInfoLEDPattern"},
+ {0x08020040, nullptr, "SetInfoLEDPatternHeader"},
+ {0x08030000, nullptr, "GetInfoLEDStatus"},
+ {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"},
+ {0x08050000, nullptr, "ClearStepHistory"},
+ {0x080600C2, nullptr, "SetStepHistory"},
+ {0x08070082, nullptr, "GetPlayHistory"},
+ {0x08080000, nullptr, "GetPlayHistoryStart"},
+ {0x08090000, nullptr, "GetPlayHistoryLength"},
+ {0x080A0000, nullptr, "ClearPlayHistory"},
+ {0x080B0080, nullptr, "CalcPlayHistoryStart"},
+ {0x080C0080, nullptr, "SetUserTime"},
+ {0x080D0000, nullptr, "InvalidateSystemTime"},
+ {0x080E0140, nullptr, "NotifyPlayEvent"},
+ {0x080F0000, nullptr, "IsLegacyPowerOff"},
+ {0x08100000, nullptr, "ClearLegacyPowerOff"},
+ {0x08110000, nullptr, "GetShellStatus"},
+ {0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
+ {0x08130000, nullptr, "FormatSavedata"},
+ {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm_sysm.h
new file mode 100644
index 000000000..0f267b214
--- /dev/null
+++ b/src/core/hle/service/ptm_sysm.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_SYSM
+
+namespace PTM_SYSM {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "ptm:sysm";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index 9cc700c46..753180add 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -76,8 +76,6 @@ static void GetShellState(Service::Interface* self) {
cmd_buff[1] = 0;
cmd_buff[2] = shell_open ? 1 : 0;
-
- LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
}
/**
@@ -142,10 +140,10 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
// Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file
// TODO(Subv): In the future we should use the FS service to query this archive
- std::string extsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA);
- ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(extsavedata_directory);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
+ ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true);
if (!ptm_shared_extsavedata->Initialize()) {
- LOG_CRITICAL(Service_PTM, "Could not initialize ExtSaveData archive for the PTM:U service");
+ LOG_CRITICAL(Service_PTM, "Could not initialize SharedExtSaveData archive for the PTM:U service");
return;
}
FileSys::Path archive_path(ptm_shared_extdata_id);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index c5233e687..446ed5164 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,10 +11,12 @@
#include "core/hle/service/am_app.h"
#include "core/hle/service/am_net.h"
#include "core/hle/service/apt_a.h"
+#include "core/hle/service/apt_s.h"
#include "core/hle/service/apt_u.h"
#include "core/hle/service/boss_u.h"
#include "core/hle/service/cecd_u.h"
#include "core/hle/service/cfg/cfg_i.h"
+#include "core/hle/service/cfg/cfg_s.h"
#include "core/hle/service/cfg/cfg_u.h"
#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dsp_dsp.h"
@@ -34,6 +36,7 @@
#include "core/hle/service/nwm_uds.h"
#include "core/hle/service/pm_app.h"
#include "core/hle/service/ptm_u.h"
+#include "core/hle/service/ptm_sysm.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
#include "core/hle/service/ssl_c.h"
@@ -46,36 +49,23 @@ Manager* g_manager = nullptr; ///< Service manager
////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Manager class
-Manager::Manager() {
-}
-
-Manager::~Manager() {
- for(Interface* service : m_services) {
- DeleteService(service->GetPortName());
- }
-}
-
-/// Add a service to the manager (does not create it though)
void Manager::AddService(Interface* service) {
// TOOD(yuriks): Fix error reporting
m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE);
m_services.push_back(service);
}
-/// Removes a service from the manager, also frees memory
void Manager::DeleteService(const std::string& port_name) {
Interface* service = FetchFromPortName(port_name);
m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end());
m_port_map.erase(port_name);
- delete service;
}
-/// Get a Service Interface from its Handle
Interface* Manager::FetchFromHandle(Handle handle) {
- return Kernel::g_handle_table.Get<Interface>(handle);
+ // TODO(yuriks): This function is very suspicious and should probably be exterminated.
+ return Kernel::g_handle_table.Get<Interface>(handle).get();
}
-/// Get a Service Interface from its port
Interface* Manager::FetchFromPortName(const std::string& port_name) {
auto itr = m_port_map.find(port_name);
if (itr == m_port_map.end()) {
@@ -98,10 +88,12 @@ void Init() {
g_manager->AddService(new AM_APP::Interface);
g_manager->AddService(new AM_NET::Interface);
g_manager->AddService(new APT_A::Interface);
+ g_manager->AddService(new APT_S::Interface);
g_manager->AddService(new APT_U::Interface);
g_manager->AddService(new BOSS_U::Interface);
g_manager->AddService(new CECD_U::Interface);
g_manager->AddService(new CFG_I::Interface);
+ g_manager->AddService(new CFG_S::Interface);
g_manager->AddService(new CFG_U::Interface);
g_manager->AddService(new CSND_SND::Interface);
g_manager->AddService(new DSP_DSP::Interface);
@@ -121,6 +113,7 @@ void Init() {
g_manager->AddService(new NWM_UDS::Interface);
g_manager->AddService(new PM_APP::Interface);
g_manager->AddService(new PTM_U::Interface);
+ g_manager->AddService(new PTM_SYSM::Interface);
g_manager->AddService(new SOC_U::Interface);
g_manager->AddService(new SSL_C::Interface);
g_manager->AddService(new Y2R_U::Interface);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 28b4ccd17..e75d5008b 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -33,6 +33,22 @@ class Interface : public Kernel::Session {
// processes.
friend class Manager;
+
+ /**
+ * Creates a function string for logging, complete with the name (or header code, depending
+ * on what's passed in) the port name, and all the cmd_buff arguments.
+ */
+ std::string MakeFunctionString(const std::string& name, const std::string& port_name, const u32* cmd_buff) {
+ // Number of params == bits 0-5 + bits 6-11
+ int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
+
+ std::string function_string = Common::StringFromFormat("function '%s': port=%s", name.c_str(), port_name.c_str());
+ for (int i = 1; i <= num_params; ++i) {
+ function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
+ }
+ return function_string;
+ }
+
public:
std::string GetName() const override { return GetPortName(); }
@@ -72,21 +88,14 @@ public:
auto itr = m_functions.find(cmd_buff[0]);
if (itr == m_functions.end() || itr->second.func == nullptr) {
- // Number of params == bits 0-5 + bits 6-11
- int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
-
- std::string error = "unknown/unimplemented function '%s': port=%s";
- for (int i = 1; i <= num_params; ++i) {
- error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
- }
-
- std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
-
- LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str());
+ std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
+ LOG_ERROR(Service, "%s %s", "unknown/unimplemented", MakeFunctionString(function_name, GetPortName(), cmd_buff).c_str());
// TODO(bunnei): Hack - ignore error
cmd_buff[1] = 0;
return MakeResult<bool>(false);
+ } else {
+ LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName(), cmd_buff).c_str());
}
itr->second.func(this);
@@ -114,29 +123,22 @@ private:
/// Simple class to manage accessing services from ports and UID handles
class Manager {
-
public:
- Manager();
-
- ~Manager();
-
- /// Add a service to the manager (does not create it though)
+ /// Add a service to the manager
void AddService(Interface* service);
- /// Removes a service from the manager (does not delete it though)
+ /// Removes a service from the manager
void DeleteService(const std::string& port_name);
- /// Get a Service Interface from its UID
- Interface* FetchFromHandle(u32 uid);
+ /// Get a Service Interface from its Handle
+ Interface* FetchFromHandle(Handle handle);
/// Get a Service Interface from its port
Interface* FetchFromPortName(const std::string& port_name);
private:
-
std::vector<Interface*> m_services;
std::map<std::string, u32> m_port_map;
-
};
/// Initialize ServiceManager
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index f502c6afe..bb8ee86be 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -7,6 +7,19 @@
#if EMU_PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
+
+// MinGW does not define several errno constants
+#ifndef _MSC_VER
+#define EBADMSG 104
+#define ENODATA 120
+#define ENOMSG 122
+#define ENOSR 124
+#define ENOSTR 125
+#define ETIME 137
+#define EIDRM 2001
+#define ENOLINK 2002
+#endif // _MSC_VER
+
#else
#include <sys/socket.h>
#include <netinet/in.h>
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 912b52adf..ac5f30a28 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -14,16 +14,12 @@ namespace SRV {
static Handle g_event_handle = 0;
static void Initialize(Service::Interface* self) {
- LOG_DEBUG(Service_SRV, "called");
-
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
}
static void GetProcSemaphore(Service::Interface* self) {
- LOG_TRACE(Service_SRV, "called");
-
u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(bunnei): Change to a semaphore once these have been implemented
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index f9e3619dd..b3d873ef0 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -12,6 +12,21 @@
namespace Y2R_U {
+/**
+ * Y2R_U::IsBusyConversion service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Whether the current conversion is of type busy conversion (?)
+ */
+static void IsBusyConversion(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, nullptr, "SetInputFormat"},
{0x00030040, nullptr, "SetOutputFormat"},
@@ -29,7 +44,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00220040, nullptr, "SetAlpha"},
{0x00260000, nullptr, "StartConversion"},
{0x00270000, nullptr, "StopConversion"},
- {0x00280000, nullptr, "IsBusyConversion"},
+ {0x00280000, IsBusyConversion, "IsBusyConversion"},
{0x002A0000, nullptr, "PingProcess"},
{0x002B0000, nullptr, "DriverInitialize"},
{0x002C0000, nullptr, "DriverFinalize"}
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
new file mode 100644
index 000000000..6033a53b4
--- /dev/null
+++ b/src/core/hle/shared_page.cpp
@@ -0,0 +1,80 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "common/log.h"
+
+#include "core/core.h"
+#include "core/mem_map.h"
+#include "core/hle/config_mem.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace SharedPage {
+
+// helper macro to properly align structure members.
+// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
+// depending on the current source line to make sure variable names are unique.
+#define INSERT_PADDING_BYTES_HELPER1(x, y) x ## y
+#define INSERT_PADDING_BYTES_HELPER2(x, y) INSERT_PADDING_BYTES_HELPER1(x, y)
+#define INSERT_PADDING_BYTES(num_words) u8 INSERT_PADDING_BYTES_HELPER2(pad, __LINE__)[(num_words)]
+
+// see http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes
+
+#pragma pack(1)
+struct DateTime {
+ u64 date_time; // 0x0
+ u64 update_tick; // 0x8
+ INSERT_PADDING_BYTES(0x20 - 0x10); // 0x10
+};
+
+struct SharedPageDef {
+ // most of these names are taken from the 3dbrew page linked above.
+ u32 date_time_selector; // 0x0
+ u8 running_hw; // 0x4
+ u8 mcu_hw_info; // 0x5: don't know what the acronyms mean
+ INSERT_PADDING_BYTES(0x20 - 0x6); // 0x6
+ DateTime date_time_0; // 0x20
+ DateTime date_time_1; // 0x40
+ u8 wifi_macaddr[6]; // 0x60
+ u8 wifi_unknown1; // 0x66: 3dbrew says these are "Likely wifi hardware related"
+ u8 wifi_unknown2; // 0x67
+ INSERT_PADDING_BYTES(0x80 - 0x68); // 0x68
+ float sliderstate_3d; // 0x80
+ u8 ledstate_3d; // 0x84
+ INSERT_PADDING_BYTES(0xA0 - 0x85); // 0x85
+ u64 menu_title_id; // 0xA0
+ u64 active_menu_title_id; // 0xA8
+ INSERT_PADDING_BYTES(0x1000 - 0xB0); // 0xB0
+};
+#pragma pack()
+
+static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
+static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page structure size is wrong");
+
+static SharedPageDef shared_page;
+
+template <typename T>
+inline void Read(T &var, const u32 addr) {
+ u32 offset = addr - Memory::SHARED_PAGE_VADDR;
+ var = *(reinterpret_cast<T*>(((uintptr_t)&shared_page) + offset));
+}
+
+// Explicitly instantiate template functions because we aren't defining this in the header:
+template void Read<u64>(u64 &var, const u32 addr);
+template void Read<u32>(u32 &var, const u32 addr);
+template void Read<u16>(u16 &var, const u32 addr);
+template void Read<u8>(u8 &var, const u32 addr);
+
+void Set3DSlider(float amount) {
+ shared_page.sliderstate_3d = amount;
+ shared_page.ledstate_3d = (amount == 0.0f); // off when non-zero
+}
+
+void Init() {
+ shared_page.running_hw = 0x1; // product
+ Set3DSlider(0.0f);
+}
+
+} // namespace
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h
new file mode 100644
index 000000000..8f93545ec
--- /dev/null
+++ b/src/core/hle/shared_page.h
@@ -0,0 +1,26 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+/**
+ * The shared page stores various runtime configuration settings. This memory page is
+ * read-only for user processes (there is a bit in the header that grants the process
+ * write access, according to 3dbrew; this is not emulated)
+ */
+
+#include "common/common_types.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace SharedPage {
+
+template <typename T>
+void Read(T &var, const u32 addr);
+
+void Set3DSlider(float amount);
+
+void Init();
+
+} // namespace
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index c25409a9f..a487f757c 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -7,6 +7,7 @@
#include "common/string_util.h"
#include "common/symbols.h"
+#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/kernel/address_arbiter.h"
@@ -15,6 +16,7 @@
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
#include "core/hle/function_wrappers.h"
#include "core/hle/result.h"
@@ -23,6 +25,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SVC
+using Kernel::SharedPtr;
+
namespace SVC {
enum ControlMemoryOperation {
@@ -92,7 +96,7 @@ static Result ConnectToPort(Handle* out, const char* port_name) {
/// Synchronize to an OS service
static Result SendSyncRequest(Handle handle) {
- Kernel::Session* session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
+ SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
if (session == nullptr) {
return InvalidHandle(ErrorModule::Kernel).raw;
}
@@ -116,20 +120,19 @@ static Result CloseHandle(Handle handle) {
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
- // TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
-
- Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handle);
+ SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle);
if (object == nullptr)
return InvalidHandle(ErrorModule::Kernel).raw;
- LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
- object->GetName().c_str(), nano_seconds);
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
+ object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
ResultVal<bool> wait = object->WaitSynchronization();
// Check for next thread to schedule
if (wait.Succeeded() && *wait) {
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds);
HLE::Reschedule(__func__);
}
@@ -139,6 +142,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
s64 nano_seconds) {
+
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
bool unlock_all = true;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
@@ -148,12 +152,12 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
// Iterate through each handle, synchronize kernel object
for (s32 i = 0; i < handle_count; i++) {
- Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handles[i]);
+ SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]);
if (object == nullptr)
return InvalidHandle(ErrorModule::Kernel).raw;
- LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
- object->GetName().c_str());
+ LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i],
+ object->GetTypeName().c_str(), object->GetName().c_str());
// TODO(yuriks): Verify how the real function behaves when an error happens here
ResultVal<bool> wait_result = object->WaitSynchronization();
@@ -180,7 +184,6 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
/// Create an address arbiter (to allocate access to shared resources)
static Result CreateAddressArbiter(u32* arbiter) {
- LOG_TRACE(Kernel_SVC, "called");
Handle handle = Kernel::CreateAddressArbiter();
*arbiter = handle;
return 0;
@@ -191,7 +194,7 @@ static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value,
LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
address, type, value);
return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
- address, value).raw;
+ address, value, nanoseconds).raw;
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -220,6 +223,8 @@ static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit,
/// Creates a new thread
static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+ using Kernel::Thread;
+
std::string name;
if (Symbols::HasSymbol(entry_point)) {
TSymbol symbol = Symbols::GetSymbol(entry_point);
@@ -228,41 +233,53 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top
name = Common::StringFromFormat("unknown-%08x", entry_point);
}
- Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id,
- stack_top);
+ ResultVal<SharedPtr<Thread>> thread_res = Kernel::Thread::Create(
+ name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE);
+ if (thread_res.Failed())
+ return thread_res.Code().raw;
+ SharedPtr<Thread> thread = std::move(*thread_res);
- Core::g_app_core->SetReg(1, thread);
+ // TODO(yuriks): Create new handle instead of using built-in
+ Core::g_app_core->SetReg(1, thread->GetHandle());
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
"threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
- name.c_str(), arg, stack_top, priority, processor_id, thread);
+ name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle());
+
+ if (THREADPROCESSORID_1 == processor_id) {
+ LOG_WARNING(Kernel_SVC,
+ "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling");
+ }
return 0;
}
/// Called when a thread exits
-static u32 ExitThread() {
- Handle thread = Kernel::GetCurrentThreadHandle();
-
- LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
+static void ExitThread() {
+ LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC());
- Kernel::StopThread(thread, __func__);
+ Kernel::GetCurrentThread()->Stop(__func__);
HLE::Reschedule(__func__);
- return 0;
}
/// Gets the priority for the specified thread
static Result GetThreadPriority(s32* priority, Handle handle) {
- ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle);
- if (priority_result.Succeeded()) {
- *priority = *priority_result;
- }
- return priority_result.Code().raw;
+ const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
+ if (thread == nullptr)
+ return InvalidHandle(ErrorModule::Kernel).raw;
+
+ *priority = thread->GetPriority();
+ return RESULT_SUCCESS.raw;
}
/// Sets the priority for the specified thread
static Result SetThreadPriority(Handle handle, s32 priority) {
- return Kernel::SetThreadPriority(handle, priority).raw;
+ SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
+ if (thread == nullptr)
+ return InvalidHandle(ErrorModule::Kernel).raw;
+
+ thread->SetPriority(priority);
+ return RESULT_SUCCESS.raw;
}
/// Create a mutex
@@ -283,8 +300,13 @@ static Result ReleaseMutex(Handle handle) {
/// Get the ID for the specified thread.
static Result GetThreadId(u32* thread_id, Handle handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
- ResultCode result = Kernel::GetThreadId(thread_id, handle);
- return result.raw;
+
+ const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
+ if (thread == nullptr)
+ return InvalidHandle(ErrorModule::Kernel).raw;
+
+ *thread_id = thread->GetThreadId();
+ return RESULT_SUCCESS.raw;
}
/// Creates a semaphore
@@ -338,12 +360,42 @@ static Result ClearEvent(Handle evt) {
return Kernel::ClearEvent(evt).raw;
}
+/// Creates a timer
+static Result CreateTimer(Handle* handle, u32 reset_type) {
+ ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type));
+ LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ reset_type, *handle);
+ return res.raw;
+}
+
+/// Clears a timer
+static Result ClearTimer(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::ClearTimer(handle).raw;
+}
+
+/// Starts a timer
+static Result SetTimer(Handle handle, s64 initial, s64 interval) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::SetTimer(handle, initial, interval).raw;
+}
+
+/// Cancels a timer
+static Result CancelTimer(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::CancelTimer(handle).raw;
+}
+
/// Sleep the current thread
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds);
+
HLE::Reschedule(__func__);
}
@@ -374,7 +426,7 @@ const HLE::FunctionDef SVC_Table[] = {
{0x06, nullptr, "GetProcessIdealProcessor"},
{0x07, nullptr, "SetProcessIdealProcessor"},
{0x08, HLE::Wrap<CreateThread>, "CreateThread"},
- {0x09, HLE::Wrap<ExitThread>, "ExitThread"},
+ {0x09, ExitThread, "ExitThread"},
{0x0A, HLE::Wrap<SleepThread>, "SleepThread"},
{0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"},
{0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"},
@@ -391,10 +443,10 @@ const HLE::FunctionDef SVC_Table[] = {
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
- {0x1A, nullptr, "CreateTimer"},
- {0x1B, nullptr, "SetTimer"},
- {0x1C, nullptr, "CancelTimer"},
- {0x1D, nullptr, "ClearTimer"},
+ {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"},
+ {0x1B, HLE::Wrap<SetTimer>, "SetTimer"},
+ {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"},
+ {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"},
{0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
{0x20, nullptr, "UnmapMemoryBlock"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index ad780818e..5d020a5ba 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -20,21 +20,6 @@ struct PageInfo {
u32 flags;
};
-struct ThreadContext {
- u32 cpu_registers[13];
- u32 sp;
- u32 lr;
- u32 pc;
- u32 cpsr;
- u32 fpu_registers[32];
- u32 fpscr;
- u32 fpexc;
-
- // These are not part of native ThreadContext, but needed by emu
- u32 reg_15;
- u32 mode;
-};
-
enum ResetType {
RESETTYPE_ONESHOT,
RESETTYPE_STICKY,
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index e346e0ad6..58eec3005 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -4,9 +4,12 @@
#include "common/common_types.h"
+#include "core/arm/arm_interface.h"
+
#include "core/settings.h"
#include "core/core.h"
#include "core/mem_map.h"
+#include "core/core_timing.h"
#include "core/hle/hle.h"
#include "core/hle/service/gsp_gpu.h"
@@ -22,14 +25,17 @@ namespace GPU {
Regs g_regs;
-bool g_skip_frame = false; ///< True if the current frame was skipped
+/// True if the current frame was skipped
+bool g_skip_frame = false;
-static u64 frame_ticks = 0; ///< 268MHz / gpu_refresh_rate frames per second
-static u64 line_ticks = 0; ///< Number of ticks for a screen line
-static u32 cur_line = 0; ///< Current screen line
-static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update
-static u64 frame_count = 0; ///< Number of frames drawn
-static bool last_skip_frame = false; ///< True if the last frame was skipped
+/// 268MHz / gpu_refresh_rate frames per second
+static u64 frame_ticks;
+/// Event id for CoreTiming
+static int vblank_event;
+/// Total number of frames drawn
+static u64 frame_count;
+/// True if the last frame was skipped
+static bool last_skip_frame = false;
template <typename T>
inline void Read(T &var, const u32 raw_addr) {
@@ -77,6 +83,12 @@ inline void Write(u32 addr, const T data) {
*ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
+
+ if (!is_second_filler) {
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0);
+ } else {
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
+ }
}
break;
}
@@ -88,22 +100,25 @@ inline void Write(u32 addr, const T data) {
u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
+ // Cheap emulation of horizontal scaling: Just skip each second pixel of the
+ // input framebuffer. We keep track of this in the pixel_skip variable.
+ unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
+
+ u32 output_width = config.output_width / pixel_skip;
+
for (u32 y = 0; y < config.output_height; ++y) {
// TODO: Why does the register seem to hold twice the framebuffer width?
- for (u32 x = 0; x < config.output_width; ++x) {
+
+ for (u32 x = 0; x < output_width; ++x) {
struct {
int r, g, b, a;
} source_color = { 0, 0, 0, 0 };
- // Cheap emulation of horizontal scaling: Just skip each second pixel of the
- // input framebuffer. We keep track of this in the pixel_skip variable.
- unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
-
switch (config.input_format) {
case Regs::PixelFormat::RGBA8:
{
// TODO: Most likely got the component order messed up.
- u8* srcptr = source_pointer + x * 4 * pixel_skip + y * config.input_width * 4 * pixel_skip;
+ u8* srcptr = source_pointer + (x * pixel_skip + y * config.input_width) * 4;
source_color.r = srcptr[0]; // blue
source_color.g = srcptr[1]; // green
source_color.b = srcptr[2]; // red
@@ -131,7 +146,7 @@ inline void Write(u32 addr, const T data) {
case Regs::PixelFormat::RGB8:
{
// TODO: Most likely got the component order messed up.
- u8* dstptr = dest_pointer + x * 3 + y * config.output_width * 3;
+ u8* dstptr = dest_pointer + (x + y * output_width) * 3;
dstptr[0] = source_color.r; // blue
dstptr[1] = source_color.g; // green
dstptr[2] = source_color.b; // red
@@ -146,10 +161,12 @@ inline void Write(u32 addr, const T data) {
}
LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
- config.output_height * config.output_width * 4,
+ config.output_height * output_width * 4,
config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
- config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
+ config.GetPhysicalOutputAddress(), (u32)output_width, (u32)config.output_height,
config.output_format.Value());
+
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
}
break;
}
@@ -184,51 +201,39 @@ template void Write<u16>(u32 addr, const u16 data);
template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
-void Update() {
+static void VBlankCallback(u64 userdata, int cycles_late) {
auto& framebuffer_top = g_regs.framebuffer_config[0];
- // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical
- // blank, we need to simulate it. Based on testing, it seems that retail applications work more
- // accurately when this is signalled between thread switches.
-
- if (HLE::g_reschedule) {
- u64 current_ticks = Core::g_app_core->GetTicks();
- u32 num_lines = static_cast<u32>((current_ticks - last_update_tick) / line_ticks);
-
- // Synchronize line...
- if (num_lines > 0) {
- GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
- cur_line += num_lines;
- last_update_tick += (num_lines * line_ticks);
- }
-
- // Synchronize frame...
- if (cur_line >= framebuffer_top.height) {
- cur_line = 0;
- frame_count++;
- last_skip_frame = g_skip_frame;
- g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
-
- // Swap buffers based on the frameskip mode, which is a little bit tricky. When
- // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
- // So, we should only swap frames if the last frame was rendered. The rules are:
- // - If frameskip == 0 (disabled), always swap buffers
- // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
- // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
- if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
- Settings::values.frame_skip == 0) {
- VideoCore::g_renderer->SwapBuffers();
- }
-
- // Signal to GSP that GPU interrupt has occurred
- GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
-
- // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
- // until we can emulate DSP interrupts, this is probably the only reasonable place to do
- // this. Certain games expect this to be periodically signaled.
- DSP_DSP::SignalInterrupt();
- }
+ frame_count++;
+ last_skip_frame = g_skip_frame;
+ g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
+
+ // Swap buffers based on the frameskip mode, which is a little bit tricky. When
+ // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
+ // So, we should only swap frames if the last frame was rendered. The rules are:
+ // - If frameskip == 0 (disabled), always swap buffers
+ // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
+ // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
+ if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
+ Settings::values.frame_skip == 0) {
+ VideoCore::g_renderer->SwapBuffers();
}
+
+ // Signal to GSP that GPU interrupt has occurred
+ // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
+ // screen, or if both use the same interrupts and these two instead determine the
+ // beginning and end of the VBlank period. If needed, split the interrupt firing into
+ // two different intervals.
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
+
+ // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
+ // until we can emulate DSP interrupts, this is probably the only reasonable place to do
+ // this. Certain games expect this to be periodically signaled.
+ DSP_DSP::SignalInterrupt();
+
+ // Reschedule recurrent event
+ CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
}
/// Initialize hardware
@@ -245,8 +250,8 @@ void Init() {
framebuffer_top.address_right1 = 0x18273000;
framebuffer_top.address_right2 = 0x182B9800;
framebuffer_sub.address_left1 = 0x1848F000;
- //framebuffer_sub.address_left2 = unknown;
- framebuffer_sub.address_right1 = 0x184C7800;
+ framebuffer_sub.address_left2 = 0x184C7800;
+ //framebuffer_sub.address_right1 = unknown;
//framebuffer_sub.address_right2 = unknown;
framebuffer_top.width = 240;
@@ -262,12 +267,12 @@ void Init() {
framebuffer_sub.active_fb = 0;
frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
- line_ticks = (GPU::frame_ticks / framebuffer_top.height);
- cur_line = 0;
- last_update_tick = Core::g_app_core->GetTicks();
last_skip_frame = false;
g_skip_frame = false;
+ vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
+ CoreTiming::ScheduleEvent(frame_ticks, vblank_event);
+
LOG_DEBUG(HW_GPU, "initialized OK");
}
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 7de055232..7c3a17ee5 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -252,9 +252,6 @@ void Read(T &var, const u32 addr);
template <typename T>
void Write(u32 addr, const T data);
-/// Update hardware
-void Update();
-
/// Initialize hardware
void Init();
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 848ab5348..503200629 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -75,7 +75,6 @@ template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
void Update() {
- GPU::Update();
}
/// Initialize hardware
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 4d072871a..958dd03e8 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -13,11 +13,9 @@
#include "3dsx.h"
-
namespace Loader {
-
-/**
+/**
* File layout:
* - File header
* - Code, rodata and data relocation table headers
@@ -46,7 +44,6 @@ enum THREEDSX_Error {
static const u32 RELOCBUFSIZE = 512;
// File header
-static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
#pragma pack(1)
struct THREEDSX_Header
{
@@ -64,9 +61,9 @@ struct THREEDSX_Header
struct THREEDSX_RelocHdr
{
// # of absolute relocations (that is, fix address to post-relocation memory layout)
- u32 cross_segment_absolute;
+ u32 cross_segment_absolute;
// # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
- u32 cross_segment_relative;
+ u32 cross_segment_relative;
// more?
// Relocations are written in this order:
@@ -88,12 +85,7 @@ struct THREEloadinfo
u32 seg_sizes[3];
};
-class THREEDSXReader {
-public:
- static int Load3DSXFile(const std::string& filename, u32 base_addr);
-};
-
-static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets)
+static u32 TranslateAddr(u32 addr, const THREEloadinfo *loadinfo, u32* offsets)
{
if (addr < offsets[0])
return loadinfo->seg_addrs[0] + addr;
@@ -102,12 +94,14 @@ static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets)
return loadinfo->seg_addrs[2] + addr - offsets[1];
}
-int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
+static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr)
{
- FileUtil::IOFile file(filename, "rb");
- if (!file.IsOpen()) {
+ if (!file.IsOpen())
return ERROR_FILE;
- }
+
+ // Reset read pointer in case this file has been read before.
+ file.Seek(0, SEEK_SET);
+
THREEDSX_Header hdr;
if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr))
return ERROR_READ;
@@ -136,8 +130,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
// Read the relocation headers
u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size);
- for (u32 current_segment = 0; current_segment < 3; current_segment++) {
- if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4)
+ for (unsigned current_segment : {0, 1, 2}) {
+ size_t size = n_reloc_tables * 4;
+ if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size)
return ERROR_READ;
}
@@ -153,9 +148,9 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size);
// Relocate the segments
- for (u32 current_segment = 0; current_segment < 3; current_segment++) {
- for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) {
- u32 n_relocs = relocs[current_segment*n_reloc_tables + current_segment_reloc_table];
+ for (unsigned current_segment : {0, 1, 2}) {
+ for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) {
+ u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table];
if (current_segment_reloc_table >= 2) {
// We are not using this table - ignore it because we don't know what it dose
file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR);
@@ -164,29 +159,35 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
static THREEDSX_Reloc reloc_table[RELOCBUFSIZE];
u32* pos = (u32*)loadinfo.seg_ptrs[current_segment];
- u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4);
+ const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4);
while (n_relocs) {
u32 remaining = std::min(RELOCBUFSIZE, n_relocs);
n_relocs -= remaining;
- if (file.ReadBytes(reloc_table, remaining*sizeof(THREEDSX_Reloc)) != remaining*sizeof(THREEDSX_Reloc))
+ if (file.ReadBytes(reloc_table, remaining * sizeof(THREEDSX_Reloc)) != remaining * sizeof(THREEDSX_Reloc))
return ERROR_READ;
- for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
- LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n",
- current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch);
- pos += reloc_table[current_inprogress].skip;
- s32 num_patches = reloc_table[current_inprogress].patch;
+ for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
+ const auto& table = reloc_table[current_inprogress];
+ LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", current_segment_reloc_table,
+ (u32)table.skip, (u32)table.patch);
+ pos += table.skip;
+ s32 num_patches = table.patch;
while (0 < num_patches && pos < end_pos) {
u32 in_addr = (char*)pos - (char*)&all_mem[0];
u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n",
- base_addr + in_addr, addr, current_segment_reloc_table, *pos);
+ base_addr + in_addr, addr, current_segment_reloc_table, *pos);
switch (current_segment_reloc_table) {
- case 0: *pos = (addr); break;
- case 1: *pos = (addr - in_addr); break;
- default: break; //this should never happen
+ case 0:
+ *pos = (addr);
+ break;
+ case 1:
+ *pos = (addr - in_addr);
+ break;
+ default:
+ break; //this should never happen
}
pos++;
num_patches--;
@@ -207,28 +208,30 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
return ERROR_NONE;
}
- /// AppLoader_DSX constructor
- AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) {
- }
+FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) {
+ u32 magic;
+ file.Seek(0, SEEK_SET);
+ if (1 != file.ReadArray<u32>(&magic, 1))
+ return FileType::Error;
- /// AppLoader_DSX destructor
- AppLoader_THREEDSX::~AppLoader_THREEDSX() {
- }
+ if (MakeMagic('3', 'D', 'S', 'X') == magic)
+ return FileType::THREEDSX;
- /**
- * Loads a 3DSX file
- * @return Success on success, otherwise Error
- */
- ResultStatus AppLoader_THREEDSX::Load() {
- LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str());
- FileUtil::IOFile file(filename, "rb");
- if (file.IsOpen()) {
- THREEDSXReader::Load3DSXFile(filename, 0x00100000);
- Kernel::LoadExec(0x00100000);
- } else {
- return ResultStatus::Error;
- }
- return ResultStatus::Success;
- }
+ return FileType::Error;
+}
+
+ResultStatus AppLoader_THREEDSX::Load() {
+ if (is_loaded)
+ return ResultStatus::ErrorAlreadyLoaded;
+
+ if (!file->IsOpen())
+ return ResultStatus::Error;
+
+ Load3DSXFile(*file, 0x00100000);
+ Kernel::LoadExec(0x00100000);
+
+ is_loaded = true;
+ return ResultStatus::Success;
+}
} // namespace Loader
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
index da8836662..a11667400 100644
--- a/src/core/loader/3dsx.h
+++ b/src/core/loader/3dsx.h
@@ -15,18 +15,20 @@ namespace Loader {
/// Loads an 3DSX file
class AppLoader_THREEDSX final : public AppLoader {
public:
- AppLoader_THREEDSX(const std::string& filename);
- ~AppLoader_THREEDSX() override;
+ AppLoader_THREEDSX(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
+
+ /**
+ * Returns the type of the file
+ * @param file FileUtil::IOFile open file
+ * @return FileType found, or FileType::Error if this loader doesn't know it
+ */
+ static FileType IdentifyType(FileUtil::IOFile& file);
/**
* Load the bootable file
* @return ResultStatus result of function
*/
ResultStatus Load() override;
-
-private:
- std::string filename;
- bool is_loaded;
};
} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 3ca60c072..e7e5df408 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -18,25 +18,25 @@
// File type
enum ElfType {
- ET_NONE = 0,
- ET_REL = 1,
- ET_EXEC = 2,
- ET_DYN = 3,
- ET_CORE = 4,
+ ET_NONE = 0,
+ ET_REL = 1,
+ ET_EXEC = 2,
+ ET_DYN = 3,
+ ET_CORE = 4,
ET_LOPROC = 0xFF00,
ET_HIPROC = 0xFFFF,
};
// Machine/Architecture
enum ElfMachine {
- EM_NONE = 0,
- EM_M32 = 1,
+ EM_NONE = 0,
+ EM_M32 = 1,
EM_SPARC = 2,
- EM_386 = 3,
- EM_68K = 4,
- EM_88K = 5,
- EM_860 = 7,
- EM_MIPS = 8
+ EM_386 = 3,
+ EM_68K = 4,
+ EM_88K = 5,
+ EM_860 = 7,
+ EM_MIPS = 8
};
// File version
@@ -54,12 +54,6 @@ enum ElfMachine {
#define EI_PAD 7
#define EI_NIDENT 16
-// Magic number
-#define ELFMAG0 0x7F
-#define ELFMAG1 'E'
-#define ELFMAG2 'L'
-#define ELFMAG3 'F'
-
// Sections constants
// Section types
@@ -83,10 +77,10 @@ enum ElfMachine {
// Section flags
enum ElfSectionFlags
{
- SHF_WRITE = 0x1,
- SHF_ALLOC = 0x2,
+ SHF_WRITE = 0x1,
+ SHF_ALLOC = 0x2,
SHF_EXECINSTR = 0x4,
- SHF_MASKPROC = 0xF0000000,
+ SHF_MASKPROC = 0xF0000000,
};
// Segment types
@@ -100,11 +94,11 @@ enum ElfSectionFlags
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7FFFFFFF
-typedef unsigned int Elf32_Addr;
+typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
-typedef unsigned int Elf32_Off;
-typedef signed int Elf32_Sword;
-typedef unsigned int Elf32_Word;
+typedef unsigned int Elf32_Off;
+typedef signed int Elf32_Sword;
+typedef unsigned int Elf32_Word;
////////////////////////////////////////////////////////////////////////////////////////////////////
// ELF file header
@@ -188,7 +182,6 @@ private:
public:
ElfReader(void *ptr);
- ~ElfReader() { }
u32 Read32(int off) const { return base32[off >> 2]; }
@@ -197,7 +190,7 @@ public:
ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); }
u32 GetEntryPoint() const { return entryPoint; }
u32 GetFlags() const { return (u32)(header->e_flags); }
- bool LoadInto(u32 vaddr);
+ void LoadInto(u32 vaddr);
bool LoadSymbols();
int GetNumSegments() const { return (int)(header->e_phnum); }
@@ -229,11 +222,11 @@ public:
ElfReader::ElfReader(void *ptr) {
base = (char*)ptr;
- base32 = (u32 *)ptr;
+ base32 = (u32*)ptr;
header = (Elf32_Ehdr*)ptr;
- segments = (Elf32_Phdr *)(base + header->e_phoff);
- sections = (Elf32_Shdr *)(base + header->e_shoff);
+ segments = (Elf32_Phdr*)(base + header->e_phoff);
+ sections = (Elf32_Shdr*)(base + header->e_shoff);
entryPoint = header->e_entry;
@@ -245,7 +238,7 @@ const char *ElfReader::GetSectionName(int section) const {
return nullptr;
int name_offset = sections[section].sh_name;
- char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx);
+ const char* ptr = (char*)GetSectionDataPtr(header->e_shstrndx);
if (ptr)
return ptr + name_offset;
@@ -253,7 +246,7 @@ const char *ElfReader::GetSectionName(int section) const {
return nullptr;
}
-bool ElfReader::LoadInto(u32 vaddr) {
+void ElfReader::LoadInto(u32 vaddr) {
LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx);
// Should we relocate?
@@ -271,20 +264,19 @@ bool ElfReader::LoadInto(u32 vaddr) {
u32 segment_addr[32];
u32 base_addr = relocate ? vaddr : 0;
- for (int i = 0; i < header->e_phnum; i++) {
- Elf32_Phdr *p = segments + i;
+ for (unsigned i = 0; i < header->e_phnum; i++) {
+ Elf32_Phdr* p = segments + i;
LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
- p->p_filesz, p->p_memsz);
+ p->p_filesz, p->p_memsz);
if (p->p_type == PT_LOAD) {
segment_addr[i] = base_addr + p->p_vaddr;
memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz);
LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
- p->p_memsz);
+ p->p_memsz);
}
}
LOG_DEBUG(Loader, "Done loading.");
- return true;
}
SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const {
@@ -305,9 +297,9 @@ bool ElfReader::LoadSymbols() {
const char *stringBase = (const char *)GetSectionDataPtr(stringSection);
//We have a symbol table!
- Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
+ Elf32_Sym* symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
- for (int sym = 0; sym < numSymbols; sym++) {
+ for (unsigned sym = 0; sym < numSymbols; sym++) {
int size = symtab[sym].st_size;
if (size == 0)
continue;
@@ -330,40 +322,38 @@ bool ElfReader::LoadSymbols() {
namespace Loader {
-/// AppLoader_ELF constructor
-AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) {
- this->filename = filename;
-}
+FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
+ u32 magic;
+ file.Seek(0, SEEK_SET);
+ if (1 != file.ReadArray<u32>(&magic, 1))
+ return FileType::Error;
+
+ if (MakeMagic('\x7f', 'E', 'L', 'F') == magic)
+ return FileType::ELF;
-/// AppLoader_NCCH destructor
-AppLoader_ELF::~AppLoader_ELF() {
+ return FileType::Error;
}
-/**
- * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
- * @param error_string Pointer to string to put error message if an error has occurred
- * @todo Move NCSD parsing out of here and create a separate function for loading these
- * @return True on success, otherwise false
- */
ResultStatus AppLoader_ELF::Load() {
- LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str());
-
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
- FileUtil::IOFile file(filename, "rb");
+ if (!file->IsOpen())
+ return ResultStatus::Error;
- if (file.IsOpen()) {
- u32 size = (u32)file.GetSize();
- std::unique_ptr<u8[]> buffer(new u8[size]);
- file.ReadBytes(&buffer[0], size);
+ // Reset read pointer in case this file has been read before.
+ file->Seek(0, SEEK_SET);
- ElfReader elf_reader(&buffer[0]);
- elf_reader.LoadInto(0x00100000);
- Kernel::LoadExec(elf_reader.GetEntryPoint());
- } else {
+ u32 size = static_cast<u32>(file->GetSize());
+ std::unique_ptr<u8[]> buffer(new u8[size]);
+ if (file->ReadBytes(&buffer[0], size) != size)
return ResultStatus::Error;
- }
+
+ ElfReader elf_reader(&buffer[0]);
+ elf_reader.LoadInto(0x00100000);
+ Kernel::LoadExec(elf_reader.GetEntryPoint());
+
+ is_loaded = true;
return ResultStatus::Success;
}
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index c221cce6d..b6e6651f5 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -15,18 +15,20 @@ namespace Loader {
/// Loads an ELF/AXF file
class AppLoader_ELF final : public AppLoader {
public:
- AppLoader_ELF(const std::string& filename);
- ~AppLoader_ELF() override;
+ AppLoader_ELF(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
+
+ /**
+ * Returns the type of the file
+ * @param file FileUtil::IOFile open file
+ * @return FileType found, or FileType::Error if this loader doesn't know it
+ */
+ static FileType IdentifyType(FileUtil::IOFile& file);
/**
* Load the bootable file
* @return ResultStatus result of function
*/
ResultStatus Load() override;
-
-private:
- std::string filename;
- bool is_loaded;
};
} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 45cf425df..60460292d 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -19,11 +19,32 @@ namespace Loader {
/**
* Identifies the type of a bootable file
+ * @param file open file
+ * @return FileType of file
+ */
+static FileType IdentifyFile(FileUtil::IOFile& file) {
+ FileType type;
+
+#define CHECK_TYPE(loader) \
+ type = AppLoader_##loader::IdentifyType(file); \
+ if (FileType::Error != type) \
+ return type;
+
+ CHECK_TYPE(THREEDSX)
+ CHECK_TYPE(ELF)
+ CHECK_TYPE(NCCH)
+
+#undef CHECK_TYPE
+
+ return FileType::Unknown;
+}
+
+/**
+ * Guess the type of a bootable file from its extension
* @param filename String filename of bootable file
- * @todo (ShizZy) this function sucks... make it actually check file contents etc.
* @return FileType of file
*/
-FileType IdentifyFile(const std::string &filename) {
+static FileType GuessFromFilename(const std::string& filename) {
if (filename.size() == 0) {
LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
return FileType::Error;
@@ -34,47 +55,74 @@ FileType IdentifyFile(const std::string &filename) {
return FileType::Unknown;
std::string extension = Common::ToLower(filename.substr(extension_loc));
- // TODO(bunnei): Do actual filetype checking instead of naively checking the extension
- if (extension == ".elf") {
+ if (extension == ".elf")
return FileType::ELF;
- } else if (extension == ".axf") {
+ else if (extension == ".axf")
return FileType::ELF;
- } else if (extension == ".cxi") {
+ else if (extension == ".cxi")
return FileType::CXI;
- } else if (extension == ".cci") {
+ else if (extension == ".cci")
return FileType::CCI;
- } else if (extension == ".bin") {
+ else if (extension == ".bin")
return FileType::BIN;
- } else if (extension == ".3ds") {
+ else if (extension == ".3ds")
return FileType::CCI;
- } else if (extension == ".3dsx") {
+ else if (extension == ".3dsx")
return FileType::THREEDSX;
- }
return FileType::Unknown;
}
-/**
- * Identifies and loads a bootable file
- * @param filename String filename of bootable file
- * @return ResultStatus result of function
- */
+static const char* GetFileTypeString(FileType type) {
+ switch (type) {
+ case FileType::CCI:
+ return "NCSD";
+ case FileType::CXI:
+ return "NCCH";
+ case FileType::ELF:
+ return "ELF";
+ case FileType::THREEDSX:
+ return "3DSX";
+ case FileType::BIN:
+ return "raw";
+ case FileType::Error:
+ case FileType::Unknown:
+ return "unknown";
+ }
+}
+
ResultStatus LoadFile(const std::string& filename) {
- LOG_INFO(Loader, "Loading file %s...", filename.c_str());
+ std::unique_ptr<FileUtil::IOFile> file(new FileUtil::IOFile(filename, "rb"));
+ if (!file->IsOpen()) {
+ LOG_ERROR(Loader, "Failed to load file %s", filename.c_str());
+ return ResultStatus::Error;
+ }
+
+ FileType type = IdentifyFile(*file);
+ FileType filename_type = GuessFromFilename(filename);
+
+ if (type != filename_type) {
+ LOG_WARNING(Loader, "File %s has a different type than its extension.", filename.c_str());
+ if (FileType::Unknown == type)
+ type = filename_type;
+ }
- switch (IdentifyFile(filename)) {
+ LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
+
+ switch (type) {
//3DSX file format...
case FileType::THREEDSX:
- return AppLoader_THREEDSX(filename).Load();
+ return AppLoader_THREEDSX(std::move(file)).Load();
// Standard ELF file format...
case FileType::ELF:
- return AppLoader_ELF(filename).Load();
+ return AppLoader_ELF(std::move(file)).Load();
// NCCH/NCSD container formats...
case FileType::CXI:
- case FileType::CCI: {
- AppLoader_NCCH app_loader(filename);
+ case FileType::CCI:
+ {
+ AppLoader_NCCH app_loader(std::move(file));
// Load application and RomFS
if (ResultStatus::Success == app_loader.Load()) {
@@ -88,16 +136,11 @@ ResultStatus LoadFile(const std::string& filename) {
// Raw BIN file format...
case FileType::BIN:
{
- LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str());
-
- FileUtil::IOFile file(filename, "rb");
-
- if (file.IsOpen()) {
- file.ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), (size_t)file.GetSize());
- Kernel::LoadExec(Memory::EXEFS_CODE_VADDR);
- } else {
+ size_t size = (size_t)file->GetSize();
+ if (file->ReadBytes(Memory::GetPointer(Memory::EXEFS_CODE_VADDR), size) != size)
return ResultStatus::Error;
- }
+
+ Kernel::LoadExec(Memory::EXEFS_CODE_VADDR);
return ResultStatus::Success;
}
@@ -106,10 +149,11 @@ ResultStatus LoadFile(const std::string& filename) {
// IdentifyFile could know identify file type...
case FileType::Unknown:
-
- default:
+ {
+ LOG_CRITICAL(Loader, "File %s is of unknown type.");
return ResultStatus::ErrorInvalidFormat;
}
+ }
return ResultStatus::Error;
}
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index ec5534d41..7456b019b 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -7,6 +7,7 @@
#include <vector>
#include "common/common.h"
+#include "common/file_util.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Loader namespace
@@ -37,10 +38,14 @@ enum class ResultStatus {
ErrorMemoryAllocationFailed,
};
+static u32 MakeMagic(char a, char b, char c, char d) {
+ return a | b << 8 | c << 16 | d << 24;
+}
+
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
- AppLoader() { }
+ AppLoader(std::unique_ptr<FileUtil::IOFile>&& file) : file(std::move(file)) { }
virtual ~AppLoader() { }
/**
@@ -93,14 +98,11 @@ public:
virtual ResultStatus ReadRomFS(std::vector<u8>& buffer) const {
return ResultStatus::ErrorNotImplemented;
}
-};
-/**
- * Identifies the type of a bootable file
- * @param filename String filename of bootable file
- * @return FileType of file
- */
-FileType IdentifyFile(const std::string &filename);
+protected:
+ std::unique_ptr<FileUtil::IOFile> file;
+ bool is_loaded = false;
+};
/**
* Identifies and loads a bootable file
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 0dc21699e..aaaa4d650 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -4,8 +4,6 @@
#include <memory>
-#include "common/file_util.h"
-
#include "core/loader/ncch.h"
#include "core/hle/kernel/kernel.h"
#include "core/mem_map.h"
@@ -15,8 +13,8 @@
namespace Loader {
-static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
-static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
+static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
+static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
/**
* Get the decompressed size of an LZSS compressed ExeFS file
@@ -24,7 +22,7 @@ static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
* @param size Size of compressed buffer
* @return Size of decompressed buffer
*/
-static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
+static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
u32 offset_size = *(u32*)(buffer + size - 4);
return offset_size + size;
}
@@ -37,9 +35,9 @@ static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
* @param decompressed_size Size of decompressed buffer
* @return True on success, otherwise false
*/
-static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
- u8* footer = compressed + compressed_size - 8;
- u32 buffer_top_and_bottom = *(u32*)footer;
+static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
+ const u8* footer = compressed + compressed_size - 8;
+ u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
u32 out = decompressed_size;
u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
@@ -47,22 +45,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
memset(decompressed, 0, decompressed_size);
memcpy(decompressed, compressed, compressed_size);
- while(index > stop_index) {
+ while (index > stop_index) {
u8 control = compressed[--index];
- for(u32 i = 0; i < 8; i++) {
- if(index <= stop_index)
+ for (unsigned i = 0; i < 8; i++) {
+ if (index <= stop_index)
break;
- if(index <= 0)
+ if (index <= 0)
break;
- if(out <= 0)
+ if (out <= 0)
break;
- if(control & 0x80) {
+ if (control & 0x80) {
// Check if compression is out of bounds
- if(index < 2) {
+ if (index < 2)
return false;
- }
index -= 2;
u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
@@ -71,23 +68,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
segment_offset += 2;
// Check if compression is out of bounds
- if(out < segment_size) {
+ if (out < segment_size)
return false;
- }
- for(u32 j = 0; j < segment_size; j++) {
+
+ for (unsigned j = 0; j < segment_size; j++) {
// Check if compression is out of bounds
- if(out + segment_offset >= decompressed_size) {
+ if (out + segment_offset >= decompressed_size)
return false;
- }
- u8 data = decompressed[out + segment_offset];
+ u8 data = decompressed[out + segment_offset];
decompressed[--out] = data;
}
} else {
// Check if compression is out of bounds
- if(out < 1) {
+ if (out < 1)
return false;
- }
decompressed[--out] = compressed[--index];
}
control <<= 1;
@@ -99,24 +94,21 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
////////////////////////////////////////////////////////////////////////////////////////////////////
// AppLoader_NCCH class
-/// AppLoader_NCCH constructor
-AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) {
- this->filename = filename;
- is_loaded = false;
- is_compressed = false;
- entry_point = 0;
- ncch_offset = 0;
- exefs_offset = 0;
-}
+FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
+ u32 magic;
+ file.Seek(0x100, SEEK_SET);
+ if (1 != file.ReadArray<u32>(&magic, 1))
+ return FileType::Error;
+
+ if (MakeMagic('N', 'C', 'S', 'D') == magic)
+ return FileType::CCI;
-/// AppLoader_NCCH destructor
-AppLoader_NCCH::~AppLoader_NCCH() {
+ if (MakeMagic('N', 'C', 'C', 'H') == magic)
+ return FileType::CXI;
+
+ return FileType::Error;
}
-/**
- * Loads .code section into memory for booting
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::LoadExec() const {
if (!is_loaded)
return ResultStatus::ErrorNotLoaded;
@@ -130,189 +122,144 @@ ResultStatus AppLoader_NCCH::LoadExec() const {
return ResultStatus::Error;
}
-/**
- * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
- * @param name Name of section to read out of NCCH file
- * @param buffer Vector to read data into
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const {
+ if (!file->IsOpen())
+ return ResultStatus::Error;
+
+ LOG_DEBUG(Loader, "%d sections:", kMaxSections);
// Iterate through the ExeFs archive until we find the .code file...
- FileUtil::IOFile file(filename, "rb");
- if (file.IsOpen()) {
- LOG_DEBUG(Loader, "%d sections:", kMaxSections);
- for (int i = 0; i < kMaxSections; i++) {
- // Load the specified section...
- if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
- LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i,
- exefs_header.section[i].offset, exefs_header.section[i].size,
- exefs_header.section[i].name);
-
- s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
- sizeof(ExeFs_Header)+ncch_offset);
- file.Seek(section_offset, 0);
-
- // Section is compressed...
- if (i == 0 && is_compressed) {
- // Read compressed .code section...
- std::unique_ptr<u8[]> temp_buffer;
- try {
- temp_buffer.reset(new u8[exefs_header.section[i].size]);
- } catch (std::bad_alloc&) {
- return ResultStatus::ErrorMemoryAllocationFailed;
- }
- file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
-
- // Decompress .code section...
- u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0],
- exefs_header.section[i].size);
- buffer.resize(decompressed_size);
- if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],
- decompressed_size)) {
- return ResultStatus::ErrorInvalidFormat;
- }
- // Section is uncompressed...
- }
- else {
- buffer.resize(exefs_header.section[i].size);
- file.ReadBytes(&buffer[0], exefs_header.section[i].size);
+ for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
+ const auto& section = exefs_header.section[section_number];
+
+ // Load the specified section...
+ if (strcmp(section.name, name) == 0) {
+ LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
+ section.offset, section.size, section.name);
+
+ s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
+ file->Seek(section_offset, SEEK_SET);
+
+ if (is_compressed) {
+ // Section is compressed, read compressed .code section...
+ std::unique_ptr<u8[]> temp_buffer;
+ try {
+ temp_buffer.reset(new u8[section.size]);
+ } catch (std::bad_alloc&) {
+ return ResultStatus::ErrorMemoryAllocationFailed;
}
- return ResultStatus::Success;
+
+ if (file->ReadBytes(&temp_buffer[0], section.size) != section.size)
+ return ResultStatus::Error;
+
+ // Decompress .code section...
+ u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
+ buffer.resize(decompressed_size);
+ if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
+ return ResultStatus::ErrorInvalidFormat;
+ } else {
+ // Section is uncompressed...
+ buffer.resize(section.size);
+ if (file->ReadBytes(&buffer[0], section.size) != section.size)
+ return ResultStatus::Error;
}
+ return ResultStatus::Success;
}
- } else {
- LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
- return ResultStatus::Error;
}
return ResultStatus::ErrorNotUsed;
}
-/**
- * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
- * @param error_string Pointer to string to put error message if an error has occurred
- * @todo Move NCSD parsing out of here and create a separate function for loading these
- * @return True on success, otherwise false
- */
ResultStatus AppLoader_NCCH::Load() {
- LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str());
-
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
- FileUtil::IOFile file(filename, "rb");
- if (file.IsOpen()) {
- file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
+ if (!file->IsOpen())
+ return ResultStatus::Error;
- // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
- if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
- LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
- ncch_offset = 0x4000;
- file.Seek(ncch_offset, 0);
- file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
- }
+ // Reset read pointer in case this file has been read before.
+ file->Seek(0, SEEK_SET);
- // Verify we are loading the correct file type...
- if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
- return ResultStatus::ErrorInvalidFormat;
+ if (file->ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
+ return ResultStatus::Error;
- // Read ExHeader...
+ // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
+ if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
+ LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
+ ncch_offset = 0x4000;
+ file->Seek(ncch_offset, SEEK_SET);
+ file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
+ }
- file.ReadBytes(&exheader_header, sizeof(ExHeader_Header));
+ // Verify we are loading the correct file type...
+ if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
+ return ResultStatus::ErrorInvalidFormat;
- is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
- entry_point = exheader_header.codeset_info.text.address;
+ // Read ExHeader...
- LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
- LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
- LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
+ if (file->ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
+ return ResultStatus::Error;
- // Read ExeFS...
+ is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
+ entry_point = exheader_header.codeset_info.text.address;
- exefs_offset = ncch_header.exefs_offset * kBlockSize;
- u32 exefs_size = ncch_header.exefs_size * kBlockSize;
+ LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
+ LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
+ LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
- LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
- LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
+ // Read ExeFS...
- file.Seek(exefs_offset + ncch_offset, 0);
- file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
+ exefs_offset = ncch_header.exefs_offset * kBlockSize;
+ u32 exefs_size = ncch_header.exefs_size * kBlockSize;
- is_loaded = true; // Set state to loaded
+ LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
+ LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
- LoadExec(); // Load the executable into memory for booting
+ file->Seek(exefs_offset + ncch_offset, SEEK_SET);
+ if (file->ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
+ return ResultStatus::Error;
- return ResultStatus::Success;
- } else {
- LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
- }
- return ResultStatus::Error;
+ is_loaded = true; // Set state to loaded
+
+ return LoadExec(); // Load the executable into memory for booting
}
-/**
- * Get the code (typically .code section) of the application
- * @param buffer Reference to buffer to store data
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const {
return LoadSectionExeFS(".code", buffer);
}
-/**
- * Get the icon (typically icon section) of the application
- * @param buffer Reference to buffer to store data
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) const {
return LoadSectionExeFS("icon", buffer);
}
-/**
- * Get the banner (typically banner section) of the application
- * @param buffer Reference to buffer to store data
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) const {
return LoadSectionExeFS("banner", buffer);
}
-/**
- * Get the logo (typically logo section) of the application
- * @param buffer Reference to buffer to store data
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const {
return LoadSectionExeFS("logo", buffer);
}
-/**
- * Get the RomFS of the application
- * @param buffer Reference to buffer to store data
- * @return ResultStatus result of function
- */
ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
- FileUtil::IOFile file(filename, "rb");
- if (file.IsOpen()) {
- // Check if the NCCH has a RomFS...
- if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
- u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
- u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
+ if (!file->IsOpen())
+ return ResultStatus::Error;
- LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
- LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
+ // Check if the NCCH has a RomFS...
+ if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
+ u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
+ u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
- buffer.resize(romfs_size);
+ LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
+ LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
- file.Seek(romfs_offset, 0);
- file.ReadBytes(&buffer[0], romfs_size);
+ buffer.resize(romfs_size);
- return ResultStatus::Success;
- }
- LOG_DEBUG(Loader, "NCCH has no RomFS");
- return ResultStatus::ErrorNotUsed;
- } else {
- LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
+ file->Seek(romfs_offset, SEEK_SET);
+ if (file->ReadBytes(&buffer[0], romfs_size) != romfs_size)
+ return ResultStatus::Error;
+
+ return ResultStatus::Success;
}
- return ResultStatus::Error;
+ LOG_DEBUG(Loader, "NCCH has no RomFS");
+ return ResultStatus::ErrorNotUsed;
}
u64 AppLoader_NCCH::GetProgramId() const {
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index fd9258970..9ae2de99f 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -5,7 +5,6 @@
#pragma once
#include "common/common.h"
-#include "common/file_util.h"
#include "core/loader/loader.h"
@@ -14,7 +13,7 @@
struct NCCH_Header {
u8 signature[0x100];
- char magic[4];
+ u32 magic;
u32 content_size;
u8 partition_id[8];
u16 maker_code;
@@ -147,8 +146,14 @@ namespace Loader {
/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
class AppLoader_NCCH final : public AppLoader {
public:
- AppLoader_NCCH(const std::string& filename);
- ~AppLoader_NCCH() override;
+ AppLoader_NCCH(std::unique_ptr<FileUtil::IOFile>&& file) : AppLoader(std::move(file)) { }
+
+ /**
+ * Returns the type of the file
+ * @param file FileUtil::IOFile open file
+ * @return FileType found, or FileType::Error if this loader doesn't know it
+ */
+ static FileType IdentifyType(FileUtil::IOFile& file);
/**
* Load the application
@@ -213,14 +218,11 @@ private:
*/
ResultStatus LoadExec() const;
- std::string filename;
-
- bool is_loaded;
- bool is_compressed;
+ bool is_compressed = false;
- u32 entry_point;
- u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
- u32 exefs_offset;
+ u32 entry_point = 0;
+ u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header
+ u32 exefs_offset = 0;
NCCH_Header ncch_header;
ExeFs_Header exefs_header;
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 97ef1c5a3..0e3b81b28 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -9,6 +9,7 @@
#include "core/mem_map.h"
#include "core/hw/hw.h"
#include "hle/config_mem.h"
+#include "hle/shared_page.h"
namespace Memory {
@@ -82,6 +83,10 @@ inline void Read(T &var, const VAddr vaddr) {
} else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) {
ConfigMem::Read<T>(var, vaddr);
+ // Shared page
+ } else if ((vaddr >= SHARED_PAGE_VADDR) && (vaddr < SHARED_PAGE_VADDR_END)) {
+ SharedPage::Read<T>(var, vaddr);
+
// DSP memory
} else if ((vaddr >= DSP_MEMORY_VADDR) && (vaddr < DSP_MEMORY_VADDR_END)) {
var = *((const T*)&g_dsp_mem[vaddr - DSP_MEMORY_VADDR]);
diff --git a/src/core/system.cpp b/src/core/system.cpp
index d6188f055..f4c2df1cd 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -21,11 +21,11 @@ void UpdateState(State state) {
void Init(EmuWindow* emu_window) {
Core::Init();
+ CoreTiming::Init();
Memory::Init();
HW::Init();
Kernel::Init();
HLE::Init();
- CoreTiming::Init();
VideoCore::Init(emu_window);
}
@@ -38,11 +38,11 @@ void RunLoopUntil(u64 global_cycles) {
void Shutdown() {
VideoCore::Shutdown();
- CoreTiming::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
HW::Shutdown();
Memory::Shutdown();
+ CoreTiming::Shutdown();
Core::Shutdown();
}