summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/CMakeLists.txt4
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/citra_qt/debugger/graphics.cpp2
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp498
-rw-r--r--src/core/arm/interpreter/armemu.cpp78
-rw-r--r--src/core/hle/function_wrappers.h7
-rw-r--r--src/core/hle/service/soc_u.cpp721
-rw-r--r--src/core/hle/service/soc_u.h1
-rw-r--r--src/core/hle/svc.cpp14
11 files changed, 1181 insertions, 148 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index 94f8d13c7..bbb3374f2 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -24,10 +24,10 @@ endif()
if (APPLE)
target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY})
elseif (WIN32)
- target_link_libraries(citra winmm)
+ target_link_libraries(citra winmm wsock32 ws2_32)
if (MINGW) # GCC does not support codecvt, so use iconv instead
target_link_libraries(citra iconv)
- endif()
+ endif()
else() # Unix
target_link_libraries(citra rt)
endif()
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 356ec754f..8a511e02f 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -68,7 +68,7 @@ endif()
if (APPLE)
target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
elseif (WIN32)
- target_link_libraries(citra-qt winmm)
+ target_link_libraries(citra-qt winmm wsock32 ws2_32)
else() # Unix
target_link_libraries(citra-qt rt)
endif()
diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics.cpp
index 6ff4c290d..9633d367e 100644
--- a/src/citra_qt/debugger/graphics.cpp
+++ b/src/citra_qt/debugger/graphics.cpp
@@ -72,7 +72,7 @@ void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_co
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr("Graphics Debugger"), parent)
{
- // TODO: set objectName!
+ setObjectName("GraphicsDebugger");
GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this);
g_debugger.RegisterObserver(command_model);
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 816d1bb55..7ac30ad50 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -39,6 +39,7 @@ static std::shared_ptr<Logger> global_logger;
SUB(Service, CFG) \
SUB(Service, DSP) \
SUB(Service, HID) \
+ SUB(Service, SOC) \
CLS(HW) \
SUB(HW, Memory) \
SUB(HW, GPU) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index d1c391862..06b99b07a 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -59,6 +59,7 @@ enum class Class : ClassType {
Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service
Service_HID, ///< The HID (User input) service
+ Service_SOC, ///< The SOC (Socket) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
HW_GPU, ///< GPU control emulation
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 926761cff..f2ea0e9dd 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -2171,29 +2171,45 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); }
-ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index)
+ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index)
{
arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst));
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
-
+
inst_base->cond = BITS(inst, 28, 31);
inst_base->idx = index;
inst_base->br = NON_BRANCH;
inst_base->load_r15 = 0;
-
+
inst_cream->Rm = BITS(inst, 0, 3);
inst_cream->Rn = BITS(inst, 16, 19);
inst_cream->Rd = BITS(inst, 12, 15);
inst_cream->op1 = BITS(inst, 20, 21);
inst_cream->op2 = BITS(inst, 5, 7);
-
+
return inst_base;
}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(inst, index);
+}
ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index)
{
- return INTERPRETER_TRANSLATE(sadd16)(inst, index);
+ return INTERPRETER_TRANSLATE(sadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(inst, index);
}
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(sadd8)(inst, index);
+}
+
ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst));
@@ -2236,13 +2252,48 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index)
return inst_base;
}
+
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(shadd8)(unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(shadd8)(inst, index);
+}
+
ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst));
@@ -2408,15 +2459,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); }
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index)
-{
- return INTERPRETER_TRANSLATE(sadd16)(inst, index);
-}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index)
-{
- return INTERPRETER_TRANSLATE(sadd16)(inst, index);
-}
+
ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst));
@@ -2785,9 +2828,46 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD8"); }
-ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD16"); }
-ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADDSUBX"); }
+
+ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst));
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->op1 = BITS(inst, 20, 21);
+ inst_cream->op2 = BITS(inst, 5, 7);
+ inst_cream->Rm = BITS(inst, 0, 3);
+ inst_cream->Rn = BITS(inst, 16, 19);
+ inst_cream->Rd = BITS(inst, 12, 15);
+
+ return inst_base;
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(inst, index);
+}
+ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index)
+{
+ return INTERPRETER_TRANSLATE(uadd8)(inst, index);
+}
+
ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index)
{
arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(generic_arm_inst));
@@ -3017,9 +3097,6 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index)
{
return INTERPRETER_TRANSLATE(ssat16)(inst, index);
}
-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)
{
@@ -5005,6 +5082,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SADD8_INST:
+ SSUB8_INST:
SADD16_INST:
SADDSUBX_INST:
SSUBADDX_INST:
@@ -5012,52 +5090,96 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+ const u8 op2 = inst_cream->op2;
- const s16 rn_lo = (RN & 0xFFFF);
- const s16 rn_hi = ((RN >> 16) & 0xFFFF);
- const s16 rm_lo = (RM & 0xFFFF);
- const s16 rm_hi = ((RM >> 16) & 0xFFFF);
+ if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
+ const s16 rn_lo = (RN & 0xFFFF);
+ const s16 rn_hi = ((RN >> 16) & 0xFFFF);
+ const s16 rm_lo = (RM & 0xFFFF);
+ const s16 rm_hi = ((RM >> 16) & 0xFFFF);
- s32 lo_result = 0;
- s32 hi_result = 0;
+ s32 lo_result = 0;
+ s32 hi_result = 0;
- // SADD16
- if (inst_cream->op2 == 0x00) {
- lo_result = (rn_lo + rm_lo);
- hi_result = (rn_hi + rm_hi);
- }
- // SASX
- else if (inst_cream->op2 == 0x01) {
- lo_result = (rn_lo - rm_hi);
- hi_result = (rn_hi + rm_lo);
- }
- // SSAX
- else if (inst_cream->op2 == 0x02) {
- lo_result = (rn_lo + rm_hi);
- hi_result = (rn_hi - rm_lo);
- }
- // SSUB16
- else if (inst_cream->op2 == 0x03) {
- lo_result = (rn_lo - rm_lo);
- hi_result = (rn_hi - rm_hi);
- }
+ // SADD16
+ if (inst_cream->op2 == 0x00) {
+ lo_result = (rn_lo + rm_lo);
+ hi_result = (rn_hi + rm_hi);
+ }
+ // SASX
+ else if (op2 == 0x01) {
+ lo_result = (rn_lo - rm_hi);
+ hi_result = (rn_hi + rm_lo);
+ }
+ // SSAX
+ else if (op2 == 0x02) {
+ lo_result = (rn_lo + rm_hi);
+ hi_result = (rn_hi - rm_lo);
+ }
+ // SSUB16
+ else if (op2 == 0x03) {
+ lo_result = (rn_lo - rm_lo);
+ hi_result = (rn_hi - rm_hi);
+ }
- RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+ RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
- if (lo_result >= 0) {
- cpu->Cpsr |= (1 << 16);
- cpu->Cpsr |= (1 << 17);
- } else {
- cpu->Cpsr &= ~(1 << 16);
- cpu->Cpsr &= ~(1 << 17);
+ if (lo_result >= 0) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
}
+ else if (op2 == 0x04 || op2 == 0x07) {
+ s32 lo_val1, lo_val2;
+ s32 hi_val1, hi_val2;
- if (hi_result >= 0) {
- cpu->Cpsr |= (1 << 18);
- cpu->Cpsr |= (1 << 19);
- } else {
- cpu->Cpsr &= ~(1 << 18);
- cpu->Cpsr &= ~(1 << 19);
+ // SADD8
+ if (op2 == 0x04) {
+ lo_val1 = (s32)(s8)(RN & 0xFF) + (s32)(s8)(RM & 0xFF);
+ lo_val2 = (s32)(s8)((RN >> 8) & 0xFF) + (s32)(s8)((RM >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((RN >> 16) & 0xFF) + (s32)(s8)((RM >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((RN >> 24) & 0xFF) + (s32)(s8)((RM >> 24) & 0xFF);
+ }
+ // SSUB8
+ else {
+ lo_val1 = (s32)(s8)(RN & 0xFF) - (s32)(s8)(RM & 0xFF);
+ lo_val2 = (s32)(s8)((RN >> 8) & 0xFF) - (s32)(s8)((RM >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((RN >> 16) & 0xFF) - (s32)(s8)((RM >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((RN >> 24) & 0xFF) - (s32)(s8)((RM >> 24) & 0xFF);
+ }
+
+ RD = ((lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24));
+
+ if (lo_val1 >= 0)
+ cpu->Cpsr |= (1 << 16);
+ else
+ cpu->Cpsr &= ~(1 << 16);
+
+ if (lo_val2 >= 0)
+ cpu->Cpsr |= (1 << 17);
+ else
+ cpu->Cpsr &= ~(1 << 17);
+
+ if (hi_val1 >= 0)
+ cpu->Cpsr |= (1 << 18);
+ else
+ cpu->Cpsr &= ~(1 << 18);
+
+ if (hi_val2 >= 0)
+ cpu->Cpsr |= (1 << 19);
+ else
+ cpu->Cpsr &= ~(1 << 19);
}
}
@@ -5142,12 +5264,79 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SETEND_INST:
- SHADD16_INST:
+
SHADD8_INST:
+ SHADD16_INST:
SHADDSUBX_INST:
- SHSUB16_INST:
SHSUB8_INST:
+ SHSUB16_INST:
SHSUBADDX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u8 op2 = inst_cream->op2;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ if (op2 == 0x00 || op2 == 0x01 || op2 == 0x02 || op2 == 0x03) {
+ s32 lo_result = 0;
+ s32 hi_result = 0;
+
+ // SHADD16
+ if (op2 == 0x00) {
+ lo_result = ((s16)(rn_val & 0xFFFF) + (s16)(rm_val & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) + (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ }
+ // SHASX
+ else if (op2 == 0x01) {
+ lo_result = ((s16)(rn_val & 0xFFFF) - (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) + (s16)(rm_val & 0xFFFF)) >> 1;
+ }
+ // SHSAX
+ else if (op2 == 0x02) {
+ lo_result = ((s16)(rn_val & 0xFFFF) + (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) - (s16)(rm_val & 0xFFFF)) >> 1;
+ }
+ // SHSUB16
+ else if (op2 == 0x03) {
+ lo_result = ((s16)(rn_val & 0xFFFF) - (s16)(rm_val & 0xFFFF)) >> 1;
+ hi_result = ((s16)((rn_val >> 16) & 0xFFFF) - (s16)((rm_val >> 16) & 0xFFFF)) >> 1;
+ }
+
+ RD = ((lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16));
+ }
+ else if (op2 == 0x04 || op2 == 0x07) {
+ s16 lo_val1, lo_val2;
+ s16 hi_val1, hi_val2;
+
+ // SHADD8
+ if (op2 == 0x04) {
+ lo_val1 = ((s8)(rn_val & 0xFF) + (s8)(rm_val & 0xFF)) >> 1;
+ lo_val2 = ((s8)((rn_val >> 8) & 0xFF) + (s8)((rm_val >> 8) & 0xFF)) >> 1;
+
+ hi_val1 = ((s8)((rn_val >> 16) & 0xFF) + (s8)((rm_val >> 16) & 0xFF)) >> 1;
+ hi_val2 = ((s8)((rn_val >> 24) & 0xFF) + (s8)((rm_val >> 24) & 0xFF)) >> 1;
+ }
+ // SHSUB8
+ else {
+ lo_val1 = ((s8)(rn_val & 0xFF) - (s8)(rm_val & 0xFF)) >> 1;
+ lo_val2 = ((s8)((rn_val >> 8) & 0xFF) - (s8)((rm_val >> 8) & 0xFF)) >> 1;
+
+ hi_val1 = ((s8)((rn_val >> 16) & 0xFF) - (s8)((rm_val >> 16) & 0xFF)) >> 1;
+ hi_val2 = ((s8)((rn_val >> 24) & 0xFF) - (s8)((rm_val >> 24) & 0xFF)) >> 1;
+ }
+
+ RD = (lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24);
+ }
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
+
SMLA_INST:
{
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
@@ -5373,7 +5562,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
FETCH_INST;
GOTO_NEXT_INST;
}
- SSUB8_INST:
+
STC_INST:
{
// Instruction not implemented
@@ -5769,9 +5958,177 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
FETCH_INST;
GOTO_NEXT_INST;
}
+
UADD8_INST:
UADD16_INST:
UADDSUBX_INST:
+ USUB8_INST:
+ USUB16_INST:
+ USUBADDX_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ generic_arm_inst* const inst_cream = (generic_arm_inst*)inst_base->component;
+
+ const u8 op2 = inst_cream->op2;
+ const u32 rm_val = RM;
+ const u32 rn_val = RN;
+
+ s32 lo_result = 0;
+ s32 hi_result = 0;
+
+ // UADD16
+ if (op2 == 0x00) {
+ lo_result = (rn_val & 0xFFFF) + (rm_val & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+
+ if (lo_result & 0xFFFF0000) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result & 0xFFFF0000) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // UASX
+ else if (op2 == 0x01) {
+ lo_result = (rn_val & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) + (rm_val & 0xFFFF);
+
+ if (lo_result >= 0) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0x10000) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // USAX
+ else if (op2 == 0x02) {
+ lo_result = (rn_val & 0xFFFF) + ((rm_val >> 16) & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) - (rm_val & 0xFFFF);
+
+ if (lo_result >= 0x10000) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if (hi_result >= 0) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // USUB16
+ else if (op2 == 0x03) {
+ lo_result = (rn_val & 0xFFFF) - (rm_val & 0xFFFF);
+ hi_result = ((rn_val >> 16) & 0xFFFF) - ((rm_val >> 16) & 0xFFFF);
+
+ if ((lo_result & 0xFFFF0000) == 0) {
+ cpu->Cpsr |= (1 << 16);
+ cpu->Cpsr |= (1 << 17);
+ } else {
+ cpu->Cpsr &= ~(1 << 16);
+ cpu->Cpsr &= ~(1 << 17);
+ }
+
+ if ((hi_result & 0xFFFF0000) == 0) {
+ cpu->Cpsr |= (1 << 18);
+ cpu->Cpsr |= (1 << 19);
+ } else {
+ cpu->Cpsr &= ~(1 << 18);
+ cpu->Cpsr &= ~(1 << 19);
+ }
+ }
+ // UADD8
+ else if (op2 == 0x04) {
+ s16 sum1 = (rn_val & 0xFF) + (rm_val & 0xFF);
+ s16 sum2 = ((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF);
+ s16 sum3 = ((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF);
+ s16 sum4 = ((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF);
+
+ if (sum1 >= 0x100)
+ state->Cpsr |= (1 << 16);
+ else
+ state->Cpsr &= ~(1 << 16);
+
+ if (sum2 >= 0x100)
+ state->Cpsr |= (1 << 17);
+ else
+ state->Cpsr &= ~(1 << 17);
+
+ if (sum3 >= 0x100)
+ state->Cpsr |= (1 << 18);
+ else
+ state->Cpsr &= ~(1 << 18);
+
+ if (sum4 >= 0x100)
+ state->Cpsr |= (1 << 19);
+ else
+ state->Cpsr &= ~(1 << 19);
+
+ lo_result = ((sum1 & 0xFF) | (sum2 & 0xFF) << 8);
+ hi_result = ((sum3 & 0xFF) | (sum4 & 0xFF) << 8);
+ }
+ // USUB8
+ else if (op2 == 0x07) {
+ s16 diff1 = (rn_val & 0xFF) - (rm_val & 0xFF);
+ s16 diff2 = ((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF);
+ s16 diff3 = ((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF);
+ s16 diff4 = ((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF);
+
+ if (diff1 >= 0)
+ state->Cpsr |= (1 << 16);
+ else
+ state->Cpsr &= ~(1 << 16);
+
+ if (diff2 >= 0)
+ state->Cpsr |= (1 << 17);
+ else
+ state->Cpsr &= ~(1 << 17);
+
+ if (diff3 >= 0)
+ state->Cpsr |= (1 << 18);
+ else
+ state->Cpsr &= ~(1 << 18);
+
+ if (diff4 >= 0)
+ state->Cpsr |= (1 << 19);
+ else
+ state->Cpsr &= ~(1 << 19);
+
+ lo_result = (diff1 & 0xFF) | ((diff2 & 0xFF) << 8);
+ hi_result = (diff3 & 0xFF) | ((diff4 & 0xFF) << 8);
+ }
+
+ RD = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(generic_arm_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
UHADD8_INST:
UHADD16_INST:
@@ -6109,9 +6466,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
GOTO_NEXT_INST;
}
- USUB16_INST:
- USUB8_INST:
- USUBADDX_INST:
UXTAB16_INST:
UXTB16_INST:
{
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp
index b9c2aa6c2..43b1ba40e 100644
--- a/src/core/arm/interpreter/armemu.cpp
+++ b/src/core/arm/interpreter/armemu.cpp
@@ -5881,67 +5881,45 @@ L_stm_s_takeabort:
const u32 rm_val = state->Reg[rm_idx];
const u32 rn_val = state->Reg[rn_idx];
- u8 lo_val1;
- u8 lo_val2;
- u8 hi_val1;
- u8 hi_val2;
+ s32 lo_val1, lo_val2;
+ s32 hi_val1, hi_val2;
// SADD8
if ((instr & 0xFF0) == 0xf90) {
- lo_val1 = (u8)((rn_val & 0xFF) + (rm_val & 0xFF));
- lo_val2 = (u8)(((rn_val >> 8) & 0xFF) + ((rm_val >> 8) & 0xFF));
- hi_val1 = (u8)(((rn_val >> 16) & 0xFF) + ((rm_val >> 16) & 0xFF));
- hi_val2 = (u8)(((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF));
-
- if (lo_val1 & 0x80)
- state->GEFlag |= (1 << 16);
- else
- state->GEFlag &= ~(1 << 16);
-
- if (lo_val2 & 0x80)
- state->GEFlag |= (1 << 17);
- else
- state->GEFlag &= ~(1 << 17);
-
- if (hi_val1 & 0x80)
- state->GEFlag |= (1 << 18);
- else
- state->GEFlag &= ~(1 << 18);
-
- if (hi_val2 & 0x80)
- state->GEFlag |= (1 << 19);
- else
- state->GEFlag &= ~(1 << 19);
+ lo_val1 = (s32)(s8)(rn_val & 0xFF) + (s32)(s8)(rm_val & 0xFF);
+ lo_val2 = (s32)(s8)((rn_val >> 8) & 0xFF) + (s32)(s8)((rm_val >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((rn_val >> 16) & 0xFF) + (s32)(s8)((rm_val >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((rn_val >> 24) & 0xFF) + (s32)(s8)((rm_val >> 24) & 0xFF);
}
// SSUB8
else {
- lo_val1 = (u8)((rn_val & 0xFF) - (rm_val & 0xFF));
- lo_val2 = (u8)(((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF));
- hi_val1 = (u8)(((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF));
- hi_val2 = (u8)(((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF));
+ lo_val1 = (s32)(s8)(rn_val & 0xFF) - (s32)(s8)(rm_val & 0xFF);
+ lo_val2 = (s32)(s8)((rn_val >> 8) & 0xFF) - (s32)(s8)((rm_val >> 8) & 0xFF);
+ hi_val1 = (s32)(s8)((rn_val >> 16) & 0xFF) - (s32)(s8)((rm_val >> 16) & 0xFF);
+ hi_val2 = (s32)(s8)((rn_val >> 24) & 0xFF) - (s32)(s8)((rm_val >> 24) & 0xFF);
+ }
- if (!(lo_val1 & 0x80))
- state->GEFlag |= (1 << 16);
- else
- state->GEFlag &= ~(1 << 16);
+ if (lo_val1 >= 0)
+ state->GEFlag |= (1 << 16);
+ else
+ state->GEFlag &= ~(1 << 16);
- if (!(lo_val2 & 0x80))
- state->GEFlag |= (1 << 17);
- else
- state->GEFlag &= ~(1 << 17);
+ if (lo_val2 >= 0)
+ state->GEFlag |= (1 << 17);
+ else
+ state->GEFlag &= ~(1 << 17);
- if (!(hi_val1 & 0x80))
- state->GEFlag |= (1 << 18);
- else
- state->GEFlag &= ~(1 << 18);
+ if (hi_val1 >= 0)
+ state->GEFlag |= (1 << 18);
+ else
+ state->GEFlag &= ~(1 << 18);
- if (!(hi_val2 & 0x80))
- state->GEFlag |= (1 << 19);
- else
- state->GEFlag &= ~(1 << 19);
- }
+ if (hi_val2 >= 0)
+ state->GEFlag |= (1 << 19);
+ else
+ state->GEFlag &= ~(1 << 19);
- state->Reg[rd_idx] = (lo_val1 | lo_val2 << 8 | hi_val1 << 16 | hi_val2 << 24);
+ state->Reg[rd_idx] = ((lo_val1 & 0xFF) | ((lo_val2 & 0xFF) << 8) | ((hi_val1 & 0xFF) << 16) | ((hi_val2 & 0xFF) << 24));
return 1;
}
else {
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 3259ce9eb..0f822f84b 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -128,6 +128,13 @@ template<s32 func(s32*, u32, s32)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index ef4f9829d..9fbf18b26 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -2,40 +2,712 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/platform.h"
+
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <poll.h>
+#endif
+
#include "common/log.h"
+#include "common/scope_exit.h"
#include "core/hle/hle.h"
#include "core/hle/service/soc_u.h"
+#include <unordered_map>
+
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+# define WSAEAGAIN WSAEWOULDBLOCK
+# define WSAEMULTIHOP -1 // Invalid dummy value
+# define ERRNO(x) WSA##x
+# define GET_ERRNO WSAGetLastError()
+# define poll(x, y, z) WSAPoll(x, y, z);
+#else
+# define ERRNO(x) x
+# define GET_ERRNO errno
+# define closesocket(x) close(x)
+#endif
+
+static const s32 SOCKET_ERROR_VALUE = -1;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SOC_U
namespace SOC_U {
+/// Holds the translation from system network errors to 3DS network errors
+static const std::unordered_map<int, int> error_map = { {
+ { E2BIG, 1 },
+ { ERRNO(EACCES), 2 },
+ { ERRNO(EADDRINUSE), 3 },
+ { ERRNO(EADDRNOTAVAIL), 4 },
+ { ERRNO(EAFNOSUPPORT), 5 },
+ { ERRNO(EAGAIN), 6 },
+ { ERRNO(EALREADY), 7 },
+ { ERRNO(EBADF), 8 },
+ { EBADMSG, 9 },
+ { EBUSY, 10 },
+ { ECANCELED, 11 },
+ { ECHILD, 12 },
+ { ERRNO(ECONNABORTED), 13 },
+ { ERRNO(ECONNREFUSED), 14 },
+ { ERRNO(ECONNRESET), 15 },
+ { EDEADLK, 16 },
+ { ERRNO(EDESTADDRREQ), 17 },
+ { EDOM, 18 },
+ { ERRNO(EDQUOT), 19 },
+ { EEXIST, 20 },
+ { ERRNO(EFAULT), 21 },
+ { EFBIG, 22 },
+ { ERRNO(EHOSTUNREACH), 23 },
+ { EIDRM, 24 },
+ { EILSEQ, 25 },
+ { ERRNO(EINPROGRESS), 26 },
+ { ERRNO(EINTR), 27 },
+ { ERRNO(EINVAL), 28 },
+ { EIO, 29 },
+ { ERRNO(EISCONN), 30 },
+ { EISDIR, 31 },
+ { ERRNO(ELOOP), 32 },
+ { ERRNO(EMFILE), 33 },
+ { EMLINK, 34 },
+ { ERRNO(EMSGSIZE), 35 },
+ { ERRNO(EMULTIHOP), 36 },
+ { ERRNO(ENAMETOOLONG), 37 },
+ { ERRNO(ENETDOWN), 38 },
+ { ERRNO(ENETRESET), 39 },
+ { ERRNO(ENETUNREACH), 40 },
+ { ENFILE, 41 },
+ { ERRNO(ENOBUFS), 42 },
+ { ENODATA, 43 },
+ { ENODEV, 44 },
+ { ENOENT, 45 },
+ { ENOEXEC, 46 },
+ { ENOLCK, 47 },
+ { ENOLINK, 48 },
+ { ENOMEM, 49 },
+ { ENOMSG, 50 },
+ { ERRNO(ENOPROTOOPT), 51 },
+ { ENOSPC, 52 },
+ { ENOSR, 53 },
+ { ENOSTR, 54 },
+ { ENOSYS, 55 },
+ { ERRNO(ENOTCONN), 56 },
+ { ENOTDIR, 57 },
+ { ERRNO(ENOTEMPTY), 58 },
+ { ERRNO(ENOTSOCK), 59 },
+ { ENOTSUP, 60 },
+ { ENOTTY, 61 },
+ { ENXIO, 62 },
+ { ERRNO(EOPNOTSUPP), 63 },
+ { EOVERFLOW, 64 },
+ { EPERM, 65 },
+ { EPIPE, 66 },
+ { EPROTO, 67 },
+ { ERRNO(EPROTONOSUPPORT), 68 },
+ { ERRNO(EPROTOTYPE), 69 },
+ { ERANGE, 70 },
+ { EROFS, 71 },
+ { ESPIPE, 72 },
+ { ESRCH, 73 },
+ { ERRNO(ESTALE), 74 },
+ { ETIME, 75 },
+ { ERRNO(ETIMEDOUT), 76 }
+}};
+
+/// Converts a network error from platform-specific to 3ds-specific
+static int TranslateError(int error) {
+ auto found = error_map.find(error);
+ if (found != error_map.end())
+ return -found->second;
+
+ return error;
+}
+
+/// Holds information about a particular socket
+struct SocketHolder {
+ u32 socket_fd; ///< The socket descriptor
+ bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
+};
+
+/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
+struct CTRPollFD {
+ u32 fd; ///< Socket handle
+
+ union Events {
+ u32 hex; ///< The complete value formed by the flags
+ BitField<0, 1, u32> pollin;
+ BitField<1, 1, u32> pollpri;
+ BitField<2, 1, u32> pollhup;
+ BitField<3, 1, u32> pollerr;
+ BitField<4, 1, u32> pollout;
+ BitField<5, 1, u32> pollnval;
+
+ Events& operator=(const Events& other) {
+ hex = other.hex;
+ return *this;
+ }
+
+ /// Translates the resulting events of a Poll operation from platform-specific to 3ds specific
+ static Events TranslateTo3DS(u32 input_event) {
+ Events ev = {};
+ if (input_event & POLLIN)
+ ev.pollin = 1;
+ if (input_event & POLLPRI)
+ ev.pollpri = 1;
+ if (input_event & POLLHUP)
+ ev.pollhup = 1;
+ if (input_event & POLLERR)
+ ev.pollerr = 1;
+ if (input_event & POLLOUT)
+ ev.pollout = 1;
+ if (input_event & POLLNVAL)
+ ev.pollnval = 1;
+ return ev;
+ }
+
+ /// Translates the resulting events of a Poll operation from 3ds specific to platform specific
+ static u32 TranslateToPlatform(Events input_event) {
+ u32 ret = 0;
+ if (input_event.pollin)
+ ret |= POLLIN;
+ if (input_event.pollpri)
+ ret |= POLLPRI;
+ if (input_event.pollhup)
+ ret |= POLLHUP;
+ if (input_event.pollerr)
+ ret |= POLLERR;
+ if (input_event.pollout)
+ ret |= POLLOUT;
+ if (input_event.pollnval)
+ ret |= POLLNVAL;
+ return ret;
+ }
+ };
+ Events events; ///< Events to poll for (input)
+ Events revents; ///< Events received (output)
+
+ /// Converts a platform-specific pollfd to a 3ds specific structure
+ static CTRPollFD FromPlatform(pollfd const& fd) {
+ CTRPollFD result;
+ result.events.hex = Events::TranslateTo3DS(fd.events).hex;
+ result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
+ result.fd = static_cast<u32>(fd.fd);
+ return result;
+ }
+
+ /// Converts a 3ds specific pollfd to a platform-specific structure
+ static pollfd ToPlatform(CTRPollFD const& fd) {
+ pollfd result;
+ result.events = Events::TranslateToPlatform(fd.events);
+ result.revents = Events::TranslateToPlatform(fd.revents);
+ result.fd = fd.fd;
+ return result;
+ }
+};
+
+/// Union to represent the 3ds' sockaddr structure
+union CTRSockAddr {
+ /// Structure to represent a raw sockaddr
+ struct {
+ u8 len; ///< The length of the entire structure, only the set fields count
+ u8 sa_family; ///< The address family of the sockaddr
+ u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family
+ } raw;
+
+ /// Structure to represent the 3ds' sockaddr_in structure
+ struct CTRSockAddrIn {
+ u8 len; ///< The length of the entire structure
+ u8 sin_family; ///< The address family of the sockaddr_in
+ u16 sin_port; ///< The port associated with this sockaddr_in
+ u32 sin_addr; ///< The actual address of the sockaddr_in
+ } in;
+
+ /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
+ static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) {
+ sockaddr result;
+ result.sa_family = ctr_addr.raw.sa_family;
+ memset(result.sa_data, 0, sizeof(result.sa_data));
+
+ // We can not guarantee ABI compatibility between platforms so we copy the fields manually
+ switch (result.sa_family) {
+ case AF_INET:
+ {
+ sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result);
+ result_in->sin_port = ctr_addr.in.sin_port;
+ result_in->sin_addr.s_addr = ctr_addr.in.sin_addr;
+ memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero));
+ break;
+ }
+ default:
+ _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
+ break;
+ }
+ return result;
+ }
+
+ /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr
+ static CTRSockAddr FromPlatform(sockaddr const& addr) {
+ CTRSockAddr result;
+ result.raw.sa_family = static_cast<u8>(addr.sa_family);
+ // We can not guarantee ABI compatibility between platforms so we copy the fields manually
+ switch (result.raw.sa_family) {
+ case AF_INET:
+ {
+ sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr);
+ result.raw.len = sizeof(CTRSockAddrIn);
+ result.in.sin_port = addr_in->sin_port;
+ result.in.sin_addr = addr_in->sin_addr.s_addr;
+ break;
+ }
+ default:
+ _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
+ break;
+ }
+ return result;
+ }
+};
+
+/// Holds info about the currently open sockets
+static std::unordered_map<u32, SocketHolder> open_sockets;
+
+/// Close all open sockets
+static void CleanupSockets() {
+ for (auto sock : open_sockets)
+ closesocket(sock.second.socket_fd);
+ open_sockets.clear();
+}
+
+static void Socket(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 domain = cmd_buffer[1]; // Address family
+ u32 type = cmd_buffer[2];
+ u32 protocol = cmd_buffer[3];
+
+ // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
+ if (protocol != 0) {
+ cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
+ return;
+ }
+
+ if (domain != AF_INET) {
+ cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
+ return;
+ }
+
+ if (type != SOCK_DGRAM && type != SOCK_STREAM) {
+ cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
+ return;
+ }
+
+ u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol));
+
+ if (socket_handle != SOCKET_ERROR_VALUE)
+ open_sockets[socket_handle] = { socket_handle, true };
+
+ int result = 0;
+ if (socket_handle == SOCKET_ERROR_VALUE)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[1] = result;
+ cmd_buffer[2] = socket_handle;
+}
+
+static void Bind(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 len = cmd_buffer[2];
+ CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
+
+ if (ctr_sock_addr == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Correct code
+ return;
+ }
+
+ sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr);
+
+ int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
+
+ int result = 0;
+ if (res != 0)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[2] = res;
+ cmd_buffer[1] = result;
+}
+
+static void Fcntl(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 ctr_cmd = cmd_buffer[2];
+ u32 ctr_arg = cmd_buffer[3];
+
+ int result = 0;
+ u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
+ SCOPE_EXIT({
+ cmd_buffer[1] = result;
+ cmd_buffer[2] = posix_ret;
+ });
+
+ if (ctr_cmd == 3) { // F_GETFL
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ posix_ret = 0;
+ auto iter = open_sockets.find(socket_handle);
+ if (iter != open_sockets.end() && iter->second.blocking == false)
+ posix_ret |= 4; // O_NONBLOCK
+#else
+ int ret = ::fcntl(socket_handle, F_GETFL, 0);
+ if (ret == SOCKET_ERROR_VALUE) {
+ result = TranslateError(GET_ERRNO);
+ posix_ret = -1;
+ return;
+ }
+ posix_ret = 0;
+ if (ret & O_NONBLOCK)
+ posix_ret |= 4; // O_NONBLOCK
+#endif
+ } else if (ctr_cmd == 4) { // F_SETFL
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
+ int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
+ if (ret == SOCKET_ERROR_VALUE) {
+ result = TranslateError(GET_ERRNO);
+ posix_ret = -1;
+ return;
+ }
+ auto iter = open_sockets.find(socket_handle);
+ if (iter != open_sockets.end())
+ iter->second.blocking = (tmp == 0);
+#else
+ int flags = ::fcntl(socket_handle, F_GETFL, 0);
+ if (flags == SOCKET_ERROR_VALUE) {
+ result = TranslateError(GET_ERRNO);
+ posix_ret = -1;
+ return;
+ }
+
+ flags &= ~O_NONBLOCK;
+ if (ctr_arg & 4) // O_NONBLOCK
+ flags |= O_NONBLOCK;
+
+ int ret = ::fcntl(socket_handle, F_SETFL, flags);
+ if (ret == SOCKET_ERROR_VALUE) {
+ result = TranslateError(GET_ERRNO);
+ posix_ret = -1;
+ return;
+ }
+#endif
+ } else {
+ LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call");
+ result = TranslateError(EINVAL); // TODO: Find the correct error
+ posix_ret = -1;
+ return;
+ }
+}
+
+static void Listen(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 backlog = cmd_buffer[2];
+
+ int ret = ::listen(socket_handle, backlog);
+ int result = 0;
+ if (ret != 0)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void Accept(Service::Interface* self) {
+ // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
+ // preventing graceful shutdown when closing the emulator, this can be fixed by always
+ // performing nonblocking operations and spinlock until the data is available
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]);
+ sockaddr addr;
+ socklen_t addr_len = sizeof(addr);
+ u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len));
+
+ if (ret != SOCKET_ERROR_VALUE)
+ open_sockets[ret] = { ret, true };
+
+ int result = 0;
+ if (ret == SOCKET_ERROR_VALUE) {
+ result = TranslateError(GET_ERRNO);
+ } else {
+ CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
+ Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len);
+ }
+
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void GetHostId(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+
+ char name[128];
+ gethostname(name, sizeof(name));
+ hostent* host = gethostbyname(name);
+ in_addr* addr = reinterpret_cast<in_addr*>(host->h_addr);
+
+ cmd_buffer[2] = addr->s_addr;
+ cmd_buffer[1] = 0;
+}
+
+static void Close(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+
+ int ret = 0;
+ open_sockets.erase(socket_handle);
+
+ ret = closesocket(socket_handle);
+
+ int result = 0;
+ if (ret != 0)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void SendTo(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 len = cmd_buffer[2];
+ u32 flags = cmd_buffer[3];
+ u32 addr_len = cmd_buffer[4];
+
+ u8* input_buff = Memory::GetPointer(cmd_buffer[8]);
+ CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10]));
+
+ if (ctr_dest_addr == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ int ret = -1;
+ if (addr_len > 0) {
+ sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr);
+ ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr));
+ } else {
+ ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0);
+ }
+
+ int result = 0;
+ if (ret == SOCKET_ERROR_VALUE)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void RecvFrom(Service::Interface* self) {
+ // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
+ // preventing graceful shutdown when closing the emulator, this can be fixed by always
+ // performing nonblocking operations and spinlock until the data is available
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 len = cmd_buffer[2];
+ u32 flags = cmd_buffer[3];
+ socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
+
+ u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
+ sockaddr src_addr;
+ socklen_t src_addr_len = sizeof(src_addr);
+ int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
+
+ if (cmd_buffer[0x1A0 >> 2] != 0) {
+ CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2]));
+ *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
+ }
+
+ int result = 0;
+ int total_received = ret;
+ if (ret == SOCKET_ERROR_VALUE) {
+ result = TranslateError(GET_ERRNO);
+ total_received = 0;
+ }
+
+ cmd_buffer[1] = result;
+ cmd_buffer[2] = ret;
+ cmd_buffer[3] = total_received;
+}
+
+static void Poll(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 nfds = cmd_buffer[1];
+ int timeout = cmd_buffer[2];
+ CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6]));
+ CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+ // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
+ // so we have to copy the data
+ pollfd* platform_pollfd = new pollfd[nfds];
+ for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
+ platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
+
+ int ret = ::poll(platform_pollfd, nfds, timeout);
+
+ // Now update the output pollfd structure
+ for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
+ output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
+
+ delete[] platform_pollfd;
+
+ int result = 0;
+ if (ret == SOCKET_ERROR_VALUE)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[1] = result;
+ cmd_buffer[2] = ret;
+}
+
+static void GetSockName(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ socklen_t ctr_len = cmd_buffer[2];
+
+ CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+ sockaddr dest_addr;
+ socklen_t dest_addr_len = sizeof(dest_addr);
+ int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
+
+ if (ctr_dest_addr != nullptr) {
+ *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ } else {
+ cmd_buffer[1] = -1; // TODO(Subv): Verify error
+ return;
+ }
+
+ int result = 0;
+ if (ret != 0)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void Shutdown(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ int how = cmd_buffer[2];
+
+ int ret = ::shutdown(socket_handle, how);
+ int result = 0;
+ if (ret != 0)
+ result = TranslateError(GET_ERRNO);
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void GetPeerName(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ socklen_t len = cmd_buffer[2];
+
+ CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+ sockaddr dest_addr;
+ socklen_t dest_addr_len = sizeof(dest_addr);
+ int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
+
+ if (ctr_dest_addr != nullptr) {
+ *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ } else {
+ cmd_buffer[1] = -1;
+ return;
+ }
+
+ int result = 0;
+ if (ret != 0)
+ result = TranslateError(GET_ERRNO);
+
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void Connect(Service::Interface* self) {
+ // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
+ // preventing graceful shutdown when closing the emulator, this can be fixed by always
+ // performing nonblocking operations and spinlock until the data is available
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ socklen_t len = cmd_buffer[2];
+
+ CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
+ if (ctr_input_addr == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Verify error
+ return;
+ }
+
+ sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr);
+ int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
+ int result = 0;
+ if (ret != 0)
+ result = TranslateError(GET_ERRNO);
+ cmd_buffer[2] = ret;
+ cmd_buffer[1] = result;
+}
+
+static void InitializeSockets(Service::Interface* self) {
+ // TODO(Subv): Implement
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 2), &data);
+#endif
+
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ cmd_buffer[1] = 0;
+}
+
+static void ShutdownSockets(Service::Interface* self) {
+ // TODO(Subv): Implement
+ CleanupSockets();
+
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ WSACleanup();
+#endif
+
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ cmd_buffer[1] = 0;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010044, nullptr, "InitializeSockets"},
- {0x000200C2, nullptr, "socket"},
- {0x00030082, nullptr, "listen"},
- {0x00040082, nullptr, "accept"},
- {0x00050084, nullptr, "bind"},
- {0x00060084, nullptr, "connect"},
+ {0x00010044, InitializeSockets, "InitializeSockets"},
+ {0x000200C2, Socket, "Socket"},
+ {0x00030082, Listen, "Listen"},
+ {0x00040082, Accept, "Accept"},
+ {0x00050084, Bind, "Bind"},
+ {0x00060084, Connect, "Connect"},
{0x00070104, nullptr, "recvfrom_other"},
- {0x00080102, nullptr, "recvfrom"},
+ {0x00080102, RecvFrom, "RecvFrom"},
{0x00090106, nullptr, "sendto_other"},
- {0x000A0106, nullptr, "sendto"},
- {0x000B0042, nullptr, "close"},
- {0x000C0082, nullptr, "shutdown"},
- {0x000D0082, nullptr, "gethostbyname"},
- {0x000E00C2, nullptr, "gethostbyaddr"},
+ {0x000A0106, SendTo, "SendTo"},
+ {0x000B0042, Close, "Close"},
+ {0x000C0082, Shutdown, "Shutdown"},
+ {0x000D0082, nullptr, "GetHostByName"},
+ {0x000E00C2, nullptr, "GetHostByAddr"},
{0x000F0106, nullptr, "unknown_resolve_ip"},
- {0x00110102, nullptr, "getsockopt"},
- {0x00120104, nullptr, "setsockopt"},
- {0x001300C2, nullptr, "fcntl"},
- {0x00140084, nullptr, "poll"},
- {0x00150042, nullptr, "sockatmark"},
- {0x00160000, nullptr, "gethostid"},
- {0x00170082, nullptr, "getsockname"},
- {0x00180082, nullptr, "getpeername"},
- {0x00190000, nullptr, "ShutdownSockets"},
+ {0x00110102, nullptr, "GetSockOpt"},
+ {0x00120104, nullptr, "SetSockOpt"},
+ {0x001300C2, Fcntl, "Fcntl"},
+ {0x00140084, Poll, "Poll"},
+ {0x00150042, nullptr, "SockAtMark"},
+ {0x00160000, GetHostId, "GetHostId"},
+ {0x00170082, GetSockName, "GetSockName"},
+ {0x00180082, GetPeerName, "GetPeerName"},
+ {0x00190000, ShutdownSockets, "ShutdownSockets"},
{0x001A00C0, nullptr, "GetNetworkOpt"},
{0x001B0040, nullptr, "ICMPSocket"},
{0x001C0104, nullptr, "ICMPPing"},
@@ -52,4 +724,11 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
+Interface::~Interface() {
+ CleanupSockets();
+#if EMU_PLATFORM == PLATFORM_WINDOWS
+ WSACleanup();
+#endif
+}
+
} // namespace
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index 2edf3b482..483b3111b 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -14,6 +14,7 @@ namespace SOC_U {
class Interface : public Service::Interface {
public:
Interface();
+ ~Interface();
std::string GetPortName() const override {
return "soc:U";
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 25944fc68..c25409a9f 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -352,6 +352,18 @@ static s64 GetSystemTick() {
return (s64)Core::g_app_core->GetTicks();
}
+/// Creates a memory block at the specified address with the specified permissions and size
+static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission,
+ u32 other_permission) {
+
+ // TODO(Subv): Implement this function
+
+ Handle shared_memory = Kernel::CreateSharedMemory();
+ *memblock = shared_memory;
+ LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
+ return 0;
+}
+
const HLE::FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"},
{0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
@@ -383,7 +395,7 @@ const HLE::FunctionDef SVC_Table[] = {
{0x1B, nullptr, "SetTimer"},
{0x1C, nullptr, "CancelTimer"},
{0x1D, nullptr, "ClearTimer"},
- {0x1E, nullptr, "CreateMemoryBlock"},
+ {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
{0x20, nullptr, "UnmapMemoryBlock"},
{0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},