From 954fb279ad8b7e16aa26f39bf09ba2e409101b41 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 25 Jun 2023 18:56:37 -0500 Subject: [PATCH] chore: move cpu implementation to it's own module There's a decent amount of Hacks and TODO:s that need revisiting I should spend a bit of time cleaning up code :pensive: --- build.zig | 1 + build.zig.zon | 4 + src/core/Bus.zig | 2 +- src/core/apu.zig | 9 +- src/core/bus/GamePak.zig | 2 +- src/core/bus/dma.zig | 15 +- src/core/bus/gpio.zig | 18 +- src/core/bus/timer.zig | 19 +- src/core/cpu.zig | 681 ------------------ src/core/cpu/arm/block_data_transfer.zig | 111 --- src/core/cpu/arm/branch.zig | 26 - src/core/cpu/arm/data_processing.zig | 185 ----- .../cpu/arm/half_signed_data_transfer.zig | 53 -- src/core/cpu/arm/multiply.zig | 57 -- src/core/cpu/arm/psr_transfer.zig | 59 -- src/core/cpu/arm/single_data_swap.zig | 29 - src/core/cpu/arm/single_data_transfer.zig | 55 -- src/core/cpu/arm/software_interrupt.zig | 23 - src/core/cpu/barrel_shifter.zig | 147 ---- src/core/cpu/thumb/alu.zig | 108 --- src/core/cpu/thumb/block_data_transfer.zig | 100 --- src/core/cpu/thumb/branch.zig | 54 -- src/core/cpu/thumb/data_processing.zig | 199 ----- src/core/cpu/thumb/data_transfer.zig | 145 ---- src/core/cpu/thumb/software_interrupt.zig | 23 - src/core/cpu_util.zig | 75 ++ src/core/emu.zig | 32 +- src/core/ppu.zig | 25 +- src/core/scheduler.zig | 29 +- src/imgui.zig | 23 +- src/main.zig | 13 +- src/platform.zig | 13 +- src/util.zig | 2 +- 33 files changed, 208 insertions(+), 2129 deletions(-) delete mode 100644 src/core/cpu.zig delete mode 100644 src/core/cpu/arm/block_data_transfer.zig delete mode 100644 src/core/cpu/arm/branch.zig delete mode 100644 src/core/cpu/arm/data_processing.zig delete mode 100644 src/core/cpu/arm/half_signed_data_transfer.zig delete mode 100644 src/core/cpu/arm/multiply.zig delete mode 100644 src/core/cpu/arm/psr_transfer.zig delete mode 100644 src/core/cpu/arm/single_data_swap.zig delete mode 100644 src/core/cpu/arm/single_data_transfer.zig delete mode 100644 src/core/cpu/arm/software_interrupt.zig delete mode 100644 src/core/cpu/barrel_shifter.zig delete mode 100644 src/core/cpu/thumb/alu.zig delete mode 100644 src/core/cpu/thumb/block_data_transfer.zig delete mode 100644 src/core/cpu/thumb/branch.zig delete mode 100644 src/core/cpu/thumb/data_processing.zig delete mode 100644 src/core/cpu/thumb/data_transfer.zig delete mode 100644 src/core/cpu/thumb/software_interrupt.zig create mode 100644 src/core/cpu_util.zig diff --git a/build.zig b/build.zig index ad7fcea..65aa6a5 100644 --- a/build.zig +++ b/build.zig @@ -30,6 +30,7 @@ pub fn build(b: *std.Build) void { exe.addModule("gdbstub", b.dependency("zba-gdbstub", .{}).module("gdbstub")); // https://git.musuka.dev/paoda/zba-gdbstub exe.addModule("zba-util", b.dependency("zba-util", .{}).module("zba-util")); // https://git.musuka.dev/paoda/zba-util exe.addModule("tomlz", b.dependency("tomlz", .{}).module("tomlz")); // https://github.com/mattyhall/tomlz + exe.addModule("arm32", b.dependency("arm32", .{}).module("arm32")); // https://git.musuka.dev/paoda/arm32 // https://github.com/fabioarnold/nfd-zig const nfd_dep = b.dependency("nfd", .{ .target = target, .optimize = optimize }); diff --git a/build.zig.zon b/build.zig.zon index 1f2cec1..8522309 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -30,5 +30,9 @@ .url = "https://github.com/mattyhall/tomlz/archive/4928d38e9bb682a9966ffe7f41230435d0111b1e.tar.gz", .hash = "12202b57d7b46fff8d16a17371c4f9b711a56b866f0cd11844e4243c09343a2c4c6d", }, + .arm32 = .{ + .url = "https://git.musuka.dev/paoda/arm32/archive/3c8a87c14dfa2501bd0a7f2236259e2d8d0fbcd9.tar.gz", + .hash = "12202a06a5d20d0da9ab6596a04821431d261c9900c7916a87148619dcb77c745044", + }, }, } diff --git a/src/core/Bus.zig b/src/core/Bus.zig index 055e6cb..d281a20 100644 --- a/src/core/Bus.zig +++ b/src/core/Bus.zig @@ -1,6 +1,6 @@ const std = @import("std"); -const Arm7tdmi = @import("cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; const Bios = @import("bus/Bios.zig"); const Ewram = @import("bus/Ewram.zig"); const GamePak = @import("bus/GamePak.zig"); diff --git a/src/core/apu.zig b/src/core/apu.zig index a93ad0b..cd96d34 100644 --- a/src/core/apu.zig +++ b/src/core/apu.zig @@ -3,7 +3,8 @@ const SDL = @import("sdl2"); const io = @import("bus/io.zig"); const util = @import("../util.zig"); -const Arm7tdmi = @import("cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("Bus.zig"); const Scheduler = @import("scheduler.zig").Scheduler; const ToneSweep = @import("apu/ToneSweep.zig"); const Tone = @import("apu/Tone.zig"); @@ -520,18 +521,20 @@ pub const Apu = struct { pub fn onDmaAudioSampleRequest(self: *Self, cpu: *Arm7tdmi, tim_id: u3) void { if (!self.cnt.apu_enable.read()) return; + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + if (@boolToInt(self.dma_cnt.chA_timer.read()) == tim_id) { if (!self.chA.enabled) return; self.chA.updateSample(); - if (self.chA.len() <= 15) cpu.bus.dma[1].requestAudio(0x0400_00A0); + if (self.chA.len() <= 15) bus_ptr.dma[1].requestAudio(0x0400_00A0); } if (@boolToInt(self.dma_cnt.chB_timer.read()) == tim_id) { if (!self.chB.enabled) return; self.chB.updateSample(); - if (self.chB.len() <= 15) cpu.bus.dma[2].requestAudio(0x0400_00A4); + if (self.chB.len() <= 15) bus_ptr.dma[2].requestAudio(0x0400_00A4); } } }; diff --git a/src/core/bus/GamePak.zig b/src/core/bus/GamePak.zig index d2203a1..8489609 100644 --- a/src/core/bus/GamePak.zig +++ b/src/core/bus/GamePak.zig @@ -1,7 +1,7 @@ const std = @import("std"); const config = @import("../../config.zig"); -const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; const Backup = @import("backup.zig").Backup; const Gpio = @import("gpio.zig").Gpio; const Allocator = std.mem.Allocator; diff --git a/src/core/bus/dma.zig b/src/core/bus/dma.zig index 9bc3274..47d32c1 100644 --- a/src/core/bus/dma.zig +++ b/src/core/bus/dma.zig @@ -3,7 +3,7 @@ const util = @import("../../util.zig"); const DmaControl = @import("io.zig").DmaControl; const Bus = @import("../Bus.zig"); -const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; pub const DmaTuple = struct { DmaController(0), DmaController(1), DmaController(2), DmaController(3) }; const log = std.log.scoped(.DmaTransfer); @@ -11,6 +11,7 @@ const log = std.log.scoped(.DmaTransfer); const getHalf = util.getHalf; const setHalf = util.setHalf; const setQuart = util.setQuart; +const handleInterrupt = @import("../cpu_util.zig").handleInterrupt; const rotr = @import("zba-util").rotr; @@ -237,6 +238,8 @@ fn DmaController(comptime id: u2) type { } pub fn step(self: *Self, cpu: *Arm7tdmi) void { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + const is_fifo = (id == 1 or id == 2) and self.cnt.start_timing.read() == 0b11; const sad_adj = @intToEnum(Adjustment, self.cnt.sad_adj.read()); const dad_adj = if (is_fifo) .Fixed else @intToEnum(Adjustment, self.cnt.dad_adj.read()); @@ -283,13 +286,13 @@ fn DmaController(comptime id: u2) type { if (self._word_count == 0) { if (self.cnt.irq.read()) { switch (id) { - 0 => cpu.bus.io.irq.dma0.set(), - 1 => cpu.bus.io.irq.dma1.set(), - 2 => cpu.bus.io.irq.dma2.set(), - 3 => cpu.bus.io.irq.dma3.set(), + 0 => bus_ptr.io.irq.dma0.set(), + 1 => bus_ptr.io.irq.dma1.set(), + 2 => bus_ptr.io.irq.dma2.set(), + 3 => bus_ptr.io.irq.dma3.set(), } - cpu.handleInterrupt(); + handleInterrupt(cpu); } // If we're not repeating, Fire the IRQs and disable the DMA diff --git a/src/core/bus/gpio.zig b/src/core/bus/gpio.zig index 93716b5..074b733 100644 --- a/src/core/bus/gpio.zig +++ b/src/core/bus/gpio.zig @@ -2,9 +2,13 @@ const std = @import("std"); const Bit = @import("bitfield").Bit; const DateTime = @import("datetime").datetime.Datetime; -const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("../Bus.zig"); +const Scheduler = @import("../scheduler.zig").Scheduler; const Allocator = std.mem.Allocator; +const handleInterrupt = @import("../cpu_util.zig").handleInterrupt; + /// GPIO Register Implementation pub const Gpio = struct { const Self = @This(); @@ -286,11 +290,13 @@ pub const Clock = struct { .gpio = gpio, // Can't use Arm7tdmi ptr b/c not initialized yet }; - cpu.sched.push(.RealTimeClock, 1 << 24); // Every Second + const sched_ptr = @ptrCast(*Scheduler, @alignCast(@alignOf(Scheduler), cpu.sched.ptr)); + sched_ptr.push(.RealTimeClock, 1 << 24); // Every Second } pub fn onClockUpdate(self: *Self, late: u64) void { - self.cpu.sched.push(.RealTimeClock, (1 << 24) -| late); // Reschedule + const sched_ptr = @ptrCast(*Scheduler, @alignCast(@alignOf(Scheduler), self.cpu.sched.ptr)); + sched_ptr.push(.RealTimeClock, (1 << 24) -| late); // Reschedule const now = DateTime.now(); self.year = bcd(@intCast(u8, now.date.year - 2000)); @@ -397,11 +403,13 @@ pub const Clock = struct { } fn irq(self: *Self) void { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), self.cpu.bus.ptr)); + // TODO: Confirm that this is the right behaviour log.debug("Force GamePak IRQ", .{}); - self.cpu.bus.io.irq.game_pak.set(); - self.cpu.handleInterrupt(); + bus_ptr.io.irq.game_pak.set(); + handleInterrupt(self.cpu); } fn processCommand(self: *Self, raw_command: u8) State { diff --git a/src/core/bus/timer.zig b/src/core/bus/timer.zig index 93ff7c4..d2bfde3 100644 --- a/src/core/bus/timer.zig +++ b/src/core/bus/timer.zig @@ -3,7 +3,10 @@ const util = @import("../../util.zig"); const TimerControl = @import("io.zig").TimerControl; const Scheduler = @import("../scheduler.zig").Scheduler; -const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("../Bus.zig"); + +const handleInterrupt = @import("../cpu_util.zig").handleInterrupt; pub const TimerTuple = struct { Timer(0), Timer(1), Timer(2), Timer(3) }; const log = std.log.scoped(.Timer); @@ -191,7 +194,9 @@ fn Timer(comptime id: u2) type { pub fn onTimerExpire(self: *Self, cpu: *Arm7tdmi, late: u64) void { // Fire IRQ if enabled - const io = &cpu.bus.io; + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + + const io = &bus_ptr.io; if (self.cnt.irq.read()) { switch (id) { @@ -201,12 +206,12 @@ fn Timer(comptime id: u2) type { 3 => io.irq.tim3.set(), } - cpu.handleInterrupt(); + handleInterrupt(cpu); } // DMA Sound Things if (id == 0 or id == 1) { - cpu.bus.apu.onDmaAudioSampleRequest(cpu, id); + bus_ptr.apu.onDmaAudioSampleRequest(cpu, id); } // Perform Cascade Behaviour @@ -214,9 +219,9 @@ fn Timer(comptime id: u2) type { inline 0, 1, 2 => |idx| { const next = idx + 1; - if (cpu.bus.tim[next].cnt.cascade.read()) { - cpu.bus.tim[next]._counter +%= 1; - if (cpu.bus.tim[next]._counter == 0) cpu.bus.tim[next].onTimerExpire(cpu, late); + if (bus_ptr.tim[next].cnt.cascade.read()) { + bus_ptr.tim[next]._counter +%= 1; + if (bus_ptr.tim[next]._counter == 0) bus_ptr.tim[next].onTimerExpire(cpu, late); } }, 3 => {}, // THere is no timer for TIM3 to cascade to diff --git a/src/core/cpu.zig b/src/core/cpu.zig deleted file mode 100644 index de13e08..0000000 --- a/src/core/cpu.zig +++ /dev/null @@ -1,681 +0,0 @@ -const std = @import("std"); - -const Bus = @import("Bus.zig"); -const Bit = @import("bitfield").Bit; -const Bitfield = @import("bitfield").Bitfield; -const Scheduler = @import("scheduler.zig").Scheduler; -const Logger = @import("../util.zig").Logger; - -const File = std.fs.File; -const log = std.log.scoped(.Arm7Tdmi); - -// ARM Instructions -pub const arm = struct { - pub const InstrFn = *const fn (*Arm7tdmi, *Bus, u32) void; - 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 - fn idx(opcode: u32) u12 { - return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 4 & 0xF); - } - - // 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; - - for (&table, 0..) |*handler, i| { - handler.* = switch (@as(u2, i >> 10)) { - 0b00 => if (i == 0x121) blk: { - break :blk branchExchange; - } else if (i & 0xFCF == 0x009) blk: { - const A = i >> 5 & 1 == 1; - const S = i >> 4 & 1 == 1; - break :blk multiply(A, S); - } else if (i & 0xFBF == 0x109) blk: { - const B = i >> 6 & 1 == 1; - break :blk swap(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(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(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(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(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(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(P, U, S, W, L); - }, - 0b01 => blk: { - const L = i >> 8 & 1 == 1; - break :blk branch(L); - }, - 0b10 => und, // COP Data Transfer - 0b11 => if (i >> 8 & 1 == 1) swi() else und, // COP Data Operation + Register Transfer - }, - }; - } - - break :comptime_blk table; - }; - } -}; - -// THUMB Instructions -pub const thumb = struct { - pub const InstrFn = *const fn (*Arm7tdmi, *Bus, u16) void; - 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 - fn idx(opcode: u16) u10 { - return @truncate(u10, 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(I, is_sub, rn); - } else blk: { - const op = i >> 5 & 0x3; - const offset = i & 0x1F; - break :blk processing.fmt1(op, offset); - }, - 0b001 => blk: { - const op = i >> 5 & 0x3; - const rd = i >> 2 & 0x7; - break :blk processing.fmt3(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(op, h1, h2); - } else blk: { - const op = i & 0xF; - break :blk alu(op); - }, - 0b01 => blk: { - const rd = i >> 2 & 0x7; - break :blk transfer.fmt6(rd); - }, - else => blk: { - const op = i >> 4 & 0x3; - const T = i >> 3 & 1 == 1; - break :blk transfer.fmt78(op, T); - }, - }, - 0b011 => blk: { - const B = i >> 6 & 1 == 1; - const L = i >> 5 & 1 == 1; - const offset = i & 0x1F; - break :blk transfer.fmt9(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(L, offset); - }, - 0b001 => blk: { - const L = i >> 5 & 1 == 1; - const rd = i >> 2 & 0x7; - break :blk transfer.fmt11(L, rd); - }, - 0b010 => blk: { - const isSP = i >> 5 & 1 == 1; - const rd = i >> 2 & 0x7; - break :blk processing.fmt12(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(L, R); - } else blk: { - const S = i >> 1 & 1 == 1; - break :blk processing.fmt13(S); - }, - 0b100 => blk: { - const L = i >> 5 & 1 == 1; - const rb = i >> 2 & 0x7; - - break :blk block_transfer.fmt15(L, rb); - }, - 0b101 => if (i >> 2 & 0xF == 0b1111) blk: { - break :blk thumb.swi(); - } else blk: { - const cond = i >> 2 & 0xF; - break :blk branch.fmt16(cond); - }, - 0b110 => branch.fmt18(), - 0b111 => blk: { - const is_low = i >> 5 & 1 == 1; - break :blk branch.fmt19(is_low); - }, - }, - }; - } - - break :comptime_blk table; - }; - } -}; - -pub const Arm7tdmi = struct { - const Self = @This(); - - r: [16]u32, - pipe: Pipeline, - sched: *Scheduler, - bus: *Bus, - cpsr: PSR, - spsr: PSR, - - bank: Bank, - - logger: ?Logger, - - /// Bank of Registers from other CPU Modes - const Bank = struct { - /// Storage for r13_, r14_ - /// e.g. [r13, r14, r13_svc, r14_svc] - r: [2 * 6]u32, - - /// Storage for R8_fiq -> R12_fiq and their normal counterparts - /// e.g [r[0 + 8], fiq_r[0 + 8], r[1 + 8], fiq_r[1 + 8]...] - fiq: [2 * 5]u32, - - spsr: [5]PSR, - - const Kind = enum(u1) { - R13 = 0, - R14, - }; - - pub fn create() Bank { - return .{ - .r = [_]u32{0x00} ** 12, - .fiq = [_]u32{0x00} ** 10, - .spsr = [_]PSR{.{ .raw = 0x0000_0000 }} ** 5, - }; - } - - inline fn regIdx(mode: Mode, kind: Kind) usize { - const idx: usize = switch (mode) { - .User, .System => 0, - .Supervisor => 1, - .Abort => 2, - .Undefined => 3, - .Irq => 4, - .Fiq => 5, - }; - - return (idx * 2) + if (kind == .R14) @as(usize, 1) else 0; - } - - inline fn spsrIdx(mode: Mode) usize { - return switch (mode) { - .Supervisor => 0, - .Abort => 1, - .Undefined => 2, - .Irq => 3, - .Fiq => 4, - else => std.debug.panic("[CPU/Mode] {} does not have a SPSR Register", .{mode}), - }; - } - - inline fn fiqIdx(i: usize, mode: Mode) usize { - return (i * 2) + if (mode == .Fiq) @as(usize, 1) else 0; - } - }; - - pub fn init(sched: *Scheduler, bus: *Bus, log_file: ?std.fs.File) Self { - return Self{ - .r = [_]u32{0x00} ** 16, - .pipe = Pipeline.init(), - .sched = sched, - .bus = bus, - .cpsr = .{ .raw = 0x0000_001F }, - .spsr = .{ .raw = 0x0000_0000 }, - .bank = Bank.create(), - .logger = if (log_file) |file| Logger.init(file) else null, - }; - } - - // FIXME: Resetting disables logging (if enabled) - pub fn reset(self: *Self) void { - const bus_ptr = self.bus; - const scheduler_ptr = self.sched; - - self.* = Self.init(scheduler_ptr, bus_ptr, null); - } - - pub inline fn hasSPSR(self: *const Self) bool { - const mode = getModeChecked(self, self.cpsr.mode.read()); - return switch (mode) { - .System, .User => false, - else => true, - }; - } - - pub inline fn isPrivileged(self: *const Self) bool { - const mode = getModeChecked(self, self.cpsr.mode.read()); - return switch (mode) { - .User => false, - else => true, - }; - } - - pub inline fn isHalted(self: *const Self) bool { - return self.bus.io.haltcnt == .Halt; - } - - pub fn setCpsr(self: *Self, value: u32) void { - if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F)); - self.cpsr.raw = value; - } - - fn changeModeFromIdx(self: *Self, next: u5) void { - self.changeMode(getModeChecked(self, next)); - } - - pub fn setUserModeRegister(self: *Self, idx: usize, value: u32) void { - const current = getModeChecked(self, self.cpsr.mode.read()); - - switch (idx) { - 8...12 => { - if (current == .Fiq) { - self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] = value; - } else self.r[idx] = value; - }, - 13, 14 => switch (current) { - .User, .System => self.r[idx] = value, - else => { - const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable; - self.bank.r[Bank.regIdx(.User, kind)] = value; - }, - }, - else => self.r[idx] = value, // R0 -> R7 and R15 - } - } - - pub fn getUserModeRegister(self: *Self, idx: usize) u32 { - const current = getModeChecked(self, self.cpsr.mode.read()); - - return switch (idx) { - 8...12 => if (current == .Fiq) self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] else self.r[idx], - 13, 14 => switch (current) { - .User, .System => self.r[idx], - else => blk: { - const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable; - break :blk self.bank.r[Bank.regIdx(.User, kind)]; - }, - }, - else => self.r[idx], // R0 -> R7 and R15 - }; - } - - pub fn changeMode(self: *Self, next: Mode) void { - const now = getModeChecked(self, self.cpsr.mode.read()); - - // Bank R8 -> r12 - for (0..5) |i| { - self.bank.fiq[Bank.fiqIdx(i, now)] = self.r[8 + i]; - } - - // Bank r13, r14, SPSR - switch (now) { - .User, .System => { - self.bank.r[Bank.regIdx(now, .R13)] = self.r[13]; - self.bank.r[Bank.regIdx(now, .R14)] = self.r[14]; - }, - else => { - self.bank.r[Bank.regIdx(now, .R13)] = self.r[13]; - self.bank.r[Bank.regIdx(now, .R14)] = self.r[14]; - self.bank.spsr[Bank.spsrIdx(now)] = self.spsr; - }, - } - - // Grab R8 -> R12 - for (0..5) |i| { - self.r[8 + i] = self.bank.fiq[Bank.fiqIdx(i, next)]; - } - - // Grab r13, r14, SPSR - switch (next) { - .User, .System => { - self.r[13] = self.bank.r[Bank.regIdx(next, .R13)]; - self.r[14] = self.bank.r[Bank.regIdx(next, .R14)]; - }, - else => { - self.r[13] = self.bank.r[Bank.regIdx(next, .R13)]; - self.r[14] = self.bank.r[Bank.regIdx(next, .R14)]; - self.spsr = self.bank.spsr[Bank.spsrIdx(next)]; - }, - } - - self.cpsr.mode.write(@enumToInt(next)); - } - - /// Advances state so that the BIOS is skipped - /// - /// Note: This accesses the CPU's bus ptr so it only may be called - /// once the Bus has been properly initialized - /// - /// TODO: Make above notice impossible to do in code - pub fn fastBoot(self: *Self) void { - self.r = std.mem.zeroes([16]u32); - - // self.r[0] = 0x08000000; - // self.r[1] = 0x000000EA; - self.r[13] = 0x0300_7F00; - self.r[15] = 0x0800_0000; - - self.bank.r[Bank.regIdx(.Irq, .R13)] = 0x0300_7FA0; - self.bank.r[Bank.regIdx(.Supervisor, .R13)] = 0x0300_7FE0; - - // self.cpsr.raw = 0x6000001F; - self.cpsr.raw = 0x0000_001F; - - self.bus.bios.addr_latch = 0x0000_00DC + 8; - } - - pub fn step(self: *Self) void { - defer { - if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); - self.pipe.flushed = false; - } - - if (self.cpsr.t.read()) { - const opcode = @truncate(u16, self.pipe.step(self, u16) orelse return); - if (self.logger) |*trace| trace.mgbaLog(self, opcode); - - thumb.lut[thumb.idx(opcode)](self, self.bus, opcode); - } else { - const opcode = self.pipe.step(self, u32) orelse return; - if (self.logger) |*trace| trace.mgbaLog(self, opcode); - - if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) { - arm.lut[arm.idx(opcode)](self, self.bus, opcode); - } - } - } - - pub fn stepDmaTransfer(self: *Self) bool { - inline for (0..4) |i| { - if (self.bus.dma[i].in_progress) { - self.bus.dma[i].step(self); - return true; - } - } - - return false; - } - - pub fn handleInterrupt(self: *Self) void { - const should_handle = self.bus.io.ie.raw & self.bus.io.irq.raw; - - // Return if IME is disabled, CPSR I is set or there is nothing to handle - if (!self.bus.io.ime or self.cpsr.i.read() or should_handle == 0) return; - - // If Pipeline isn't full, we have a bug - std.debug.assert(self.pipe.isFull()); - - // log.debug("Handling Interrupt!", .{}); - self.bus.io.haltcnt = .Execute; - - // FIXME: This seems weird, but retAddr.gba suggests I need to make these changes - const ret_addr = self.r[15] - if (self.cpsr.t.read()) 0 else @as(u32, 4); - const new_spsr = self.cpsr.raw; - - self.changeMode(.Irq); - self.cpsr.t.write(false); - self.cpsr.i.write(true); - - self.r[14] = ret_addr; - self.spsr.raw = new_spsr; - self.r[15] = 0x0000_0018; - self.pipe.reload(self); - } - - inline fn fetch(self: *Self, comptime T: type, address: u32) T { - comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB) - - // Bus.read will advance the scheduler. There are different timings for CPU fetches, - // so we want to undo what Bus.read will apply. We can do this by caching the current tick - // This is very dumb. - // - // FIXME: Please rework this - const tick_cache = self.sched.tick; - defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)]; - - return self.bus.read(T, address); - } - - pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn { - var i: usize = 0; - while (i < 16) : (i += 4) { - const i_1 = i + 1; - const i_2 = i + 2; - const i_3 = i + 3; - std.debug.print("R{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\n", .{ i, self.r[i], i_1, self.r[i_1], i_2, self.r[i_2], i_3, self.r[i_3] }); - } - std.debug.print("cpsr: 0x{X:0>8} ", .{self.cpsr.raw}); - self.cpsr.toString(); - - std.debug.print("spsr: 0x{X:0>8} ", .{self.spsr.raw}); - self.spsr.toString(); - - std.debug.print("pipeline: {??X:0>8}\n", .{self.pipe.stage}); - - if (self.cpsr.t.read()) { - const opcode = self.bus.dbgRead(u16, self.r[15] - 4); - const id = thumb.idx(opcode); - std.debug.print("opcode: ID: 0x{b:0>10} 0x{X:0>4}\n", .{ id, opcode }); - } else { - const opcode = self.bus.dbgRead(u32, self.r[15] - 4); - const id = arm.idx(opcode); - std.debug.print("opcode: ID: 0x{X:0>3} 0x{X:0>8}\n", .{ id, opcode }); - } - - std.debug.print("tick: {}\n\n", .{self.sched.tick}); - - std.debug.panic(format, args); - } -}; - -const condition_lut = [_]u16{ - 0xF0F0, // EQ - Equal - 0x0F0F, // NE - Not Equal - 0xCCCC, // CS - Unsigned higher or same - 0x3333, // CC - Unsigned lower - 0xFF00, // MI - Negative - 0x00FF, // PL - Positive or Zero - 0xAAAA, // VS - Overflow - 0x5555, // VC - No Overflow - 0x0C0C, // HI - unsigned hierh - 0xF3F3, // LS - unsigned lower or same - 0xAA55, // GE - greater or equal - 0x55AA, // LT - less than - 0x0A05, // GT - greater than - 0xF5FA, // LE - less than or equal - 0xFFFF, // AL - always - 0x0000, // NV - never -}; - -pub inline fn checkCond(cpsr: PSR, cond: u4) bool { - const flags = @truncate(u4, cpsr.raw >> 28); - - return condition_lut[cond] & (@as(u16, 1) << flags) != 0; -} - -const Pipeline = struct { - const Self = @This(); - stage: [2]?u32, - flushed: bool, - - fn init() Self { - return .{ - .stage = [_]?u32{null} ** 2, - .flushed = false, - }; - } - - pub fn isFull(self: *const Self) bool { - return self.stage[0] != null and self.stage[1] != null; - } - - pub fn step(self: *Self, cpu: *Arm7tdmi, comptime T: type) ?u32 { - comptime std.debug.assert(T == u32 or T == u16); - - const opcode = self.stage[0]; - self.stage[0] = self.stage[1]; - self.stage[1] = cpu.fetch(T, cpu.r[15]); - - return opcode; - } - - pub fn reload(self: *Self, cpu: *Arm7tdmi) void { - if (cpu.cpsr.t.read()) { - self.stage[0] = cpu.fetch(u16, cpu.r[15]); - self.stage[1] = cpu.fetch(u16, cpu.r[15] + 2); - cpu.r[15] += 4; - } else { - self.stage[0] = cpu.fetch(u32, cpu.r[15]); - self.stage[1] = cpu.fetch(u32, cpu.r[15] + 4); - cpu.r[15] += 8; - } - - self.flushed = true; - } -}; - -pub const PSR = extern union { - mode: Bitfield(u32, 0, 5), - t: Bit(u32, 5), - f: Bit(u32, 6), - i: Bit(u32, 7), - v: Bit(u32, 28), - c: Bit(u32, 29), - z: Bit(u32, 30), - n: Bit(u32, 31), - raw: u32, - - fn toString(self: PSR) void { - std.debug.print("[", .{}); - - if (self.n.read()) std.debug.print("N", .{}) else std.debug.print("-", .{}); - if (self.z.read()) std.debug.print("Z", .{}) else std.debug.print("-", .{}); - if (self.c.read()) std.debug.print("C", .{}) else std.debug.print("-", .{}); - if (self.v.read()) std.debug.print("V", .{}) else std.debug.print("-", .{}); - if (self.i.read()) std.debug.print("I", .{}) else std.debug.print("-", .{}); - if (self.f.read()) std.debug.print("F", .{}) else std.debug.print("-", .{}); - if (self.t.read()) std.debug.print("T", .{}) else std.debug.print("-", .{}); - std.debug.print("|", .{}); - if (getMode(self.mode.read())) |m| std.debug.print("{s}", .{m.toString()}) else std.debug.print("---", .{}); - - std.debug.print("]\n", .{}); - } -}; - -pub const Mode = enum(u5) { - User = 0b10000, - Fiq = 0b10001, - Irq = 0b10010, - Supervisor = 0b10011, - Abort = 0b10111, - Undefined = 0b11011, - System = 0b11111, - - pub fn toString(self: Mode) []const u8 { - return switch (self) { - .User => "usr", - .Fiq => "fiq", - .Irq => "irq", - .Supervisor => "svc", - .Abort => "abt", - .Undefined => "und", - .System => "sys", - }; - } -}; - -fn getMode(bits: u5) ?Mode { - return std.meta.intToEnum(Mode, bits) catch null; -} - -fn getModeChecked(cpu: *const Arm7tdmi, bits: u5) Mode { - return getMode(bits) orelse cpu.panic("[CPU/CPSR] 0b{b:0>5} is an invalid CPU mode", .{bits}); -} diff --git a/src/core/cpu/arm/block_data_transfer.zig b/src/core/cpu/arm/block_data_transfer.zig deleted file mode 100644 index ffe87ac..0000000 --- a/src/core/cpu/arm/block_data_transfer.zig +++ /dev/null @@ -1,111 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, comptime W: bool, comptime L: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void { - const rn = @truncate(u4, opcode >> 16 & 0xF); - const rlist = opcode & 0xFFFF; - const r15 = rlist >> 15 & 1 == 1; - - var count: u32 = 0; - var i: u5 = 0; - var first: u4 = 0; - var write_to_base = true; - - while (i < 16) : (i += 1) { - const r = @truncate(u4, 15 - i); - if (rlist >> r & 1 == 1) { - first = r; - count += 1; - } - } - - var start = cpu.r[rn]; - if (U) { - start += if (P) 4 else 0; - } else { - start = start - (4 * count) + if (!P) 4 else 0; - } - - var end = cpu.r[rn]; - if (U) { - end = end + (4 * count) - if (!P) 4 else 0; - } else { - end -= if (P) 4 else 0; - } - - var new_base = cpu.r[rn]; - if (U) { - new_base += 4 * count; - } else { - new_base -= 4 * count; - } - - var address = start; - - if (rlist == 0) { - var und_addr = cpu.r[rn]; - if (U) { - und_addr += if (P) 4 else 0; - } else { - und_addr -= 0x40 - if (!P) 4 else 0; - } - - if (L) { - cpu.r[15] = bus.read(u32, und_addr); - cpu.pipe.reload(cpu); - } else { - bus.write(u32, und_addr, cpu.r[15] + 4); - } - - cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40; - return; - } - - i = first; - while (i < 16) : (i += 1) { - if (rlist >> i & 1 == 1) { - transfer(cpu, bus, r15, i, address); - address += 4; - - if (W and !L and write_to_base) { - cpu.r[rn] = new_base; - write_to_base = false; - } - } - } - - if (W and L and rlist >> rn & 1 == 0) cpu.r[rn] = new_base; - } - - fn transfer(cpu: *Arm7tdmi, bus: *Bus, r15_present: bool, i: u5, address: u32) void { - if (L) { - if (S and !r15_present) { - // Always Transfer User mode Registers - cpu.setUserModeRegister(i, bus.read(u32, address)); - } else { - const value = bus.read(u32, address); - - cpu.r[i] = value; - if (i == 0xF) { - cpu.r[i] &= ~@as(u32, 3); // Align r15 - cpu.pipe.reload(cpu); - - if (S) cpu.setCpsr(cpu.spsr.raw); - } - } - } else { - if (S) { - // Always Transfer User mode Registers - // This happens regardless if r15 is in the list - const value = cpu.getUserModeRegister(i); - bus.write(u32, address, value + if (i == 0xF) 4 else @as(u32, 0)); // PC is already 8 ahead to make 12 - } else { - bus.write(u32, address, cpu.r[i] + if (i == 0xF) 4 else @as(u32, 0)); - } - } - } - }.inner; -} diff --git a/src/core/cpu/arm/branch.zig b/src/core/cpu/arm/branch.zig deleted file mode 100644 index 18d7b6e..0000000 --- a/src/core/cpu/arm/branch.zig +++ /dev/null @@ -1,26 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -const sext = @import("zba-util").sext; - -pub fn branch(comptime L: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { - if (L) cpu.r[14] = cpu.r[15] - 4; - - cpu.r[15] +%= sext(u32, u24, opcode) << 2; - cpu.pipe.reload(cpu); - } - }.inner; -} - -pub fn branchAndExchange(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { - const rn = opcode & 0xF; - - const thumb = cpu.r[rn] & 1 == 1; - cpu.r[15] = cpu.r[rn] & if (thumb) ~@as(u32, 1) else ~@as(u32, 3); - - cpu.cpsr.t.write(thumb); - cpu.pipe.reload(cpu); -} diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig deleted file mode 100644 index 2e92b38..0000000 --- a/src/core/cpu/arm/data_processing.zig +++ /dev/null @@ -1,185 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -const exec = @import("../barrel_shifter.zig").exec; -const ror = @import("../barrel_shifter.zig").ror; - -pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { - const rd = @truncate(u4, opcode >> 12 & 0xF); - const rn = opcode >> 16 & 0xF; - const old_carry = @boolToInt(cpu.cpsr.c.read()); - - // If certain conditions are met, PC is 12 ahead instead of 8 - // TODO: Why these conditions? - if (!I and opcode >> 4 & 1 == 1) cpu.r[15] += 4; - const op1 = cpu.r[rn]; - - const amount = @truncate(u8, (opcode >> 8 & 0xF) << 1); - const op2 = if (I) ror(S, &cpu.cpsr, opcode & 0xFF, amount) else exec(S, cpu, opcode); - - // Undo special condition from above - if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; - - var result: u32 = undefined; - var overflow: u1 = undefined; - - // Perform Data Processing Logic - switch (kind) { - 0x0 => result = op1 & op2, // AND - 0x1 => result = op1 ^ op2, // EOR - 0x2 => result = op1 -% op2, // SUB - 0x3 => result = op2 -% op1, // RSB - 0x4 => result = add(&overflow, op1, op2), // ADD - 0x5 => result = adc(&overflow, op1, op2, old_carry), // ADC - 0x6 => result = sbc(op1, op2, old_carry), // SBC - 0x7 => result = sbc(op2, op1, old_carry), // RSC - 0x8 => { - // TST - if (rd == 0xF) - return undefinedTestBehaviour(cpu); - - result = op1 & op2; - }, - 0x9 => { - // TEQ - if (rd == 0xF) - return undefinedTestBehaviour(cpu); - - result = op1 ^ op2; - }, - 0xA => { - // CMP - if (rd == 0xF) - return undefinedTestBehaviour(cpu); - - result = op1 -% op2; - }, - 0xB => { - // CMN - if (rd == 0xF) - return undefinedTestBehaviour(cpu); - - const tmp = @addWithOverflow(op1, op2); - result = tmp[0]; - overflow = tmp[1]; - }, - 0xC => result = op1 | op2, // ORR - 0xD => result = op2, // MOV - 0xE => result = op1 & ~op2, // BIC - 0xF => result = ~op2, // MVN - } - - // Write to Destination Register - switch (kind) { - 0x8, 0x9, 0xA, 0xB => {}, // Test Operations - else => { - cpu.r[rd] = result; - if (rd == 0xF) { - if (S) cpu.setCpsr(cpu.spsr.raw); - cpu.pipe.reload(cpu); - } - }, - } - - // Write Flags - switch (kind) { - 0x0, 0x1, 0xC, 0xD, 0xE, 0xF => if (S and rd != 0xF) { - // Logic Operation Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // C set by Barrel Shifter, V is unaffected - - }, - 0x2, 0x3 => if (S and rd != 0xF) { - // SUB, RSB Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - - if (kind == 0x2) { - // SUB specific - cpu.cpsr.c.write(op2 <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - } else { - // RSB Specific - cpu.cpsr.c.write(op1 <= op2); - cpu.cpsr.v.write(((op2 ^ result) & (~op1 ^ result)) >> 31 & 1 == 1); - } - }, - 0x4, 0x5 => if (S and rd != 0xF) { - // ADD, ADC Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(overflow == 0b1); - cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - }, - 0x6, 0x7 => if (S and rd != 0xF) { - // SBC, RSC Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - - if (kind == 0x6) { - // SBC specific - const subtrahend = @as(u64, op2) -% old_carry +% 1; - cpu.cpsr.c.write(subtrahend <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - } else { - // RSC Specific - const subtrahend = @as(u64, op1) -% old_carry +% 1; - cpu.cpsr.c.write(subtrahend <= op2); - cpu.cpsr.v.write(((op2 ^ result) & (~op1 ^ result)) >> 31 & 1 == 1); - } - }, - 0x8, 0x9, 0xA, 0xB => { - // Test Operation Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - - if (kind == 0xA) { - // CMP specific - cpu.cpsr.c.write(op2 <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - } else if (kind == 0xB) { - // CMN specific - cpu.cpsr.c.write(overflow == 0b1); - cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - } else { - // TST, TEQ specific - // Barrel Shifter should always calc CPSR C in TST - if (!S) _ = exec(true, cpu, opcode); - } - }, - } - } - }.inner; -} - -pub fn sbc(left: u32, right: u32, old_carry: u1) u32 { - // TODO: Make your own version (thanks peach.bot) - const subtrahend = @as(u64, right) -% old_carry +% 1; - const ret = @truncate(u32, left -% subtrahend); - - return ret; -} - -pub fn add(overflow: *u1, left: u32, right: u32) u32 { - const ret = @addWithOverflow(left, right); - overflow.* = ret[1]; - - return ret[0]; -} - -pub fn adc(overflow: *u1, left: u32, right: u32, old_carry: u1) u32 { - const tmp = @addWithOverflow(left, right); - const ret = @addWithOverflow(tmp[0], old_carry); - overflow.* = tmp[1] | ret[1]; - - return ret[0]; -} - -fn undefinedTestBehaviour(cpu: *Arm7tdmi) void { - @setCold(true); - cpu.setCpsr(cpu.spsr.raw); -} diff --git a/src/core/cpu/arm/half_signed_data_transfer.zig b/src/core/cpu/arm/half_signed_data_transfer.zig deleted file mode 100644 index e95c9c2..0000000 --- a/src/core/cpu/arm/half_signed_data_transfer.zig +++ /dev/null @@ -1,53 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -const sext = @import("zba-util").sext; -const rotr = @import("zba-util").rotr; - -pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I: bool, comptime W: bool, comptime L: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void { - const rn = opcode >> 16 & 0xF; - const rd = opcode >> 12 & 0xF; - const rm = opcode & 0xF; - const imm_offset_high = opcode >> 8 & 0xF; - - const base = cpu.r[rn] + if (!L and rn == 0xF) 4 else @as(u32, 0); - const offset = if (I) imm_offset_high << 4 | rm else cpu.r[rm]; - - const modified_base = if (U) base +% offset else base -% offset; - var address = if (P) modified_base else base; - - var result: u32 = undefined; - if (L) { - switch (@truncate(u2, opcode >> 5)) { - 0b01 => { - // LDRH - const value = bus.read(u16, address); - result = rotr(u32, value, 8 * (address & 1)); - }, - 0b10 => { - // LDRSB - result = sext(u32, u8, bus.read(u8, address)); - }, - 0b11 => { - // LDRSH - const value = bus.read(u16, address); - result = if (address & 1 == 1) sext(u32, u8, @truncate(u8, value >> 8)) else sext(u32, u16, value); - }, - 0b00 => unreachable, // SWP - } - } else { - if (opcode >> 5 & 0x01 == 0x01) { - // STRH - bus.write(u16, address, @truncate(u16, cpu.r[rd])); - } else unreachable; // SWP - } - - address = modified_base; - if (W and P or !P) cpu.r[rn] = address; - if (L) cpu.r[rd] = result; // // This emulates the LDR rd == rn behaviour - } - }.inner; -} diff --git a/src/core/cpu/arm/multiply.zig b/src/core/cpu/arm/multiply.zig deleted file mode 100644 index f4b4104..0000000 --- a/src/core/cpu/arm/multiply.zig +++ /dev/null @@ -1,57 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -pub fn multiply(comptime A: bool, comptime S: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { - const rd = opcode >> 16 & 0xF; - const rn = opcode >> 12 & 0xF; - const rs = opcode >> 8 & 0xF; - const rm = opcode & 0xF; - - const temp: u64 = @as(u64, cpu.r[rm]) * @as(u64, cpu.r[rs]) + if (A) cpu.r[rn] else 0; - const result = @truncate(u32, temp); - cpu.r[rd] = result; - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // V is unaffected, C is *actually* undefined in ARMv4 - } - } - }.inner; -} - -pub fn multiplyLong(comptime U: bool, comptime A: bool, comptime S: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { - const rd_hi = opcode >> 16 & 0xF; - const rd_lo = opcode >> 12 & 0xF; - const rs = opcode >> 8 & 0xF; - const rm = opcode & 0xF; - - if (U) { - // Signed (WHY IS IT U THEN?) - var result: i64 = @as(i64, @bitCast(i32, cpu.r[rm])) * @as(i64, @bitCast(i32, cpu.r[rs])); - if (A) result +%= @bitCast(i64, @as(u64, cpu.r[rd_hi]) << 32 | @as(u64, cpu.r[rd_lo])); - - cpu.r[rd_hi] = @bitCast(u32, @truncate(i32, result >> 32)); - cpu.r[rd_lo] = @bitCast(u32, @truncate(i32, result)); - } else { - // Unsigned - var result: u64 = @as(u64, cpu.r[rm]) * @as(u64, cpu.r[rs]); - if (A) result +%= @as(u64, cpu.r[rd_hi]) << 32 | @as(u64, cpu.r[rd_lo]); - - cpu.r[rd_hi] = @truncate(u32, result >> 32); - cpu.r[rd_lo] = @truncate(u32, result); - } - - if (S) { - cpu.cpsr.z.write(cpu.r[rd_hi] == 0 and cpu.r[rd_lo] == 0); - cpu.cpsr.n.write(cpu.r[rd_hi] >> 31 & 1 == 1); - // C and V are set to meaningless values - } - } - }.inner; -} diff --git a/src/core/cpu/arm/psr_transfer.zig b/src/core/cpu/arm/psr_transfer.zig deleted file mode 100644 index 8eba6f6..0000000 --- a/src/core/cpu/arm/psr_transfer.zig +++ /dev/null @@ -1,59 +0,0 @@ -const std = @import("std"); - -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; -const PSR = @import("../../cpu.zig").PSR; - -const log = std.log.scoped(.PsrTransfer); - -const rotr = @import("zba-util").rotr; - -pub fn psrTransfer(comptime I: bool, comptime R: bool, comptime kind: u2) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { - switch (kind) { - 0b00 => { - // MRS - const rd = opcode >> 12 & 0xF; - - if (R and !cpu.hasSPSR()) log.err("Tried to read SPSR from User/System Mode", .{}); - cpu.r[rd] = if (R) cpu.spsr.raw else cpu.cpsr.raw; - }, - 0b10 => { - // MSR - const field_mask = @truncate(u4, opcode >> 16 & 0xF); - const rm_idx = opcode & 0xF; - const right = if (I) rotr(u32, opcode & 0xFF, (opcode >> 8 & 0xF) * 2) else cpu.r[rm_idx]; - - if (R and !cpu.hasSPSR()) log.err("Tried to write to SPSR in User/System Mode", .{}); - - if (R) { - // arm.gba seems to expect the SPSR to do somethign in SYS mode, - // so we just assume that despite writing to the SPSR in USR or SYS mode - // being UNPREDICTABLE, it just magically has a working SPSR somehow - cpu.spsr.raw = fieldMask(&cpu.spsr, field_mask, right); - } else { - if (cpu.isPrivileged()) cpu.setCpsr(fieldMask(&cpu.cpsr, field_mask, right)); - } - }, - else => cpu.panic("[CPU/PSR Transfer] Bits 21:220 of {X:0>8} are undefined", .{opcode}), - } - } - }.inner; -} - -fn fieldMask(psr: *const PSR, field_mask: u4, right: u32) u32 { - // This bitwise ORs bits 3 and 0 of the field mask into a u2 - // We do this because we only care about bits 7:0 and 31:28 of the CPSR - const bits = @truncate(u2, (field_mask >> 2 & 0x2) | (field_mask & 1)); - - const mask: u32 = switch (bits) { - 0b00 => 0x0000_0000, - 0b01 => 0x0000_00FF, - 0b10 => 0xF000_0000, - 0b11 => 0xF000_00FF, - }; - - return (psr.raw & ~mask) | (right & mask); -} diff --git a/src/core/cpu/arm/single_data_swap.zig b/src/core/cpu/arm/single_data_swap.zig deleted file mode 100644 index 7a588f3..0000000 --- a/src/core/cpu/arm/single_data_swap.zig +++ /dev/null @@ -1,29 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -const rotr = @import("zba-util").rotr; - -pub fn singleDataSwap(comptime B: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void { - const rn = opcode >> 16 & 0xF; - const rd = opcode >> 12 & 0xF; - const rm = opcode & 0xF; - - const address = cpu.r[rn]; - - if (B) { - // SWPB - const value = bus.read(u8, address); - bus.write(u8, address, @truncate(u8, cpu.r[rm])); - cpu.r[rd] = value; - } else { - // SWP - const value = rotr(u32, bus.read(u32, address), 8 * (address & 0x3)); - bus.write(u32, address, cpu.r[rm]); - cpu.r[rd] = value; - } - } - }.inner; -} diff --git a/src/core/cpu/arm/single_data_transfer.zig b/src/core/cpu/arm/single_data_transfer.zig deleted file mode 100644 index 8a2e2d2..0000000 --- a/src/core/cpu/arm/single_data_transfer.zig +++ /dev/null @@ -1,55 +0,0 @@ -const shifter = @import("../barrel_shifter.zig"); -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -const rotr = @import("zba-util").rotr; - -pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, comptime B: bool, comptime W: bool, comptime L: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void { - const rn = opcode >> 16 & 0xF; - const rd = opcode >> 12 & 0xF; - - const base = cpu.r[rn]; - const offset = if (I) shifter.immediate(false, cpu, opcode) else opcode & 0xFFF; - - const modified_base = if (U) base +% offset else base -% offset; - var address = if (P) modified_base else base; - - var result: u32 = undefined; - if (L) { - if (B) { - // LDRB - result = bus.read(u8, address); - } else { - // LDR - const value = bus.read(u32, address); - result = rotr(u32, value, 8 * (address & 0x3)); - } - } else { - if (B) { - // STRB - const value = cpu.r[rd] + if (rd == 0xF) 4 else @as(u32, 0); // PC is 12 ahead - bus.write(u8, address, @truncate(u8, value)); - } else { - // STR - const value = cpu.r[rd] + if (rd == 0xF) 4 else @as(u32, 0); - bus.write(u32, address, value); - } - } - - address = modified_base; - if (W and P or !P) { - cpu.r[rn] = address; - if (rn == 0xF) cpu.pipe.reload(cpu); - } - - if (L) { - // This emulates the LDR rd == rn behaviour - cpu.r[rd] = result; - if (rd == 0xF) cpu.pipe.reload(cpu); - } - } - }.inner; -} diff --git a/src/core/cpu/arm/software_interrupt.zig b/src/core/cpu/arm/software_interrupt.zig deleted file mode 100644 index b44cf99..0000000 --- a/src/core/cpu/arm/software_interrupt.zig +++ /dev/null @@ -1,23 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").arm.InstrFn; - -pub fn armSoftwareInterrupt() InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, _: u32) void { - // Copy Values from Current Mode - const ret_addr = cpu.r[15] - 4; - const cpsr = cpu.cpsr.raw; - - // Switch Mode - cpu.changeMode(.Supervisor); - cpu.cpsr.t.write(false); // Force ARM Mode - cpu.cpsr.i.write(true); // Disable normal interrupts - - cpu.r[14] = ret_addr; // Resume Execution - cpu.spsr.raw = cpsr; // Previous mode CPSR - cpu.r[15] = 0x0000_0008; - cpu.pipe.reload(cpu); - } - }.inner; -} diff --git a/src/core/cpu/barrel_shifter.zig b/src/core/cpu/barrel_shifter.zig deleted file mode 100644 index c5a6c81..0000000 --- a/src/core/cpu/barrel_shifter.zig +++ /dev/null @@ -1,147 +0,0 @@ -const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; -const CPSR = @import("../cpu.zig").PSR; - -const rotr = @import("zba-util").rotr; - -pub fn exec(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { - var result: u32 = undefined; - if (opcode >> 4 & 1 == 1) { - result = register(S, cpu, opcode); - } else { - result = immediate(S, cpu, opcode); - } - - return result; -} - -fn register(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { - const rs_idx = opcode >> 8 & 0xF; - const rm = cpu.r[opcode & 0xF]; - const rs = @truncate(u8, cpu.r[rs_idx]); - - return switch (@truncate(u2, opcode >> 5)) { - 0b00 => lsl(S, &cpu.cpsr, rm, rs), - 0b01 => lsr(S, &cpu.cpsr, rm, rs), - 0b10 => asr(S, &cpu.cpsr, rm, rs), - 0b11 => ror(S, &cpu.cpsr, rm, rs), - }; -} - -pub fn immediate(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { - const amount = @truncate(u8, opcode >> 7 & 0x1F); - const rm = cpu.r[opcode & 0xF]; - - var result: u32 = undefined; - if (amount == 0) { - switch (@truncate(u2, opcode >> 5)) { - 0b00 => { - // LSL #0 - result = rm; - }, - 0b01 => { - // LSR #0 aka LSR #32 - if (S) cpu.cpsr.c.write(rm >> 31 & 1 == 1); - result = 0x0000_0000; - }, - 0b10 => { - // ASR #0 aka ASR #32 - result = @bitCast(u32, @bitCast(i32, rm) >> 31); - if (S) cpu.cpsr.c.write(result >> 31 & 1 == 1); - }, - 0b11 => { - // ROR #0 aka RRX - const carry: u32 = @boolToInt(cpu.cpsr.c.read()); - if (S) cpu.cpsr.c.write(rm & 1 == 1); - - result = (carry << 31) | (rm >> 1); - }, - } - } else { - switch (@truncate(u2, opcode >> 5)) { - 0b00 => result = lsl(S, &cpu.cpsr, rm, amount), - 0b01 => result = lsr(S, &cpu.cpsr, rm, amount), - 0b10 => result = asr(S, &cpu.cpsr, rm, amount), - 0b11 => result = ror(S, &cpu.cpsr, rm, amount), - } - } - - return result; -} - -pub fn lsl(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 { - const amount = @truncate(u5, total_amount); - const bit_count: u8 = @typeInfo(u32).Int.bits; - - var result: u32 = 0x0000_0000; - if (total_amount < bit_count) { - // We can perform a well-defined shift here - result = rm << amount; - - if (S and total_amount != 0) { - const carry_bit = @truncate(u5, bit_count - amount); - cpsr.c.write(rm >> carry_bit & 1 == 1); - } - } else { - if (S) { - if (total_amount == bit_count) { - // Shifted all bits out, carry bit is bit 0 of rm - cpsr.c.write(rm & 1 == 1); - } else { - cpsr.c.write(false); - } - } - } - - return result; -} - -pub fn lsr(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u32) u32 { - const amount = @truncate(u5, total_amount); - const bit_count: u8 = @typeInfo(u32).Int.bits; - - var result: u32 = 0x0000_0000; - if (total_amount < bit_count) { - // We can perform a well-defined shift - result = rm >> amount; - if (S and total_amount != 0) cpsr.c.write(rm >> (amount - 1) & 1 == 1); - } else { - if (S) { - if (total_amount == bit_count) { - // LSR #32 - cpsr.c.write(rm >> 31 & 1 == 1); - } else { - // All bits have been shifted out, including carry bit - cpsr.c.write(false); - } - } - } - - return result; -} - -pub fn asr(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 { - const amount = @truncate(u5, total_amount); - const bit_count: u8 = @typeInfo(u32).Int.bits; - - var result: u32 = 0x0000_0000; - if (total_amount < bit_count) { - result = @bitCast(u32, @bitCast(i32, rm) >> amount); - if (S and total_amount != 0) cpsr.c.write(rm >> (amount - 1) & 1 == 1); - } else { - // ASR #32 and ASR #>32 have the same result - result = @bitCast(u32, @bitCast(i32, rm) >> 31); - if (S) cpsr.c.write(result >> 31 & 1 == 1); - } - - return result; -} - -pub fn ror(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 { - const result = rotr(u32, rm, total_amount); - - if (S and total_amount != 0) { - cpsr.c.write(result >> 31 & 1 == 1); - } - - return result; -} diff --git a/src/core/cpu/thumb/alu.zig b/src/core/cpu/thumb/alu.zig deleted file mode 100644 index a8038c8..0000000 --- a/src/core/cpu/thumb/alu.zig +++ /dev/null @@ -1,108 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").thumb.InstrFn; - -const adc = @import("../arm/data_processing.zig").adc; -const sbc = @import("../arm/data_processing.zig").sbc; - -const lsl = @import("../barrel_shifter.zig").lsl; -const lsr = @import("../barrel_shifter.zig").lsr; -const asr = @import("../barrel_shifter.zig").asr; -const ror = @import("../barrel_shifter.zig").ror; - -pub fn fmt4(comptime op: u4) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const rs = opcode >> 3 & 0x7; - const rd = opcode & 0x7; - const carry = @boolToInt(cpu.cpsr.c.read()); - - const op1 = cpu.r[rd]; - const op2 = cpu.r[rs]; - - var result: u32 = undefined; - var overflow: u1 = undefined; - - switch (op) { - 0x0 => result = op1 & op2, // AND - 0x1 => result = op1 ^ op2, // EOR - 0x2 => result = lsl(true, &cpu.cpsr, op1, @truncate(u8, op2)), // LSL - 0x3 => result = lsr(true, &cpu.cpsr, op1, @truncate(u8, op2)), // LSR - 0x4 => result = asr(true, &cpu.cpsr, op1, @truncate(u8, op2)), // ASR - 0x5 => result = adc(&overflow, op1, op2, carry), // ADC - 0x6 => result = sbc(op1, op2, carry), // SBC - 0x7 => result = ror(true, &cpu.cpsr, op1, @truncate(u8, op2)), // ROR - 0x8 => result = op1 & op2, // TST - 0x9 => result = 0 -% op2, // NEG - 0xA => result = op1 -% op2, // CMP - 0xB => { - // CMN - const tmp = @addWithOverflow(op1, op2); - result = tmp[0]; - overflow = tmp[1]; - }, - 0xC => result = op1 | op2, // ORR - 0xD => result = @truncate(u32, @as(u64, op2) * @as(u64, op1)), - 0xE => result = op1 & ~op2, - 0xF => result = ~op2, - } - - // Write to Destination Register - switch (op) { - 0x8, 0xA, 0xB => {}, - else => cpu.r[rd] = result, - } - - // Write Flags - switch (op) { - 0x0, 0x1, 0x2, 0x3, 0x4, 0x7, 0xC, 0xE, 0xF => { - // Logic Operations - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // C set by Barrel Shifter, V is unaffected - }, - 0x8, 0xA => { - // Test Flags - // CMN (0xB) is handled with ADC - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - - if (op == 0xA) { - // CMP specific - cpu.cpsr.c.write(op2 <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - } - }, - 0x5, 0xB => { - // ADC, CMN - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(overflow == 0b1); - cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - }, - 0x6 => { - // SBC - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - - const subtrahend = @as(u64, op2) -% carry +% 1; - cpu.cpsr.c.write(subtrahend <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - }, - 0x9 => { - // NEG - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(op2 <= 0); - cpu.cpsr.v.write(((0 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - }, - 0xD => { - // Multiplication - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // V is unaffected, assuming similar behaviour to ARMv4 MUL C is undefined - }, - } - } - }.inner; -} diff --git a/src/core/cpu/thumb/block_data_transfer.zig b/src/core/cpu/thumb/block_data_transfer.zig deleted file mode 100644 index 5ab2556..0000000 --- a/src/core/cpu/thumb/block_data_transfer.zig +++ /dev/null @@ -1,100 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").thumb.InstrFn; - -pub fn fmt14(comptime L: bool, comptime R: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - const count = @boolToInt(R) + countRlist(opcode); - const start = cpu.r[13] - if (!L) count * 4 else 0; - - var end = cpu.r[13]; - if (L) { - end += count * 4; - } else { - end -= 4; - } - - var address = start; - - var i: u4 = 0; - while (i < 8) : (i += 1) { - if (opcode >> i & 1 == 1) { - if (L) { - cpu.r[i] = bus.read(u32, address); - } else { - bus.write(u32, address, cpu.r[i]); - } - - address += 4; - } - } - - if (R) { - if (L) { - const value = bus.read(u32, address); - cpu.r[15] = value & ~@as(u32, 1); - cpu.pipe.reload(cpu); - } else { - bus.write(u32, address, cpu.r[14]); - } - address += 4; - } - - cpu.r[13] = if (L) end else start; - } - }.inner; -} - -pub fn fmt15(comptime L: bool, comptime rb: u3) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - var address = cpu.r[rb]; - const end_address = cpu.r[rb] + 4 * countRlist(opcode); - - if (opcode & 0xFF == 0) { - if (L) { - cpu.r[15] = bus.read(u32, address); - cpu.pipe.reload(cpu); - } else { - bus.write(u32, address, cpu.r[15] + 2); - } - - cpu.r[rb] += 0x40; - return; - } - - var i: u4 = 0; - var first_write = true; - - while (i < 8) : (i += 1) { - if (opcode >> i & 1 == 1) { - if (L) { - cpu.r[i] = bus.read(u32, address); - } else { - bus.write(u32, address, cpu.r[i]); - } - - if (!L and first_write) { - cpu.r[rb] = end_address; - first_write = false; - } - - address += 4; - } - } - - if (L and opcode >> rb & 1 != 1) cpu.r[rb] = address; - } - }.inner; -} - -inline fn countRlist(opcode: u16) u32 { - var count: u32 = 0; - - inline for (0..8) |i| { - if (opcode >> (7 - i) & 1 == 1) count += 1; - } - - return count; -} diff --git a/src/core/cpu/thumb/branch.zig b/src/core/cpu/thumb/branch.zig deleted file mode 100644 index c531ac9..0000000 --- a/src/core/cpu/thumb/branch.zig +++ /dev/null @@ -1,54 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").thumb.InstrFn; - -const checkCond = @import("../../cpu.zig").checkCond; -const sext = @import("zba-util").sext; - -pub fn fmt16(comptime cond: u4) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - // B - if (cond == 0xE or cond == 0xF) - cpu.panic("[CPU/THUMB.16] Undefined conditional branch with condition {}", .{cond}); - - if (!checkCond(cpu.cpsr, cond)) return; - - cpu.r[15] +%= sext(u32, u8, opcode & 0xFF) << 1; - cpu.pipe.reload(cpu); - } - }.inner; -} - -pub fn fmt18() InstrFn { - return struct { - // B but conditional - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1; - cpu.pipe.reload(cpu); - } - }.inner; -} - -pub fn fmt19(comptime is_low: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - // BL - const offset = opcode & 0x7FF; - - if (is_low) { - // Instruction 2 - const next_opcode = cpu.r[15] - 2; - - cpu.r[15] = cpu.r[14] +% (offset << 1); - cpu.r[14] = next_opcode | 1; - - cpu.pipe.reload(cpu); - } else { - // Instruction 1 - const lr_offset = sext(u32, u11, offset) << 12; - cpu.r[14] = (cpu.r[15] +% lr_offset) & ~@as(u32, 1); - } - } - }.inner; -} diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig deleted file mode 100644 index a8a97e1..0000000 --- a/src/core/cpu/thumb/data_processing.zig +++ /dev/null @@ -1,199 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").thumb.InstrFn; - -const add = @import("../arm/data_processing.zig").add; - -const lsl = @import("../barrel_shifter.zig").lsl; -const lsr = @import("../barrel_shifter.zig").lsr; -const asr = @import("../barrel_shifter.zig").asr; - -pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const rs = opcode >> 3 & 0x7; - const rd = opcode & 0x7; - - const result = switch (op) { - 0b00 => blk: { - // LSL - if (offset == 0) { - break :blk cpu.r[rs]; - } else { - break :blk lsl(true, &cpu.cpsr, cpu.r[rs], offset); - } - }, - 0b01 => blk: { - // LSR - if (offset == 0) { - cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); - break :blk @as(u32, 0); - } else { - break :blk lsr(true, &cpu.cpsr, cpu.r[rs], offset); - } - }, - 0b10 => blk: { - // ASR - if (offset == 0) { - cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); - break :blk @bitCast(u32, @bitCast(i32, cpu.r[rs]) >> 31); - } else { - break :blk asr(true, &cpu.cpsr, cpu.r[rs], offset); - } - }, - else => cpu.panic("[CPU/THUMB.1] 0b{b:0>2} is not a valid op", .{op}), - }; - - // Equivalent to an ARM MOVS - cpu.r[rd] = result; - - // Write Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - } - }.inner; -} - -pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7); - const rd = @as(u4, h1) << 3 | (opcode & 0x7); - - const op1 = cpu.r[rd]; - const op2 = cpu.r[rs]; - - var result: u32 = undefined; - var overflow: u1 = undefined; - switch (op) { - 0b00 => result = add(&overflow, op1, op2), // ADD - 0b01 => result = op1 -% op2, // CMP - 0b10 => result = op2, // MOV - 0b11 => {}, - } - - // Write to Destination Register - switch (op) { - 0b01 => {}, // Test Instruction - 0b11 => { - // BX - const is_thumb = op2 & 1 == 1; - cpu.r[15] = op2 & ~@as(u32, 1); - - cpu.cpsr.t.write(is_thumb); - cpu.pipe.reload(cpu); - }, - else => { - cpu.r[rd] = result; - if (rd == 0xF) { - cpu.r[15] &= ~@as(u32, 1); - cpu.pipe.reload(cpu); - } - }, - } - - // Write Flags - switch (op) { - 0b01 => { - // CMP - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(op2 <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - }, - 0b00, 0b10, 0b11 => {}, // MOV and Branch Instruction - } - } - }.inner; -} - -pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const rs = opcode >> 3 & 0x7; - const rd = @truncate(u3, opcode); - const op1 = cpu.r[rs]; - const op2: u32 = if (I) rn else cpu.r[rn]; - - if (is_sub) { - // SUB - const result = op1 -% op2; - cpu.r[rd] = result; - - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(op2 <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - } else { - // ADD - var overflow: u1 = undefined; - const result = add(&overflow, op1, op2); - cpu.r[rd] = result; - - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(overflow == 0b1); - cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - } - } - }.inner; -} - -pub fn fmt3(comptime op: u2, comptime rd: u3) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const op1 = cpu.r[rd]; - const op2: u32 = opcode & 0xFF; // Offset - - var overflow: u1 = undefined; - const result: u32 = switch (op) { - 0b00 => op2, // MOV - 0b01 => op1 -% op2, // CMP - 0b10 => add(&overflow, op1, op2), // ADD - 0b11 => op1 -% op2, // SUB - }; - - // Write to Register - if (op != 0b01) cpu.r[rd] = result; - - // Write Flags - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - - switch (op) { - 0b00 => {}, // MOV | C set by Barrel Shifter, V is unaffected - 0b01, 0b11 => { - // SUB, CMP - cpu.cpsr.c.write(op2 <= op1); - cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); - }, - 0b10 => { - // ADD - cpu.cpsr.c.write(overflow == 0b1); - cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - }, - } - } - }.inner; -} - -pub fn fmt12(comptime isSP: bool, comptime rd: u3) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - // ADD - const left = if (isSP) cpu.r[13] else cpu.r[15] & ~@as(u32, 2); - const right = (opcode & 0xFF) << 2; - cpu.r[rd] = left + right; - } - }.inner; -} - -pub fn fmt13(comptime S: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - // ADD - const offset = (opcode & 0x7F) << 2; - cpu.r[13] = if (S) cpu.r[13] - offset else cpu.r[13] + offset; - } - }.inner; -} diff --git a/src/core/cpu/thumb/data_transfer.zig b/src/core/cpu/thumb/data_transfer.zig deleted file mode 100644 index b7cfd7f..0000000 --- a/src/core/cpu/thumb/data_transfer.zig +++ /dev/null @@ -1,145 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").thumb.InstrFn; - -const rotr = @import("zba-util").rotr; -const sext = @import("zba-util").sext; - -pub fn fmt6(comptime rd: u3) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - // LDR - const offset = (opcode & 0xFF) << 2; - - // Bit 1 of the PC intentionally ignored - cpu.r[rd] = bus.read(u32, (cpu.r[15] & ~@as(u32, 2)) + offset); - } - }.inner; -} - -pub fn fmt78(comptime op: u2, comptime T: bool) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - const ro = opcode >> 6 & 0x7; - const rb = opcode >> 3 & 0x7; - const rd = opcode & 0x7; - - const address = cpu.r[rb] +% cpu.r[ro]; - - if (T) { - // Format 8 - switch (op) { - 0b00 => { - // STRH - bus.write(u16, address, @truncate(u16, cpu.r[rd])); - }, - 0b01 => { - // LDSB - cpu.r[rd] = sext(u32, u8, bus.read(u8, address)); - }, - 0b10 => { - // LDRH - const value = bus.read(u16, address); - cpu.r[rd] = rotr(u32, value, 8 * (address & 1)); - }, - 0b11 => { - // LDRSH - const value = bus.read(u16, address); - cpu.r[rd] = if (address & 1 == 1) sext(u32, u8, @truncate(u8, value >> 8)) else sext(u32, u16, value); - }, - } - } else { - // Format 7 - switch (op) { - 0b00 => { - // STR - bus.write(u32, address, cpu.r[rd]); - }, - 0b01 => { - // STRB - bus.write(u8, address, @truncate(u8, cpu.r[rd])); - }, - 0b10 => { - // LDR - const value = bus.read(u32, address); - cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3)); - }, - 0b11 => { - // LDRB - cpu.r[rd] = bus.read(u8, address); - }, - } - } - } - }.inner; -} - -pub fn fmt9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - const rb = opcode >> 3 & 0x7; - const rd = opcode & 0x7; - - if (L) { - if (B) { - // LDRB - const address = cpu.r[rb] + offset; - cpu.r[rd] = bus.read(u8, address); - } else { - // LDR - const address = cpu.r[rb] + (@as(u32, offset) << 2); - const value = bus.read(u32, address); - cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3)); - } - } else { - if (B) { - // STRB - const address = cpu.r[rb] + offset; - bus.write(u8, address, @truncate(u8, cpu.r[rd])); - } else { - // STR - const address = cpu.r[rb] + (@as(u32, offset) << 2); - bus.write(u32, address, cpu.r[rd]); - } - } - } - }.inner; -} - -pub fn fmt10(comptime L: bool, comptime offset: u5) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - const rb = opcode >> 3 & 0x7; - const rd = opcode & 0x7; - - const address = cpu.r[rb] + (@as(u6, offset) << 1); - - if (L) { - // LDRH - const value = bus.read(u16, address); - cpu.r[rd] = rotr(u32, value, 8 * (address & 1)); - } else { - // STRH - bus.write(u16, address, @truncate(u16, cpu.r[rd])); - } - } - }.inner; -} - -pub fn fmt11(comptime L: bool, comptime rd: u3) InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { - const offset = (opcode & 0xFF) << 2; - const address = cpu.r[13] + offset; - - if (L) { - // LDR - const value = bus.read(u32, address); - cpu.r[rd] = rotr(u32, value, 8 * (address & 0x3)); - } else { - // STR - bus.write(u32, address, cpu.r[rd]); - } - } - }.inner; -} diff --git a/src/core/cpu/thumb/software_interrupt.zig b/src/core/cpu/thumb/software_interrupt.zig deleted file mode 100644 index 893f902..0000000 --- a/src/core/cpu/thumb/software_interrupt.zig +++ /dev/null @@ -1,23 +0,0 @@ -const Bus = @import("../../Bus.zig"); -const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; -const InstrFn = @import("../../cpu.zig").thumb.InstrFn; - -pub fn fmt17() InstrFn { - return struct { - fn inner(cpu: *Arm7tdmi, _: *Bus, _: u16) void { - // Copy Values from Current Mode - const ret_addr = cpu.r[15] - 2; - const cpsr = cpu.cpsr.raw; - - // Switch Mode - cpu.changeMode(.Supervisor); - cpu.cpsr.t.write(false); // Force ARM Mode - cpu.cpsr.i.write(true); // Disable normal interrupts - - cpu.r[14] = ret_addr; // Resume Execution - cpu.spsr.raw = cpsr; // Previous mode CPSR - cpu.r[15] = 0x0000_0008; - cpu.pipe.reload(cpu); - } - }.inner; -} diff --git a/src/core/cpu_util.zig b/src/core/cpu_util.zig new file mode 100644 index 0000000..4e76fa9 --- /dev/null +++ b/src/core/cpu_util.zig @@ -0,0 +1,75 @@ +const std = @import("std"); + +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bank = @import("arm32").Arm7tdmi.Bank; +const Bus = @import("Bus.zig"); + +pub inline fn isHalted(cpu: *const Arm7tdmi) bool { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + + return bus_ptr.io.haltcnt == .Halt; +} + +pub fn stepDmaTransfer(cpu: *Arm7tdmi) bool { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + + inline for (0..4) |i| { + if (bus_ptr.dma[i].in_progress) { + bus_ptr.dma[i].step(cpu); + return true; + } + } + + return false; +} + +pub fn handleInterrupt(cpu: *Arm7tdmi) void { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + const should_handle = bus_ptr.io.ie.raw & bus_ptr.io.irq.raw; + + // Return if IME is disabled, CPSR I is set or there is nothing to handle + if (!bus_ptr.io.ime or cpu.cpsr.i.read() or should_handle == 0) return; + + // If Pipeline isn't full, we have a bug + std.debug.assert(cpu.pipe.isFull()); + + // log.debug("Handling Interrupt!", .{}); + bus_ptr.io.haltcnt = .Execute; + + // FIXME: This seems weird, but retAddr.gba suggests I need to make these changes + const ret_addr = cpu.r[15] - if (cpu.cpsr.t.read()) 0 else @as(u32, 4); + const new_spsr = cpu.cpsr.raw; + + cpu.changeMode(.Irq); + cpu.cpsr.t.write(false); + cpu.cpsr.i.write(true); + + cpu.r[14] = ret_addr; + cpu.spsr.raw = new_spsr; + cpu.r[15] = 0x0000_0018; + cpu.pipe.reload(cpu); +} + +/// Advances state so that the BIOS is skipped +/// +/// Note: This accesses the CPU's bus ptr so it only may be called +/// once the Bus has been properly initialized +/// +/// TODO: Make above notice impossible to do in code +pub fn fastBoot(cpu: *Arm7tdmi) void { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + cpu.r = std.mem.zeroes([16]u32); + + // cpu.r[0] = 0x08000000; + // cpu.r[1] = 0x000000EA; + cpu.r[13] = 0x0300_7F00; + cpu.r[15] = 0x0800_0000; + + cpu.bank.r[Bank.regIdx(.Irq, .R13)] = 0x0300_7FA0; + cpu.bank.r[Bank.regIdx(.Supervisor, .R13)] = 0x0300_7FE0; + + // cpu.cpsr.raw = 0x6000001F; + cpu.cpsr.raw = 0x0000_001F; + + bus_ptr.bios.addr_latch = 0x0000_00DC + 8; +} diff --git a/src/core/emu.zig b/src/core/emu.zig index 251c01f..46b8297 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -3,12 +3,16 @@ const SDL = @import("sdl2"); const config = @import("../config.zig"); const Scheduler = @import("scheduler.zig").Scheduler; -const Arm7tdmi = @import("cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("Bus.zig"); const Tracker = @import("../util.zig").FpsTracker; const Channel = @import("zba-util").Channel(Message, 0x100); pub const Message = enum { Pause, Resume, Quit }; +const stepDmaTransfer = @import("cpu_util.zig").stepDmaTransfer; +const isHalted = @import("cpu_util.zig").isHalted; + const Timer = std.time.Timer; /// 4 Cycles in 1 dot @@ -54,6 +58,8 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S log.info("FPS tracking enabled", .{}); } + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + var paused: bool = false; switch (kind) { @@ -69,7 +75,7 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S if (paused) continue; runFrame(scheduler, cpu); - audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full); + audioSync(audio_sync, bus_ptr.apu.stream, &bus_ptr.apu.is_buffer_full); if (kind == .UnlimitedFPS) tracker.?.tick(); } @@ -95,7 +101,7 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S // the amount of time needed for audio to catch up rather than // our expected wake-up time - audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full); + audioSync(audio_sync, bus_ptr.apu.stream, &bus_ptr.apu.is_buffer_full); if (!audio_sync) spinLoop(&timer, wake_time); wake_time = new_wake_time; @@ -109,8 +115,8 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void { const frame_end = sched.tick + cycles_per_frame; while (sched.tick < frame_end) { - if (!cpu.stepDmaTransfer()) { - if (cpu.isHalted()) { + if (!stepDmaTransfer(cpu)) { + if (isHalted(cpu)) { // Fast-forward to next Event sched.tick = sched.nextTimestamp(); } else { @@ -227,8 +233,8 @@ pub const EmuThing = struct { // TODO: How can I make it easier to keep this in lock-step with runFrame? while (!did_step) { - if (!cpu.stepDmaTransfer()) { - if (cpu.isHalted()) { + if (!stepDmaTransfer(cpu)) { + if (isHalted(cpu)) { // Fast-forward to next Event sched.tick = sched.queue.peek().?.tick; } else { @@ -250,14 +256,18 @@ pub fn reset(cpu: *Arm7tdmi) void { } pub fn replaceGamepak(cpu: *Arm7tdmi, file_path: []const u8) !void { - try cpu.bus.replaceGamepak(file_path); + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + + try bus_ptr.replaceGamepak(file_path); reset(cpu); } pub fn replaceBios(cpu: *Arm7tdmi, file_path: []const u8) !void { - const allocator = cpu.bus.bios.allocator; + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + + const allocator = bus_ptr.bios.allocator; const bios_len = 0x4000; - cpu.bus.bios.buf = try allocator.alloc(u8, bios_len); - try cpu.bus.bios.load(file_path); + bus_ptr.bios.buf = try allocator.alloc(u8, bios_len); + try bus_ptr.bios.load(file_path); } diff --git a/src/core/ppu.zig b/src/core/ppu.zig index 1dc4068..a4b2b9d 100644 --- a/src/core/ppu.zig +++ b/src/core/ppu.zig @@ -10,7 +10,8 @@ const Oam = @import("ppu/Oam.zig"); const Palette = @import("ppu/Palette.zig"); const Vram = @import("ppu/Vram.zig"); const Scheduler = @import("scheduler.zig").Scheduler; -const Arm7tdmi = @import("cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("Bus.zig"); const FrameBuffer = @import("../util.zig").FrameBuffer; const Allocator = std.mem.Allocator; @@ -20,6 +21,8 @@ const getHalf = util.getHalf; const setHalf = util.setHalf; const setQuart = util.setQuart; +const handleInterrupt = @import("cpu_util.zig").handleInterrupt; + pub const width = 240; pub const height = 160; pub const framebuf_pitch = width * @sizeOf(u32); @@ -1000,21 +1003,25 @@ pub const Ppu = struct { } pub fn onHdrawEnd(self: *Self, cpu: *Arm7tdmi, late: u64) void { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + // Transitioning to a Hblank if (self.dispstat.hblank_irq.read()) { - cpu.bus.io.irq.hblank.set(); - cpu.handleInterrupt(); + bus_ptr.io.irq.hblank.set(); + handleInterrupt(cpu); } // If we're not also in VBlank, attempt to run any pending DMA Reqs if (!self.dispstat.vblank.read()) - dma.onBlanking(cpu.bus, .HBlank); + dma.onBlanking(bus_ptr, .HBlank); self.dispstat.hblank.set(); self.sched.push(.HBlank, 68 * 4 -| late); } pub fn onHblankEnd(self: *Self, cpu: *Arm7tdmi, late: u64) void { + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + // The End of a Hblank (During Draw or Vblank) const old_scanline = self.vcount.scanline.read(); const scanline = (old_scanline + 1) % 228; @@ -1027,8 +1034,8 @@ pub const Ppu = struct { self.dispstat.coincidence.write(coincidence); if (coincidence and self.dispstat.vcount_irq.read()) { - cpu.bus.io.irq.coincidence.set(); - cpu.handleInterrupt(); + bus_ptr.io.irq.coincidence.set(); + handleInterrupt(cpu); } if (scanline < 160) { @@ -1042,15 +1049,15 @@ pub const Ppu = struct { self.dispstat.vblank.set(); if (self.dispstat.vblank_irq.read()) { - cpu.bus.io.irq.vblank.set(); - cpu.handleInterrupt(); + bus_ptr.io.irq.vblank.set(); + handleInterrupt(cpu); } self.aff_bg[0].latchRefPoints(); self.aff_bg[1].latchRefPoints(); // See if Vblank DMA is present and not enabled - dma.onBlanking(cpu.bus, .VBlank); + dma.onBlanking(bus_ptr, .VBlank); } if (scanline == 227) self.dispstat.vblank.unset(); diff --git a/src/core/scheduler.zig b/src/core/scheduler.zig index 86ebab1..2fc31bb 100644 --- a/src/core/scheduler.zig +++ b/src/core/scheduler.zig @@ -1,6 +1,7 @@ const std = @import("std"); -const Arm7tdmi = @import("cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("Bus.zig"); const Clock = @import("bus/gpio.zig").Clock; const Order = std.math.Order; @@ -45,6 +46,8 @@ pub const Scheduler = struct { const event = self.queue.remove(); const late = self.tick - event.tick; + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + switch (event.kind) { .HeatDeath => { log.err("u64 overflow. This *actually* should never happen.", .{}); @@ -52,33 +55,33 @@ pub const Scheduler = struct { }, .Draw => { // The end of a VDraw - cpu.bus.ppu.drawScanline(); - cpu.bus.ppu.onHdrawEnd(cpu, late); + bus_ptr.ppu.drawScanline(); + bus_ptr.ppu.onHdrawEnd(cpu, late); }, .TimerOverflow => |id| { switch (id) { - inline 0...3 => |idx| cpu.bus.tim[idx].onTimerExpire(cpu, late), + inline 0...3 => |idx| bus_ptr.tim[idx].onTimerExpire(cpu, late), } }, .ApuChannel => |id| { switch (id) { - 0 => cpu.bus.apu.ch1.onToneSweepEvent(late), - 1 => cpu.bus.apu.ch2.onToneEvent(late), - 2 => cpu.bus.apu.ch3.onWaveEvent(late), - 3 => cpu.bus.apu.ch4.onNoiseEvent(late), + 0 => bus_ptr.apu.ch1.onToneSweepEvent(late), + 1 => bus_ptr.apu.ch2.onToneEvent(late), + 2 => bus_ptr.apu.ch3.onWaveEvent(late), + 3 => bus_ptr.apu.ch4.onNoiseEvent(late), } }, .RealTimeClock => { - const device = &cpu.bus.pak.gpio.device; + const device = &bus_ptr.pak.gpio.device; if (device.kind != .Rtc or device.ptr == null) return; const clock = @ptrCast(*Clock, @alignCast(@alignOf(*Clock), device.ptr.?)); clock.onClockUpdate(late); }, - .FrameSequencer => cpu.bus.apu.onSequencerTick(late), - .SampleAudio => cpu.bus.apu.sampleAudio(late), - .HBlank => cpu.bus.ppu.onHblankEnd(cpu, late), // The end of a HBlank - .VBlank => cpu.bus.ppu.onHdrawEnd(cpu, late), // The end of a VBlank + .FrameSequencer => bus_ptr.apu.onSequencerTick(late), + .SampleAudio => bus_ptr.apu.sampleAudio(late), + .HBlank => bus_ptr.ppu.onHblankEnd(cpu, late), // The end of a HBlank + .VBlank => bus_ptr.ppu.onHdrawEnd(cpu, late), // The end of a VBlank } } diff --git a/src/imgui.zig b/src/imgui.zig index e5b5ea9..8bc8fcf 100644 --- a/src/imgui.zig +++ b/src/imgui.zig @@ -10,7 +10,10 @@ const config = @import("config.zig"); const emu = @import("core/emu.zig"); const Gui = @import("platform.zig").Gui; -const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Scheduler = @import("core/scheduler.zig").Scheduler; +const Bus = @import("core/Bus.zig"); + const RingBuffer = @import("zba-util").RingBuffer; const Dimensions = @import("platform.zig").Dimensions; @@ -69,6 +72,7 @@ pub const State = struct { pub fn draw(state: *State, win_dim: Dimensions, tex_id: GLuint, cpu: *Arm7tdmi) bool { const scn_scale = config.config().host.win_scale; + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); zgui.backend.newFrame(@intToFloat(f32, win_dim.width), @intToFloat(f32, win_dim.height)); @@ -100,7 +104,7 @@ pub fn draw(state: *State, win_dim: Dimensions, tex_id: GLuint, cpu: *Arm7tdmi) break :blk; }; - state.title = handleTitle(&cpu.bus.pak.title); + state.title = handleTitle(&bus_ptr.pak.title); state.emulation = .{ .Transition = .Active }; } @@ -240,8 +244,8 @@ pub fn draw(state: *State, win_dim: Dimensions, tex_id: GLuint, cpu: *Arm7tdmi) zgui.separator(); - widgets.interrupts(" IE", cpu.bus.io.ie); - widgets.interrupts("IRQ", cpu.bus.io.irq); + widgets.interrupts(" IE", bus_ptr.io.ie); + widgets.interrupts("IRQ", bus_ptr.io.irq); } if (state.win_stat.show_perf) { @@ -313,15 +317,16 @@ pub fn draw(state: *State, win_dim: Dimensions, tex_id: GLuint, cpu: *Arm7tdmi) const scheduler = cpu.sched; - zgui.text("tick: {X:0>16}", .{scheduler.tick}); + zgui.text("tick: {X:0>16}", .{scheduler.now()}); zgui.separator(); - const Event = std.meta.Child(@TypeOf(scheduler.queue.items)); + const sched_ptr = @ptrCast(*Scheduler, @alignCast(@alignOf(Scheduler), cpu.sched.ptr)); + const Event = std.meta.Child(@TypeOf(sched_ptr.queue.items)); var items: [20]Event = undefined; - const len = scheduler.queue.len; + const len = sched_ptr.queue.len; - @memcpy(&items, scheduler.queue.items); + @memcpy(&items, sched_ptr.queue.items); std.mem.sort(Event, items[0..len], {}, widgets.eventDesc(Event)); for (items[0..len]) |event| { @@ -441,7 +446,7 @@ const widgets = struct { } fn psr(comptime label: []const u8, register: anytype) void { - const Mode = @import("core/cpu.zig").Mode; + const Mode = @import("arm32").arm.Mode; const maybe_mode = std.meta.intToEnum(Mode, register.mode.read()) catch null; const mode = if (maybe_mode) |mode| mode.toString() else "???"; diff --git a/src/main.zig b/src/main.zig index 09702c4..7548c6b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,12 +9,15 @@ const emu = @import("core/emu.zig"); const Channel = @import("zba-util").Channel(emu.Message, 0x100); const Gui = @import("platform.zig").Gui; const Bus = @import("core/Bus.zig"); -const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi; const Scheduler = @import("core/scheduler.zig").Scheduler; const FilePaths = @import("util.zig").FilePaths; const FpsTracker = @import("util.zig").FpsTracker; const Allocator = std.mem.Allocator; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const IBus = @import("arm32").Bus; +const IScheduler = @import("arm32").Scheduler; + const log = std.log.scoped(.Cli); pub const log_level = if (builtin.mode != .Debug) .info else std.log.default_level; @@ -81,13 +84,17 @@ pub fn main() void { defer scheduler.deinit(); var bus: Bus = undefined; - var cpu = Arm7tdmi.init(&scheduler, &bus, log_file); + + var ischeduler = IScheduler.init(&scheduler); + var ibus = IBus.init(&bus); + + var cpu = Arm7tdmi.init(ischeduler, ibus); bus.init(allocator, &scheduler, &cpu, paths) catch |e| exitln("failed to init zba bus: {}", .{e}); defer bus.deinit(); if (config.config().guest.skip_bios or result.args.skip != 0 or paths.bios == null) { - cpu.fastBoot(); + @import("core/cpu_util.zig").fastBoot(&cpu); } const title_ptr = if (paths.rom != null) &bus.pak.title else null; diff --git a/src/platform.zig b/src/platform.zig index 792cb87..284c1a8 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -8,7 +8,8 @@ const config = @import("config.zig"); const imgui = @import("imgui.zig"); const Apu = @import("core/apu.zig").Apu; -const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; +const Bus = @import("core/Bus.zig"); const Scheduler = @import("core/scheduler.zig").Scheduler; const FpsTracker = @import("util.zig").FpsTracker; const Channel = @import("zba-util").Channel(emu.Message, 0x100); @@ -107,10 +108,12 @@ pub const Gui = struct { const tracker = opt.tracker; const ch = opt.ch; + const bus_ptr = @ptrCast(*Bus, @alignCast(@alignOf(Bus), cpu.bus.ptr)); + const objects = opengl_impl.createObjects(); defer gl.deleteBuffers(3, @as(*const [3]GLuint, &.{ objects.vao, objects.vbo, objects.ebo })); - const emu_tex = opengl_impl.createScreenTexture(cpu.bus.ppu.framebuf.get(.Renderer)); + const emu_tex = opengl_impl.createScreenTexture(bus_ptr.ppu.framebuf.get(.Renderer)); const out_tex = opengl_impl.createOutputTexture(); defer gl.deleteTextures(2, &[_]GLuint{ emu_tex, out_tex }); @@ -153,7 +156,7 @@ pub const Gui = struct { else => {}, } - cpu.bus.io.keyinput.fetchAnd(~keyinput.raw, .Monotonic); + bus_ptr.io.keyinput.fetchAnd(~keyinput.raw, .Monotonic); }, SDL.SDL_KEYUP => { // TODO: Make use of compare_and_xor? @@ -174,7 +177,7 @@ pub const Gui = struct { else => {}, } - cpu.bus.io.keyinput.fetchOr(keyinput.raw, .Monotonic); + bus_ptr.io.keyinput.fetchOr(keyinput.raw, .Monotonic); }, SDL.SDL_WINDOWEVENT => { if (event.window.event == SDL.SDL_WINDOWEVENT_RESIZED) { @@ -246,7 +249,7 @@ pub const Gui = struct { gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_id); defer gl.bindFramebuffer(gl.FRAMEBUFFER, 0); - const buf = cpu.bus.ppu.framebuf.get(.Renderer); + const buf = bus_ptr.ppu.framebuf.get(.Renderer); gl.viewport(0, 0, gba_width, gba_height); opengl_impl.drawScreenTexture(emu_tex, prog_id, objects, buf); } diff --git a/src/util.zig b/src/util.zig index 498c481..7a566f3 100644 --- a/src/util.zig +++ b/src/util.zig @@ -3,7 +3,7 @@ const builtin = @import("builtin"); const config = @import("config.zig"); const Log2Int = std.math.Log2Int; -const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi; +const Arm7tdmi = @import("arm32").Arm7tdmi; const Allocator = std.mem.Allocator;