diff options
-rw-r--r-- | src/citra/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/citra_qt/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/citra_qt/debugger/graphics.cpp | 2 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 1 | ||||
-rw-r--r-- | src/common/logging/log.h | 1 | ||||
-rw-r--r-- | src/core/arm/dyncom/arm_dyncom_interpreter.cpp | 498 | ||||
-rw-r--r-- | src/core/arm/interpreter/armemu.cpp | 78 | ||||
-rw-r--r-- | src/core/hle/function_wrappers.h | 7 | ||||
-rw-r--r-- | src/core/hle/service/soc_u.cpp | 721 | ||||
-rw-r--r-- | src/core/hle/service/soc_u.h | 1 | ||||
-rw-r--r-- | src/core/hle/svc.cpp | 14 |
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(¶m_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"}, |