• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Disarm / 14178991249

31 Mar 2025 06:44PM UTC coverage: 58.085% (+0.3%) from 57.754%
14178991249

push

github

SamboyCoding
fix: Cleanup messy code in  LoadStoreRegFromImmUnsigned.

Fixes #42

1474 of 3070 branches covered (48.01%)

Branch coverage included in aggregate %.

28 of 45 new or added lines in 1 file covered. (62.22%)

2 existing lines in 1 file now uncovered.

2833 of 4345 relevant lines covered (65.2%)

91.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.22
/Disarm/InternalDisassembly/Arm64LoadsStores.cs
1
namespace Disarm.InternalDisassembly;
2

3
internal static class Arm64LoadsStores
4
{
5
    public static Arm64Instruction Disassemble(uint instruction)
6
    {
7
        var op0 = instruction >> 28; //Bits 28-31
223✔
8
        var op1 = (instruction >> 26) & 1; //Bit 26
223✔
9
        var op2 = (instruction >> 23) & 0b11; //Bits 23-24
223✔
10
        var op3 = (instruction >> 16) & 0b11_1111; //Bits 16-21
223✔
11
        var op4 = (instruction >> 10) & 0b11; //Bits 10-11
12

13
        //As can perhaps be imagined, this is by far the most deeply-nested tree of instructions
14
        //At this level, despite having 5 separate operands to differentiate which path we take, most of these paths are defined by masks, not by values.
15
        //Unfortunately, this makes the code a bit ugly.
16

17
        //They are, at least, *somewhat* grouped by category using op0
18
        if ((op0 & 0b1011) == 0)
223!
19
            //Mostly undefined instructions, but a couple of them are defined
20
            return DisassembleAdvancedLoadStore(instruction);
×
21

22
        if (op0 == 0b1101 && op1 == 0 && op2 >> 1 == 1 && op3 >> 5 == 1)
223✔
23
            //Literally the only concretely defined value for op0, but it still needs others to match conditions - load/store memory tags
24
            return DisassembleLoadStoreMemoryTags(instruction);
8✔
25

26
        //Five more categories for op0
27

28
        if ((op0 & 0b1011) == 0b1000)
215!
29
        {
30
            //Load/store exclusive pair, or undefined
31
            if (op1 == 0 && op2 == 0 && op3.TestBit(5))
×
32
                return LoadStoreExclusivePair(instruction);
×
33
            
34
            throw new Arm64UndefinedInstructionException($"Load/store: Undefined instruction - op0={op0}, op1={op1}, op2={op2}, op3={op3}");
×
35
        }
36

37
        //The last 4 categories look only at the last 2 bits of op0, so we can switch now
38
        op0 &= 0b11;
215✔
39

40
        //Ok i lied half of these are barely grouped at all in any way that makes sense to me 
41
        return op0 switch
215!
42
        {
215✔
43
            0b00 => DisassembleLoadStoreExclusiveRegOrderedOrCompareSwap(instruction), //load/store exclusive reg, load/store ordered, or compare + swap 
×
44
            0b01 => DisassembleLdAprRegisterLiteralOrMemoryCopySet(instruction), //ldapr/stlr unscaled immediate, load register literal, or memory copy/set
7✔
45
            0b10 => DisassembleLoadStorePairs(instruction), //actual group! load/store pairs
87✔
46
            0b11 => DisassembleLoadStoreRegisterOrAtomic(instruction), //various kinds of load/store register, or atomic memory operations
121✔
47
            _ => throw new("Loads/stores: Impossible op0 value")
×
48
        };
215✔
49
    }
50

51
    private static Arm64Instruction DisassembleAdvancedLoadStore(uint instruction)
52
    {
53
        //Most of these are actually unimplemented. Only two categories are defined, and they are both SIMD, so we can shunt over to that class.
54

55
        var op2 = (instruction >> 23) & 0b11; //Bits 23-24
×
56
        var op3 = (instruction >> 16) & 0b11_1111; //Bits 16-21
×
57

58
        if (op2 == 0b11)
×
59
            //Post-indexed simd load/store structure
60
            return Arm64Simd.LoadStoreSingleStructurePostIndexed(instruction);
×
61

62
        //Doesn't matter what op2 is at this point, unless the bottom 5 bits of op3 are zeroed, this is unimplemented.
63
        if ((op3 & 0b1_1111) == 0)
×
64
            return Arm64Simd.LoadStoreSingleStructure(instruction);
×
65

66
        throw new Arm64UndefinedInstructionException($"Advanced load/store: Congrats, you hit the minefield of undefined instructions. op2: {op2}, op3: {op3}");
×
67
    }
68

69
    private static Arm64Instruction DisassembleLoadStoreMemoryTags(uint instruction)
70
    {
71
        var opc = (instruction >> 22) & 0b11; // Bits 22-23
8✔
72
        var imm9 = (long) (instruction >> 12) & 0b1_1111_1111; // Bits 12-20
8✔
73
        var op2 = (instruction >> 10) & 0b11; // Bits 10-11
8✔
74
        var rn = (int)(instruction >> 5) & 0b1_1111; // Bits 5-9
8✔
75
        var rt = (int)instruction & 0b1_1111; // Bits 0-5
8✔
76

77
        imm9 = Arm64CommonUtils.SignExtend(imm9, 9, 64);
8✔
78
        var offset = imm9 << Arm64CommonUtils.LOG2_TAG_GRANULE;
8✔
79
        
80
        return opc switch
8!
81
        {
8✔
82
            0b00 when offset != 0 => new()
3!
83
            {
1✔
84
                Mnemonic = Arm64Mnemonic.STG,
1✔
85
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
86
                MemIndexMode = op2 switch
1✔
87
                {
1✔
88
                    0b01 => Arm64MemoryIndexMode.PostIndex,
1✔
89
                    0b10 => Arm64MemoryIndexMode.Offset,
×
90
                    0b11 => Arm64MemoryIndexMode.PreIndex,
×
91
                    _ => throw new Arm64UndefinedInstructionException("Bad memory index mode")
×
92
                },
1✔
93
                MemOffset = offset,
1✔
94
                Op0Kind = Arm64OperandKind.Register,
1✔
95
                Op1Kind = Arm64OperandKind.Memory,
1✔
96
                Op0Reg = Arm64Register.X0 + rn,
1✔
97
                MemBase = Arm64Register.X0 + rt
1✔
98
            },
1✔
99
            0b00 when offset == 0 => new()
2!
100
            {
1✔
101
                Mnemonic = Arm64Mnemonic.STZGM,
1✔
102
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
103
                Op0Kind = Arm64OperandKind.Register,
1✔
104
                Op1Kind = Arm64OperandKind.Register,
1✔
105
                Op0Reg = Arm64Register.X0 + rn,
1✔
106
                Op1Reg = Arm64Register.X0 + rt
1✔
107
            },
1✔
108
            0b01 when op2 == 0 =>  new()
3✔
109
            {
1✔
110
                Mnemonic = Arm64Mnemonic.LDG,
1✔
111
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
112
                MemIndexMode = Arm64MemoryIndexMode.Offset,
1✔
113
                MemOffset = offset,
1✔
114
                Op0Kind = Arm64OperandKind.Register,
1✔
115
                Op1Kind = Arm64OperandKind.Register,
1✔
116
                Op0Reg = Arm64Register.X0 + rn,
1✔
117
                Op1Reg = Arm64Register.X0 + rt
1✔
118
            },
1✔
119
            0b01 when op2 != 0 =>  new()
2!
120
            {
1✔
121
                Mnemonic = Arm64Mnemonic.STZG,
1✔
122
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
123
                MemIndexMode = op2 switch
1✔
124
                {
1✔
125
                    0b01 => Arm64MemoryIndexMode.PostIndex,
1✔
126
                    0b10 => Arm64MemoryIndexMode.Offset,
×
127
                    0b11 => Arm64MemoryIndexMode.PreIndex,
×
128
                    _ => throw new Arm64UndefinedInstructionException("Bad memory index mode")
×
129
                },
1✔
130
                MemOffset = offset,
1✔
131
                Op0Kind = Arm64OperandKind.Register,
1✔
132
                Op1Kind = Arm64OperandKind.Memory,
1✔
133
                Op0Reg = Arm64Register.X0 + rn,
1✔
134
                MemBase = Arm64Register.X0 + rt
1✔
135
            },
1✔
136
            0b10 when offset != 0 =>  new()
3!
137
            {
1✔
138
                Mnemonic = Arm64Mnemonic.ST2G,
1✔
139
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
140
                MemIndexMode = op2 switch
1✔
141
                {
1✔
142
                    0b01 => Arm64MemoryIndexMode.PostIndex,
1✔
143
                    0b10 => Arm64MemoryIndexMode.Offset,
×
144
                    0b11 => Arm64MemoryIndexMode.PreIndex,
×
145
                    _ => throw new Arm64UndefinedInstructionException("Bad memory index mode")
×
146
                },
1✔
147
                MemOffset = offset,
1✔
148
                Op0Kind = Arm64OperandKind.Register,
1✔
149
                Op1Kind = Arm64OperandKind.Memory,
1✔
150
                Op0Reg = Arm64Register.X0 + rn,
1✔
151
                MemBase = Arm64Register.X0 + rt
1✔
152
            },
1✔
153
            0b10 when offset == 0 && op2 == 0 =>  new()
2!
154
            {
1✔
155
                Mnemonic = Arm64Mnemonic.STGM,
1✔
156
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
157
                Op0Kind = Arm64OperandKind.Register,
1✔
158
                Op1Kind = Arm64OperandKind.Register,
1✔
159
                Op0Reg = Arm64Register.X0 + rn,
1✔
160
                Op1Reg = Arm64Register.X0 + rt
1✔
161
            },
1✔
162
            0b11 when offset != 0 =>  new()
3!
163
            {
1✔
164
                Mnemonic = Arm64Mnemonic.STZ2G,
1✔
165
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
166
                MemIndexMode = op2 switch
1✔
167
                {
1✔
168
                    0b01 => Arm64MemoryIndexMode.PostIndex,
1✔
169
                    0b10 => Arm64MemoryIndexMode.Offset,
×
170
                    0b11 => Arm64MemoryIndexMode.PreIndex,
×
171
                    _ => throw new Arm64UndefinedInstructionException("Bad memory index mode")
×
172
                },
1✔
173
                MemOffset = offset,
1✔
174
                Op0Kind = Arm64OperandKind.Register,
1✔
175
                Op1Kind = Arm64OperandKind.Memory,
1✔
176
                Op0Reg = Arm64Register.X0 + rn,
1✔
177
                MemBase = Arm64Register.X0 + rt
1✔
178
            },
1✔
179
            0b11 when offset == 0 && op2 == 0 =>  new()
2!
180
            {
1✔
181
                Mnemonic = Arm64Mnemonic.LDGM,
1✔
182
                MnemonicCategory = Arm64MnemonicCategory.MemoryTagging,
1✔
183
                Op0Kind = Arm64OperandKind.Register,
1✔
184
                Op1Kind = Arm64OperandKind.Register,
1✔
185
                Op0Reg = Arm64Register.X0 + rn,
1✔
186
                Op1Reg = Arm64Register.X0 + rt
1✔
187
            },
1✔
188
            _ => throw new Arm64UndefinedInstructionException("Unallocated")
×
189
        };
8✔
190
    }
191

192
    private static Arm64Instruction DisassembleLoadStoreExclusiveRegOrderedOrCompareSwap(uint instruction)
193
    {
194
        //Load/store exclusive register, load/store ordered, or compare + swap
195
        var op0 = (instruction >> 28) & 0b1111; //Bits 28-31
×
196
        var op1 = instruction.TestBit(26); //Bit 26
×
197
        var op2 = (instruction >> 23) & 0b11; //Bits 23-24
×
198
        var op3 = (instruction >> 16) & 0b11_1111; //Bits 16-21
×
199
        var op4 = (instruction >> 10) & 0b11; // Bits 10-11
200
        
201
        if(op1)
×
202
            throw new Arm64UndefinedInstructionException("Load/store (exclusive register|ordered)|compare/swap: op1 set");
×
203
        
204
        if(op2 == 0 && op3.TestBit(6))
×
205
            throw new Arm64UndefinedInstructionException("Load/store (exclusive register|ordered)|compare/swap: op2=0, op3 hi bit set");
×
206

207
        // op0 xx00 | op1 0 | op2 0x | op3 empty | op4 empty | Load/store exclusive
208
        if ((op0 & 0b11) == 0 && !op1 && !op2.TestBit(1)) 
×
209
            return LoadStoreExclusive(instruction);
×
210
        
211
        if(op2 != 1)
×
212
            throw new Arm64UndefinedInstructionException("Load/store (exclusive register|ordered)|compare/swap: op2 was not 0 or 1");
×
213

214
        if (op3.TestBit(6))
×
215
            return CompareAndSwap(instruction);
×
216

217
        return LoadStoreOrdered(instruction);
×
218
    }
219
    
220
    private static Arm64Instruction LoadStoreExclusive(uint instruction)
221
    {
222
        return new()
×
223
        {
×
224
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
225
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
×
226
        };
×
227
    }
228
    
229
    private static Arm64Instruction CompareAndSwap(uint instruction)
230
    {
231
        return new()
×
232
        {
×
233
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
234
            MnemonicCategory = Arm64MnemonicCategory.Comparison, 
×
235
        };
×
236
    }
237
    
238
    private static Arm64Instruction LoadStoreOrdered(uint instruction)
239
    {
240
        return new()
×
241
        {
×
242
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
243
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
×
244
        };
×
245
    }
246

247
    private static Arm64Instruction DisassembleLdAprRegisterLiteralOrMemoryCopySet(uint instruction)
248
    {
249
        //LDAPR/STLR, load/store register literal, memory copy, or memory set
250
        var op1 = instruction.TestBit(26); //Bit 26
7✔
251
        var op2 = (instruction >> 23) & 0b11; //Bits 23-24
7✔
252
        var op3 = (instruction >> 16) & 0b11_1111; //Bits 16-21
7✔
253
        var op4 = (instruction >> 10) & 0b11; //Bits 10-11
7✔
254

255
        if (!op2.TestBit(1))
7!
256
            return LoadRegisterLiteral(instruction);
7✔
257
        
258
        if(op3.TestBit(5))
×
259
            throw new Arm64UndefinedInstructionException("LdAprRegisterLiteralOrMemoryCopySet: op3 hi bit set");
×
260

261
        return op4.TestBit(0)
×
262
            ? MemoryCopyOrSet(instruction)
×
263
            : LdarpOrStlr(instruction);
×
264
    }
265
    
266
    private static Arm64Instruction LoadRegisterLiteral(uint instruction)
267
    {
268
        var opc = (instruction >> 30) & 0b11; // Bits 30-31
7✔
269
        var v = instruction.TestBit(26);
7✔
270
        var imm19 = (instruction >> 5) & 0b111_1111_1111_1111_1111; // Bits 5-23
7✔
271
        imm19 <<= 2; //4-byte aligned
7✔
272
        var label = Arm64CommonUtils.SignExtend(imm19, 21, 64); //21 is 19 + 2 for the left shift
7✔
273
        var rt = (int)instruction & 0b1_1111;
7✔
274
        
275
        return opc switch
7!
276
        {
7✔
277
            (0b00 or 0b01) when !v => new()
6✔
278
            {
2✔
279
                Mnemonic = Arm64Mnemonic.LDR,
2✔
280
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
2✔
281
                Op0Kind = Arm64OperandKind.Register,
2✔
282
                Op1Kind = Arm64OperandKind.ImmediatePcRelative,
2✔
283
                Op0Reg = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rt,
2✔
284
                Op1Imm = label
2✔
285
            },
2✔
286
            0b10 when !v =>  new()
3✔
287
            {
1✔
288
                Mnemonic = Arm64Mnemonic.LDRSW,
1✔
289
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
1✔
290
                Op0Kind = Arm64OperandKind.Register,
1✔
291
                Op1Kind = Arm64OperandKind.ImmediatePcRelative,
1✔
292
                Op0Reg = Arm64Register.X0 + rt,
1✔
293
                Op1Imm = label
1✔
294
            },
1✔
295
            0b11 when !v =>  new()
2!
296
            {
1✔
297
                Mnemonic = Arm64Mnemonic.PRFM,
1✔
298
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
1✔
299
                Op0Kind = Arm64OperandKind.Immediate,
1✔
300
                Op1Kind = Arm64OperandKind.ImmediatePcRelative,
1✔
301
                Op0Imm = rt, //TODO Prefetch isn't just a raw imm, it's a combination of well-defined type | cache level | cache policy
1✔
302
                Op1Imm = label
1✔
303
            },
1✔
304
            (0b00 or 0b01 or 0b10) when v => new()
6!
305
            {
3✔
306
                Mnemonic = Arm64Mnemonic.LDR,
3✔
307
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
3✔
308
                Op0Kind = Arm64OperandKind.Register,
3✔
309
                Op1Kind = Arm64OperandKind.ImmediatePcRelative,
3✔
310
                Op0Reg = (opc == 0b00 ? Arm64Register.S0 : opc == 0b01 ? Arm64Register.D0 : Arm64Register.V0) + rt,
3✔
311
                Op1Imm = label
3✔
312
            },
3✔
313
            _ => throw new Arm64UndefinedInstructionException("Unallocated")
×
314
        };
7✔
315
    }
316
    
317
    private static Arm64Instruction MemoryCopyOrSet(uint instruction)
318
    {
319
        return new()
×
320
        {
×
321
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
322
            MnemonicCategory = Arm64MnemonicCategory.Move, 
×
323
        };
×
324
    }
325
    
326
    private static Arm64Instruction LdarpOrStlr(uint instruction)
327
    {
328
        // FEAT_LRCPC2
329
        return new()
×
330
        {
×
331
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
332
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
×
333
        };
×
334
    }
335

336
    private static Arm64Instruction DisassembleLoadStorePairs(uint instruction)
337
    {
338
        var op2 = (instruction >> 23) & 0b11; //Bits 23-24
87✔
339

340
        return op2 switch
87!
341
        {
87✔
342
            0b00 => LoadStoreNoAllocatePairs(instruction), //load/store no-allocate pairs
×
343
            0b01 => LoadStoreRegisterPair(instruction, Arm64MemoryIndexMode.PostIndex), //load/store register pair (post-indexed)
7✔
344
            0b10 => LoadStoreRegisterPair(instruction, Arm64MemoryIndexMode.Offset), //load/store register pair (offset)
73✔
345
            0b11 => LoadStoreRegisterPair(instruction, Arm64MemoryIndexMode.PreIndex), //load/store register pair (pre-indexed)
7✔
346
            _ => throw new("Loads/store pairs: Impossible op2 value")
×
347
        };
87✔
348
    }
349

350
    //The 'xx11' category of loads/stores
351
    private static Arm64Instruction DisassembleLoadStoreRegisterOrAtomic(uint instruction)
352
    {
353
        var op2 = (instruction >> 23) & 0b11; //Bits 23-24
121✔
354
        var op3 = (instruction >> 16) & 0b11_1111; //Bits 16-21
121✔
355
        var op4 = (instruction >> 10) & 0b11; //Bits 10-11
121✔
356

357
        //Bottom bit of op2 is irrelevant
358
        op2 >>= 1;
121✔
359

360
        if (op2 == 1)
121✔
361
            //Load/store reg unsigned immediate
362
            return LoadStoreRegFromImmUnsigned(instruction);
118✔
363

364
        //Check top bit of op3
365
        if (op3 >> 5 == 1)
3✔
366
            //Atomic, or load/store reg with non-immediate, depending on op1
367
            return op4 switch
1!
368
            {
1✔
369
                0b00 => AtomicMemoryOperation(instruction), //Atomic
×
370
                0b10 => LoadStoreRegisterFromRegisterOffset(instruction), //Load/store (reg), (reg + x)
1✔
371
                _ => LoadStoreRegisterFromPac(instruction), //Load store (reg), (pac)
×
372
            };
1✔
373

374
        //Some kind of load/store reg with an immediate
375
        return op4 switch
2!
376
        {
2✔
377
            0b00 => LoadStoreRegisterFromImmUnscaled(instruction), //Load/store (reg), (unscaled immediate)
×
378
            0b01 => LoadStoreRegisterFromImm(instruction, Arm64MemoryIndexMode.PostIndex), //Load/store (reg), (post-indexed immediate)
1✔
379
            0b10 => LoadStoreRegisterUnprivileged(instruction), //Load/store (reg), (unprivileged)
×
380
            0b11 => LoadStoreRegisterFromImm(instruction, Arm64MemoryIndexMode.PreIndex), //Load/Store (reg), (pre-indexed immediate)
1✔
381
            _ => throw new("Impossible op4"),
×
382
        };
2✔
383
    }
384

385
    private static Arm64Instruction LoadStoreRegisterFromImm(uint instruction, Arm64MemoryIndexMode memoryIndexMode)
386
    {
387
        // Load/store immediate pre-indexed
388

389
        var size = (instruction >> 30) & 0b11; //Bits 30-31
2✔
390
        var isVector = instruction.TestBit(26); //Bit 26
2✔
391
        var opc = (instruction >> 22) & 0b11; //Bits 22-23
2✔
392
        var imm9 = (instruction >> 12) & 0b1_1111_1111; //Bits 12-20
2✔
393
        var rn = (int)(instruction >> 5) & 0b11111; //Bits 5-9
2✔
394
        var rt = (int)(instruction & 0b11111); //Bits 0-4
2✔
395

396
        if (size is 0b10 or 0b11)
2✔
397
        {
398
            var invalid = isVector ? opc is 0b10 or 0b11 : opc is 0b11;
1!
399
            
400
            if (invalid)
1!
401
                throw new Arm64UndefinedInstructionException($"Load/store immediate pre-indexed: Invalid size/opc combination. size: {size}, opc: {opc}");
×
402
        }
403
        
404
        //Note to self - this logic is copied from further down but has had some minor adjustments made, it may still be incorrect in places
405
        //so if something seems wrong, it probably is!
406
        var mnemonic = opc switch
2!
407
        {
2✔
408
            0b00 => size switch
×
409
            {
×
410
                0b00 when !isVector => Arm64Mnemonic.STRB,
×
411
                0b01 when !isVector => Arm64Mnemonic.STRH,
×
412
                0b10 or 0b11 when !isVector => Arm64Mnemonic.STR,
×
413
                _ when isVector => Arm64Mnemonic.STR,
×
414
                _ => throw new($"Impossible size: {size}")
×
415
            },
×
416
            0b01 => size switch
2!
417
            {
2✔
418
                0b00 when !isVector => Arm64Mnemonic.LDRB,
2!
419
                0b01 when !isVector => Arm64Mnemonic.LDRH,
×
420
                0b10 or 0b11 when !isVector => Arm64Mnemonic.LDR,
2!
421
                _ when isVector => Arm64Mnemonic.LDR,
×
422
                _ => throw new($"Impossible size: {size}")
×
423
            },
2✔
424
            0b10 => size switch
×
425
            {
×
426
                0b00 when !isVector => Arm64Mnemonic.LDRSB, //64-bit variant
×
427
                0b01 when !isVector => Arm64Mnemonic.LDRSH, //64-bit variant
×
428
                0b10 when !isVector => Arm64Mnemonic.LDRSW,
×
429
                0b00 when isVector => Arm64Mnemonic.STR, //128-bit store
×
430
                _ => throw new($"Impossible size: {size}")
×
431
            },
×
432
            0b11 => size switch
×
433
            {
×
434
                0b00 when !isVector => Arm64Mnemonic.LDRSB, //32-bit variant
×
435
                0b01 when !isVector => Arm64Mnemonic.LDRSH, //32-bit variant
×
436
                0b00 when isVector => Arm64Mnemonic.LDR, //128-bit load
×
437
                _ => throw new($"Impossible size: {size}")
×
438
            },
×
439
            _ => throw new("Impossible opc value")
×
440
        };
2✔
441
        
442
        var baseReg = mnemonic switch
2!
443
        {
2✔
444
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector && opc is 0 => size switch
1!
445
            {
×
446
                0 => Arm64Register.B0,
×
447
                1 => Arm64Register.H0,
×
448
                2 => Arm64Register.S0,
×
449
                3 => Arm64Register.D0,
×
450
                _ => throw new("Impossible size")
×
451
            },
×
452
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector => Arm64Register.V0, //128-bit vector
1!
453
            Arm64Mnemonic.STRB or Arm64Mnemonic.LDRB or Arm64Mnemonic.STRH or Arm64Mnemonic.LDRH => Arm64Register.W0,
1✔
454
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when size is 0b10 => Arm64Register.W0,
1!
455
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR => Arm64Register.X0,
1✔
456
            Arm64Mnemonic.LDRSH when opc is 0b10 => Arm64Register.X0,
×
457
            Arm64Mnemonic.LDRSH => Arm64Register.W0,
×
458
            Arm64Mnemonic.LDRSW => Arm64Register.X0,
×
459
            _ => throw new("Impossible mnemonic")
×
460
        };
2✔
461
        
462
        var regT = baseReg + rt;
2✔
463
        var regN = Arm64Register.X0 + rn;
2✔
464
        
465
        var offset = Arm64CommonUtils.SignExtend(imm9, 9, 64);
2✔
466

467
        return new()
2✔
468
        {
2✔
469
            Mnemonic = mnemonic,
2✔
470
            Op0Kind = Arm64OperandKind.Register,
2✔
471
            Op1Kind = Arm64OperandKind.Memory,
2✔
472
            MemIndexMode = memoryIndexMode,
2✔
473
            Op0Reg = regT,
2✔
474
            MemBase = regN,
2✔
475
            MemOffset = offset,
2✔
476
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
2✔
477
        };
2✔
478
    }
479

480
    private static Arm64Instruction LoadStoreNoAllocatePairs(uint instruction)
481
    {
482
        var opc = (instruction >> 30) & 0b11; // Bits 30-31
×
483
        var v = instruction.TestBit(26);
×
484
        var l = instruction.TestBit(22);
×
485

486
        var imm7 = (instruction >> 15) & 0b111_1111; // Bits - 15-21
×
487
        var rt2 = (int)(instruction >> 10) & 0b1_1111; // Bits - 10-14
×
488
        var rn = (int)(instruction >> 5) & 0b1_1111; // Bits - 5-9
×
489
        var rt = (int)instruction & 0b1_1111; // Bits - 0-14
×
490

491
        var offset = Arm64CommonUtils.SignExtend(imm7, 7, 64) << (2 + (l ? 1 : 0));
×
492
        
493
        return opc switch
×
494
        {
×
495
            (0b00 or 0b10) when !v && !l => new()
×
496
            {
×
497
                Mnemonic = Arm64Mnemonic.STNP,
×
498
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
499
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
500
                MemOffset = offset,
×
501
                MemBase = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rn,
×
502
                Op0Kind = Arm64OperandKind.Register,
×
503
                Op1Kind = Arm64OperandKind.Register,
×
504
                Op2Kind = Arm64OperandKind.Memory,
×
505
                Op0Reg = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rt,
×
506
                Op1Reg = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rt2
×
507
            },
×
508
            (0b00 or 0b10) when !v && l => new()
×
509
            {
×
510
                Mnemonic = Arm64Mnemonic.LDNP,
×
511
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
512
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
513
                MemOffset = offset,
×
514
                MemBase = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rn,
×
515
                Op0Kind = Arm64OperandKind.Register,
×
516
                Op1Kind = Arm64OperandKind.Register,
×
517
                Op2Kind = Arm64OperandKind.Memory,
×
518
                Op0Reg = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rt,
×
519
                Op1Reg = (opc == 0b00 ? Arm64Register.W0 : Arm64Register.X0) + rt2
×
520
            },
×
521
            0b00 when v && !l => new()
×
522
            {
×
523
                Mnemonic = Arm64Mnemonic.STNP,
×
524
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
525
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
526
                MemOffset = offset,
×
527
                MemBase = Arm64Register.X0 + rn,
×
528
                Op0Kind = Arm64OperandKind.Register,
×
529
                Op1Kind = Arm64OperandKind.Register,
×
530
                Op2Kind = Arm64OperandKind.Memory,
×
531
                Op0Reg = Arm64Register.S0 + rt,
×
532
                Op1Reg = Arm64Register.S0 + rt2
×
533
            },
×
534
            0b01 when v && !l => new()
×
535
            {
×
536
                Mnemonic = Arm64Mnemonic.STNP,
×
537
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
538
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
539
                MemOffset = offset,
×
540
                MemBase = Arm64Register.X0 + rn,
×
541
                Op0Kind = Arm64OperandKind.Register,
×
542
                Op1Kind = Arm64OperandKind.Register,
×
543
                Op2Kind = Arm64OperandKind.Memory,
×
544
                Op0Reg = Arm64Register.D0 + rt,
×
545
                Op1Reg = Arm64Register.D0 + rt2
×
546
            },
×
547
            0b10 when v && !l => new()
×
548
            {
×
549
                Mnemonic = Arm64Mnemonic.STNP,
×
550
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
551
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
552
                MemOffset = offset,
×
553
                MemBase = Arm64Register.X0 + rn,
×
554
                Op0Kind = Arm64OperandKind.Register,
×
555
                Op1Kind = Arm64OperandKind.Register,
×
556
                Op2Kind = Arm64OperandKind.Memory,
×
557
                Op0Reg = Arm64Register.V0 + rt, // aka Q0
×
558
                Op1Reg = Arm64Register.V0 + rt2 // aka Q0
×
559
            },
×
560
            0b00 when v && l => new()
×
561
            {
×
562
                Mnemonic = Arm64Mnemonic.LDNP,
×
563
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
564
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
565
                MemOffset = offset,
×
566
                MemBase = Arm64Register.X0 + rn,
×
567
                Op0Kind = Arm64OperandKind.Register,
×
568
                Op1Kind = Arm64OperandKind.Register,
×
569
                Op2Kind = Arm64OperandKind.Memory,
×
570
                Op0Reg = Arm64Register.S0 + rt,
×
571
                Op1Reg = Arm64Register.S0 + rt2
×
572
            },
×
573
            0b01 when v && l => new()
×
574
            {
×
575
                Mnemonic = Arm64Mnemonic.LDNP,
×
576
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
577
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
578
                MemOffset = offset,
×
579
                MemBase = Arm64Register.X0 + rn,
×
580
                Op0Kind = Arm64OperandKind.Register,
×
581
                Op1Kind = Arm64OperandKind.Register,
×
582
                Op2Kind = Arm64OperandKind.Memory,
×
583
                Op0Reg = Arm64Register.D0 + rt,
×
584
                Op1Reg = Arm64Register.D0 + rt2
×
585
            },
×
586
            0b10 when v && l => new()
×
587
            {
×
588
                Mnemonic = Arm64Mnemonic.LDNP,
×
589
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
590
                MemIndexMode = Arm64MemoryIndexMode.Offset,
×
591
                MemOffset = offset,
×
592
                MemBase = Arm64Register.X0 + rn,
×
593
                Op0Kind = Arm64OperandKind.Register,
×
594
                Op1Kind = Arm64OperandKind.Register,
×
595
                Op2Kind = Arm64OperandKind.Memory,
×
596
                Op0Reg = Arm64Register.V0 + rt, // aka Q0
×
597
                Op1Reg = Arm64Register.V0 + rt2 // aka Q0
×
598
            },
×
599
            _ => throw new Arm64UndefinedInstructionException("Unallocated")
×
600
        };
×
601
    }
602

603
    private static Arm64Instruction LoadStoreRegisterPair(uint instruction, Arm64MemoryIndexMode mode)
604
    {
605
        //Page C4-559
606

607
        var opc = (instruction >> 30) & 0b11; //Bits 30-31
87✔
608
        var imm7 = (instruction >> 15) & 0b111_1111; //Bits 15-21
87✔
609
        var rt2 = (int)(instruction >> 10) & 0b1_1111; //Bits 10-14
87✔
610
        var rn = (int)(instruction >> 5) & 0b1_1111; //Bits 5-9
87✔
611
        var rt = (int)(instruction & 0b1_1111); //Bits 0-4
87✔
612

613
        var isVector = instruction.TestBit(26);
87✔
614
        var isLoad = instruction.TestBit(22);
87✔
615

616
        //opc: 
617
        //00 - stp/ldp (32-bit + 32-bit fp)
618
        //01 - stgp, ldpsw, stp/ldp (64-bit fp)
619
        //10 - stp/ldp (64-bit + 128-bit fp)
620
        //11 - reserved
621

622
        if (opc == 0b11)
87!
623
            throw new Arm64UndefinedInstructionException("Load/store register pair (pre-indexed): opc == 0b11");
×
624

625
        var mnemonic = isLoad ? Arm64Mnemonic.LDP : Arm64Mnemonic.STP;
87✔
626

627
        if (opc == 1 && !isVector)
87!
628
            mnemonic = isLoad ? Arm64Mnemonic.LDPSW : Arm64Mnemonic.STGP; //Store Allocation taG (64-bit) and Pair/LoaD Pair of registers Signed Ward (32-bit) 
×
629

630
        var destBaseReg = opc switch
87!
631
        {
87✔
632
            0b00 when isVector => Arm64Register.S0, //32-bit vector
×
633
            0b00 => Arm64Register.W0, //32-bit
×
634
            0b01 when mnemonic == Arm64Mnemonic.STGP => Arm64Register.W0, //32-bit
×
635
            0b01 => Arm64Register.D0, //All other group 1 is 64-bit vector
×
636
            0b10 when isVector => Arm64Register.V0, //128-bit vector
87!
637
            0b10 => Arm64Register.X0, //64-bit
87✔
638
            _ => throw new("Impossible opc value")
×
639
        };
87✔
640

641
        var dataSizeBits = opc switch
87!
642
        {
87✔
643
            0b00 => 32,
×
644
            0b01 when mnemonic == Arm64Mnemonic.STGP => 32,
×
645
            0b01 => 64,
×
646
            0b10 when isVector => 128,
87!
647
            0b10 => 64,
87✔
648
            _ => throw new("Impossible opc value")
×
649
        };
87✔
650

651
        var dataSizeBytes = dataSizeBits / 8;
87✔
652

653
        //The offset must be aligned to the size of the data so is stored in imm7 divided by this factor
654
        //So we multiply by the size of the data to get the offset
655
        //It is stored signed.
656
        var realImm7 = Arm64CommonUtils.CorrectSignBit(imm7, 7);
87✔
657

658
        var reg1 = destBaseReg + rt;
87✔
659
        var reg2 = destBaseReg + rt2;
87✔
660
        var regN = Arm64Register.X0 + rn;
87✔
661

662
        return new()
87✔
663
        {
87✔
664
            Mnemonic = mnemonic,
87✔
665
            Op0Kind = Arm64OperandKind.Register,
87✔
666
            Op1Kind = Arm64OperandKind.Register,
87✔
667
            Op2Kind = Arm64OperandKind.Memory,
87✔
668
            Op0Reg = reg1,
87✔
669
            Op1Reg = reg2,
87✔
670
            MemBase = regN,
87✔
671
            MemOffset = realImm7 * dataSizeBytes,
87✔
672
            MemIndexMode = mode,
87✔
673
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
87✔
674
        };
87✔
675
    }
676

677
    private static Arm64Instruction LoadStoreRegFromImmUnsigned(uint instruction)
678
    {
679
        var size = (instruction >> 30) & 0b11; //Bits 30-31
118✔
680
        var isVector = instruction.TestBit(26);
118✔
681
        var opc = (instruction >> 22) & 0b11; //Bits 22-23
118✔
682
        var imm12 = (instruction >> 10) & 0b1111_1111_1111; //Bits 10-21
118✔
683
        var rn = (int)(instruction >> 5) & 0b11111; //Bits 5-9
118✔
684
        var rt = (int)(instruction & 0b11111); //Bits 0-4
118✔
685
        
686
        //Zero extend imm12 to 64-bit
687
        var immediate = (long)imm12;
118✔
688
        immediate <<= (int) size; //Shift left by the size... apparently?
118✔
689

690
        Arm64Register baseReg;
691
        Arm64Mnemonic mnemonic;
692
        
693
        if (isVector)
118✔
694
        {
695
            //For once, SIMD/FP is the simple path. It's always LDR or STR, and for all but the 128-bit version, the register depends on size
696
            //Let's get the 128-bit check out first
697
            if (opc is 0b10 or 0b11)
1!
698
            {
699
                //128-bit. Ensure size is 00
NEW
700
                if (size != 0)
×
NEW
701
                    throw new Arm64UndefinedInstructionException("Load/store register from immediate (unsigned): opc 0b10/0b11 unallocated for size > 0");
×
702
                
NEW
703
                mnemonic = opc == 0b10 ? Arm64Mnemonic.STR : Arm64Mnemonic.LDR;
×
NEW
704
                baseReg = Arm64Register.V0; //128-bit variant
×
705
            }
706
            else
707
            {
708
                mnemonic = opc == 0b00 ? Arm64Mnemonic.STR : Arm64Mnemonic.LDR;
1!
709
                baseReg = size switch
1!
710
                {
1✔
NEW
711
                    0b00 => Arm64Register.B0, //8-bit variant
×
NEW
712
                    0b01 => Arm64Register.H0, //16-bit variant
×
NEW
713
                    0b10 => Arm64Register.S0, //32-bit variant
×
714
                    0b11 => Arm64Register.D0, //64-bit variant
1✔
NEW
715
                    _ => throw new("Impossible size")
×
716
                };
1✔
717
            }
718
            
719
            return new()
1✔
720
            {
1✔
721
                Mnemonic = mnemonic,
1✔
722
                Op0Kind = Arm64OperandKind.Register,
1✔
723
                Op1Kind = Arm64OperandKind.Memory,
1✔
724
                Op0Reg = baseReg + rt,
1✔
725
                MemBase = Arm64Register.X0 + rn,
1✔
726
                MemOffset = immediate,
1✔
727
                MemIndexMode = Arm64MemoryIndexMode.Offset,
1✔
728
                MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
1✔
729
            };
1✔
730
        }
731
        
732
        //Now we have to deal with the non-SIMD/FP case.
733

734
        //This is considerably less clean than it perhaps could be because they don't stick to patterns and 
735
        //the mnemonics are different for different sizes.
736
        //Example - in general, even opc is a store, odd is a load. But opc == 11 && !v && size = 0 => LDRSB. :(
737
        mnemonic = opc switch
117!
738
        {
117✔
739
            0b00 => size switch
8!
740
            {
8✔
741
                0b00 => Arm64Mnemonic.STRB,
7✔
NEW
742
                0b01 => Arm64Mnemonic.STRH,
×
743
                0b10 or 0b11 => Arm64Mnemonic.STR, //32 or 64-bit
1✔
UNCOV
744
                _ => throw new($"Impossible size: {size}")
×
745
            },
8✔
746
            0b01 => size switch
109!
747
            {
109✔
748
                0b00 => Arm64Mnemonic.LDRB,
7✔
NEW
749
                0b01 => Arm64Mnemonic.LDRH,
×
750
                0b10 or 0b11 => Arm64Mnemonic.LDR, //32 or 64-bit
102✔
UNCOV
751
                _ => throw new($"Impossible size: {size}")
×
752
            },
109✔
753
            0b10 => size switch
×
754
            {
×
NEW
755
                //These all break the rules, they are loads but they are even opc
×
NEW
756
                0b00 => Arm64Mnemonic.LDRSB, //64-bit variant
×
NEW
757
                0b01 => Arm64Mnemonic.LDRSH, //64-bit variant
×
NEW
758
                0b10 => Arm64Mnemonic.LDRSW,
×
759
                0b11 => throw new Arm64UndefinedInstructionException("Load/store register from immediate (unsigned): opc 0b10 unallocated for size 0b11"), 
×
760
                _ => throw new($"Impossible size: {size}")
×
761
            },
×
762
            0b11 => size switch
×
763
            {
×
NEW
764
                0b00 => Arm64Mnemonic.LDRSB, //32-bit variant
×
NEW
765
                0b01 => Arm64Mnemonic.LDRSH, //32-bit variant
×
NEW
766
                0b10 => Arm64Mnemonic.PRFM, //TODO?
×
767
                0b11 => throw new Arm64UndefinedInstructionException("Load/store register from immediate (unsigned): opc 0b11 unallocated for size 0b11"),
×
768
                _ => throw new($"Impossible size: {size}")
×
769
            },
×
770
            _ => throw new("Impossible opc value")
×
771
        };
117✔
772

773
        if (mnemonic == Arm64Mnemonic.PRFM)
117!
774
            throw new NotImplementedException("If you're seeing this, reach out, because PRFM is not implemented.");
×
775

776
        baseReg = mnemonic switch
117!
777
        {
117✔
778
            Arm64Mnemonic.STRB or Arm64Mnemonic.LDRB or Arm64Mnemonic.STRH or Arm64Mnemonic.LDRH => Arm64Register.W0,
14✔
779
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when size is 0b10 => Arm64Register.W0,
117✔
780
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR => Arm64Register.X0,
89✔
781
            Arm64Mnemonic.LDRSH when opc is 0b10 => Arm64Register.X0,
×
782
            Arm64Mnemonic.LDRSH => Arm64Register.W0,
×
783
            Arm64Mnemonic.LDRSW => Arm64Register.X0,
×
784
            _ => throw new("Impossible mnemonic")
×
785
        };
117✔
786

787
        return new()
117✔
788
        {
117✔
789
            Mnemonic = mnemonic,
117✔
790
            Op0Kind = Arm64OperandKind.Register,
117✔
791
            Op1Kind = Arm64OperandKind.Memory,
117✔
792
            Op0Reg = baseReg + rt,
117✔
793
            MemBase = Arm64Register.X0 + rn,
117✔
794
            MemOffset = immediate,
117✔
795
            MemIndexMode = Arm64MemoryIndexMode.Offset,
117✔
796
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
117✔
797
        };
117✔
798
    }
799

800
    private static Arm64Instruction AtomicMemoryOperation(uint instruction)
801
    {
802
        return new()
×
803
        {
×
804
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
805
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
806
        };
×
807
    }
808

809
    private static Arm64Instruction LoadStoreRegisterFromRegisterOffset(uint instruction)
810
    {
811
        var size = (instruction >> 30) & 0b11; //Bits 30-31
1✔
812
        var isVector = instruction.TestBit(26);
1✔
813
        var opc = (instruction >> 22) & 0b11; //Bits 22-23
1✔
814
        var rm = (int)(instruction >> 16) & 0b1_1111; //Bits 16-20
1✔
815
        var option = (instruction >> 13) & 0b111; //Bits 13-15
1✔
816
        var sFlag = instruction.TestBit(12);
1✔
817
        var rn = (int)(instruction >> 5) & 0b1_1111; //Bits 5-9
1✔
818
        var rt = (int)(instruction & 0b1_1111); //Bits 0-4
1✔
819
        
820
        var mnemonic = opc switch
1!
821
        {
1✔
822
            0b00 => size switch
×
823
            {
×
824
                0b00 when !isVector => Arm64Mnemonic.STRB,
×
825
                0b01 when !isVector => Arm64Mnemonic.STRH,
×
826
                0b10 or 0b11 when !isVector => Arm64Mnemonic.STR,
×
827
                _ when isVector => Arm64Mnemonic.STR,
×
828
                _ => throw new($"Impossible size: {size}")
×
829
            },
×
830
            0b01 => size switch
1!
831
            {
1✔
832
                0b00 when !isVector => Arm64Mnemonic.LDRB,
×
833
                0b01 when !isVector => Arm64Mnemonic.LDRH,
×
834
                0b10 or 0b11 when !isVector => Arm64Mnemonic.LDR,
2!
835
                _ when isVector => Arm64Mnemonic.LDR,
×
836
                _ => throw new($"Impossible size: {size}")
×
837
            },
1✔
838
            0b10 => size switch
×
839
            {
×
840
                0b00 when !isVector => Arm64Mnemonic.LDRSB, //64-bit variant
×
841
                0b01 when !isVector => Arm64Mnemonic.LDRSH, //64-bit variant
×
842
                0b10 when !isVector => Arm64Mnemonic.LDRSW,
×
843
                0b11 when !isVector => Arm64Mnemonic.PRFM,
×
844
                0b00 when isVector => Arm64Mnemonic.STR, 
×
845
                _ when isVector => throw new Arm64UndefinedInstructionException("Load/store register from register offset: opc 0b10 unallocated for vectors when size > 0"),
×
846
                _ => throw new($"Impossible size: {size}")
×
847
            },
×
848
            0b11 => size switch
×
849
            {
×
850
                0b00 when !isVector => Arm64Mnemonic.LDRSB, //32-bit variant
×
851
                0b01 when !isVector => Arm64Mnemonic.LDRSH, //32-bit variant
×
852
                0b00 when isVector => Arm64Mnemonic.LDR, //128-bit load
×
853
                0b10 or 0b11 => throw new Arm64UndefinedInstructionException("Load/store register from register offset: opc 0b11 unallocated for size 0b1x"),
×
854
                _ when isVector => throw new Arm64UndefinedInstructionException("Load/store register from register offset: opc 0b11 unallocated for vectors when size > 0"),
×
855
                _ => throw new($"Impossible size: {size}")
×
856
            },
×
857
            _ => throw new("Impossible opc value")
×
858
        };
1✔
859

860
        var isShiftedRegister = option == 0b011;
1✔
861
        
862
        if (mnemonic == Arm64Mnemonic.PRFM)
1!
863
            throw new NotImplementedException("If you're seeing this, reach out, because PRFM is not implemented.");
×
864

865
        var baseReg = mnemonic switch
1!
866
        {
1✔
867
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector && opc is 0 => size switch
1!
868
            {
×
869
                0 => Arm64Register.B0,
×
870
                1 => Arm64Register.H0,
×
871
                2 => Arm64Register.S0,
×
872
                3 => Arm64Register.D0,
×
873
                _ => throw new("Impossible size")
×
874
            },
×
875
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when isVector => Arm64Register.V0, //128-bit vector
1!
876
            Arm64Mnemonic.STRB or Arm64Mnemonic.LDRB or Arm64Mnemonic.STRH or Arm64Mnemonic.LDRH => Arm64Register.W0,
×
877
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR when size is 0b10 => Arm64Register.W0,
2!
878
            Arm64Mnemonic.STR or Arm64Mnemonic.LDR => Arm64Register.X0,
×
879
            Arm64Mnemonic.LDRSH when opc is 0b10 => Arm64Register.X0,
×
880
            Arm64Mnemonic.LDRSH => Arm64Register.W0,
×
881
            Arm64Mnemonic.LDRSW => Arm64Register.X0,
×
882
            _ => throw new("Impossible mnemonic")
×
883
        };
1✔
884

885
        var secondReg64Bit = option.TestBit(0);
1✔
886
        var secondRegBase = secondReg64Bit ? Arm64Register.X0 : Arm64Register.W0;
1!
887
        var extendKind = (Arm64ExtendType)option;
1✔
888
        //Extended register: Mnemonic Wt, [Xn, Xm|Wm, ExtendKind Amount]
889
        //Shifted register: Mnemonic Wt, [Xn, Xm|Wm, LSL Amount]
890

891
        var shiftAmount = 0;
1✔
892
        if (sFlag && isShiftedRegister)
1✔
893
        {
894
            //Shift set, amount is size-dependent
895
            shiftAmount = size switch
1!
896
            {
1✔
897
                0b00 when isVector && opc == 0b11 => 4, //128-bit variant
×
898
                0b00 => 0, //8-bit variant, vector or otherwise
×
899
                0b01 => 1,
×
900
                0b10 => 2,
1✔
901
                0b11 => 3,
×
902
                _ => throw new("Impossible size")
×
903
            };
1✔
904
        }
905
        
906
        return new()
1!
907
        {
1✔
908
            Mnemonic = mnemonic,
1✔
909
            Op0Kind = Arm64OperandKind.Register,
1✔
910
            Op1Kind = Arm64OperandKind.Memory,
1✔
911
            Op0Reg = baseReg + rt,
1✔
912
            MemBase = Arm64Register.X0 + rn,
1✔
913
            MemAddendReg = secondRegBase + rm,
1✔
914
            MemIndexMode = Arm64MemoryIndexMode.Offset,
1✔
915
            MemExtendType = isShiftedRegister ? Arm64ExtendType.NONE : extendKind,
1✔
916
            MemShiftType = isShiftedRegister ? Arm64ShiftType.LSL : Arm64ShiftType.NONE,
1✔
917
            MemExtendOrShiftAmount = shiftAmount,
1✔
918
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
1✔
919
        };
1✔
920
    }
921

922
    private static Arm64Instruction LoadStoreRegisterFromPac(uint instruction)
923
    {
924
        return new()
×
925
        {
×
926
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
927
            MnemonicCategory = Arm64MnemonicCategory.PointerAuthentication,
×
928
        };
×
929
    }
930

931
    private static Arm64Instruction LoadStoreRegisterFromImmUnscaled(uint instruction)
932
    {
933
        var size = (instruction >> 30) & 0b11; //Bits 30-31
×
934
        var isVector = instruction.TestBit(26);
×
935
        var opc = (instruction >> 22) & 0b11; //Bits 22-23
×
936
        var imm9 = (instruction >> 12) & 0b1_1111_1111; //Bits 12-20
×
937
        var rn = (int)(instruction >> 5) & 0b1_1111; //Bits 5-9
×
938
        var rt = (int)(instruction & 0b1_1111); //Bits 0-4
×
939
        
940
        if(size is 1 or 3 && isVector && opc > 1)
×
941
            throw new Arm64UndefinedInstructionException("Load/store register from immediate (unsigned): opc > 1 unallocated for vectors when size > 1");
×
942
        
943
        //Here we go with this dance again...
944
        var mnemonic = opc switch
×
945
        {
×
946
            0b00 => size switch
×
947
            {
×
948
                0b00 when !isVector => Arm64Mnemonic.STURB,
×
949
                0b01 when !isVector => Arm64Mnemonic.STURH,
×
950
                0b10 or 0b11 when !isVector => Arm64Mnemonic.STUR,
×
951
                _ when isVector => Arm64Mnemonic.STUR,
×
952
                _ => throw new($"Impossible size: {size}")
×
953
            },
×
954
            0b01 => size switch
×
955
            {
×
956
                0b00 when !isVector => Arm64Mnemonic.LDURB,
×
957
                0b01 when !isVector => Arm64Mnemonic.LDURH,
×
958
                0b10 or 0b11 when !isVector => Arm64Mnemonic.LDUR,
×
959
                _ when isVector => Arm64Mnemonic.LDUR,
×
960
                _ => throw new($"Impossible size: {size}")
×
961
            },
×
962
            0b10 => size switch
×
963
            {
×
964
                0b00 when !isVector => Arm64Mnemonic.LDURSB, //64-bit variant
×
965
                0b01 when !isVector => Arm64Mnemonic.LDURSH, //64-bit variant
×
966
                0b10 when !isVector => Arm64Mnemonic.LDURSW,
×
967
                0b00 when isVector => Arm64Mnemonic.STUR, //128-bit store
×
968
                _ when isVector => throw new Arm64UndefinedInstructionException("Load/store register from immediate (unscaled): opc 0b10 unallocated for vectors when size > 0"),
×
969
                _ => throw new($"Impossible size: {size}")
×
970
            },
×
971
            0b11 => size switch
×
972
            {
×
973
                0b00 when !isVector => Arm64Mnemonic.LDURSB, //32-bit variant
×
974
                0b01 when !isVector => Arm64Mnemonic.LDURSH, //32-bit variant
×
975
                0b10 when !isVector => Arm64Mnemonic.PRFUM, //TODO?
×
976
                0b00 when isVector => Arm64Mnemonic.LDUR, //128-bit store
×
977
                0b11 => throw new Arm64UndefinedInstructionException("Load/store register from immediate (unscaled): opc 0b11 unallocated for size 0b11"),
×
978
                _ => throw new($"Impossible size: {size}")
×
979
            },
×
980
            _ => throw new("Impossible opc value")
×
981
        };
×
982
        
983
        if (mnemonic == Arm64Mnemonic.PRFUM)
×
984
            throw new NotImplementedException("If you're seeing this, reach out, because PRFUM is not implemented.");
×
985
        
986
        var baseReg = mnemonic switch
×
987
        {
×
988
            Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR when isVector && opc is 0 => size switch
×
989
            {
×
990
                0 => Arm64Register.B0,
×
991
                1 => Arm64Register.H0,
×
992
                2 => Arm64Register.S0,
×
993
                3 => Arm64Register.D0,
×
994
                _ => throw new("Impossible size")
×
995
            },
×
996
            Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR when isVector => Arm64Register.V0, //128-bit vector
×
997
            Arm64Mnemonic.STURB or Arm64Mnemonic.LDURB or Arm64Mnemonic.STURH or Arm64Mnemonic.LDURH => Arm64Register.W0,
×
998
            Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR when size is 0b10 => Arm64Register.W0,
×
999
            Arm64Mnemonic.STUR or Arm64Mnemonic.LDUR => Arm64Register.X0,
×
1000
            Arm64Mnemonic.LDURSH when opc is 0b10 => Arm64Register.X0,
×
1001
            Arm64Mnemonic.LDURSH => Arm64Register.W0,
×
1002
            Arm64Mnemonic.LDURSW => Arm64Register.X0,
×
1003
            _ => throw new("Impossible mnemonic")
×
1004
        };
×
1005
        
1006
        var regT = baseReg + rt;
×
1007
        var regN = Arm64Register.X0 + rn;
×
1008
        
1009
        //Sign extend imm9 to 64-bit
1010
        var immediate = Arm64CommonUtils.SignExtend(imm9, 9, 64);
×
1011
        
1012
        return new()
×
1013
        {
×
1014
            Mnemonic = mnemonic,
×
1015
            Op0Kind = Arm64OperandKind.Register,
×
1016
            Op1Kind = Arm64OperandKind.Memory,
×
1017
            Op0Reg = regT,
×
1018
            MemBase = regN,
×
1019
            MemOffset = immediate,
×
1020
            MemIndexMode = Arm64MemoryIndexMode.Offset,
×
1021
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
1022
        };
×
1023
    }
1024

1025
    private static Arm64Instruction LoadStoreRegisterUnprivileged(uint instruction)
1026
    {
1027
        return new()
×
1028
        {
×
1029
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
1030
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister,
×
1031
        };
×
1032
    }
1033

1034
    private static Arm64Instruction LoadStoreExclusivePair(uint instruction)
1035
    {
1036
        return new()
×
1037
        {
×
1038
            Mnemonic = Arm64Mnemonic.UNIMPLEMENTED,
×
1039
            MnemonicCategory = Arm64MnemonicCategory.MemoryToOrFromRegister, 
×
1040
        };
×
1041
    }
1042
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc