| ... | @@ -113,6 +113,13 @@ test "Register.id" { | ... | @@ -113,6 +113,13 @@ test "Register.id" { |
| 113 | testing.expectEqual(@as(u4, 15), Register.pc.id()); | 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 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; | 123 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; |
| 117 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | 124 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 118 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | 125 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; |
| ... | @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { | ... | @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { |
| 135 | offset: u12, | 142 | offset: u12, |
| 136 | rd: u4, | 143 | rd: u4, |
| 137 | rn: u4, | 144 | rn: u4, |
| 138 | l: u1, | 145 | load_store: u1, |
| 139 | w: u1, | 146 | write_back: u1, |
| 140 | b: u1, | 147 | byte_word: u1, |
| 141 | u: u1, | 148 | up_down: u1, |
| 142 | p: u1, | 149 | pre_post: u1, |
| 143 | i: u1, | 150 | imm: u1, |
| 144 | fixed: u2 = 0b01, | 151 | fixed: u2 = 0b01, |
| 145 | cond: u4, | 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 | Branch: packed struct { | 165 | Branch: packed struct { |
| 148 | offset: u24, | 166 | offset: u24, |
| 149 | link: u1, | 167 | link: u1, |
| ... | @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { | ... | @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { |
| 235 | rs: u4, | 253 | rs: u4, |
| 236 | }, | 254 | }, |
| 237 | | 255 | |
| 238 | const Type = enum(u2) { | 256 | pub const Type = enum(u2) { |
| 239 | LogicalLeft, | 257 | logical_left, |
| 240 | LogicalRight, | 258 | logical_right, |
| 241 | ArithmeticRight, | 259 | arithmetic_right, |
| 242 | RotateRight, | 260 | rotate_right, |
| 243 | }; | 261 | }; |
| 244 | | 262 | |
| 245 | const none = Shift{ | 263 | pub const none = Shift{ |
| 246 | .Immediate = .{ | 264 | .Immediate = .{ |
| 247 | .amount = 0, | 265 | .amount = 0, |
| 248 | .typ = 0, | 266 | .typ = 0, |
| ... | @@ -338,10 +356,32 @@ pub const Instruction = union(enum) { | ... | @@ -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 | pub fn toU32(self: Instruction) u32 { | 380 | pub fn toU32(self: Instruction) u32 { |
| 342 | return switch (self) { | 381 | return switch (self) { |
| 343 | .DataProcessing => |v| @bitCast(u32, v), | 382 | .DataProcessing => |v| @bitCast(u32, v), |
| 344 | .SingleDataTransfer => |v| @bitCast(u32, v), | 383 | .SingleDataTransfer => |v| @bitCast(u32, v), |
| | 384 | .BlockDataTransfer => |v| @bitCast(u32, v), |
| 345 | .Branch => |v| @bitCast(u32, v), | 385 | .Branch => |v| @bitCast(u32, v), |
| 346 | .BranchExchange => |v| @bitCast(u32, v), | 386 | .BranchExchange => |v| @bitCast(u32, v), |
| 347 | .SupervisorCall => |v| @bitCast(u32, v), | 387 | .SupervisorCall => |v| @bitCast(u32, v), |
| ... | @@ -380,7 +420,7 @@ pub const Instruction = union(enum) { | ... | @@ -380,7 +420,7 @@ pub const Instruction = union(enum) { |
| 380 | pre_post: u1, | 420 | pre_post: u1, |
| 381 | up_down: u1, | 421 | up_down: u1, |
| 382 | byte_word: u1, | 422 | byte_word: u1, |
| 383 | writeback: u1, | 423 | write_back: u1, |
| 384 | load_store: u1, | 424 | load_store: u1, |
| 385 | ) Instruction { | 425 | ) Instruction { |
| 386 | return Instruction{ | 426 | return Instruction{ |
| ... | @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { | ... | @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { |
| 389 | .rn = rn.id(), | 429 | .rn = rn.id(), |
| 390 | .rd = rd.id(), | 430 | .rd = rd.id(), |
| 391 | .offset = offset.toU12(), | 431 | .offset = offset.toU12(), |
| 392 | .l = load_store, | 432 | .load_store = load_store, |
| 393 | .w = writeback, | 433 | .write_back = write_back, |
| 394 | .b = byte_word, | 434 | .byte_word = byte_word, |
| 395 | .u = up_down, | 435 | .up_down = up_down, |
| 396 | .p = pre_post, | 436 | .pre_post = pre_post, |
| 397 | .i = if (offset == .Immediate) 0 else 1, | 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,36 +506,68 @@ pub const Instruction = union(enum) { |
| 442 | | 506 | |
| 443 | // Data processing | 507 | // Data processing |
| 444 | | 508 | |
| 445 | pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 509 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 446 | return dataProcessing(cond, .@"and", s, rd, rn, op2); | 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 { | 541 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 450 | return dataProcessing(cond, .eor, s, rd, rn, op2); | 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 { | 545 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 454 | return dataProcessing(cond, .sub, s, rd, rn, op2); | 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 { | 549 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 458 | return dataProcessing(cond, .rsb, s, rd, rn, op2); | 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 { | 553 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 462 | return dataProcessing(cond, .add, s, rd, rn, op2); | 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 { | 557 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 466 | return dataProcessing(cond, .adc, s, rd, rn, op2); | 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 { | 561 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 470 | return dataProcessing(cond, .sbc, s, rd, rn, op2); | 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 { | 565 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 474 | return dataProcessing(cond, .rsc, s, rd, rn, op2); | 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 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | 573 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { |
| ... | @@ -490,20 +586,42 @@ pub const Instruction = union(enum) { | ... | @@ -490,20 +586,42 @@ pub const Instruction = union(enum) { |
| 490 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | 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 { | 589 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 494 | return dataProcessing(cond, .orr, s, rd, rn, op2); | 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 { | 605 | pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 498 | return dataProcessing(cond, .mov, s, rd, .r0, op2); | 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 { | 609 | pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 502 | return dataProcessing(cond, .bic, s, rd, rn, op2); | 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 { | 613 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 506 | return dataProcessing(cond, .mvn, s, rd, .r0, op2); | 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 | // Single data transfer | 627 | // Single data transfer |
| ... | @@ -512,10 +630,28 @@ pub const Instruction = union(enum) { | ... | @@ -512,10 +630,28 @@ pub const Instruction = union(enum) { |
| 512 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); | 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 | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | 637 | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 516 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); | 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 | // Branch | 655 | // Branch |
| 520 | | 656 | |
| 521 | pub fn b(cond: Condition, offset: i24) Instruction { | 657 | pub fn b(cond: Condition, offset: i24) Instruction { |
| ... | @@ -559,17 +695,21 @@ test "serialize instructions" { | ... | @@ -559,17 +695,21 @@ test "serialize instructions" { |
| 559 | | 695 | |
| 560 | const testcases = [_]Testcase{ | 696 | const testcases = [_]Testcase{ |
| 561 | .{ // add r0, r0, r0 | 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 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | 699 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, |
| 564 | }, | 700 | }, |
| 565 | .{ // mov r4, r2 | 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 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | 703 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, |
| 568 | }, | 704 | }, |
| 569 | .{ // mov r0, #42 | 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 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | 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 | .{ // ldr r0, [r2, #42] | 713 | .{ // ldr r0, [r2, #42] |
| 574 | .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), | 714 | .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), |
| 575 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | 715 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, |
| ... | @@ -598,6 +738,10 @@ test "serialize instructions" { | ... | @@ -598,6 +738,10 @@ test "serialize instructions" { |
| 598 | .inst = Instruction.bkpt(42), | 738 | .inst = Instruction.bkpt(42), |
| 599 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | 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 | for (testcases) |case| { | 747 | for (testcases) |case| { |