diff options
| -rw-r--r-- | src/core/arm/interpreter/armemu.cpp | 164 | ||||
| -rw-r--r-- | src/core/hle/kernel/shared_memory.h | 14 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 4 |
3 files changed, 115 insertions, 67 deletions
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 1a589e39c..b9ac8b9ad 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 |
| @@ -5677,8 +5689,24 @@ L_stm_s_takeabort: | |||
| 5677 | case 0x03: | 5689 | case 0x03: |
| 5678 | printf ("Unhandled v6 insn: ldr\n"); | 5690 | printf ("Unhandled v6 insn: ldr\n"); |
| 5679 | break; | 5691 | break; |
| 5680 | case 0x04: | 5692 | case 0x04: // UMAAL |
| 5681 | printf ("Unhandled v6 insn: umaal\n"); | 5693 | { |
| 5694 | const u8 rm_idx = BITS(8, 11); | ||
| 5695 | const u8 rn_idx = BITS(0, 3); | ||
| 5696 | const u8 rd_lo_idx = BITS(12, 15); | ||
| 5697 | const u8 rd_hi_idx = BITS(16, 19); | ||
| 5698 | |||
| 5699 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 5700 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 5701 | const u32 rd_lo_val = state->Reg[rd_lo_idx]; | ||
| 5702 | const u32 rd_hi_val = state->Reg[rd_hi_idx]; | ||
| 5703 | |||
| 5704 | const u64 result = (rn_val * rm_val) + rd_lo_val + rd_hi_val; | ||
| 5705 | |||
| 5706 | state->Reg[rd_lo_idx] = (result & 0xFFFFFFFF); | ||
| 5707 | state->Reg[rd_hi_idx] = ((result >> 32) & 0xFFFFFFFF); | ||
| 5708 | return 1; | ||
| 5709 | } | ||
| 5682 | break; | 5710 | break; |
| 5683 | case 0x06: | 5711 | case 0x06: |
| 5684 | printf ("Unhandled v6 insn: mls/str\n"); | 5712 | printf ("Unhandled v6 insn: mls/str\n"); |
| @@ -5799,49 +5827,38 @@ L_stm_s_takeabort: | |||
| 5799 | case 0x3f: | 5827 | case 0x3f: |
| 5800 | printf ("Unhandled v6 insn: rbit\n"); | 5828 | printf ("Unhandled v6 insn: rbit\n"); |
| 5801 | break; | 5829 | break; |
| 5802 | case 0x61: | 5830 | case 0x61: // SSUB16, SADD16, SSAX, and SASX |
| 5803 | if ((instr & 0xFF0) == 0xf70) { //ssub16 | 5831 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10 || |
| 5804 | u8 tar = BITS(12, 15); | 5832 | (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf30) |
| 5805 | u8 src1 = BITS(16, 19); | 5833 | { |
| 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); | 5834 | const u8 rd_idx = BITS(12, 15); |
| 5815 | const u8 rm_idx = BITS(0, 3); | 5835 | const u8 rm_idx = BITS(0, 3); |
| 5816 | const u8 rn_idx = BITS(16, 19); | 5836 | 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); | 5837 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5820 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); | 5838 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); |
| 5839 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); | ||
| 5840 | const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF); | ||
| 5821 | 5841 | ||
| 5822 | state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16); | 5842 | // SSUB16 |
| 5823 | return 1; | 5843 | if ((instr & 0xFF0) == 0xf70) { |
| 5824 | } else if ((instr & 0xFF0) == 0xf50) { //ssax | 5844 | state->Reg[rd_idx] = ((rn_lo - rm_lo) & 0xFFFF) | (((rn_hi - rm_hi) & 0xFFFF) << 16); |
| 5825 | u8 tar = BITS(12, 15); | 5845 | } |
| 5826 | u8 src1 = BITS(16, 19); | 5846 | // SADD16 |
| 5827 | u8 src2 = BITS(0, 3); | 5847 | else if ((instr & 0xFF0) == 0xf10) { |
| 5828 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5848 | state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16); |
| 5829 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5849 | } |
| 5830 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5850 | // SSAX |
| 5831 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5851 | else if ((instr & 0xFF0) == 0xf50) { |
| 5832 | state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10); | 5852 | state->Reg[rd_idx] = ((rn_lo + rm_hi) & 0xFFFF) | (((rn_hi - rm_lo) & 0xFFFF) << 16); |
| 5833 | return 1; | 5853 | } |
| 5834 | } else if ((instr & 0xFF0) == 0xf30) { //sasx | 5854 | // SASX |
| 5835 | u8 tar = BITS(12, 15); | 5855 | else { |
| 5836 | u8 src1 = BITS(16, 19); | 5856 | state->Reg[rd_idx] = ((rn_lo - rm_hi) & 0xFFFF) | (((rn_hi + rm_lo) & 0xFFFF) << 16); |
| 5837 | u8 src2 = BITS(0, 3); | 5857 | } |
| 5838 | s16 a1 = (state->Reg[src1] & 0xFFFF); | ||
| 5839 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | ||
| 5840 | s16 b1 = (state->Reg[src2] & 0xFFFF); | ||
| 5841 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | ||
| 5842 | state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10); | ||
| 5843 | return 1; | 5858 | return 1; |
| 5844 | } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n"); | 5859 | } else { |
| 5860 | printf("Unhandled v6 insn: %08x", BITS(20, 27)); | ||
| 5861 | } | ||
| 5845 | break; | 5862 | break; |
| 5846 | case 0x62: // QSUB16 and QADD16 | 5863 | case 0x62: // QSUB16 and QADD16 |
| 5847 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) { | 5864 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) { |
| @@ -5930,11 +5947,29 @@ L_stm_s_takeabort: | |||
| 5930 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); | 5947 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); |
| 5931 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); | 5948 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); |
| 5932 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); | 5949 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); |
| 5933 | if (b1 & 0xffffff00) state->Cpsr |= (1 << 16); | 5950 | |
| 5934 | if (b2 & 0xffffff00) state->Cpsr |= (1 << 17); | 5951 | if (b1 & 0xffffff00) |
| 5935 | if (b3 & 0xffffff00) state->Cpsr |= (1 << 18); | 5952 | state->Cpsr |= (1 << 16); |
| 5936 | if (b4 & 0xffffff00) state->Cpsr |= (1 << 19); | 5953 | else |
| 5954 | state->Cpsr &= ~(1 << 16); | ||
| 5955 | |||
| 5956 | if (b2 & 0xffffff00) | ||
| 5957 | state->Cpsr |= (1 << 17); | ||
| 5958 | else | ||
| 5959 | state->Cpsr &= ~(1 << 17); | ||
| 5960 | |||
| 5961 | if (b3 & 0xffffff00) | ||
| 5962 | state->Cpsr |= (1 << 18); | ||
| 5963 | else | ||
| 5964 | state->Cpsr &= ~(1 << 18); | ||
| 5965 | |||
| 5966 | |||
| 5967 | if (b4 & 0xffffff00) | ||
| 5968 | state->Cpsr |= (1 << 19); | ||
| 5969 | else | ||
| 5970 | state->Cpsr &= ~(1 << 19); | ||
| 5937 | } | 5971 | } |
| 5972 | |||
| 5938 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); | 5973 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); |
| 5939 | return 1; | 5974 | return 1; |
| 5940 | } | 5975 | } |
| @@ -6049,7 +6084,7 @@ L_stm_s_takeabort: | |||
| 6049 | break; | 6084 | break; |
| 6050 | } | 6085 | } |
| 6051 | 6086 | ||
| 6052 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6087 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | ((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF; |
| 6053 | if (Rm & 0x80) | 6088 | if (Rm & 0x80) |
| 6054 | Rm |= 0xffffff00; | 6089 | Rm |= 0xffffff00; |
| 6055 | 6090 | ||
| @@ -6058,11 +6093,12 @@ L_stm_s_takeabort: | |||
| 6058 | state->Reg[BITS(12, 15)] = Rm; | 6093 | state->Reg[BITS(12, 15)] = Rm; |
| 6059 | else | 6094 | else |
| 6060 | /* SXTAB */ | 6095 | /* SXTAB */ |
| 6061 | state->Reg[BITS(12, 15)] += Rm; | 6096 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6062 | 6097 | ||
| 6063 | return 1; | 6098 | return 1; |
| 6064 | } | 6099 | } |
| 6065 | case 0x6b: { | 6100 | case 0x6b: |
| 6101 | { | ||
| 6066 | ARMword Rm; | 6102 | ARMword Rm; |
| 6067 | int ror = -1; | 6103 | int ror = -1; |
| 6068 | 6104 | ||
| @@ -6080,10 +6116,10 @@ L_stm_s_takeabort: | |||
| 6080 | ror = 24; | 6116 | ror = 24; |
| 6081 | break; | 6117 | break; |
| 6082 | 6118 | ||
| 6083 | case 0xf3: | 6119 | case 0xf3: // REV |
| 6084 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); | 6120 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); |
| 6085 | return 1; | 6121 | return 1; |
| 6086 | case 0xfb: | 6122 | case 0xfb: // REV16 |
| 6087 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); | 6123 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); |
| 6088 | return 1; | 6124 | return 1; |
| 6089 | default: | 6125 | default: |
| @@ -6093,7 +6129,7 @@ L_stm_s_takeabort: | |||
| 6093 | if (ror == -1) | 6129 | if (ror == -1) |
| 6094 | break; | 6130 | break; |
| 6095 | 6131 | ||
| 6096 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6132 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | ((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF; |
| 6097 | if (Rm & 0x8000) | 6133 | if (Rm & 0x8000) |
| 6098 | Rm |= 0xffff0000; | 6134 | Rm |= 0xffff0000; |
| 6099 | 6135 | ||
| @@ -6180,7 +6216,7 @@ L_stm_s_takeabort: | |||
| 6180 | break; | 6216 | break; |
| 6181 | } | 6217 | } |
| 6182 | 6218 | ||
| 6183 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6219 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | ((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF; |
| 6184 | 6220 | ||
| 6185 | if (BITS(16, 19) == 0xf) | 6221 | if (BITS(16, 19) == 0xf) |
| 6186 | /* UXTB */ | 6222 | /* UXTB */ |
| @@ -6210,9 +6246,13 @@ L_stm_s_takeabort: | |||
| 6210 | ror = 24; | 6246 | ror = 24; |
| 6211 | break; | 6247 | break; |
| 6212 | 6248 | ||
| 6213 | case 0xfb: | 6249 | case 0xfb: // REVSH |
| 6214 | printf("Unhandled v6 insn: revsh\n"); | 6250 | { |
| 6215 | return 0; | 6251 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00) >> 8); |
| 6252 | if (DEST & 0x8000) | ||
| 6253 | DEST |= 0xffff0000; | ||
| 6254 | return 1; | ||
| 6255 | } | ||
| 6216 | default: | 6256 | default: |
| 6217 | break; | 6257 | break; |
| 6218 | } | 6258 | } |
| @@ -6220,13 +6260,13 @@ L_stm_s_takeabort: | |||
| 6220 | if (ror == -1) | 6260 | if (ror == -1) |
| 6221 | break; | 6261 | break; |
| 6222 | 6262 | ||
| 6223 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6263 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | ((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF; |
| 6224 | 6264 | ||
| 6225 | /* UXT */ | 6265 | /* UXT */ |
| 6226 | /* state->Reg[BITS (12, 15)] = Rm; */ | 6266 | /* state->Reg[BITS (12, 15)] = Rm; */ |
| 6227 | /* dyf add */ | 6267 | /* dyf add */ |
| 6228 | if (BITS(16, 19) == 0xf) { | 6268 | if (BITS(16, 19) == 0xf) { |
| 6229 | state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF; | 6269 | state->Reg[BITS(12, 15)] = Rm; |
| 6230 | } | 6270 | } |
| 6231 | else { | 6271 | else { |
| 6232 | /* UXTAH */ | 6272 | /* UXTAH */ |
| @@ -6234,7 +6274,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)] | 6274 | // 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)); | 6275 | // , Rm, BITS(10, 11)); |
| 6236 | // printf("icounter is %lld\n", state->NumInstrs); | 6276 | // printf("icounter is %lld\n", state->NumInstrs); |
| 6237 | state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm; | 6277 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6238 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); | 6278 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); |
| 6239 | // exit(-1); | 6279 | // exit(-1); |
| 6240 | } | 6280 | } |
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/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)); |