| ... | ... | @@ -113,6 +113,13 @@ test "Register.id" { |
| 113 | 113 | testing.expectEqual(@as(u4, 15), Register.pc.id()); |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | /// Program status registers containing flags, mode bits and other |
| 117 | /// vital information |
| 118 | pub const Psr = enum { |
| 119 | cpsr, |
| 120 | spsr, |
| 121 | }; |
| 122 | |
| 116 | 123 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; |
| 117 | 124 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 118 | 125 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; |
| ... | ... | @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { |
| 135 | 142 | offset: u12, |
| 136 | 143 | rd: u4, |
| 137 | 144 | rn: u4, |
| 138 | | l: u1, |
| 139 | | w: u1, |
| 140 | | b: u1, |
| 141 | | u: u1, |
| 142 | | p: u1, |
| 143 | | i: u1, |
| 145 | load_store: u1, |
| 146 | write_back: u1, |
| 147 | byte_word: u1, |
| 148 | up_down: u1, |
| 149 | pre_post: u1, |
| 150 | imm: u1, |
| 144 | 151 | fixed: u2 = 0b01, |
| 145 | 152 | cond: u4, |
| 146 | 153 | }, |
| 154 | BlockDataTransfer: packed struct { |
| 155 | register_list: u16, |
| 156 | rn: u4, |
| 157 | load_store: u1, |
| 158 | write_back: u1, |
| 159 | psr_or_user: u1, |
| 160 | up_down: u1, |
| 161 | pre_post: u1, |
| 162 | fixed: u3 = 0b100, |
| 163 | cond: u4, |
| 164 | }, |
| 147 | 165 | Branch: packed struct { |
| 148 | 166 | offset: u24, |
| 149 | 167 | link: u1, |
| ... | ... | @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { |
| 235 | 253 | rs: u4, |
| 236 | 254 | }, |
| 237 | 255 | |
| 238 | | const Type = enum(u2) { |
| 239 | | LogicalLeft, |
| 240 | | LogicalRight, |
| 241 | | ArithmeticRight, |
| 242 | | RotateRight, |
| 256 | pub const Type = enum(u2) { |
| 257 | logical_left, |
| 258 | logical_right, |
| 259 | arithmetic_right, |
| 260 | rotate_right, |
| 243 | 261 | }; |
| 244 | 262 | |
| 245 | | const none = Shift{ |
| 263 | pub const none = Shift{ |
| 246 | 264 | .Immediate = .{ |
| 247 | 265 | .amount = 0, |
| 248 | 266 | .typ = 0, |
| ... | ... | @@ -338,10 +356,32 @@ pub const Instruction = union(enum) { |
| 338 | 356 | } |
| 339 | 357 | }; |
| 340 | 358 | |
| 359 | /// Represents the register list operand to a block data transfer |
| 360 | /// instruction |
| 361 | pub const RegisterList = packed struct { |
| 362 | r0: bool = false, |
| 363 | r1: bool = false, |
| 364 | r2: bool = false, |
| 365 | r3: bool = false, |
| 366 | r4: bool = false, |
| 367 | r5: bool = false, |
| 368 | r6: bool = false, |
| 369 | r7: bool = false, |
| 370 | r8: bool = false, |
| 371 | r9: bool = false, |
| 372 | r10: bool = false, |
| 373 | r11: bool = false, |
| 374 | r12: bool = false, |
| 375 | r13: bool = false, |
| 376 | r14: bool = false, |
| 377 | r15: bool = false, |
| 378 | }; |
| 379 | |
| 341 | 380 | pub fn toU32(self: Instruction) u32 { |
| 342 | 381 | return switch (self) { |
| 343 | 382 | .DataProcessing => |v| @bitCast(u32, v), |
| 344 | 383 | .SingleDataTransfer => |v| @bitCast(u32, v), |
| 384 | .BlockDataTransfer => |v| @bitCast(u32, v), |
| 345 | 385 | .Branch => |v| @bitCast(u32, v), |
| 346 | 386 | .BranchExchange => |v| @bitCast(u32, v), |
| 347 | 387 | .SupervisorCall => |v| @bitCast(u32, v), |
| ... | ... | @@ -380,7 +420,7 @@ pub const Instruction = union(enum) { |
| 380 | 420 | pre_post: u1, |
| 381 | 421 | up_down: u1, |
| 382 | 422 | byte_word: u1, |
| 383 | | writeback: u1, |
| 423 | write_back: u1, |
| 384 | 424 | load_store: u1, |
| 385 | 425 | ) Instruction { |
| 386 | 426 | return Instruction{ |
| ... | ... | @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { |
| 389 | 429 | .rn = rn.id(), |
| 390 | 430 | .rd = rd.id(), |
| 391 | 431 | .offset = offset.toU12(), |
| 392 | | .l = load_store, |
| 393 | | .w = writeback, |
| 394 | | .b = byte_word, |
| 395 | | .u = up_down, |
| 396 | | .p = pre_post, |
| 397 | | .i = if (offset == .Immediate) 0 else 1, |
| 432 | .load_store = load_store, |
| 433 | .write_back = write_back, |
| 434 | .byte_word = byte_word, |
| 435 | .up_down = up_down, |
| 436 | .pre_post = pre_post, |
| 437 | .imm = if (offset == .Immediate) 0 else 1, |
| 438 | }, |
| 439 | }; |
| 440 | } |
| 441 | |
| 442 | fn blockDataTransfer( |
| 443 | cond: Condition, |
| 444 | rn: Register, |
| 445 | reg_list: RegisterList, |
| 446 | pre_post: u1, |
| 447 | up_down: u1, |
| 448 | psr_or_user: u1, |
| 449 | write_back: u1, |
| 450 | load_store: u1, |
| 451 | ) Instruction { |
| 452 | return Instruction{ |
| 453 | .BlockDataTransfer = .{ |
| 454 | .register_list = @bitCast(u16, reg_list), |
| 455 | .rn = rn.id(), |
| 456 | .load_store = load_store, |
| 457 | .write_back = write_back, |
| 458 | .psr_or_user = psr_or_user, |
| 459 | .up_down = up_down, |
| 460 | .pre_post = pre_post, |
| 461 | .cond = @enumToInt(cond), |
| 398 | 462 | }, |
| 399 | 463 | }; |
| 400 | 464 | } |
| ... | ... | @@ -442,36 +506,68 @@ pub const Instruction = union(enum) { |
| 442 | 506 | |
| 443 | 507 | // Data processing |
| 444 | 508 | |
| 445 | | pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 446 | | return dataProcessing(cond, .@"and", s, rd, rn, op2); |
| 509 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 510 | return dataProcessing(cond, .@"and", 0, rd, rn, op2); |
| 511 | } |
| 512 | |
| 513 | pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 514 | return dataProcessing(cond, .@"and", 1, rd, rn, op2); |
| 515 | } |
| 516 | |
| 517 | pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 518 | return dataProcessing(cond, .eor, 0, rd, rn, op2); |
| 519 | } |
| 520 | |
| 521 | pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 522 | return dataProcessing(cond, .eor, 1, rd, rn, op2); |
| 523 | } |
| 524 | |
| 525 | pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 526 | return dataProcessing(cond, .sub, 0, rd, rn, op2); |
| 527 | } |
| 528 | |
| 529 | pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 530 | return dataProcessing(cond, .sub, 1, rd, rn, op2); |
| 531 | } |
| 532 | |
| 533 | pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 534 | return dataProcessing(cond, .rsb, 0, rd, rn, op2); |
| 535 | } |
| 536 | |
| 537 | pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 538 | return dataProcessing(cond, .rsb, 1, rd, rn, op2); |
| 447 | 539 | } |
| 448 | 540 | |
| 449 | | pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 450 | | return dataProcessing(cond, .eor, s, rd, rn, op2); |
| 541 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 542 | return dataProcessing(cond, .add, 0, rd, rn, op2); |
| 451 | 543 | } |
| 452 | 544 | |
| 453 | | pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 454 | | return dataProcessing(cond, .sub, s, rd, rn, op2); |
| 545 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 546 | return dataProcessing(cond, .add, 1, rd, rn, op2); |
| 455 | 547 | } |
| 456 | 548 | |
| 457 | | pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 458 | | return dataProcessing(cond, .rsb, s, rd, rn, op2); |
| 549 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 550 | return dataProcessing(cond, .adc, 0, rd, rn, op2); |
| 459 | 551 | } |
| 460 | 552 | |
| 461 | | pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 462 | | return dataProcessing(cond, .add, s, rd, rn, op2); |
| 553 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 554 | return dataProcessing(cond, .adc, 1, rd, rn, op2); |
| 463 | 555 | } |
| 464 | 556 | |
| 465 | | pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 466 | | return dataProcessing(cond, .adc, s, rd, rn, op2); |
| 557 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 558 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); |
| 467 | 559 | } |
| 468 | 560 | |
| 469 | | pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 470 | | return dataProcessing(cond, .sbc, s, rd, rn, op2); |
| 561 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 562 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); |
| 471 | 563 | } |
| 472 | 564 | |
| 473 | | pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 474 | | return dataProcessing(cond, .rsc, s, rd, rn, op2); |
| 565 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 566 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); |
| 567 | } |
| 568 | |
| 569 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 570 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); |
| 475 | 571 | } |
| 476 | 572 | |
| 477 | 573 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { |
| ... | ... | @@ -490,20 +586,42 @@ pub const Instruction = union(enum) { |
| 490 | 586 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); |
| 491 | 587 | } |
| 492 | 588 | |
| 493 | | pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { |
| 494 | | return dataProcessing(cond, .orr, s, rd, rn, op2); |
| 589 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 590 | return dataProcessing(cond, .orr, 0, rd, rn, op2); |
| 591 | } |
| 592 | |
| 593 | pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 594 | return dataProcessing(cond, .orr, 1, rd, rn, op2); |
| 595 | } |
| 596 | |
| 597 | pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 598 | return dataProcessing(cond, .mov, 0, rd, .r0, op2); |
| 599 | } |
| 600 | |
| 601 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 602 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); |
| 495 | 603 | } |
| 496 | 604 | |
| 497 | | pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { |
| 498 | | return dataProcessing(cond, .mov, s, rd, .r0, op2); |
| 605 | pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 606 | return dataProcessing(cond, .bic, 0, rd, rn, op2); |
| 499 | 607 | } |
| 500 | 608 | |
| 501 | | pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { |
| 502 | | return dataProcessing(cond, .bic, s, rd, rn, op2); |
| 609 | pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 610 | return dataProcessing(cond, .bic, 1, rd, rn, op2); |
| 503 | 611 | } |
| 504 | 612 | |
| 505 | | pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { |
| 506 | | return dataProcessing(cond, .mvn, s, rd, .r0, op2); |
| 613 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 614 | return dataProcessing(cond, .mvn, 0, rd, .r0, op2); |
| 615 | } |
| 616 | |
| 617 | pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 618 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); |
| 619 | } |
| 620 | |
| 621 | // PSR transfer |
| 622 | |
| 623 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { |
| 624 | return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none)); |
| 507 | 625 | } |
| 508 | 626 | |
| 509 | 627 | // Single data transfer |
| ... | ... | @@ -512,10 +630,28 @@ pub const Instruction = union(enum) { |
| 512 | 630 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); |
| 513 | 631 | } |
| 514 | 632 | |
| 633 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 634 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 1); |
| 635 | } |
| 636 | |
| 515 | 637 | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 516 | 638 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); |
| 517 | 639 | } |
| 518 | 640 | |
| 641 | pub fn strb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 642 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 0); |
| 643 | } |
| 644 | |
| 645 | // Block data transfer |
| 646 | |
| 647 | pub fn ldm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction { |
| 648 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 1); |
| 649 | } |
| 650 | |
| 651 | pub fn stm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction { |
| 652 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 0); |
| 653 | } |
| 654 | |
| 519 | 655 | // Branch |
| 520 | 656 | |
| 521 | 657 | pub fn b(cond: Condition, offset: i24) Instruction { |
| ... | ... | @@ -559,17 +695,21 @@ test "serialize instructions" { |
| 559 | 695 | |
| 560 | 696 | const testcases = [_]Testcase{ |
| 561 | 697 | .{ // add r0, r0, r0 |
| 562 | | .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), |
| 698 | .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), |
| 563 | 699 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, |
| 564 | 700 | }, |
| 565 | 701 | .{ // mov r4, r2 |
| 566 | | .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), |
| 702 | .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), |
| 567 | 703 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, |
| 568 | 704 | }, |
| 569 | 705 | .{ // mov r0, #42 |
| 570 | | .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)), |
| 706 | .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), |
| 571 | 707 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, |
| 572 | 708 | }, |
| 709 | .{ // mrs r5, cpsr |
| 710 | .inst = Instruction.mrs(.al, .r5, .cpsr), |
| 711 | .expected = 0b1110_00010_0_001111_0101_000000000000, |
| 712 | }, |
| 573 | 713 | .{ // ldr r0, [r2, #42] |
| 574 | 714 | .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), |
| 575 | 715 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, |
| ... | ... | @@ -598,6 +738,10 @@ test "serialize instructions" { |
| 598 | 738 | .inst = Instruction.bkpt(42), |
| 599 | 739 | .expected = 0b1110_0001_0010_000000000010_0111_1010, |
| 600 | 740 | }, |
| 741 | .{ // stmfd r9, {r0} |
| 742 | .inst = Instruction.stm(.al, .r9, .{ .r0 = true }), |
| 743 | .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, |
| 744 | }, |
| 601 | 745 | }; |
| 602 | 746 | |
| 603 | 747 | for (testcases) |case| { |