diff options
| -rw-r--r-- | src/common/common_paths.h | 1 | ||||
| -rw-r--r-- | src/common/file_util.cpp | 2 | ||||
| -rw-r--r-- | src/common/file_util.h | 1 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/arm/interpreter/armemu.cpp | 241 | ||||
| -rw-r--r-- | src/core/file_sys/archive_savedata.h | 3 | ||||
| -rw-r--r-- | src/core/file_sys/archive_systemsavedata.cpp | 33 | ||||
| -rw-r--r-- | src/core/file_sys/archive_systemsavedata.h | 33 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.h | 14 | ||||
| -rw-r--r-- | src/core/hle/service/fs/archive.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 26 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/gpu_debugger.h | 2 |
13 files changed, 277 insertions, 94 deletions
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index a86889756..966402a3d 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -42,6 +42,7 @@ | |||
| 42 | #define SDMC_DIR "sdmc" | 42 | #define SDMC_DIR "sdmc" |
| 43 | #define SAVEDATA_DIR "savedata" | 43 | #define SAVEDATA_DIR "savedata" |
| 44 | #define SYSDATA_DIR "sysdata" | 44 | #define SYSDATA_DIR "sysdata" |
| 45 | #define SYSSAVEDATA_DIR "syssavedata" | ||
| 45 | #define SHADERCACHE_DIR "shader_cache" | 46 | #define SHADERCACHE_DIR "shader_cache" |
| 46 | #define STATESAVES_DIR "state_saves" | 47 | #define STATESAVES_DIR "state_saves" |
| 47 | #define SCREENSHOTS_DIR "screenShots" | 48 | #define SCREENSHOTS_DIR "screenShots" |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 42cdf3262..20c680571 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -678,6 +678,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 679 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | 679 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; |
| 680 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | 680 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; |
| 681 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 681 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 682 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 682 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 683 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 683 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 684 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
| @@ -720,6 +721,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 720 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 721 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 721 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 722 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 722 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | 723 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; |
| 724 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 723 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 725 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 724 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 726 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 725 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 727 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
diff --git a/src/common/file_util.h b/src/common/file_util.h index e691b6139..b1a60fb81 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -29,6 +29,7 @@ enum { | |||
| 29 | D_SDMC_IDX, | 29 | D_SDMC_IDX, |
| 30 | D_SAVEDATA_IDX, | 30 | D_SAVEDATA_IDX, |
| 31 | D_SYSDATA_IDX, | 31 | D_SYSDATA_IDX, |
| 32 | D_SYSSAVEDATA_IDX, | ||
| 32 | D_HIRESTEXTURES_IDX, | 33 | D_HIRESTEXTURES_IDX, |
| 33 | D_DUMP_IDX, | 34 | D_DUMP_IDX, |
| 34 | D_DUMPFRAMES_IDX, | 35 | D_DUMPFRAMES_IDX, |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f71232c1a..3381524e3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -20,6 +20,7 @@ set(SRCS | |||
| 20 | file_sys/archive_romfs.cpp | 20 | file_sys/archive_romfs.cpp |
| 21 | file_sys/archive_savedata.cpp | 21 | file_sys/archive_savedata.cpp |
| 22 | file_sys/archive_sdmc.cpp | 22 | file_sys/archive_sdmc.cpp |
| 23 | file_sys/archive_systemsavedata.cpp | ||
| 23 | file_sys/disk_archive.cpp | 24 | file_sys/disk_archive.cpp |
| 24 | file_sys/file_romfs.cpp | 25 | file_sys/file_romfs.cpp |
| 25 | file_sys/directory_romfs.cpp | 26 | file_sys/directory_romfs.cpp |
| @@ -101,6 +102,7 @@ set(HEADERS | |||
| 101 | file_sys/archive_romfs.h | 102 | file_sys/archive_romfs.h |
| 102 | file_sys/archive_savedata.h | 103 | file_sys/archive_savedata.h |
| 103 | file_sys/archive_sdmc.h | 104 | file_sys/archive_sdmc.h |
| 105 | file_sys/archive_systemsavedata.h | ||
| 104 | file_sys/disk_archive.h | 106 | file_sys/disk_archive.h |
| 105 | file_sys/file_backend.h | 107 | file_sys/file_backend.h |
| 106 | file_sys/file_romfs.h | 108 | file_sys/file_romfs.h |
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 1a589e39c..63cfd03c6 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp | |||
| @@ -1356,7 +1356,13 @@ mainswitch: | |||
| 1356 | } | 1356 | } |
| 1357 | break; | 1357 | break; |
| 1358 | 1358 | ||
| 1359 | case 0x04: /* SUB reg */ | 1359 | case 0x04: /* SUB reg */ |
| 1360 | // Signifies UMAAL | ||
| 1361 | if (state->is_v6 && BITS(4, 7) == 0x09) { | ||
| 1362 | if (handle_v6_insn(state, instr)) | ||
| 1363 | break; | ||
| 1364 | } | ||
| 1365 | |||
| 1360 | #ifdef MODET | 1366 | #ifdef MODET |
| 1361 | if (BITS (4, 7) == 0xB) { | 1367 | if (BITS (4, 7) == 0xB) { |
| 1362 | /* STRH immediate offset, no write-back, down, post indexed. */ | 1368 | /* STRH immediate offset, no write-back, down, post indexed. */ |
| @@ -3103,12 +3109,18 @@ mainswitch: | |||
| 3103 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); | 3109 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); |
| 3104 | break; | 3110 | break; |
| 3105 | } else if ((instr & 0x70) == 0x50) { //pkhtb | 3111 | } else if ((instr & 0x70) == 0x50) { //pkhtb |
| 3106 | u8 idest = BITS(12, 15); | 3112 | const u8 rd_idx = BITS(12, 15); |
| 3107 | u8 rfis = BITS(16, 19); | 3113 | const u8 rn_idx = BITS(16, 19); |
| 3108 | u8 rlast = BITS(0, 3); | 3114 | const u8 rm_idx = BITS(0, 3); |
| 3109 | u8 ishi = BITS(7, 11); | 3115 | const u8 imm5 = BITS(7, 11); |
| 3110 | if (ishi == 0)ishi = 0x20; | 3116 | |
| 3111 | state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000); | 3117 | ARMword val; |
| 3118 | if (imm5 >= 32) | ||
| 3119 | val = (state->Reg[rm_idx] >> 31); | ||
| 3120 | else | ||
| 3121 | val = (state->Reg[rm_idx] >> imm5); | ||
| 3122 | |||
| 3123 | state->Reg[rd_idx] = (val & 0xFFFF) | ((state->Reg[rn_idx]) & 0xFFFF0000); | ||
| 3112 | break; | 3124 | break; |
| 3113 | } else if (BIT (4)) { | 3125 | } else if (BIT (4)) { |
| 3114 | #ifdef MODE32 | 3126 | #ifdef MODE32 |
| @@ -5669,16 +5681,29 @@ L_stm_s_takeabort: | |||
| 5669 | /* Attempt to emulate an ARMv6 instruction. | 5681 | /* Attempt to emulate an ARMv6 instruction. |
| 5670 | Returns non-zero upon success. */ | 5682 | Returns non-zero upon success. */ |
| 5671 | 5683 | ||
| 5672 | static int | 5684 | static int handle_v6_insn(ARMul_State* state, ARMword instr) { |
| 5673 | handle_v6_insn (ARMul_State * state, ARMword instr) { | 5685 | switch (BITS(20, 27)) { |
| 5674 | ARMword lhs, temp; | ||
| 5675 | |||
| 5676 | switch (BITS (20, 27)) { | ||
| 5677 | case 0x03: | 5686 | case 0x03: |
| 5678 | printf ("Unhandled v6 insn: ldr\n"); | 5687 | printf ("Unhandled v6 insn: ldr\n"); |
| 5679 | break; | 5688 | break; |
| 5680 | case 0x04: | 5689 | case 0x04: // UMAAL |
| 5681 | printf ("Unhandled v6 insn: umaal\n"); | 5690 | { |
| 5691 | const u8 rm_idx = BITS(8, 11); | ||
| 5692 | const u8 rn_idx = BITS(0, 3); | ||
| 5693 | const u8 rd_lo_idx = BITS(12, 15); | ||
| 5694 | const u8 rd_hi_idx = BITS(16, 19); | ||
| 5695 | |||
| 5696 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 5697 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 5698 | const u32 rd_lo_val = state->Reg[rd_lo_idx]; | ||
| 5699 | const u32 rd_hi_val = state->Reg[rd_hi_idx]; | ||
| 5700 | |||
| 5701 | const u64 result = (rn_val * rm_val) + rd_lo_val + rd_hi_val; | ||
| 5702 | |||
| 5703 | state->Reg[rd_lo_idx] = (result & 0xFFFFFFFF); | ||
| 5704 | state->Reg[rd_hi_idx] = ((result >> 32) & 0xFFFFFFFF); | ||
| 5705 | return 1; | ||
| 5706 | } | ||
| 5682 | break; | 5707 | break; |
| 5683 | case 0x06: | 5708 | case 0x06: |
| 5684 | printf ("Unhandled v6 insn: mls/str\n"); | 5709 | printf ("Unhandled v6 insn: mls/str\n"); |
| @@ -5691,7 +5716,7 @@ L_stm_s_takeabort: | |||
| 5691 | /* strex */ | 5716 | /* strex */ |
| 5692 | u32 l = LHSReg; | 5717 | u32 l = LHSReg; |
| 5693 | u32 r = RHSReg; | 5718 | u32 r = RHSReg; |
| 5694 | lhs = LHS; | 5719 | u32 lhs = LHS; |
| 5695 | 5720 | ||
| 5696 | bool enter = false; | 5721 | bool enter = false; |
| 5697 | 5722 | ||
| @@ -5716,7 +5741,7 @@ L_stm_s_takeabort: | |||
| 5716 | case 0x19: | 5741 | case 0x19: |
| 5717 | /* ldrex */ | 5742 | /* ldrex */ |
| 5718 | if (BITS(4, 7) == 0x9) { | 5743 | if (BITS(4, 7) == 0x9) { |
| 5719 | lhs = LHS; | 5744 | u32 lhs = LHS; |
| 5720 | 5745 | ||
| 5721 | state->currentexaddr = lhs; | 5746 | state->currentexaddr = lhs; |
| 5722 | state->currentexval = ARMul_ReadWord(state, lhs); | 5747 | state->currentexval = ARMul_ReadWord(state, lhs); |
| @@ -5735,7 +5760,7 @@ L_stm_s_takeabort: | |||
| 5735 | case 0x1c: | 5760 | case 0x1c: |
| 5736 | if (BITS(4, 7) == 0x9) { | 5761 | if (BITS(4, 7) == 0x9) { |
| 5737 | /* strexb */ | 5762 | /* strexb */ |
| 5738 | lhs = LHS; | 5763 | u32 lhs = LHS; |
| 5739 | 5764 | ||
| 5740 | bool enter = false; | 5765 | bool enter = false; |
| 5741 | 5766 | ||
| @@ -5765,11 +5790,11 @@ L_stm_s_takeabort: | |||
| 5765 | case 0x1d: | 5790 | case 0x1d: |
| 5766 | if ((BITS(4, 7)) == 0x9) { | 5791 | if ((BITS(4, 7)) == 0x9) { |
| 5767 | /* ldrexb */ | 5792 | /* ldrexb */ |
| 5768 | temp = LHS; | 5793 | u32 lhs = LHS; |
| 5769 | LoadByte(state, instr, temp, LUNSIGNED); | 5794 | LoadByte(state, instr, lhs, LUNSIGNED); |
| 5770 | 5795 | ||
| 5771 | state->currentexaddr = temp; | 5796 | state->currentexaddr = lhs; |
| 5772 | state->currentexval = (u32)ARMul_ReadByte(state, temp); | 5797 | state->currentexval = (u32)ARMul_ReadByte(state, lhs); |
| 5773 | 5798 | ||
| 5774 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); | 5799 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); |
| 5775 | //printf("ldrexb\n"); | 5800 | //printf("ldrexb\n"); |
| @@ -5799,49 +5824,63 @@ L_stm_s_takeabort: | |||
| 5799 | case 0x3f: | 5824 | case 0x3f: |
| 5800 | printf ("Unhandled v6 insn: rbit\n"); | 5825 | printf ("Unhandled v6 insn: rbit\n"); |
| 5801 | break; | 5826 | break; |
| 5802 | case 0x61: | 5827 | case 0x61: // SSUB16, SADD16, SSAX, and SASX |
| 5803 | if ((instr & 0xFF0) == 0xf70) { //ssub16 | 5828 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10 || |
| 5804 | u8 tar = BITS(12, 15); | 5829 | (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf30) |
| 5805 | u8 src1 = BITS(16, 19); | 5830 | { |
| 5806 | u8 src2 = BITS(0, 3); | ||
| 5807 | s16 a1 = (state->Reg[src1] & 0xFFFF); | ||
| 5808 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | ||
| 5809 | s16 b1 = (state->Reg[src2] & 0xFFFF); | ||
| 5810 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | ||
| 5811 | state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10); | ||
| 5812 | return 1; | ||
| 5813 | } else if ((instr & 0xFF0) == 0xf10) { //sadd16 | ||
| 5814 | const u8 rd_idx = BITS(12, 15); | 5831 | const u8 rd_idx = BITS(12, 15); |
| 5815 | const u8 rm_idx = BITS(0, 3); | 5832 | const u8 rm_idx = BITS(0, 3); |
| 5816 | const u8 rn_idx = BITS(16, 19); | 5833 | const u8 rn_idx = BITS(16, 19); |
| 5817 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); | ||
| 5818 | const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF); | ||
| 5819 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); | 5834 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5820 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); | 5835 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); |
| 5836 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); | ||
| 5837 | const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF); | ||
| 5821 | 5838 | ||
| 5822 | state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16); | 5839 | s32 lo_result; |
| 5823 | return 1; | 5840 | s32 hi_result; |
| 5824 | } else if ((instr & 0xFF0) == 0xf50) { //ssax | 5841 | |
| 5825 | u8 tar = BITS(12, 15); | 5842 | // SSUB16 |
| 5826 | u8 src1 = BITS(16, 19); | 5843 | if ((instr & 0xFF0) == 0xf70) { |
| 5827 | u8 src2 = BITS(0, 3); | 5844 | lo_result = (rn_lo - rm_lo); |
| 5828 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5845 | hi_result = (rn_hi - rm_hi); |
| 5829 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5846 | } |
| 5830 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5847 | // SADD16 |
| 5831 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5848 | else if ((instr & 0xFF0) == 0xf10) { |
| 5832 | state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10); | 5849 | lo_result = (rn_lo + rm_lo); |
| 5833 | return 1; | 5850 | hi_result = (rn_hi + rm_hi); |
| 5834 | } else if ((instr & 0xFF0) == 0xf30) { //sasx | 5851 | } |
| 5835 | u8 tar = BITS(12, 15); | 5852 | // SSAX |
| 5836 | u8 src1 = BITS(16, 19); | 5853 | else if ((instr & 0xFF0) == 0xf50) { |
| 5837 | u8 src2 = BITS(0, 3); | 5854 | lo_result = (rn_lo + rm_hi); |
| 5838 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5855 | hi_result = (rn_hi - rm_lo); |
| 5839 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5856 | } |
| 5840 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5857 | // SASX |
| 5841 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5858 | else { |
| 5842 | state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10); | 5859 | lo_result = (rn_lo - rm_hi); |
| 5860 | hi_result = (rn_hi + rm_lo); | ||
| 5861 | } | ||
| 5862 | |||
| 5863 | state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); | ||
| 5864 | |||
| 5865 | if (lo_result >= 0) { | ||
| 5866 | state->Cpsr |= (1 << 16); | ||
| 5867 | state->Cpsr |= (1 << 17); | ||
| 5868 | } else { | ||
| 5869 | state->Cpsr &= ~(1 << 16); | ||
| 5870 | state->Cpsr &= ~(1 << 17); | ||
| 5871 | } | ||
| 5872 | |||
| 5873 | if (hi_result >= 0) { | ||
| 5874 | state->Cpsr |= (1 << 18); | ||
| 5875 | state->Cpsr |= (1 << 19); | ||
| 5876 | } else { | ||
| 5877 | state->Cpsr &= ~(1 << 18); | ||
| 5878 | state->Cpsr &= ~(1 << 19); | ||
| 5879 | } | ||
| 5843 | return 1; | 5880 | return 1; |
| 5844 | } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n"); | 5881 | } else { |
| 5882 | printf("Unhandled v6 insn: %08x", BITS(20, 27)); | ||
| 5883 | } | ||
| 5845 | break; | 5884 | break; |
| 5846 | case 0x62: // QSUB16 and QADD16 | 5885 | case 0x62: // QSUB16 and QADD16 |
| 5847 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) { | 5886 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) { |
| @@ -5930,11 +5969,29 @@ L_stm_s_takeabort: | |||
| 5930 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); | 5969 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); |
| 5931 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); | 5970 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); |
| 5932 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); | 5971 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); |
| 5933 | if (b1 & 0xffffff00) state->Cpsr |= (1 << 16); | 5972 | |
| 5934 | if (b2 & 0xffffff00) state->Cpsr |= (1 << 17); | 5973 | if (b1 & 0xffffff00) |
| 5935 | if (b3 & 0xffffff00) state->Cpsr |= (1 << 18); | 5974 | state->Cpsr |= (1 << 16); |
| 5936 | if (b4 & 0xffffff00) state->Cpsr |= (1 << 19); | 5975 | else |
| 5976 | state->Cpsr &= ~(1 << 16); | ||
| 5977 | |||
| 5978 | if (b2 & 0xffffff00) | ||
| 5979 | state->Cpsr |= (1 << 17); | ||
| 5980 | else | ||
| 5981 | state->Cpsr &= ~(1 << 17); | ||
| 5982 | |||
| 5983 | if (b3 & 0xffffff00) | ||
| 5984 | state->Cpsr |= (1 << 18); | ||
| 5985 | else | ||
| 5986 | state->Cpsr &= ~(1 << 18); | ||
| 5987 | |||
| 5988 | |||
| 5989 | if (b4 & 0xffffff00) | ||
| 5990 | state->Cpsr |= (1 << 19); | ||
| 5991 | else | ||
| 5992 | state->Cpsr &= ~(1 << 19); | ||
| 5937 | } | 5993 | } |
| 5994 | |||
| 5938 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); | 5995 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); |
| 5939 | return 1; | 5996 | return 1; |
| 5940 | } | 5997 | } |
| @@ -6049,7 +6106,7 @@ L_stm_s_takeabort: | |||
| 6049 | break; | 6106 | break; |
| 6050 | } | 6107 | } |
| 6051 | 6108 | ||
| 6052 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6109 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF); |
| 6053 | if (Rm & 0x80) | 6110 | if (Rm & 0x80) |
| 6054 | Rm |= 0xffffff00; | 6111 | Rm |= 0xffffff00; |
| 6055 | 6112 | ||
| @@ -6058,11 +6115,12 @@ L_stm_s_takeabort: | |||
| 6058 | state->Reg[BITS(12, 15)] = Rm; | 6115 | state->Reg[BITS(12, 15)] = Rm; |
| 6059 | else | 6116 | else |
| 6060 | /* SXTAB */ | 6117 | /* SXTAB */ |
| 6061 | state->Reg[BITS(12, 15)] += Rm; | 6118 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6062 | 6119 | ||
| 6063 | return 1; | 6120 | return 1; |
| 6064 | } | 6121 | } |
| 6065 | case 0x6b: { | 6122 | case 0x6b: |
| 6123 | { | ||
| 6066 | ARMword Rm; | 6124 | ARMword Rm; |
| 6067 | int ror = -1; | 6125 | int ror = -1; |
| 6068 | 6126 | ||
| @@ -6080,10 +6138,10 @@ L_stm_s_takeabort: | |||
| 6080 | ror = 24; | 6138 | ror = 24; |
| 6081 | break; | 6139 | break; |
| 6082 | 6140 | ||
| 6083 | case 0xf3: | 6141 | case 0xf3: // REV |
| 6084 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); | 6142 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); |
| 6085 | return 1; | 6143 | return 1; |
| 6086 | case 0xfb: | 6144 | case 0xfb: // REV16 |
| 6087 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); | 6145 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); |
| 6088 | return 1; | 6146 | return 1; |
| 6089 | default: | 6147 | default: |
| @@ -6093,7 +6151,7 @@ L_stm_s_takeabort: | |||
| 6093 | if (ror == -1) | 6151 | if (ror == -1) |
| 6094 | break; | 6152 | break; |
| 6095 | 6153 | ||
| 6096 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6154 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF); |
| 6097 | if (Rm & 0x8000) | 6155 | if (Rm & 0x8000) |
| 6098 | Rm |= 0xffff0000; | 6156 | Rm |= 0xffff0000; |
| 6099 | 6157 | ||
| @@ -6156,18 +6214,27 @@ L_stm_s_takeabort: | |||
| 6156 | //ichfly | 6214 | //ichfly |
| 6157 | //USAT16 | 6215 | //USAT16 |
| 6158 | { | 6216 | { |
| 6159 | u8 tar = BITS(12, 15); | 6217 | const u8 rd_idx = BITS(12, 15); |
| 6160 | u8 src = BITS(0, 3); | 6218 | const u8 rn_idx = BITS(0, 3); |
| 6161 | u8 val = BITS(16, 19); | 6219 | const u8 num_bits = BITS(16, 19); |
| 6162 | s16 a1 = (state->Reg[src]); | 6220 | const s16 max = 0xFFFF >> (16 - num_bits); |
| 6163 | s16 a2 = (state->Reg[src] >> 0x10); | 6221 | s16 rn_lo = (state->Reg[rn_idx]); |
| 6164 | s16 max = 0xFFFF >> (16 - val); | 6222 | s16 rn_hi = (state->Reg[rn_idx] >> 16); |
| 6165 | if (max < a1) a1 = max; | 6223 | |
| 6166 | if (max < a2) a2 = max; | 6224 | if (max < rn_lo) |
| 6167 | u32 temp2 = ((u32)(a2)) << 0x10; | 6225 | rn_lo = max; |
| 6168 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | 6226 | else if (rn_lo < 0) |
| 6227 | rn_lo = 0; | ||
| 6228 | |||
| 6229 | if (max < rn_hi) | ||
| 6230 | rn_hi = max; | ||
| 6231 | else if (rn_hi < 0) | ||
| 6232 | rn_hi = 0; | ||
| 6233 | |||
| 6234 | state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi << 16) & 0xFFFF); | ||
| 6235 | return 1; | ||
| 6169 | } | 6236 | } |
| 6170 | return 1; | 6237 | |
| 6171 | default: | 6238 | default: |
| 6172 | break; | 6239 | break; |
| 6173 | } | 6240 | } |
| @@ -6180,7 +6247,7 @@ L_stm_s_takeabort: | |||
| 6180 | break; | 6247 | break; |
| 6181 | } | 6248 | } |
| 6182 | 6249 | ||
| 6183 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6250 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF); |
| 6184 | 6251 | ||
| 6185 | if (BITS(16, 19) == 0xf) | 6252 | if (BITS(16, 19) == 0xf) |
| 6186 | /* UXTB */ | 6253 | /* UXTB */ |
| @@ -6210,9 +6277,13 @@ L_stm_s_takeabort: | |||
| 6210 | ror = 24; | 6277 | ror = 24; |
| 6211 | break; | 6278 | break; |
| 6212 | 6279 | ||
| 6213 | case 0xfb: | 6280 | case 0xfb: // REVSH |
| 6214 | printf("Unhandled v6 insn: revsh\n"); | 6281 | { |
| 6215 | return 0; | 6282 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00) >> 8); |
| 6283 | if (DEST & 0x8000) | ||
| 6284 | DEST |= 0xffff0000; | ||
| 6285 | return 1; | ||
| 6286 | } | ||
| 6216 | default: | 6287 | default: |
| 6217 | break; | 6288 | break; |
| 6218 | } | 6289 | } |
| @@ -6220,13 +6291,13 @@ L_stm_s_takeabort: | |||
| 6220 | if (ror == -1) | 6291 | if (ror == -1) |
| 6221 | break; | 6292 | break; |
| 6222 | 6293 | ||
| 6223 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6294 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF); |
| 6224 | 6295 | ||
| 6225 | /* UXT */ | 6296 | /* UXT */ |
| 6226 | /* state->Reg[BITS (12, 15)] = Rm; */ | 6297 | /* state->Reg[BITS (12, 15)] = Rm; */ |
| 6227 | /* dyf add */ | 6298 | /* dyf add */ |
| 6228 | if (BITS(16, 19) == 0xf) { | 6299 | if (BITS(16, 19) == 0xf) { |
| 6229 | state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF; | 6300 | state->Reg[BITS(12, 15)] = Rm; |
| 6230 | } | 6301 | } |
| 6231 | else { | 6302 | else { |
| 6232 | /* UXTAH */ | 6303 | /* UXTAH */ |
| @@ -6234,7 +6305,7 @@ L_stm_s_takeabort: | |||
| 6234 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] | 6305 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] |
| 6235 | // , Rm, BITS(10, 11)); | 6306 | // , Rm, BITS(10, 11)); |
| 6236 | // printf("icounter is %lld\n", state->NumInstrs); | 6307 | // printf("icounter is %lld\n", state->NumInstrs); |
| 6237 | state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm; | 6308 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6238 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); | 6309 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); |
| 6239 | // exit(-1); | 6310 | // exit(-1); |
| 6240 | } | 6311 | } |
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h index b3e561130..d394ad37e 100644 --- a/src/core/file_sys/archive_savedata.h +++ b/src/core/file_sys/archive_savedata.h | |||
| @@ -21,8 +21,7 @@ public: | |||
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * Initialize the archive. | 23 | * Initialize the archive. |
| 24 | * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists, | 24 | * @return true if it initialized successfully |
| 25 | * Success if it was created properly and Failure if there was any error | ||
| 26 | */ | 25 | */ |
| 27 | bool Initialize(); | 26 | bool Initialize(); |
| 28 | 27 | ||
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp new file mode 100644 index 000000000..dc2c23b41 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/archive_systemsavedata.h" | ||
| 11 | #include "core/file_sys/disk_archive.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point) | ||
| 20 | : DiskArchive(mount_point) { | ||
| 21 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); | ||
| 22 | } | ||
| 23 | |||
| 24 | bool Archive_SystemSaveData::Initialize() { | ||
| 25 | if (!FileUtil::CreateFullPath(mount_point)) { | ||
| 26 | LOG_ERROR(Service_FS, "Unable to create SystemSaveData path."); | ||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h new file mode 100644 index 000000000..360ed1e13 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/disk_archive.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the SystemSaveData archive | ||
| 18 | /// TODO(Subv): This archive should point to a location in the NAND, | ||
| 19 | /// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High> | ||
| 20 | class Archive_SystemSaveData final : public DiskArchive { | ||
| 21 | public: | ||
| 22 | Archive_SystemSaveData(const std::string& mount_point); | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Initialize the archive. | ||
| 26 | * @return true if it initialized successfully | ||
| 27 | */ | ||
| 28 | bool Initialize(); | ||
| 29 | |||
| 30 | std::string GetName() const override { return "SystemSaveData"; } | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 304cf5b67..bb778ec26 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -12,11 +12,15 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | /// Permissions for mapped shared memory blocks | 13 | /// Permissions for mapped shared memory blocks |
| 14 | enum class MemoryPermission : u32 { | 14 | enum class MemoryPermission : u32 { |
| 15 | None = 0, | 15 | None = 0, |
| 16 | Read = (1u << 0), | 16 | Read = (1u << 0), |
| 17 | Write = (1u << 1), | 17 | Write = (1u << 1), |
| 18 | ReadWrite = (Read | Write), | 18 | ReadWrite = (Read | Write), |
| 19 | DontCare = (1u << 28) | 19 | Execute = (1u << 2), |
| 20 | ReadExecute = (Read | Execute), | ||
| 21 | WriteExecute = (Write | Execute), | ||
| 22 | ReadWriteExecute = (Read | Write | Execute), | ||
| 23 | DontCare = (1u << 28) | ||
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | /** | 26 | /** |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 9c3834733..5ab82729c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -419,6 +419,15 @@ void ArchiveInit() { | |||
| 419 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | 419 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); |
| 420 | else | 420 | else |
| 421 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | 421 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); |
| 422 | |||
| 423 | std::string systemsavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX); | ||
| 424 | auto systemsavedata_archive = std::make_unique<FileSys::Archive_SDMC>(systemsavedata_directory); | ||
| 425 | if (systemsavedata_archive->Initialize()) { | ||
| 426 | CreateArchive(std::move(systemsavedata_archive), ArchiveIdCode::SystemSaveData); | ||
| 427 | } else { | ||
| 428 | LOG_ERROR(Service_FS, "Can't instantiate SystemSaveData archive with path %s", | ||
| 429 | systemsavedata_directory.c_str()); | ||
| 430 | } | ||
| 422 | } | 431 | } |
| 423 | 432 | ||
| 424 | /// Shutdown archives | 433 | /// Shutdown archives |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index db8027142..8c9ad2712 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -145,6 +145,30 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | /** | 147 | /** |
| 148 | * GSP_GPU::FlushDataCache service function | ||
| 149 | * | ||
| 150 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 151 | * | ||
| 152 | * Inputs: | ||
| 153 | * 1 : Address | ||
| 154 | * 2 : Size | ||
| 155 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 156 | * 4 : KProcess handle | ||
| 157 | * Outputs: | ||
| 158 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 159 | */ | ||
| 160 | static void FlushDataCache(Service::Interface* self) { | ||
| 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 162 | u32 address = cmd_buff[1]; | ||
| 163 | u32 size = cmd_buff[2]; | ||
| 164 | u32 process = cmd_buff[4]; | ||
| 165 | |||
| 166 | // TODO(purpasmart96): Verify return header on HW | ||
| 167 | |||
| 168 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 148 | * GSP_GPU::RegisterInterruptRelayQueue service function | 172 | * GSP_GPU::RegisterInterruptRelayQueue service function |
| 149 | * Inputs: | 173 | * Inputs: |
| 150 | * 1 : "Flags" field, purpose is unknown | 174 | * 1 : "Flags" field, purpose is unknown |
| @@ -335,7 +359,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 335 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, | 359 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, |
| 336 | {0x00060082, nullptr, "SetCommandList"}, | 360 | {0x00060082, nullptr, "SetCommandList"}, |
| 337 | {0x000700C2, nullptr, "RequestDma"}, | 361 | {0x000700C2, nullptr, "RequestDma"}, |
| 338 | {0x00080082, nullptr, "FlushDataCache"}, | 362 | {0x00080082, FlushDataCache, "FlushDataCache"}, |
| 339 | {0x00090082, nullptr, "InvalidateDataCache"}, | 363 | {0x00090082, nullptr, "InvalidateDataCache"}, |
| 340 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, | 364 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, |
| 341 | {0x000B0040, nullptr, "SetLcdForceBlack"}, | 365 | {0x000B0040, nullptr, "SetLcdForceBlack"}, |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 15cc240f4..47e9bf77e 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -64,6 +64,10 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other | |||
| 64 | case Kernel::MemoryPermission::Read: | 64 | case Kernel::MemoryPermission::Read: |
| 65 | case Kernel::MemoryPermission::Write: | 65 | case Kernel::MemoryPermission::Write: |
| 66 | case Kernel::MemoryPermission::ReadWrite: | 66 | case Kernel::MemoryPermission::ReadWrite: |
| 67 | case Kernel::MemoryPermission::Execute: | ||
| 68 | case Kernel::MemoryPermission::ReadExecute: | ||
| 69 | case Kernel::MemoryPermission::WriteExecute: | ||
| 70 | case Kernel::MemoryPermission::ReadWriteExecute: | ||
| 67 | case Kernel::MemoryPermission::DontCare: | 71 | case Kernel::MemoryPermission::DontCare: |
| 68 | Kernel::MapSharedMemory(handle, addr, permissions_type, | 72 | Kernel::MapSharedMemory(handle, addr, permissions_type, |
| 69 | static_cast<Kernel::MemoryPermission>(other_permissions)); | 73 | static_cast<Kernel::MemoryPermission>(other_permissions)); |
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 16b1656bb..4eb8b3d4d 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h | |||
| @@ -85,7 +85,7 @@ public: | |||
| 85 | 85 | ||
| 86 | void UnregisterObserver(DebuggerObserver* observer) | 86 | void UnregisterObserver(DebuggerObserver* observer) |
| 87 | { | 87 | { |
| 88 | std::remove(observers.begin(), observers.end(), observer); | 88 | observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end()); |
| 89 | observer->observed = nullptr; | 89 | observer->observed = nullptr; |
| 90 | } | 90 | } |
| 91 | 91 | ||