From 96a3a45d9b86a525fa3e1dcf141bb50bfae2327d Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Mon, 17 Jul 2023 23:16:31 -0500 Subject: [PATCH] feat: add v5te arm and thumb namespaces also, drop the comptime parameter from arm and thumb namespaces --- src/arm.zig | 11 +- src/arm/v4t.zig | 413 +++++++++++++++++++++++------------------------ src/arm/v5te.zig | 227 ++++++++++++++++++++++++++ 3 files changed, 441 insertions(+), 210 deletions(-) create mode 100644 src/arm/v5te.zig diff --git a/src/arm.zig b/src/arm.zig index fafa4a9..0ce93b8 100644 --- a/src/arm.zig +++ b/src/arm.zig @@ -40,8 +40,15 @@ pub fn Arm32(comptime arch: Architecture) type { bank: Bank = Bank.create(), - const arm = @import("arm/v4t.zig").arm(Self); - const thumb = @import("arm/v4t.zig").thumb(Self); + const arm = switch (arch) { + .v4t => @import("arm/v4t.zig").arm, + .v5te => @import("arm/v5te.zig").arm, + }; + + const thumb = switch (arch) { + .v4t => @import("arm/v4t.zig").thumb, + .v5te => @import("arm/v5te.zig").thumb, + }; const Pipeline = struct { stage: [2]?u32, diff --git a/src/arm/v4t.zig b/src/arm/v4t.zig index 4899798..e131e83 100644 --- a/src/arm/v4t.zig +++ b/src/arm/v4t.zig @@ -1,230 +1,227 @@ const Bus = @import("../lib.zig").Bus; +const Arm7tdmi = @import("../arm.zig").Arm32(.v4t); -pub fn arm(comptime Arm32: type) type { - return struct { - pub const InstrFn = *const fn (*Arm32, Bus, u32) void; - pub const lut: [0x1000]InstrFn = populate(); +pub const arm = struct { + pub const InstrFn = *const fn (*Arm7tdmi, Bus, u32) void; + pub const lut: [0x1000]InstrFn = populate(); - const processing = @import("cpu/arm/data_processing.zig").dataProcessing; - const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer; - const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer; - const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer; - const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer; - const branch = @import("cpu/arm/branch.zig").branch; - const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange; - const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt; - const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap; + const processing = @import("cpu/arm/data_processing.zig").dataProcessing; + const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer; + const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer; + const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer; + const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer; + const branch = @import("cpu/arm/branch.zig").branch; + const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange; + const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt; + const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap; - const multiply = @import("cpu/arm/multiply.zig").multiply; - const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong; + const multiply = @import("cpu/arm/multiply.zig").multiply; + const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong; - /// Determine index into ARM InstrFn LUT - pub fn idx(opcode: u32) u12 { - // FIXME: omit these? - return @as(u12, @truncate(opcode >> 20 & 0xFF)) << 4 | @as(u12, @truncate(opcode >> 4 & 0xF)); - } + /// Determine index into ARM InstrFn LUT + pub fn idx(opcode: u32) u12 { + // FIXME: omit these? + return @as(u12, @truncate(opcode >> 20 & 0xFF)) << 4 | @as(u12, @truncate(opcode >> 4 & 0xF)); + } - // Undefined ARM Instruction handler - fn und(cpu: *Arm32, _: Bus, opcode: u32) void { - const id = idx(opcode); - cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode }); - } + // Undefined ARM Instruction handler + fn und(cpu: *Arm7tdmi, _: Bus, opcode: u32) void { + const id = idx(opcode); + cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode }); + } - fn populate() [0x1000]InstrFn { - return comptime comptime_blk: { - @setEvalBranchQuota(0xE000); - var table = [_]InstrFn{und} ** 0x1000; + fn populate() [0x1000]InstrFn { + return comptime comptime_blk: { + @setEvalBranchQuota(0xE000); + var table = [_]InstrFn{und} ** 0x1000; - for (&table, 0..) |*handler, i| { - handler.* = switch (@as(u2, i >> 10)) { - 0b00 => if (i == 0x121) blk: { - break :blk branchExchange(InstrFn); - } else if (i & 0xFCF == 0x009) blk: { - const A = i >> 5 & 1 == 1; - const S = i >> 4 & 1 == 1; - break :blk multiply(InstrFn, A, S); - } else if (i & 0xFBF == 0x109) blk: { - const B = i >> 6 & 1 == 1; - break :blk swap(InstrFn, B); - } else if (i & 0xF8F == 0x089) blk: { - const U = i >> 6 & 1 == 1; - const A = i >> 5 & 1 == 1; - const S = i >> 4 & 1 == 1; - break :blk multiplyLong(InstrFn, U, A, S); - } else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: { + for (&table, 0..) |*handler, i| { + handler.* = switch (@as(u2, i >> 10)) { + 0b00 => if (i == 0x121) blk: { + break :blk branchExchange(InstrFn); + } else if (i & 0xFCF == 0x009) blk: { + const A = i >> 5 & 1 == 1; + const S = i >> 4 & 1 == 1; + break :blk multiply(InstrFn, A, S); + } else if (i & 0xFBF == 0x109) blk: { + const B = i >> 6 & 1 == 1; + break :blk swap(InstrFn, B); + } else if (i & 0xF8F == 0x089) blk: { + const U = i >> 6 & 1 == 1; + const A = i >> 5 & 1 == 1; + const S = i >> 4 & 1 == 1; + break :blk multiplyLong(InstrFn, U, A, S); + } else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: { + const P = i >> 8 & 1 == 1; + const U = i >> 7 & 1 == 1; + const I = i >> 6 & 1 == 1; + const W = i >> 5 & 1 == 1; + const L = i >> 4 & 1 == 1; + break :blk halfSignedTransfer(InstrFn, P, U, I, W, L); + } else if (i & 0xD90 == 0x100) blk: { + const I = i >> 9 & 1 == 1; + const R = i >> 6 & 1 == 1; + const kind = i >> 4 & 0x3; + break :blk psrTransfer(InstrFn, I, R, kind); + } else blk: { + const I = i >> 9 & 1 == 1; + const S = i >> 4 & 1 == 1; + const instrKind = i >> 5 & 0xF; + break :blk processing(InstrFn, I, S, instrKind); + }, + 0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: { + const I = i >> 9 & 1 == 1; + const P = i >> 8 & 1 == 1; + const U = i >> 7 & 1 == 1; + const B = i >> 6 & 1 == 1; + const W = i >> 5 & 1 == 1; + const L = i >> 4 & 1 == 1; + break :blk transfer(InstrFn, I, P, U, B, W, L); + }, + else => switch (@as(u2, i >> 9 & 0x3)) { + // MSB is guaranteed to be 1 + 0b00 => blk: { const P = i >> 8 & 1 == 1; const U = i >> 7 & 1 == 1; - const I = i >> 6 & 1 == 1; + const S = i >> 6 & 1 == 1; const W = i >> 5 & 1 == 1; const L = i >> 4 & 1 == 1; - break :blk halfSignedTransfer(InstrFn, P, U, I, W, L); - } else if (i & 0xD90 == 0x100) blk: { - const I = i >> 9 & 1 == 1; - const R = i >> 6 & 1 == 1; - const kind = i >> 4 & 0x3; - break :blk psrTransfer(InstrFn, I, R, kind); + break :blk blockTransfer(InstrFn, P, U, S, W, L); + }, + 0b01 => blk: { + const L = i >> 8 & 1 == 1; + break :blk branch(InstrFn, L); + }, + 0b10 => und, // COP Data Transfer + 0b11 => if (i >> 8 & 1 == 1) swi(InstrFn) else und, // COP Data Operation + Register Transfer + }, + }; + } + + break :comptime_blk table; + }; + } +}; + +pub const thumb = struct { + pub const InstrFn = *const fn (*Arm7tdmi, Bus, u16) void; + pub const lut: [0x400]InstrFn = populate(); + + const processing = @import("cpu/thumb/data_processing.zig"); + const alu = @import("cpu/thumb/alu.zig").fmt4; + const transfer = @import("cpu/thumb/data_transfer.zig"); + const block_transfer = @import("cpu/thumb/block_data_transfer.zig"); + const swi = @import("cpu/thumb/software_interrupt.zig").fmt17; + const branch = @import("cpu/thumb/branch.zig"); + + /// Determine index into THUMB InstrFn LUT + pub fn idx(opcode: u16) u10 { + return @truncate(opcode >> 6); + } + + /// Undefined THUMB Instruction Handler + fn und(cpu: *Arm7tdmi, _: Bus, opcode: u16) void { + const id = idx(opcode); + cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode }); + } + + fn populate() [0x400]InstrFn { + return comptime comptime_blk: { + @setEvalBranchQuota(5025); // This is exact + var table = [_]InstrFn{und} ** 0x400; + + for (&table, 0..) |*handler, i| { + handler.* = switch (@as(u3, i >> 7 & 0x7)) { + 0b000 => if (i >> 5 & 0x3 == 0b11) blk: { + const I = i >> 4 & 1 == 1; + const is_sub = i >> 3 & 1 == 1; + const rn = i & 0x7; + break :blk processing.fmt2(InstrFn, I, is_sub, rn); + } else blk: { + const op = i >> 5 & 0x3; + const offset = i & 0x1F; + break :blk processing.fmt1(InstrFn, op, offset); + }, + 0b001 => blk: { + const op = i >> 5 & 0x3; + const rd = i >> 2 & 0x7; + break :blk processing.fmt3(InstrFn, op, rd); + }, + 0b010 => switch (@as(u2, i >> 5 & 0x3)) { + 0b00 => if (i >> 4 & 1 == 1) blk: { + const op = i >> 2 & 0x3; + const h1 = i >> 1 & 1; + const h2 = i & 1; + break :blk processing.fmt5(InstrFn, op, h1, h2); } else blk: { - const I = i >> 9 & 1 == 1; - const S = i >> 4 & 1 == 1; - const instrKind = i >> 5 & 0xF; - break :blk processing(InstrFn, I, S, instrKind); + const op = i & 0xF; + break :blk alu(InstrFn, op); }, - 0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: { - const I = i >> 9 & 1 == 1; - const P = i >> 8 & 1 == 1; - const U = i >> 7 & 1 == 1; - const B = i >> 6 & 1 == 1; - const W = i >> 5 & 1 == 1; - const L = i >> 4 & 1 == 1; - break :blk transfer(InstrFn, I, P, U, B, W, L); - }, - else => switch (@as(u2, i >> 9 & 0x3)) { - // MSB is guaranteed to be 1 - 0b00 => blk: { - const P = i >> 8 & 1 == 1; - const U = i >> 7 & 1 == 1; - const S = i >> 6 & 1 == 1; - const W = i >> 5 & 1 == 1; - const L = i >> 4 & 1 == 1; - break :blk blockTransfer(InstrFn, P, U, S, W, L); - }, - 0b01 => blk: { - const L = i >> 8 & 1 == 1; - break :blk branch(InstrFn, L); - }, - 0b10 => und, // COP Data Transfer - 0b11 => if (i >> 8 & 1 == 1) swi(InstrFn) else und, // COP Data Operation + Register Transfer - }, - }; - } - - break :comptime_blk table; - }; - } - }; -} - -pub fn thumb(comptime Arm32: type) type { - return struct { - pub const InstrFn = *const fn (*Arm32, Bus, u16) void; - pub const lut: [0x400]InstrFn = populate(); - - const processing = @import("cpu/thumb/data_processing.zig"); - const alu = @import("cpu/thumb/alu.zig").fmt4; - const transfer = @import("cpu/thumb/data_transfer.zig"); - const block_transfer = @import("cpu/thumb/block_data_transfer.zig"); - const swi = @import("cpu/thumb/software_interrupt.zig").fmt17; - const branch = @import("cpu/thumb/branch.zig"); - - /// Determine index into THUMB InstrFn LUT - pub fn idx(opcode: u16) u10 { - return @truncate(opcode >> 6); - } - - /// Undefined THUMB Instruction Handler - fn und(cpu: *Arm32, _: Bus, opcode: u16) void { - const id = idx(opcode); - cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode }); - } - - fn populate() [0x400]InstrFn { - return comptime comptime_blk: { - @setEvalBranchQuota(5025); // This is exact - var table = [_]InstrFn{und} ** 0x400; - - for (&table, 0..) |*handler, i| { - handler.* = switch (@as(u3, i >> 7 & 0x7)) { - 0b000 => if (i >> 5 & 0x3 == 0b11) blk: { - const I = i >> 4 & 1 == 1; - const is_sub = i >> 3 & 1 == 1; - const rn = i & 0x7; - break :blk processing.fmt2(InstrFn, I, is_sub, rn); - } else blk: { - const op = i >> 5 & 0x3; - const offset = i & 0x1F; - break :blk processing.fmt1(InstrFn, op, offset); - }, - 0b001 => blk: { - const op = i >> 5 & 0x3; + 0b01 => blk: { const rd = i >> 2 & 0x7; - break :blk processing.fmt3(InstrFn, op, rd); + break :blk transfer.fmt6(InstrFn, rd); }, - 0b010 => switch (@as(u2, i >> 5 & 0x3)) { - 0b00 => if (i >> 4 & 1 == 1) blk: { - const op = i >> 2 & 0x3; - const h1 = i >> 1 & 1; - const h2 = i & 1; - break :blk processing.fmt5(InstrFn, op, h1, h2); - } else blk: { - const op = i & 0xF; - break :blk alu(InstrFn, op); - }, - 0b01 => blk: { - const rd = i >> 2 & 0x7; - break :blk transfer.fmt6(InstrFn, rd); - }, - else => blk: { - const op = i >> 4 & 0x3; - const T = i >> 3 & 1 == 1; - break :blk transfer.fmt78(InstrFn, op, T); - }, + else => blk: { + const op = i >> 4 & 0x3; + const T = i >> 3 & 1 == 1; + break :blk transfer.fmt78(InstrFn, op, T); }, - 0b011 => blk: { - const B = i >> 6 & 1 == 1; + }, + 0b011 => blk: { + const B = i >> 6 & 1 == 1; + const L = i >> 5 & 1 == 1; + const offset = i & 0x1F; + break :blk transfer.fmt9(InstrFn, B, L, offset); + }, + else => switch (@as(u3, i >> 6 & 0x7)) { + // MSB is guaranteed to be 1 + 0b000 => blk: { const L = i >> 5 & 1 == 1; const offset = i & 0x1F; - break :blk transfer.fmt9(InstrFn, B, L, offset); + break :blk transfer.fmt10(InstrFn, L, offset); }, - else => switch (@as(u3, i >> 6 & 0x7)) { - // MSB is guaranteed to be 1 - 0b000 => blk: { - const L = i >> 5 & 1 == 1; - const offset = i & 0x1F; - break :blk transfer.fmt10(InstrFn, L, offset); - }, - 0b001 => blk: { - const L = i >> 5 & 1 == 1; - const rd = i >> 2 & 0x7; - break :blk transfer.fmt11(InstrFn, L, rd); - }, - 0b010 => blk: { - const isSP = i >> 5 & 1 == 1; - const rd = i >> 2 & 0x7; - break :blk processing.fmt12(InstrFn, isSP, rd); - }, - 0b011 => if (i >> 4 & 1 == 1) blk: { - const L = i >> 5 & 1 == 1; - const R = i >> 2 & 1 == 1; - break :blk block_transfer.fmt14(InstrFn, L, R); - } else blk: { - const S = i >> 1 & 1 == 1; - break :blk processing.fmt13(InstrFn, S); - }, - 0b100 => blk: { - const L = i >> 5 & 1 == 1; - const rb = i >> 2 & 0x7; - - break :blk block_transfer.fmt15(InstrFn, L, rb); - }, - 0b101 => if (i >> 2 & 0xF == 0b1111) blk: { - break :blk swi(InstrFn); - } else blk: { - const cond = i >> 2 & 0xF; - break :blk branch.fmt16(InstrFn, cond); - }, - 0b110 => branch.fmt18( - InstrFn, - ), - 0b111 => blk: { - const is_low = i >> 5 & 1 == 1; - break :blk branch.fmt19(InstrFn, is_low); - }, + 0b001 => blk: { + const L = i >> 5 & 1 == 1; + const rd = i >> 2 & 0x7; + break :blk transfer.fmt11(InstrFn, L, rd); }, - }; - } + 0b010 => blk: { + const isSP = i >> 5 & 1 == 1; + const rd = i >> 2 & 0x7; + break :blk processing.fmt12(InstrFn, isSP, rd); + }, + 0b011 => if (i >> 4 & 1 == 1) blk: { + const L = i >> 5 & 1 == 1; + const R = i >> 2 & 1 == 1; + break :blk block_transfer.fmt14(InstrFn, L, R); + } else blk: { + const S = i >> 1 & 1 == 1; + break :blk processing.fmt13(InstrFn, S); + }, + 0b100 => blk: { + const L = i >> 5 & 1 == 1; + const rb = i >> 2 & 0x7; - break :comptime_blk table; - }; - } - }; -} + break :blk block_transfer.fmt15(InstrFn, L, rb); + }, + 0b101 => if (i >> 2 & 0xF == 0b1111) blk: { + break :blk swi(InstrFn); + } else blk: { + const cond = i >> 2 & 0xF; + break :blk branch.fmt16(InstrFn, cond); + }, + 0b110 => branch.fmt18( + InstrFn, + ), + 0b111 => blk: { + const is_low = i >> 5 & 1 == 1; + break :blk branch.fmt19(InstrFn, is_low); + }, + }, + }; + } + + break :comptime_blk table; + }; + } +}; diff --git a/src/arm/v5te.zig b/src/arm/v5te.zig new file mode 100644 index 0000000..eee5431 --- /dev/null +++ b/src/arm/v5te.zig @@ -0,0 +1,227 @@ +const Bus = @import("../lib.zig").Bus; +const Arm946es = @import("../arm.zig").Arm32(.v5te); + +pub const arm = struct { + pub const InstrFn = *const fn (*Arm946es, Bus, u32) void; + pub const lut: [0x1000]InstrFn = populate(); + + const processing = @import("cpu/arm/data_processing.zig").dataProcessing; + const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer; + const transfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer; + const halfSignedTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer; + const blockTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer; + const branch = @import("cpu/arm/branch.zig").branch; + const branchExchange = @import("cpu/arm/branch.zig").branchAndExchange; + const swi = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt; + const swap = @import("cpu/arm/single_data_swap.zig").singleDataSwap; + + const multiply = @import("cpu/arm/multiply.zig").multiply; + const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong; + + /// Determine index into ARM InstrFn LUT + pub fn idx(opcode: u32) u12 { + // FIXME: omit these? + return @as(u12, @truncate(opcode >> 20 & 0xFF)) << 4 | @as(u12, @truncate(opcode >> 4 & 0xF)); + } + + // Undefined ARM Instruction handler + fn und(cpu: *Arm946es, _: Bus, opcode: u32) void { + const id = idx(opcode); + cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode }); + } + + fn populate() [0x1000]InstrFn { + return comptime comptime_blk: { + @setEvalBranchQuota(0xE000); + var table = [_]InstrFn{und} ** 0x1000; + + for (&table, 0..) |*handler, i| { + handler.* = switch (@as(u2, i >> 10)) { + 0b00 => if (i == 0x121) blk: { + break :blk branchExchange(InstrFn); + } else if (i & 0xFCF == 0x009) blk: { + const A = i >> 5 & 1 == 1; + const S = i >> 4 & 1 == 1; + break :blk multiply(InstrFn, A, S); + } else if (i & 0xFBF == 0x109) blk: { + const B = i >> 6 & 1 == 1; + break :blk swap(InstrFn, B); + } else if (i & 0xF8F == 0x089) blk: { + const U = i >> 6 & 1 == 1; + const A = i >> 5 & 1 == 1; + const S = i >> 4 & 1 == 1; + break :blk multiplyLong(InstrFn, U, A, S); + } else if (i & 0xE49 == 0x009 or i & 0xE49 == 0x049) blk: { + const P = i >> 8 & 1 == 1; + const U = i >> 7 & 1 == 1; + const I = i >> 6 & 1 == 1; + const W = i >> 5 & 1 == 1; + const L = i >> 4 & 1 == 1; + break :blk halfSignedTransfer(InstrFn, P, U, I, W, L); + } else if (i & 0xD90 == 0x100) blk: { + const I = i >> 9 & 1 == 1; + const R = i >> 6 & 1 == 1; + const kind = i >> 4 & 0x3; + break :blk psrTransfer(InstrFn, I, R, kind); + } else blk: { + const I = i >> 9 & 1 == 1; + const S = i >> 4 & 1 == 1; + const instrKind = i >> 5 & 0xF; + break :blk processing(InstrFn, I, S, instrKind); + }, + 0b01 => if (i >> 9 & 1 == 1 and i & 1 == 1) und else blk: { + const I = i >> 9 & 1 == 1; + const P = i >> 8 & 1 == 1; + const U = i >> 7 & 1 == 1; + const B = i >> 6 & 1 == 1; + const W = i >> 5 & 1 == 1; + const L = i >> 4 & 1 == 1; + break :blk transfer(InstrFn, I, P, U, B, W, L); + }, + else => switch (@as(u2, i >> 9 & 0x3)) { + // MSB is guaranteed to be 1 + 0b00 => blk: { + const P = i >> 8 & 1 == 1; + const U = i >> 7 & 1 == 1; + const S = i >> 6 & 1 == 1; + const W = i >> 5 & 1 == 1; + const L = i >> 4 & 1 == 1; + break :blk blockTransfer(InstrFn, P, U, S, W, L); + }, + 0b01 => blk: { + const L = i >> 8 & 1 == 1; + break :blk branch(InstrFn, L); + }, + 0b10 => und, // COP Data Transfer + 0b11 => if (i >> 8 & 1 == 1) swi(InstrFn) else und, // COP Data Operation + Register Transfer + }, + }; + } + + break :comptime_blk table; + }; + } +}; + +pub const thumb = struct { + pub const InstrFn = *const fn (*Arm946es, Bus, u16) void; + pub const lut: [0x400]InstrFn = populate(); + + const processing = @import("cpu/thumb/data_processing.zig"); + const alu = @import("cpu/thumb/alu.zig").fmt4; + const transfer = @import("cpu/thumb/data_transfer.zig"); + const block_transfer = @import("cpu/thumb/block_data_transfer.zig"); + const swi = @import("cpu/thumb/software_interrupt.zig").fmt17; + const branch = @import("cpu/thumb/branch.zig"); + + /// Determine index into THUMB InstrFn LUT + pub fn idx(opcode: u16) u10 { + return @truncate(opcode >> 6); + } + + /// Undefined THUMB Instruction Handler + fn und(cpu: *Arm946es, _: Bus, opcode: u16) void { + const id = idx(opcode); + cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode }); + } + + fn populate() [0x400]InstrFn { + return comptime comptime_blk: { + @setEvalBranchQuota(5025); // This is exact + var table = [_]InstrFn{und} ** 0x400; + + for (&table, 0..) |*handler, i| { + handler.* = switch (@as(u3, i >> 7 & 0x7)) { + 0b000 => if (i >> 5 & 0x3 == 0b11) blk: { + const I = i >> 4 & 1 == 1; + const is_sub = i >> 3 & 1 == 1; + const rn = i & 0x7; + break :blk processing.fmt2(InstrFn, I, is_sub, rn); + } else blk: { + const op = i >> 5 & 0x3; + const offset = i & 0x1F; + break :blk processing.fmt1(InstrFn, op, offset); + }, + 0b001 => blk: { + const op = i >> 5 & 0x3; + const rd = i >> 2 & 0x7; + break :blk processing.fmt3(InstrFn, op, rd); + }, + 0b010 => switch (@as(u2, i >> 5 & 0x3)) { + 0b00 => if (i >> 4 & 1 == 1) blk: { + const op = i >> 2 & 0x3; + const h1 = i >> 1 & 1; + const h2 = i & 1; + break :blk processing.fmt5(InstrFn, op, h1, h2); + } else blk: { + const op = i & 0xF; + break :blk alu(InstrFn, op); + }, + 0b01 => blk: { + const rd = i >> 2 & 0x7; + break :blk transfer.fmt6(InstrFn, rd); + }, + else => blk: { + const op = i >> 4 & 0x3; + const T = i >> 3 & 1 == 1; + break :blk transfer.fmt78(InstrFn, op, T); + }, + }, + 0b011 => blk: { + const B = i >> 6 & 1 == 1; + const L = i >> 5 & 1 == 1; + const offset = i & 0x1F; + break :blk transfer.fmt9(InstrFn, B, L, offset); + }, + else => switch (@as(u3, i >> 6 & 0x7)) { + // MSB is guaranteed to be 1 + 0b000 => blk: { + const L = i >> 5 & 1 == 1; + const offset = i & 0x1F; + break :blk transfer.fmt10(InstrFn, L, offset); + }, + 0b001 => blk: { + const L = i >> 5 & 1 == 1; + const rd = i >> 2 & 0x7; + break :blk transfer.fmt11(InstrFn, L, rd); + }, + 0b010 => blk: { + const isSP = i >> 5 & 1 == 1; + const rd = i >> 2 & 0x7; + break :blk processing.fmt12(InstrFn, isSP, rd); + }, + 0b011 => if (i >> 4 & 1 == 1) blk: { + const L = i >> 5 & 1 == 1; + const R = i >> 2 & 1 == 1; + break :blk block_transfer.fmt14(InstrFn, L, R); + } else blk: { + const S = i >> 1 & 1 == 1; + break :blk processing.fmt13(InstrFn, S); + }, + 0b100 => blk: { + const L = i >> 5 & 1 == 1; + const rb = i >> 2 & 0x7; + + break :blk block_transfer.fmt15(InstrFn, L, rb); + }, + 0b101 => if (i >> 2 & 0xF == 0b1111) blk: { + break :blk swi(InstrFn); + } else blk: { + const cond = i >> 2 & 0xF; + break :blk branch.fmt16(InstrFn, cond); + }, + 0b110 => branch.fmt18( + InstrFn, + ), + 0b111 => blk: { + const is_low = i >> 5 & 1 == 1; + break :blk branch.fmt19(InstrFn, is_low); + }, + }, + }; + } + + break :comptime_blk table; + }; + } +};