| ... | @@ -19,7 +19,7 @@ pub const Register = enum(u6) { | ... | @@ -19,7 +19,7 @@ pub const Register = enum(u6) { |
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | 19 | w16, w17, w18, w19, w20, w21, w22, w23, |
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | 20 | w24, w25, w26, w27, w28, w29, w30, wzr, |
| 21 | | 21 | |
| 22 | pub const sp = .xzr; | 22 | pub const sp = Register.xzr; |
| 23 | | 23 | |
| 24 | pub fn id(self: Register) u5 { | 24 | pub fn id(self: Register) u5 { |
| 25 | return @truncate(u5, @enumToInt(self)); | 25 | return @truncate(u5, @enumToInt(self)); |
| ... | @@ -72,6 +72,9 @@ test "Register.id" { | ... | @@ -72,6 +72,9 @@ test "Register.id" { |
| 72 | | 72 | |
| 73 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); | 73 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); |
| 74 | testing.expectEqual(@as(u5, 31), Register.wzr.id()); | 74 | testing.expectEqual(@as(u5, 31), Register.wzr.id()); |
| | 75 | |
| | 76 | testing.expectEqual(@as(u5, 31), Register.sp.id()); |
| | 77 | testing.expectEqual(@as(u5, 31), Register.sp.id()); |
| 75 | } | 78 | } |
| 76 | | 79 | |
| 77 | test "Register.size" { | 80 | test "Register.size" { |
| ... | @@ -232,6 +235,16 @@ pub const Instruction = union(enum) { | ... | @@ -232,6 +235,16 @@ pub const Instruction = union(enum) { |
| 232 | fixed: u4 = 0b111_0, | 235 | fixed: u4 = 0b111_0, |
| 233 | size: u2, | 236 | size: u2, |
| 234 | }, | 237 | }, |
| | 238 | LoadStorePairOfRegisters: packed struct { |
| | 239 | rt1: u5, |
| | 240 | rn: u5, |
| | 241 | rt2: u5, |
| | 242 | imm7: u7, |
| | 243 | load: u1, |
| | 244 | encoding: u2, |
| | 245 | fixed: u5 = 0b101_0_0, |
| | 246 | opc: u2, |
| | 247 | }, |
| 235 | LoadLiteral: packed struct { | 248 | LoadLiteral: packed struct { |
| 236 | rt: u5, | 249 | rt: u5, |
| 237 | imm19: u19, | 250 | imm19: u19, |
| ... | @@ -268,6 +281,7 @@ pub const Instruction = union(enum) { | ... | @@ -268,6 +281,7 @@ pub const Instruction = union(enum) { |
| 268 | .MoveWideImmediate => |v| @bitCast(u32, v), | 281 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 269 | .PCRelativeAddress => |v| @bitCast(u32, v), | 282 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 270 | .LoadStoreRegister => |v| @bitCast(u32, v), | 283 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| | 284 | .LoadStorePairOfRegisters => |v| @bitCast(u32, v), |
| 271 | .LoadLiteral => |v| @bitCast(u32, v), | 285 | .LoadLiteral => |v| @bitCast(u32, v), |
| 272 | .ExceptionGeneration => |v| @bitCast(u32, v), | 286 | .ExceptionGeneration => |v| @bitCast(u32, v), |
| 273 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), | 287 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| ... | @@ -276,123 +290,6 @@ pub const Instruction = union(enum) { | ... | @@ -276,123 +290,6 @@ pub const Instruction = union(enum) { |
| 276 | }; | 290 | }; |
| 277 | } | 291 | } |
| 278 | | 292 | |
| 279 | /// Represents the offset operand of a load or store instruction. | | |
| 280 | /// Data can be loaded from memory with either an immediate offset | | |
| 281 | /// or an offset that is stored in some register. | | |
| 282 | pub const Offset = union(enum) { | | |
| 283 | Immediate: union(enum) { | | |
| 284 | PostIndex: i9, | | |
| 285 | PreIndex: i9, | | |
| 286 | Unsigned: u12, | | |
| 287 | }, | | |
| 288 | Register: struct { | | |
| 289 | rm: u5, | | |
| 290 | shift: union(enum) { | | |
| 291 | Uxtw: u2, | | |
| 292 | Lsl: u2, | | |
| 293 | Sxtw: u2, | | |
| 294 | Sxtx: u2, | | |
| 295 | }, | | |
| 296 | }, | | |
| 297 | | | |
| 298 | pub const none = Offset{ | | |
| 299 | .Immediate = .{ .Unsigned = 0 }, | | |
| 300 | }; | | |
| 301 | | | |
| 302 | pub fn toU12(self: Offset) u12 { | | |
| 303 | return switch (self) { | | |
| 304 | .Immediate => |imm_type| switch (imm_type) { | | |
| 305 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, | | |
| 306 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, | | |
| 307 | .Unsigned => |v| v, | | |
| 308 | }, | | |
| 309 | .Register => |r| switch (r.shift) { | | |
| 310 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, | | |
| 311 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, | | |
| 312 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, | | |
| 313 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, | | |
| 314 | }, | | |
| 315 | }; | | |
| 316 | } | | |
| 317 | | | |
| 318 | pub fn imm(offset: u12) Offset { | | |
| 319 | return Offset{ | | |
| 320 | .Immediate = .{ .Unsigned = offset }, | | |
| 321 | }; | | |
| 322 | } | | |
| 323 | | | |
| 324 | pub fn imm_post_index(offset: i9) Offset { | | |
| 325 | return Offset{ | | |
| 326 | .Immediate = .{ .PostIndex = offset }, | | |
| 327 | }; | | |
| 328 | } | | |
| 329 | | | |
| 330 | pub fn imm_pre_index(offset: i9) Offset { | | |
| 331 | return Offset{ | | |
| 332 | .Immediate = .{ .PreIndex = offset }, | | |
| 333 | }; | | |
| 334 | } | | |
| 335 | | | |
| 336 | pub fn reg(rm: Register) Offset { | | |
| 337 | return Offset{ | | |
| 338 | .Register = .{ | | |
| 339 | .rm = rm.id(), | | |
| 340 | .shift = .{ | | |
| 341 | .Lsl = 0, | | |
| 342 | }, | | |
| 343 | }, | | |
| 344 | }; | | |
| 345 | } | | |
| 346 | | | |
| 347 | pub fn reg_uxtw(rm: Register, shift: u2) Offset { | | |
| 348 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | | |
| 349 | return Offset{ | | |
| 350 | .Register = .{ | | |
| 351 | .rm = rm.id(), | | |
| 352 | .shift = .{ | | |
| 353 | .Uxtw = shift, | | |
| 354 | }, | | |
| 355 | }, | | |
| 356 | }; | | |
| 357 | } | | |
| 358 | | | |
| 359 | pub fn reg_lsl(rm: Register, shift: u2) Offset { | | |
| 360 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | | |
| 361 | return Offset{ | | |
| 362 | .Register = .{ | | |
| 363 | .rm = rm.id(), | | |
| 364 | .shift = .{ | | |
| 365 | .Lsl = shift, | | |
| 366 | }, | | |
| 367 | }, | | |
| 368 | }; | | |
| 369 | } | | |
| 370 | | | |
| 371 | pub fn reg_sxtw(rm: Register, shift: u2) Offset { | | |
| 372 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | | |
| 373 | return Offset{ | | |
| 374 | .Register = .{ | | |
| 375 | .rm = rm.id(), | | |
| 376 | .shift = .{ | | |
| 377 | .Sxtw = shift, | | |
| 378 | }, | | |
| 379 | }, | | |
| 380 | }; | | |
| 381 | } | | |
| 382 | | | |
| 383 | pub fn reg_sxtx(rm: Register, shift: u2) Offset { | | |
| 384 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | | |
| 385 | return Offset{ | | |
| 386 | .Register = .{ | | |
| 387 | .rm = rm.id(), | | |
| 388 | .shift = .{ | | |
| 389 | .Sxtx = shift, | | |
| 390 | }, | | |
| 391 | }, | | |
| 392 | }; | | |
| 393 | } | | |
| 394 | }; | | |
| 395 | | | |
| 396 | pub const RegisterShift = struct { | 293 | pub const RegisterShift = struct { |
| 397 | rn: u5, | 294 | rn: u5, |
| 398 | imm6: u6, | 295 | imm6: u6, |
| ... | @@ -500,7 +397,124 @@ pub const Instruction = union(enum) { | ... | @@ -500,7 +397,124 @@ pub const Instruction = union(enum) { |
| 500 | }; | 397 | }; |
| 501 | } | 398 | } |
| 502 | | 399 | |
| 503 | 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 { |
| 504 | const off = offset.toU12(); | 518 | const off = offset.toU12(); |
| 505 | const op1: u2 = blk: { | 519 | const op1: u2 = blk: { |
| 506 | switch (offset) { | 520 | switch (offset) { |
| ... | @@ -542,6 +556,49 @@ pub const Instruction = union(enum) { | ... | @@ -542,6 +556,49 @@ pub const Instruction = union(enum) { |
| 542 | } | 556 | } |
| 543 | } | 557 | } |
| 544 | | 558 | |
| | 559 | fn loadStorePairOfRegisters( |
| | 560 | rt1: Register, |
| | 561 | rt2: Register, |
| | 562 | rn: Register, |
| | 563 | offset: i9, |
| | 564 | encoding: u2, |
| | 565 | load: bool, |
| | 566 | ) Instruction { |
| | 567 | switch (rt1.size()) { |
| | 568 | 32 => { |
| | 569 | assert(-256 <= offset and offset <= 252); |
| | 570 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); |
| | 571 | return Instruction{ |
| | 572 | .LoadStorePairOfRegisters = .{ |
| | 573 | .rt1 = rt1.id(), |
| | 574 | .rn = rn.id(), |
| | 575 | .rt2 = rt2.id(), |
| | 576 | .imm7 = imm7, |
| | 577 | .load = @boolToInt(load), |
| | 578 | .encoding = encoding, |
| | 579 | .opc = 0b00, |
| | 580 | }, |
| | 581 | }; |
| | 582 | }, |
| | 583 | 64 => { |
| | 584 | assert(-512 <= offset and offset <= 504); |
| | 585 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); |
| | 586 | return Instruction{ |
| | 587 | .LoadStorePairOfRegisters = .{ |
| | 588 | .rt1 = rt1.id(), |
| | 589 | .rn = rn.id(), |
| | 590 | .rt2 = rt2.id(), |
| | 591 | .imm7 = imm7, |
| | 592 | .load = @boolToInt(load), |
| | 593 | .encoding = encoding, |
| | 594 | .opc = 0b10, |
| | 595 | }, |
| | 596 | }; |
| | 597 | }, |
| | 598 | else => unreachable, // unexpected register size |
| | 599 | } |
| | 600 | } |
| | 601 | |
| 545 | fn loadLiteral(rt: Register, imm19: u19) Instruction { | 602 | fn loadLiteral(rt: Register, imm19: u19) Instruction { |
| 546 | switch (rt.size()) { | 603 | switch (rt.size()) { |
| 547 | 32 => { | 604 | 32 => { |
| ... | @@ -652,9 +709,10 @@ pub const Instruction = union(enum) { | ... | @@ -652,9 +709,10 @@ pub const Instruction = union(enum) { |
| 652 | | 709 | |
| 653 | pub const LdrArgs = struct { | 710 | pub const LdrArgs = struct { |
| 654 | rn: ?Register = null, | 711 | rn: ?Register = null, |
| 655 | offset: Offset = Offset.none, | 712 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 656 | literal: ?u19 = null, | 713 | literal: ?u19 = null, |
| 657 | }; | 714 | }; |
| | 715 | |
| 658 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | 716 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| 659 | if (args.rn) |rn| { | 717 | if (args.rn) |rn| { |
| 660 | return loadStoreRegister(rt, rn, args.offset, true); | 718 | return loadStoreRegister(rt, rn, args.offset, true); |
| ... | @@ -664,12 +722,56 @@ pub const Instruction = union(enum) { | ... | @@ -664,12 +722,56 @@ pub const Instruction = union(enum) { |
| 664 | } | 722 | } |
| 665 | | 723 | |
| 666 | pub const StrArgs = struct { | 724 | pub const StrArgs = struct { |
| 667 | offset: Offset = Offset.none, | 725 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 668 | }; | 726 | }; |
| | 727 | |
| 669 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | 728 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 670 | return loadStoreRegister(rt, rn, args.offset, false); | 729 | return loadStoreRegister(rt, rn, args.offset, false); |
| 671 | } | 730 | } |
| 672 | | 731 | |
| | 732 | // Load or store pair of registers |
| | 733 | |
| | 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 | } |
| | 757 | }; |
| | 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); |
| | 761 | } |
| | 762 | |
| | 763 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| | 764 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); |
| | 765 | } |
| | 766 | |
| | 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); |
| | 769 | } |
| | 770 | |
| | 771 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| | 772 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, false); |
| | 773 | } |
| | 774 | |
| 673 | // Exception generation | 775 | // Exception generation |
| 674 | | 776 | |
| 675 | pub fn svc(imm16: u16) Instruction { | 777 | pub fn svc(imm16: u16) Instruction { |
| ... | @@ -787,15 +889,15 @@ test "serialize instructions" { | ... | @@ -787,15 +889,15 @@ test "serialize instructions" { |
| 787 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, | 889 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, |
| 788 | }, | 890 | }, |
| 789 | .{ // ldr x2, [x1, #1]! | 891 | .{ // ldr x2, [x1, #1]! |
| 790 | .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) }), |
| 791 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, | 893 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, |
| 792 | }, | 894 | }, |
| 793 | .{ // ldr x2, [x1], #-1 | 895 | .{ // ldr x2, [x1], #-1 |
| 794 | .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) }), |
| 795 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, | 897 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, |
| 796 | }, | 898 | }, |
| 797 | .{ // ldr x2, [x1], (x3) | 899 | .{ // ldr x2, [x1], (x3) |
| 798 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }), | 900 | .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 799 | .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, |
| 800 | }, | 902 | }, |
| 801 | .{ // ldr x2, label | 903 | .{ // ldr x2, label |
| ... | @@ -807,7 +909,7 @@ test "serialize instructions" { | ... | @@ -807,7 +909,7 @@ test "serialize instructions" { |
| 807 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, | 909 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, |
| 808 | }, | 910 | }, |
| 809 | .{ // str x2, [x1], (x3) | 911 | .{ // str x2, [x1], (x3) |
| 810 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.Offset.reg(.x3) }), | 912 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), |
| 811 | .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, |
| 812 | }, | 914 | }, |
| 813 | .{ // adr x2, #0x8 | 915 | .{ // adr x2, #0x8 |
| ... | @@ -826,6 +928,22 @@ test "serialize instructions" { | ... | @@ -826,6 +928,22 @@ test "serialize instructions" { |
| 826 | .inst = Instruction.adrp(.x2, -0x8), | 928 | .inst = Instruction.adrp(.x2, -0x8), |
| 827 | .expected = 0b1_00_10000_1111111111111111110_00010, | 929 | .expected = 0b1_00_10000_1111111111111111110_00010, |
| 828 | }, | 930 | }, |
| | 931 | .{ // stp x1, x2, [sp, #8] |
| | 932 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), |
| | 933 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, |
| | 934 | }, |
| | 935 | .{ // ldp x1, x2, [sp, #8] |
| | 936 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), |
| | 937 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, |
| | 938 | }, |
| | 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, |
| | 946 | }, |
| 829 | }; | 947 | }; |
| 830 | | 948 | |
| 831 | for (testcases) |case| { | 949 | for (testcases) |case| { |