| ... | @@ -290,123 +290,6 @@ pub const Instruction = union(enum) { | ... | @@ -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 | pub const RegisterShift = struct { | 293 | pub const RegisterShift = struct { |
| 411 | rn: u5, | 294 | rn: u5, |
| 412 | imm6: u6, | 295 | imm6: u6, |
| ... | @@ -514,7 +397,124 @@ pub const Instruction = union(enum) { | ... | @@ -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 | const off = offset.toU12(); | 518 | const off = offset.toU12(); |
| 519 | const op1: u2 = blk: { | 519 | const op1: u2 = blk: { |
| 520 | switch (offset) { | 520 | switch (offset) { |
| ... | @@ -709,9 +709,10 @@ pub const Instruction = union(enum) { | ... | @@ -709,9 +709,10 @@ pub const Instruction = union(enum) { |
| 709 | | 709 | |
| 710 | pub const LdrArgs = struct { | 710 | pub const LdrArgs = struct { |
| 711 | rn: ?Register = null, | 711 | rn: ?Register = null, |
| 712 | offset: Offset = Offset.none, | 712 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 713 | literal: ?u19 = null, | 713 | literal: ?u19 = null, |
| 714 | }; | 714 | }; |
| | 715 | |
| 715 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | 716 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| 716 | if (args.rn) |rn| { | 717 | if (args.rn) |rn| { |
| 717 | return loadStoreRegister(rt, rn, args.offset, true); | 718 | return loadStoreRegister(rt, rn, args.offset, true); |
| ... | @@ -721,29 +722,50 @@ pub const Instruction = union(enum) { | ... | @@ -721,29 +722,50 @@ pub const Instruction = union(enum) { |
| 721 | } | 722 | } |
| 722 | | 723 | |
| 723 | pub const StrArgs = struct { | 724 | pub const StrArgs = struct { |
| 724 | offset: Offset = Offset.none, | 725 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 725 | }; | 726 | }; |
| | 727 | |
| 726 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | 728 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 727 | return loadStoreRegister(rt, rn, args.offset, false); | 729 | return loadStoreRegister(rt, rn, args.offset, false); |
| 728 | } | 730 | } |
| 729 | | 731 | |
| 730 | // Load or store pair of registers | 732 | // Load or store pair of registers |
| 731 | | 733 | |
| 732 | pub const LoadStorePairEncoding = enum(u2) { | 734 | pub const LoadStorePairOffset = struct { |
| 733 | PostIndex = 0b01, | 735 | encoding: enum(u2) { |
| 734 | SignedOffset = 0b10, | 736 | PostIndex = 0b01, |
| 735 | PreIndex = 0b11, | 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 { | 758 | |
| 738 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), true); | 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 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | 763 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 742 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); | 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 { | 767 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 746 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), false); | 768 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); |
| 747 | } | 769 | } |
| 748 | | 770 | |
| 749 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | 771 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| ... | @@ -867,15 +889,15 @@ test "serialize instructions" { | ... | @@ -867,15 +889,15 @@ test "serialize instructions" { |
| 867 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, | 889 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, |
| 868 | }, | 890 | }, |
| 869 | .{ // ldr x2, [x1, #1]! | 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 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, | 893 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, |
| 872 | }, | 894 | }, |
| 873 | .{ // ldr x2, [x1], #-1 | 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 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, | 897 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, |
| 876 | }, | 898 | }, |
| 877 | .{ // ldr x2, [x1], (x3) | 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 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, | 901 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, |
| 880 | }, | 902 | }, |
| 881 | .{ // ldr x2, label | 903 | .{ // ldr x2, label |
| ... | @@ -887,7 +909,7 @@ test "serialize instructions" { | ... | @@ -887,7 +909,7 @@ test "serialize instructions" { |
| 887 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, | 909 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, |
| 888 | }, | 910 | }, |
| 889 | .{ // str x2, [x1], (x3) | 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 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, | 913 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, |
| 892 | }, | 914 | }, |
| 893 | .{ // adr x2, #0x8 | 915 | .{ // adr x2, #0x8 |
| ... | @@ -907,20 +929,20 @@ test "serialize instructions" { | ... | @@ -907,20 +929,20 @@ test "serialize instructions" { |
| 907 | .expected = 0b1_00_10000_1111111111111111110_00010, | 929 | .expected = 0b1_00_10000_1111111111111111110_00010, |
| 908 | }, | 930 | }, |
| 909 | .{ // stp x1, x2, [sp, #8] | 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 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, | 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 | .{ // ldp x1, x2, [sp, #8] | 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 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, | 937 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, |
| 920 | }, | 938 | }, |
| 921 | .{ // ldp x1, x2, [sp, #16] | 939 | .{ // stp x1, x2, [sp, #-16]! |
| 922 | .inst = Instruction.ldp(.x1, .x2, Register.sp, 16, .SignedOffset), | 940 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), |
| 923 | .expected = 0b10_101_0_010_1_0000010_00010_11111_00001, | 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 | |