summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt54
-rw-r--r--src/core/arm/arm_interface.h8
-rw-r--r--src/core/arm/disassembler/load_symbol_map.cpp4
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp13
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h4
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp369
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.h4
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp4
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h4
-rw-r--r--src/core/arm/interpreter/armemu.cpp1069
-rw-r--r--src/core/arm/interpreter/armsupp.cpp8
-rw-r--r--src/core/arm/interpreter/thumbemu.cpp2
-rw-r--r--src/core/arm/skyeye_common/armcpu.h2
-rw-r--r--src/core/arm/skyeye_common/armdefs.h510
-rw-r--r--src/core/arm/skyeye_common/armemu.h22
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp14
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/core_timing.cpp70
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/file_sys/archive.h104
-rw-r--r--src/core/file_sys/archive_backend.h225
-rw-r--r--src/core/file_sys/archive_romfs.cpp78
-rw-r--r--src/core/file_sys/archive_romfs.h67
-rw-r--r--src/core/file_sys/archive_savedata.cpp33
-rw-r--r--src/core/file_sys/archive_savedata.h32
-rw-r--r--src/core/file_sys/archive_sdmc.cpp102
-rw-r--r--src/core/file_sys/archive_sdmc.h72
-rw-r--r--src/core/file_sys/directory_backend.h (renamed from src/core/file_sys/directory.h)12
-rw-r--r--src/core/file_sys/directory_romfs.cpp4
-rw-r--r--src/core/file_sys/directory_romfs.h10
-rw-r--r--src/core/file_sys/directory_sdmc.cpp81
-rw-r--r--src/core/file_sys/directory_sdmc.h48
-rw-r--r--src/core/file_sys/disk_archive.cpp167
-rw-r--r--src/core/file_sys/disk_archive.h101
-rw-r--r--src/core/file_sys/file_backend.h (renamed from src/core/file_sys/file.h)11
-rw-r--r--src/core/file_sys/file_romfs.cpp19
-rw-r--r--src/core/file_sys/file_romfs.h14
-rw-r--r--src/core/file_sys/file_sdmc.cpp107
-rw-r--r--src/core/file_sys/file_sdmc.h75
-rw-r--r--src/core/hle/config_mem.cpp5
-rw-r--r--src/core/hle/coprocessor.cpp2
-rw-r--r--src/core/hle/function_wrappers.h18
-rw-r--r--src/core/hle/hle.cpp19
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp21
-rw-r--r--src/core/hle/kernel/address_arbiter.h4
-rw-r--r--src/core/hle/kernel/archive.cpp436
-rw-r--r--src/core/hle/kernel/archive.h70
-rw-r--r--src/core/hle/kernel/event.cpp47
-rw-r--r--src/core/hle/kernel/event.h14
-rw-r--r--src/core/hle/kernel/kernel.cpp29
-rw-r--r--src/core/hle/kernel/kernel.h83
-rw-r--r--src/core/hle/kernel/mutex.cpp124
-rw-r--r--src/core/hle/kernel/mutex.h11
-rw-r--r--src/core/hle/kernel/semaphore.cpp94
-rw-r--r--src/core/hle/kernel/semaphore.h32
-rw-r--r--src/core/hle/kernel/session.h58
-rw-r--r--src/core/hle/kernel/shared_memory.cpp53
-rw-r--r--src/core/hle/kernel/shared_memory.h7
-rw-r--r--src/core/hle/kernel/thread.cpp215
-rw-r--r--src/core/hle/kernel/thread.h28
-rw-r--r--src/core/hle/result.h402
-rw-r--r--src/core/hle/service/ac_u.cpp26
-rw-r--r--src/core/hle/service/ac_u.h4
-rw-r--r--src/core/hle/service/am_app.cpp24
-rw-r--r--src/core/hle/service/am_app.h27
-rw-r--r--src/core/hle/service/am_net.cpp47
-rw-r--r--src/core/hle/service/am_net.h (renamed from src/core/hle/service/fs_user.h)10
-rw-r--r--src/core/hle/service/apt_u.cpp203
-rw-r--r--src/core/hle/service/apt_u.h4
-rw-r--r--src/core/hle/service/boss_u.cpp28
-rw-r--r--src/core/hle/service/boss_u.h27
-rw-r--r--src/core/hle/service/cecd_u.cpp24
-rw-r--r--src/core/hle/service/cecd_u.h27
-rw-r--r--src/core/hle/service/cfg_i.cpp59
-rw-r--r--src/core/hle/service/cfg_i.h27
-rw-r--r--src/core/hle/service/cfg_u.cpp92
-rw-r--r--src/core/hle/service/cfg_u.h2
-rw-r--r--src/core/hle/service/csnd_snd.cpp39
-rw-r--r--src/core/hle/service/csnd_snd.h27
-rw-r--r--src/core/hle/service/dsp_dsp.cpp194
-rw-r--r--src/core/hle/service/dsp_dsp.h4
-rw-r--r--src/core/hle/service/err_f.cpp4
-rw-r--r--src/core/hle/service/err_f.h4
-rw-r--r--src/core/hle/service/frd_u.cpp35
-rw-r--r--src/core/hle/service/frd_u.h27
-rw-r--r--src/core/hle/service/fs/archive.cpp431
-rw-r--r--src/core/hle/service/fs/archive.h125
-rw-r--r--src/core/hle/service/fs/fs_user.cpp529
-rw-r--r--src/core/hle/service/fs/fs_user.h33
-rw-r--r--src/core/hle/service/fs_user.cpp354
-rw-r--r--src/core/hle/service/gsp_gpu.cpp80
-rw-r--r--src/core/hle/service/hid_user.cpp17
-rw-r--r--src/core/hle/service/hid_user.h6
-rw-r--r--src/core/hle/service/ir_rst.cpp36
-rw-r--r--src/core/hle/service/ir_rst.h27
-rw-r--r--src/core/hle/service/ir_u.cpp45
-rw-r--r--src/core/hle/service/ir_u.h27
-rw-r--r--src/core/hle/service/ldr_ro.cpp28
-rw-r--r--src/core/hle/service/ldr_ro.h27
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/mic_u.h2
-rw-r--r--src/core/hle/service/nim_aoc.cpp31
-rw-r--r--src/core/hle/service/nim_aoc.h27
-rw-r--r--src/core/hle/service/nwm_uds.h2
-rw-r--r--src/core/hle/service/pm_app.cpp35
-rw-r--r--src/core/hle/service/pm_app.h27
-rw-r--r--src/core/hle/service/ptm_u.cpp98
-rw-r--r--src/core/hle/service/ptm_u.h2
-rw-r--r--src/core/hle/service/service.cpp36
-rw-r--r--src/core/hle/service/service.h74
-rw-r--r--src/core/hle/service/soc_u.h2
-rw-r--r--src/core/hle/service/srv.cpp28
-rw-r--r--src/core/hle/svc.cpp215
-rw-r--r--src/core/hle/svc.h2
-rw-r--r--src/core/hw/gpu.cpp23
-rw-r--r--src/core/hw/gpu.h2
-rw-r--r--src/core/hw/hw.cpp21
-rw-r--r--src/core/hw/ndma.cpp47
-rw-r--r--src/core/hw/ndma.h26
-rw-r--r--src/core/loader/3dsx.cpp236
-rw-r--r--src/core/loader/3dsx.h32
-rw-r--r--src/core/loader/elf.cpp16
-rw-r--r--src/core/loader/loader.cpp18
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/ncch.cpp48
-rw-r--r--src/core/loader/ncch.h8
-rw-r--r--src/core/mem_map.cpp64
-rw-r--r--src/core/mem_map.h33
-rw-r--r--src/core/mem_map_funcs.cpp90
-rw-r--r--src/core/settings.h4
-rw-r--r--src/core/system.cpp12
-rw-r--r--src/core/system.h22
134 files changed, 5782 insertions, 3563 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f67481359..f71232c1a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,29 +18,42 @@ set(SRCS
arm/skyeye_common/vfp/vfpinstr.cpp
arm/skyeye_common/vfp/vfpsingle.cpp
file_sys/archive_romfs.cpp
+ file_sys/archive_savedata.cpp
file_sys/archive_sdmc.cpp
+ file_sys/disk_archive.cpp
file_sys/file_romfs.cpp
- file_sys/file_sdmc.cpp
file_sys/directory_romfs.cpp
- file_sys/directory_sdmc.cpp
hle/kernel/address_arbiter.cpp
- hle/kernel/archive.cpp
hle/kernel/event.cpp
hle/kernel/kernel.cpp
hle/kernel/mutex.cpp
+ hle/kernel/semaphore.cpp
hle/kernel/shared_memory.cpp
hle/kernel/thread.cpp
hle/service/ac_u.cpp
+ hle/service/am_app.cpp
+ hle/service/am_net.cpp
hle/service/apt_u.cpp
+ hle/service/boss_u.cpp
+ hle/service/cecd_u.cpp
+ hle/service/cfg_i.cpp
hle/service/cfg_u.cpp
+ hle/service/csnd_snd.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
- hle/service/fs_user.cpp
+ hle/service/fs/archive.cpp
+ hle/service/fs/fs_user.cpp
+ hle/service/frd_u.cpp
hle/service/gsp_gpu.cpp
hle/service/hid_user.cpp
+ hle/service/ir_rst.cpp
+ hle/service/ir_u.cpp
+ hle/service/ldr_ro.cpp
hle/service/mic_u.cpp
+ hle/service/nim_aoc.cpp
hle/service/ndm_u.cpp
hle/service/nwm_uds.cpp
+ hle/service/pm_app.cpp
hle/service/ptm_u.cpp
hle/service/service.cpp
hle/service/soc_u.cpp
@@ -51,10 +64,10 @@ set(SRCS
hle/svc.cpp
hw/gpu.cpp
hw/hw.cpp
- hw/ndma.cpp
loader/elf.cpp
loader/loader.cpp
loader/ncch.cpp
+ loader/3dsx.cpp
core.cpp
core_timing.cpp
mem_map.cpp
@@ -84,48 +97,63 @@ set(HEADERS
arm/skyeye_common/vfp/vfp.h
arm/skyeye_common/vfp/vfp_helper.h
arm/arm_interface.h
- file_sys/archive.h
+ file_sys/archive_backend.h
file_sys/archive_romfs.h
+ file_sys/archive_savedata.h
file_sys/archive_sdmc.h
- file_sys/file.h
+ file_sys/disk_archive.h
+ file_sys/file_backend.h
file_sys/file_romfs.h
- file_sys/file_sdmc.h
- file_sys/directory.h
+ file_sys/directory_backend.h
file_sys/directory_romfs.h
- file_sys/directory_sdmc.h
hle/kernel/address_arbiter.h
- hle/kernel/archive.h
hle/kernel/event.h
hle/kernel/kernel.h
hle/kernel/mutex.h
+ hle/kernel/semaphore.h
+ hle/kernel/session.h
hle/kernel/shared_memory.h
hle/kernel/thread.h
hle/service/ac_u.h
+ hle/service/am_app.h
+ hle/service/am_net.h
hle/service/apt_u.h
+ hle/service/boss_u.h
+ hle/service/cecd_u.h
+ hle/service/cfg_i.h
hle/service/cfg_u.h
+ hle/service/csnd_snd.h
hle/service/dsp_dsp.h
hle/service/err_f.h
- hle/service/fs_user.h
+ hle/service/fs/archive.h
+ hle/service/fs/fs_user.h
+ hle/service/frd_u.h
hle/service/gsp_gpu.h
hle/service/hid_user.h
+ hle/service/ir_rst.h
+ hle/service/ir_u.h
+ hle/service/ldr_ro.h
hle/service/mic_u.h
+ hle/service/nim_aoc.h
hle/service/ndm_u.h
hle/service/nwm_uds.h
+ hle/service/pm_app.h
hle/service/ptm_u.h
hle/service/service.h
hle/service/soc_u.h
hle/service/srv.h
hle/service/ssl_c.h
hle/config_mem.h
+ hle/result.h
hle/function_wrappers.h
hle/hle.h
hle/svc.h
hw/gpu.h
hw/hw.h
- hw/ndma.h
loader/elf.h
loader/loader.h
loader/ncch.h
+ loader/3dsx.h
core.h
core_timing.h
mem_map.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index be677ae20..3ae528562 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -16,7 +16,7 @@ public:
num_instructions = 0;
}
- ~ARM_Interface() {
+ virtual ~ARM_Interface() {
}
/**
@@ -63,7 +63,7 @@ public:
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
- virtual u32 GetCPSR() const = 0;
+ virtual u32 GetCPSR() const = 0;
/**
* Set the current CPSR register
@@ -98,7 +98,7 @@ public:
}
protected:
-
+
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp
index 0f384ad3e..55278474b 100644
--- a/src/core/arm/disassembler/load_symbol_map.cpp
+++ b/src/core/arm/disassembler/load_symbol_map.cpp
@@ -22,8 +22,8 @@ void LoadSymbolMap(std::string filename) {
while (std::getline(infile, line)) {
std::istringstream iss(line);
- if (!(iss >> address_str >> size >> function_name)) {
- break; // Error parsing
+ if (!(iss >> address_str >> size >> function_name)) {
+ break; // Error parsing
}
u32 address = std::stoul(address_str, nullptr, 16);
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 669b612fc..6c8ea211e 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/arm/skyeye_common/armcpu.h"
#include "core/arm/skyeye_common/armemu.h"
@@ -60,7 +60,7 @@ void ARM_DynCom::SetPC(u32 pc) {
* @return Returns current PC
*/
u32 ARM_DynCom::GetPC() const {
- return state->pc;
+ return state->Reg[15];
}
/**
@@ -110,9 +110,12 @@ u64 ARM_DynCom::GetTicks() const {
* @param num_instructions Number of instructions to executes
*/
void ARM_DynCom::ExecuteInstructions(int num_instructions) {
- ticks += num_instructions;
state->NumInstrsToExecute = num_instructions;
- InterpreterMainLoop(state.get());
+
+ // 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());
}
/**
@@ -126,7 +129,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
ctx.sp = state->Reg[13];
ctx.lr = state->Reg[14];
- ctx.pc = state->pc;
+ ctx.pc = state->Reg[15];
ctx.cpsr = state->Cpsr;
ctx.fpscr = state->VFP[1];
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 1f8cd3a3a..51eea41ed 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -19,7 +19,7 @@ public:
/**
* Set the Program Counter to an address
- * @param addr Address to set PC to
+ * @param pc Address to set PC to
*/
void SetPC(u32 pc) override;
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index fe1501b59..68012bffd 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -26,7 +26,7 @@
#define CITRA_IGNORE_EXIT(x)
#include <algorithm>
-#include <map>
+#include <unordered_map>
#include <stdio.h>
#include <assert.h>
#include <cstdio>
@@ -94,9 +94,8 @@ typedef unsigned int (*shtop_fp_t)(arm_processor *cpu, unsigned int sht_oper);
/* exclusive memory access */
static int exclusive_detect(ARMul_State* state, ARMword addr){
- int i;
#if 0
- for(i = 0; i < 128; i++){
+ for(int i = 0; i < 128; i++){
if(state->exclusive_tag_array[i] == addr)
return 0;
}
@@ -108,9 +107,8 @@ static int exclusive_detect(ARMul_State* state, ARMword addr){
}
static void add_exclusive_addr(ARMul_State* state, ARMword addr){
- int i;
#if 0
- for(i = 0; i < 128; i++){
+ for(int i = 0; i < 128; i++){
if(state->exclusive_tag_array[i] == 0xffffffff){
state->exclusive_tag_array[i] = addr;
//DEBUG_LOG(ARM11, "In %s, add addr 0x%x\n", __func__, addr);
@@ -435,9 +433,7 @@ typedef struct _ldst_inst {
unsigned int inst;
get_addr_fp_t get_addr;
} ldst_inst;
-#define DEBUG_MSG DEBUG_LOG(ARM11, "in %s %d\n", __FUNCTION__, __LINE__); \
- DEBUG_LOG(ARM11, "inst is %x\n", inst); \
- CITRA_IGNORE_EXIT(0)
+#define DEBUG_MSG LOG_DEBUG(Core_ARM11, "inst is %x", inst); CITRA_IGNORE_EXIT(0)
int CondPassed(arm_processor *cpu, unsigned int cond);
#define LnSWoUB(s) glue(LnSWoUB, s)
@@ -1425,7 +1421,7 @@ inline void *AllocBuffer(unsigned int size)
int start = top;
top += size;
if (top > CACHE_BUFFER_SIZE) {
- DEBUG_LOG(ARM11, "inst_buf is full\n");
+ LOG_ERROR(Core_ARM11, "inst_buf is full");
CITRA_IGNORE_EXIT(-1);
}
return (void *)&inst_buf[start];
@@ -1611,6 +1607,10 @@ get_addr_fp_t get_calc_addr_op(unsigned int inst)
#define CHECK_RM (inst_cream->Rm == 15)
#define CHECK_RS (inst_cream->Rs == 15)
+#define UNIMPLEMENTED_INSTRUCTION(mnemonic) \
+ LOG_ERROR(Core_ARM11, "unimplemented instruction: %s", mnemonic); \
+ CITRA_IGNORE_EXIT(-1); \
+ return nullptr;
ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index)
{
@@ -1725,7 +1725,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(unsigned int inst, int index)
inst_base->br = INDIRECT_BRANCH;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BKPT"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst));
@@ -1760,7 +1760,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bx)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BXJ"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst));
cdp_inst *inst_cream = (cdp_inst *)inst_base->component;
@@ -1777,7 +1777,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){
inst_cream->opcode_1 = BITS(inst, 20, 23);
inst_cream->inst = inst;
- DEBUG_LOG(ARM11, "in func %s inst %x index %x\n", __FUNCTION__, inst, index);
+ LOG_TRACE(Core_ARM11, "inst %x index %x", inst, index);
return inst_base;
}
ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index)
@@ -2207,7 +2207,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(unsigned int inst, int index)
inst_cream->inst = inst;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MCRR"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst));
@@ -2266,7 +2266,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(unsigned int inst, int index)
inst_cream->inst = inst;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MRRC"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst));
@@ -2360,8 +2360,8 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(orr)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHBT"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHTB"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst));
@@ -2373,16 +2373,16 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUBADDX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst));
@@ -2412,8 +2412,8 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rev16)(unsigned int inst, int index){
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("REVSH"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("RFE"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst));
@@ -2462,9 +2462,9 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADDSUBX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst));
@@ -2489,14 +2489,14 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SEL"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUBADDX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst));
@@ -2555,15 +2555,15 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLAW"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSLD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLA"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLS"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMUL"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUAD"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst));
@@ -2626,13 +2626,13 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smulw)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUSD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUBADDX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst));
@@ -2939,9 +2939,9 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTAB16"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){
- DEBUG_LOG(ARM11, "in func %s, SXTAH untested\n", __func__);
+ LOG_WARNING(Core_ARM11, "SXTAH untested");
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst));
sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component;
@@ -2957,7 +2957,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTB16"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst));
@@ -3001,16 +3001,16 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst));
@@ -3113,21 +3113,21 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(blx_1_thumb)(unsigned int tinst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUBADDX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USADA8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUBADDX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTAB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTB16"); }
@@ -3309,9 +3309,8 @@ const transop_fp_t arm_instruction_trans[] = {
INTERPRETER_TRANSLATE(blx_1_thumb)
};
-typedef map<unsigned int, int> bb_map;
-bb_map CreamCache[65536];
-bb_map ProfileCache[65536];
+typedef std::unordered_map<u32, int> bb_map;
+bb_map CreamCache;
//#define USE_DUMMY_CACHE
@@ -3319,14 +3318,12 @@ bb_map ProfileCache[65536];
unsigned int DummyCache[0x100000];
#endif
-#define HASH(x) ((x + (x << 3) + (x >> 6)) % 65536)
void insert_bb(unsigned int addr, int start)
{
#ifdef USE_DUMMY_CACHE
DummyCache[addr] = start;
#else
-// CreamCache[addr] = start;
- CreamCache[HASH(addr)][addr] = start;
+ CreamCache[addr] = start;
#endif
}
@@ -3341,8 +3338,8 @@ int find_bb(unsigned int addr, int &start)
} else
ret = -1;
#else
- bb_map::const_iterator it = CreamCache[HASH(addr)].find(addr);
- if (it != CreamCache[HASH(addr)].end()) {
+ bb_map::const_iterator it = CreamCache.find(addr);
+ if (it != CreamCache.end()) {
start = static_cast<int>(it->second);
ret = 0;
#if HYBRID_MODE
@@ -3396,7 +3393,7 @@ static tdstate decode_thumb_instr(arm_processor *cpu, uint32_t inst, addr_t addr
}
else{
/* something wrong */
- DEBUG_LOG(ARM11, "In %s, thumb decoder error\n", __FUNCTION__);
+ LOG_ERROR(Core_ARM11, "thumb decoder error");
}
break;
case 28:
@@ -3473,30 +3470,15 @@ void flush_bb(uint32_t addr)
uint32_t start;
addr &= 0xfffff000;
- for (int i = 0; i < 65536; i ++) {
- for (it = CreamCache[i].begin(); it != CreamCache[i].end(); ) {
- start = static_cast<uint32_t>(it->first);
- //start = (start >> 12) << 12;
- start &= 0xfffff000;
- if (start == addr) {
- //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
- CreamCache[i].erase(it ++);
- } else
- ++it;
- }
- }
-
- for (int i = 0; i < 65536; i ++) {
- for (it = ProfileCache[i].begin(); it != ProfileCache[i].end(); ) {
- start = static_cast<uint32_t>(it->first);
- //start = (start >> 12) << 12;
- start &= 0xfffff000;
- if (start == addr) {
- //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
- ProfileCache[i].erase(it ++);
- } else
- ++it;
- }
+ for (it = CreamCache.begin(); it != CreamCache.end(); ) {
+ start = static_cast<uint32_t>(it->first);
+ //start = (start >> 12) << 12;
+ start &= 0xfffff000;
+ if (start == addr) {
+ //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
+ CreamCache.erase(it++);
+ } else
+ ++it;
}
//DEBUG_LOG(ARM11, "flush bb @ %x\n", addr);
@@ -3619,7 +3601,7 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr)
bank->bank_read(32, phys_addr, &inst);
}
else {
- DEBUG_LOG(ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr);
+ LOG_ERROR(Core_ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr);
return FETCH_FAILURE;
}
#else
@@ -3649,8 +3631,8 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr)
ret = decode_arm_instr(inst, &idx);
if (ret == DECODE_FAILURE) {
- DEBUG_LOG(ARM11, "[info] : Decode failure.\tPC : [0x%x]\tInstruction : [%x]\n", phys_addr, inst);
- DEBUG_LOG(ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x\n", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
+ LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : [%x]", phys_addr, inst);
+ LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
CITRA_IGNORE_EXIT(-1);
}
// DEBUG_LOG(ARM11, "PC : [0x%x] INST : %s\n", cpu->translate_pc, arm_instruction[idx].name);
@@ -3694,7 +3676,7 @@ void InterpreterInitInstLength(unsigned long long int *ptr, size_t size)
}
}
for (int i = 0; i < array_size - 4; i ++)
- DEBUG_LOG(ARM11, "[%d]:%d\n", i, InstLength[i]);
+ LOG_DEBUG(Core_ARM11, "[%d]:%d", i, InstLength[i]);
}
int clz(unsigned int x)
@@ -3718,7 +3700,7 @@ static bool InAPrivilegedMode(arm_core_t *core)
}
/* r15 = r15 + 8 */
-void InterpreterMainLoop(ARMul_State* state)
+unsigned InterpreterMainLoop(ARMul_State* state)
{
#define CRn inst_cream->crn
#define OPCODE_2 inst_cream->opcode_2
@@ -3741,22 +3723,28 @@ void InterpreterMainLoop(ARMul_State* state)
//if (debug_function(core)) \
if (core->check_int_flag) \
goto END
- //DEBUG_LOG(ARM11, "icounter is %llx line is %d pc is %x\n", cpu->icounter, __LINE__, cpu->Reg[15])
+ //LOG_TRACE(Core_ARM11, "icounter is %llx pc is %x\n", cpu->icounter, cpu->Reg[15])
#else
#define INC_ICOUNTER ;
#endif
#define FETCH_INST if (inst_base->br != NON_BRANCH) \
- goto PROFILING; \
+ goto DISPATCH; \
inst_base = (arm_inst *)&inst_buf[ptr]
#define INC_PC(l) ptr += sizeof(arm_inst) + l
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
// clunky switch statement.
#if defined __GNUC__ || defined __clang__
-#define GOTO_NEXT_INST goto *InstLabel[inst_base->idx]
+#define GOTO_NEXT_INST \
+ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
+ num_instrs++; \
+ goto *InstLabel[inst_base->idx]
#else
-#define GOTO_NEXT_INST switch(inst_base->idx) { \
+#define GOTO_NEXT_INST \
+ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
+ num_instrs++; \
+ switch(inst_base->idx) { \
case 0: goto VMLA_INST; \
case 1: goto VMLS_INST; \
case 2: goto VNMLA_INST; \
@@ -4028,20 +4016,15 @@ void InterpreterMainLoop(ARMul_State* state)
unsigned int addr;
unsigned int phys_addr;
unsigned int last_pc = 0;
+ unsigned int num_instrs = 0;
fault_t fault;
static unsigned int last_physical_base = 0, last_logical_base = 0;
int ptr;
+ bool single_step = (cpu->NumInstrsToExecute == 1);
LOAD_NZCVT;
DISPATCH:
{
- if (cpu->NumInstrsToExecute == 0)
- return;
-
- cpu->NumInstrsToExecute--;
-
- //NOTICE_LOG(ARM11, "instr!");
-
if (!cpu->NirqSig) {
if (!(cpu->Cpsr & 0x80)) {
goto END;
@@ -4179,10 +4162,6 @@ void InterpreterMainLoop(ARMul_State* state)
inst_base = (arm_inst *)&inst_buf[ptr];
GOTO_NEXT_INST;
}
- PROFILING:
- {
- goto DISPATCH;
- }
ADC_INST:
{
INC_ICOUNTER;
@@ -4207,7 +4186,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(adc_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4241,7 +4220,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(add_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4272,7 +4251,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(and_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4290,11 +4269,11 @@ void InterpreterMainLoop(ARMul_State* state)
}
SET_PC;
INC_PC(sizeof(bbl_inst));
- goto PROFILING;
+ goto DISPATCH;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(bbl_inst));
- goto PROFILING;
+ goto DISPATCH;
}
BIC_INST:
{
@@ -4322,7 +4301,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(bic_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4358,12 +4337,12 @@ void InterpreterMainLoop(ARMul_State* state)
//DEBUG_MSG;
}
INC_PC(sizeof(blx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
// INC_PC(sizeof(bx_inst));
INC_PC(sizeof(blx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
BX_INST:
{
@@ -4371,17 +4350,17 @@ void InterpreterMainLoop(ARMul_State* state)
bx_inst *inst_cream = (bx_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
if (inst_cream->Rm == 15)
- DEBUG_LOG(ARM11, "In %s, BX at pc %x: use of Rm = R15 is discouraged\n", __FUNCTION__, cpu->Reg[15]);
+ LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]);
cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe;
// cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
INC_PC(sizeof(bx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
// INC_PC(sizeof(bx_inst));
INC_PC(sizeof(bx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
BXJ_INST:
CDP_INST:
@@ -4393,12 +4372,13 @@ void InterpreterMainLoop(ARMul_State* state)
#define CP_ACCESS_ALLOW 0
if(CP_ACCESS_ALLOW){
/* undefined instruction here */
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
- ERROR_LOG(ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]);
+ LOG_ERROR(Core_ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]);
unsigned cpab = (cpu->CDP[inst_cream->cp_num]) (cpu, ARMul_FIRST, inst_cream->inst);
if(cpab != ARMul_DONE){
- ERROR_LOG(ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num);
+ LOG_ERROR(Core_ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num);
//CITRA_IGNORE_EXIT(-1);
}
}
@@ -4522,7 +4502,7 @@ void InterpreterMainLoop(ARMul_State* state)
// RD = RM;
if ((inst_cream->Rd == 15)) {
INC_PC(sizeof(mov_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
// DEBUG_LOG(ARM11, "cpy inst %x\n", cpu->Reg[15]);
@@ -4558,7 +4538,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(eor_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4717,7 +4697,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (BIT(inst, 15)) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4764,7 +4744,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->TFlag = value & 0x1;
cpu->Reg[15] &= 0xFFFFFFFE;
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
//}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4794,7 +4774,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->TFlag = value & 0x1;
cpu->Reg[15] &= 0xFFFFFFFE;
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4825,7 +4805,7 @@ void InterpreterMainLoop(ARMul_State* state)
& 0xffff;
RD = RN + operand2;
if (inst_cream->Rn == 15 || inst_cream->Rm == 15) {
- DEBUG_LOG(ARM11, "in line %d\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operands for UXTAH");
CITRA_IGNORE_EXIT(-1);
}
}
@@ -4848,7 +4828,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4869,7 +4849,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4888,7 +4868,7 @@ void InterpreterMainLoop(ARMul_State* state)
uint32_t rear_phys_addr;
fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 1);
if(fault){
- ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n");
+ LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n");
CITRA_IGNORE_EXIT(-1);
goto MMU_EXCEPTION;
}
@@ -4926,7 +4906,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4953,7 +4933,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4980,7 +4960,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5006,7 +4986,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5031,7 +5011,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5058,7 +5038,7 @@ void InterpreterMainLoop(ARMul_State* state)
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5111,7 +5091,7 @@ void InterpreterMainLoop(ARMul_State* state)
switch(OPCODE_2){
case 0: /* invalidate all */
//invalidate_all_tlb(state);
- DEBUG_LOG(ARM11, "{TLB} [INSN] invalidate all\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate all");
//remove_tlb(INSN_TLB);
//erase_all(core, INSN_TLB);
break;
@@ -5137,7 +5117,7 @@ void InterpreterMainLoop(ARMul_State* state)
//invalidate_all_tlb(state);
//remove_tlb(DATA_TLB);
//erase_all(core, DATA_TLB);
- DEBUG_LOG(ARM11, "{TLB} [DATA] invalidate all\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate all");
break;
case 1: /* invalidate by MVA */
//invalidate_by_mva(state, value);
@@ -5169,13 +5149,13 @@ void InterpreterMainLoop(ARMul_State* state)
//invalidate_by_mva(state, value);
//erase_by_mva(core, RD, DATA_TLB);
//erase_by_mva(core, RD, INSN_TLB);
- DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by mva\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by mva");
break;
case 2: /* invalidate by asid */
//invalidate_by_asid(state, value);
//erase_by_asid(core, RD, DATA_TLB);
//erase_by_asid(core, RD, INSN_TLB);
- DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by asid\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by asid");
break;
default:
break;
@@ -5197,7 +5177,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
} else {
- DEBUG_LOG(ARM11, "mcr is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2);
+ LOG_ERROR(Core_ARM11, "mcr CRn=%d, CRm=%d OP2=%d is not implemented", CRn, CRm, OPCODE_2);
}
}
}
@@ -5217,7 +5197,7 @@ void InterpreterMainLoop(ARMul_State* state)
uint64_t rs = RS;
uint64_t rn = RN;
if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) {
- DEBUG_LOG(ARM11, "in __line__\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operands for MLA");
CITRA_IGNORE_EXIT(-1);
}
// RD = dst = RM * RS + RN;
@@ -5228,7 +5208,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mla_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5260,7 +5240,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mov_inst));
- goto PROFILING;
+ goto DISPATCH;
}
// return;
}
@@ -5331,7 +5311,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
}
else {
- DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2);
+ LOG_ERROR(Core_ARM11, "mrc CRn=%d, CRm=%d, OP2=%d is not implemented", CRn, CRm, OPCODE_2);
}
}
//DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2);
@@ -5422,7 +5402,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mul_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5451,7 +5431,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mvn_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5483,7 +5463,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(orr_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5522,7 +5502,7 @@ void InterpreterMainLoop(ARMul_State* state)
(((RM >> 16) & 0xff) << 8) |
((RM >> 24) & 0xff);
if (inst_cream->Rm == 15) {
- DEBUG_LOG(ARM11, "in line %d\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operand for REV");
CITRA_IGNORE_EXIT(-1);
}
}
@@ -5575,7 +5555,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsb_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5612,7 +5592,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsc_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5653,7 +5633,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sbc_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5975,7 +5955,7 @@ void InterpreterMainLoop(ARMul_State* state)
sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
if (inst_cream->Rm == 15) {
- DEBUG_LOG(ARM11, "line is %d\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operand for SXTB");
CITRA_IGNORE_EXIT(-1);
}
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
@@ -6066,7 +6046,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
//if (BITS(inst_cream->inst, 12, 15) == 15)
- // goto PROFILING;
+ // goto DISPATCH;
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
@@ -6081,7 +6061,7 @@ void InterpreterMainLoop(ARMul_State* state)
uint32_t rear_phys_addr;
fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 0);
if (fault){
- ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n");
+ LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n");
CITRA_IGNORE_EXIT(-1);
goto MMU_EXCEPTION;
}
@@ -6175,7 +6155,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
//if (BITS(inst_cream->inst, 12, 15) == 15)
- // goto PROFILING;
+ // goto DISPATCH;
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
@@ -6225,7 +6205,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sub_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6449,7 +6429,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
//DEBUG_LOG(ARM11, " BL_1_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]);
INC_PC(sizeof(b_2_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
B_COND_THUMB:
{
@@ -6461,7 +6441,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[15] += 2;
//DEBUG_LOG(ARM11, " B_COND_THUMB: imm=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[15]);
INC_PC(sizeof(b_cond_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
BL_1_THUMB:
{
@@ -6487,7 +6467,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[14] = tmp;
//DEBUG_LOG(ARM11, " BL_2_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]);
INC_PC(sizeof(bl_2_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
BLX_1_THUMB:
{
@@ -6503,7 +6483,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->TFlag = 0;
//DEBUG_LOG(ARM11, "In BLX_1_THUMB, BLX(1),imm=0x%x,r14=0x%x, r15=0x%x, \n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]);
INC_PC(sizeof(blx_1_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
UQADD16_INST:
@@ -6532,12 +6512,14 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->AbortAddr = addr;
cpu->CP15[CP15(CP15_FAULT_STATUS)] = fault & 0xff;
cpu->CP15[CP15(CP15_FAULT_ADDRESS)] = addr;
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
END:
{
SAVE_NZCVT;
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
INIT_INST_LENGTH:
{
@@ -6557,7 +6539,8 @@ void InterpreterMainLoop(ARMul_State* state)
DEBUG_LOG(ARM11, "%llx\n", InstLabel[1]);
DEBUG_LOG(ARM11, "%lld\n", (char *)InstEndLabel[1] - (char *)InstLabel[1]);
#endif
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
}
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.h b/src/core/arm/dyncom/arm_dyncom_interpreter.h
index d73f8f65f..3a2462f55 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.h
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.h
@@ -1,7 +1,7 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
-void InterpreterMainLoop(ARMul_State* state);
+unsigned InterpreterMainLoop(ARMul_State* state);
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index ed4415082..e2aa5ce92 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/arm/interpreter/arm_interpreter.h"
@@ -24,7 +24,7 @@ ARM_Interpreter::ARM_Interpreter() {
state->lateabtSig = LOW;
// Reset the core to initial state
- ARMul_CoProInit(state);
+ ARMul_CoProInit(state);
ARMul_Reset(state);
state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext
state->Emulate = 3;
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index ceb1be438..ed53d997c 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -18,7 +18,7 @@ public:
/**
* Set the Program Counter to an address
- * @param addr Address to set PC to
+ * @param pc Address to set PC to
*/
void SetPC(u32 pc) override;
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp
index 73223874e..1a589e39c 100644
--- a/src/core/arm/interpreter/armemu.cpp
+++ b/src/core/arm/interpreter/armemu.cpp
@@ -949,7 +949,7 @@ ARMul_Emulate26 (ARMul_State * state)
//printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp);
if (armOp == 0xDEADC0DE) {
- DEBUG("Failed to decode thumb opcode %04X at %08X\n", instr, pc);
+ LOG_ERROR(Core_ARM11, "Failed to decode thumb opcode %04X at %08X", instr, pc);
}
instr = armOp;
@@ -1166,7 +1166,7 @@ mainswitch:
else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) {
//(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m))
unsigned msb ,tmp_rn, tmp_rd, dst;
- msb = tmp_rd = tmp_rn = dst = 0;
+ tmp_rd = tmp_rn = dst = 0;
Rd = BITS(12, 15);
Rn = BITS(0, 3);
lsb = BITS(7, 11);
@@ -1737,7 +1737,7 @@ mainswitch:
//chy 2006-02-15 if in user mode, can not set cpsr 0:23
//from p165 of ARMARM book
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS & rhs;
@@ -1877,7 +1877,7 @@ mainswitch:
/* TEQP reg */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS ^ rhs;
@@ -1993,7 +1993,7 @@ mainswitch:
/* CMPP reg. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS - rhs;
@@ -2112,7 +2112,7 @@ mainswitch:
if (DESTReg == 15) {
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS + rhs;
@@ -2200,17 +2200,57 @@ mainswitch:
Handle_Store_Double (state, instr);
break;
}
+ if (BITS(4, 11) == 0xF9) { //strexd
+ u32 l = LHSReg;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr)&&
+ state->currentexvald == (u32)ARMul_ReadWord(state, state->currentexaddr + 4))
+ enter = true;
+
+
+ //todo bug this and STREXD and LDREXD http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDGJGGC.html
+
+
+ if (enter) {
+ ARMul_StoreWordN(state, LHS, state->Reg[RHSReg]);
+ ARMul_StoreWordN(state,LHS + 4 , state->Reg[RHSReg + 1]);
+ state->Reg[DESTReg] = 0;
+ } else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ break;
+ }
#endif
dest = DPRegRHS;
WRITEDEST (dest);
break;
- case 0x1b: /* MOVS reg */
+ case 0x1B: /* MOVS reg */
#ifdef MODET
+ /* ldrexd ichfly */
+ if (BITS(0, 11) == 0xF9F) { //strexd
+ lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = (u32)ARMul_ReadWord(state, lhs);
+ state->currentexvald = (u32)ARMul_ReadWord(state, lhs + 4);
+
+ state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs);
+ state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs + 4);
+ break;
+ }
+
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, write-back, up, pre indexed. */
LHPREUPWB ();
/* Continue with remaining instruction decoding. */
+
+
+
+
#endif
dest = DPSRegRHS;
WRITESDEST (dest);
@@ -2297,12 +2337,12 @@ mainswitch:
if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true;
- ARMul_StoreHalfWord(state, lhs, RHS);
//StoreWord(state, lhs, RHS)
if (state->Aborted) {
TAKEABORT;
}
if (enter) {
+ ARMul_StoreHalfWord(state, lhs, RHS);
state->Reg[DESTReg] = 0;
} else {
state->Reg[DESTReg] = 1;
@@ -2520,7 +2560,7 @@ mainswitch:
/* TSTP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS & DPImmRHS;
SETR15PSR (temp);
@@ -2547,7 +2587,7 @@ mainswitch:
/* TEQP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS ^ DPImmRHS;
SETR15PSR (temp);
@@ -2568,7 +2608,7 @@ mainswitch:
/* CMPP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS - DPImmRHS;
SETR15PSR (temp);
@@ -2604,7 +2644,7 @@ mainswitch:
/* CMNP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS + DPImmRHS;
SETR15PSR (temp);
@@ -3055,17 +3095,14 @@ mainswitch:
case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */
//ichfly PKHBT PKHTB todo check this
- if ((instr & 0x70) == 0x10) //pkhbt
- {
+ if ((instr & 0x70) == 0x10) { //pkhbt
u8 idest = BITS(12, 15);
u8 rfis = BITS(16, 19);
u8 rlast = BITS(0, 3);
u8 ishi = BITS(7,11);
state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000);
break;
- }
- else if ((instr & 0x70) == 0x50)//pkhtb
- {
+ } else if ((instr & 0x70) == 0x50) { //pkhtb
u8 idest = BITS(12, 15);
u8 rfis = BITS(16, 19);
u8 rlast = BITS(0, 3);
@@ -3073,8 +3110,7 @@ mainswitch:
if (ishi == 0)ishi = 0x20;
state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000);
break;
- }
- else if (BIT (4)) {
+ } else if (BIT (4)) {
#ifdef MODE32
if (state->is_v6
&& handle_v6_insn (state, instr))
@@ -3437,7 +3473,7 @@ mainswitch:
case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */
if (BIT (4)) {
- DEBUG("got unhandled special breakpoint\n");
+ LOG_DEBUG(Core_ARM11, "got unhandled special breakpoint");
return 1;
}
UNDEF_LSRBaseEQOffWb;
@@ -3686,13 +3722,11 @@ mainswitch:
/* Co-Processor Data Transfers. */
case 0xc4:
- if ((instr & 0x0FF00FF0) == 0xC400B10) //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0
- {
+ if ((instr & 0x0FF00FF0) == 0xC400B10) { //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0
state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)];
state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)];
break;
- }
- else if (state->is_v5) {
+ } else if (state->is_v5) {
/* Reading from R15 is UNPREDICTABLE. */
if (BITS (12, 15) == 15 || BITS (16, 19) == 15)
ARMul_UndefInstr (state, instr);
@@ -3712,22 +3746,18 @@ mainswitch:
break;
case 0xc5:
- if ((instr & 0x00000FF0) == 0xB10) //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0
- {
+ if ((instr & 0x00000FF0) == 0xB10) { //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0
state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1];
state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1];
break;
- }
- else if (state->is_v5) {
+ } else if (state->is_v5) {
/* Writes to R15 are UNPREDICATABLE. */
if (DESTReg == 15 || LHSReg == 15)
ARMul_UndefInstr (state, instr);
/* Is access to the coprocessor allowed ? */
- else if (!CP_ACCESS_ALLOWED(state, CPNum))
- {
+ else if (!CP_ACCESS_ALLOWED(state, CPNum)) {
ARMul_UndefInstr(state, instr);
- }
- else {
+ } else {
/* MRRC, ARMv5TE and up */
ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg]));
break;
@@ -4565,7 +4595,7 @@ out:
#ifdef MODE32
if (state->Bank > 0) {
state->Cpsr = state->Spsr[state->Bank];
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
}
#ifdef MODET
if (TFLAG)
@@ -5256,7 +5286,7 @@ L_ldm_s_makeabort:
//chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
if (state->Mode != USER26MODE && state->Mode != USER32MODE ) {
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
}
WriteR15 (state, PC);
@@ -5641,30 +5671,9 @@ L_stm_s_takeabort:
static int
handle_v6_insn (ARMul_State * state, ARMword instr) {
- switch (BITS (20, 27)) {
- //ichfly
- case 0x66: //UQSUB8
- if ((instr & 0x0FF00FF0) == 0x06600FF0) {
- u32 rd = (instr >> 12) & 0xF;
- u32 rm = (instr >> 16) & 0xF;
- u32 rn = (instr >> 0) & 0xF;
- u32 subfrom = state->Reg[rm];
- u32 tosub = state->Reg[rn];
+ ARMword lhs, temp;
- u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub));
- if (b1 > (u8)(subfrom)) b1 = 0;
- u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8));
- if (b2 > (u8)(subfrom >> 8)) b2 = 0;
- u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16));
- if (b3 > (u8)(subfrom >> 16)) b3 = 0;
- u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24));
- if (b4 > (u8)(subfrom >> 24)) b4 = 0;
- state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24);
- return 1;
- } else {
- printf("UQSUB8 decoding fail %08X",instr);
- }
-#if 0
+ switch (BITS (20, 27)) {
case 0x03:
printf ("Unhandled v6 insn: ldr\n");
break;
@@ -5678,9 +5687,43 @@ L_stm_s_takeabort:
printf ("Unhandled v6 insn: smi\n");
break;
case 0x18:
+ if (BITS(4, 7) == 0x9) {
+ /* strex */
+ u32 l = LHSReg;
+ u32 r = RHSReg;
+ lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
+ //StoreWord(state, lhs, RHS)
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+
+ if (enter) {
+ ARMul_StoreWordS(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ }
+ else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ return 1;
+ }
printf ("Unhandled v6 insn: strex\n");
break;
case 0x19:
+ /* ldrex */
+ if (BITS(4, 7) == 0x9) {
+ lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = ARMul_ReadWord(state, lhs);
+
+ LoadWord(state, instr, lhs);
+ return 1;
+ }
printf ("Unhandled v6 insn: ldrex\n");
break;
case 0x1a:
@@ -5690,9 +5733,52 @@ L_stm_s_takeabort:
printf ("Unhandled v6 insn: ldrexd\n");
break;
case 0x1c:
+ if (BITS(4, 7) == 0x9) {
+ /* strexb */
+ lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
+
+ BUSUSEDINCPCN;
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+
+
+ if (enter) {
+ ARMul_StoreByte(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ }
+ else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ //printf("In %s, strexb not implemented\n", __FUNCTION__);
+ UNDEF_LSRBPC;
+ /* WRITESDEST (dest); */
+ return 1;
+ }
printf ("Unhandled v6 insn: strexb\n");
break;
case 0x1d:
+ if ((BITS(4, 7)) == 0x9) {
+ /* ldrexb */
+ temp = LHS;
+ LoadByte(state, instr, temp, LUNSIGNED);
+
+ state->currentexaddr = temp;
+ state->currentexval = (u32)ARMul_ReadByte(state, temp);
+
+ //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
+ //printf("ldrexb\n");
+ //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
+ //exit(-1);
+
+ //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
+ return 1;
+ }
printf ("Unhandled v6 insn: ldrexb\n");
break;
case 0x1e:
@@ -5713,10 +5799,8 @@ L_stm_s_takeabort:
case 0x3f:
printf ("Unhandled v6 insn: rbit\n");
break;
-#endif
case 0x61:
- if ((instr & 0xFF0) == 0xf70)//ssub16
- {
+ if ((instr & 0xFF0) == 0xf70) { //ssub16
u8 tar = BITS(12, 15);
u8 src1 = BITS(16, 19);
u8 src2 = BITS(0, 3);
@@ -5724,23 +5808,20 @@ L_stm_s_takeabort:
s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
s16 b1 = (state->Reg[src2] & 0xFFFF);
s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a1 - a2)&0xFFFF | (((b1 - b2)&0xFFFF)<< 0x10);
+ state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10);
return 1;
- }
- else if ((instr & 0xFF0) == 0xf10)//sadd16
- {
- u8 tar = BITS(12, 15);
- u8 src1 = BITS(16, 19);
- u8 src2 = BITS(0, 3);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = (state->Reg[src2] & 0xFFFF);
- s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a1 + a2)&0xFFFF | (((b1 + b2)&0xFFFF)<< 0x10);
+ } else if ((instr & 0xFF0) == 0xf10) { //sadd16
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
+ const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF);
+ const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
+ const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF);
+
+ state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16);
return 1;
- }
- else if ((instr & 0xFF0) == 0xf50)//ssax
- {
+ } else if ((instr & 0xFF0) == 0xf50) { //ssax
u8 tar = BITS(12, 15);
u8 src1 = BITS(16, 19);
u8 src2 = BITS(0, 3);
@@ -5748,11 +5829,9 @@ L_stm_s_takeabort:
s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
s16 b1 = (state->Reg[src2] & 0xFFFF);
s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a1 - b2) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10);
+ state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10);
return 1;
- }
- else if ((instr & 0xFF0) == 0xf30)//sasx
- {
+ } else if ((instr & 0xFF0) == 0xf30) { //sasx
u8 tar = BITS(12, 15);
u8 src1 = BITS(16, 19);
u8 src2 = BITS(0, 3);
@@ -5760,127 +5839,445 @@ L_stm_s_takeabort:
s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
s16 b1 = (state->Reg[src2] & 0xFFFF);
s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a2 - b1) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10);
+ state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10);
return 1;
- }
- else printf ("Unhandled v6 insn: sadd/ssub\n");
+ } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n");
break;
- case 0x62:
- if ((instr & 0xFF0) == 0xf70)//QSUB16
- {
- u8 tar = BITS(12, 15);
- u8 src1 = BITS(16, 19);
- u8 src2 = BITS(0, 3);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = (state->Reg[src2] & 0xFFFF);
- s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- s32 res1 = (a1 - b1);
- s32 res2 = (a2 - b2);
- if (res1 > 0x7FFF) res1 = 0x7FFF;
- if (res2 > 0x7FFF) res2 = 0x7FFF;
- if (res1 < 0x7FFF) res1 = -0x8000;
- if (res2 < 0x7FFF) res2 = -0x8000;
- state->Reg[tar] = (res1 & 0xFFFF) | ((res2 & 0xFFFF) << 0x10);
- return 1;
- }
- else if ((instr & 0xFF0) == 0xf10)//QADD16
- {
- u8 tar = BITS(12, 15);
- u8 src1 = BITS(16, 19);
- u8 src2 = BITS(0, 3);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = (state->Reg[src2] & 0xFFFF);
- s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- s32 res1 = (a1 + b1);
- s32 res2 = (a2 + b2);
- if (res1 > 0x7FFF) res1 = 0x7FFF;
- if (res2 > 0x7FFF) res2 = 0x7FFF;
- if (res1 < 0x7FFF) res1 = -0x8000;
- if (res2 < 0x7FFF) res2 = -0x8000;
- state->Reg[tar] = ((res1) & 0xFFFF) | (((res2) & 0xFFFF) << 0x10);
+ case 0x62: // QSUB16 and QADD16
+ if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rm_idx = BITS(0, 3);
+ const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
+ const s16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF);
+ const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
+ const s16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF);
+
+ s32 lo_result;
+ s32 hi_result;
+
+ // QSUB16
+ if ((instr & 0xFF0) == 0xf70) {
+ lo_result = (rn_lo - rm_lo);
+ hi_result = (rn_hi - rm_hi);
+ }
+ else { // QADD16
+ lo_result = (rn_lo + rm_lo);
+ hi_result = (rn_hi + rm_hi);
+ }
+
+ if (lo_result > 0x7FFF)
+ lo_result = 0x7FFF;
+ else if (lo_result < -0x8000)
+ lo_result = -0x8000;
+
+ if (hi_result > 0x7FFF)
+ hi_result = 0x7FFF;
+ else if (hi_result < -0x8000)
+ hi_result = -0x8000;
+
+ state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
return 1;
+ } else {
+ printf("Unhandled v6 insn: %08x", BITS(20, 27));
}
- else printf ("Unhandled v6 insn: qadd/qsub\n");
break;
-#if 0
case 0x63:
printf ("Unhandled v6 insn: shadd/shsub\n");
break;
case 0x65:
- printf ("Unhandled v6 insn: uadd/usub\n");
+ {
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rn = (instr >> 16) & 0xF;
+ u32 rm = (instr >> 0) & 0xF;
+ u32 from = state->Reg[rn];
+ u32 to = state->Reg[rm];
+
+ if ((instr & 0xFF0) == 0xF10 || (instr & 0xFF0) == 0xF70) { // UADD16/USUB16
+ u32 h1, h2;
+ state->Cpsr &= 0xfff0ffff;
+ if ((instr & 0x0F0) == 0x070) { // USUB16
+ h1 = ((u16)from - (u16)to);
+ h2 = ((u16)(from >> 16) - (u16)(to >> 16));
+ if (!(h1 & 0xffff0000)) state->Cpsr |= (3 << 16);
+ if (!(h2 & 0xffff0000)) state->Cpsr |= (3 << 18);
+ }
+ else { // UADD16
+ h1 = ((u16)from + (u16)to);
+ h2 = ((u16)(from >> 16) + (u16)(to >> 16));
+ if (h1 & 0xffff0000) state->Cpsr |= (3 << 16);
+ if (h2 & 0xffff0000) state->Cpsr |= (3 << 18);
+ }
+ state->Reg[rd] = (u32)((h1 & 0xffff) | ((h2 & 0xffff) << 16));
+ return 1;
+ }
+ else
+ if ((instr & 0xFF0) == 0xF90 || (instr & 0xFF0) == 0xFF0) { // UADD8/USUB8
+ u32 b1, b2, b3, b4;
+ state->Cpsr &= 0xfff0ffff;
+ if ((instr & 0x0F0) == 0x0F0) { // USUB8
+ b1 = ((u8)from - (u8)to);
+ b2 = ((u8)(from >> 8) - (u8)(to >> 8));
+ b3 = ((u8)(from >> 16) - (u8)(to >> 16));
+ b4 = ((u8)(from >> 24) - (u8)(to >> 24));
+ if (!(b1 & 0xffffff00)) state->Cpsr |= (1 << 16);
+ if (!(b2 & 0xffffff00)) state->Cpsr |= (1 << 17);
+ if (!(b3 & 0xffffff00)) state->Cpsr |= (1 << 18);
+ if (!(b4 & 0xffffff00)) state->Cpsr |= (1 << 19);
+ }
+ else { // UADD8
+ b1 = ((u8)from + (u8)to);
+ b2 = ((u8)(from >> 8) + (u8)(to >> 8));
+ b3 = ((u8)(from >> 16) + (u8)(to >> 16));
+ b4 = ((u8)(from >> 24) + (u8)(to >> 24));
+ if (b1 & 0xffffff00) state->Cpsr |= (1 << 16);
+ if (b2 & 0xffffff00) state->Cpsr |= (1 << 17);
+ if (b3 & 0xffffff00) state->Cpsr |= (1 << 18);
+ if (b4 & 0xffffff00) state->Cpsr |= (1 << 19);
+ }
+ state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24);
+ return 1;
+ }
+ }
+ printf("Unhandled v6 insn: uasx/usax\n");
break;
case 0x66:
- printf ("Unhandled v6 insn: uqadd/uqsub\n");
+ if ((instr & 0x0FF00FF0) == 0x06600FF0) { //uqsub8
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rm = (instr >> 16) & 0xF;
+ u32 rn = (instr >> 0) & 0xF;
+ u32 subfrom = state->Reg[rm];
+ u32 tosub = state->Reg[rn];
+
+ u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub));
+ if (b1 > (u8)(subfrom)) b1 = 0;
+ u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8));
+ if (b2 > (u8)(subfrom >> 8)) b2 = 0;
+ u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16));
+ if (b3 > (u8)(subfrom >> 16)) b3 = 0;
+ u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24));
+ if (b4 > (u8)(subfrom >> 24)) b4 = 0;
+ state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24);
+ return 1;
+ } else {
+ printf ("Unhandled v6 insn: uqsub16\n");
+ }
break;
case 0x67:
printf ("Unhandled v6 insn: uhadd/uhsub\n");
break;
case 0x68:
- printf ("Unhandled v6 insn: pkh/sxtab/selsxtb\n");
- break;
-#endif
- case 0x6c:
- if ((instr & 0xf03f0) == 0xf0070) //uxtb16
- {
- u8 src1 = BITS(0, 3);
- u8 tar = BITS(12, 15);
- u32 base = state->Reg[src1];
- u32 shamt = BITS(9,10)* 8;
- u32 in = ((base << (32 - shamt)) | (base >> shamt));
- state->Reg[tar] = in & 0x00FF00FF;
+ {
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rn = (instr >> 16) & 0xF;
+ u32 rm = (instr >> 0) & 0xF;
+ u32 from = state->Reg[rn];
+ u32 to = state->Reg[rm];
+ u32 cpsr = state->Cpsr;
+ if ((instr & 0xFF0) == 0xFB0) { // SEL
+ u32 result;
+ if (cpsr & (1 << 16))
+ result = from & 0xff;
+ else
+ result = to & 0xff;
+ if (cpsr & (1 << 17))
+ result |= from & 0x0000ff00;
+ else
+ result |= to & 0x0000ff00;
+ if (cpsr & (1 << 18))
+ result |= from & 0x00ff0000;
+ else
+ result |= to & 0x00ff0000;
+ if (cpsr & (1 << 19))
+ result |= from & 0xff000000;
+ else
+ result |= to & 0xff000000;
+ state->Reg[rd] = result;
return 1;
}
- else
- printf ("Unhandled v6 insn: uxtb16/uxtab16\n");
+ }
+ printf("Unhandled v6 insn: pkh/sxtab/selsxtb\n");
break;
+ case 0x6a: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0x01:
+ case 0xf3:
+ //ichfly
+ //SSAT16
+ {
+ u8 tar = BITS(12, 15);
+ u8 src = BITS(0, 3);
+ u8 val = BITS(16, 19) + 1;
+ s16 a1 = (state->Reg[src]);
+ s16 a2 = (state->Reg[src] >> 0x10);
+ s16 min = (s16)(0x8000 >> (16 - val));
+ s16 max = 0x7FFF >> (16 - val);
+ if (min > a1) a1 = min;
+ if (max < a1) a1 = max;
+ if (min > a2) a2 = min;
+ if (max < a2) a2 = max;
+ u32 temp2 = ((u32)(a2)) << 0x10;
+ state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
+ }
+
+ return 1;
+ default:
+ break;
+ }
+
+ if (ror == -1) {
+ if (BITS(4, 6) == 0x7) {
+ printf("Unhandled v6 insn: ssat\n");
+ return 0;
+ }
+ break;
+ }
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF);
+ if (Rm & 0x80)
+ Rm |= 0xffffff00;
+
+ if (BITS(16, 19) == 0xf)
+ /* SXTB */
+ state->Reg[BITS(12, 15)] = Rm;
+ else
+ /* SXTAB */
+ state->Reg[BITS(12, 15)] += Rm;
+
+ return 1;
+ }
+ case 0x6b: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0xf3:
+ DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
+ return 1;
+ case 0xfb:
+ DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
+ return 1;
+ default:
+ break;
+ }
+
+ if (ror == -1)
+ break;
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF);
+ if (Rm & 0x8000)
+ Rm |= 0xffff0000;
+
+ if (BITS(16, 19) == 0xf)
+ /* SXTH */
+ state->Reg[BITS(12, 15)] = Rm;
+ else
+ /* SXTAH */
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm;
+
+ return 1;
+ }
+ case 0x6c: // UXTB16 and UXTAB16
+ {
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rd_idx = BITS(12, 15);
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+ const u32 rotation = BITS(10, 11) * 8;
+ const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
+
+ // UXTB16
+ if ((instr & 0xf03f0) == 0xf0070) {
+ state->Reg[rd_idx] = rotated_rm & 0x00FF00FF;
+ }
+ else { // UXTAB16
+ const u8 lo_rotated = (rotated_rm & 0xFF);
+ const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated;
+
+ const u8 hi_rotated = (rotated_rm >> 16) & 0xFF;
+ const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated;
+
+ state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF));
+ }
+
+ return 1;
+ }
+ break;
+ case 0x6e: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0x01:
+ case 0xf3:
+ //ichfly
+ //USAT16
+ {
+ u8 tar = BITS(12, 15);
+ u8 src = BITS(0, 3);
+ u8 val = BITS(16, 19);
+ s16 a1 = (state->Reg[src]);
+ s16 a2 = (state->Reg[src] >> 0x10);
+ s16 max = 0xFFFF >> (16 - val);
+ if (max < a1) a1 = max;
+ if (max < a2) a2 = max;
+ u32 temp2 = ((u32)(a2)) << 0x10;
+ state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
+ }
+ return 1;
+ default:
+ break;
+ }
+
+ if (ror == -1) {
+ if (BITS(4, 6) == 0x7) {
+ printf("Unhandled v6 insn: usat\n");
+ return 0;
+ }
+ break;
+ }
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF);
+
+ if (BITS(16, 19) == 0xf)
+ /* UXTB */
+ state->Reg[BITS(12, 15)] = Rm;
+ else
+ /* UXTAB */
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm;
+
+ return 1;
+ }
+
+ case 0x6f: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0xfb:
+ printf("Unhandled v6 insn: revsh\n");
+ return 0;
+ default:
+ break;
+ }
+
+ if (ror == -1)
+ break;
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF);
+
+ /* UXT */
+ /* state->Reg[BITS (12, 15)] = Rm; */
+ /* dyf add */
+ if (BITS(16, 19) == 0xf) {
+ state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF;
+ }
+ else {
+ /* UXTAH */
+ /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */
+ // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)]
+ // , Rm, BITS(10, 11));
+ // printf("icounter is %lld\n", state->NumInstrs);
+ state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm;
+ // printf("rd is %x\n", state->Reg[BITS (12, 15)]);
+ // exit(-1);
+ }
+
+ return 1;
+ }
case 0x70:
- if ((instr & 0xf0d0) == 0xf010)//smuad //ichfly
- {
- u8 tar = BITS(16, 19);
- u8 src1 = BITS(0, 3);
- u8 src2 = BITS(8, 11);
- u8 swap = BIT(5);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
- s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = a1*a2 + b1*b2;
- return 1;
-
- }
- else if ((instr & 0xf0d0) == 0xf050)//smusd
- {
- u8 tar = BITS(16, 19);
- u8 src1 = BITS(0, 3);
- u8 src2 = BITS(8, 11);
- u8 swap = BIT(5);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
- s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = a1*a2 - b1*b2;
- return 1;
- }
- else if ((instr & 0xd0) == 0x10)//smlad
- {
- u8 tar = BITS(16, 19);
- u8 src1 = BITS(0, 3);
- u8 src2 = BITS(8, 11);
- u8 src3 = BITS(12, 15);
- u8 swap = BIT(5);
-
- u32 a3 = state->Reg[src3];
-
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
- s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = a1*a2 + b1*b2 + a3;
+ // ichfly
+ // SMUAD, SMUSD, SMLAD
+ if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || (instr & 0xd0) == 0x10) {
+ const u8 rd_idx = BITS(16, 19);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 rm_idx = BITS(8, 11);
+ const bool do_swap = (BIT(5) == 1);
+
+ u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ if (do_swap)
+ rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
+
+ const s16 rm_lo = (rm_val & 0xFFFF);
+ const s16 rm_hi = ((rm_val >> 16) & 0xFFFF);
+ const s16 rn_lo = (rn_val & 0xFFFF);
+ const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
+
+ // SMUAD
+ if ((instr & 0xf0d0) == 0xf010) {
+ state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi);
+ }
+ // SMUSD
+ else if ((instr & 0xf0d0) == 0xf050) {
+ state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi);
+ }
+ // SMLAD
+ else {
+ const u8 ra_idx = BITS(12, 15);
+ state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx];
+ }
return 1;
+ } else {
+ printf ("Unhandled v6 insn: smlsd\n");
}
- else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n");
break;
case 0x74:
printf ("Unhandled v6 insn: smlald/smlsld\n");
@@ -5891,332 +6288,18 @@ L_stm_s_takeabort:
case 0x78:
printf ("Unhandled v6 insn: usad/usada8\n");
break;
-#if 0
case 0x7a:
printf ("Unhandled v6 insn: usbfx\n");
break;
case 0x7c:
printf ("Unhandled v6 insn: bfc/bfi\n");
break;
-#endif
-
-
- /* add new instr for arm v6. */
- ARMword lhs, temp;
- case 0x18: { /* ORR reg */
- /* dyf add armv6 instr strex 2010.9.17 */
- if (BITS (4, 7) == 0x9) {
- u32 l = LHSReg;
- u32 r = RHSReg;
- lhs = LHS;
-
- bool enter = false;
-
- if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
- ARMul_StoreWordS(state, lhs, RHS);
- //StoreWord(state, lhs, RHS)
- if (state->Aborted) {
- TAKEABORT;
- }
-
- if (enter) {
- state->Reg[DESTReg] = 0;
- } else {
- state->Reg[DESTReg] = 1;
- }
-
- return 1;
- }
- break;
- }
-
- case 0x19: { /* orrs reg */
- /* dyf add armv6 instr ldrex */
- if (BITS (4, 7) == 0x9) {
- lhs = LHS;
-
- state->currentexaddr = lhs;
- state->currentexval = ARMul_ReadWord(state, lhs);
-
- LoadWord (state, instr, lhs);
- return 1;
- }
- break;
- }
-
- case 0x1c: { /* BIC reg */
- /* dyf add for STREXB */
- if (BITS (4, 7) == 0x9) {
- lhs = LHS;
-
- bool enter = false;
-
- if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
-
- ARMul_StoreByte (state, lhs, RHS);
- BUSUSEDINCPCN;
- if (state->Aborted) {
- TAKEABORT;
- }
-
-
- if (enter) {
- state->Reg[DESTReg] = 0;
- } else {
- state->Reg[DESTReg] = 1;
- }
-
- //printf("In %s, strexb not implemented\n", __FUNCTION__);
- UNDEF_LSRBPC;
- /* WRITESDEST (dest); */
- return 1;
- }
- break;
- }
-
- case 0x1d: { /* BICS reg */
- if ((BITS (4, 7)) == 0x9) {
- /* ldrexb */
- temp = LHS;
- LoadByte (state, instr, temp, LUNSIGNED);
-
- state->currentexaddr = temp;
- state->currentexval = (u32)ARMul_ReadByte(state, temp);
-
- //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
- //printf("ldrexb\n");
- //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
- //exit(-1);
-
- //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
- return 1;
- }
- break;
- }
- /* add end */
-
- case 0x6a: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0x01:
- case 0xf3:
- //ichfly
- //SSAT16
- {
- u8 tar = BITS(12,15);
- u8 src = BITS(0, 3);
- u8 val = BITS(16, 19) + 1;
- s16 a1 = (state->Reg[src]);
- s16 a2 = (state->Reg[src] >> 0x10);
- s16 min = (s16)(0x8000) >> (16 - val);
- s16 max = 0x7FFF >> (16 - val);
- if (min > a1) a1 = min;
- if (max < a1) a1 = max;
- if (min > a2) a2 = min;
- if (max < a2) a2 = max;
- u32 temp2 = ((u32)(a2)) << 0x10;
- state->Reg[tar] = (a1&0xFFFF) | (temp2);
- }
-
- return 1;
- default:
- break;
- }
-
- if (ror == -1) {
- if (BITS (4, 6) == 0x7) {
- printf ("Unhandled v6 insn: ssat\n");
- return 0;
- }
- break;
- }
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
- if (Rm & 0x80)
- Rm |= 0xffffff00;
-
- if (BITS (16, 19) == 0xf)
- /* SXTB */
- state->Reg[BITS (12, 15)] = Rm;
- else
- /* SXTAB */
- state->Reg[BITS (12, 15)] += Rm;
- }
- return 1;
-
- case 0x6b: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0xf3:
- DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
- return 1;
- case 0xfb:
- DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
- return 1;
- default:
- break;
- }
-
- if (ror == -1)
- break;
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
- if (Rm & 0x8000)
- Rm |= 0xffff0000;
-
- if (BITS (16, 19) == 0xf)
- /* SXTH */
- state->Reg[BITS (12, 15)] = Rm;
- else
- /* SXTAH */
- state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
- }
- return 1;
-
- case 0x6e: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0x01:
- case 0xf3:
- //ichfly
- //USAT16
- {
- u8 tar = BITS(12, 15);
- u8 src = BITS(0, 3);
- u8 val = BITS(16, 19);
- s16 a1 = (state->Reg[src]);
- s16 a2 = (state->Reg[src] >> 0x10);
- s16 max = 0xFFFF >> (16 - val);
- if (max < a1) a1 = max;
- if (max < a2) a2 = max;
- u32 temp2 = ((u32)(a2)) << 0x10;
- state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
- }
- return 1;
- default:
- break;
- }
-
- if (ror == -1) {
- if (BITS (4, 6) == 0x7) {
- printf ("Unhandled v6 insn: usat\n");
- return 0;
- }
- break;
- }
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
-
- if (BITS (16, 19) == 0xf)
- /* UXTB */
- state->Reg[BITS (12, 15)] = Rm;
- else
- /* UXTAB */
- state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
- }
- return 1;
-
- case 0x6f: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0xfb:
- printf ("Unhandled v6 insn: revsh\n");
- return 0;
- default:
- break;
- }
-
- if (ror == -1)
- break;
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
-
- /* UXT */
- /* state->Reg[BITS (12, 15)] = Rm; */
- /* dyf add */
- if (BITS (16, 19) == 0xf) {
- state->Reg[BITS (12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF;
- } else {
- /* UXTAH */
- /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */
-// printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)]
-// , Rm, BITS(10, 11));
-// printf("icounter is %lld\n", state->NumInstrs);
- state->Reg[BITS (12, 15)] = (state->Reg[BITS (16, 19)] >> (8 * (BITS(10, 11)))) + Rm;
-// printf("rd is %x\n", state->Reg[BITS (12, 15)]);
-// exit(-1);
- }
- }
- return 1;
-
-#if 0
case 0x84:
printf ("Unhandled v6 insn: srs\n");
break;
-#endif
default:
break;
}
printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27));
return 0;
- }
+ } \ No newline at end of file
diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp
index 2568b93ef..30519f216 100644
--- a/src/core/arm/interpreter/armsupp.cpp
+++ b/src/core/arm/interpreter/armsupp.cpp
@@ -665,7 +665,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
//if (!CP_ACCESS_ALLOWED (state, CPNum)) {
if (!state->MCR[CPNum]) {
//chy 2004-07-19 should fix in the future ????!!!!
- DEBUG("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x\n",CPNum, source);
+ LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x",CPNum, source);
ARMul_UndefInstr (state, instr);
return;
}
@@ -690,7 +690,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
}
if (cpab == ARMul_CANT) {
- DEBUG("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x\n", instr, CPNum, source); //ichfly todo
+ LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x", instr, CPNum, source); //ichfly todo
//ARMul_Abort (state, ARMul_UndefinedInstrV);
} else {
BUSUSEDINCPCN;
@@ -762,7 +762,7 @@ ARMword ARMul_MRC (ARMul_State * state, ARMword instr)
//if (!CP_ACCESS_ALLOWED (state, CPNum)) {
if (!state->MRC[CPNum]) {
//chy 2004-07-19 should fix in the future????!!!!
- DEBUG("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x\n", CPNum, instr);
+ LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x", CPNum, instr);
ARMul_UndefInstr (state, instr);
return -1;
}
@@ -865,7 +865,7 @@ void
ARMul_UndefInstr (ARMul_State * state, ARMword instr)
{
std::string disasm = ARM_Disasm::Disassemble(state->pc, instr);
- ERROR_LOG(ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
+ LOG_ERROR(Core_ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
ARMul_Abort (state, ARMul_UndefinedInstrV);
}
diff --git a/src/core/arm/interpreter/thumbemu.cpp b/src/core/arm/interpreter/thumbemu.cpp
index f7f11f714..9cf80672d 100644
--- a/src/core/arm/interpreter/thumbemu.cpp
+++ b/src/core/arm/interpreter/thumbemu.cpp
@@ -467,7 +467,7 @@ ARMul_ThumbDecode (
(state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC;
state->Reg[14] = (tmp | 1);
CLEART;
- DEBUG_LOG(ARM11, "In %s, After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
+ LOG_DEBUG(Core_ARM11, "After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x", state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
valid = t_branch;
FLUSHPIPE;
}
diff --git a/src/core/arm/skyeye_common/armcpu.h b/src/core/arm/skyeye_common/armcpu.h
index 3a029f0e7..2b756c5bc 100644
--- a/src/core/arm/skyeye_common/armcpu.h
+++ b/src/core/arm/skyeye_common/armcpu.h
@@ -24,8 +24,6 @@
#include <stddef.h>
#include <stdio.h>
-#include "common/thread.h"
-
#include "core/arm/skyeye_common/armdefs.h"
typedef struct ARM_CPU_State_s {
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 8e71948c6..28a4a0db4 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -18,38 +18,26 @@
#ifndef _ARMDEFS_H_
#define _ARMDEFS_H_
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include "common/platform.h"
-
-//teawater add for arm2x86 2005.02.14-------------------------------------------
-// koodailar remove it for mingw 2005.12.18----------------
-//anthonylee modify it for portable 2007.01.30
-//#include "portable/mman.h"
+#include <cerrno>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include "arm_regformat.h"
+#include "common/common_types.h"
#include "common/platform.h"
+#include "core/arm/skyeye_common/armmmu.h"
#include "core/arm/skyeye_common/skyeye_defs.h"
-//AJ2D--------------------------------------------------------------------------
-
-//teawater add for arm2x86 2005.07.03-------------------------------------------
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#if EMU_PLATFORM == PLATFORM_LINUX
+#include <sys/time.h>
#include <unistd.h>
#endif
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-//#include <memory_space.h>
-//AJ2D--------------------------------------------------------------------------
#if 0
#if 0
#define DIFF_STATE 1
@@ -70,25 +58,8 @@
#define LOWHIGH 1
#define HIGHLOW 2
-//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
-#include <signal.h>
-
-#include "common/platform.h"
-
-#if EMU_PLATFORM == PLATFORM_LINUX
-#include <sys/time.h>
-#endif
-
//#define DBCT_TEST_SPEED
#define DBCT_TEST_SPEED_SEC 10
-//AJ2D--------------------------------------------------------------------------
-
-//teawater add compile switch for DBCT GDB RSP function 2005.10.21--------------
-//#define DBCT_GDBRSP
-//AJ2D--------------------------------------------------------------------------
-
-//#include <skyeye_defs.h>
-//#include <skyeye_types.h>
#define ARM_BYTE_TYPE 0
#define ARM_HALFWORD_TYPE 1
@@ -103,71 +74,34 @@
typedef char *VoidStar;
#endif
-typedef unsigned long long ARMdword; /* must be 64 bits wide */
-typedef unsigned int ARMword; /* must be 32 bits wide */
-typedef unsigned char ARMbyte; /* must be 8 bits wide */
-typedef unsigned short ARMhword; /* must be 16 bits wide */
+typedef u64 ARMdword; // must be 64 bits wide
+typedef u32 ARMword; // must be 32 bits wide
+typedef u16 ARMhword; // must be 16 bits wide
+typedef u8 ARMbyte; // must be 8 bits wide
typedef struct ARMul_State ARMul_State;
typedef struct ARMul_io ARMul_io;
typedef struct ARMul_Energy ARMul_Energy;
-//teawater add for arm2x86 2005.06.24-------------------------------------------
-#include <stdint.h>
-//AJ2D--------------------------------------------------------------------------
-/*
-//chy 2005-05-11
-#ifndef __CYGWIN__
-//teawater add for arm2x86 2005.02.14-------------------------------------------
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int u32;
-#if defined (__x86_64__)
-typedef unsigned long uint64_t;
-#else
-typedef unsigned long long uint64_t;
-#endif
-////AJ2D--------------------------------------------------------------------------
-#endif
-*/
-#include "core/arm/skyeye_common/armmmu.h"
-//#include "lcd/skyeye_lcd.h"
-
-
-//#include "skyeye.h"
-//#include "skyeye_device.h"
-//#include "net/skyeye_net.h"
-//#include "skyeye_config.h"
-
-
-typedef unsigned ARMul_CPInits (ARMul_State * state);
-typedef unsigned ARMul_CPExits (ARMul_State * state);
-typedef unsigned ARMul_LDCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword value);
-typedef unsigned ARMul_STCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword * value);
-typedef unsigned ARMul_MRCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword * value);
-typedef unsigned ARMul_MCRs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword value);
-typedef unsigned ARMul_MRRCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword * value1, ARMword * value2);
-typedef unsigned ARMul_MCRRs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword value1, ARMword value2);
-typedef unsigned ARMul_CDPs (ARMul_State * state, unsigned type,
- ARMword instr);
-typedef unsigned ARMul_CPReads (ARMul_State * state, unsigned reg,
- ARMword * value);
-typedef unsigned ARMul_CPWrites (ARMul_State * state, unsigned reg,
- ARMword value);
+typedef unsigned ARMul_CPInits(ARMul_State* state);
+typedef unsigned ARMul_CPExits(ARMul_State* state);
+typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
+typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
+typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr);
+typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value);
+typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value);
//added by ksh,2004-3-5
struct ARMul_io
{
- ARMword *instr; //to display the current interrupt state
- ARMword *net_flag; //to judge if network is enabled
- ARMword *net_int; //netcard interrupt
+ ARMword *instr; // to display the current interrupt state
+ ARMword *net_flag; // to judge if network is enabled
+ ARMword *net_int; // netcard interrupt
//ywc,2004-04-01
ARMword *ts_int;
@@ -180,17 +114,17 @@ struct ARMul_io
/* added by ksh,2004-11-26,some energy profiling */
struct ARMul_Energy
{
- int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
- int enable_func_energy; /* <tktan> BUG200105181702 */
+ int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
+ int enable_func_energy; /* <tktan> BUG200105181702 */
char *func_energy;
- int func_display; /* <tktan> BUG200103311509 : for function call display */
+ int func_display; /* <tktan> BUG200103311509 : for function call display */
int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */
- char *start_func; /* <tktan> BUG200104191428 */
+ char *start_func; /* <tktan> BUG200104191428 */
- FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
+ FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
long long tcycle, pcycle;
float t_energy;
- void *cur_task; /* <tktan> BUG200103291737 */
+ void *cur_task; /* <tktan> BUG200103291737 */
long long t_mem_cycle, t_idle_cycle, t_uart_cycle;
long long p_mem_cycle, p_idle_cycle, p_uart_cycle;
long long p_io_update_tcycle;
@@ -203,13 +137,12 @@ struct ARMul_Energy
typedef struct mem_bank
{
- ARMword (*read_byte) (ARMul_State * state, ARMword addr);
- void (*write_byte) (ARMul_State * state, ARMword addr, ARMword data);
- ARMword (*read_halfword) (ARMul_State * state, ARMword addr);
- void (*write_halfword) (ARMul_State * state, ARMword addr,
- ARMword data);
- ARMword (*read_word) (ARMul_State * state, ARMword addr);
- void (*write_word) (ARMul_State * state, ARMword addr, ARMword data);
+ ARMword (*read_byte) (ARMul_State* state, ARMword addr);
+ void (*write_byte) (ARMul_State* state, ARMword addr, ARMword data);
+ ARMword (*read_halfword) (ARMul_State* state, ARMword addr);
+ void (*write_halfword) (ARMul_State* state, ARMword addr, ARMword data);
+ ARMword (*read_word) (ARMul_State* state, ARMword addr);
+ void (*write_word) (ARMul_State* state, ARMword addr, ARMword data);
unsigned int addr, len;
char filename[MAX_STR];
unsigned type; //chy 2003-09-21: maybe io,ram,rom
@@ -224,24 +157,24 @@ typedef struct
#define VFP_REG_NUM 64
struct ARMul_State
{
- ARMword Emulate; /* to start and stop emulation */
- unsigned EndCondition; /* reason for stopping */
+ ARMword Emulate; /* to start and stop emulation */
+ unsigned EndCondition; /* reason for stopping */
unsigned ErrorCode; /* type of illegal instruction */
/* Order of the following register should not be modified */
- ARMword Reg[16]; /* the current register file */
- ARMword Cpsr; /* the current psr */
+ ARMword Reg[16]; /* the current register file */
+ ARMword Cpsr; /* the current psr */
ARMword Spsr_copy;
ARMword phys_pc;
ARMword Reg_usr[2];
- ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
+ ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */
ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */
ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */
ARMword Reg_firq[7]; /* R8---R14 FIRQ */
- ARMword Spsr[7]; /* the exception psr's */
- ARMword Mode; /* the current mode */
- ARMword Bank; /* the current register bank */
+ ARMword Spsr[7]; /* the exception psr's */
+ ARMword Mode; /* the current mode */
+ ARMword Bank; /* the current register bank */
ARMword exclusive_tag;
ARMword exclusive_state;
ARMword exclusive_result;
@@ -281,38 +214,39 @@ struct ARMul_State
ARMword currentexaddr;
ARMword currentexval;
+ ARMword currentexvald;
ARMword servaddr;
unsigned NextInstr;
- unsigned VectorCatch; /* caught exception mask */
- unsigned CallDebug; /* set to call the debugger */
- unsigned CanWatch; /* set by memory interface if its willing to suffer the
- overhead of checking for watchpoints on each memory
- access */
+ unsigned VectorCatch; /* caught exception mask */
+ unsigned CallDebug; /* set to call the debugger */
+ unsigned CanWatch; /* set by memory interface if its willing to suffer the
+ overhead of checking for watchpoints on each memory
+ access */
unsigned int StopHandle;
- char *CommandLine; /* Command Line from ARMsd */
-
- ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
- ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
- ARMul_LDCs *LDC[16]; /* LDC instruction */
- ARMul_STCs *STC[16]; /* STC instruction */
- ARMul_MRCs *MRC[16]; /* MRC instruction */
- ARMul_MCRs *MCR[16]; /* MCR instruction */
- ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
- ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
- ARMul_CDPs *CDP[16]; /* CDP instruction */
- ARMul_CPReads *CPRead[16]; /* Read CP register */
- ARMul_CPWrites *CPWrite[16]; /* Write CP register */
- unsigned char *CPData[16]; /* Coprocessor data */
+ char *CommandLine; /* Command Line from ARMsd */
+
+ ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
+ ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
+ ARMul_LDCs *LDC[16]; /* LDC instruction */
+ ARMul_STCs *STC[16]; /* STC instruction */
+ ARMul_MRCs *MRC[16]; /* MRC instruction */
+ ARMul_MCRs *MCR[16]; /* MCR instruction */
+ ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
+ ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
+ ARMul_CDPs *CDP[16]; /* CDP instruction */
+ ARMul_CPReads *CPRead[16]; /* Read CP register */
+ ARMul_CPWrites *CPWrite[16]; /* Write CP register */
+ unsigned char *CPData[16]; /* Coprocessor data */
unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */
- unsigned EventSet; /* the number of events in the queue */
- unsigned int Now; /* time to the nearest cycle */
- struct EventNode **EventPtr; /* the event list */
+ unsigned EventSet; /* the number of events in the queue */
+ unsigned int Now; /* time to the nearest cycle */
+ struct EventNode **EventPtr; /* the event list */
- unsigned Debug; /* show instructions as they are executed */
- unsigned NresetSig; /* reset the processor */
+ unsigned Debug; /* show instructions as they are executed */
+ unsigned NresetSig; /* reset the processor */
unsigned NfiqSig;
unsigned NirqSig;
@@ -356,12 +290,12 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
*/
unsigned lateabtSig;
- ARMword Vector; /* synthesize aborts in cycle modes */
- ARMword Aborted; /* sticky flag for aborts */
- ARMword Reseted; /* sticky flag for Reset */
+ ARMword Vector; /* synthesize aborts in cycle modes */
+ ARMword Aborted; /* sticky flag for aborts */
+ ARMword Reseted; /* sticky flag for Reset */
ARMword Inted, LastInted; /* sticky flags for interrupts */
- ARMword Base; /* extra hand for base writeback */
- ARMword AbortAddr; /* to keep track of Prefetch aborts */
+ ARMword Base; /* extra hand for base writeback */
+ ARMword AbortAddr; /* to keep track of Prefetch aborts */
const struct Dbg_HostosInterface *hostif;
@@ -378,7 +312,7 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
//chy: 2003-08-11, for different arm core type
unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */
unsigned is_v5; /* Are we emulating a v5 architecture ? */
- unsigned is_v5e; /* Are we emulating a v5e architecture ? */
+ unsigned is_v5e; /* Are we emulating a v5e architecture ? */
unsigned is_v6; /* Are we emulating a v6 architecture ? */
unsigned is_v7; /* Are we emulating a v7 architecture ? */
unsigned is_XScale; /* Are we emulating an XScale architecture ? */
@@ -387,51 +321,43 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
//chy 2005-09-19
unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */
//chy: seems only used in xscale's CP14
- unsigned int LastTime; /* Value of last call to ARMul_Time() */
+ unsigned int LastTime; /* Value of last call to ARMul_Time() */
ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */
-//added by ksh:for handle different machs io 2004-3-5
+ //added by ksh:for handle different machs io 2004-3-5
ARMul_io mach_io;
-/*added by ksh,2004-11-26,some energy profiling*/
+ /*added by ksh,2004-11-26,some energy profiling*/
ARMul_Energy energy;
-//teawater add for next_dis 2004.10.27-----------------------
+ //teawater add for next_dis 2004.10.27-----------------------
int disassemble;
-//AJ2D------------------------------------------
-//teawater add for arm2x86 2005.02.15-------------------------------------------
+
+ //teawater add for arm2x86 2005.02.15-------------------------------------------
u32 trap;
u32 tea_break_addr;
u32 tea_break_ok;
int tea_pc;
-//AJ2D--------------------------------------------------------------------------
-//teawater add for arm2x86 2005.07.03-------------------------------------------
-
- /*
- * 2007-01-24 removed the term-io functions by Anthony Lee,
- * moved to "device/uart/skyeye_uart_stdio.c".
- */
-//AJ2D--------------------------------------------------------------------------
-//teawater add for arm2x86 2005.07.05-------------------------------------------
+ //teawater add for arm2x86 2005.07.05-------------------------------------------
//arm_arm A2-18
int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model
-//AJ2D--------------------------------------------------------------------------
-//teawater change for return if running tb dirty 2005.07.09---------------------
+
+ //teawater change for return if running tb dirty 2005.07.09---------------------
void *tb_now;
-//AJ2D--------------------------------------------------------------------------
-//teawater add for record reg value to ./reg.txt 2005.07.10---------------------
+
+ //teawater add for record reg value to ./reg.txt 2005.07.10---------------------
FILE *tea_reg_fd;
-//AJ2D--------------------------------------------------------------------------
-/*added by ksh in 2005-10-1*/
+
+ /*added by ksh in 2005-10-1*/
cpu_config_t *cpu;
//mem_config_t *mem_bank;
-/* added LPC remap function */
+ /* added LPC remap function */
int vector_remap_flag;
u32 vector_remap_addr;
u32 vector_remap_size;
@@ -486,17 +412,14 @@ typedef ARMul_State arm_core_t;
#define ARM_Debug_Prop 0x10
#define ARM_Isync_Prop ARM_Debug_Prop
#define ARM_Lock_Prop 0x20
-//chy 2003-08-11
#define ARM_v4_Prop 0x40
#define ARM_v5_Prop 0x80
-/*jeff.du 2010-08-05 */
#define ARM_v6_Prop 0xc0
#define ARM_v5e_Prop 0x100
#define ARM_XScale_Prop 0x200
#define ARM_ep9312_Prop 0x400
#define ARM_iWMMXt_Prop 0x800
-//chy 2005-09-19
#define ARM_PXA27X_Prop 0x1000
#define ARM_v7_Prop 0x2000
@@ -591,47 +514,44 @@ typedef ARMul_State arm_core_t;
#ifdef __cplusplus
extern "C" {
#endif
-extern void ARMul_EmulateInit (void);
-extern void ARMul_Reset (ARMul_State * state);
+extern void ARMul_EmulateInit();
+extern void ARMul_Reset(ARMul_State* state);
#ifdef __cplusplus
}
#endif
-extern ARMul_State *ARMul_NewState (ARMul_State * state);
-extern ARMword ARMul_DoProg (ARMul_State * state);
-extern ARMword ARMul_DoInstr (ARMul_State * state);
+extern ARMul_State *ARMul_NewState(ARMul_State* state);
+extern ARMword ARMul_DoProg(ARMul_State* state);
+extern ARMword ARMul_DoInstr(ARMul_State* state);
/***************************************************************************\
* Definitons of things for event handling *
\***************************************************************************/
-extern void ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay,
- unsigned (*func) ());
-extern void ARMul_EnvokeEvent (ARMul_State * state);
-extern unsigned int ARMul_Time (ARMul_State * state);
+extern void ARMul_ScheduleEvent(ARMul_State* state, unsigned int delay, unsigned(*func) ());
+extern void ARMul_EnvokeEvent(ARMul_State* state);
+extern unsigned int ARMul_Time(ARMul_State* state);
/***************************************************************************\
* Useful support routines *
\***************************************************************************/
-extern ARMword ARMul_GetReg (ARMul_State * state, unsigned mode,
- unsigned reg);
-extern void ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg,
- ARMword value);
-extern ARMword ARMul_GetPC (ARMul_State * state);
-extern ARMword ARMul_GetNextPC (ARMul_State * state);
-extern void ARMul_SetPC (ARMul_State * state, ARMword value);
-extern ARMword ARMul_GetR15 (ARMul_State * state);
-extern void ARMul_SetR15 (ARMul_State * state, ARMword value);
-
-extern ARMword ARMul_GetCPSR (ARMul_State * state);
-extern void ARMul_SetCPSR (ARMul_State * state, ARMword value);
-extern ARMword ARMul_GetSPSR (ARMul_State * state, ARMword mode);
-extern void ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value);
+extern ARMword ARMul_GetReg (ARMul_State* state, unsigned mode, unsigned reg);
+extern void ARMul_SetReg (ARMul_State* state, unsigned mode, unsigned reg, ARMword value);
+extern ARMword ARMul_GetPC(ARMul_State* state);
+extern ARMword ARMul_GetNextPC(ARMul_State* state);
+extern void ARMul_SetPC(ARMul_State* state, ARMword value);
+extern ARMword ARMul_GetR15(ARMul_State* state);
+extern void ARMul_SetR15(ARMul_State* state, ARMword value);
+
+extern ARMword ARMul_GetCPSR(ARMul_State* state);
+extern void ARMul_SetCPSR(ARMul_State* state, ARMword value);
+extern ARMword ARMul_GetSPSR(ARMul_State* state, ARMword mode);
+extern void ARMul_SetSPSR(ARMul_State* state, ARMword mode, ARMword value);
/***************************************************************************\
* Definitons of things to handle aborts *
\***************************************************************************/
-extern void ARMul_Abort (ARMul_State * state, ARMword address);
+extern void ARMul_Abort(ARMul_State* state, ARMword address);
#ifdef MODET
#define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */
#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \
@@ -649,54 +569,40 @@ extern void ARMul_Abort (ARMul_State * state, ARMword address);
* Definitons of things in the memory interface *
\***************************************************************************/
-extern unsigned ARMul_MemoryInit (ARMul_State * state,
- unsigned int initmemsize);
-extern void ARMul_MemoryExit (ARMul_State * state);
+extern unsigned ARMul_MemoryInit(ARMul_State* state, unsigned int initmemsize);
+extern void ARMul_MemoryExit(ARMul_State* state);
-extern ARMword ARMul_LoadInstrS (ARMul_State * state, ARMword address,
- ARMword isize);
-extern ARMword ARMul_LoadInstrN (ARMul_State * state, ARMword address,
- ARMword isize);
+extern ARMword ARMul_LoadInstrS(ARMul_State* state, ARMword address, ARMword isize);
+extern ARMword ARMul_LoadInstrN(ARMul_State* state, ARMword address, ARMword isize);
#ifdef __cplusplus
extern "C" {
#endif
-extern ARMword ARMul_ReLoadInstr (ARMul_State * state, ARMword address,
- ARMword isize);
+extern ARMword ARMul_ReLoadInstr(ARMul_State* state, ARMword address, ARMword isize);
#ifdef __cplusplus
}
#endif
-extern ARMword ARMul_LoadWordS (ARMul_State * state, ARMword address);
-extern ARMword ARMul_LoadWordN (ARMul_State * state, ARMword address);
-extern ARMword ARMul_LoadHalfWord (ARMul_State * state, ARMword address);
-extern ARMword ARMul_LoadByte (ARMul_State * state, ARMword address);
-
-extern void ARMul_StoreWordS (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_StoreWordN (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_StoreHalfWord (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_StoreByte (ARMul_State * state, ARMword address,
- ARMword data);
-
-extern ARMword ARMul_SwapWord (ARMul_State * state, ARMword address,
- ARMword data);
-extern ARMword ARMul_SwapByte (ARMul_State * state, ARMword address,
- ARMword data);
-
-extern void ARMul_Icycles (ARMul_State * state, unsigned number,
- ARMword address);
-extern void ARMul_Ccycles (ARMul_State * state, unsigned number,
- ARMword address);
-
-extern ARMword ARMul_ReadWord (ARMul_State * state, ARMword address);
-extern ARMword ARMul_ReadByte (ARMul_State * state, ARMword address);
-extern void ARMul_WriteWord (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_WriteByte (ARMul_State * state, ARMword address,
- ARMword data);
-
-extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
+extern ARMword ARMul_LoadWordS(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadWordN(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadHalfWord(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadByte(ARMul_State* state, ARMword address);
+
+extern void ARMul_StoreWordS(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreWordN(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreHalfWord(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern ARMword ARMul_SwapWord(ARMul_State* state, ARMword address, ARMword data);
+extern ARMword ARMul_SwapByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern void ARMul_Icycles(ARMul_State* state, unsigned number, ARMword address);
+extern void ARMul_Ccycles(ARMul_State* state, unsigned number, ARMword address);
+
+extern ARMword ARMul_ReadWord(ARMul_State* state, ARMword address);
+extern ARMword ARMul_ReadByte(ARMul_State* state, ARMword address);
+extern void ARMul_WriteWord(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_WriteByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern ARMword ARMul_MemAccess(ARMul_State* state, ARMword, ARMword,
ARMword, ARMword, ARMword, ARMword, ARMword,
ARMword, ARMword, ARMword);
@@ -739,82 +645,58 @@ extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
#define ARMul_CP15_DBCON_E1 0x000c
#define ARMul_CP15_DBCON_E0 0x0003
-extern unsigned ARMul_CoProInit (ARMul_State * state);
-extern void ARMul_CoProExit (ARMul_State * state);
-extern void ARMul_CoProAttach (ARMul_State * state, unsigned number,
- ARMul_CPInits * init, ARMul_CPExits * exit,
- ARMul_LDCs * ldc, ARMul_STCs * stc,
- ARMul_MRCs * mrc, ARMul_MCRs * mcr,
- ARMul_MRRCs * mrrc, ARMul_MCRRs * mcrr,
- ARMul_CDPs * cdp,
- ARMul_CPReads * read, ARMul_CPWrites * write);
-extern void ARMul_CoProDetach (ARMul_State * state, unsigned number);
+extern unsigned ARMul_CoProInit(ARMul_State* state);
+extern void ARMul_CoProExit(ARMul_State* state);
+extern void ARMul_CoProAttach (ARMul_State* state, unsigned number,
+ ARMul_CPInits* init, ARMul_CPExits* exit,
+ ARMul_LDCs* ldc, ARMul_STCs* stc,
+ ARMul_MRCs* mrc, ARMul_MCRs* mcr,
+ ARMul_MRRCs* mrrc, ARMul_MCRRs* mcrr,
+ ARMul_CDPs* cdp,
+ ARMul_CPReads* read, ARMul_CPWrites* write);
+extern void ARMul_CoProDetach(ARMul_State* state, unsigned number);
/***************************************************************************\
* Definitons of things in the host environment *
\***************************************************************************/
-extern unsigned ARMul_OSInit (ARMul_State * state);
-extern void ARMul_OSExit (ARMul_State * state);
+extern unsigned ARMul_OSInit(ARMul_State* state);
+extern void ARMul_OSExit(ARMul_State* state);
#ifdef __cplusplus
extern "C" {
#endif
-extern unsigned ARMul_OSHandleSWI (ARMul_State * state, ARMword number);
+extern unsigned ARMul_OSHandleSWI(ARMul_State* state, ARMword number);
#ifdef __cplusplus
}
#endif
-extern ARMword ARMul_OSLastErrorP (ARMul_State * state);
+extern ARMword ARMul_OSLastErrorP(ARMul_State* state);
-extern ARMword ARMul_Debug (ARMul_State * state, ARMword pc, ARMword instr);
-extern unsigned ARMul_OSException (ARMul_State * state, ARMword vector,
- ARMword pc);
+extern ARMword ARMul_Debug(ARMul_State* state, ARMword pc, ARMword instr);
+extern unsigned ARMul_OSException(ARMul_State* state, ARMword vector, ARMword pc);
extern int rdi_log;
-/***************************************************************************\
-* Host-dependent stuff *
-\***************************************************************************/
-
-#ifdef macintosh
-pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
-# define HOURGLASS SpinCursor( 1 )
-# define HOURGLASS_RATE 1023 /* 2^n - 1 */
-#endif
-
-//teawater add for arm2x86 2005.02.14-------------------------------------------
-/*ywc 2005-03-31*/
-/*
-#include "arm2x86.h"
-#include "arm2x86_dp.h"
-#include "arm2x86_movl.h"
-#include "arm2x86_psr.h"
-#include "arm2x86_shift.h"
-#include "arm2x86_mem.h"
-#include "arm2x86_mul.h"
-#include "arm2x86_test.h"
-#include "arm2x86_other.h"
-#include "list.h"
-#include "tb.h"
-*/
-#define EQ 0
-#define NE 1
-#define CS 2
-#define CC 3
-#define MI 4
-#define PL 5
-#define VS 6
-#define VC 7
-#define HI 8
-#define LS 9
-#define GE 10
-#define LT 11
-#define GT 12
-#define LE 13
-#define AL 14
-#define NV 15
+enum ConditionCode {
+ EQ = 0,
+ NE = 1,
+ CS = 2,
+ CC = 3,
+ MI = 4,
+ PL = 5,
+ VS = 6,
+ VC = 7,
+ HI = 8,
+ LS = 9,
+ GE = 10,
+ LT = 11,
+ GT = 12,
+ LE = 13,
+ AL = 14,
+ NV = 15,
+};
#ifndef NFLAG
#define NFLAG state->NFlag
@@ -849,32 +731,16 @@ pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
#define ZBIT_SHIFT 30
#define CBIT_SHIFT 29
#define VBIT_SHIFT 28
-#ifdef DBCT
-//teawater change for local tb branch directly jump 2005.10.18------------------
-#include "dbct/list.h"
-#include "dbct/arm2x86.h"
-#include "dbct/arm2x86_dp.h"
-#include "dbct/arm2x86_movl.h"
-#include "dbct/arm2x86_psr.h"
-#include "dbct/arm2x86_shift.h"
-#include "dbct/arm2x86_mem.h"
-#include "dbct/arm2x86_mul.h"
-#include "dbct/arm2x86_test.h"
-#include "dbct/arm2x86_other.h"
-#include "dbct/arm2x86_coproc.h"
-#include "dbct/tb.h"
-#endif
-//AJ2D--------------------------------------------------------------------------
-//AJ2D--------------------------------------------------------------------------
+
#define SKYEYE_OUTREGS(fd) { fprintf ((fd), "R %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,C %x,S %x,%x,%x,%x,%x,%x,%x,M %x,B %x,E %x,I %x,P %x,T %x,L %x,D %x,",\
state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \
state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \
state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \
state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \
- state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
+ state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\
- state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
- state->temp,state->loaded,state->decoded);}
+ state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
+ state->temp,state->loaded,state->decoded);}
#define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\
RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
@@ -912,17 +778,17 @@ RUn %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",\
#define SA1110 0x6901b110
#define SA1100 0x4401a100
-#define PXA250 0x69052100
-#define PXA270 0x69054110
-//#define PXA250 0x69052903
+#define PXA250 0x69052100
+#define PXA270 0x69054110
+//#define PXA250 0x69052903
// 0x69052903; //PXA250 B1 from intel 278522-001.pdf
-extern void ARMul_UndefInstr (ARMul_State *, ARMword);
-extern void ARMul_FixCPSR (ARMul_State *, ARMword, ARMword);
-extern void ARMul_FixSPSR (ARMul_State *, ARMword, ARMword);
-extern void ARMul_ConsolePrint (ARMul_State *, const char *, ...);
-extern void ARMul_SelectProcessor (ARMul_State *, unsigned);
+extern void ARMul_UndefInstr(ARMul_State*, ARMword);
+extern void ARMul_FixCPSR(ARMul_State*, ARMword, ARMword);
+extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
+extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
+extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
#define DIFF_LOG 0
#define SAVE_LOG 0
diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h
index c0f0270fe..7f7c0e682 100644
--- a/src/core/arm/skyeye_common/armemu.h
+++ b/src/core/arm/skyeye_common/armemu.h
@@ -23,26 +23,6 @@
//extern ARMword isize;
-#define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
-
-/* Condition code values. */
-#define EQ 0
-#define NE 1
-#define CS 2
-#define CC 3
-#define MI 4
-#define PL 5
-#define VS 6
-#define VC 7
-#define HI 8
-#define LS 9
-#define GE 10
-#define LT 11
-#define GT 12
-#define LE 13
-#define AL 14
-#define NV 15
-
/* Shift Opcodes. */
#define LSL 0
#define LSR 1
@@ -503,7 +483,7 @@ tdstate;
* out-of-updated with the newer ISA.
* -- Michael.Kang
********************************************************************************/
-#define UNDEF_WARNING WARN_LOG(ARM11, "undefined or unpredicted behavior for arm instruction.\n");
+#define UNDEF_WARNING LOG_WARNING(Core_ARM11, "undefined or unpredicted behavior for arm instruction.");
/* Macros to scrutinize instructions. */
#define UNDEF_Test UNDEF_WARNING
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index 07d0c1f44..6c33d8b78 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -522,8 +522,7 @@ static s64 vfp_single_to_doubleintern(ARMul_State* state, s32 m, u32 fpscr) //ic
if (tm == VFP_QNAN)
vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
goto pack_nan;
- }
- else if (tm & VFP_ZERO)
+ } else if (tm & VFP_ZERO)
vdd.exponent = 0;
else
vdd.exponent = vsm.exponent + (1023 - 127);
@@ -615,12 +614,12 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
exceptions |= FPSCR_IDC;
if (tm & VFP_NAN)
- vsm.sign = 0;
+ vsm.sign = 1;
if (vsm.exponent >= 127 + 32) {
d = vsm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC;
- } else if (vsm.exponent >= 127 - 1) {
+ } else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@@ -705,7 +704,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
if (vsm.sign)
d = ~d;
exceptions |= FPSCR_IOC;
- } else if (vsm.exponent >= 127 - 1) {
+ } else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@@ -1149,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
/*
* Subtraction is addition with one sign inverted.
*/
- return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr);
+ if (m != 0x7FC00000) // Only negate if m isn't NaN.
+ m = vfp_single_packed_negate(m);
+
+ return vfp_single_fadd(state, sd, sn, m, fpscr);
}
/*
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 25c78d33c..64de0cbba 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -16,10 +16,10 @@
namespace Core {
-u64 g_last_ticks = 0; ///< Last CPU ticks
-ARM_Disasm* g_disasm = nullptr; ///< ARM disassembler
-ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
-ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) 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) {
@@ -47,9 +47,9 @@ void Stop() {
/// Initialize the core
int Init() {
- NOTICE_LOG(MASTER_LOG, "initialized OK");
+ LOG_DEBUG(Core, "initialized OK");
- g_disasm = new ARM_Disasm();
+ disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
switch (Settings::values.cpu_core) {
@@ -62,17 +62,17 @@ int Init() {
break;
}
- g_last_ticks = Core::g_app_core->GetTicks();
+ last_ticks = Core::g_app_core->GetTicks();
return 0;
}
void Shutdown() {
- delete g_disasm;
+ delete disasm;
delete g_app_core;
delete g_sys_core;
- NOTICE_LOG(MASTER_LOG, "shutdown OK");
+ LOG_DEBUG(Core, "shutdown OK");
}
} // namespace
diff --git a/src/core/core.h b/src/core/core.h
index 872dc0cd1..850bb0ab4 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -26,13 +26,13 @@ void Start();
/**
* Run the core CPU loop
- * This function loops for 100 instructions in the CPU before trying to update hardware. This is a
- * little bit faster than SingleStep, and should be pretty much equivalent. The number of
- * instructions chosen is fairly arbitrary, however a large number will more drastically affect the
- * frequency of GSP interrupts and likely break things. The point of this is to just loop in the CPU
- * for more than 1 instruction to reduce overhead and make it a little bit faster...
+ * This function runs the core for the specified number of CPU instructions before trying to update
+ * hardware. This is much faster than SingleStep (and should be equivalent), as the CPU is not
+ * required to do a full dispatch with each instruction. NOTE: the number of instructions requested
+ * is not guaranteed to run, as this will be interrupted preemptively if a hardware update is
+ * requested (e.g. on a thread switch).
*/
-void RunLoop(int tight_loop=100);
+void RunLoop(int tight_loop=1000);
/// Step the CPU one instruction
void SingleStep();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0116cb376..1a0b2724a 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -41,7 +41,7 @@ struct BaseEvent
s64 time;
u64 userdata;
int type;
- // Event *next;
+ // Event *next;
};
typedef LinkedListItem<BaseEvent> Event;
@@ -67,7 +67,7 @@ s64 idledCycles;
static std::recursive_mutex externalEventSection;
// Warning: not included in save state.
-void(*advanceCallback)(int cyclesExecuted) = NULL;
+void(*advanceCallback)(int cyclesExecuted) = nullptr;
void SetClockFrequencyMHz(int cpuMhz)
{
@@ -124,7 +124,7 @@ int RegisterEvent(const char *name, TimedCallback callback)
void AntiCrashCallback(u64 userdata, int cyclesLate)
{
- ERROR_LOG(TIME, "Savestate broken: an unregistered event was called.");
+ LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
Core::Halt("invalid timing events");
}
@@ -176,7 +176,7 @@ void Shutdown()
u64 GetTicks()
{
- ERROR_LOG(TIME, "Unimplemented function!");
+ LOG_ERROR(Core, "Unimplemented function!");
return 0;
//return (u64)globalTimer + slicelength - currentMIPS->downcount;
}
@@ -231,7 +231,7 @@ void ClearPendingEvents()
void AddEventToQueue(Event* ne)
{
- Event* prev = NULL;
+ Event* prev = nullptr;
Event** pNext = &first;
for (;;)
{
@@ -249,7 +249,7 @@ void AddEventToQueue(Event* ne)
// This must be run ONLY from within the cpu thread
// cyclesIntoFuture may be VERY inaccurate if called from anything else
-// than Advance
+// than Advance
void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
{
Event *ne = GetNewEvent();
@@ -327,7 +327,7 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
}
if (!tsFirst)
{
- tsLast = NULL;
+ tsLast = nullptr;
return result;
}
@@ -433,7 +433,7 @@ void RemoveThreadsafeEvent(int event_type)
}
if (!tsFirst)
{
- tsLast = NULL;
+ tsLast = nullptr;
return;
}
Event *prev = tsFirst;
@@ -469,8 +469,8 @@ void ProcessFifoWaitEvents()
{
if (first->time <= globalTimer)
{
- // LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
- // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
+ //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
+ // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
Event* evt = first;
first = first->next;
event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
@@ -495,7 +495,7 @@ void MoveEvents()
AddEventToQueue(tsFirst);
tsFirst = next;
}
- tsLast = NULL;
+ tsLast = nullptr;
// Move free events to threadsafe pool
while (allocatedTsEvents > 0 && eventPool)
@@ -510,29 +510,29 @@ void MoveEvents()
void Advance()
{
- ERROR_LOG(TIME, "Unimplemented function!");
+ LOG_ERROR(Core, "Unimplemented function!");
//int cyclesExecuted = slicelength - currentMIPS->downcount;
//globalTimer += cyclesExecuted;
//currentMIPS->downcount = slicelength;
//if (Common::AtomicLoadAcquire(hasTsEvents))
- // MoveEvents();
+ // MoveEvents();
//ProcessFifoWaitEvents();
//if (!first)
//{
- // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
- // currentMIPS->downcount += 10000;
+ // // 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;
+ // slicelength = (int)(first->time - globalTimer);
+ // if (slicelength > MAX_SLICE_LENGTH)
+ // slicelength = MAX_SLICE_LENGTH;
+ // currentMIPS->downcount = slicelength;
//}
//if (advanceCallback)
- // advanceCallback(cyclesExecuted);
+ // advanceCallback(cyclesExecuted);
}
void LogPendingEvents()
@@ -547,23 +547,23 @@ void LogPendingEvents()
void Idle(int maxIdle)
{
- ERROR_LOG(TIME, "Unimplemented function!");
+ LOG_ERROR(Core, "Unimplemented function!");
//int cyclesDown = currentMIPS->downcount;
//if (maxIdle != 0 && cyclesDown > maxIdle)
- // 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;
- // }
+ // 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));
@@ -571,7 +571,7 @@ void Idle(int maxIdle)
//idledCycles += cyclesDown;
//currentMIPS->downcount -= cyclesDown;
//if (currentMIPS->downcount == 0)
- // currentMIPS->downcount = -1;
+ // currentMIPS->downcount = -1;
}
std::string GetScheduledEventsSummary()
@@ -614,7 +614,7 @@ void DoState(PointerWrap &p)
// These (should) be filled in later by the modules.
event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
- p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL);
+ p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
p.Do(g_clock_rate_arm11);
@@ -623,4 +623,4 @@ void DoState(PointerWrap &p)
p.Do(idledCycles);
}
-} // namespace
+} // namespace
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 09fdf7a90..b197cf40c 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -106,4 +106,4 @@ void SetClockFrequencyMHz(int cpuMhz);
int GetClockFrequencyMHz();
extern int slicelength;
-}; // namespace
+} // namespace
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h
deleted file mode 100644
index aeabf09ac..000000000
--- a/src/core/file_sys/archive.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-
-#include "common/common_types.h"
-#include "common/bit_field.h"
-
-#include "core/file_sys/file.h"
-#include "core/file_sys/directory.h"
-
-#include "core/hle/kernel/kernel.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-union Mode {
- u32 hex;
- BitField<0, 1, u32> read_flag;
- BitField<1, 1, u32> write_flag;
- BitField<2, 1, u32> create_flag;
-};
-
-class Archive : NonCopyable {
-public:
- /// Supported archive types
- enum class IdCode : u32 {
- RomFS = 0x00000003,
- SaveData = 0x00000004,
- ExtSaveData = 0x00000006,
- SharedExtSaveData = 0x00000007,
- SystemSaveData = 0x00000008,
- SDMC = 0x00000009,
- SDMCWriteOnly = 0x0000000A,
- };
-
- Archive() { }
- virtual ~Archive() { }
-
- /**
- * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
- * @return IdCode of the archive
- */
- virtual IdCode GetIdCode() const = 0;
-
- /**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or nullptr
- */
- virtual std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const = 0;
-
- /**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Whether the directory could be created
- */
- virtual bool CreateDirectory(const std::string& path) const = 0;
-
- /**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
- virtual std::unique_ptr<Directory> OpenDirectory(const std::string& path) const = 0;
-
- /**
- * Read data from the archive
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
- virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0;
-
- /**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
- */
- virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0;
-
- /**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
- virtual size_t GetSize() const = 0;
-
- /**
- * Set the size of the archive in bytes
- */
- virtual void SetSize(const u64 size) = 0;
-};
-
-} // namespace FileSys
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
new file mode 100644
index 000000000..18c314884
--- /dev/null
+++ b/src/core/file_sys/archive_backend.h
@@ -0,0 +1,225 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+#include "common/string_util.h"
+#include "common/bit_field.h"
+
+#include "core/file_sys/file_backend.h"
+#include "core/file_sys/directory_backend.h"
+
+#include "core/mem_map.h"
+#include "core/hle/kernel/kernel.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+// Path string type
+enum LowPathType : u32 {
+ Invalid = 0,
+ Empty = 1,
+ Binary = 2,
+ Char = 3,
+ Wchar = 4
+};
+
+union Mode {
+ u32 hex;
+ BitField<0, 1, u32> read_flag;
+ BitField<1, 1, u32> write_flag;
+ BitField<2, 1, u32> create_flag;
+};
+
+class Path {
+public:
+
+ Path():
+ type(Invalid)
+ {
+ }
+
+ Path(LowPathType type, u32 size, u32 pointer):
+ type(type)
+ {
+ switch (type) {
+ case Binary:
+ {
+ u8* data = Memory::GetPointer(pointer);
+ binary = std::vector<u8>(data, data + size);
+ break;
+ }
+ case Char:
+ {
+ const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
+ string = std::string(data, size - 1); // Data is always null-terminated.
+ break;
+ }
+ case Wchar:
+ {
+ const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
+ u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ LowPathType GetType() const {
+ return type;
+ }
+
+ /**
+ * Gets the string representation of the path for debugging
+ * @return String representation of the path for debugging
+ */
+ const std::string DebugStr() const {
+ switch (GetType()) {
+ case Invalid:
+ return "[Invalid]";
+ case Empty:
+ return "[Empty]";
+ case Binary:
+ {
+ std::stringstream res;
+ res << "[Binary: ";
+ for (unsigned byte : binary)
+ res << std::hex << std::setw(2) << std::setfill('0') << byte;
+ res << ']';
+ return res.str();
+ }
+ case Char:
+ return "[Char: " + AsString() + ']';
+ case Wchar:
+ return "[Wchar: " + AsString() + ']';
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
+ return {};
+ }
+ }
+
+ const std::string AsString() const {
+ switch (GetType()) {
+ case Char:
+ return string;
+ case Wchar:
+ return Common::UTF16ToUTF8(u16str);
+ case Empty:
+ return {};
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
+ return {};
+ }
+ }
+
+ const std::u16string AsU16Str() const {
+ switch (GetType()) {
+ case Char:
+ return Common::UTF8ToUTF16(string);
+ case Wchar:
+ return u16str;
+ case Empty:
+ return {};
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
+ return {};
+ }
+ }
+
+ const std::vector<u8> AsBinary() const {
+ switch (GetType()) {
+ case Binary:
+ return binary;
+ case Char:
+ return std::vector<u8>(string.begin(), string.end());
+ case Wchar:
+ return std::vector<u8>(u16str.begin(), u16str.end());
+ case Empty:
+ return {};
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
+ return {};
+ }
+ }
+
+private:
+ LowPathType type;
+ std::vector<u8> binary;
+ std::string string;
+ std::u16string u16str;
+};
+
+class ArchiveBackend : NonCopyable {
+public:
+ virtual ~ArchiveBackend() { }
+
+ /**
+ * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
+ */
+ virtual std::string GetName() const = 0;
+
+ /**
+ * Open a file specified by its path, using the specified mode
+ * @param path Path relative to the archive
+ * @param mode Mode to open the file with
+ * @return Opened file, or nullptr
+ */
+ virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0;
+
+ /**
+ * Delete a file specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the file could be deleted
+ */
+ virtual bool DeleteFile(const FileSys::Path& path) const = 0;
+
+ /**
+ * Rename a File specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
+ */
+ virtual bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0;
+
+ /**
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be deleted
+ */
+ virtual bool DeleteDirectory(const FileSys::Path& path) const = 0;
+
+ /**
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be created
+ */
+ virtual bool CreateDirectory(const Path& path) const = 0;
+
+ /**
+ * Rename a Directory specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
+ */
+ virtual bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0;
+
+ /**
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or nullptr
+ */
+ virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
index cc759faa8..0709b62a1 100644
--- a/src/core/file_sys/archive_romfs.cpp
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include <memory>
+
#include "common/common_types.h"
#include "core/file_sys/archive_romfs.h"
@@ -16,81 +18,67 @@ namespace FileSys {
Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) {
- WARN_LOG(FILESYS, "Unable to read RomFS!");
+ LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
-Archive_RomFS::~Archive_RomFS() {
-}
-
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
-std::unique_ptr<File> Archive_RomFS::OpenFile(const std::string& path, const Mode mode) const {
- return std::unique_ptr<File>(new File_RomFS);
+std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const {
+ return std::make_unique<File_RomFS>(this);
}
/**
- * Create a directory specified by its path
+ * Delete a file specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be created
+ * @return Whether the file could be deleted
*/
-bool Archive_RomFS::CreateDirectory(const std::string& path) const {
- ERROR_LOG(FILESYS, "Attempted to create a directory in ROMFS.");
+bool Archive_RomFS::DeleteFile(const FileSys::Path& path) const {
+ LOG_WARNING(Service_FS, "Attempted to delete a file from ROMFS.");
return false;
-};
+}
-/**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
-std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const std::string& path) const {
- return std::unique_ptr<Directory>(new Directory_RomFS);
+bool Archive_RomFS::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
+ return false;
}
/**
- * Read data from the archive
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be deleted
*/
-size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
- DEBUG_LOG(FILESYS, "called offset=%llu, length=%d", offset, length);
- memcpy(buffer, &raw_data[(u32)offset], length);
- return length;
+bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const {
+ LOG_WARNING(Service_FS, "Attempted to delete a directory from ROMFS.");
+ return false;
}
/**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be created
*/
-size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
- ERROR_LOG(FILESYS, "Attempted to write to ROMFS.");
- return 0;
+bool Archive_RomFS::CreateDirectory(const Path& path) const {
+ LOG_WARNING(Service_FS, "Attempted to create a directory in ROMFS.");
+ return false;
}
-/**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
-size_t Archive_RomFS::GetSize() const {
- return sizeof(u8) * raw_data.size();
+bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
+ return false;
}
/**
- * Set the size of the archive in bytes
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or nullptr
*/
-void Archive_RomFS::SetSize(const u64 size) {
- ERROR_LOG(FILESYS, "Attempted to set the size of ROMFS");
+std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const {
+ return std::make_unique<Directory_RomFS>();
}
} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
index ae2344e82..5b1ee6332 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_romfs.h
@@ -8,7 +8,7 @@
#include "common/common_types.h"
-#include "core/file_sys/archive.h"
+#include "core/file_sys/archive_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,16 +17,11 @@
namespace FileSys {
/// File system interface to the RomFS archive
-class Archive_RomFS final : public Archive {
+class Archive_RomFS final : public ArchiveBackend {
public:
Archive_RomFS(const Loader::AppLoader& app_loader);
- ~Archive_RomFS() override;
- /**
- * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
- * @return IdCode of the archive
- */
- IdCode GetIdCode() const override { return IdCode::RomFS; };
+ std::string GetName() const override { return "RomFS"; }
/**
* Open a file specified by its path, using the specified mode
@@ -34,53 +29,55 @@ public:
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
- std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override;
+ std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
/**
- * Create a directory specified by its path
+ * Delete a file specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be created
+ * @return Whether the file could be deleted
*/
- bool CreateDirectory(const std::string& path) const override;
+ bool DeleteFile(const FileSys::Path& path) const override;
/**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
+ * Rename a File specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
*/
- std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override;
+ bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
/**
- * Read data from the archive
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be deleted
*/
- size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
+ bool DeleteDirectory(const FileSys::Path& path) const override;
/**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be created
*/
- size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
+ bool CreateDirectory(const Path& path) const override;
/**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
+ * Rename a Directory specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
*/
- size_t GetSize() const override;
-
+ bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
+
/**
- * Set the size of the archive in bytes
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or nullptr
*/
- void SetSize(const u64 size) override;
+ std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
private:
+ friend class File_RomFS;
+
std::vector<u8> raw_data;
};
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
new file mode 100644
index 000000000..2414564e4
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -0,0 +1,33 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <sys/stat.h>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/archive_savedata.h"
+#include "core/file_sys/disk_archive.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id)
+ : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) {
+ LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
+}
+
+bool Archive_SaveData::Initialize() {
+ if (!FileUtil::CreateFullPath(mount_point)) {
+ LOG_ERROR(Service_FS, "Unable to create SaveData path.");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h
new file mode 100644
index 000000000..b3e561130
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/disk_archive.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// File system interface to the SaveData archive
+class Archive_SaveData final : public DiskArchive {
+public:
+ Archive_SaveData(const std::string& mount_point, u64 program_id);
+
+ /**
+ * Initialize the archive.
+ * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists,
+ * Success if it was created properly and Failure if there was any error
+ */
+ bool Initialize();
+
+ std::string GetName() const override { return "SaveData"; }
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 66931e93e..dccdf7f67 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,8 +8,7 @@
#include "common/file_util.h"
#include "core/file_sys/archive_sdmc.h"
-#include "core/file_sys/directory_sdmc.h"
-#include "core/file_sys/file_sdmc.h"
+#include "core/file_sys/disk_archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,113 +16,22 @@
namespace FileSys {
-Archive_SDMC::Archive_SDMC(const std::string& mount_point) {
- this->mount_point = mount_point;
- DEBUG_LOG(FILESYS, "Directory %s set as SDMC.", mount_point.c_str());
+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() {
-}
-
-/**
- * Initialize the archive.
- * @return true if it initialized successfully
- */
bool Archive_SDMC::Initialize() {
if (!Settings::values.use_virtual_sd) {
- WARN_LOG(FILESYS, "SDMC disabled by config.");
+ LOG_WARNING(Service_FS, "SDMC disabled by config.");
return false;
}
if (!FileUtil::CreateFullPath(mount_point)) {
- WARN_LOG(FILESYS, "Unable to create SDMC path.");
+ LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}
return true;
}
-/**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or nullptr
- */
-std::unique_ptr<File> Archive_SDMC::OpenFile(const std::string& path, const Mode mode) const {
- DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.c_str(), mode);
- File_SDMC* file = new File_SDMC(this, path, mode);
- if (!file->Open())
- return nullptr;
- return std::unique_ptr<File>(file);
-}
-
-/**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Whether the directory could be created
- */
-bool Archive_SDMC::CreateDirectory(const std::string& path) const {
- return FileUtil::CreateDir(GetMountPoint() + path);
-}
-
-/**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
-std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const std::string& path) const {
- DEBUG_LOG(FILESYS, "called path=%s", path.c_str());
- Directory_SDMC* directory = new Directory_SDMC(this, path);
- return std::unique_ptr<Directory>(directory);
-}
-
-/**
- * Read data from the archive
- * @param offset Offset in bytes to start reading archive from
- * @param length Length in bytes to read data from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
-size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
- return -1;
-}
-
-/**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
- */
-size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
- return -1;
-}
-
-/**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
-size_t Archive_SDMC::GetSize() const {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
- return 0;
-}
-
-/**
- * Set the size of the archive in bytes
- */
-void Archive_SDMC::SetSize(const u64 size) {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
-}
-
-/**
- * Getter for the path used for this Archive
- * @return Mount point of that passthrough archive
- */
-std::string Archive_SDMC::GetMountPoint() const {
- return mount_point;
-}
-
} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 0e059b635..c84c6948e 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/file_sys/archive.h"
+#include "core/file_sys/disk_archive.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,10 +15,9 @@
namespace FileSys {
/// File system interface to the SDMC archive
-class Archive_SDMC final : public Archive {
+class Archive_SDMC final : public DiskArchive {
public:
Archive_SDMC(const std::string& mount_point);
- ~Archive_SDMC() override;
/**
* Initialize the archive.
@@ -26,72 +25,7 @@ public:
*/
bool Initialize();
- /**
- * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
- * @return IdCode of the archive
- */
- IdCode GetIdCode() const override { return IdCode::SDMC; };
-
- /**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or nullptr
- */
- std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override;
-
- /**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Whether the directory could be created
- */
- bool CreateDirectory(const std::string& path) const override;
-
- /**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
- std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override;
-
- /**
- * Read data from the archive
- * @param offset Offset in bytes to start reading archive from
- * @param length Length in bytes to read data from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
- size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
-
- /**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
- */
- size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
-
- /**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
- size_t GetSize() const override;
-
- /**
- * Set the size of the archive in bytes
- */
- void SetSize(const u64 size) override;
-
- /**
- * Getter for the path used for this Archive
- * @return Mount point of that passthrough archive
- */
- std::string GetMountPoint() const;
-
-private:
- std::string mount_point;
+ std::string GetName() const override { return "SDMC"; }
};
} // namespace FileSys
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h
index e10431337..188746a6f 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory_backend.h
@@ -36,10 +36,16 @@ static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension i
static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
-class Directory : NonCopyable {
+class DirectoryBackend : NonCopyable {
public:
- Directory() { }
- virtual ~Directory() { }
+ DirectoryBackend() { }
+ virtual ~DirectoryBackend() { }
+
+ /**
+ * Open the directory
+ * @return true if the directory opened correctly
+ */
+ virtual bool Open() = 0;
/**
* List files contained in the directory
diff --git a/src/core/file_sys/directory_romfs.cpp b/src/core/file_sys/directory_romfs.cpp
index 4e8f4c04d..e6d571391 100644
--- a/src/core/file_sys/directory_romfs.cpp
+++ b/src/core/file_sys/directory_romfs.cpp
@@ -17,6 +17,10 @@ Directory_RomFS::Directory_RomFS() {
Directory_RomFS::~Directory_RomFS() {
}
+bool Directory_RomFS::Open() {
+ return false;
+}
+
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h
index 4b71c4b13..b775f014d 100644
--- a/src/core/file_sys/directory_romfs.h
+++ b/src/core/file_sys/directory_romfs.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/file_sys/directory.h"
+#include "core/file_sys/directory_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,12 +14,18 @@
namespace FileSys {
-class Directory_RomFS final : public Directory {
+class Directory_RomFS final : public DirectoryBackend {
public:
Directory_RomFS();
~Directory_RomFS() override;
/**
+ * Open the directory
+ * @return true if the directory opened correctly
+ */
+ bool Open() override;
+
+ /**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp
deleted file mode 100644
index fd558def9..000000000
--- a/src/core/file_sys/directory_sdmc.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <sys/stat.h>
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/directory_sdmc.h"
-#include "core/file_sys/archive_sdmc.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const std::string& path) {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../usr/bin can give the emulated program your installed programs.
- std::string absolute_path = archive->GetMountPoint() + path;
- FileUtil::ScanDirectoryTree(absolute_path, directory);
- children_iterator = directory.children.begin();
-}
-
-Directory_SDMC::~Directory_SDMC() {
- Close();
-}
-
-/**
- * List files contained in the directory
- * @param count Number of entries to return at once in entries
- * @param entries Buffer to read data into
- * @return Number of entries listed
- */
-u32 Directory_SDMC::Read(const u32 count, Entry* entries) {
- u32 entries_read = 0;
-
- while (entries_read < count && children_iterator != directory.children.cend()) {
- const FileUtil::FSTEntry& file = *children_iterator;
- const std::string& filename = file.virtualName;
- Entry& entry = entries[entries_read];
-
- WARN_LOG(FILESYS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
-
- // TODO(Link Mauve): use a proper conversion to UTF-16.
- for (int j = 0; j < FILENAME_LENGTH; ++j) {
- entry.filename[j] = filename[j];
- if (!filename[j])
- break;
- }
-
- FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
-
- entry.is_directory = file.isDirectory;
- entry.is_hidden = (filename[0] == '.');
- entry.is_read_only = 0;
- entry.file_size = file.size;
-
- // We emulate a SD card where the archive bit has never been cleared, as it would be on
- // most user SD cards.
- // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
- // file bit.
- entry.is_archive = !file.isDirectory;
-
- ++entries_read;
- ++children_iterator;
- }
- return entries_read;
-}
-
-/**
- * Close the directory
- * @return true if the directory closed correctly
- */
-bool Directory_SDMC::Close() const {
- return true;
-}
-
-} // namespace FileSys
diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h
deleted file mode 100644
index cb8d32fda..000000000
--- a/src/core/file_sys/directory_sdmc.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/directory.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/loader/loader.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-class Directory_SDMC final : public Directory {
-public:
- Directory_SDMC();
- Directory_SDMC(const Archive_SDMC* archive, const std::string& path);
- ~Directory_SDMC() override;
-
- /**
- * List files contained in the directory
- * @param count Number of entries to return at once in entries
- * @param entries Buffer to read data into
- * @return Number of entries listed
- */
- u32 Read(const u32 count, Entry* entries) override;
-
- /**
- * Close the directory
- * @return true if the directory closed correctly
- */
- bool Close() const override;
-
-private:
- u32 total_entries_in_directory;
- FileUtil::FSTEntry directory;
-
- // We need to remember the last entry we returned, so a subsequent call to Read will continue
- // from the next one. This iterator will always point to the next unread entry.
- std::vector<FileUtil::FSTEntry>::iterator children_iterator;
-};
-
-} // namespace FileSys
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
new file mode 100644
index 000000000..eabf58057
--- /dev/null
+++ b/src/core/file_sys/disk_archive.cpp
@@ -0,0 +1,167 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <sys/stat.h>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/disk_archive.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
+ LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
+ DiskFile* file = new DiskFile(this, path, mode);
+ if (!file->Open())
+ return nullptr;
+ return std::unique_ptr<FileBackend>(file);
+}
+
+bool DiskArchive::DeleteFile(const FileSys::Path& path) const {
+ return FileUtil::Delete(GetMountPoint() + path.AsString());
+}
+
+bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
+}
+
+bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const {
+ return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
+}
+
+bool DiskArchive::CreateDirectory(const Path& path) const {
+ return FileUtil::CreateDir(GetMountPoint() + path.AsString());
+}
+
+bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
+}
+
+std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
+ LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
+ DiskDirectory* directory = new DiskDirectory(this, path);
+ if (!directory->Open())
+ return nullptr;
+ return std::unique_ptr<DirectoryBackend>(directory);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) {
+ // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
+ // the root directory we set while opening the archive.
+ // For example, opening /../../etc/passwd can give the emulated program your users list.
+ this->path = archive->GetMountPoint() + path.AsString();
+ this->mode.hex = mode.hex;
+ this->archive = archive;
+}
+
+bool DiskFile::Open() {
+ if (!mode.create_flag && !FileUtil::Exists(path)) {
+ LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str());
+ return false;
+ }
+
+ std::string mode_string;
+ if (mode.create_flag)
+ mode_string = "w+";
+ else if (mode.write_flag)
+ mode_string = "r+"; // Files opened with Write access can be read from
+ else if (mode.read_flag)
+ mode_string = "r";
+
+ // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
+ mode_string += "b";
+
+ file = new FileUtil::IOFile(path, mode_string.c_str());
+ return true;
+}
+
+size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const {
+ file->Seek(offset, SEEK_SET);
+ return file->ReadBytes(buffer, length);
+}
+
+size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
+ file->Seek(offset, SEEK_SET);
+ size_t written = file->WriteBytes(buffer, length);
+ if (flush)
+ file->Flush();
+ return written;
+}
+
+size_t DiskFile::GetSize() const {
+ return static_cast<size_t>(file->GetSize());
+}
+
+bool DiskFile::SetSize(const u64 size) const {
+ file->Resize(size);
+ file->Flush();
+ return true;
+}
+
+bool DiskFile::Close() const {
+ return file->Close();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) {
+ // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
+ // the root directory we set while opening the archive.
+ // For example, opening /../../usr/bin can give the emulated program your installed programs.
+ this->path = archive->GetMountPoint() + path.AsString();
+ this->archive = archive;
+}
+
+bool DiskDirectory::Open() {
+ if (!FileUtil::IsDirectory(path))
+ return false;
+ FileUtil::ScanDirectoryTree(path, directory);
+ children_iterator = directory.children.begin();
+ return true;
+}
+
+u32 DiskDirectory::Read(const u32 count, Entry* entries) {
+ u32 entries_read = 0;
+
+ while (entries_read < count && children_iterator != directory.children.cend()) {
+ const FileUtil::FSTEntry& file = *children_iterator;
+ const std::string& filename = file.virtualName;
+ Entry& entry = entries[entries_read];
+
+ LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
+
+ // TODO(Link Mauve): use a proper conversion to UTF-16.
+ for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
+ entry.filename[j] = filename[j];
+ if (!filename[j])
+ break;
+ }
+
+ FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
+
+ entry.is_directory = file.isDirectory;
+ entry.is_hidden = (filename[0] == '.');
+ entry.is_read_only = 0;
+ entry.file_size = file.size;
+
+ // We emulate a SD card where the archive bit has never been cleared, as it would be on
+ // most user SD cards.
+ // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
+ // file bit.
+ entry.is_archive = !file.isDirectory;
+
+ ++entries_read;
+ ++children_iterator;
+ }
+ return entries_read;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
new file mode 100644
index 000000000..778c83953
--- /dev/null
+++ b/src/core/file_sys/disk_archive.h
@@ -0,0 +1,101 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive_backend.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/**
+ * Helper which implements a backend accessing the host machine's filesystem.
+ * This should be subclassed by concrete archive types, which will provide the
+ * base directory on the host filesystem and override any required functionality.
+ */
+class DiskArchive : public ArchiveBackend {
+public:
+ DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
+
+ virtual std::string GetName() const = 0;
+ std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
+ bool DeleteFile(const FileSys::Path& path) const override;
+ bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
+ bool DeleteDirectory(const FileSys::Path& path) const override;
+ bool CreateDirectory(const Path& path) const override;
+ bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
+ std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+
+ /**
+ * Getter for the path used for this Archive
+ * @return Mount point of that passthrough archive
+ */
+ const std::string& GetMountPoint() const {
+ return mount_point;
+ }
+
+protected:
+ std::string mount_point;
+};
+
+class DiskFile : public FileBackend {
+public:
+ DiskFile();
+ DiskFile(const DiskArchive* archive, const Path& path, const Mode mode);
+
+ ~DiskFile() override {
+ Close();
+ }
+
+ bool Open() override;
+ size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
+ size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
+ size_t GetSize() const override;
+ bool SetSize(const u64 size) const override;
+ bool Close() const override;
+
+ void Flush() const override {
+ file->Flush();
+ }
+
+protected:
+ const DiskArchive* archive;
+ std::string path;
+ Mode mode;
+ FileUtil::IOFile* file;
+};
+
+class DiskDirectory : public DirectoryBackend {
+public:
+ DiskDirectory();
+ DiskDirectory(const DiskArchive* archive, const Path& path);
+
+ ~DiskDirectory() override {
+ Close();
+ }
+
+ bool Open() override;
+ u32 Read(const u32 count, Entry* entries) override;
+
+ bool Close() const override {
+ return true;
+ }
+
+protected:
+ const DiskArchive* archive;
+ std::string path;
+ u32 total_entries_in_directory;
+ FileUtil::FSTEntry directory;
+
+ // We need to remember the last entry we returned, so a subsequent call to Read will continue
+ // from the next one. This iterator will always point to the next unread entry.
+ std::vector<FileUtil::FSTEntry>::iterator children_iterator;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h
index 4013b6c3e..539ec7314 100644
--- a/src/core/file_sys/file.h
+++ b/src/core/file_sys/file_backend.h
@@ -13,10 +13,10 @@
namespace FileSys {
-class File : NonCopyable {
+class FileBackend : NonCopyable {
public:
- File() { }
- virtual ~File() { }
+ FileBackend() { }
+ virtual ~FileBackend() { }
/**
* Open the file
@@ -61,6 +61,11 @@ public:
* @return true if the file closed correctly
*/
virtual bool Close() const = 0;
+
+ /**
+ * Flushes the file
+ */
+ virtual void Flush() const = 0;
};
} // namespace FileSys
diff --git a/src/core/file_sys/file_romfs.cpp b/src/core/file_sys/file_romfs.cpp
index b55708df4..5f38c2704 100644
--- a/src/core/file_sys/file_romfs.cpp
+++ b/src/core/file_sys/file_romfs.cpp
@@ -5,24 +5,19 @@
#include "common/common_types.h"
#include "core/file_sys/file_romfs.h"
+#include "core/file_sys/archive_romfs.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
-File_RomFS::File_RomFS() {
-}
-
-File_RomFS::~File_RomFS() {
-}
-
/**
* Open the file
* @return true if the file opened correctly
*/
bool File_RomFS::Open() {
- return false;
+ return true;
}
/**
@@ -33,7 +28,9 @@ bool File_RomFS::Open() {
* @return Number of bytes read
*/
size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
- return -1;
+ LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length);
+ memcpy(buffer, &archive->raw_data[(u32)offset], length);
+ return length;
}
/**
@@ -45,7 +42,8 @@ size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
* @return Number of bytes written
*/
size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
- return -1;
+ LOG_WARNING(Service_FS, "Attempted to write to ROMFS.");
+ return 0;
}
/**
@@ -53,7 +51,7 @@ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, co
* @return Size of the file in bytes
*/
size_t File_RomFS::GetSize() const {
- return -1;
+ return sizeof(u8) * archive->raw_data.size();
}
/**
@@ -62,6 +60,7 @@ size_t File_RomFS::GetSize() const {
* @return true if successful
*/
bool File_RomFS::SetSize(const u64 size) const {
+ LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS");
return false;
}
diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h
index 5196701d3..32fa6b6d3 100644
--- a/src/core/file_sys/file_romfs.h
+++ b/src/core/file_sys/file_romfs.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/file_sys/file.h"
+#include "core/file_sys/file_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,10 +14,11 @@
namespace FileSys {
-class File_RomFS final : public File {
+class Archive_RomFS;
+
+class File_RomFS final : public FileBackend {
public:
- File_RomFS();
- ~File_RomFS() override;
+ File_RomFS(const Archive_RomFS* archive) : archive(archive) {}
/**
* Open the file
@@ -62,6 +63,11 @@ public:
* @return true if the file closed correctly
*/
bool Close() const override;
+
+ void Flush() const override { }
+
+private:
+ const Archive_RomFS* archive;
};
} // namespace FileSys
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp
deleted file mode 100644
index 26204392c..000000000
--- a/src/core/file_sys/file_sdmc.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <sys/stat.h>
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/file_sdmc.h"
-#include "core/file_sys/archive_sdmc.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-File_SDMC::File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode) {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../etc/passwd can give the emulated program your users list.
- this->path = archive->GetMountPoint() + path;
- this->mode.hex = mode.hex;
-}
-
-File_SDMC::~File_SDMC() {
- Close();
-}
-
-/**
- * Open the file
- * @return true if the file opened correctly
- */
-bool File_SDMC::Open() {
- if (!mode.create_flag && !FileUtil::Exists(path)) {
- ERROR_LOG(FILESYS, "Non-existing file %s can’t be open without mode create.", path.c_str());
- return false;
- }
-
- std::string mode_string;
- if (mode.read_flag && mode.write_flag)
- mode_string = "w+";
- else if (mode.read_flag)
- mode_string = "r";
- else if (mode.write_flag)
- mode_string = "w";
-
- file = new FileUtil::IOFile(path, mode_string.c_str());
- return true;
-}
-
-/**
- * Read data from the file
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from file
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
-size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
- file->Seek(offset, SEEK_SET);
- return file->ReadBytes(buffer, length);
-}
-
-/**
- * Write data to the file
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to file
- * @param flush The flush parameters (0 == do not flush)
- * @param buffer Buffer to read data from
- * @return Number of bytes written
- */
-size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
- file->Seek(offset, SEEK_SET);
- size_t written = file->WriteBytes(buffer, length);
- if (flush)
- file->Flush();
- return written;
-}
-
-/**
- * Get the size of the file in bytes
- * @return Size of the file in bytes
- */
-size_t File_SDMC::GetSize() const {
- return static_cast<size_t>(file->GetSize());
-}
-
-/**
- * Set the size of the file in bytes
- * @param size New size of the file
- * @return true if successful
- */
-bool File_SDMC::SetSize(const u64 size) const {
- file->Resize(size);
- file->Flush();
- return true;
-}
-
-/**
- * Close the file
- * @return true if the file closed correctly
- */
-bool File_SDMC::Close() const {
- return file->Close();
-}
-
-} // namespace FileSys
diff --git a/src/core/file_sys/file_sdmc.h b/src/core/file_sys/file_sdmc.h
deleted file mode 100644
index df032f7c0..000000000
--- a/src/core/file_sys/file_sdmc.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/file.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/loader/loader.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-class File_SDMC final : public File {
-public:
- File_SDMC();
- File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode);
- ~File_SDMC() override;
-
- /**
- * Open the file
- * @return true if the file opened correctly
- */
- bool Open() override;
-
- /**
- * Read data from the file
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from file
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
- size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
-
- /**
- * Write data to the file
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to file
- * @param flush The flush parameters (0 == do not flush)
- * @param buffer Buffer to read data from
- * @return Number of bytes written
- */
- size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
-
- /**
- * Get the size of the file in bytes
- * @return Size of the file in bytes
- */
- size_t GetSize() const override;
-
- /**
- * Set the size of the file in bytes
- * @param size New size of the file
- * @return true if successful
- */
- bool SetSize(const u64 size) const override;
-
- /**
- * Close the file
- * @return true if the file closed correctly
- */
- bool Close() const override;
-
-private:
- std::string path;
- Mode mode;
- FileUtil::IOFile* file;
-};
-
-} // namespace FileSys
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index a45e61427..d8ba9e6cf 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -1,8 +1,9 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common_types.h"
+#include "common/log.h"
#include "core/hle/config_mem.h"
@@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
break;
default:
- ERROR_LOG(HLE, "unknown addr=0x%08X", addr);
+ LOG_ERROR(Kernel, "unknown addr=0x%08X", addr);
}
}
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp
index 1eb33eb86..e34229a57 100644
--- a/src/core/hle/coprocessor.cpp
+++ b/src/core/hle/coprocessor.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/hle/coprocessor.h"
#include "core/hle/hle.h"
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 55eaf0621..b44479b2f 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -50,7 +50,7 @@ template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
s32 param_1 = 0;
- s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
+ s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
(PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
Core::g_app_core->SetReg(1, (u32)param_1);
FuncReturn(retval);
@@ -103,7 +103,7 @@ template<s32 func(void*)> void Wrap() {
}
template<s32 func(s64*, u32, void*, s32)> void Wrap(){
- FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
+ FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
(s32)PARAM(3)));
}
@@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32*, s32, s32)> void Wrap() {
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
+template<s32 func(s32*, u32, s32)> void Wrap() {
+ s32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index b03894ad7..cc3d5255a 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <vector>
@@ -8,6 +8,7 @@
#include "core/hle/hle.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,7 +21,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n
const FunctionDef* GetSVCInfo(u32 opcode) {
u32 func_num = opcode & 0xFFFFFF; // 8 bits
if (func_num > 0xFF) {
- ERROR_LOG(HLE,"unknown svc=0x%02X", func_num);
+ LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
return nullptr;
}
return &g_module_db[0].func_table[func_num];
@@ -35,14 +36,12 @@ void CallSVC(u32 opcode) {
if (info->func) {
info->func();
} else {
- ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str());
+ LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
}
}
void Reschedule(const char *reason) {
-#ifdef _DEBUG
- _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
-#endif
+ _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
Core::g_app_core->PrepareReschedule();
g_reschedule = true;
}
@@ -58,18 +57,20 @@ void RegisterAllModules() {
void Init() {
Service::Init();
-
+ Service::FS::ArchiveInit();
+
RegisterAllModules();
- NOTICE_LOG(HLE, "initialized OK");
+ LOG_DEBUG(Kernel, "initialized OK");
}
void Shutdown() {
+ Service::FS::ArchiveShutdown();
Service::Shutdown();
g_module_db.clear();
- NOTICE_LOG(HLE, "shutdown OK");
+ LOG_DEBUG(Kernel, "shutdown OK");
}
} // namespace
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index bf4d84575..4ab258c69 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 2b21657da..9a921108d 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -24,23 +24,12 @@ public:
Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; }
std::string name; ///< Name of address arbiter object (optional)
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
@@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
HLE::Reschedule(__func__);
}
break;
default:
- ERROR_LOG(KERNEL, "unknown type=%d", type);
- return -1;
+ LOG_ERROR(Kernel, "unknown type=%d", type);
+ return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Create an address arbiter
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index a483fe466..8a5fb10b4 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -11,7 +11,7 @@
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
-// semphores.
+// semphores.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
};
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
/// Create an address arbiter
Handle CreateAddressArbiter(const std::string& name = "Unknown");
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
deleted file mode 100644
index 764082d71..000000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,436 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-#include "common/math_util.h"
-
-#include "core/file_sys/archive.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/file_sys/directory.h"
-#include "core/hle/service/service.h"
-#include "core/hle/kernel/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-// Command to access archive file
-enum class FileCommand : u32 {
- Dummy1 = 0x000100C6,
- Control = 0x040100C4,
- OpenSubFile = 0x08010100,
- Read = 0x080200C2,
- Write = 0x08030102,
- GetSize = 0x08040000,
- SetSize = 0x08050080,
- GetAttributes = 0x08060000,
- SetAttributes = 0x08070040,
- Close = 0x08080000,
- Flush = 0x08090000,
-};
-
-// Command to access directory
-enum class DirectoryCommand : u32 {
- Dummy1 = 0x000100C6,
- Control = 0x040100C4,
- Read = 0x08010042,
- Close = 0x08020000,
-};
-
-class Archive : public Object {
-public:
- std::string GetTypeName() const override { return "Archive"; }
- std::string GetName() const override { return name; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
-
- std::string name; ///< Name of archive (optional)
- FileSys::Archive* backend; ///< Archive backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
-
- switch (cmd) {
- // Read from archive...
- case FileCommand::Read:
- {
- u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- u32 length = cmd_buff[3];
- u32 address = cmd_buff[5];
-
- // Number of bytes read
- cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
- break;
- }
- // Write to archive...
- case FileCommand::Write:
- {
- u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- u32 length = cmd_buff[3];
- u32 flush = cmd_buff[4];
- u32 address = cmd_buff[6];
-
- // Number of bytes written
- cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
- break;
- }
- case FileCommand::GetSize:
- {
- u64 filesize = (u64) backend->GetSize();
- cmd_buff[2] = (u32) filesize; // Lower word
- cmd_buff[3] = (u32) (filesize >> 32); // Upper word
- break;
- }
- case FileCommand::SetSize:
- {
- backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
- break;
- }
- case FileCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<Archive>(GetHandle());
- CloseArchive(backend->GetIdCode());
- break;
- }
- // Unknown command...
- default:
- {
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- return -1;
- }
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class File : public Object {
-public:
- std::string GetTypeName() const override { return "File"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
- Kernel::HandleType GetHandleType() const override { return HandleType::File; }
-
- std::string path; ///< Path of the file
- std::unique_ptr<FileSys::File> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
- switch (cmd) {
-
- // Read from file...
- case FileCommand::Read:
- {
- u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
- u32 length = cmd_buff[3];
- u32 address = cmd_buff[5];
- DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
- GetTypeName().c_str(), GetName().c_str(), offset, length, address);
- cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
- break;
- }
-
- // Write to file...
- case FileCommand::Write:
- {
- u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
- u32 length = cmd_buff[3];
- u32 flush = cmd_buff[4];
- u32 address = cmd_buff[6];
- DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
- GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
- cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
- break;
- }
-
- case FileCommand::GetSize:
- {
- DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
- u64 size = backend->GetSize();
- cmd_buff[2] = (u32)size;
- cmd_buff[3] = size >> 32;
- break;
- }
-
- case FileCommand::SetSize:
- {
- u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size);
- backend->SetSize(size);
- break;
- }
-
- case FileCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<File>(GetHandle());
- break;
- }
-
- // Unknown command...
- default:
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class Directory : public Object {
-public:
- std::string GetTypeName() const override { return "Directory"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
-
- std::string path; ///< Path of the directory
- std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
- switch (cmd) {
-
- // Read from directory...
- case DirectoryCommand::Read:
- {
- u32 count = cmd_buff[1];
- u32 address = cmd_buff[3];
- FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
- DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
-
- // Number of entries actually read
- cmd_buff[2] = backend->Read(count, entries);
- break;
- }
-
- case DirectoryCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<Directory>(GetHandle());
- break;
- }
-
- // Unknown command...
- default:
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code) {
- auto itr = g_archive_map.find(id_code);
- if (itr == g_archive_map.end()) {
- return 0;
- }
- return itr->second;
-}
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code) {
- if (1 != g_archive_map.erase(id_code)) {
- ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
- return -1;
- }
-
- INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
- return 0;
-}
-
-/**
- * Mounts an archive
- * @param archive Pointer to the archive to mount
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result MountArchive(Archive* archive) {
- FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
- if (0 != OpenArchive(id_code)) {
- ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
- return -1;
- }
- g_archive_map[id_code] = archive->GetHandle();
- INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
- return 0;
-}
-
-/**
- * Creates an Archive
- * @param handle Handle to newly created archive object
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Newly created Archive object
- */
-Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
- Archive* archive = new Archive;
- handle = Kernel::g_object_pool.Create(archive);
- archive->name = name;
- archive->backend = backend;
-
- MountArchive(archive);
-
- return archive;
-}
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
- Handle handle;
- CreateArchive(handle, backend, name);
- return handle;
-}
-
-/**
- * Open a File from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the File inside of the Archive
- * @param mode Mode under which to open the File
- * @return Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
- File* file = new File;
- Handle handle = Kernel::g_object_pool.Create(file);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- file->path = path;
- file->backend = archive->backend->OpenFile(path, mode);
-
- if (!file->backend)
- return 0;
-
- return handle;
-}
-
-/**
- * Create a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- if (archive == nullptr)
- return -1;
- if (archive->backend->CreateDirectory(path))
- return 0;
- return -1;
-}
-
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Directory* directory = new Directory;
- Handle handle = Kernel::g_object_pool.Create(directory);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- directory->path = path;
- directory->backend = archive->backend->OpenDirectory(path);
-
- return handle;
-}
-
-/// Initialize archives
-void ArchiveInit() {
- g_archive_map.clear();
-
- // 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.
-
- std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
- auto archive = new FileSys::Archive_SDMC(sdmc_directory);
- if (archive->Initialize())
- CreateArchive(archive, "SDMC");
- else
- ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
-}
-
-/// Shutdown archives
-void ArchiveShutdown() {
- g_archive_map.clear();
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
deleted file mode 100644
index 0230996b6..000000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-#include "core/hle/kernel/kernel.h"
-#include "core/file_sys/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return true if it worked fine
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
-
-/**
- * Open a File from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the File inside of the Archive
- * @param mode Mode under which to open the File
- * @return Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode);
-
-/**
- * Create a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Whether creation of directory succeeded
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/// Initialize archives
-void ArchiveInit();
-
-/// Shutdown archives
-void ArchiveShutdown();
-
-} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 45ed79be8..288080209 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <algorithm>
@@ -30,13 +30,8 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
std::string name; ///< Name of event (optional)
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- *wait = locked;
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = locked;
if (locked) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
@@ -47,7 +42,7 @@ public:
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
locked = true;
}
- return 0;
+ return MakeResult<bool>(wait);
}
};
@@ -57,12 +52,12 @@ public:
* @param permanent_locked Boolean permanent locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
* @param locked Boolean locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetEventLocked(const Handle handle, const bool locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = locked;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) {
* @param handle Handle to event to signal
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SignalEvent(const Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ 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]);
- // If any thread is signalled awake by this event, assume the event was "caught" and reset
+ // 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,
// the event will not be reset, and the next thread to call WaitSynchronization on it will
// not block. Not sure if this is correct behavior, but it seems to work.
@@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) {
if (!evt->permanent_locked) {
evt->locked = event_caught;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) {
* @param handle Handle to event to clear
* @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode ClearEvent(Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = true;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index c39b33180..73aec4e79 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -15,31 +15,27 @@ namespace Kernel {
* Changes whether an event is locked or not
* @param handle Handle to event to change
* @param locked Boolean locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked);
+ResultCode SetEventLocked(const Handle handle, const bool locked);
/**
* Hackish function to set an events permanent lock state, used to pass through synch blocks
* @param handle Handle to event to change
* @param permanent_locked Boolean permanent locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked);
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
/**
* Signals an event
* @param handle Handle to event to signal
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle);
+ResultCode SignalEvent(const Handle handle);
/**
* Clears an event
* @param handle Handle to event to clear
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle);
+ResultCode ClearEvent(Handle handle);
/**
* Creates an event
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 88cbc1af5..6a690e915 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,18 +1,20 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
+
+#include <algorithm>
#include "common/common.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/archive.h"
namespace Kernel {
Handle g_main_thread = 0;
ObjectPool g_object_pool;
+u64 g_program_id = 0;
ObjectPool::ObjectPool() {
next_id = INITIAL_NEXT_ID;
@@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
return i + HANDLE_OFFSET;
}
}
- ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
+ LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use.");
return 0;
}
-bool ObjectPool::IsValid(Handle handle) {
+bool ObjectPool::IsValid(Handle handle) const {
int index = handle - HANDLE_OFFSET;
if (index < 0)
return false;
@@ -60,7 +62,7 @@ void ObjectPool::Clear() {
Object* &ObjectPool::operator [](Handle handle)
{
- _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
+ _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
return pool[handle - HANDLE_OFFSET];
}
@@ -68,37 +70,30 @@ void ObjectPool::List() {
for (int i = 0; i < MAX_COUNT; i++) {
if (occupied[i]) {
if (pool[i]) {
- INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
+ LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
pool[i]->GetName().c_str());
}
}
}
}
-int ObjectPool::GetCount() {
- int count = 0;
- for (int i = 0; i < MAX_COUNT; i++) {
- if (occupied[i])
- count++;
- }
- return count;
+int ObjectPool::GetCount() const {
+ return std::count(occupied.begin(), occupied.end(), true);
}
Object* ObjectPool::CreateByIDType(int type) {
- ERROR_LOG(COMMON, "Unimplemented: %d.", type);
+ LOG_ERROR(Kernel, "Unimplemented: %d.", type);
return nullptr;
}
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
- Kernel::ArchiveInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
- Kernel::ArchiveShutdown();
g_object_pool.Clear(); // Free all kernel objects
}
@@ -109,8 +104,6 @@ void Shutdown() {
* @return True on success, otherwise false
*/
bool LoadExec(u32 entry_point) {
- Init();
-
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 867d1b89c..7123485be 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,12 +1,13 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include "common/common.h"
+#include "core/hle/result.h"
typedef u32 Handle;
typedef s32 Result;
@@ -21,7 +22,7 @@ enum KernelHandle {
enum class HandleType : u32 {
Unknown = 0,
Port = 1,
- Service = 2,
+ Session = 2,
Event = 3,
Mutex = 4,
SharedMemory = 5,
@@ -29,12 +30,9 @@ enum class HandleType : u32 {
Thread = 7,
Process = 8,
AddressArbiter = 9,
- File = 10,
- Semaphore = 11,
- Archive = 12,
- Directory = 13,
+ Semaphore = 10,
};
-
+
enum {
DEFAULT_STACK_SIZE = 0x4000,
};
@@ -52,21 +50,13 @@ public:
virtual Kernel::HandleType GetHandleType() const = 0;
/**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
+ * Wait for kernel object to synchronize.
+ * @return True if the current thread should wait as a result of the wait
*/
- virtual Result SyncRequest(bool* wait) {
- ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
- return -1;
+ virtual ResultVal<bool> WaitSynchronization() {
+ LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
+ return UnimplementedFunction(ErrorModule::Kernel);
}
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- virtual Result WaitSynchronization(bool* wait) = 0;
};
class ObjectPool : NonCopyable {
@@ -80,38 +70,29 @@ public:
static Object* CreateByIDType(int type);
template <class T>
- u32 Destroy(Handle handle) {
- u32 error;
- if (Get<T>(handle, error)) {
+ void Destroy(Handle handle) {
+ if (Get<T>(handle)) {
occupied[handle - HANDLE_OFFSET] = false;
delete pool[handle - HANDLE_OFFSET];
}
- return error;
- };
+ }
- bool IsValid(Handle handle);
+ bool IsValid(Handle handle) const;
template <class T>
- T* Get(Handle handle, u32& outError) {
+ T* Get(Handle handle) {
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
- // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
- if (handle != 0 && (u32)handle != 0x80020001) {
- WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if (handle != 0) {
+ LOG_ERROR(Kernel, "Bad object handle %08x", handle);
}
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ return nullptr;
} else {
- // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
- // it just acted as a static case and everything worked. This means that we will never
- // see the Wrong type object error below, but we'll just have to live with that danger.
- T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
- if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
- WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ Object* t = pool[handle - HANDLE_OFFSET];
+ if (t->GetHandleType() != T::GetStaticHandleType()) {
+ LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
+ return nullptr;
}
- outError = 0;//SCE_KERNEL_ERROR_OK;
- return t;
+ return static_cast<T*>(t);
}
}
@@ -119,7 +100,7 @@ public:
template <class T>
T *GetFast(Handle handle) {
const Handle realHandle = handle - HANDLE_OFFSET;
- _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
+ _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
return static_cast<T*>(pool[realHandle]);
}
@@ -139,9 +120,9 @@ public:
}
bool GetIDType(Handle handle, HandleType* type) const {
- if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
- !occupied[handle - HANDLE_OFFSET]) {
- ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
+ !occupied[handle - HANDLE_OFFSET]) {
+ LOG_ERROR(Kernel, "Bad object handle %08X", handle);
return false;
}
Object* t = pool[handle - HANDLE_OFFSET];
@@ -152,10 +133,10 @@ public:
Object* &operator [](Handle handle);
void List();
void Clear();
- int GetCount();
+ int GetCount() const;
private:
-
+
enum {
MAX_COUNT = 0x1000,
HANDLE_OFFSET = 0x100,
@@ -170,6 +151,12 @@ private:
extern ObjectPool g_object_pool;
extern Handle g_main_thread;
+/// The ID code of the currently running game
+/// TODO(Subv): This variable should not be here,
+/// we need a way to store information about the currently loaded application
+/// for later query during runtime, maybe using the LDR service?
+extern u64 g_program_id;
+
/// Initialize the kernel
void Init();
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index fcfd061ac..5a173e129 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <vector>
@@ -27,32 +27,7 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
std::string name; ///< Name of mutex (optional)
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- // TODO(bunnei): ImplementMe
- locked = true;
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- *wait = locked;
-
- if (locked) {
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
- }
-
- return 0;
- }
+ ResultVal<bool> WaitSynchronization() override;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,21 +35,46 @@ public:
typedef std::multimap<Handle, Handle> MutexMap;
static MutexMap g_mutex_held_locks;
-void MutexAcquireLock(Mutex* mutex, Handle thread) {
+/**
+ * Acquires the specified mutex for the specified thread
+ * @param mutex Mutex that is to be acquired
+ * @param thread Thread that will acquired
+ */
+void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
mutex->lock_thread = thread;
}
-void MutexAcquireLock(Mutex* mutex) {
- Handle thread = GetCurrentThreadHandle();
+bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
MutexAcquireLock(mutex, thread);
+ Kernel::ResumeThreadFromWait(thread);
+ return true;
+}
+
+/**
+ * Resumes a thread waiting for the specified mutex
+ * @param mutex The mutex that some thread is waiting on
+ */
+void ResumeWaitingThread(Mutex* mutex) {
+ // Find the next waiting thread for the mutex...
+ if (mutex->waiting_threads.empty()) {
+ // Reset mutex lock thread handle, nothing is waiting
+ mutex->locked = false;
+ mutex->lock_thread = -1;
+ }
+ else {
+ // Resume the next waiting thread and re-lock the mutex
+ std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
+ ReleaseMutexForThread(mutex, *iter);
+ mutex->waiting_threads.erase(iter);
+ }
}
void MutexEraseLock(Mutex* mutex) {
Handle handle = mutex->GetHandle();
auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- if ((*iter).second == handle) {
+ if (iter->second == handle) {
g_mutex_held_locks.erase(iter);
break;
}
@@ -82,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
mutex->lock_thread = -1;
}
+void ReleaseThreadMutexes(Handle thread) {
+ auto locked = g_mutex_held_locks.equal_range(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_object_pool.GetFast<Mutex>(iter->second);
+ ResumeWaitingThread(mutex);
+ }
+
+ // Erase all the locks that this thread holds
+ g_mutex_held_locks.erase(thread);
+}
+
bool LockMutex(Mutex* mutex) {
// Mutex alread locked?
if (mutex->locked) {
@@ -91,43 +104,27 @@ bool LockMutex(Mutex* mutex) {
return true;
}
-bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
- MutexAcquireLock(mutex, thread);
- Kernel::ResumeThreadFromWait(thread);
- return true;
-}
-
bool ReleaseMutex(Mutex* mutex) {
MutexEraseLock(mutex);
- bool woke_threads = false;
-
- // Find the next waiting thread for the mutex...
- while (!woke_threads && !mutex->waiting_threads.empty()) {
- std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
- woke_threads |= ReleaseMutexForThread(mutex, *iter);
- mutex->waiting_threads.erase(iter);
- }
- // Reset mutex lock thread handle, nothing is waiting
- if (!woke_threads) {
- mutex->locked = false;
- mutex->lock_thread = -1;
- }
- return woke_threads;
+ ResumeWaitingThread(mutex);
+ return true;
}
/**
* Releases a mutex
* @param handle Handle to mutex to release
*/
-Result ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
-
- _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
+ResultCode ReleaseMutex(Handle handle) {
+ Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
+ if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
- return -1;
+ // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
+ // what error condition this is supposed to be signaling.
+ return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
+ ErrorSummary::NothingHappened, ErrorLevel::Temporary);
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -167,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
return handle;
}
+ResultVal<bool> Mutex::WaitSynchronization() {
+ bool wait = locked;
+ if (locked) {
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
+ }
+ else {
+ // Lock the mutex when the first thread accesses it
+ locked = true;
+ MutexAcquireLock(this);
+ }
+
+ return MakeResult<bool>(wait);
+}
} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 7d7b5137e..7f4909a6e 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -13,9 +13,8 @@ namespace Kernel {
/**
* Releases a mutex
* @param handle Handle to mutex to release
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ReleaseMutex(Handle handle);
+ResultCode ReleaseMutex(Handle handle);
/**
* Creates a mutex
@@ -25,4 +24,10 @@ Result ReleaseMutex(Handle handle);
*/
Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
+/**
+ * Releases all the mutexes held by the specified thread
+ * @param thread Thread that is holding the mutexes
+ */
+void ReleaseThreadMutexes(Handle thread);
+
} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
new file mode 100644
index 000000000..6f56da8a9
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,94 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <queue>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/semaphore.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Semaphore : public Object {
+public:
+ std::string GetTypeName() const override { return "Semaphore"; }
+ std::string GetName() const override { return name; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; }
+
+ u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
+ u32 available_count; ///< Number of free slots left in the semaphore
+ std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
+ std::string name; ///< Name of semaphore (optional)
+
+ /**
+ * Tests whether a semaphore still has free slots
+ * @return Whether the semaphore is available
+ */
+ bool IsAvailable() const {
+ return available_count > 0;
+ }
+
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = !IsAvailable();
+
+ if (wait) {
+ Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
+ waiting_threads.push(GetCurrentThreadHandle());
+ } else {
+ --available_count;
+ }
+
+ return MakeResult<bool>(wait);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ResultCode CreateSemaphore(Handle* handle, u32 initial_count,
+ u32 max_count, const std::string& name) {
+
+ if (initial_count > max_count)
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+
+ Semaphore* semaphore = new Semaphore;
+ *handle = g_object_pool.Create(semaphore);
+
+ // When the semaphore is created, some slots are reserved for other threads,
+ // and the rest is reserved for the caller thread
+ semaphore->max_count = max_count;
+ semaphore->available_count = initial_count;
+ semaphore->name = name;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
+ Semaphore* semaphore = g_object_pool.Get<Semaphore>(handle);
+ if (semaphore == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ if (semaphore->max_count - semaphore->available_count < release_count)
+ return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+
+ *count = semaphore->available_count;
+ semaphore->available_count += 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());
+ semaphore->waiting_threads.pop();
+ --semaphore->available_count;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
new file mode 100644
index 000000000..f0075fdb8
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+/**
+ * Creates a semaphore.
+ * @param handle Pointer to the handle of the newly created object
+ * @param initial_count Number of slots reserved for other threads
+ * @param max_count Maximum number of slots the semaphore can have
+ * @param name Optional name of semaphore
+ * @return ResultCode of the error
+ */
+ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown");
+
+/**
+ * Releases a certain number of slots from a semaphore.
+ * @param count The number of free slots the semaphore had before this call
+ * @param handle The handle of the semaphore to release
+ * @param release_count The number of slots to release
+ * @return ResultCode of the error
+ */
+ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
+
+} // namespace
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 000000000..06ae4bc39
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
+
+/**
+ * Returns a pointer to the command buffer in kernel memory
+ * @param offset Optional offset into command buffer
+ * @return Pointer to command buffer
+ */
+inline static u32* GetCommandBuffer(const int offset=0) {
+ return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
+}
+
+/**
+ * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
+ * primitive for communication between different processes, and are used to implement service calls
+ * to the various system services.
+ *
+ * To make a service call, the client must write the command header and parameters to the buffer
+ * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
+ * SVC call with its Session handle. The kernel will read the command header, using it to marshall
+ * the parameters to the process at the server endpoint of the session. After the server replies to
+ * the request, the response is marshalled back to the caller's TLS buffer and control is
+ * transferred back to it.
+ *
+ * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
+ * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
+ * with the session handle, this class's SyncRequest method is called, which should read the TLS
+ * buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
+ * no parameter marshalling is done.
+ *
+ * In the long term, this should be turned into the full-fledged IPC mechanism implemented by
+ * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
+ * opposed to HLE simulations.
+ */
+class Session : public Object {
+public:
+ std::string GetTypeName() const override { return "Session"; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; }
+
+ /**
+ * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
+ * aren't supported yet.
+ */
+ virtual ResultVal<bool> SyncRequest() = 0;
+};
+
+}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 6bd5e2728..3c8c502c6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common.h"
@@ -16,17 +16,6 @@ public:
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-
u32 base_address; ///< Address of shared memory block in RAM
MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
@@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
return shared_memory;
}
-/**
- * Creates a shared memory object
- * @param name Optional name of shared memory object
- * @return Handle of newly created shared memory object
- */
Handle CreateSharedMemory(const std::string& name) {
Handle handle;
CreateSharedMemory(handle, name);
@@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) {
* @param other_permissions Memory block map other permissions (specified by SVC field)
* @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions) {
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
- ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
- handle);
- return -1;
+ LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
+ handle, address);
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
- return 0;
+ return RESULT_SUCCESS;
}
-/**
- * Gets a pointer to the shared memory block
- * @param handle Shared memory block handle
- * @param offset Offset from the start of the shared memory block to get pointer
- * @return Pointer to the shared memory block from the specified offset
- */
-u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
- return Memory::GetPointer(shared_memory->base_address + offset);
+ return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
- ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
- return nullptr;
+ LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
+ // TODO(yuriks): Verify error code.
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidState, ErrorLevel::Permanent);
}
} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 6204d8a45..bb778ec26 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown");
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions);
/**
@@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
* @param offset Offset from the start of the shared memory block to get pointer
* @return Pointer to the shared memory block from the specified offset
*/
-u8* GetSharedMemoryPointer(Handle handle, u32 offset);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e15590c49..1c04701de 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <algorithm>
#include <list>
@@ -11,10 +11,12 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
-#include "core/mem_map.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/result.h"
+#include "core/mem_map.h"
namespace Kernel {
@@ -33,25 +35,23 @@ public:
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- if (status != THREADSTATUS_DORMANT) {
+ 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());
- *wait = true;
}
- return 0;
+
+ return MakeResult<bool>(wait);
}
ThreadContext context;
+ u32 thread_id;
+
u32 status;
u32 entry_point;
u32 stack_top;
@@ -64,6 +64,7 @@ public:
WaitType wait_type;
Handle wait_handle;
+ VAddr wait_address;
std::vector<Handle> waiting_threads;
@@ -71,17 +72,20 @@ public:
};
// Lists all thread ids that aren't deleted/etc.
-std::vector<Handle> g_thread_queue;
+static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-Common::ThreadQueueList<Handle> g_thread_ready_queue;
+static Common::ThreadQueueList<Handle> thread_ready_queue;
+
+static Handle current_thread_handle;
+static Thread* current_thread;
-Handle g_current_thread_handle;
-Thread* g_current_thread;
+static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
+static u32 next_thread_id; ///< The next available thread id
/// Gets the current thread
inline Thread* GetCurrentThread() {
- return g_current_thread;
+ return current_thread;
}
/// Gets the current thread handle
@@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() {
/// Sets the current thread
inline void SetCurrentThread(Thread* t) {
- g_current_thread = t;
- g_current_thread_handle = t->GetHandle();
+ current_thread = t;
+ current_thread_handle = t->GetHandle();
}
/// Saves the current CPU context
@@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
t->context.pc = t->context.reg_15 = t->entry_point;
t->context.sp = t->stack_top;
t->context.cpsr = 0x1F; // Usermode
-
+
// TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a
// thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be
// agnostic of the CPU core.
@@ -124,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
}
t->wait_type = WAITTYPE_NONE;
t->wait_handle = 0;
+ t->wait_address = 0;
}
/// Change a thread to "ready" state
@@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) {
Handle handle = t->GetHandle();
if (t->IsReady()) {
if (!ready) {
- g_thread_ready_queue.remove(t->current_priority, handle);
+ thread_ready_queue.remove(t->current_priority, handle);
}
} else if (ready) {
if (t->IsRunning()) {
- g_thread_ready_queue.push_front(t->current_priority, handle);
+ thread_ready_queue.push_front(t->current_priority, handle);
} else {
- g_thread_ready_queue.push_back(t->current_priority, handle);
+ thread_ready_queue.push_back(t->current_priority, handle);
}
t->status = THREADSTATUS_READY;
}
}
/// Verify that a thread has not been released from waiting
-inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
- if (type != thread->wait_type || wait_handle != thread->wait_handle)
- return false;
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting());
+}
- return true;
+/// Verify that a thread has not been released from waiting (with wait address)
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address);
}
/// Stops the current thread
-void StopThread(Handle handle, const char* reason) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
+ResultCode StopThread(Handle handle, const char* reason) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ // Release all the mutexes that this thread holds
+ ReleaseThreadMutexes(handle);
+
ChangeReadyState(thread, false);
thread->status = THREADSTATUS_DORMANT;
- for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
- const Handle waiting_thread = thread->waiting_threads[i];
+ for (Handle waiting_handle : thread->waiting_threads) {
+ Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
- ResumeThreadFromWait(waiting_thread);
+ ResumeThreadFromWait(waiting_handle);
}
}
thread->waiting_threads.clear();
@@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) {
// Stopped threads are never waiting.
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
+
+ return RESULT_SUCCESS;
}
/// Changes a threads state
@@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
t->status = new_status;
-
+
if (new_status == THREADSTATUS_WAIT) {
if (t->wait_type == WAITTYPE_NONE) {
- ERROR_LOG(KERNEL, "Waittype none not allowed");
+ LOG_ERROR(Kernel, "Waittype none not allowed");
}
}
}
@@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
continue;
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ if (thread == nullptr)
+ continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
+
if(thread->current_priority <= priority) {
highest_priority_thread = handle;
priority = thread->current_priority;
@@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
/// Arbitrate all threads currently waiting
void ArbitrateAllThreads(u32 arbiter, u32 address) {
-
+
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
ResumeThreadFromWait(handle);
}
}
@@ -238,11 +252,11 @@ void CallThread(Thread* t) {
/// Switches CPU context to that of the specified thread
void SwitchContext(Thread* t) {
Thread* cur = GetCurrentThread();
-
+
// Save context for current thread
if (cur) {
SaveContext(cur->context);
-
+
if (cur->IsRunning()) {
ChangeReadyState(cur, true);
}
@@ -263,23 +277,18 @@ void SwitchContext(Thread* t) {
Thread* NextThread() {
Handle next;
Thread* cur = GetCurrentThread();
-
+
if (cur && cur->IsRunning()) {
- next = g_thread_ready_queue.pop_first_better(cur->current_priority);
+ next = thread_ready_queue.pop_first_better(cur->current_priority);
} else {
- next = g_thread_ready_queue.pop_first();
+ next = thread_ready_queue.pop_first();
}
if (next == 0) {
return nullptr;
}
- return Kernel::g_object_pool.GetFast<Thread>(next);
+ return Kernel::g_object_pool.Get<Thread>(next);
}
-/**
- * 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
- */
void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
Thread* thread = GetCurrentThread();
thread->wait_type = wait_type;
@@ -287,10 +296,14 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
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);
+ GetCurrentThread()->wait_address = wait_address;
+}
+
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
- u32 error;
- Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
+ Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
if (thread) {
thread->status &= ~THREADSTATUS_WAIT;
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
@@ -305,12 +318,12 @@ void DebugThreadQueue() {
if (!thread) {
return;
}
- INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
- for (u32 i = 0; i < g_thread_queue.size(); i++) {
- Handle handle = g_thread_queue[i];
- s32 priority = g_thread_ready_queue.contains(handle);
+ 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);
if (priority != -1) {
- INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
}
}
}
@@ -319,16 +332,17 @@ void DebugThreadQueue() {
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
s32 processor_id, u32 stack_top, int stack_size) {
- _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
- "CreateThread priority=%d, outside of allowable range!", priority)
+ _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
+ "priority=%d, outside of allowable range!", priority)
Thread* thread = new Thread;
handle = Kernel::g_object_pool.Create(thread);
- g_thread_queue.push_back(handle);
- g_thread_ready_queue.prepare(priority);
+ thread_queue.push_back(handle);
+ thread_ready_queue.prepare(priority);
+ thread->thread_id = next_thread_id++;
thread->status = THREADSTATUS_DORMANT;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
@@ -337,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
thread->processor_id = processor_id;
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
thread->name = name;
return thread;
@@ -347,28 +362,28 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
u32 stack_top, int stack_size) {
if (name == nullptr) {
- ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
+ LOG_ERROR(Kernel_SVC, "nullptr name");
return -1;
}
if ((u32)stack_size < 0x200) {
- ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name,
+ 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);
- WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
+ 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)) {
- ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, 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,
+ Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
stack_size);
ResetThread(thread, arg, 0);
@@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
}
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
- return thread->current_priority;
+ResultVal<u32> GetThreadPriority(const Handle handle) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ return MakeResult<u32>(thread->current_priority);
}
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority) {
+ResultCode SetThreadPriority(Handle handle, s32 priority) {
Thread* thread = nullptr;
if (!handle) {
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
} else {
- thread = g_object_pool.GetFast<Thread>(handle);
+ thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel);
+ }
}
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
// If priority is invalid, clamp to valid range
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
+ LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
@@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) {
// Change thread priority
s32 old = thread->current_priority;
- g_thread_ready_queue.remove(old, handle);
+ thread_ready_queue.remove(old, handle);
thread->current_priority = priority;
- g_thread_ready_queue.prepare(thread->current_priority);
+ thread_ready_queue.prepare(thread->current_priority);
// Change thread status to "ready" and push to ready queue
if (thread->IsRunning()) {
thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
if (thread->IsReady()) {
- g_thread_ready_queue.push_back(thread->current_priority, handle);
+ thread_ready_queue.push_back(thread->current_priority, handle);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Sets up the primary application thread
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
-
+
// Initialize new "main" thread
- Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
-
+
ResetThread(thread, 0, 0);
-
+
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
if (cur && cur->IsRunning()) {
ChangeReadyState(cur, true);
}
-
+
// Run new "main" thread
SetCurrentThread(thread);
thread->status = THREADSTATUS_RUNNING;
@@ -451,13 +470,13 @@ void Reschedule() {
Thread* next = NextThread();
HLE::g_reschedule = false;
if (next > 0) {
- INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
-
+ LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+
SwitchContext(next);
// Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
// by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
- // This results in the current thread yielding on a VBLANK once, and then it will be
+ // This results in the current thread yielding on a VBLANK once, and then it will be
// immediately placed back in the queue for execution.
if (prev->wait_type == WAITTYPE_VBLANK) {
ResumeThreadFromWait(prev->GetHandle());
@@ -465,9 +484,21 @@ void Reschedule() {
}
}
+ResultCode GetThreadId(u32* thread_id, Handle handle) {
+ Thread* thread = g_object_pool.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;
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 39fa38b75..be7adface 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,11 +1,15 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
+
+#include "core/mem_map.h"
+
#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
enum ThreadPriority {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
@@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
void Reschedule();
/// Stops the current thread
-void StopThread(Handle thread, const char* reason);
+ResultCode StopThread(Handle thread, const char* reason);
+
+/**
+ * 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);
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle);
@@ -76,14 +88,22 @@ Handle GetCurrentThreadHandle();
*/
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+/**
+ * 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_address Arbitration address used to resume from wait
+ */
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
+
/// Put current thread in a wait state - on WaitSynchronization
void WaitThread_Synchronization();
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle);
+ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority);
+ResultCode SetThreadPriority(Handle handle, s32 priority);
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
new file mode 100644
index 000000000..14d2be4a2
--- /dev/null
+++ b/src/core/hle/result.h
@@ -0,0 +1,402 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+#include "common/common_types.h"
+#include "common/bit_field.h"
+
+// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
+
+/// Detailed description of the error. This listing is likely incomplete.
+enum class ErrorDescription : u32 {
+ Success = 0,
+ FS_NotFound = 100,
+ FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
+ InvalidSection = 1000,
+ TooLarge = 1001,
+ NotAuthorized = 1002,
+ AlreadyDone = 1003,
+ InvalidSize = 1004,
+ InvalidEnumValue = 1005,
+ InvalidCombination = 1006,
+ NoData = 1007,
+ Busy = 1008,
+ MisalignedAddress = 1009,
+ MisalignedSize = 1010,
+ OutOfMemory = 1011,
+ NotImplemented = 1012,
+ InvalidAddress = 1013,
+ InvalidPointer = 1014,
+ InvalidHandle = 1015,
+ NotInitialized = 1016,
+ AlreadyInitialized = 1017,
+ NotFound = 1018,
+ CancelRequested = 1019,
+ AlreadyExists = 1020,
+ OutOfRange = 1021,
+ Timeout = 1022,
+ InvalidResultValue = 1023,
+};
+
+/**
+ * Identifies the module which caused the error. Error codes can be propagated through a call
+ * chain, meaning that this doesn't always correspond to the module where the API call made is
+ * contained.
+ */
+enum class ErrorModule : u32 {
+ Common = 0,
+ Kernel = 1,
+ Util = 2,
+ FileServer = 3,
+ LoaderServer = 4,
+ TCB = 5,
+ OS = 6,
+ DBG = 7,
+ DMNT = 8,
+ PDN = 9,
+ GX = 10,
+ I2C = 11,
+ GPIO = 12,
+ DD = 13,
+ CODEC = 14,
+ SPI = 15,
+ PXI = 16,
+ FS = 17,
+ DI = 18,
+ HID = 19,
+ CAM = 20,
+ PI = 21,
+ PM = 22,
+ PM_LOW = 23,
+ FSI = 24,
+ SRV = 25,
+ NDM = 26,
+ NWM = 27,
+ SOC = 28,
+ LDR = 29,
+ ACC = 30,
+ RomFS = 31,
+ AM = 32,
+ HIO = 33,
+ Updater = 34,
+ MIC = 35,
+ FND = 36,
+ MP = 37,
+ MPWL = 38,
+ AC = 39,
+ HTTP = 40,
+ DSP = 41,
+ SND = 42,
+ DLP = 43,
+ HIO_LOW = 44,
+ CSND = 45,
+ SSL = 46,
+ AM_LOW = 47,
+ NEX = 48,
+ Friends = 49,
+ RDT = 50,
+ Applet = 51,
+ NIM = 52,
+ PTM = 53,
+ MIDI = 54,
+ MC = 55,
+ SWC = 56,
+ FatFS = 57,
+ NGC = 58,
+ CARD = 59,
+ CARDNOR = 60,
+ SDMC = 61,
+ BOSS = 62,
+ DBM = 63,
+ Config = 64,
+ PS = 65,
+ CEC = 66,
+ IR = 67,
+ UDS = 68,
+ PL = 69,
+ CUP = 70,
+ Gyroscope = 71,
+ MCU = 72,
+ NS = 73,
+ News = 74,
+ RO_1 = 75,
+ GD = 76,
+ CardSPI = 77,
+ EC = 78,
+ RO_2 = 79,
+ WebBrowser = 80,
+ Test = 81,
+ ENC = 82,
+ PIA = 83,
+
+ Application = 254,
+ InvalidResult = 255
+};
+
+/// A less specific error cause.
+enum class ErrorSummary : u32 {
+ Success = 0,
+ NothingHappened = 1,
+ WouldBlock = 2,
+ OutOfResource = 3, ///< There are no more kernel resources (memory, table slots) to
+ ///< execute the operation.
+ NotFound = 4, ///< A file or resource was not found.
+ InvalidState = 5,
+ NotSupported = 6, ///< The operation is not supported or not implemented.
+ InvalidArgument = 7, ///< Returned when a passed argument is invalid in the current runtime
+ ///< context. (Invalid handle, out-of-bounds pointer or size, etc.)
+ WrongArgument = 8, ///< Returned when a passed argument is in an incorrect format for use
+ ///< with the function. (E.g. Invalid enum value)
+ Canceled = 9,
+ StatusChanged = 10,
+ Internal = 11,
+
+ InvalidResult = 63
+};
+
+/// The severity of the error.
+enum class ErrorLevel : u32 {
+ Success = 0,
+ Info = 1,
+
+ Status = 25,
+ Temporary = 26,
+ Permanent = 27,
+ Usage = 28,
+ Reinitialize = 29,
+ Reset = 30,
+ Fatal = 31
+};
+
+/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
+union ResultCode {
+ u32 raw;
+
+ BitField<0, 10, ErrorDescription> description;
+ BitField<10, 8, ErrorModule> module;
+
+ BitField<21, 6, ErrorSummary> summary;
+ BitField<27, 5, ErrorLevel> level;
+
+ // The last bit of `level` is checked by apps and the kernel to determine if a result code is an error
+ BitField<31, 1, u32> is_error;
+
+ explicit ResultCode(u32 raw) : raw(raw) {}
+ ResultCode(ErrorDescription description_, ErrorModule module_,
+ ErrorSummary summary_, ErrorLevel level_) : raw(0) {
+ description = description_;
+ module = module_;
+ summary = summary_;
+ level = level_;
+ }
+
+ ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
+
+ bool IsSuccess() const {
+ return is_error == 0;
+ }
+
+ bool IsError() const {
+ return is_error == 1;
+ }
+};
+
+inline bool operator==(const ResultCode a, const ResultCode b) {
+ return a.raw == b.raw;
+}
+
+inline bool operator!=(const ResultCode a, const ResultCode b) {
+ return a.raw != b.raw;
+}
+
+// Convenience functions for creating some common kinds of errors:
+
+/// The default success `ResultCode`.
+const ResultCode RESULT_SUCCESS(0);
+
+/// Might be returned instead of a dummy success for unimplemented APIs.
+inline ResultCode UnimplementedFunction(ErrorModule module) {
+ return ResultCode(ErrorDescription::NotImplemented, module,
+ ErrorSummary::NotSupported, ErrorLevel::Permanent);
+}
+/// Returned when a function is passed an invalid handle.
+inline ResultCode InvalidHandle(ErrorModule module) {
+ return ResultCode(ErrorDescription::InvalidHandle, module,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+}
+
+/**
+ * This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
+ * also holds a result of type `T`. If the code is an error code then trying to access the inner
+ * value fails, thus ensuring that the ResultCode of functions is always checked properly before
+ * their return value is used. It is similar in concept to the `std::optional` type
+ * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
+ * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
+ *
+ * An example of how it could be used:
+ * \code
+ * ResultVal<int> Frobnicate(float strength) {
+ * if (strength < 0.f || strength > 1.0f) {
+ * // Can't frobnicate too weakly or too strongly
+ * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common,
+ * ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ * } else {
+ * // Frobnicated! Give caller a cookie
+ * return MakeResult<int>(42);
+ * }
+ * }
+ * \endcode
+ *
+ * \code
+ * ResultVal<int> frob_result = Frobnicate(0.75f);
+ * if (frob_result) {
+ * // Frobbed ok
+ * printf("My cookie is %d\n", *frob_result);
+ * } else {
+ * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex);
+ * }
+ * \endcode
+ */
+template <typename T>
+class ResultVal {
+public:
+ /// Constructs an empty `ResultVal` with the given error code. The code must not be a success code.
+ ResultVal(ResultCode error_code = ResultCode(-1))
+ : result_code(error_code)
+ {
+ assert(error_code.IsError());
+ UpdateDebugPtr();
+ }
+
+ /**
+ * Similar to the non-member function `MakeResult`, with the exception that you can manually
+ * specify the success code. `success_code` must not be an error code.
+ */
+ template <typename... Args>
+ static ResultVal WithCode(ResultCode success_code, Args&&... args) {
+ ResultVal<T> result;
+ result.emplace(success_code, std::forward<Args>(args)...);
+ return result;
+ }
+
+ ResultVal(const ResultVal& o)
+ : result_code(o.result_code)
+ {
+ if (!o.empty()) {
+ new (&storage) T(*o.GetPointer());
+ }
+ UpdateDebugPtr();
+ }
+
+ ResultVal(ResultVal&& o)
+ : result_code(o.result_code)
+ {
+ if (!o.empty()) {
+ new (&storage) T(std::move(*o.GetPointer()));
+ }
+ UpdateDebugPtr();
+ }
+
+ ~ResultVal() {
+ if (!empty()) {
+ GetPointer()->~T();
+ }
+ }
+
+ ResultVal& operator=(const ResultVal& o) {
+ if (*this) {
+ if (o) {
+ *GetPointer() = *o.GetPointer();
+ } else {
+ GetPointer()->~T();
+ }
+ } else {
+ if (o) {
+ new (&storage) T(*o.GetPointer());
+ }
+ }
+ result_code = o.result_code;
+ UpdateDebugPtr();
+
+ return *this;
+ }
+
+ /**
+ * Replaces the current result with a new constructed result value in-place. The code must not
+ * be an error code.
+ */
+ template <typename... Args>
+ void emplace(ResultCode success_code, Args&&... args) {
+ assert(success_code.IsSuccess());
+ if (!empty()) {
+ GetPointer()->~T();
+ }
+ new (&storage) T(std::forward<Args>(args)...);
+ result_code = success_code;
+ UpdateDebugPtr();
+ }
+
+ /// Returns true if the `ResultVal` contains an error code and no value.
+ bool empty() const { return result_code.IsError(); }
+
+ /// Returns true if the `ResultVal` contains a return value.
+ bool Succeeded() const { return result_code.IsSuccess(); }
+ /// Returns true if the `ResultVal` contains an error code and no value.
+ bool Failed() const { return empty(); }
+
+ ResultCode Code() const { return result_code; }
+
+ const T& operator* () const { return *GetPointer(); }
+ T& operator* () { return *GetPointer(); }
+ const T* operator->() const { return GetPointer(); }
+ T* operator->() { return GetPointer(); }
+
+ /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
+ template <typename U>
+ T ValueOr(U&& value) const {
+ return !empty() ? *GetPointer() : std::move(value);
+ }
+
+private:
+ typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
+
+ StorageType storage;
+ ResultCode result_code;
+#if _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
+ debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
+#endif
+ }
+
+ const T* GetPointer() const {
+ assert(!empty());
+ return static_cast<const T*>(static_cast<const void*>(&storage));
+ }
+
+ T* GetPointer() {
+ assert(!empty());
+ return static_cast<T*>(static_cast<void*>(&storage));
+ }
+};
+
+/**
+ * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct
+ * `T` with and creates a success `ResultVal` contained the constructed value.
+ */
+template <typename T, typename... Args>
+ResultVal<T> MakeResult(Args&&... args) {
+ return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...);
+}
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index b39603bdf..311682abf 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -11,6 +11,24 @@
namespace AC_U {
+/**
+ * AC_U::GetWifiStatus service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
+ */
+void GetWifiStatus(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 0; // Connection type set to none
+
+ LOG_WARNING(Service_AC, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "CreateDefaultConfig"},
{0x00040006, nullptr, "ConnectAsync"},
@@ -18,16 +36,16 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080004, nullptr, "CloseAsync"},
{0x00090002, nullptr, "GetCloseResult"},
{0x000A0000, nullptr, "GetLastErrorCode"},
- {0x000D0000, nullptr, "GetWifiStatus"},
+ {0x000D0000, GetWifiStatus, "GetWifiStatus"},
{0x000E0042, nullptr, "GetCurrentAPInfo"},
{0x00100042, nullptr, "GetCurrentNZoneInfo"},
{0x00110042, nullptr, "GetNZoneApNumService"},
- {0x00240042, nullptr, "AddDenyApType "},
- {0x00270002, nullptr, "GetInfraPriority "},
+ {0x00240042, nullptr, "AddDenyApType"},
+ {0x00270002, nullptr, "GetInfraPriority"},
{0x002D0082, nullptr, "SetRequestEulaVersion"},
{0x00300004, nullptr, "RegisterDisconnectEvent"},
{0x003C0042, nullptr, "GetAPSSIDList"},
- {0x003E0042, nullptr, "IsConnected "},
+ {0x003E0042, nullptr, "IsConnected"},
{0x00400042, nullptr, "SetClientVersion"},
};
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h
index 3c5958d27..c91b28353 100644
--- a/src/core/hle/service/ac_u.h
+++ b/src/core/hle/service/ac_u.h
@@ -9,7 +9,7 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace AC_U
-// socket service "ac:u"
+// socket service "ac:u"
namespace AC_U {
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "ac:u";
}
};
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp
new file mode 100644
index 000000000..b8b06418c
--- /dev/null
+++ b/src/core/hle/service/am_app.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_app.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_APP
+
+namespace AM_APP {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h
new file mode 100644
index 000000000..86a5f5b74
--- /dev/null
+++ b/src/core/hle/service/am_app.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_APP
+
+namespace AM_APP {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "am:app";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp
new file mode 100644
index 000000000..403cac353
--- /dev/null
+++ b/src/core/hle/service/am_net.cpp
@@ -0,0 +1,47 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_net.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_NET
+
+namespace AM_NET {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x08010000, nullptr, "OpenTicket"},
+ {0x08020002, nullptr, "TicketAbortInstall"},
+ {0x08030002, nullptr, "TicketFinalizeInstall"},
+ {0x08040100, nullptr, "InstallTitleBegin"},
+ {0x08050000, nullptr, "InstallTitleAbort"},
+ {0x080600C0, nullptr, "InstallTitleResume"},
+ {0x08070000, nullptr, "InstallTitleAbortTMD"},
+ {0x08080000, nullptr, "InstallTitleFinish"},
+ {0x080A0000, nullptr, "OpenTMD"},
+ {0x080B0002, nullptr, "TMDAbortInstall"},
+ {0x080C0042, nullptr, "TMDFinalizeInstall"},
+ {0x080E0040, nullptr, "OpenContentCreate"},
+ {0x080F0002, nullptr, "ContentAbortInstall"},
+ {0x08100040, nullptr, "OpenContentResume"},
+ {0x08120002, nullptr, "ContentFinalizeInstall"},
+ {0x08130000, nullptr, "GetTotalContents"},
+ {0x08140042, nullptr, "GetContentIndexes"},
+ {0x08150044, nullptr, "GetContentsInfo"},
+ {0x08190108, nullptr, "Unknown"},
+ {0x081B00C2, nullptr, "InstallTitlesFinish"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/am_net.h
index 005382540..4816e1697 100644
--- a/src/core/hle/service/fs_user.h
+++ b/src/core/hle/service/am_net.h
@@ -7,24 +7,20 @@
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace FS_User
+// Namespace AM_NET
-namespace FS_User {
+namespace AM_NET {
-/// Interface to "fs:USER" service
class Interface : public Service::Interface {
public:
-
Interface();
-
~Interface();
-
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
- return "fs:USER";
+ return "am:net";
}
};
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index 617b6add4..ebfba4d8d 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -4,10 +4,12 @@
#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 "apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,20 @@
namespace APT_U {
+// Address used for shared font (as observed on HW)
+// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
+// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
+// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
+// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
+// 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
+static Handle shared_font_mem = 0;
+
+static Handle lock_handle = 0;
+static std::vector<u8> shared_font;
+
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
@@ -24,83 +40,178 @@ enum class SignalType : u32 {
};
void Initialize(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
+ 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
Kernel::SetEventLocked(cmd_buff[3], true);
Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+ _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
+ Kernel::ReleaseMutex(lock_handle);
+
cmd_buff[1] = 0; // No error
- DEBUG_LOG(KERNEL, "called");
+
+ LOG_DEBUG(Service_APT, "called");
}
void GetLockHandle(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+
+ if (0 == lock_handle) {
+ // TODO(bunnei): Verify if this is created here or at application boot?
+ lock_handle = Kernel::CreateMutex(false, "APT_U:Lock");
+ Kernel::ReleaseMutex(lock_handle);
+ }
cmd_buff[1] = 0; // No error
- cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock");
- DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]);
+
+ // Not sure what these parameters are used for, but retail apps check that they are 0 after
+ // GetLockHandle has been called.
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ cmd_buff[4] = 0;
+
+ cmd_buff[5] = lock_handle;
+ LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
}
void Enable(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
cmd_buff[1] = 0; // No error
- WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk);
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
}
void InquireNotification(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id);
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
+/**
+ * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state,
+ * from the source process which set the parameters. Once finished, NS will clear a flag in the NS
+ * state so that this command will return an error if this command is used again if parameters were
+ * not set again. This is called when the second Initialize event is triggered. It returns a signal
+ * type indicating why it was triggered.
+ * Inputs:
+ * 1 : AppID
+ * 2 : Parameter buffer size, max size is 0x1000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown, for now assume AppID of the process which sent these parameters
+ * 3 : Unknown, for now assume Signal type
+ * 4 : Actual parameter buffer size, this is <= to the the input size
+ * 5 : Value
+ * 6 : Handle from the source process which set the parameters, likely used for shared memory
+ * 7 : Size
+ * 8 : Output parameter buffer ptr
+ */
void ReceiveParameter(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0x10;
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
/**
-* APT_U::GlanceParameter service function
-* Inputs:
-* 1 : AppID
-* 2 : Parameter buffer size, max size is 0x1000
-* Outputs:
-* 1 : Result of function, 0 on success, otherwise error code
-* 2 : Unknown, for now assume AppID of the process which sent these parameters
-* 3 : Unknown, for now assume Signal type
-* 4 : Actual parameter buffer size, this is <= to the the input size
-* 5 : Value
-* 6 : Handle from the source process which set the parameters, likely used for shared memory
-* 7 : Size
-* 8 : Output parameter buffer ptr
-*/
+ * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
+ * (except for the word value prior to the output handle), except this will not clear the flag
+ * (except when responseword[3]==8 || responseword[3]==9) in NS state.
+ * Inputs:
+ * 1 : AppID
+ * 2 : Parameter buffer size, max size is 0x1000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown, for now assume AppID of the process which sent these parameters
+ * 3 : Unknown, for now assume Signal type
+ * 4 : Actual parameter buffer size, this is <= to the the input size
+ * 5 : Value
+ * 6 : Handle from the source process which set the parameters, likely used for shared memory
+ * 7 : Size
+ * 8 : Output parameter buffer ptr
+ */
void GlanceParameter(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
+
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0;
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
- cmd_buff[8] = 0;
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+}
+
+/**
+ * APT_U::AppletUtility service function
+ * Inputs:
+ * 1 : Unknown, but clearly used for something
+ * 2 : Buffer 1 size (purpose is unknown)
+ * 3 : Buffer 2 size (purpose is unknown)
+ * 5 : Buffer 1 address (purpose is unknown)
+ * 65 : Buffer 2 address (purpose is unknown)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void AppletUtility(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // These are from 3dbrew - I'm not really sure what they're used for.
+ u32 unk = cmd_buff[1];
+ u32 buffer1_size = cmd_buff[2];
+ u32 buffer2_size = cmd_buff[3];
+ u32 buffer1_addr = cmd_buff[5];
+ u32 buffer2_addr = cmd_buff[65];
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
+ "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
+ buffer1_addr, buffer2_addr);
+}
+
+/**
+ * APT_U::GetSharedFont service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Virtual address of where shared font will be loaded in memory
+ * 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()) {
+ // TODO(bunnei): This function shouldn't copy the shared font every time it's called.
+ // Instead, it should probably map the shared font as RO memory. We don't currently have
+ // an easy way to do this, but the copy should be sufficient for now.
+ memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
+
+ cmd_buff[0] = 0x00440082;
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = SHARED_FONT_VADDR;
+ cmd_buff[4] = shared_font_mem;
+ } else {
+ cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
+ LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
+ }
}
const Interface::FunctionInfo FunctionTable[] = {
@@ -171,14 +282,14 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
{0x00420080, nullptr, "SleepSystem"},
{0x00430040, nullptr, "NotifyToWait"},
- {0x00440000, nullptr, "GetSharedFont"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
{0x00460104, nullptr, "Wrap"},
{0x00470104, nullptr, "Unwrap"},
{0x00480100, nullptr, "GetProgramInfo"},
{0x00490180, nullptr, "Reboot"},
{0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, nullptr, "AppletUtility"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
{0x004C0000, nullptr, "SetFatalErrDispMode"},
{0x004D0080, nullptr, "GetAppletProgramInfo"},
{0x004E0000, nullptr, "HardwareResetAsync"},
@@ -190,6 +301,32 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
+ // Load the shared system font (if available).
+ // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
+ // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
+ // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
+ // "shared_font.bin" in the Citra "sysdata" directory.
+
+ shared_font.clear();
+ std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
+
+ FileUtil::CreateFullPath(filepath); // Create path if not already created
+ FileUtil::IOFile file(filepath, "rb");
+
+ if (file.IsOpen()) {
+ // Read shared font data
+ shared_font.resize(file.GetSize());
+ file.ReadBytes(shared_font.data(), file.GetSize());
+
+ // Create shared font memory object
+ shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
+ } else {
+ LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
+ shared_font_mem = 0;
+ }
+
+ lock_handle = 0;
+
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h
index 5af39e085..306730400 100644
--- a/src/core/hle/service/apt_u.h
+++ b/src/core/hle/service/apt_u.h
@@ -13,8 +13,8 @@ namespace APT_U {
// 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
+// 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:U" service
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp
new file mode 100644
index 000000000..b2ff4a756
--- /dev/null
+++ b/src/core/hle/service/boss_u.cpp
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/boss_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_U
+
+namespace BOSS_U {
+
+ const Interface::FunctionInfo FunctionTable[] = {
+ {0x00020100, nullptr, "GetStorageInfo"},
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Interface class
+
+ Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ }
+
+ Interface::~Interface() {
+ }
+
+} // namespace
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h
new file mode 100644
index 000000000..af39b8e65
--- /dev/null
+++ b/src/core/hle/service/boss_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_U
+
+namespace BOSS_U {
+
+ class Interface : public Service::Interface {
+ public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const {
+ return "boss:U";
+ }
+ };
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp
new file mode 100644
index 000000000..25d903516
--- /dev/null
+++ b/src/core/hle/service/cecd_u.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cecd_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_U
+
+namespace CECD_U {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h
new file mode 100644
index 000000000..969e1ed1b
--- /dev/null
+++ b/src/core/hle/service/cecd_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_U
+
+namespace CECD_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "cecd:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp
new file mode 100644
index 000000000..88d13d459
--- /dev/null
+++ b/src/core/hle/service/cfg_i.cpp
@@ -0,0 +1,59 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cfg_i.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_I
+
+namespace CFG_I {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x04010082, nullptr, "GetConfigInfoBlk8"},
+ {0x04020082, nullptr, "GetConfigInfoBlk4"},
+ {0x04030000, nullptr, "UpdateConfigNANDSavegame"},
+ {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x04060000, nullptr, "SecureInfoGetRegion"},
+ {0x04070000, nullptr, "SecureInfoGetByte101"},
+ {0x04080042, nullptr, "SecureInfoGetSerialNo"},
+ {0x04090000, nullptr, "UpdateConfigBlk00040003"},
+ {0x08010082, nullptr, "GetConfigInfoBlk8"},
+ {0x08020082, nullptr, "GetConfigInfoBlk4"},
+ {0x08030000, nullptr, "UpdateConfigNANDSavegame"},
+ {0x080400C2, nullptr, "CreateConfigInfoBlk"},
+ {0x08050000, nullptr, "DeleteConfigNANDSavefile"},
+ {0x08060000, nullptr, "FormatConfig"},
+ {0x08070000, nullptr, "Unknown"},
+ {0x08080000, nullptr, "UpdateConfigBlk1"},
+ {0x08090000, nullptr, "UpdateConfigBlk2"},
+ {0x080A0000, nullptr, "UpdateConfigBlk3"},
+ {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
+ {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
+ {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
+ {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
+ {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x08100000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x08110084, nullptr, "SetSecureInfo"},
+ {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
+ {0x08130000, nullptr, "VerifySigSecureInfo"},
+ {0x08140042, nullptr, "SecureInfoGetData"},
+ {0x08150042, nullptr, "SecureInfoGetSignature"},
+ {0x08160000, nullptr, "SecureInfoGetRegion"},
+ {0x08170000, nullptr, "SecureInfoGetByte101"},
+ {0x08180042, nullptr, "SecureInfoGetSerialNo"},
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg_i.h
new file mode 100644
index 000000000..fe343c968
--- /dev/null
+++ b/src/core/hle/service/cfg_i.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_I
+
+namespace CFG_I {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "cfg:i";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp
index 822b0e2b8..2e9d7bf21 100644
--- a/src/core/hle/service/cfg_u.cpp
+++ b/src/core/hle/service/cfg_u.cpp
@@ -11,6 +11,94 @@
namespace CFG_U {
+// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
+#define C(code) ((code)[0] | ((code)[1] << 8))
+
+static const std::array<u16, 187> country_codes = {
+ 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
+ C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
+ C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
+ C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
+ C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
+ C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
+ C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
+ 0, 0, 0, 0, 0, 0, 0, 0, // 56-63
+ C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
+ C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
+ C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
+ C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
+ C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
+ C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
+ C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
+ C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
+ C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
+ C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
+ C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
+ C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
+ C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167
+ C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175
+ C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183
+ C("SM"), C("VA"), C("BM") // 184-186
+};
+
+#undef C
+
+/**
+ * CFG_User::GetCountryCodeString service function
+ * Inputs:
+ * 1 : Country Code ID
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Country's 2-char string
+ */
+static void GetCountryCodeString(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 country_code_id = cmd_buffer[1];
+
+ if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
+ LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
+ cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
+ return;
+ }
+
+ cmd_buffer[1] = 0;
+ cmd_buffer[2] = country_codes[country_code_id];
+}
+
+/**
+ * CFG_User::GetCountryCodeID service function
+ * Inputs:
+ * 1 : Country Code 2-char string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Country Code ID
+ */
+static void GetCountryCodeID(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u16 country_code = cmd_buffer[1];
+ u16 country_code_id = 0;
+
+ // The following algorithm will fail if the first country code isn't 0.
+ _dbg_assert_(Service_CFG, country_codes[0] == 0);
+
+ for (size_t id = 0; id < country_codes.size(); ++id) {
+ if (country_codes[id] == country_code) {
+ country_code_id = id;
+ break;
+ }
+ }
+
+ if (0 == country_code_id) {
+ LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8);
+ cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
+ cmd_buffer[2] = 0xFFFF;
+ return;
+ }
+
+ cmd_buffer[1] = 0;
+ cmd_buffer[2] = country_code_id;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010082, nullptr, "GetConfigInfoBlk2"},
{0x00020000, nullptr, "SecureInfoGetRegion"},
@@ -20,8 +108,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00060000, nullptr, "GetModelNintendo2DS"},
{0x00070040, nullptr, "unknown"},
{0x00080080, nullptr, "unknown"},
- {0x00090080, nullptr, "GetCountryCodeString"},
- {0x000A0040, nullptr, "GetCountryCodeID"},
+ {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
+ {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg_u.h
index 7525bd7c6..8075d19a8 100644
--- a/src/core/hle/service/cfg_u.h
+++ b/src/core/hle/service/cfg_u.h
@@ -19,7 +19,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "cfg:u";
}
};
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
new file mode 100644
index 000000000..6e59a9bf3
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -0,0 +1,39 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/csnd_snd.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CSND_SND
+
+namespace CSND_SND {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010140, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030040, nullptr, "Unknown"},
+ {0x00040080, nullptr, "Unknown"},
+ {0x00050000, nullptr, "Unknown"},
+ {0x00060000, nullptr, "Unknown"},
+ {0x00070000, nullptr, "Unknown"},
+ {0x00080040, nullptr, "Unknown"},
+ {0x00090082, nullptr, "FlushDCache"},
+ {0x000A0082, nullptr, "StoreDCache"},
+ {0x000B0082, nullptr, "InvalidateDCache"},
+ {0x000C0000, nullptr, "Unknown"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
new file mode 100644
index 000000000..31cc85b07
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CSND_SND
+
+namespace CSND_SND {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "csnd:SND";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 9e84ac938..bd82063c6 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -4,6 +4,7 @@
#include "common/log.h"
#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/dsp_dsp.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,38 +12,181 @@
namespace DSP_DSP {
+static u32 read_pipe_count;
+static Handle semaphore_event;
+static Handle interrupt_event;
+
+/**
+ * DSP_DSP::ConvertProcessAddressFromDspDram service function
+ * Inputs:
+ * 1 : Address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
+ */
+void ConvertProcessAddressFromDspDram(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 addr = cmd_buff[1];
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr);
+}
+
+/**
+ * DSP_DSP::LoadComponent service function
+ * Inputs:
+ * 1 : Size
+ * 2 : Unknown (observed only half word used)
+ * 3 : Unknown (observed only half word used)
+ * 4 : (size << 4) | 0xA
+ * 5 : Buffer address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Component loaded, 0 on not loaded, 1 on loaded
+ */
+void LoadComponent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
+
+ // TODO(bunnei): Implement real DSP firmware loading
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::GetSemaphoreEventHandle service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Semaphore event handle
+ */
+void GetSemaphoreEventHandle(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = semaphore_event; // Event handle
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::RegisterInterruptEvents service function
+ * Inputs:
+ * 1 : Parameter 0 (purpose unknown)
+ * 2 : Parameter 1 (purpose unknown)
+ * 4 : Interrupt event handle
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void RegisterInterruptEvents(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ interrupt_event = static_cast<Handle>(cmd_buff[4]);
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::WriteReg0x10 service function
+ * Inputs:
+ * 1 : Unknown (observed only half word used)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void WriteReg0x10(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ Kernel::SignalEvent(interrupt_event);
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::ReadPipeIfPossible service function
+ * Inputs:
+ * 1 : Unknown
+ * 2 : Unknown
+ * 3 : Size in bytes of read (observed only lower half word used)
+ * 0x41 : Virtual address to read from DSP pipe to in memory
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Number of bytes read from pipe
+ */
+void ReadPipeIfPossible(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
+ VAddr addr = cmd_buff[0x41];
+
+ // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
+ // TODO: Remove this hack :)
+ static const std::array<u16, 16> canned_read_pipe = {
+ 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
+ 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
+ };
+
+ u32 initial_size = read_pipe_count;
+
+ for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
+ if (read_pipe_count < canned_read_pipe.size()) {
+ Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
+ read_pipe_count++;
+ } else {
+ LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
+ break;
+ }
+ }
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr);
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, nullptr, "RecvData"},
- {0x00020040, nullptr, "RecvDataIsReady"},
- {0x00030080, nullptr, "SendData"},
- {0x00040040, nullptr, "SendDataIsEmpty"},
- {0x00070040, nullptr, "WriteReg0x10"},
- {0x00080000, nullptr, "GetSemaphore"},
- {0x00090040, nullptr, "ClearSemaphore"},
- {0x000B0000, nullptr, "CheckSemaphoreRequest"},
- {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"},
- {0x000D0082, nullptr, "WriteProcessPipe"},
- {0x001000C0, nullptr, "ReadPipeIfPossible"},
- {0x001100C2, nullptr, "LoadComponent"},
- {0x00120000, nullptr, "UnloadComponent"},
- {0x00130082, nullptr, "FlushDataCache"},
- {0x00140082, nullptr, "InvalidateDCache "},
- {0x00150082, nullptr, "RegisterInterruptEvents"},
- {0x00160000, nullptr, "GetSemaphoreEventHandle"},
- {0x00170040, nullptr, "SetSemaphoreMask"},
- {0x00180040, nullptr, "GetPhysicalAddress"},
- {0x00190040, nullptr, "GetVirtualAddress" },
- {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
- {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
- {0x001C0082, nullptr, "SetIirFilterEQ"},
- {0x001F0000, nullptr, "GetHeadphoneStatus"},
- {0x00210000, nullptr, "GetIsDspOccupied"},
+ {0x00010040, nullptr, "RecvData"},
+ {0x00020040, nullptr, "RecvDataIsReady"},
+ {0x00030080, nullptr, "SendData"},
+ {0x00040040, nullptr, "SendDataIsEmpty"},
+ {0x00070040, WriteReg0x10, "WriteReg0x10"},
+ {0x00080000, nullptr, "GetSemaphore"},
+ {0x00090040, nullptr, "ClearSemaphore"},
+ {0x000B0000, nullptr, "CheckSemaphoreRequest"},
+ {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
+ {0x000D0082, nullptr, "WriteProcessPipe"},
+ {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
+ {0x001100C2, LoadComponent, "LoadComponent"},
+ {0x00120000, nullptr, "UnloadComponent"},
+ {0x00130082, nullptr, "FlushDataCache"},
+ {0x00140082, nullptr, "InvalidateDCache"},
+ {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"},
+ {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
+ {0x00170040, nullptr, "SetSemaphoreMask"},
+ {0x00180040, nullptr, "GetPhysicalAddress"},
+ {0x00190040, nullptr, "GetVirtualAddress"},
+ {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
+ {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
+ {0x001C0082, nullptr, "SetIirFilterEQ"},
+ {0x001F0000, nullptr, "GetHeadphoneStatus"},
+ {0x00210000, nullptr, "GetIsDspOccupied"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
+ semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
+ interrupt_event = 0;
+ read_pipe_count = 0;
+
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index c439ed266..9431b62f6 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -19,8 +19,8 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
- return "dsp:DSP";
+ std::string GetPortName() const override {
+ return "dsp::DSP";
}
};
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 917b2f8ca..785c351e9 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -20,8 +20,8 @@ namespace ERR_F {
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
-
+
Interface::~Interface() {
}
-
+
} // namespace
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h
index 5da663267..6d7141c1b 100644
--- a/src/core/hle/service/err_f.h
+++ b/src/core/hle/service/err_f.h
@@ -19,9 +19,9 @@ namespace ERR_F {
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "err:f";
}
};
-
+
} // namespace
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp
new file mode 100644
index 000000000..58023e536
--- /dev/null
+++ b/src/core/hle/service/frd_u.cpp
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/frd_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_U
+
+namespace FRD_U {
+
+ const Interface::FunctionInfo FunctionTable[] = {
+ {0x00050000, nullptr, "GetFriendKey"},
+ {0x00080000, nullptr, "GetMyPresence"},
+ {0x00100040, nullptr, "GetPassword"},
+ {0x00190042, nullptr, "GetFriendFavoriteGame"},
+ {0x001A00C4, nullptr, "GetFriendInfo"},
+ {0x001B0080, nullptr, "IsOnFriendList"},
+ {0x001C0042, nullptr, "DecodeLocalFriendCode"},
+ {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
+ {0x00320042, nullptr, "SetClientSdkVersion"}
+ };
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Interface class
+
+ Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ }
+
+ Interface::~Interface() {
+ }
+
+} // namespace
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h
new file mode 100644
index 000000000..4020c6664
--- /dev/null
+++ b/src/core/hle/service/frd_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_U
+
+namespace FRD_U {
+
+ class Interface : public Service::Interface {
+ public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "frd:u";
+ }
+ };
+
+} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
new file mode 100644
index 000000000..9c3834733
--- /dev/null
+++ b/src/core/hle/service/fs/archive.cpp
@@ -0,0 +1,431 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "common/math_util.h"
+
+#include "core/file_sys/archive_savedata.h"
+#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/kernel/session.h"
+#include "core/hle/result.h"
+
+// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
+// Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
+namespace std {
+ template <>
+ struct hash<Service::FS::ArchiveIdCode> {
+ typedef Service::FS::ArchiveIdCode argument_type;
+ typedef std::size_t result_type;
+
+ result_type operator()(const argument_type& id_code) const {
+ typedef std::underlying_type<argument_type>::type Type;
+ return std::hash<Type>()(static_cast<Type>(id_code));
+ }
+ };
+}
+
+namespace Service {
+namespace FS {
+
+// Command to access archive file
+enum class FileCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ OpenSubFile = 0x08010100,
+ Read = 0x080200C2,
+ Write = 0x08030102,
+ GetSize = 0x08040000,
+ SetSize = 0x08050080,
+ GetAttributes = 0x08060000,
+ SetAttributes = 0x08070040,
+ Close = 0x08080000,
+ Flush = 0x08090000,
+};
+
+// Command to access directory
+enum class DirectoryCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ Read = 0x08010042,
+ Close = 0x08020000,
+};
+
+class Archive {
+public:
+ Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code)
+ : backend(std::move(backend)), id_code(id_code) {
+ }
+
+ std::string GetName() const { return "Archive: " + backend->GetName(); }
+
+ ArchiveIdCode id_code; ///< Id code of the archive
+ std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface
+};
+
+class File : public Kernel::Session {
+public:
+ File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
+ : backend(std::move(backend)), path(path) {
+ }
+
+ std::string GetName() const override { return "Path: " + path.DebugStr(); }
+
+ FileSys::Path path; ///< Path of the file
+ std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
+
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from file...
+ case FileCommand::Read:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 address = cmd_buff[5];
+ LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
+ GetTypeName().c_str(), GetName().c_str(), offset, length, address);
+ cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
+ break;
+ }
+
+ // Write to file...
+ case FileCommand::Write:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 flush = cmd_buff[4];
+ u32 address = cmd_buff[6];
+ LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
+ GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
+ cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
+ break;
+ }
+
+ case FileCommand::GetSize:
+ {
+ LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
+ u64 size = backend->GetSize();
+ cmd_buff[2] = (u32)size;
+ cmd_buff[3] = size >> 32;
+ break;
+ }
+
+ case FileCommand::SetSize:
+ {
+ u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
+ LOG_TRACE(Service_FS, "SetSize %s %s size=%llu",
+ GetTypeName().c_str(), GetName().c_str(), size);
+ backend->SetSize(size);
+ break;
+ }
+
+ case FileCommand::Close:
+ {
+ LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
+ Kernel::g_object_pool.Destroy<File>(GetHandle());
+ break;
+ }
+
+ case FileCommand::Flush:
+ {
+ LOG_TRACE(Service_FS, "Flush");
+ backend->Flush();
+ break;
+ }
+
+ // Unknown command...
+ default:
+ LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
+ ResultCode error = UnimplementedFunction(ErrorModule::FS);
+ cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
+ return error;
+ }
+ cmd_buff[1] = 0; // No error
+ return MakeResult<bool>(false);
+ }
+};
+
+class Directory : public Kernel::Session {
+public:
+ Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path)
+ : backend(std::move(backend)), path(path) {
+ }
+
+ std::string GetName() const override { return "Directory: " + path.DebugStr(); }
+
+ FileSys::Path path; ///< Path of the directory
+ std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
+
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from directory...
+ case DirectoryCommand::Read:
+ {
+ u32 count = cmd_buff[1];
+ u32 address = cmd_buff[3];
+ auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
+ LOG_TRACE(Service_FS, "Read %s %s: count=%d",
+ GetTypeName().c_str(), GetName().c_str(), count);
+
+ // Number of entries actually read
+ cmd_buff[2] = backend->Read(count, entries);
+ break;
+ }
+
+ case DirectoryCommand::Close:
+ {
+ LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
+ Kernel::g_object_pool.Destroy<Directory>(GetHandle());
+ break;
+ }
+
+ // Unknown command...
+ default:
+ LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
+ ResultCode error = UnimplementedFunction(ErrorModule::FS);
+ cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
+ return MakeResult<bool>(false);
+ }
+ cmd_buff[1] = 0; // No error
+ return MakeResult<bool>(false);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Map of registered archives, identified by id code. Once an archive is registered here, it is
+ * never removed until the FS service is shut down.
+ */
+static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map;
+
+/**
+ * Map of active archive handles. Values are pointers to the archives in `idcode_map`.
+ */
+static std::unordered_map<ArchiveHandle, Archive*> handle_map;
+static ArchiveHandle next_handle;
+
+static Archive* GetArchive(ArchiveHandle handle) {
+ auto itr = handle_map.find(handle);
+ return (itr == handle_map.end()) ? nullptr : itr->second;
+}
+
+ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
+ LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code);
+
+ auto itr = id_code_map.find(id_code);
+ if (itr == id_code_map.end()) {
+ if (id_code == ArchiveIdCode::SaveData) {
+ // 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.
+ // Returning the NotFormatted error code will signal the game to provision the SaveData archive
+ // with the files and folders that it expects.
+ // The FormatSaveData service call will create the SaveData archive when it is called.
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+ // TODO: Verify error against hardware
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Permanent);
+ }
+
+ // This should never even happen in the first place with 64-bit handles,
+ while (handle_map.count(next_handle) != 0) {
+ ++next_handle;
+ }
+ handle_map.emplace(next_handle, itr->second.get());
+ return MakeResult<ArchiveHandle>(next_handle++);
+}
+
+ResultCode CloseArchive(ArchiveHandle handle) {
+ if (handle_map.erase(handle) == 0)
+ return InvalidHandle(ErrorModule::FS);
+ else
+ return RESULT_SUCCESS;
+}
+
+// TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in
+// http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22
+ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) {
+ auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code));
+
+ bool inserted = result.second;
+ _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code");
+
+ auto& archive = result.first->second;
+ LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code);
+ return RESULT_SUCCESS;
+}
+
+ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
+ if (backend == nullptr) {
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+ }
+
+ auto file = std::make_unique<File>(std::move(backend), path);
+ Handle handle = Kernel::g_object_pool.Create(file.release());
+ return MakeResult<Handle>(handle);
+}
+
+ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->DeleteFile(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
+ Archive* src_archive = GetArchive(src_archive_handle);
+ Archive* dest_archive = GetArchive(dest_archive_handle);
+ if (src_archive == nullptr || dest_archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (src_archive == dest_archive) {
+ if (src_archive->backend->RenameFile(src_path, dest_path))
+ return RESULT_SUCCESS;
+ } else {
+ // TODO: Implement renaming across archives
+ return UnimplementedFunction(ErrorModule::FS);
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->DeleteDirectory(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->CreateDirectory(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
+ Archive* src_archive = GetArchive(src_archive_handle);
+ Archive* dest_archive = GetArchive(dest_archive_handle);
+ if (src_archive == nullptr || dest_archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (src_archive == dest_archive) {
+ if (src_archive->backend->RenameDirectory(src_path, dest_path))
+ return RESULT_SUCCESS;
+ } else {
+ // TODO: Implement renaming across archives
+ return UnimplementedFunction(ErrorModule::FS);
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+/**
+ * Open a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Opened Directory object
+ */
+ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
+ if (backend == nullptr) {
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Permanent);
+ }
+
+ auto directory = std::make_unique<Directory>(std::move(backend), path);
+ Handle handle = Kernel::g_object_pool.Create(directory.release());
+ return MakeResult<Handle>(handle);
+}
+
+ResultCode FormatSaveData() {
+ // TODO(Subv): Actually wipe the savedata folder after creating or opening it
+
+ // Do not create the archive again if it already exists
+ if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
+
+ // Create the SaveData archive
+ std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
+ auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
+ Kernel::g_program_id);
+
+ if (savedata_archive->Initialize()) {
+ CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
+ return RESULT_SUCCESS;
+ } else {
+ LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
+ savedata_archive->GetMountPoint().c_str());
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
+ }
+}
+
+/// Initialize archives
+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.
+
+ std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
+ auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
+ if (sdmc_archive->Initialize())
+ CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
+ else
+ LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
+}
+
+/// Shutdown archives
+void ArchiveShutdown() {
+ handle_map.clear();
+ id_code_map.clear();
+}
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
new file mode 100644
index 000000000..a128276b6
--- /dev/null
+++ b/src/core/hle/service/fs/archive.h
@@ -0,0 +1,125 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive_backend.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
+
+namespace Service {
+namespace FS {
+
+/// Supported archive types
+enum class ArchiveIdCode : u32 {
+ RomFS = 0x00000003,
+ SaveData = 0x00000004,
+ ExtSaveData = 0x00000006,
+ SharedExtSaveData = 0x00000007,
+ SystemSaveData = 0x00000008,
+ SDMC = 0x00000009,
+ SDMCWriteOnly = 0x0000000A,
+};
+
+typedef u64 ArchiveHandle;
+
+/**
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to the opened archive
+ */
+ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code);
+
+/**
+ * Closes an archive
+ * @param id_code IdCode of the archive to open
+ */
+ResultCode CloseArchive(ArchiveHandle handle);
+
+/**
+ * Creates an Archive
+ * @param backend File system backend interface to the archive
+ * @param id_code Id code used to access this type of archive
+ */
+ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code);
+
+/**
+ * Open a File from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the File inside of the Archive
+ * @param mode Mode under which to open the File
+ * @return Handle to the opened File object
+ */
+ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
+
+/**
+ * Delete a File from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the File inside of the Archive
+ * @return Whether deletion succeeded
+ */
+ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Rename a File between two Archives
+ * @param src_archive_handle Handle to the source Archive object
+ * @param src_path Path to the File inside of the source Archive
+ * @param dest_archive_handle Handle to the destination Archive object
+ * @param dest_path Path to the File inside of the destination Archive
+ * @return Whether rename succeeded
+ */
+ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
+
+/**
+ * Delete a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Whether deletion succeeded
+ */
+ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Create a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Whether creation of directory succeeded
+ */
+ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Rename a Directory between two Archives
+ * @param src_archive_handle Handle to the source Archive object
+ * @param src_path Path to the Directory inside of the source Archive
+ * @param dest_archive_handle Handle to the destination Archive object
+ * @param dest_path Path to the Directory inside of the destination Archive
+ * @return Whether rename succeeded
+ */
+ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
+
+/**
+ * Open a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Handle to the opened File object
+ */
+ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Creates a blank SaveData archive.
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode FormatSaveData();
+
+/// Initialize archives
+void ArchiveInit();
+
+/// Shutdown archives
+void ArchiveShutdown();
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
new file mode 100644
index 000000000..f99d84b2f
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -0,0 +1,529 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common.h"
+#include "common/file_util.h"
+#include "common/scope_exit.h"
+#include "common/string_util.h"
+#include "core/hle/result.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/fs/fs_user.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FS_User
+
+namespace Service {
+namespace FS {
+
+static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) {
+ return (u64)low_word | ((u64)high_word << 32);
+}
+
+static void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // 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");
+}
+
+/**
+ * FS_User::OpenFile service function
+ * Inputs:
+ * 1 : Transaction
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Low path type
+ * 5 : Low path size
+ * 6 : Open flags
+ * 7 : Attributes
+ * 8 : (LowPathSize << 14) | 2
+ * 9 : Low path data pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : File handle
+ */
+static void OpenFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 filename_size = cmd_buff[5];
+ FileSys::Mode mode; mode.hex = cmd_buff[6];
+ u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
+ u32 filename_ptr = cmd_buff[9];
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes);
+
+ ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
+ }
+}
+
+/**
+ * FS_User::OpenFileDirectly service function
+ * Inputs:
+ * 1 : Transaction
+ * 2 : Archive ID
+ * 3 : Archive low path type
+ * 4 : Archive low path size
+ * 5 : File low path type
+ * 6 : File low path size
+ * 7 : Flags
+ * 8 : Attributes
+ * 9 : (ArchiveLowPathSize << 14) | 0x802
+ * 10 : Archive low path
+ * 11 : (FileLowPathSize << 14) | 2
+ * 12 : File low path
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : File handle
+ */
+static void OpenFileDirectly(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
+ u32 archivename_size = cmd_buff[4];
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]);
+ u32 filename_size = cmd_buff[6];
+ FileSys::Mode mode; mode.hex = cmd_buff[7];
+ u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
+ u32 archivename_ptr = cmd_buff[10];
+ u32 filename_ptr = cmd_buff[12];
+ 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);
+
+ if (archive_path.GetType() != FileSys::Empty) {
+ LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
+ cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ cmd_buff[3] = 0;
+ return;
+ }
+
+ ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id);
+ if (archive_handle.Failed()) {
+ LOG_ERROR(Service_FS, "failed to get a handle for archive");
+ cmd_buff[1] = archive_handle.Code().raw;
+ cmd_buff[3] = 0;
+ return;
+ }
+ SCOPE_EXIT({ CloseArchive(*archive_handle); });
+
+ ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
+ }
+}
+
+/*
+ * FS_User::DeleteFile service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : File path string type
+ * 5 : File path string size
+ * 7 : File path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 filename_size = cmd_buff[5];
+ u32 filename_ptr = cmd_buff[7];
+
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
+ filename_type, filename_size, file_path.DebugStr().c_str());
+
+ cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw;
+}
+
+/*
+ * FS_User::RenameFile service function
+ * Inputs:
+ * 2 : Source archive handle lower word
+ * 3 : Source archive handle upper word
+ * 4 : Source file path type
+ * 5 : Source file path size
+ * 6 : Dest archive handle lower word
+ * 7 : Dest archive handle upper word
+ * 8 : Dest file path type
+ * 9 : Dest file path size
+ * 11: Source file path string data
+ * 13: Dest file path string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void RenameFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 src_filename_size = cmd_buff[5];
+ ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);;
+ auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
+ u32 dest_filename_size = cmd_buff[9];
+ u32 src_filename_ptr = cmd_buff[11];
+ u32 dest_filename_ptr = cmd_buff[13];
+
+ FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr);
+ FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr);
+
+ LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
+ src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(),
+ dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str());
+
+ cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw;
+}
+
+/*
+ * FS_User::DeleteDirectory service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Directory path string type
+ * 5 : Directory path string size
+ * 7 : Directory path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 dirname_size = cmd_buff[5];
+ u32 dirname_ptr = cmd_buff[7];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
+ dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw;
+}
+
+/*
+ * FS_User::CreateDirectory service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Directory path string type
+ * 5 : Directory path string size
+ * 8 : Directory path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CreateDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 dirname_size = cmd_buff[5];
+ u32 dirname_ptr = cmd_buff[8];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw;
+}
+
+/*
+ * FS_User::RenameDirectory service function
+ * Inputs:
+ * 2 : Source archive handle lower word
+ * 3 : Source archive handle upper word
+ * 4 : Source dir path type
+ * 5 : Source dir path size
+ * 6 : Dest archive handle lower word
+ * 7 : Dest archive handle upper word
+ * 8 : Dest dir path type
+ * 9 : Dest dir path size
+ * 11: Source dir path string data
+ * 13: Dest dir path string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void RenameDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 src_dirname_size = cmd_buff[5];
+ ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);
+ auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
+ u32 dest_dirname_size = cmd_buff[9];
+ u32 src_dirname_ptr = cmd_buff[11];
+ u32 dest_dirname_ptr = cmd_buff[13];
+
+ FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr);
+ FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
+ src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(),
+ dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw;
+}
+
+/**
+ * FS_User::OpenDirectory service function
+ * Inputs:
+ * 1 : Archive handle low word
+ * 2 : Archive handle high word
+ * 3 : Low path type
+ * 4 : Low path size
+ * 7 : (LowPathSize << 14) | 2
+ * 8 : Low path data pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Directory handle
+ */
+static void OpenDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
+ u32 dirname_size = cmd_buff[4];
+ u32 dirname_ptr = cmd_buff[6];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ LOG_ERROR(Service_FS, "failed to get a handle for directory");
+ }
+}
+
+/**
+ * FS_User::OpenArchive service function
+ * Inputs:
+ * 1 : Archive ID
+ * 2 : Archive low path type
+ * 3 : Archive low path size
+ * 4 : (LowPathSize << 14) | 2
+ * 5 : Archive low path
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Archive handle lower word (unused)
+ * 3 : Archive handle upper word (same as file handle)
+ */
+static void OpenArchive(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
+ u32 archivename_size = cmd_buff[3];
+ 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());
+
+ if (archive_path.GetType() != FileSys::Empty) {
+ LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
+ cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ return;
+ }
+
+ ResultVal<ArchiveHandle> handle = OpenArchive(archive_id);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[2] = *handle & 0xFFFFFFFF;
+ cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF;
+ } else {
+ cmd_buff[2] = cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for archive");
+ }
+}
+
+/**
+ * FS_User::CloseArchive service function
+ * Inputs:
+ * 0 : 0x080E0080
+ * 1 : Archive handle low word
+ * 2 : Archive handle high word
+ * Outputs:
+ * 0 : ??? TODO(yuriks): Verify return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CloseArchive(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ cmd_buff[1] = CloseArchive(archive_handle).raw;
+}
+
+/*
+* FS_User::IsSdmcDetected service function
+* Outputs:
+* 1 : Result of function, 0 on success, otherwise error code
+* 2 : Whether the Sdmc could be detected
+*/
+static void IsSdmcDetected(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
+
+ LOG_DEBUG(Service_FS, "called");
+}
+
+/**
+ * FS_User::FormatSaveData service function
+ * Inputs:
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void FormatSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ LOG_DEBUG(Service_FS, "(STUBBED)");
+
+ // TODO(Subv): Find out what the inputs and outputs of this function are
+
+ cmd_buff[1] = FormatSaveData().raw;
+}
+
+/**
+ * FS_User::FormatThisUserSaveData service function
+ * Inputs:
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void FormatThisUserSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ LOG_DEBUG(Service_FS, "(STUBBED)");
+
+ // TODO(Subv): Find out what the inputs and outputs of this function are
+
+ cmd_buff[1] = FormatSaveData().raw;
+}
+
+const FSUserInterface::FunctionInfo FunctionTable[] = {
+ {0x000100C6, nullptr, "Dummy1"},
+ {0x040100C4, nullptr, "Control"},
+ {0x08010002, Initialize, "Initialize"},
+ {0x080201C2, OpenFile, "OpenFile"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
+ {0x08040142, DeleteFile, "DeleteFile"},
+ {0x08050244, RenameFile, "RenameFile"},
+ {0x08060142, DeleteDirectory, "DeleteDirectory"},
+ {0x08070142, nullptr, "DeleteDirectoryRecursively"},
+ {0x08080202, nullptr, "CreateFile"},
+ {0x08090182, CreateDirectory, "CreateDirectory"},
+ {0x080A0244, RenameDirectory, "RenameDirectory"},
+ {0x080B0102, OpenDirectory, "OpenDirectory"},
+ {0x080C00C2, OpenArchive, "OpenArchive"},
+ {0x080D0144, nullptr, "ControlArchive"},
+ {0x080E0080, CloseArchive, "CloseArchive"},
+ {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
+ {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08110040, nullptr, "DeleteSystemSaveData"},
+ {0x08120080, nullptr, "GetFreeBytes"},
+ {0x08130000, nullptr, "GetCardType"},
+ {0x08140000, nullptr, "GetSdmcArchiveResource"},
+ {0x08150000, nullptr, "GetNandArchiveResource"},
+ {0x08160000, nullptr, "GetSdmcFatfsErro"},
+ {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
+ {0x08180000, nullptr, "IsSdmcWritable"},
+ {0x08190042, nullptr, "GetSdmcCid"},
+ {0x081A0042, nullptr, "GetNandCid"},
+ {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
+ {0x081C0000, nullptr, "GetNandSpeedInfo"},
+ {0x081D0042, nullptr, "GetSdmcLog"},
+ {0x081E0042, nullptr, "GetNandLog"},
+ {0x081F0000, nullptr, "ClearSdmcLog"},
+ {0x08200000, nullptr, "ClearNandLog"},
+ {0x08210000, nullptr, "CardSlotIsInserted"},
+ {0x08220000, nullptr, "CardSlotPowerOn"},
+ {0x08230000, nullptr, "CardSlotPowerOff"},
+ {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
+ {0x08250040, nullptr, "CardNorDirectCommand"},
+ {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
+ {0x08270082, nullptr, "CardNorDirectRead"},
+ {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
+ {0x08290082, nullptr, "CardNorDirectWrite"},
+ {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
+ {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
+ {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
+ {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
+ {0x082E0040, nullptr, "GetProductInfo"},
+ {0x082F0040, nullptr, "GetProgramLaunchInfo"},
+ {0x08300182, nullptr, "CreateExtSaveData"},
+ {0x08310180, nullptr, "CreateSharedExtSaveData"},
+ {0x08320102, nullptr, "ReadExtSaveDataIcon"},
+ {0x08330082, nullptr, "EnumerateExtSaveData"},
+ {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
+ {0x08350080, nullptr, "DeleteExtSaveData"},
+ {0x08360080, nullptr, "DeleteSharedExtSaveData"},
+ {0x08370040, nullptr, "SetCardSpiBaudRate"},
+ {0x08380040, nullptr, "SetCardSpiBusMode"},
+ {0x08390000, nullptr, "SendInitializeInfoTo9"},
+ {0x083A0100, nullptr, "GetSpecialContentIndex"},
+ {0x083B00C2, nullptr, "GetLegacyRomHeader"},
+ {0x083C00C2, nullptr, "GetLegacyBannerData"},
+ {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
+ {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
+ {0x083F00C0, nullptr, "GetExtDataBlockSize"},
+ {0x08400040, nullptr, "AbnegateAccessRight"},
+ {0x08410000, nullptr, "DeleteSdmcRoot"},
+ {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
+ {0x08430000, nullptr, "InitializeCtrFileSystem"},
+ {0x08440000, nullptr, "CreateSeed"},
+ {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x08460102, nullptr, "GetLegacyRomHeader2"},
+ {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
+ {0x08480042, nullptr, "GetSdmcCtrRootPath"},
+ {0x08490040, nullptr, "GetArchiveResource"},
+ {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
+ {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
+ {0x084C0242, FormatSaveData, "FormatSaveData"},
+ {0x084D0102, nullptr, "GetLegacySubBannerData"},
+ {0x084E0342, nullptr, "UpdateSha256Context"},
+ {0x084F0102, nullptr, "ReadSpecialFile"},
+ {0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
+ {0x08610042, nullptr, "InitializeWithSdkVersion"},
+ {0x08620040, nullptr, "SetPriority"},
+ {0x08630000, nullptr, "GetPriority"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+FSUserInterface::FSUserInterface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+FSUserInterface::~FSUserInterface() {
+}
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h
new file mode 100644
index 000000000..80e3804e0
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.h
@@ -0,0 +1,33 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FS_User
+
+namespace Service {
+namespace FS {
+
+/// Interface to "fs:USER" service
+class FSUserInterface : public Service::Interface {
+public:
+
+ FSUserInterface();
+
+ ~FSUserInterface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "fs:USER";
+ }
+};
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs_user.cpp
deleted file mode 100644
index 48d806e2f..000000000
--- a/src/core/hle/service/fs_user.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common.h"
-
-#include "fs_user.h"
-#include "core/settings.h"
-#include "core/hle/kernel/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace FS_User
-
-namespace FS_User {
-
-// Command to access archive file
-enum class LowPathType : u32 {
- Invalid = 0,
- Empty = 1,
- Binary = 2,
- Char = 3,
- Wchar = 4
-};
-
-std::string GetStringFromCmdBuff(const u32 pointer, const u32 size) {
- auto data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
- return std::string(data, size - 1);
-}
-
-// We currently return 0 for success and -1 for failure in cmd_buff[1]. -1 was chosen because it
-// puts all the sections of the http://3dbrew.org/wiki/Error_codes to something non-zero, to make
-// sure we don't mislead the application into thinking something worked.
-
-void Initialize(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // 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] = 0;
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenFile(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- u32 transaction = cmd_buff[1];
- // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
- u32 size = cmd_buff[5];
- FileSys::Mode mode; mode.hex = cmd_buff[6];
- u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
- u32 pointer = cmd_buff[9];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "file LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string file_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d mode=%d attrs=%d data=%s", type, size, mode, attributes, file_name.c_str());
-
- Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenFileDirectly(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- u32 transaction = cmd_buff[1];
- auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
- LowPathType archive_type = static_cast<LowPathType>(cmd_buff[3]);
- u32 archive_size = cmd_buff[4];
- LowPathType file_type = static_cast<LowPathType>(cmd_buff[5]);
- u32 size = cmd_buff[6];
- FileSys::Mode mode; mode.hex = cmd_buff[7];
- u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
- u32 archive_pointer = cmd_buff[10];
- u32 pointer = cmd_buff[12];
-
- if (archive_type != LowPathType::Empty) {
- ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string archive_name = GetStringFromCmdBuff(archive_pointer, archive_size);
- std::string file_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "archive_type=%d archive_size=%d archive_data=%s "
- "file_type=%d file_size=%d file_mode=%d file_attrs=%d file_data=%s",
- archive_type, archive_size, archive_name.c_str(),
- file_type, size, mode, attributes, file_name.c_str());
-
- // TODO(Link Mauve): check if we should even get a handle for the archive, and don't leak it.
- Handle archive_handle = Kernel::OpenArchive(archive_id);
- if (archive_handle) {
- cmd_buff[1] = 0;
- // cmd_buff[2] isn't used according to 3dmoo's implementation.
- cmd_buff[3] = archive_handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- return;
- }
-
- if (file_type != LowPathType::Char) {
- WARN_LOG(KERNEL, "file LowPath type other than char is currently unsupported; returning archive handle instead");
- return;
- }
-
- Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-/*
- * FS_User::CreateDirectory service function
- * Inputs:
- * 2 : Archive handle lower word
- * 3 : Archive handle upper word
- * 4 : Directory path string type
- * 5 : Directory path string size
- * 8 : Directory path string data
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- */
-void CreateDirectory(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
- u32 name_size = cmd_buff[5];
- u32 name_offset = cmd_buff[8];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string dir_name = GetStringFromCmdBuff(name_offset, name_size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, name_size, dir_name.c_str());
-
- cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_name);
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenDirectory(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[2]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[3]);
- u32 size = cmd_buff[4];
- u32 pointer = cmd_buff[6];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string dir_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, dir_name.c_str());
-
- Handle handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_name);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for directory %s", dir_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenArchive(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- auto arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[2]);
- u32 size = cmd_buff[3];
- u32 pointer = cmd_buff[5];
-
- if (type != LowPathType::Empty) {
- ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string archive_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, archive_name.c_str());
-
- Handle handle = Kernel::OpenArchive(arch_id);
- if (handle) {
- cmd_buff[1] = 0;
- // cmd_buff[2] isn't used according to 3dmoo's implementation.
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-/*
-* FS_User::IsSdmcDetected service function
-* Outputs:
-* 1 : Result of function, 0 on success, otherwise error code
-* 2 : Whether the Sdmc could be detected
-*/
-void IsSdmcDetected(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- cmd_buff[1] = 0;
- cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C6, nullptr, "Dummy1"},
- {0x040100C4, nullptr, "Control"},
- {0x08010002, Initialize, "Initialize"},
- {0x080201C2, OpenFile, "OpenFile"},
- {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
- {0x08040142, nullptr, "DeleteFile"},
- {0x08050244, nullptr, "RenameFile"},
- {0x08060142, nullptr, "DeleteDirectory"},
- {0x08070142, nullptr, "DeleteDirectoryRecursively"},
- {0x08080202, nullptr, "CreateFile"},
- {0x08090182, CreateDirectory, "CreateDirectory"},
- {0x080A0244, nullptr, "RenameDirectory"},
- {0x080B0102, OpenDirectory, "OpenDirectory"},
- {0x080C00C2, OpenArchive, "OpenArchive"},
- {0x080D0144, nullptr, "ControlArchive"},
- {0x080E0080, nullptr, "CloseArchive"},
- {0x080F0180, nullptr, "FormatThisUserSaveData"},
- {0x08100200, nullptr, "CreateSystemSaveData"},
- {0x08110040, nullptr, "DeleteSystemSaveData"},
- {0x08120080, nullptr, "GetFreeBytes"},
- {0x08130000, nullptr, "GetCardType"},
- {0x08140000, nullptr, "GetSdmcArchiveResource"},
- {0x08150000, nullptr, "GetNandArchiveResource"},
- {0x08160000, nullptr, "GetSdmcFatfsErro"},
- {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
- {0x08180000, nullptr, "IsSdmcWritable"},
- {0x08190042, nullptr, "GetSdmcCid"},
- {0x081A0042, nullptr, "GetNandCid"},
- {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
- {0x081C0000, nullptr, "GetNandSpeedInfo"},
- {0x081D0042, nullptr, "GetSdmcLog"},
- {0x081E0042, nullptr, "GetNandLog"},
- {0x081F0000, nullptr, "ClearSdmcLog"},
- {0x08200000, nullptr, "ClearNandLog"},
- {0x08210000, nullptr, "CardSlotIsInserted"},
- {0x08220000, nullptr, "CardSlotPowerOn"},
- {0x08230000, nullptr, "CardSlotPowerOff"},
- {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
- {0x08250040, nullptr, "CardNorDirectCommand"},
- {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
- {0x08270082, nullptr, "CardNorDirectRead"},
- {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
- {0x08290082, nullptr, "CardNorDirectWrite"},
- {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
- {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
- {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
- {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
- {0x082E0040, nullptr, "GetProductInfo"},
- {0x082F0040, nullptr, "GetProgramLaunchInfo"},
- {0x08300182, nullptr, "CreateExtSaveData"},
- {0x08310180, nullptr, "CreateSharedExtSaveData"},
- {0x08320102, nullptr, "ReadExtSaveDataIcon"},
- {0x08330082, nullptr, "EnumerateExtSaveData"},
- {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
- {0x08350080, nullptr, "DeleteExtSaveData"},
- {0x08360080, nullptr, "DeleteSharedExtSaveData"},
- {0x08370040, nullptr, "SetCardSpiBaudRate"},
- {0x08380040, nullptr, "SetCardSpiBusMode"},
- {0x08390000, nullptr, "SendInitializeInfoTo9"},
- {0x083A0100, nullptr, "GetSpecialContentIndex"},
- {0x083B00C2, nullptr, "GetLegacyRomHeader"},
- {0x083C00C2, nullptr, "GetLegacyBannerData"},
- {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
- {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
- {0x083F00C0, nullptr, "GetExtDataBlockSize"},
- {0x08400040, nullptr, "AbnegateAccessRight"},
- {0x08410000, nullptr, "DeleteSdmcRoot"},
- {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
- {0x08430000, nullptr, "InitializeCtrFileSystem"},
- {0x08440000, nullptr, "CreateSeed"},
- {0x084500C2, nullptr, "GetFormatInfo"},
- {0x08460102, nullptr, "GetLegacyRomHeader2"},
- {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
- {0x08480042, nullptr, "GetSdmcCtrRootPath"},
- {0x08490040, nullptr, "GetArchiveResource"},
- {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
- {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
- {0x084C0242, nullptr, "FormatSaveData"},
- {0x084D0102, nullptr, "GetLegacySubBannerData"},
- {0x084E0342, nullptr, "UpdateSha256Context"},
- {0x084F0102, nullptr, "ReadSpecialFile"},
- {0x08500040, nullptr, "GetSpecialFileSize"},
- {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
- {0x08610042, nullptr, "InitializeWithSdkVersion"},
- {0x08620040, nullptr, "SetPriority"},
- {0x08630000, nullptr, "GetPriority"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
-}
-
-Interface::~Interface() {
-}
-
-} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 6119e6300..db8027142 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -28,41 +28,36 @@ u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(u32 thread_id) {
- if (0 == g_shared_memory)
- return nullptr;
-
- return Kernel::GetSharedMemoryPointer(g_shared_memory,
- 0x800 + (thread_id * sizeof(CommandBuffer)));
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer)));
+ return ptr.ValueOr(nullptr);
}
static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
- if (0 == g_shared_memory)
- return nullptr;
-
- _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
+ _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index");
// For each thread there are two FrameBufferUpdate fields
u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
- return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr));
}
/// Gets a pointer to the interrupt relay queue for a given thread index
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
- return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory,
- sizeof(InterruptRelayQueue) * thread_id);
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id);
+ return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr));
}
-void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
+static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
// TODO: Return proper error codes
if (base_address + size_in_bytes >= 0x420000) {
- ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)",
+ LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)",
base_address, size_in_bytes);
return;
}
// size should be word-aligned
if ((size_in_bytes % 4) != 0) {
- ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes);
+ LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes);
return;
}
@@ -76,8 +71,8 @@ void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
}
/// Write a GSP GPU hardware register
-void WriteHWRegs(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void WriteHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
@@ -87,20 +82,20 @@ void WriteHWRegs(Service::Interface* self) {
}
/// Read a GSP GPU hardware register
-void ReadHWRegs(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void ReadHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
// TODO: Return proper error codes
if (reg_addr + size >= 0x420000) {
- ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
+ LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
return;
}
// size should be word-aligned
if ((size % 4) != 0) {
- ERROR_LOG(GPU, "Invalid size 0x%08x", size);
+ LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size);
return;
}
@@ -115,7 +110,7 @@ void ReadHWRegs(Service::Interface* self) {
}
}
-void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
+static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
u32 base_address = 0x400000;
if (info.active_fb == 0) {
WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left);
@@ -140,8 +135,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
* Outputs:
* 1: Result code
*/
-void SetBufferSwap(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void SetBufferSwap(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 screen_id = cmd_buff[1];
FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
SetBufferSwap(screen_id, *fb_info);
@@ -159,15 +154,16 @@ void SetBufferSwap(Service::Interface* self) {
* 2 : Thread index into GSP command buffer
* 4 : Handle to GSP shared memory
*/
-void RegisterInterruptRelayQueue(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void RegisterInterruptRelayQueue(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1];
g_interrupt_event = cmd_buff[3];
g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
_assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
- cmd_buff[2] = g_thread_id++; // ThreadID
+ cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init
+ cmd_buff[2] = g_thread_id++; // Thread ID
cmd_buff[4] = g_shared_memory; // GSP shared memory
Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
@@ -177,14 +173,15 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
* Signals that the specified interrupt type has occurred to userland code
* @param interrupt_id ID of interrupt that is being signalled
* @todo This should probably take a thread_id parameter and only signal this thread?
+ * @todo This probably does not belong in the GSP module, instead move to video_core
*/
void SignalInterrupt(InterruptId interrupt_id) {
if (0 == g_interrupt_event) {
- WARN_LOG(GSP, "cannot synchronize until GSP event has been created!");
+ LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
if (0 == g_shared_memory) {
- WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!");
+ LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
return;
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -202,7 +199,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
}
/// Executes the next GSP command
-void ExecuteCommand(const Command& command, u32 thread_id) {
+static void ExecuteCommand(const Command& command, u32 thread_id) {
// Utility function to convert register ID to address
auto WriteGPURegister = [](u32 id, u32 data) {
GPU::Write<u32>(0x1EF00000 + 4 * id, data);
@@ -215,6 +212,7 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
memcpy(Memory::GetPointer(command.dma_request.dest_address),
Memory::GetPointer(command.dma_request.source_address),
command.dma_request.size);
+ SignalInterrupt(InterruptId::DMA);
break;
// ctrulib homebrew sends all relevant command list data with this command,
@@ -223,13 +221,13 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
case CommandId::SET_COMMAND_LIST_LAST:
{
auto& params = command.set_command_list_last;
+
WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3);
- WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3);
+ WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size);
// TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
- SignalInterrupt(InterruptId::P3D);
break;
}
@@ -247,6 +245,8 @@ 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;
}
@@ -260,14 +260,9 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
- // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to
- // work well enough for running demos. Need to figure out how these all work and trigger
- // them correctly.
- SignalInterrupt(InterruptId::PSC0);
+ // TODO(bunnei): Determine if these interrupts should be signalled here.
SignalInterrupt(InterruptId::PSC1);
SignalInterrupt(InterruptId::PPF);
- SignalInterrupt(InterruptId::P3D);
- SignalInterrupt(InterruptId::DMA);
// Update framebuffer information if requested
for (int screen_id = 0; screen_id < 2; ++screen_id) {
@@ -303,12 +298,14 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
}
default:
- ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value());
+ LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value());
}
}
/// This triggers handling of the GX command written to the command buffer in shared memory.
-void TriggerCmdReqQueue(Service::Interface* self) {
+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) {
@@ -325,6 +322,9 @@ void TriggerCmdReqQueue(Service::Interface* self) {
command_buffer->number_commands = command_buffer->number_commands - 1;
}
}
+
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = 0; // No error
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index 0eb32ba4a..eb2d35964 100644
--- a/src/core/hle/service/hid_user.cpp
+++ b/src/core/hle/service/hid_user.cpp
@@ -34,10 +34,7 @@ static s16 next_circle_y = 0;
* Gets a pointer to the PadData structure inside HID shared memory
*/
static inline PadData* GetPadData() {
- if (0 == shared_mem)
- return nullptr;
-
- return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0));
+ return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr));
}
/**
@@ -47,7 +44,7 @@ static inline PadData* GetPadData() {
*
* Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
*/
-void UpdateNextCirclePadState() {
+static void UpdateNextCirclePadState() {
static const s16 max_value = 0x9C;
next_circle_x = next_state.circle_left ? -max_value : 0x0;
next_circle_x += next_state.circle_right ? max_value : 0x0;
@@ -58,7 +55,7 @@ void UpdateNextCirclePadState() {
/**
* Sets a Pad state (button or button combo) as pressed
*/
-void PadButtonPress(PadState pad_state) {
+void PadButtonPress(const PadState& pad_state) {
next_state.hex |= pad_state.hex;
UpdateNextCirclePadState();
}
@@ -66,7 +63,7 @@ void PadButtonPress(PadState pad_state) {
/**
* Sets a Pad state (button or button combo) as released
*/
-void PadButtonRelease(PadState pad_state) {
+void PadButtonRelease(const PadState& pad_state) {
next_state.hex &= ~pad_state.hex;
UpdateNextCirclePadState();
}
@@ -155,8 +152,8 @@ void PadUpdateComplete() {
* 7 : Gyroscope event
* 8 : Event signaled by HID_User
*/
-void GetIPCHandles(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void GetIPCHandles(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
cmd_buff[3] = shared_mem;
@@ -166,7 +163,7 @@ void GetIPCHandles(Service::Interface* self) {
cmd_buff[7] = event_gyroscope;
cmd_buff[8] = event_debug_pad;
- DEBUG_LOG(KERNEL, "called");
+ LOG_TRACE(Service_HID, "called");
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h
index 9f6c4d5ed..8f53befdb 100644
--- a/src/core/hle/service/hid_user.h
+++ b/src/core/hle/service/hid_user.h
@@ -15,7 +15,7 @@
namespace HID_User {
-/**
+/**
* Structure of a Pad controller state.
*/
struct PadState {
@@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}};
const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
// Methods for updating the HID module's state
-void PadButtonPress(PadState pad_state);
-void PadButtonRelease(PadState pad_state);
+void PadButtonPress(const PadState& pad_state);
+void PadButtonRelease(const PadState& pad_state);
void PadUpdateComplete();
/**
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp
new file mode 100644
index 000000000..be15db231
--- /dev/null
+++ b/src/core/hle/service/ir_rst.cpp
@@ -0,0 +1,36 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ir_rst.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_RST
+
+namespace IR_RST {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010000, nullptr, "GetHandles"},
+ {0x00020080, nullptr, "Initialize"},
+ {0x00030000, nullptr, "Shutdown"},
+ {0x00040000, nullptr, "Unknown"},
+ {0x00050000, nullptr, "Unknown"},
+ {0x00060000, nullptr, "Unknown"},
+ {0x00070080, nullptr, "Unknown"},
+ {0x00080000, nullptr, "Unknown"},
+ {0x00090000, nullptr, "Unknown"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h
new file mode 100644
index 000000000..73effd7e3
--- /dev/null
+++ b/src/core/hle/service/ir_rst.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_RST
+
+namespace IR_RST {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ir:rst";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp
new file mode 100644
index 000000000..aa9db6f6d
--- /dev/null
+++ b/src/core/hle/service/ir_u.cpp
@@ -0,0 +1,45 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ir_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_U
+
+namespace IR_U {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010000, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030042, nullptr, "StartSendTransfer"},
+ {0x00040000, nullptr, "WaitSendTransfer"},
+ {0x000500C2, nullptr, "StartRecvTransfer"},
+ {0x00060000, nullptr, "WaitRecvTransfer"},
+ {0x00070080, nullptr, "GetRecvTransferCount"},
+ {0x00080000, nullptr, "GetSendState"},
+ {0x00090040, nullptr, "SetBitRate"},
+ {0x000A0000, nullptr, "GetBitRate"},
+ {0x000B0040, nullptr, "SetIRLEDState"},
+ {0x000C0000, nullptr, "GetIRLEDRecvState"},
+ {0x000D0000, nullptr, "GetSendFinishedEvent"},
+ {0x000E0000, nullptr, "GetRecvFinishedEvent"},
+ {0x000F0000, nullptr, "GetTransferState"},
+ {0x00100000, nullptr, "GetErrorStatus"},
+ {0x00110040, nullptr, "SetSleepModeActive"},
+ {0x00120040, nullptr, "SetSleepModeState"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h
new file mode 100644
index 000000000..86d98d079
--- /dev/null
+++ b/src/core/hle/service/ir_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_U
+
+namespace IR_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ir:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
new file mode 100644
index 000000000..91b1a6fc5
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ldr_ro.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C2, nullptr, "Initialize"},
+ {0x00020082, nullptr, "CRR_Load"},
+ {0x00030042, nullptr, "CRR_Unload"},
+ {0x000402C2, nullptr, "CRO_LoadAndFix"},
+ {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"}
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h
new file mode 100644
index 000000000..32d7c29cf
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ldr:ro";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 58051f133..d6f30e9ae 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000D0040, nullptr, "SetClamp"},
{0x000E0000, nullptr, "GetClamp"},
{0x000F0040, nullptr, "unknown_input1"},
- {0x00100040, nullptr, "unknown_input2"},
+ {0x00100040, nullptr, "unknown_input2"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index 72ba048ef..2a495f3a9 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "mic:u";
}
};
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp
new file mode 100644
index 000000000..04c1e0cf3
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.cpp
@@ -0,0 +1,31 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/nim_aoc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NIM_AOC
+
+namespace NIM_AOC {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00030042, nullptr, "SetApplicationId"},
+ {0x00040042, nullptr, "SetTin"},
+ {0x000902D0, nullptr, "ListContentSetsEx"},
+ {0x00180000, nullptr, "GetBalance"},
+ {0x001D0000, nullptr, "GetCustomerSupportCode"},
+ {0x00210000, nullptr, "Initialize"},
+ {0x00240282, nullptr, "CalculateContentsRequiredSize"},
+ {0x00250000, nullptr, "RefreshServerTime"},
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h
new file mode 100644
index 000000000..2cc673118
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NIM_AOC
+
+namespace NIM_AOC {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "nim:aoc";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h
index a956ca812..69d2c2002 100644
--- a/src/core/hle/service/nwm_uds.h
+++ b/src/core/hle/service/nwm_uds.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "nwm:UDS";
}
};
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
new file mode 100644
index 000000000..90e9b1bfa
--- /dev/null
+++ b/src/core/hle/service/pm_app.cpp
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/pm_app.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PM_APP
+
+namespace PM_APP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010140, nullptr, "LaunchTitle"},
+ {0x00020082, nullptr, "LaunchFIRMSetParams"},
+ {0x00030080, nullptr, "TerminateProcesse"},
+ {0x00040100, nullptr, "TerminateProcessTID"},
+ {0x000500C0, nullptr, "TerminateProcessTID_unknown"},
+ {0x00070042, nullptr, "GetFIRMLaunchParams"},
+ {0x00080100, nullptr, "GetTitleExheaderFlags"},
+ {0x00090042, nullptr, "SetFIRMLaunchParams"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h
new file mode 100644
index 000000000..28c38f582
--- /dev/null
+++ b/src/core/hle/service/pm_app.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PM_APP
+
+namespace PM_APP {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "pm:app";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index f6a14d509..b8c0f6da8 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -11,19 +11,105 @@
namespace PTM_U {
+/// Charge levels used by PTM functions
+enum class ChargeLevels : u32 {
+ CriticalBattery = 1,
+ LowBattery = 2,
+ HalfFull = 3,
+ MostlyFull = 4,
+ CompletelyFull = 5,
+};
+
+static bool shell_open = true;
+
+static bool battery_is_charging = true;
+
+/**
+ * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
+ * it is likely to just be a duplicate function of GetBatteryChargeState
+ * that controls another part of the HW.
+ * PTM_U::GetAdapterState service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, 0 = not charging, 1 = charging.
+ */
+static void GetAdapterState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = battery_is_charging ? 1 : 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+/*
+ * PTM_User::GetShellState service function.
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
+ */
+static void GetShellState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = shell_open ? 1 : 0;
+
+ LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
+}
+
+/**
+ * PTM_U::GetBatteryLevel service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
+ * 3 = half full battery, 2 = low battery, 1 = critical battery.
+ */
+static void GetBatteryLevel(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+/**
+ * PTM_U::GetBatteryChargeState service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, 0 = not charging, 1 = charging.
+ */
+static void GetBatteryChargeState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = battery_is_charging ? 1 : 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010002, nullptr, "RegisterAlarmClient"},
{0x00020080, nullptr, "SetRtcAlarm"},
{0x00030000, nullptr, "GetRtcAlarm"},
{0x00040000, nullptr, "CancelRtcAlarm"},
- {0x00050000, nullptr, "GetAdapterState"},
- {0x00060000, nullptr, "GetShellState "},
- {0x00070000, nullptr, "GetBatteryLevel"},
- {0x00080000, nullptr, "GetBatteryChargeState"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
{0x00090000, nullptr, "GetPedometerState"},
{0x000A0042, nullptr, "GetStepHistoryEntry"},
- {0x000B00C2, nullptr, "GetStepHistory "},
- {0x000C0000, nullptr, "GetTotalStepCount "},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, nullptr, "GetTotalStepCount"},
{0x000D0040, nullptr, "SetPedometerRecordingMode"},
{0x000E0000, nullptr, "GetPedometerRecordingMode"},
{0x000F0084, nullptr, "GetStepHistoryAll"},
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h
index 82749fa39..f8d9f57be 100644
--- a/src/core/hle/service/ptm_u.h
+++ b/src/core/hle/service/ptm_u.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "ptm:u";
}
};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b144a77d4..2230045e3 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,16 +7,28 @@
#include "core/hle/service/service.h"
#include "core/hle/service/ac_u.h"
+#include "core/hle/service/am_app.h"
+#include "core/hle/service/am_net.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_i.h"
#include "core/hle/service/cfg_u.h"
+#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
-#include "core/hle/service/fs_user.h"
+#include "core/hle/service/fs/fs_user.h"
+#include "core/hle/service/frd_u.h"
#include "core/hle/service/gsp_gpu.h"
#include "core/hle/service/hid_user.h"
+#include "core/hle/service/ir_rst.h"
+#include "core/hle/service/ir_u.h"
+#include "core/hle/service/ldr_ro.h"
#include "core/hle/service/mic_u.h"
+#include "core/hle/service/nim_aoc.h"
#include "core/hle/service/ndm_u.h"
#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/soc_u.h"
#include "core/hle/service/srv.h"
@@ -54,7 +66,7 @@ void Manager::DeleteService(const std::string& port_name) {
/// Get a Service Interface from its Handle
Interface* Manager::FetchFromHandle(Handle handle) {
- return Kernel::g_object_pool.GetFast<Interface>(handle);
+ return Kernel::g_object_pool.Get<Interface>(handle);
}
/// Get a Service Interface from its port
@@ -73,30 +85,42 @@ Interface* Manager::FetchFromPortName(const std::string& port_name) {
/// Initialize ServiceManager
void Init() {
g_manager = new Manager;
-
+
g_manager->AddService(new SRV::Interface);
g_manager->AddService(new AC_U::Interface);
+ g_manager->AddService(new AM_APP::Interface);
+ g_manager->AddService(new AM_NET::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_U::Interface);
+ g_manager->AddService(new CSND_SND::Interface);
g_manager->AddService(new DSP_DSP::Interface);
g_manager->AddService(new ERR_F::Interface);
- g_manager->AddService(new FS_User::Interface);
+ g_manager->AddService(new FRD_U::Interface);
+ g_manager->AddService(new FS::FSUserInterface);
g_manager->AddService(new GSP_GPU::Interface);
g_manager->AddService(new HID_User::Interface);
+ g_manager->AddService(new IR_RST::Interface);
+ g_manager->AddService(new IR_U::Interface);
+ g_manager->AddService(new LDR_RO::Interface);
g_manager->AddService(new MIC_U::Interface);
+ g_manager->AddService(new NIM_AOC::Interface);
g_manager->AddService(new NDM_U::Interface);
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 SOC_U::Interface);
g_manager->AddService(new SSL_C::Interface);
- NOTICE_LOG(HLE, "initialized OK");
+ LOG_DEBUG(Service, "initialized OK");
}
/// Shutdown ServiceManager
void Shutdown() {
delete g_manager;
- NOTICE_LOG(HLE, "shutdown OK");
+ LOG_DEBUG(Service, "shutdown OK");
}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2f5a866c9..9cd906150 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -10,9 +10,11 @@
#include <string>
#include "common/common.h"
+#include "common/string_util.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/session.h"
#include "core/hle/svc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,30 +22,19 @@
namespace Service {
-static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters)
-static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
-
-/**
- * Returns a pointer to the command buffer in kernel memory
- * @param offset Optional offset into command buffer
- * @return Pointer to command buffer
- */
-inline static u32* GetCommandBuffer(const int offset=0) {
- return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
-}
+static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
class Manager;
/// Interface to a CTROS service
-class Interface : public Kernel::Object {
+class Interface : public Kernel::Session {
+ // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
+ // just something that encapsulates a session and acts as a helper to implement service
+ // processes.
+
friend class Manager;
public:
-
std::string GetName() const override { return GetPortName(); }
- std::string GetTypeName() const override { return GetPortName(); }
-
- static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
- Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; }
typedef void (*Function)(Interface*);
@@ -75,48 +66,31 @@ public:
m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
}
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = GetCommandBuffer();
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
auto itr = m_functions.find(cmd_buff[0]);
- if (itr == m_functions.end()) {
- ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X",
- GetPortName().c_str(), 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);
- // TODO(bunnei): Hack - ignore error
- u32* cmd_buff = Service::GetCommandBuffer();
- cmd_buff[1] = 0;
- return 0;
- }
- if (itr->second.func == nullptr) {
- ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
- GetPortName().c_str(), itr->second.name.c_str());
+ 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());
// TODO(bunnei): Hack - ignore error
- u32* cmd_buff = Service::GetCommandBuffer();
cmd_buff[1] = 0;
- return 0;
- }
+ return MakeResult<bool>(false);
+ }
itr->second.func(this);
- return 0; // TODO: Implement return from actual function
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "unimplemented function");
- return 0;
+ return MakeResult<bool>(false); // TODO: Implement return from actual function
}
protected:
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index e27a2b1fe..d5590a683 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -19,7 +19,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "soc:U";
}
};
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 6c02a43d9..165fd7aac 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -11,20 +11,20 @@
namespace SRV {
-Handle g_event_handle = 0;
+static Handle g_event_handle = 0;
-void Initialize(Service::Interface* self) {
- DEBUG_LOG(OSHLE, "called");
+static void Initialize(Service::Interface* self) {
+ LOG_DEBUG(Service_SRV, "called");
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
}
-void GetProcSemaphore(Service::Interface* self) {
- DEBUG_LOG(OSHLE, "called");
+static void GetProcSemaphore(Service::Interface* self) {
+ LOG_TRACE(Service_SRV, "called");
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(bunnei): Change to a semaphore once these have been implemented
g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event");
@@ -34,21 +34,21 @@ void GetProcSemaphore(Service::Interface* self) {
cmd_buff[3] = g_event_handle;
}
-void GetServiceHandle(Service::Interface* self) {
- Result res = 0;
- u32* cmd_buff = Service::GetCommandBuffer();
+static void GetServiceHandle(Service::Interface* self) {
+ ResultCode res = RESULT_SUCCESS;
+ u32* cmd_buff = Kernel::GetCommandBuffer();
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
if (nullptr != service) {
cmd_buff[3] = service->GetHandle();
- DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
+ LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
} else {
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
- res = -1;
+ LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
+ res = UnimplementedFunction(ErrorModule::SRV);
}
- cmd_buff[1] = res;
+ cmd_buff[1] = res.raw;
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2aa1303f6..47e9bf77e 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
@@ -12,10 +12,12 @@
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/function_wrappers.h"
+#include "core/hle/result.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,8 +31,8 @@ enum ControlMemoryOperation {
};
/// Map application or GSP heap memory
-Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
- DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
+static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+ LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
operation, addr0, addr1, size, permissions);
switch (operation) {
@@ -42,19 +44,19 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz
// Map GSP heap memory
case MEMORY_OPERATION_GSP_HEAP:
- *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions);
+ *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions);
break;
// Unknown ControlMemory operation
default:
- ERROR_LOG(SVC, "unknown operation=0x%08X", operation);
+ LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
}
return 0;
}
/// Maps a memory block to specified address
-Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
- DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
+static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
+ LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
@@ -67,20 +69,20 @@ Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permis
case Kernel::MemoryPermission::WriteExecute:
case Kernel::MemoryPermission::ReadWriteExecute:
case Kernel::MemoryPermission::DontCare:
- Kernel::MapSharedMemory(handle, addr, permissions_type,
+ Kernel::MapSharedMemory(handle, addr, permissions_type,
static_cast<Kernel::MemoryPermission>(other_permissions));
break;
default:
- ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions);
+ LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
}
return 0;
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-Result ConnectToPort(Handle* out, const char* port_name) {
+static Result ConnectToPort(Handle* out, const char* port_name) {
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
- DEBUG_LOG(SVC, "called port_name=%s", port_name);
+ LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
_assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
*out = service->GetHandle();
@@ -89,78 +91,80 @@ Result ConnectToPort(Handle* out, const char* port_name) {
}
/// Synchronize to an OS service
-Result SendSyncRequest(Handle handle) {
- Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+static Result SendSyncRequest(Handle handle) {
+ Kernel::Session* session = Kernel::g_object_pool.Get<Kernel::Session>(handle);
+ if (session == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
- _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
- DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
- bool wait = false;
- Result res = object->SyncRequest(&wait);
- if (wait) {
+ ResultVal<bool> wait = session->SyncRequest();
+ if (wait.Succeeded() && *wait) {
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
}
- return res;
+ return wait.Code().raw;
}
/// Close a handle
-Result CloseHandle(Handle handle) {
+static Result CloseHandle(Handle handle) {
// ImplementMe
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
return 0;
}
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
+static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool wait = false;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
+ if (!Kernel::g_object_pool.IsValid(handle)) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+ _dbg_assert_(Kernel, object != nullptr);
- DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
object->GetName().c_str(), nano_seconds);
- _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
-
- Result res = object->WaitSynchronization(&wait);
+ ResultVal<bool> wait = object->WaitSynchronization();
// Check for next thread to schedule
- if (wait) {
+ if (wait.Succeeded() && *wait) {
HLE::Reschedule(__func__);
- return 0;
}
- return res;
+ return wait.Code().raw;
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
+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
- DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
+ LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
handle_count, (wait_all ? "true" : "false"), nano_seconds);
// Iterate through each handle, synchronize kernel object
for (s32 i = 0; i < handle_count; i++) {
- bool wait = false;
+ if (!Kernel::g_object_pool.IsValid(handles[i])) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
- _assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object "
- "is nullptr!", handles[i]);
-
- DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
+ LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
object->GetName().c_str());
- Result res = object->WaitSynchronization(&wait);
+ // TODO(yuriks): Verify how the real function behaves when an error happens here
+ ResultVal<bool> wait_result = object->WaitSynchronization();
+ bool wait = wait_result.Succeeded() && *wait_result;
if (!wait && !wait_all) {
*out = i;
- return 0;
+ return RESULT_SUCCESS.raw;
} else {
unlock_all = false;
}
@@ -168,55 +172,57 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa
if (wait_all && unlock_all) {
*out = handle_count;
- return 0;
+ return RESULT_SUCCESS.raw;
}
// Check for next thread to schedule
HLE::Reschedule(__func__);
- return 0;
+ return RESULT_SUCCESS.raw;
}
/// Create an address arbiter (to allocate access to shared resources)
-Result CreateAddressArbiter(u32* arbiter) {
- DEBUG_LOG(SVC, "called");
+static Result CreateAddressArbiter(u32* arbiter) {
+ LOG_TRACE(Kernel_SVC, "called");
Handle handle = Kernel::CreateAddressArbiter();
*arbiter = handle;
return 0;
}
/// Arbitrate address
-Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
- return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address,
- value);
+static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
+ 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;
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-void OutputDebugString(const char* string) {
- OS_LOG(SVC, "%s", string);
+static void OutputDebugString(const char* string) {
+ LOG_DEBUG(Debug_Emulated, "%s", string);
}
/// Get resource limit
-Result GetResourceLimit(Handle* resource_limit, Handle process) {
+static Result GetResourceLimit(Handle* resource_limit, Handle process) {
// With regards to proceess values:
- // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
+ // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
// the current KThread.
*resource_limit = 0xDEADBEEF;
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
return 0;
}
/// Get resource limit current values
-Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
+static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
s32 name_count) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
resource_limit, names, name_count);
Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
return 0;
}
/// Creates a new thread
-Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
std::string name;
if (Symbols::HasSymbol(entry_point)) {
TSymbol symbol = Symbols::GetSymbol(entry_point);
@@ -230,18 +236,18 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
Core::g_app_core->SetReg(1, thread);
- DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
- "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
+ 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);
-
+
return 0;
}
/// Called when a thread exits
-u32 ExitThread() {
+static u32 ExitThread() {
Handle thread = Kernel::GetCurrentThreadHandle();
- DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
+ LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
Kernel::StopThread(thread, __func__);
HLE::Reschedule(__func__);
@@ -249,55 +255,73 @@ u32 ExitThread() {
}
/// Gets the priority for the specified thread
-Result GetThreadPriority(s32* priority, Handle handle) {
- *priority = Kernel::GetThreadPriority(handle);
- return 0;
+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;
}
/// Sets the priority for the specified thread
-Result SetThreadPriority(Handle handle, s32 priority) {
- return Kernel::SetThreadPriority(handle, priority);
+static Result SetThreadPriority(Handle handle, s32 priority) {
+ return Kernel::SetThreadPriority(handle, priority).raw;
}
/// Create a mutex
-Result CreateMutex(Handle* mutex, u32 initial_locked) {
+static Result CreateMutex(Handle* mutex, u32 initial_locked) {
*mutex = Kernel::CreateMutex((initial_locked != 0));
- DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X",
+ LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
initial_locked ? "true" : "false", *mutex);
return 0;
}
/// Release a mutex
-Result ReleaseMutex(Handle handle) {
- DEBUG_LOG(SVC, "called handle=0x%08X", handle);
- _assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!");
- Kernel::ReleaseMutex(handle);
- return 0;
+static Result ReleaseMutex(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
+ ResultCode res = Kernel::ReleaseMutex(handle);
+ return res.raw;
}
-/// Get current thread ID
-Result GetThreadId(u32* thread_id, Handle thread) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread);
- return 0;
+/// 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;
+}
+
+/// Creates a semaphore
+static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) {
+ ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count);
+ LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
+ initial_count, max_count, *semaphore);
+ return res.raw;
+}
+
+/// Releases a certain number of slots in a semaphore
+static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) {
+ LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore);
+ ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count);
+ return res.raw;
}
/// Query memory
-Result QueryMemory(void* info, void* out, u32 addr) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
+static Result QueryMemory(void* info, void* out, u32 addr) {
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
return 0;
}
/// Create an event
-Result CreateEvent(Handle* evt, u32 reset_type) {
+static Result CreateEvent(Handle* evt, u32 reset_type) {
*evt = Kernel::CreateEvent((ResetType)reset_type);
- DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
reset_type, *evt);
return 0;
}
/// Duplicates a kernel handle
-Result DuplicateHandle(Handle* out, Handle handle) {
- DEBUG_LOG(SVC, "called handle=0x%08X", handle);
+static Result DuplicateHandle(Handle* out, Handle handle) {
+ LOG_WARNING(Kernel_SVC, "(STUBBED) called handle=0x%08X", handle);
// Translate kernel handles -> real handles
if (handle == Kernel::CurrentThread) {
@@ -305,7 +329,7 @@ Result DuplicateHandle(Handle* out, Handle handle) {
}
_assert_msg_(KERNEL, (handle != Kernel::CurrentProcess),
"(UNIMPLEMENTED) process handle duplication!");
-
+
// TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
*out = handle;
@@ -313,26 +337,27 @@ Result DuplicateHandle(Handle* out, Handle handle) {
}
/// Signals an event
-Result SignalEvent(Handle evt) {
- Result res = Kernel::SignalEvent(evt);
- DEBUG_LOG(SVC, "called event=0x%08X", evt);
- return res;
+static Result SignalEvent(Handle evt) {
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
+ return Kernel::SignalEvent(evt).raw;
}
/// Clears an event
-Result ClearEvent(Handle evt) {
- Result res = Kernel::ClearEvent(evt);
- DEBUG_LOG(SVC, "called event=0x%08X", evt);
- return res;
+static Result ClearEvent(Handle evt) {
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
+ return Kernel::ClearEvent(evt).raw;
}
/// Sleep the current thread
-void SleepThread(s64 nanoseconds) {
- DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds);
+static void SleepThread(s64 nanoseconds) {
+ LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
+
+ // Check for next thread to schedule
+ HLE::Reschedule(__func__);
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
-s64 GetSystemTick() {
+static s64 GetSystemTick() {
return (s64)Core::g_app_core->GetTicks();
}
@@ -358,8 +383,8 @@ const HLE::FunctionDef SVC_Table[] = {
{0x12, nullptr, "Run"},
{0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
{0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
- {0x15, nullptr, "CreateSemaphore"},
- {0x16, nullptr, "ReleaseSemaphore"},
+ {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"},
+ {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"},
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 1d125faf6..6be393d0b 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 3ad801c63..da78b85e5 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -35,7 +35,7 @@ inline void Read(T &var, const u32 raw_addr) {
// Reads other than u32 are untested, so I'd rather have them abort than silently fail
if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
- ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
+ LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
return;
}
@@ -49,7 +49,7 @@ inline void Write(u32 addr, const T data) {
// Writes other than u32 are untested, so I'd rather have them abort than silently fail
if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
- ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+ LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
return;
}
@@ -73,7 +73,7 @@ inline void Write(u32 addr, const T data) {
for (u32* ptr = start; ptr < end; ++ptr)
*ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
- DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
+ LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
}
break;
}
@@ -105,7 +105,7 @@ inline void Write(u32 addr, const T data) {
}
default:
- ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value());
+ LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value());
break;
}
@@ -132,16 +132,16 @@ inline void Write(u32 addr, const T data) {
}
default:
- ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
+ LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
break;
}
}
}
- DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
+ 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.GetPhysicalInputAddress(), config.input_width, config.input_height,
- config.GetPhysicalOutputAddress(), config.output_width, config.output_height,
+ config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
+ config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
config.output_format.Value());
}
break;
@@ -154,8 +154,7 @@ inline void Write(u32 addr, const T data) {
if (config.trigger & 1)
{
u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress()));
- u32 size = config.size << 3;
- Pica::CommandProcessor::ProcessCommandList(buffer, size);
+ Pica::CommandProcessor::ProcessCommandList(buffer, config.size);
}
break;
}
@@ -252,12 +251,12 @@ void Init() {
framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
framebuffer_sub.active_fb = 0;
- NOTICE_LOG(GPU, "initialized OK");
+ LOG_DEBUG(HW_GPU, "initialized OK");
}
/// Shutdown hardware
void Shutdown() {
- NOTICE_LOG(GPU, "shutdown OK");
+ LOG_DEBUG(HW_GPU, "shutdown OK");
}
} // namespace
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 3fa7b9ccf..86cd5e680 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -169,7 +169,7 @@ struct Regs {
INSERT_PADDING_WORDS(0x331);
struct {
- // command list size
+ // command list size (in bytes)
u32 size;
INSERT_PADDING_WORDS(0x1);
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 33f75c50a..af42b41fb 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -6,7 +6,6 @@
#include "core/hw/hw.h"
#include "core/hw/gpu.h"
-#include "core/hw/ndma.h"
namespace HW {
@@ -39,36 +38,26 @@ enum {
template <typename T>
inline void Read(T &var, const u32 addr) {
switch (addr & 0xFFFFF000) {
-
- // TODO(bunnei): What is the virtual address of NDMA?
- // case VADDR_NDMA:
- // NDMA::Read(var, addr);
- // break;
case VADDR_GPU:
GPU::Read(var, addr);
break;
default:
- ERROR_LOG(HW, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
+ LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
}
}
template <typename T>
inline void Write(u32 addr, const T data) {
switch (addr & 0xFFFFF000) {
-
- // TODO(bunnei): What is the virtual address of NDMA?
- // case VADDR_NDMA
- // NDMA::Write(addr, data);
- // break;
case VADDR_GPU:
GPU::Write(addr, data);
break;
default:
- ERROR_LOG(HW, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+ LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
}
}
@@ -87,19 +76,17 @@ template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
void Update() {
GPU::Update();
- NDMA::Update();
}
/// Initialize hardware
void Init() {
GPU::Init();
- NDMA::Init();
- NOTICE_LOG(HW, "initialized OK");
+ LOG_DEBUG(HW, "initialized OK");
}
/// Shutdown hardware
void Shutdown() {
- NOTICE_LOG(HW, "shutdown OK");
+ LOG_DEBUG(HW, "shutdown OK");
}
} \ No newline at end of file
diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp
deleted file mode 100644
index e29a773f1..000000000
--- a/src/core/hw/ndma.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-
-#include "core/hw/ndma.h"
-
-namespace NDMA {
-
-template <typename T>
-inline void Read(T &var, const u32 addr) {
- ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
-}
-
-template <typename T>
-inline void Write(u32 addr, const T data) {
- ERROR_LOG(NDMA, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
-}
-
-// 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);
-
-template void Write<u64>(u32 addr, const u64 data);
-template void Write<u32>(u32 addr, const u32 data);
-template void Write<u16>(u32 addr, const u16 data);
-template void Write<u8>(u32 addr, const u8 data);
-
-/// Update hardware
-void Update() {
-}
-
-/// Initialize hardware
-void Init() {
- NOTICE_LOG(GPU, "initialized OK");
-}
-
-/// Shutdown hardware
-void Shutdown() {
- NOTICE_LOG(GPU, "shutdown OK");
-}
-
-} // namespace
diff --git a/src/core/hw/ndma.h b/src/core/hw/ndma.h
deleted file mode 100644
index d8fa3d40b..000000000
--- a/src/core/hw/ndma.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-namespace NDMA {
-
-template <typename T>
-inline void Read(T &var, const u32 addr);
-
-template <typename T>
-inline void Write(u32 addr, const T data);
-
-/// Update hardware
-void Update();
-
-/// Initialize hardware
-void Init();
-
-/// Shutdown hardware
-void Shutdown();
-
-} // namespace
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
new file mode 100644
index 000000000..0437e5374
--- /dev/null
+++ b/src/core/loader/3dsx.cpp
@@ -0,0 +1,236 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <vector>
+
+#include "core/file_sys/archive_romfs.h"
+#include "core/loader/elf.h"
+#include "core/loader/ncch.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/mem_map.h"
+
+#include "3dsx.h"
+
+
+namespace Loader {
+
+
+/**
+ * File layout:
+ * - File header
+ * - Code, rodata and data relocation table headers
+ * - Code segment
+ * - Rodata segment
+ * - Loadable (non-BSS) part of the data segment
+ * - Code relocation table
+ * - Rodata relocation table
+ * - Data relocation table
+ *
+ * Memory layout before relocations are applied:
+ * [0..codeSegSize) -> code segment
+ * [codeSegSize..rodataSegSize) -> rodata segment
+ * [rodataSegSize..dataSegSize) -> data segment
+ *
+ * Memory layout after relocations are applied: well, however the loader sets it up :)
+ * The entrypoint is always the start of the code segment.
+ * The BSS section must be cleared manually by the application.
+ */
+enum THREEDSX_Error {
+ ERROR_NONE = 0,
+ ERROR_READ = 1,
+ ERROR_FILE = 2,
+ ERROR_ALLOC = 3
+};
+static const u32 RELOCBUFSIZE = 512;
+
+// File header
+static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
+#pragma pack(1)
+struct THREEDSX_Header
+{
+ u32 magic;
+ u16 header_size, reloc_hdr_size;
+ u32 format_ver;
+ u32 flags;
+
+ // Sizes of the code, rodata and data segments +
+ // size of the BSS section (uninitialized latter half of the data segment)
+ u32 code_seg_size, rodata_seg_size, data_seg_size, bss_size;
+};
+
+// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts.
+struct THREEDSX_RelocHdr
+{
+ // # of absolute relocations (that is, fix address to post-relocation memory layout)
+ u32 cross_segment_absolute;
+ // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
+ u32 cross_segment_relative;
+ // more?
+
+ // Relocations are written in this order:
+ // - Absolute relocations
+ // - Relative relocations
+};
+
+// Relocation entry: from the current pointer, skip X words and patch Y words
+struct THREEDSX_Reloc
+{
+ u16 skip, patch;
+};
+#pragma pack()
+
+struct THREEloadinfo
+{
+ u8* seg_ptrs[3]; // code, rodata & data
+ u32 seg_addrs[3];
+ 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)
+{
+ if (addr < offsets[0])
+ return loadinfo->seg_addrs[0] + addr;
+ if (addr < offsets[1])
+ return loadinfo->seg_addrs[1] + addr - offsets[0];
+ return loadinfo->seg_addrs[2] + addr - offsets[1];
+}
+
+int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
+{
+ FileUtil::IOFile file(filename, "rb");
+ if (!file.IsOpen()) {
+ return ERROR_FILE;
+ }
+ THREEDSX_Header hdr;
+ if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr))
+ return ERROR_READ;
+
+ THREEloadinfo loadinfo;
+ //loadinfo segments must be a multiple of 0x1000
+ loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) &~0xFFF;
+ loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF;
+ loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF;
+ u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] };
+ u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF;
+ u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size;
+ u32 n_reloc_tables = hdr.reloc_hdr_size / 4;
+ std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables);
+
+ loadinfo.seg_addrs[0] = base_addr;
+ loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0];
+ loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1];
+ loadinfo.seg_ptrs[0] = &all_mem[0];
+ loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0];
+ loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1];
+
+ // Skip header for future compatibility
+ file.Seek(hdr.header_size, SEEK_SET);
+
+ // 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)
+ return ERROR_READ;
+ }
+
+ // Read the segments
+ if (file.ReadBytes(loadinfo.seg_ptrs[0], hdr.code_seg_size) != hdr.code_seg_size)
+ return ERROR_READ;
+ if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size)
+ return ERROR_READ;
+ if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != hdr.data_seg_size - hdr.bss_size)
+ return ERROR_READ;
+
+ // BSS clear
+ 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];
+ 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);
+ continue;
+ }
+ static THREEDSX_Reloc reloc_table[RELOCBUFSIZE];
+
+ u32* pos = (u32*)loadinfo.seg_ptrs[current_segment];
+ 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))
+ 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;
+ 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);
+ switch (current_segment_reloc_table) {
+ case 0: *pos = (addr); break;
+ case 1: *pos = (addr - in_addr); break;
+ default: break; //this should never happen
+ }
+ pos++;
+ num_patches--;
+ }
+ }
+ }
+ }
+ }
+
+ // Write the data
+ memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]);
+
+ LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000);
+ LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000);
+ LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000);
+ LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000);
+
+ return ERROR_NONE;
+}
+
+ /// AppLoader_DSX constructor
+ AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) {
+ }
+
+ /// AppLoader_DSX destructor
+ AppLoader_THREEDSX::~AppLoader_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 reader;
+ reader.Load3DSXFile(filename, 0x00100000);
+ Kernel::LoadExec(0x00100000);
+ } else {
+ return ResultStatus::Error;
+ }
+ return ResultStatus::Success;
+ }
+
+} // namespace Loader
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
new file mode 100644
index 000000000..848d3ef8a
--- /dev/null
+++ b/src/core/loader/3dsx.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Dolphin Emulator Project / Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Loader namespace
+
+namespace Loader {
+
+/// Loads an 3DSX file
+class AppLoader_THREEDSX final : public AppLoader {
+public:
+ AppLoader_THREEDSX(const std::string& filename);
+ ~AppLoader_THREEDSX() override;
+
+ /**
+ * 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 389d5a8c9..c95882f4a 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -254,18 +254,18 @@ const char *ElfReader::GetSectionName(int section) const {
}
bool ElfReader::LoadInto(u32 vaddr) {
- DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx);
+ LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx);
// Should we relocate?
relocate = (header->e_type != ET_EXEC);
if (relocate) {
- DEBUG_LOG(MASTER_LOG, "Relocatable module");
+ LOG_DEBUG(Loader, "Relocatable module");
entryPoint += vaddr;
} else {
- DEBUG_LOG(MASTER_LOG, "Prerelocated executable");
+ LOG_DEBUG(Loader, "Prerelocated executable");
}
- INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum);
+ LOG_DEBUG(Loader, "%i segments:", header->e_phnum);
// First pass : Get the bits into RAM
u32 segment_addr[32];
@@ -273,17 +273,17 @@ bool ElfReader::LoadInto(u32 vaddr) {
for (int i = 0; i < header->e_phnum; i++) {
Elf32_Phdr *p = segments + i;
- INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
+ LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
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);
- INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
+ LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
p->p_memsz);
}
}
- INFO_LOG(MASTER_LOG, "Done loading.");
+ LOG_DEBUG(Loader, "Done loading.");
return true;
}
@@ -346,7 +346,7 @@ AppLoader_ELF::~AppLoader_ELF() {
* @return True on success, otherwise false
*/
ResultStatus AppLoader_ELF::Load() {
- INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str());
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index a268e021a..480274d23 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -5,9 +5,10 @@
#include <memory>
#include "core/file_sys/archive_romfs.h"
+#include "core/loader/3dsx.h"
#include "core/loader/elf.h"
#include "core/loader/ncch.h"
-#include "core/hle/kernel/archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/mem_map.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -22,7 +23,7 @@ namespace Loader {
*/
FileType IdentifyFile(const std::string &filename) {
if (filename.size() == 0) {
- ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
+ LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
return FileType::Error;
}
@@ -42,6 +43,8 @@ FileType IdentifyFile(const std::string &filename) {
return FileType::CCI;
} else if (extension == ".bin") {
return FileType::BIN;
+ } else if (extension == ".3dsx") {
+ return FileType::THREEDSX;
}
return FileType::Unknown;
}
@@ -52,10 +55,14 @@ FileType IdentifyFile(const std::string &filename) {
* @return ResultStatus result of function
*/
ResultStatus LoadFile(const std::string& filename) {
- INFO_LOG(LOADER, "Loading file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading file %s...", filename.c_str());
switch (IdentifyFile(filename)) {
+ //3DSX file format...
+ case FileType::THREEDSX:
+ return AppLoader_THREEDSX(filename).Load();
+
// Standard ELF file format...
case FileType::ELF:
return AppLoader_ELF(filename).Load();
@@ -67,7 +74,8 @@ ResultStatus LoadFile(const std::string& filename) {
// Load application and RomFS
if (ResultStatus::Success == app_loader.Load()) {
- Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS");
+ Kernel::g_program_id = app_loader.GetProgramId();
+ Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
return ResultStatus::Success;
}
break;
@@ -76,7 +84,7 @@ ResultStatus LoadFile(const std::string& filename) {
// Raw BIN file format...
case FileType::BIN:
{
- INFO_LOG(LOADER, "Loading BIN file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str());
FileUtil::IOFile file(filename, "rb");
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 68f843005..0f836d285 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -22,6 +22,7 @@ enum class FileType {
CIA,
ELF,
BIN,
+ THREEDSX, //3DSX
};
/// Return type for functions in Loader namespace
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 1e5501e6d..4d23656ec 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -24,7 +24,7 @@ static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
* @param size Size of compressed buffer
* @return Size of decompressed buffer
*/
-u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
+static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
u32 offset_size = *(u32*)(buffer + size - 4);
return offset_size + size;
}
@@ -37,7 +37,7 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
* @param decompressed_size Size of decompressed buffer
* @return True on success, otherwise false
*/
-bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
+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;
u32 out = decompressed_size;
@@ -118,7 +118,7 @@ AppLoader_NCCH::~AppLoader_NCCH() {
* @return ResultStatus result of function
*/
ResultStatus AppLoader_NCCH::LoadExec() const {
- if (!is_loaded)
+ if (!is_loaded)
return ResultStatus::ErrorNotLoaded;
std::vector<u8> code;
@@ -140,13 +140,13 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
// 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) {
- INFO_LOG(LOADER, "ExeFS section %d:", i);
- INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name);
- INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset);
- INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
+ 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);
@@ -181,11 +181,11 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
}
}
} else {
- ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
+ 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)
@@ -194,7 +194,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
* @return True on success, otherwise false
*/
ResultStatus AppLoader_NCCH::Load() {
- INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str());
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
@@ -205,12 +205,12 @@ ResultStatus AppLoader_NCCH::Load() {
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
- WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!");
+ 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));
}
-
+
// Verify we are loading the correct file type...
if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
return ResultStatus::ErrorInvalidFormat;
@@ -222,17 +222,17 @@ ResultStatus AppLoader_NCCH::Load() {
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
entry_point = exheader_header.codeset_info.text.address;
- INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name);
- INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no");
- INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point);
+ 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);
// Read ExeFS...
exefs_offset = ncch_header.exefs_offset * kBlockSize;
u32 exefs_size = ncch_header.exefs_size * kBlockSize;
- INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset);
- INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size);
+ LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
+ LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
file.Seek(exefs_offset + ncch_offset, 0);
file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
@@ -243,7 +243,7 @@ ResultStatus AppLoader_NCCH::Load() {
return ResultStatus::Success;
} else {
- ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
+ LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
}
return ResultStatus::Error;
}
@@ -297,8 +297,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
- INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset);
- INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size);
+ LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
+ LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
buffer.resize(romfs_size);
@@ -307,12 +307,16 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
return ResultStatus::Success;
}
- NOTICE_LOG(LOADER, "RomFS unused");
+ LOG_DEBUG(Loader, "NCCH has no RomFS");
return ResultStatus::ErrorNotUsed;
} else {
- ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
+ LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
}
return ResultStatus::Error;
}
+u64 AppLoader_NCCH::GetProgramId() const {
+ return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]);
+}
+
} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index f40a258b7..2fe2a7d82 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -191,6 +191,12 @@ public:
*/
ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
+ /*
+ * Gets the program id from the NCCH header
+ * @return u64 Program id
+ */
+ u64 GetProgramId() const;
+
private:
/**
@@ -215,7 +221,7 @@ private:
u32 entry_point;
u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
u32 exefs_offset;
-
+
NCCH_Header ncch_header;
ExeFs_Header exefs_header;
ExHeader_Header exheader_header;
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index cf12f24d9..d1c44ed24 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -11,38 +11,38 @@
namespace Memory {
-u8* g_base = NULL; ///< The base pointer to the auto-mirrored arena.
+u8* g_base = nullptr; ///< The base pointer to the auto-mirrored arena.
-MemArena g_arena; ///< The MemArena class
+static MemArena arena; ///< The MemArena class
-u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here
-u8* g_system_mem = NULL; ///< System memory
-u8* g_heap = NULL; ///< Application heap (main memory)
-u8* g_heap_gsp = NULL; ///< GSP heap (main memory)
-u8* g_vram = NULL; ///< Video memory (VRAM) pointer
-u8* g_shared_mem = NULL; ///< Shared memory
-u8* g_kernel_mem; ///< Kernel memory
+u8* g_exefs_code = nullptr; ///< ExeFS:/.code is loaded here
+u8* g_system_mem = nullptr; ///< System memory
+u8* g_heap = nullptr; ///< Application heap (main memory)
+u8* g_heap_linear = nullptr; ///< Linear heap
+u8* g_vram = nullptr; ///< Video memory (VRAM) pointer
+u8* g_shared_mem = nullptr; ///< Shared memory
+u8* g_kernel_mem; ///< Kernel memory
-u8* g_physical_bootrom = NULL; ///< Bootrom physical memory
-u8* g_uncached_bootrom = NULL;
+static u8* physical_bootrom = nullptr; ///< Bootrom physical memory
+static u8* uncached_bootrom = nullptr;
-u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here
-u8* g_physical_system_mem = NULL; ///< System physical memory
-u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM)
-u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory
-u8* g_physical_vram = NULL; ///< Video physical memory (VRAM)
-u8* g_physical_shared_mem = NULL; ///< Physical shared memory
-u8* g_physical_kernel_mem; ///< Kernel memory
+static u8* physical_exefs_code = nullptr; ///< Phsical ExeFS:/.code is loaded here
+static u8* physical_system_mem = nullptr; ///< System physical memory
+static u8* physical_fcram = nullptr; ///< Main physical memory (FCRAM)
+static u8* physical_heap_gsp = nullptr; ///< GSP heap physical memory
+static u8* physical_vram = nullptr; ///< Video physical memory (VRAM)
+static u8* physical_shared_mem = nullptr; ///< Physical shared memory
+static u8* physical_kernel_mem; ///< Kernel memory
// We don't declare the IO region in here since its handled by other means.
static MemoryView g_views[] = {
- {&g_exefs_code, &g_physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0},
- {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
- {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
- {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
- {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
- {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
- {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0},
+ {&g_exefs_code, &physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0},
+ {&g_vram, &physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
+ {&g_heap, &physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
+ {&g_shared_mem, &physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
+ {&g_system_mem, &physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
+ {&g_kernel_mem, &physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
+ {&g_heap_linear, &physical_heap_gsp, HEAP_LINEAR_VADDR, HEAP_LINEAR_SIZE, 0},
};
/*static MemoryView views[] =
@@ -69,20 +69,20 @@ void Init() {
g_views[i].size = FCRAM_SIZE;
}
- g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &g_arena);
+ g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena);
- NOTICE_LOG(MEMMAP, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap,
- g_physical_fcram);
+ LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap,
+ physical_fcram);
}
void Shutdown() {
u32 flags = 0;
- MemoryMap_Shutdown(g_views, kNumMemViews, flags, &g_arena);
+ MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena);
- g_arena.ReleaseSpace();
- g_base = NULL;
+ arena.ReleaseSpace();
+ g_base = nullptr;
- NOTICE_LOG(MEMMAP, "shutdown OK");
+ LOG_DEBUG(HW_Memory, "shutdown OK");
}
} // namespace
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index eed445046..7b750f848 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -16,10 +16,9 @@ typedef u32 PAddr; ///< Represents a pointer in the physical address space.
////////////////////////////////////////////////////////////////////////////////////////////////////
-enum {
+enum : u32 {
BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size
MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size
- DSP_SIZE = 0x00080000, ///< DSP memory size
AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size
FCRAM_SIZE = 0x08000000, ///< FCRAM size
@@ -27,47 +26,42 @@ enum {
FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space
FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address
FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space
- FCRAM_MASK = (FCRAM_SIZE - 1), ///< FCRAM mask
SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size
SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory
SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE),
- SHARED_MEMORY_MASK = (SHARED_MEMORY_SIZE - 1),
+
+ DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size
+ DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address
CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size
CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address
CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE),
- CONFIG_MEMORY_MASK = (CONFIG_MEMORY_SIZE - 1),
KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size
KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are
KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE),
- KERNEL_MEMORY_MASK = (KERNEL_MEMORY_SIZE - 1),
EXEFS_CODE_SIZE = 0x03F00000,
EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here
EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE),
- EXEFS_CODE_MASK = 0x03FFFFFF,
// Region of FCRAM used by system
SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB
SYSTEM_MEMORY_VADDR = 0x04000000,
SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE),
- SYSTEM_MEMORY_MASK = 0x03FFFFFF,
HEAP_SIZE = FCRAM_SIZE, ///< Application heap size
//HEAP_PADDR = HEAP_GSP_SIZE,
//HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE),
HEAP_VADDR = 0x08000000,
HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE),
- HEAP_MASK = (HEAP_SIZE - 1),
- HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly?
- HEAP_GSP_VADDR = 0x14000000,
- HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE),
- HEAP_GSP_PADDR = 0x00000000,
- HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE),
- HEAP_GSP_MASK = (HEAP_GSP_SIZE - 1),
+ HEAP_LINEAR_SIZE = 0x08000000, ///< Linear heap size... TODO: Define correctly?
+ HEAP_LINEAR_VADDR = 0x14000000,
+ HEAP_LINEAR_VADDR_END = (HEAP_LINEAR_VADDR + HEAP_LINEAR_SIZE),
+ HEAP_LINEAR_PADDR = 0x00000000,
+ HEAP_LINEAR_PADDR_END = (HEAP_LINEAR_PADDR + HEAP_LINEAR_SIZE),
HARDWARE_IO_SIZE = 0x01000000,
HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start
@@ -80,12 +74,10 @@ enum {
VRAM_VADDR = 0x1F000000,
VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE),
VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE),
- VRAM_MASK = 0x007FFFFF,
SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader
SCRATCHPAD_VADDR_END = 0x10000000,
SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space
- SCRATCHPAD_MASK = (SCRATCHPAD_SIZE - 1), ///< Scratchpad memory mask
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -120,7 +112,7 @@ extern u8 *g_base;
// These are guaranteed to point to "low memory" addresses (sub-32-bit).
// 64-bit: Pointers to low-mem (sub-0x10000000) mirror
// 32-bit: Same as the corresponding physical/virtual pointers.
-extern u8* g_heap_gsp; ///< GSP heap (main memory)
+extern u8* g_heap_linear; ///< Linear heap (main memory)
extern u8* g_heap; ///< Application heap (main memory)
extern u8* g_vram; ///< Video memory (VRAM)
extern u8* g_shared_mem; ///< Shared memory
@@ -147,6 +139,7 @@ u32 Read16_ZX(VAddr addr);
void Write8(VAddr addr, u8 data);
void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
+void Write64(VAddr addr, u64 data);
void WriteBlock(VAddr addr, const u8* data, size_t size);
@@ -156,7 +149,7 @@ u8* GetPointer(VAddr virtual_address);
* Maps a block of memory on the heap
* @param size Size of block in bytes
* @param operation Memory map operation type
- * @param flags Memory allocation flags
+ * @param permissions Memory allocation permissions
*/
u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
@@ -166,7 +159,7 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
* @param operation Memory map operation type
* @param permissions Control memory permissions
*/
-u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions);
+u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions);
inline const char* GetCharPointer(const VAddr address) {
return (const char *)GetPointer(address);
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 90951812b..7f7e77233 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -12,9 +12,9 @@
namespace Memory {
-std::map<u32, MemoryBlock> g_heap_map;
-std::map<u32, MemoryBlock> g_heap_gsp_map;
-std::map<u32, MemoryBlock> g_shared_map;
+static std::map<u32, MemoryBlock> heap_map;
+static std::map<u32, MemoryBlock> heap_linear_map;
+static std::map<u32, MemoryBlock> shared_map;
/// Convert a physical address to virtual address
VAddr PhysicalToVirtualAddress(const PAddr addr) {
@@ -28,7 +28,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) {
return addr - FCRAM_PADDR + FCRAM_VADDR;
}
- ERROR_LOG(MEMMAP, "Unknown physical address @ 0x%08x", addr);
+ LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr);
return addr;
}
@@ -44,7 +44,7 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) {
return addr - FCRAM_VADDR + FCRAM_PADDR;
}
- ERROR_LOG(MEMMAP, "Unknown virtual address @ 0x%08x", addr);
+ LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr);
return addr;
}
@@ -56,7 +56,7 @@ inline void Read(T &var, const VAddr vaddr) {
// Kernel memory command buffer
if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
- var = *((const T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK]);
+ var = *((const T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR]);
// Hardware I/O register reads
// 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
@@ -65,23 +65,23 @@ inline void Read(T &var, const VAddr vaddr) {
// ExeFS:/.code is loaded here
} else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
- var = *((const T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK]);
+ var = *((const T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR]);
- // FCRAM - GSP heap
- } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
- var = *((const T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK]);
+ // FCRAM - linear heap
+ } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
+ var = *((const T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR]);
// FCRAM - application heap
} else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
- var = *((const T*)&g_heap[vaddr & HEAP_MASK]);
+ var = *((const T*)&g_heap[vaddr - HEAP_VADDR]);
// Shared memory
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
- var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]);
+ var = *((const T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR]);
// System memory
} else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
- var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]);
+ var = *((const T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR]);
// Config memory
} else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) {
@@ -89,10 +89,10 @@ inline void Read(T &var, const VAddr vaddr) {
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
- var = *((const T*)&g_vram[vaddr & VRAM_MASK]);
+ var = *((const T*)&g_vram[vaddr - VRAM_VADDR]);
} else {
- ERROR_LOG(MEMMAP, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr);
+ LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr);
}
}
@@ -101,7 +101,7 @@ inline void Write(const VAddr vaddr, const T data) {
// Kernel memory command buffer
if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
- *(T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK] = data;
+ *(T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR] = data;
// Hardware I/O register writes
// 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
@@ -110,27 +110,27 @@ inline void Write(const VAddr vaddr, const T data) {
// ExeFS:/.code is loaded here
} else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
- *(T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK] = data;
+ *(T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR] = data;
- // FCRAM - GSP heap
- } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
- *(T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK] = data;
+ // FCRAM - linear heap
+ } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
+ *(T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR] = data;
// FCRAM - application heap
} else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
- *(T*)&g_heap[vaddr & HEAP_MASK] = data;
+ *(T*)&g_heap[vaddr - HEAP_VADDR] = data;
// Shared memory
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
- *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data;
+ *(T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR] = data;
// System memory
} else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
- *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data;
+ *(T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR] = data;
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
- *(T*)&g_vram[vaddr & VRAM_MASK] = data;
+ *(T*)&g_vram[vaddr - VRAM_VADDR] = data;
//} else if ((vaddr & 0xFFF00000) == 0x1FF00000) {
// _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory");
@@ -141,41 +141,41 @@ inline void Write(const VAddr vaddr, const T data) {
// Error out...
} else {
- ERROR_LOG(MEMMAP, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, vaddr);
+ LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, vaddr);
}
}
u8 *GetPointer(const VAddr vaddr) {
// Kernel memory command buffer
if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
- return g_kernel_mem + (vaddr & KERNEL_MEMORY_MASK);
+ return g_kernel_mem + (vaddr - KERNEL_MEMORY_VADDR);
// ExeFS:/.code is loaded here
} else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
- return g_exefs_code + (vaddr & EXEFS_CODE_MASK);
+ return g_exefs_code + (vaddr - EXEFS_CODE_VADDR);
- // FCRAM - GSP heap
- } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
- return g_heap_gsp + (vaddr & HEAP_GSP_MASK);
+ // FCRAM - linear heap
+ } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
+ return g_heap_linear + (vaddr - HEAP_LINEAR_VADDR);
// FCRAM - application heap
} else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
- return g_heap + (vaddr & HEAP_MASK);
+ return g_heap + (vaddr - HEAP_VADDR);
// Shared memory
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
- return g_shared_mem + (vaddr & SHARED_MEMORY_MASK);
+ return g_shared_mem + (vaddr - SHARED_MEMORY_VADDR);
// System memory
} else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
- return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK);
+ return g_system_mem + (vaddr - SYSTEM_MEMORY_VADDR);
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
- return g_vram + (vaddr & VRAM_MASK);
+ return g_vram + (vaddr - VRAM_VADDR);
} else {
- ERROR_LOG(MEMMAP, "unknown GetPointer @ 0x%08x", vaddr);
+ LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
return 0;
}
}
@@ -194,34 +194,34 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) {
block.operation = operation;
block.permissions = permissions;
- if (g_heap_map.size() > 0) {
- const MemoryBlock last_block = g_heap_map.rbegin()->second;
+ if (heap_map.size() > 0) {
+ const MemoryBlock last_block = heap_map.rbegin()->second;
block.address = last_block.address + last_block.size;
}
- g_heap_map[block.GetVirtualAddress()] = block;
+ heap_map[block.GetVirtualAddress()] = block;
return block.GetVirtualAddress();
}
/**
- * Maps a block of memory on the GSP heap
+ * Maps a block of memory on the linear heap
* @param size Size of block in bytes
* @param operation Memory map operation type
* @param flags Memory allocation flags
*/
-u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions) {
+u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) {
MemoryBlock block;
- block.base_address = HEAP_GSP_VADDR;
+ block.base_address = HEAP_LINEAR_VADDR;
block.size = size;
block.operation = operation;
block.permissions = permissions;
- if (g_heap_gsp_map.size() > 0) {
- const MemoryBlock last_block = g_heap_gsp_map.rbegin()->second;
+ if (heap_linear_map.size() > 0) {
+ const MemoryBlock last_block = heap_linear_map.rbegin()->second;
block.address = last_block.address + last_block.size;
}
- g_heap_gsp_map[block.GetVirtualAddress()] = block;
+ heap_linear_map[block.GetVirtualAddress()] = block;
return block.GetVirtualAddress();
}
@@ -239,7 +239,7 @@ u16 Read16(const VAddr addr) {
// Check for 16-bit unaligned memory reads...
if (addr & 1) {
// TODO(bunnei): Implement 16-bit unaligned memory reads
- ERROR_LOG(MEMMAP, "16-bit unaligned memory reads are not implemented!");
+ LOG_ERROR(HW_Memory, "16-bit unaligned memory reads are not implemented!");
}
return (u16)data;
diff --git a/src/core/settings.h b/src/core/settings.h
index 6a6265e18..138ffc615 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -4,6 +4,8 @@
#pragma once
+#include <string>
+
namespace Settings {
struct Values {
@@ -32,6 +34,8 @@ struct Values {
// Data Storage
bool use_virtual_sd;
+
+ std::string log_filter;
} extern values;
}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 43d0eef2c..2885ff45f 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -23,10 +23,10 @@ void Init(EmuWindow* emu_window) {
Core::Init();
Memory::Init();
HW::Init();
+ Kernel::Init();
HLE::Init();
CoreTiming::Init();
VideoCore::Init(emu_window);
- Kernel::Init();
}
void RunLoopFor(int cycles) {
@@ -37,13 +37,13 @@ void RunLoopUntil(u64 global_cycles) {
}
void Shutdown() {
- Core::Shutdown();
- Memory::Shutdown();
- HW::Shutdown();
- HLE::Shutdown();
- CoreTiming::Shutdown();
VideoCore::Shutdown();
+ CoreTiming::Shutdown();
+ HLE::Shutdown();
Kernel::Shutdown();
+ HW::Shutdown();
+ Memory::Shutdown();
+ Core::Shutdown();
}
} // namespace
diff --git a/src/core/system.h b/src/core/system.h
index 8f8ddf87b..2bc2edc75 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -11,16 +11,16 @@
namespace System {
// State of the full emulator
-typedef enum {
- STATE_NULL = 0, ///< System is in null state, nothing initialized
- STATE_IDLE, ///< System is in an initialized state, but not running
- STATE_RUNNING, ///< System is running
- STATE_LOADING, ///< System is loading a ROM
- STATE_HALTED, ///< System is halted (error)
- STATE_STALLED, ///< System is stalled (unused)
- STATE_DEBUG, ///< System is in a special debug mode (unused)
- STATE_DIE ///< System is shutting down
-} State;
+enum State {
+ STATE_NULL = 0, ///< System is in null state, nothing initialized
+ STATE_IDLE, ///< System is in an initialized state, but not running
+ STATE_RUNNING, ///< System is running
+ STATE_LOADING, ///< System is loading a ROM
+ STATE_HALTED, ///< System is halted (error)
+ STATE_STALLED, ///< System is stalled (unused)
+ STATE_DEBUG, ///< System is in a special debug mode (unused)
+ STATE_DIE ///< System is shutting down
+};
extern volatile State g_state;
@@ -30,4 +30,4 @@ void RunLoopFor(int cycles);
void RunLoopUntil(u64 global_cycles);
void Shutdown();
-};
+}