| ... | ... | @@ -290,123 +290,6 @@ pub const Instruction = union(enum) { |
| 290 | 290 | }; |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | | /// Represents the offset operand of a load or store instruction. |
| 294 | | /// Data can be loaded from memory with either an immediate offset |
| 295 | | /// or an offset that is stored in some register. |
| 296 | | pub const Offset = union(enum) { |
| 297 | | Immediate: union(enum) { |
| 298 | | PostIndex: i9, |
| 299 | | PreIndex: i9, |
| 300 | | Unsigned: u12, |
| 301 | | }, |
| 302 | | Register: struct { |
| 303 | | rm: u5, |
| 304 | | shift: union(enum) { |
| 305 | | Uxtw: u2, |
| 306 | | Lsl: u2, |
| 307 | | Sxtw: u2, |
| 308 | | Sxtx: u2, |
| 309 | | }, |
| 310 | | }, |
| 311 | | |
| 312 | | pub const none = Offset{ |
| 313 | | .Immediate = .{ .Unsigned = 0 }, |
| 314 | | }; |
| 315 | | |
| 316 | | pub fn toU12(self: Offset) u12 { |
| 317 | | return switch (self) { |
| 318 | | .Immediate => |imm_type| switch (imm_type) { |
| 319 | | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, |
| 320 | | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, |
| 321 | | .Unsigned => |v| v, |
| 322 | | }, |
| 323 | | .Register => |r| switch (r.shift) { |
| 324 | | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, |
| 325 | | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, |
| 326 | | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, |
| 327 | | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, |
| 328 | | }, |
| 329 | | }; |
| 330 | | } |
| 331 | | |
| 332 | | pub fn imm(offset: u12) Offset { |
| 333 | | return Offset{ |
| 334 | | .Immediate = .{ .Unsigned = offset }, |
| 335 | | }; |
| 336 | | } |
| 337 | | |
| 338 | | pub fn imm_post_index(offset: i9) Offset { |
| 339 | | return Offset{ |
| 340 | | .Immediate = .{ .PostIndex = offset }, |
| 341 | | }; |
| 342 | | } |
| 343 | | |
| 344 | | pub fn imm_pre_index(offset: i9) Offset { |
| 345 | | return Offset{ |
| 346 | | .Immediate = .{ .PreIndex = offset }, |
| 347 | | }; |
| 348 | | } |
| 349 | | |
| 350 | | pub fn reg(rm: Register) Offset { |
| 351 | | return Offset{ |
| 352 | | .Register = .{ |
| 353 | | .rm = rm.id(), |
| 354 | | .shift = .{ |
| 355 | | .Lsl = 0, |
| 356 | | }, |
| 357 | | }, |
| 358 | | }; |
| 359 | | } |
| 360 | | |
| 361 | | pub fn reg_uxtw(rm: Register, shift: u2) Offset { |
| 362 | | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 363 | | return Offset{ |
| 364 | | .Register = .{ |
| 365 | | .rm = rm.id(), |
| 366 | | .shift = .{ |
| 367 | | .Uxtw = shift, |
| 368 | | }, |
| 369 | | }, |
| 370 | | }; |
| 371 | | } |
| 372 | | |
| 373 | | pub fn reg_lsl(rm: Register, shift: u2) Offset { |
| 374 | | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 375 | | return Offset{ |
| 376 | | .Register = .{ |
| 377 | | .rm = rm.id(), |
| 378 | | .shift = .{ |
| 379 | | .Lsl = shift, |
| 380 | | }, |
| 381 | | }, |
| 382 | | }; |
| 383 | | } |
| 384 | | |
| 385 | | pub fn reg_sxtw(rm: Register, shift: u2) Offset { |
| 386 | | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 387 | | return Offset{ |
| 388 | | .Register = .{ |
| 389 | | .rm = rm.id(), |
| 390 | | .shift = .{ |
| 391 | | .Sxtw = shift, |
| 392 | | }, |
| 393 | | }, |
| 394 | | }; |
| 395 | | } |
| 396 | | |
| 397 | | pub fn reg_sxtx(rm: Register, shift: u2) Offset { |
| 398 | | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 399 | | return Offset{ |
| 400 | | .Register = .{ |
| 401 | | .rm = rm.id(), |
| 402 | | .shift = .{ |
| 403 | | .Sxtx = shift, |
| 404 | | }, |
| 405 | | }, |
| 406 | | }; |
| 407 | | } |
| 408 | | }; |
| 409 | | |
| 410 | 293 | pub const RegisterShift = struct { |
| 411 | 294 | rn: u5, |
| 412 | 295 | imm6: u6, |
| ... | ... | @@ -514,7 +397,124 @@ pub const Instruction = union(enum) { |
| 514 | 397 | }; |
| 515 | 398 | } |
| 516 | 399 | |
| 517 | | fn loadStoreRegister(rt: Register, rn: Register, offset: Offset, load: bool) Instruction { |
| 400 | /// Represents the offset operand of a load or store instruction. |
| 401 | /// Data can be loaded from memory with either an immediate offset |
| 402 | /// or an offset that is stored in some register. |
| 403 | pub const LoadStoreOffset = union(enum) { |
| 404 | Immediate: union(enum) { |
| 405 | PostIndex: i9, |
| 406 | PreIndex: i9, |
| 407 | Unsigned: u12, |
| 408 | }, |
| 409 | Register: struct { |
| 410 | rm: u5, |
| 411 | shift: union(enum) { |
| 412 | Uxtw: u2, |
| 413 | Lsl: u2, |
| 414 | Sxtw: u2, |
| 415 | Sxtx: u2, |
| 416 | }, |
| 417 | }, |
| 418 | |
| 419 | pub const none = LoadStoreOffset{ |
| 420 | .Immediate = .{ .Unsigned = 0 }, |
| 421 | }; |
| 422 | |
| 423 | pub fn toU12(self: LoadStoreOffset) u12 { |
| 424 | return switch (self) { |
| 425 | .Immediate => |imm_type| switch (imm_type) { |
| 426 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, |
| 427 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, |
| 428 | .Unsigned => |v| v, |
| 429 | }, |
| 430 | .Register => |r| switch (r.shift) { |
| 431 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, |
| 432 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, |
| 433 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, |
| 434 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, |
| 435 | }, |
| 436 | }; |
| 437 | } |
| 438 | |
| 439 | pub fn imm(offset: u12) LoadStoreOffset { |
| 440 | return .{ |
| 441 | .Immediate = .{ .Unsigned = offset }, |
| 442 | }; |
| 443 | } |
| 444 | |
| 445 | pub fn imm_post_index(offset: i9) LoadStoreOffset { |
| 446 | return .{ |
| 447 | .Immediate = .{ .PostIndex = offset }, |
| 448 | }; |
| 449 | } |
| 450 | |
| 451 | pub fn imm_pre_index(offset: i9) LoadStoreOffset { |
| 452 | return .{ |
| 453 | .Immediate = .{ .PreIndex = offset }, |
| 454 | }; |
| 455 | } |
| 456 | |
| 457 | pub fn reg(rm: Register) LoadStoreOffset { |
| 458 | return .{ |
| 459 | .Register = .{ |
| 460 | .rm = rm.id(), |
| 461 | .shift = .{ |
| 462 | .Lsl = 0, |
| 463 | }, |
| 464 | }, |
| 465 | }; |
| 466 | } |
| 467 | |
| 468 | pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { |
| 469 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 470 | return .{ |
| 471 | .Register = .{ |
| 472 | .rm = rm.id(), |
| 473 | .shift = .{ |
| 474 | .Uxtw = shift, |
| 475 | }, |
| 476 | }, |
| 477 | }; |
| 478 | } |
| 479 | |
| 480 | pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { |
| 481 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 482 | return .{ |
| 483 | .Register = .{ |
| 484 | .rm = rm.id(), |
| 485 | .shift = .{ |
| 486 | .Lsl = shift, |
| 487 | }, |
| 488 | }, |
| 489 | }; |
| 490 | } |
| 491 | |
| 492 | pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { |
| 493 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); |
| 494 | return .{ |
| 495 | .Register = .{ |
| 496 | .rm = rm.id(), |
| 497 | .shift = .{ |
| 498 | .Sxtw = shift, |
| 499 | }, |
| 500 | }, |
| 501 | }; |
| 502 | } |
| 503 | |
| 504 | pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { |
| 505 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); |
| 506 | return .{ |
| 507 | .Register = .{ |
| 508 | .rm = rm.id(), |
| 509 | .shift = .{ |
| 510 | .Sxtx = shift, |
| 511 | }, |
| 512 | }, |
| 513 | }; |
| 514 | } |
| 515 | }; |
| 516 | |
| 517 | fn loadStoreRegister(rt: Register, rn: Register, offset: LoadStoreOffset, load: bool) Instruction { |
| 518 | 518 | const off = offset.toU12(); |
| 519 | 519 | const op1: u2 = blk: { |
| 520 | 520 | switch (offset) { |
| ... | ... | @@ -709,9 +709,10 @@ pub const Instruction = union(enum) { |
| 709 | 709 | |
| 710 | 710 | pub const LdrArgs = struct { |
| 711 | 711 | rn: ?Register = null, |
| 712 | | offset: Offset = Offset.none, |
| 712 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 713 | 713 | literal: ?u19 = null, |
| 714 | 714 | }; |
| 715 | |
| 715 | 716 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| 716 | 717 | if (args.rn) |rn| { |
| 717 | 718 | return loadStoreRegister(rt, rn, args.offset, true); |
| ... | ... | @@ -721,29 +722,50 @@ pub const Instruction = union(enum) { |
| 721 | 722 | } |
| 722 | 723 | |
| 723 | 724 | pub const StrArgs = struct { |
| 724 | | offset: Offset = Offset.none, |
| 725 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 725 | 726 | }; |
| 727 | |
| 726 | 728 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 727 | 729 | return loadStoreRegister(rt, rn, args.offset, false); |
| 728 | 730 | } |
| 729 | 731 | |
| 730 | 732 | // Load or store pair of registers |
| 731 | 733 | |
| 732 | | pub const LoadStorePairEncoding = enum(u2) { |
| 733 | | PostIndex = 0b01, |
| 734 | | SignedOffset = 0b10, |
| 735 | | PreIndex = 0b11, |
| 734 | pub const LoadStorePairOffset = struct { |
| 735 | encoding: enum(u2) { |
| 736 | PostIndex = 0b01, |
| 737 | Signed = 0b10, |
| 738 | PreIndex = 0b11, |
| 739 | }, |
| 740 | offset: i9, |
| 741 | |
| 742 | pub fn none() LoadStorePairOffset { |
| 743 | return .{ .encoding = .Signed, .offset = 0 }; |
| 744 | } |
| 745 | |
| 746 | pub fn post_index(imm: i9) LoadStorePairOffset { |
| 747 | return .{ .encoding = .PostIndex, .offset = imm }; |
| 748 | } |
| 749 | |
| 750 | pub fn pre_index(imm: i9) LoadStorePairOffset { |
| 751 | return .{ .encoding = .PreIndex, .offset = imm }; |
| 752 | } |
| 753 | |
| 754 | pub fn signed(imm: i9) LoadStorePairOffset { |
| 755 | return .{ .encoding = .Signed, .offset = imm }; |
| 756 | } |
| 736 | 757 | }; |
| 737 | | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: i9, encoding: LoadStorePairEncoding) Instruction { |
| 738 | | return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), true); |
| 758 | |
| 759 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 760 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); |
| 739 | 761 | } |
| 740 | 762 | |
| 741 | 763 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 742 | 764 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); |
| 743 | 765 | } |
| 744 | 766 | |
| 745 | | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: i9, encoding: LoadStorePairEncoding) Instruction { |
| 746 | | return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), false); |
| 767 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 768 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); |
| 747 | 769 | } |
| 748 | 770 | |
| 749 | 771 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| ... | ... | @@ -867,15 +889,15 @@ test "serialize instructions" { |
| 867 | 889 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, |
| 868 | 890 | }, |
| 869 | 891 | .{ // ldr x2, [x1, #1]! |
| 870 | | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.imm_pre_index(1) }), |
| 892 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) }), |
| 871 | 893 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, |
| 872 | 894 | }, |
| 873 | 895 | .{ // ldr x2, [x1], #-1 |
| 874 | | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.imm_post_index(-1) }), |
| 896 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) }), |
| 875 | 897 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, |
| 876 | 898 | }, |
| 877 | 899 | .{ // ldr x2, [x1], (x3) |
| 878 | | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }), |
| 900 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 879 | 901 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, |
| 880 | 902 | }, |
| 881 | 903 | .{ // ldr x2, label |
| ... | ... | @@ -887,7 +909,7 @@ test "serialize instructions" { |
| 887 | 909 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, |
| 888 | 910 | }, |
| 889 | 911 | .{ // str x2, [x1], (x3) |
| 890 | | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.Offset.reg(.x3) }), |
| 912 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 891 | 913 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, |
| 892 | 914 | }, |
| 893 | 915 | .{ // adr x2, #0x8 |
| ... | ... | @@ -907,20 +929,20 @@ test "serialize instructions" { |
| 907 | 929 | .expected = 0b1_00_10000_1111111111111111110_00010, |
| 908 | 930 | }, |
| 909 | 931 | .{ // stp x1, x2, [sp, #8] |
| 910 | | .inst = Instruction.stp(.x1, .x2, Register.sp, 8, .SignedOffset), |
| 932 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), |
| 911 | 933 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, |
| 912 | 934 | }, |
| 913 | | .{ // stp x1, x2, [sp, #-16] |
| 914 | | .inst = Instruction.stp(.x1, .x2, Register.sp, -16, .SignedOffset), |
| 915 | | .expected = 0b10_101_0_010_0_1111110_00010_11111_00001, |
| 916 | | }, |
| 917 | 935 | .{ // ldp x1, x2, [sp, #8] |
| 918 | | .inst = Instruction.ldp(.x1, .x2, Register.sp, 8, .SignedOffset), |
| 936 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), |
| 919 | 937 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, |
| 920 | 938 | }, |
| 921 | | .{ // ldp x1, x2, [sp, #16] |
| 922 | | .inst = Instruction.ldp(.x1, .x2, Register.sp, 16, .SignedOffset), |
| 923 | | .expected = 0b10_101_0_010_1_0000010_00010_11111_00001, |
| 939 | .{ // stp x1, x2, [sp, #-16]! |
| 940 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), |
| 941 | .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, |
| 942 | }, |
| 943 | .{ // ldp x1, x2, [sp], #16 |
| 944 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), |
| 945 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, |
| 924 | 946 | }, |
| 925 | 947 | }; |
| 926 | 948 | |