From aa100de581f853ea3631986dfb7adce7f157c43c Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 27 Jul 2022 14:49:55 -0300 Subject: [PATCH 01/20] feat: reimplement cpu logging --- src/util.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.zig b/src/util.zig index 2275451..36f3882 100644 --- a/src/util.zig +++ b/src/util.zig @@ -215,7 +215,7 @@ pub const Logger = struct { cpu.r[12], cpu.r[13], cpu.r[14], - cpu.r[15], + cpu.r[15] - 4, cpu.cpsr.raw, opcode, }; From 62162ba49254e94d06b39760ef8e5245555736e2 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 28 Jul 2022 21:03:32 -0300 Subject: [PATCH 02/20] feat: resolve off-by-{word, halfword} errors when printing debug info --- src/util.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.zig b/src/util.zig index 36f3882..7b40392 100644 --- a/src/util.zig +++ b/src/util.zig @@ -215,7 +215,7 @@ pub const Logger = struct { cpu.r[12], cpu.r[13], cpu.r[14], - cpu.r[15] - 4, + cpu.r[15] - if (cpu.cpsr.t.read()) 2 else @as(u32, 4), cpu.cpsr.raw, opcode, }; From b3ada64e64ee84e8f49e9648c8fa132af6118e06 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 29 Jun 2022 06:33:17 -0300 Subject: [PATCH 03/20] feat: implement basic pipeline passes arm.gba, thumb.gb and armwrestler, fails in actual games TODO: run FuzzARM debug specific titles --- src/core/cpu.zig | 80 ++++++++++++++++--- src/core/cpu/arm/block_data_transfer.zig | 18 +++-- src/core/cpu/arm/branch.zig | 13 ++- src/core/cpu/arm/data_processing.zig | 41 ++++------ .../cpu/arm/half_signed_data_transfer.zig | 16 +--- src/core/cpu/arm/single_data_transfer.zig | 25 +++--- src/core/cpu/arm/software_interrupt.zig | 5 +- src/core/cpu/barrel_shifter.zig | 8 +- src/core/cpu/thumb/block_data_transfer.zig | 11 ++- src/core/cpu/thumb/branch.zig | 26 +++--- src/core/cpu/thumb/data_processing.zig | 5 +- src/core/cpu/thumb/data_transfer.zig | 4 +- src/core/cpu/thumb/software_interrupt.zig | 5 +- 13 files changed, 158 insertions(+), 99 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index 80d0a1b..13ebe41 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -242,6 +242,7 @@ pub const Arm7tdmi = struct { const Self = @This(); r: [16]u32, + pipe: Pipline, sched: *Scheduler, bus: *Bus, cpsr: PSR, @@ -262,6 +263,7 @@ pub const Arm7tdmi = struct { pub fn init(sched: *Scheduler, bus: *Bus, log_file: ?std.fs.File) Self { return Self{ .r = [_]u32{0x00} ** 16, + .pipe = Pipline.init(), .sched = sched, .bus = bus, .cpsr = .{ .raw = 0x0000_001F }, @@ -321,8 +323,21 @@ pub const Arm7tdmi = struct { return self.bus.io.haltcnt == .Halt; } + pub fn setCpsrNoFlush(self: *Self, value: u32) void { + if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F)); + self.cpsr.raw = value; + } + pub fn setCpsr(self: *Self, value: u32) void { if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F)); + + const new: PSR = .{ .raw = value }; + if (self.cpsr.t.read() != new.t.read()) { + // If THUMB to ARM or ARM to THUMB, flush pipeline + self.r[15] &= if (new.t.read()) ~@as(u32, 1) else ~@as(u32, 3); + self.pipe.flush(); + } + self.cpsr.raw = value; } @@ -425,19 +440,22 @@ pub const Arm7tdmi = struct { } pub fn step(self: *Self) void { - if (self.cpsr.t.read()) { - const opcode = self.fetch(u16); + if (self.cpsr.t.read()) blk: { + const opcode = @truncate(u16, self.pipe.step(self, u16) orelse break :blk); if (self.logger) |*trace| trace.mgbaLog(self, opcode); thumb.lut[thumb.idx(opcode)](self, self.bus, opcode); - } else { - const opcode = self.fetch(u32); + } else blk: { + const opcode = self.pipe.step(self, u32) orelse break :blk; 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); } } + + if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); + self.pipe.flushed = false; } pub fn stepDmaTransfer(self: *Self) bool { @@ -481,8 +499,8 @@ pub const Arm7tdmi = struct { if (!self.bus.io.ime or self.cpsr.i.read()) return; // log.debug("An interrupt was Handled!", .{}); - // retAddr.gba says r15 on it's own is off by -04h in both ARM and THUMB mode - const r15 = self.r[15] + 4; + // FIXME: Is the return address ahead? + const r15 = self.r[15]; const cpsr = self.cpsr.raw; self.changeMode(.Irq); @@ -506,8 +524,12 @@ pub const Arm7tdmi = struct { return self.bus.read(T, self.r[15]); } - pub fn fakePC(self: *const Self) u32 { - return self.r[15] + 4; + fn debug_log(self: *const Self, file: *const File, opcode: u32) void { + if (self.binary_log) { + self.skyLog(file) catch unreachable; + } else { + self.mgbaLog(file, opcode) catch unreachable; + } } pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn { @@ -587,7 +609,7 @@ pub const Arm7tdmi = struct { const r12 = self.r[12]; const r13 = self.r[13]; const r14 = self.r[14]; - const r15 = self.r[15]; + const r15 = self.r[15] -| if (self.cpsr.t.read()) 2 else @as(u32, 4); const c_psr = self.cpsr.raw; @@ -595,7 +617,7 @@ pub const Arm7tdmi = struct { if (self.cpsr.t.read()) { if (opcode >> 11 == 0x1E) { // Instruction 1 of a BL Opcode, print in ARM mode - const other_half = self.bus.dbgRead(u16, self.r[15]); + const other_half = self.bus.debugRead(u16, self.r[15] - 2); const bl_opcode = @as(u32, opcode) << 16 | other_half; log_str = try std.fmt.bufPrint(&buf, arm_fmt, .{ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, c_psr, bl_opcode }); @@ -631,6 +653,44 @@ pub fn checkCond(cpsr: PSR, cond: u4) bool { }; } +const Pipline = struct { + const Self = @This(); + stage: [2]?u32, + flushed: bool, + + fn init() Self { + return .{ + .stage = [_]?u32{null} ** 2, + .flushed = false, + }; + } + + pub fn flush(self: *Self) void { + for (self.stage) |*opcode| opcode.* = null; + self.flushed = true; + } + + 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.bus.read(T, cpu.r[15]); + + return opcode; + } + + fn reload(self: *Self, cpu: *Arm7tdmi, comptime T: type) void { + comptime std.debug.assert(T == u32 or T == u16); + const inc = if (T == u32) 4 else 2; + + self.stage[0] = cpu.bus.read(T, cpu.r[15]); + self.stage[1] = cpu.bus.read(T, cpu.r[15] + inc); + cpu.r[15] += inc * 2; + } +}; + pub const PSR = extern union { mode: Bitfield(u32, 0, 5), t: Bit(u32, 5), diff --git a/src/core/cpu/arm/block_data_transfer.zig b/src/core/cpu/arm/block_data_transfer.zig index 637cc0a..6265e88 100644 --- a/src/core/cpu/arm/block_data_transfer.zig +++ b/src/core/cpu/arm/block_data_transfer.zig @@ -55,8 +55,10 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c if (L) { cpu.r[15] = bus.read(u32, und_addr); + cpu.pipe.flush(); } else { - bus.write(u32, und_addr, cpu.r[15] + 8); + // FIXME: Should r15 on write be +12 ahead? + bus.write(u32, und_addr, cpu.r[15] + 4); } cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40; @@ -86,17 +88,23 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c cpu.setUserModeRegister(i, bus.read(u32, address)); } else { const value = bus.read(u32, address); - cpu.r[i] = if (i == 0xF) value & 0xFFFF_FFFC else value; - if (S and i == 0xF) cpu.setCpsr(cpu.spsr.raw); + + cpu.r[i] = value; + if (i == 0xF) { + cpu.r[i] &= ~@as(u32, 3); // Align r15 + cpu.pipe.flush(); + + 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) 8 else @as(u32, 0)); // PC is already 4 ahead to make 12 + 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) 8 else @as(u32, 0)); + bus.write(u32, address, cpu.r[i] + if (i == 0xF) 4 else @as(u32, 0)); } } } diff --git a/src/core/cpu/arm/branch.zig b/src/core/cpu/arm/branch.zig index 6015eb3..d25e6ce 100644 --- a/src/core/cpu/arm/branch.zig +++ b/src/core/cpu/arm/branch.zig @@ -9,14 +9,19 @@ const sext = @import("../../../util.zig").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]; - cpu.r[15] = cpu.fakePC() +% (sext(u32, u24, opcode) << 2); + if (L) cpu.r[14] = cpu.r[15] - 4; + + cpu.r[15] +%= sext(u32, u24, opcode) << 2; + cpu.pipe.flush(); } }.inner; } pub fn branchAndExchange(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { const rn = opcode & 0xF; - cpu.cpsr.t.write(cpu.r[rn] & 1 == 1); - cpu.r[15] = cpu.r[rn] & 0xFFFF_FFFE; + + 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.flush(); } diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 2a3b77d..547e753 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -13,17 +13,12 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4 const old_carry = @boolToInt(cpu.cpsr.c.read()); // If certain conditions are met, PC is 12 ahead instead of 8 + // TODO: What are these conditions? I can't remember if (!I and opcode >> 4 & 1 == 1) cpu.r[15] += 4; + const op1 = cpu.r[rn]; - const op1 = if (rn == 0xF) cpu.fakePC() else cpu.r[rn]; - - var op2: u32 = undefined; - if (I) { - const amount = @truncate(u8, (opcode >> 8 & 0xF) << 1); - op2 = rotateRight(S, &cpu.cpsr, opcode & 0xFF, amount); - } else { - op2 = execute(S, cpu, opcode); - } + const amount = @truncate(u8, (opcode >> 8 & 0xF) << 1); + const op2 = if (I) rotateRight(S, &cpu.cpsr, opcode & 0xFF, amount) else execute(S, cpu, opcode); // Undo special condition from above if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; @@ -67,39 +62,31 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4 }, 0x8 => { // TST - if (rd == 0xF) { - undefinedTestBehaviour(cpu); - return; - } + if (rd == 0xF) + return undefinedTestBehaviour(cpu); const result = op1 & op2; setTestOpFlags(S, cpu, opcode, result); }, 0x9 => { // TEQ - if (rd == 0xF) { - undefinedTestBehaviour(cpu); - return; - } + if (rd == 0xF) + return undefinedTestBehaviour(cpu); const result = op1 ^ op2; setTestOpFlags(S, cpu, opcode, result); }, 0xA => { // CMP - if (rd == 0xF) { - undefinedTestBehaviour(cpu); - return; - } + if (rd == 0xF) + return undefinedTestBehaviour(cpu); cmp(cpu, op1, op2); }, 0xB => { // CMN - if (rd == 0xF) { - undefinedTestBehaviour(cpu); - return; - } + if (rd == 0xF) + return undefinedTestBehaviour(cpu); cmn(cpu, op1, op2); }, @@ -127,6 +114,8 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4 setArmLogicOpFlags(S, cpu, rd, result); }, } + + if (rd == 0xF) cpu.pipe.flush(); } }.inner; } @@ -280,5 +269,5 @@ fn setTestOpFlags(comptime S: bool, cpu: *Arm7tdmi, opcode: u32, result: u32) vo fn undefinedTestBehaviour(cpu: *Arm7tdmi) void { @setCold(true); - cpu.setCpsr(cpu.spsr.raw); + cpu.setCpsrNoFlush(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 index d681da0..2b12ec4 100644 --- a/src/core/cpu/arm/half_signed_data_transfer.zig +++ b/src/core/cpu/arm/half_signed_data_transfer.zig @@ -15,20 +15,8 @@ pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I: const rm = opcode & 0xF; const imm_offset_high = opcode >> 8 & 0xF; - var base: u32 = undefined; - if (rn == 0xF) { - base = cpu.fakePC(); - if (!L) base += 4; - } else { - base = cpu.r[rn]; - } - - var offset: u32 = undefined; - if (I) { - offset = imm_offset_high << 4 | rm; - } else { - offset = cpu.r[rm]; - } + 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; diff --git a/src/core/cpu/arm/single_data_transfer.zig b/src/core/cpu/arm/single_data_transfer.zig index 3a82b0c..7335b8e 100644 --- a/src/core/cpu/arm/single_data_transfer.zig +++ b/src/core/cpu/arm/single_data_transfer.zig @@ -14,13 +14,8 @@ pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, const rn = opcode >> 16 & 0xF; const rd = opcode >> 12 & 0xF; - var base: u32 = undefined; - if (rn == 0xF) { - base = cpu.fakePC(); - if (!L) base += 4; // Offset of 12 - } else { - base = cpu.r[rn]; - } + // rn is r15 and L is not set, the PC is 12 ahead + const base = cpu.r[rn] + if (!L and rn == 0xF) 4 else @as(u32, 0); const offset = if (I) shifter.immShift(false, cpu, opcode) else opcode & 0xFFF; @@ -40,18 +35,26 @@ pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, } else { if (B) { // STRB - const value = if (rd == 0xF) cpu.r[rd] + 8 else cpu.r[rd]; + 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 = if (rd == 0xF) cpu.r[rd] + 8 else cpu.r[rd]; + 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 (L) cpu.r[rd] = result; // This emulates the LDR rd == rn behaviour + if (W and P or !P) { + cpu.r[rn] = address; + if (rn == 0xF) cpu.pipe.flush(); + } + + if (L) { + // This emulates the LDR rd == rn behaviour + cpu.r[rd] = result; + if (rd == 0xF) cpu.pipe.flush(); + } } }.inner; } diff --git a/src/core/cpu/arm/software_interrupt.zig b/src/core/cpu/arm/software_interrupt.zig index 849a6eb..8dd1c0b 100644 --- a/src/core/cpu/arm/software_interrupt.zig +++ b/src/core/cpu/arm/software_interrupt.zig @@ -6,7 +6,7 @@ pub fn armSoftwareInterrupt() InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, _: u32) void { // Copy Values from Current Mode - const r15 = cpu.r[15]; + const ret_addr = cpu.r[15] - 4; const cpsr = cpu.cpsr.raw; // Switch Mode @@ -14,9 +14,10 @@ pub fn armSoftwareInterrupt() InstrFn { cpu.cpsr.t.write(false); // Force ARM Mode cpu.cpsr.i.write(true); // Disable normal interrupts - cpu.r[14] = r15; // Resume Execution + cpu.r[14] = ret_addr; // Resume Execution cpu.spsr.raw = cpsr; // Previous mode CPSR cpu.r[15] = 0x0000_0008; + cpu.pipe.flush(); } }.inner; } diff --git a/src/core/cpu/barrel_shifter.zig b/src/core/cpu/barrel_shifter.zig index 32afff0..71cb236 100644 --- a/src/core/cpu/barrel_shifter.zig +++ b/src/core/cpu/barrel_shifter.zig @@ -18,11 +18,9 @@ pub fn execute(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { fn registerShift(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]); - const rm_idx = opcode & 0xF; - const rm = if (rm_idx == 0xF) cpu.fakePC() else cpu.r[rm_idx]; - return switch (@truncate(u2, opcode >> 5)) { 0b00 => logicalLeft(S, &cpu.cpsr, rm, rs), 0b01 => logicalRight(S, &cpu.cpsr, rm, rs), @@ -33,9 +31,7 @@ fn registerShift(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { pub fn immShift(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { const amount = @truncate(u8, opcode >> 7 & 0x1F); - - const rm_idx = opcode & 0xF; - const rm = if (rm_idx == 0xF) cpu.fakePC() else cpu.r[rm_idx]; + const rm = cpu.r[opcode & 0xF]; var result: u32 = undefined; if (amount == 0) { diff --git a/src/core/cpu/thumb/block_data_transfer.zig b/src/core/cpu/thumb/block_data_transfer.zig index c2df7e6..25173cd 100644 --- a/src/core/cpu/thumb/block_data_transfer.zig +++ b/src/core/cpu/thumb/block_data_transfer.zig @@ -33,7 +33,8 @@ pub fn fmt14(comptime L: bool, comptime R: bool) InstrFn { if (R) { if (L) { const value = bus.read(u32, address); - cpu.r[15] = value & 0xFFFF_FFFE; + cpu.r[15] = value & ~@as(u32, 1); + cpu.pipe.flush(); } else { bus.write(u32, address, cpu.r[14]); } @@ -52,7 +53,13 @@ pub fn fmt15(comptime L: bool, comptime rb: u3) InstrFn { const end_address = cpu.r[rb] + 4 * countRlist(opcode); if (opcode & 0xFF == 0) { - if (L) cpu.r[15] = bus.read(u32, address) else bus.write(u32, address, cpu.r[15] + 4); + if (L) { + cpu.r[15] = bus.read(u32, address); + cpu.pipe.flush(); + } else { + bus.write(u32, address, cpu.r[15] + 2); + } + cpu.r[rb] += 0x40; return; } diff --git a/src/core/cpu/thumb/branch.zig b/src/core/cpu/thumb/branch.zig index 3425f3c..45be2f1 100644 --- a/src/core/cpu/thumb/branch.zig +++ b/src/core/cpu/thumb/branch.zig @@ -9,16 +9,13 @@ pub fn fmt16(comptime cond: u4) InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { // B - const offset = sext(u32, u8, opcode & 0xFF) << 1; + if (cond == 0xE or cond == 0xF) + cpu.panic("[CPU/THUMB.16] Undefined conditional branch with condition {}", .{cond}); - const should_execute = switch (cond) { - 0xE, 0xF => cpu.panic("[CPU/THUMB.16] Undefined conditional branch with condition {}", .{cond}), - else => checkCond(cpu.cpsr, cond), - }; + if (!checkCond(cpu.cpsr, cond)) return; - if (should_execute) { - cpu.r[15] = (cpu.r[15] + 2) +% offset; - } + cpu.r[15] +%= sext(u32, u8, opcode & 0xFF) << 1; + cpu.pipe.flush(); } }.inner; } @@ -27,8 +24,8 @@ pub fn fmt18() InstrFn { return struct { // B but conditional fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const offset = sext(u32, u11, opcode & 0x7FF) << 1; - cpu.r[15] = (cpu.r[15] + 2) +% offset; + cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1; + cpu.pipe.flush(); } }.inner; } @@ -41,13 +38,16 @@ pub fn fmt19(comptime is_low: bool) InstrFn { if (is_low) { // Instruction 2 - const old_pc = cpu.r[15]; + const next_opcode = cpu.r[15] - 2; cpu.r[15] = cpu.r[14] +% (offset << 1); - cpu.r[14] = old_pc | 1; + cpu.r[14] = next_opcode | 1; + + cpu.pipe.flush(); } else { // Instruction 1 - cpu.r[14] = (cpu.r[15] + 2) +% (sext(u32, u11, offset) << 12); + 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 index d352147..d449f39 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -133,10 +133,9 @@ 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] + 2) & 0xFFFF_FFFD; + const left = if (isSP) cpu.r[13] else cpu.r[15] & ~@as(u32, 2); const right = (opcode & 0xFF) << 2; - const result = left + right; - cpu.r[rd] = result; + cpu.r[rd] = left + right; } }.inner; } diff --git a/src/core/cpu/thumb/data_transfer.zig b/src/core/cpu/thumb/data_transfer.zig index 8dda707..5e76291 100644 --- a/src/core/cpu/thumb/data_transfer.zig +++ b/src/core/cpu/thumb/data_transfer.zig @@ -12,7 +12,9 @@ pub fn fmt6(comptime rd: u3) InstrFn { fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { // LDR const offset = (opcode & 0xFF) << 2; - cpu.r[rd] = bus.read(u32, (cpu.r[15] + 2 & 0xFFFF_FFFD) + offset); + + // Bit 1 of the PC intentionally ignored + cpu.r[rd] = bus.read(u32, (cpu.r[15] & ~@as(u32, 2)) + offset); } }.inner; } diff --git a/src/core/cpu/thumb/software_interrupt.zig b/src/core/cpu/thumb/software_interrupt.zig index abb69ba..d80957a 100644 --- a/src/core/cpu/thumb/software_interrupt.zig +++ b/src/core/cpu/thumb/software_interrupt.zig @@ -6,7 +6,7 @@ pub fn fmt17() InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, _: u16) void { // Copy Values from Current Mode - const r15 = cpu.r[15]; + const ret_addr = cpu.r[15] - 2; const cpsr = cpu.cpsr.raw; // Switch Mode @@ -14,9 +14,10 @@ pub fn fmt17() InstrFn { cpu.cpsr.t.write(false); // Force ARM Mode cpu.cpsr.i.write(true); // Disable normal interrupts - cpu.r[14] = r15; // Resume Execution + cpu.r[14] = ret_addr; // Resume Execution cpu.spsr.raw = cpsr; // Previous mode CPSR cpu.r[15] = 0x0000_0008; + cpu.pipe.flush(); } }.inner; } From 2799c3f2028a5c53811f3e08cd90cdfa817bb07a Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 13 Jul 2022 05:10:58 -0300 Subject: [PATCH 04/20] fix: reimpl handleInterrupt code --- src/core/cpu.zig | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index 13ebe41..4b0920f 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -490,27 +490,26 @@ pub const Arm7tdmi = struct { pub fn handleInterrupt(self: *Self) void { const should_handle = self.bus.io.ie.raw & self.bus.io.irq.raw; - if (should_handle != 0) { - self.bus.io.haltcnt = .Execute; - // log.debug("An Interrupt was Fired!", .{}); + // 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; - // Either IME is not true or I in CPSR is true - // Don't handle interrupts - if (!self.bus.io.ime or self.cpsr.i.read()) return; - // log.debug("An interrupt was Handled!", .{}); + // If pipeline isn't full, return but reschedule the handling of the event + if (!self.pipe.isFull()) return; - // FIXME: Is the return address ahead? - const r15 = self.r[15]; - const cpsr = self.cpsr.raw; + // log.debug("Handling Interrupt!", .{}); + self.bus.io.haltcnt = .Execute; - self.changeMode(.Irq); - self.cpsr.t.write(false); - self.cpsr.i.write(true); + const ret_addr = self.r[15] - if (self.cpsr.t.read()) 2 else @as(u32, 4); + const new_spsr = self.cpsr.raw; - self.r[14] = r15; - self.spsr.raw = cpsr; - self.r[15] = 0x000_0018; - } + 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.flush(); } inline fn fetch(self: *Self, comptime T: type) T { @@ -670,6 +669,10 @@ const Pipline = struct { self.flushed = true; } + 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); From 72a63eeb98b3b05c626e82e6990a20c9e56e6294 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 18 Aug 2022 19:51:46 -0300 Subject: [PATCH 05/20] chore: instantly refill the pipeline on flush I believe this to be necessary in order to get hardware interrupts working. thumb.gba test 108 fails but I'm committing anyways (despite the regression) because this is kind of rebase/merge hell and I have something that at least sort of works rn --- src/core/cpu.zig | 30 +- src/core/cpu/arm/block_data_transfer.zig | 4 +- src/core/cpu/arm/branch.zig | 5 +- src/core/cpu/arm/data_processing.zig | 320 ++++++++++++++++----- src/core/cpu/arm/single_data_transfer.zig | 4 +- src/core/cpu/arm/software_interrupt.zig | 2 +- src/core/cpu/thumb/block_data_transfer.zig | 4 +- src/core/cpu/thumb/branch.zig | 6 +- src/core/cpu/thumb/data_processing.zig | 12 +- src/core/cpu/thumb/software_interrupt.zig | 2 +- src/util.zig | 1 + 11 files changed, 298 insertions(+), 92 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index 4b0920f..08e5334 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -335,7 +335,7 @@ pub const Arm7tdmi = struct { if (self.cpsr.t.read() != new.t.read()) { // If THUMB to ARM or ARM to THUMB, flush pipeline self.r[15] &= if (new.t.read()) ~@as(u32, 1) else ~@as(u32, 3); - self.pipe.flush(); + if (new.t.read()) self.pipe.reload(u16, self) else self.pipe.reload(u32, self); } self.cpsr.raw = value; @@ -428,15 +428,16 @@ pub const Arm7tdmi = struct { pub fn fastBoot(self: *Self) void { self.r = std.mem.zeroes([16]u32); - self.r[0] = 0x08000000; - self.r[1] = 0x000000EA; + // self.r[0] = 0x08000000; + // self.r[1] = 0x000000EA; self.r[13] = 0x0300_7F00; self.r[15] = 0x0800_0000; self.banked_r[bankedIdx(.Irq, .R13)] = 0x0300_7FA0; self.banked_r[bankedIdx(.Supervisor, .R13)] = 0x0300_7FE0; - self.cpsr.raw = 0x6000001F; + // self.cpsr.raw = 0x6000001F; + self.cpsr.raw = 0x0000_001F; } pub fn step(self: *Self) void { @@ -454,7 +455,8 @@ pub const Arm7tdmi = struct { } } - if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); + if (self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); + self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); self.pipe.flushed = false; } @@ -509,7 +511,7 @@ pub const Arm7tdmi = struct { self.r[14] = ret_addr; self.spsr.raw = new_spsr; self.r[15] = 0x0000_0018; - self.pipe.flush(); + self.pipe.reload(u32, self); } inline fn fetch(self: *Self, comptime T: type) T { @@ -667,6 +669,10 @@ const Pipline = struct { pub fn flush(self: *Self) void { for (self.stage) |*opcode| opcode.* = null; self.flushed = true; + + // Note: If using this, add + // if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); + // to the end of Arm7tdmi.step } pub fn isFull(self: *const Self) bool { @@ -684,13 +690,17 @@ const Pipline = struct { return opcode; } - fn reload(self: *Self, cpu: *Arm7tdmi, comptime T: type) void { + pub fn reload(self: *Self, comptime T: type, cpu: *Arm7tdmi) void { comptime std.debug.assert(T == u32 or T == u16); - const inc = if (T == u32) 4 else 2; + + // Sometimes, the pipeline can be reloaded twice in the same instruction + // This can happen if: + // 1. R15 is written to + // 2. The CPSR is written to (and T changes), so R15 is written to again self.stage[0] = cpu.bus.read(T, cpu.r[15]); - self.stage[1] = cpu.bus.read(T, cpu.r[15] + inc); - cpu.r[15] += inc * 2; + self.stage[1] = cpu.bus.read(T, cpu.r[15] + if (T == u32) 4 else @as(u32, 2)); + self.flushed = true; } }; diff --git a/src/core/cpu/arm/block_data_transfer.zig b/src/core/cpu/arm/block_data_transfer.zig index 6265e88..32aafba 100644 --- a/src/core/cpu/arm/block_data_transfer.zig +++ b/src/core/cpu/arm/block_data_transfer.zig @@ -55,7 +55,7 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c if (L) { cpu.r[15] = bus.read(u32, und_addr); - cpu.pipe.flush(); + cpu.pipe.reload(u32, cpu); } else { // FIXME: Should r15 on write be +12 ahead? bus.write(u32, und_addr, cpu.r[15] + 4); @@ -92,7 +92,7 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c cpu.r[i] = value; if (i == 0xF) { cpu.r[i] &= ~@as(u32, 3); // Align r15 - cpu.pipe.flush(); + cpu.pipe.reload(u32, cpu); if (S) cpu.setCpsr(cpu.spsr.raw); } diff --git a/src/core/cpu/arm/branch.zig b/src/core/cpu/arm/branch.zig index d25e6ce..368735a 100644 --- a/src/core/cpu/arm/branch.zig +++ b/src/core/cpu/arm/branch.zig @@ -12,7 +12,7 @@ pub fn branch(comptime L: bool) InstrFn { if (L) cpu.r[14] = cpu.r[15] - 4; cpu.r[15] +%= sext(u32, u24, opcode) << 2; - cpu.pipe.flush(); + cpu.pipe.reload(u32, cpu); } }.inner; } @@ -22,6 +22,7 @@ pub fn branchAndExchange(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { 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.flush(); + if (thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); } diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 547e753..e2630b9 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -5,7 +5,7 @@ const InstrFn = @import("../../cpu.zig").arm.InstrFn; const rotateRight = @import("../barrel_shifter.zig").rotateRight; const execute = @import("../barrel_shifter.zig").execute; -pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4) InstrFn { +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); @@ -13,7 +13,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4 const old_carry = @boolToInt(cpu.cpsr.c.read()); // If certain conditions are met, PC is 12 ahead instead of 8 - // TODO: What are these conditions? I can't remember + // TODO: Why these conditions? if (!I and opcode >> 4 & 1 == 1) cpu.r[15] += 4; const op1 = cpu.r[rn]; @@ -23,103 +23,266 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4 // Undo special condition from above if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; - switch (instrKind) { - 0x0 => { - // AND - const result = op1 & op2; - cpu.r[rd] = result; - setArmLogicOpFlags(S, cpu, rd, result); - }, - 0x1 => { - // EOR - const result = op1 ^ op2; - cpu.r[rd] = result; - setArmLogicOpFlags(S, cpu, rd, result); - }, - 0x2 => { - // SUB - cpu.r[rd] = armSub(S, cpu, rd, op1, op2); - }, - 0x3 => { - // RSB - cpu.r[rd] = armSub(S, cpu, rd, op2, op1); - }, - 0x4 => { - // ADD - cpu.r[rd] = armAdd(S, cpu, rd, op1, op2); - }, - 0x5 => { - // ADC - cpu.r[rd] = armAdc(S, cpu, rd, op1, op2, old_carry); - }, - 0x6 => { - // SBC - cpu.r[rd] = armSbc(S, cpu, rd, op1, op2, old_carry); - }, - 0x7 => { - // RSC - cpu.r[rd] = armSbc(S, cpu, rd, op2, op1, old_carry); - }, + var result: u32 = undefined; + var didOverflow: bool = 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 = newAdd(&didOverflow, op1, op2), // ADD + 0x5 => result = newAdc(&didOverflow, op1, op2, old_carry), // ADC + 0x6 => result = newSbc(op1, op2, old_carry), // SBC + 0x7 => result = newSbc(op2, op1, old_carry), // RSC 0x8 => { // TST if (rd == 0xF) return undefinedTestBehaviour(cpu); - const result = op1 & op2; - setTestOpFlags(S, cpu, opcode, result); + result = op1 & op2; }, 0x9 => { // TEQ if (rd == 0xF) return undefinedTestBehaviour(cpu); - const result = op1 ^ op2; - setTestOpFlags(S, cpu, opcode, result); + result = op1 ^ op2; }, 0xA => { // CMP if (rd == 0xF) return undefinedTestBehaviour(cpu); - cmp(cpu, op1, op2); + result = op1 -% op2; }, 0xB => { // CMN if (rd == 0xF) return undefinedTestBehaviour(cpu); - cmn(cpu, op1, op2); + didOverflow = @addWithOverflow(u32, op1, op2, &result); }, - 0xC => { - // ORR - const result = op1 | op2; + 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; - setArmLogicOpFlags(S, cpu, rd, result); - }, - 0xD => { - // MOV - cpu.r[rd] = op2; - setArmLogicOpFlags(S, cpu, rd, op2); - }, - 0xE => { - // BIC - const result = op1 & ~op2; - cpu.r[rd] = result; - setArmLogicOpFlags(S, cpu, rd, result); - }, - 0xF => { - // MVN - const result = ~op2; - cpu.r[rd] = result; - setArmLogicOpFlags(S, cpu, rd, result); + if (rd == 0xF) cpu.pipe.reload(u32, cpu); }, } - if (rd == 0xF) cpu.pipe.flush(); + // Write Flags + switch (kind) { + 0x0, 0x1, 0xC, 0xD, 0xE, 0xF => { + // Logic Operation Flags + if (S) { + if (rd == 0xF) { + cpu.setCpsr(cpu.spsr.raw); + } else { + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + // C set by Barrel Shifter, V is unaffected + } + } + }, + 0x2, 0x3 => { + // SUB, RSB Flags + if (S) { + 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); + } + + if (rd == 0xF) cpu.setCpsr(cpu.spsr.raw); + } + }, + 0x4, 0x5 => { + // ADD, ADC Flags + if (S) { + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(didOverflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); + + if (rd == 0xF) cpu.setCpsr(cpu.spsr.raw); + } + }, + 0x6, 0x7 => { + // SBC, RSC Flags + if (S) { + 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); + } + + if (rd == 0xF) cpu.setCpsr(cpu.spsr.raw); + } + }, + 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(didOverflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); + } else { + // TEST, TEQ specific + // Barrel Shifter should always calc CPSR C in TST + if (!S) _ = execute(true, cpu, opcode); + } + }, + } } }.inner; } +// pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: 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: What are these conditions? I can't remember +// 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) rotateRight(S, &cpu.cpsr, opcode & 0xFF, amount) else execute(S, cpu, opcode); + +// // Undo special condition from above +// if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; + +// switch (instrKind) { +// 0x0 => { +// // AND +// const result = op1 & op2; +// cpu.r[rd] = result; +// setArmLogicOpFlags(S, cpu, rd, result); +// }, +// 0x1 => { +// // EOR +// const result = op1 ^ op2; +// cpu.r[rd] = result; +// setArmLogicOpFlags(S, cpu, rd, result); +// }, +// 0x2 => { +// // SUB +// cpu.r[rd] = armSub(S, cpu, rd, op1, op2); +// }, +// 0x3 => { +// // RSB +// cpu.r[rd] = armSub(S, cpu, rd, op2, op1); +// }, +// 0x4 => { +// // ADD +// cpu.r[rd] = armAdd(S, cpu, rd, op1, op2); +// }, +// 0x5 => { +// // ADC +// cpu.r[rd] = armAdc(S, cpu, rd, op1, op2, old_carry); +// }, +// 0x6 => { +// // SBC +// cpu.r[rd] = armSbc(S, cpu, rd, op1, op2, old_carry); +// }, +// 0x7 => { +// // RSC +// cpu.r[rd] = armSbc(S, cpu, rd, op2, op1, old_carry); +// }, +// 0x8 => { +// // TST +// if (rd == 0xF) +// return undefinedTestBehaviour(cpu); + +// const result = op1 & op2; +// setTestOpFlags(S, cpu, opcode, result); +// }, +// 0x9 => { +// // TEQ +// if (rd == 0xF) +// return undefinedTestBehaviour(cpu); + +// const result = op1 ^ op2; +// setTestOpFlags(S, cpu, opcode, result); +// }, +// 0xA => { +// // CMP +// if (rd == 0xF) +// return undefinedTestBehaviour(cpu); + +// cmp(cpu, op1, op2); +// }, +// 0xB => { +// // CMN +// if (rd == 0xF) +// return undefinedTestBehaviour(cpu); + +// cmn(cpu, op1, op2); +// }, +// 0xC => { +// // ORR +// const result = op1 | op2; +// cpu.r[rd] = result; +// setArmLogicOpFlags(S, cpu, rd, result); +// }, +// 0xD => { +// // MOV +// cpu.r[rd] = op2; +// setArmLogicOpFlags(S, cpu, rd, op2); +// }, +// 0xE => { +// // BIC +// const result = op1 & ~op2; +// cpu.r[rd] = result; +// setArmLogicOpFlags(S, cpu, rd, result); +// }, +// 0xF => { +// // MVN +// const result = ~op2; +// cpu.r[rd] = result; +// setArmLogicOpFlags(S, cpu, rd, result); +// }, +// } + +// if (rd == 0xF) cpu.pipe.reload(u32, cpu); +// } +// }.inner; +// } + fn armSbc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_carry: u1) u32 { var result: u32 = undefined; if (S and rd == 0xF) { @@ -132,6 +295,14 @@ fn armSbc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_c return result; } +fn newSbc(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 sbc(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32, old_carry: u1) u32 { // TODO: Make your own version (thanks peach.bot) const subtrahend = @as(u64, right) -% old_carry +% 1; @@ -184,6 +355,12 @@ fn armAdd(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32) u32 { return result; } +fn newAdd(didOverflow: *bool, left: u32, right: u32) u32 { + var ret: u32 = undefined; + didOverflow.* = @addWithOverflow(u32, left, right, &ret); + return ret; +} + pub fn add(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { var result: u32 = undefined; const didOverflow = @addWithOverflow(u32, left, right, &result); @@ -210,6 +387,15 @@ fn armAdc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_c return result; } +fn newAdc(didOverflow: *bool, left: u32, right: u32, old_carry: u1) u32 { + var ret: u32 = undefined; + const did = @addWithOverflow(u32, left, right, &ret); + const overflow = @addWithOverflow(u32, ret, old_carry, &ret); + + didOverflow.* = did or overflow; + return ret; +} + pub fn adc(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32, old_carry: u1) u32 { var result: u32 = undefined; const did = @addWithOverflow(u32, left, right, &result); diff --git a/src/core/cpu/arm/single_data_transfer.zig b/src/core/cpu/arm/single_data_transfer.zig index 7335b8e..091e151 100644 --- a/src/core/cpu/arm/single_data_transfer.zig +++ b/src/core/cpu/arm/single_data_transfer.zig @@ -47,13 +47,13 @@ pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, address = modified_base; if (W and P or !P) { cpu.r[rn] = address; - if (rn == 0xF) cpu.pipe.flush(); + if (rn == 0xF) cpu.pipe.reload(u32, cpu); } if (L) { // This emulates the LDR rd == rn behaviour cpu.r[rd] = result; - if (rd == 0xF) cpu.pipe.flush(); + if (rd == 0xF) cpu.pipe.reload(u32, cpu); } } }.inner; diff --git a/src/core/cpu/arm/software_interrupt.zig b/src/core/cpu/arm/software_interrupt.zig index 8dd1c0b..50f6674 100644 --- a/src/core/cpu/arm/software_interrupt.zig +++ b/src/core/cpu/arm/software_interrupt.zig @@ -17,7 +17,7 @@ pub fn armSoftwareInterrupt() InstrFn { cpu.r[14] = ret_addr; // Resume Execution cpu.spsr.raw = cpsr; // Previous mode CPSR cpu.r[15] = 0x0000_0008; - cpu.pipe.flush(); + cpu.pipe.reload(u32, cpu); } }.inner; } diff --git a/src/core/cpu/thumb/block_data_transfer.zig b/src/core/cpu/thumb/block_data_transfer.zig index 25173cd..2f3b2a3 100644 --- a/src/core/cpu/thumb/block_data_transfer.zig +++ b/src/core/cpu/thumb/block_data_transfer.zig @@ -34,7 +34,7 @@ pub fn fmt14(comptime L: bool, comptime R: bool) InstrFn { if (L) { const value = bus.read(u32, address); cpu.r[15] = value & ~@as(u32, 1); - cpu.pipe.flush(); + cpu.pipe.reload(u16, cpu); } else { bus.write(u32, address, cpu.r[14]); } @@ -55,7 +55,7 @@ pub fn fmt15(comptime L: bool, comptime rb: u3) InstrFn { if (opcode & 0xFF == 0) { if (L) { cpu.r[15] = bus.read(u32, address); - cpu.pipe.flush(); + cpu.pipe.reload(u16, cpu); } else { bus.write(u32, address, cpu.r[15] + 2); } diff --git a/src/core/cpu/thumb/branch.zig b/src/core/cpu/thumb/branch.zig index 45be2f1..4c8a076 100644 --- a/src/core/cpu/thumb/branch.zig +++ b/src/core/cpu/thumb/branch.zig @@ -15,7 +15,7 @@ pub fn fmt16(comptime cond: u4) InstrFn { if (!checkCond(cpu.cpsr, cond)) return; cpu.r[15] +%= sext(u32, u8, opcode & 0xFF) << 1; - cpu.pipe.flush(); + cpu.pipe.reload(u16, cpu); } }.inner; } @@ -25,7 +25,7 @@ pub fn fmt18() InstrFn { // B but conditional fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1; - cpu.pipe.flush(); + cpu.pipe.reload(u16, cpu); } }.inner; } @@ -43,7 +43,7 @@ pub fn fmt19(comptime is_low: bool) InstrFn { cpu.r[15] = cpu.r[14] +% (offset << 1); cpu.r[14] = next_opcode | 1; - cpu.pipe.flush(); + cpu.pipe.reload(u16, cpu); } else { // Instruction 1 const lr_offset = sext(u32, u11, offset) << 12; diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig index d449f39..7c9d0bd 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -77,10 +77,18 @@ pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn { }, 0b11 => { // BX - cpu.cpsr.t.write(src & 1 == 1); - cpu.r[15] = src & 0xFFFF_FFFE; + const thumb = src & 1 == 1; + cpu.r[15] = src & ~@as(u32, 1); + cpu.cpsr.t.write(thumb); + + if (thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); + + // Pipeline alrady flushed + return; // FIXME: Is this necessary? (Refactor out?) }, } + + if (dst_idx == 0xF) cpu.pipe.reload(u16, cpu); } }.inner; } diff --git a/src/core/cpu/thumb/software_interrupt.zig b/src/core/cpu/thumb/software_interrupt.zig index d80957a..96806d2 100644 --- a/src/core/cpu/thumb/software_interrupt.zig +++ b/src/core/cpu/thumb/software_interrupt.zig @@ -17,7 +17,7 @@ pub fn fmt17() InstrFn { cpu.r[14] = ret_addr; // Resume Execution cpu.spsr.raw = cpsr; // Previous mode CPSR cpu.r[15] = 0x0000_0008; - cpu.pipe.flush(); + cpu.pipe.reload(u32, cpu); } }.inner; } diff --git a/src/util.zig b/src/util.zig index 7b40392..cca1297 100644 --- a/src/util.zig +++ b/src/util.zig @@ -176,6 +176,7 @@ pub const Logger = struct { pub fn print(self: *Self, comptime format: []const u8, args: anytype) !void { try self.buf.writer().print(format, args); + try self.buf.flush(); // FIXME: On panics, whatever is in the buffer isn't written to file } pub fn mgbaLog(self: *Self, cpu: *const Arm7tdmi, opcode: u32) void { From 1f9eeedfe8a1b76335069c5a8932174337617fdc Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 26 Aug 2022 19:23:50 -0500 Subject: [PATCH 06/20] fix: impl workaround for stage2 miscompilation --- src/core/cpu.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index 08e5334..e54a5fa 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -682,7 +682,8 @@ const Pipline = struct { 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]; + // FIXME: https://github.com/ziglang/zig/issues/12642 + const opcode = self.stage[0..1][0]; self.stage[0] = self.stage[1]; self.stage[1] = cpu.bus.read(T, cpu.r[15]); From 3cf1bf54e90fb96c605049eb5fe9875b4078c2d3 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 26 Aug 2022 20:44:26 -0500 Subject: [PATCH 07/20] fix: reimpl THUMB.5 instructions pipeline branch now passes arm.gba and thumb.gba again (TODO: Stop rewriting my commits away) --- src/core/cpu/thumb/data_processing.zig | 31 +++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig index 7c9d0bd..ebc897a 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -10,8 +10,6 @@ const sub = @import("../arm/data_processing.zig").sub; const cmp = @import("../arm/data_processing.zig").cmp; const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; -const log = std.log.scoped(.Thumb1); - pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { @@ -58,37 +56,38 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const src_idx = @as(u4, h2) << 3 | (opcode >> 3 & 0x7); - const dst_idx = @as(u4, h1) << 3 | (opcode & 0x7); + const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7); + const rd = @as(u4, h1) << 3 | (opcode & 0x7); - const src = if (src_idx == 0xF) (cpu.r[src_idx] + 2) & 0xFFFF_FFFE else cpu.r[src_idx]; - const dst = if (dst_idx == 0xF) (cpu.r[dst_idx] + 2) & 0xFFFF_FFFE else cpu.r[dst_idx]; + const rs_value = if (rs == 0xF) cpu.r[rs] & ~@as(u32, 1) else cpu.r[rs]; + const rd_value = if (rd == 0xF) cpu.r[rd] & ~@as(u32, 1) else cpu.r[rd]; switch (op) { 0b00 => { // ADD - const sum = add(false, cpu, dst, src); - cpu.r[dst_idx] = if (dst_idx == 0xF) sum & 0xFFFF_FFFE else sum; + const sum = add(false, cpu, rd_value, rs_value); + cpu.r[rd] = if (rd == 0xF) sum & ~@as(u32, 1) else sum; }, - 0b01 => cmp(cpu, dst, src), // CMP + 0b01 => cmp(cpu, rd_value, rs_value), // CMP 0b10 => { // MOV - cpu.r[dst_idx] = if (dst_idx == 0xF) src & 0xFFFF_FFFE else src; + cpu.r[rd] = if (rd == 0xF) rs_value & ~@as(u32, 1) else rs_value; }, 0b11 => { // BX - const thumb = src & 1 == 1; - cpu.r[15] = src & ~@as(u32, 1); - cpu.cpsr.t.write(thumb); + const thumb = rs_value & 1 == 1; + cpu.r[15] = rs_value & ~@as(u32, 1); + cpu.cpsr.t.write(thumb); if (thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); - // Pipeline alrady flushed - return; // FIXME: Is this necessary? (Refactor out?) + // TODO: We shouldn't need to worry about the if statement + // below, because in BX, rd SBZ (and H1 is guaranteed to be 0) + return; }, } - if (dst_idx == 0xF) cpu.pipe.reload(u16, cpu); + if (rd == 0xF) cpu.pipe.reload(u16, cpu); } }.inner; } From 48b81c8e7ac48b96a9f133c51ce8c5b33bc3bd93 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 26 Aug 2022 21:45:35 -0500 Subject: [PATCH 08/20] chore: dump pipeline state on cpu panic --- src/core/cpu.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index e54a5fa..092dc20 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -547,6 +547,8 @@ pub const Arm7tdmi = struct { std.debug.print("spsr: 0x{X:0>8} ", .{self.spsr.raw}); prettyPrintPsr(&self.spsr); + 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); From e4451738b5d69529b665a61ccc741f4e01ea477f Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 26 Aug 2022 23:59:59 -0500 Subject: [PATCH 09/20] fix: advance r15, even when the pipeline is reloaded from the scheduler The PC would fall behind whenever an IRQ was called because the pipeline was reloaded (+8 to PC), however that was never actually done by any code Now, the PC is always incremented when the pipeline is reloaded --- src/core/cpu.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index 092dc20..d77b68d 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -455,8 +455,7 @@ pub const Arm7tdmi = struct { } } - if (self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); - self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); + if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); self.pipe.flushed = false; } @@ -703,6 +702,8 @@ const Pipline = struct { self.stage[0] = cpu.bus.read(T, cpu.r[15]); self.stage[1] = cpu.bus.read(T, cpu.r[15] + if (T == u32) 4 else @as(u32, 2)); + + cpu.r[15] += if (T == u32) 8 else @as(u32, 4); self.flushed = true; } }; From 014180cbd025e9e7a31cdd45fc884154652a091d Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 9 Sep 2022 05:37:13 -0300 Subject: [PATCH 10/20] chore: update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b19c46a..3633d0c 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ An in-progress Game Boy Advance Emulator written in Zig ⚡! ## Tests -- [ ] [jsmolka's GBA Test Collection](https://github.com/jsmolka/gba-tests) +- [x] [jsmolka's GBA Test Collection](https://github.com/jsmolka/gba-tests) - [x] `arm.gba` and `thumb.gba` - [x] `flash64.gba`, `flash128.gba`, `none.gba`, and `sram.gba` - [x] `hello.gba`, `shades.gba`, and `stripes.gba` - [x] `memory.gba` - [x] `bios.gba` - - [ ] `nes.gba` + - [x] `nes.gba` - [ ] [DenSinH's GBA ROMs](https://github.com/DenSinH/GBARoms) - [x] `eeprom-test` and `flash-test` - [x] `midikey2freq` From a948c6f9008536932d603ab8d567ab923dee0830 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 10:01:52 -0300 Subject: [PATCH 11/20] chore: don't write to CPSR + swap with SPSR at the same time --- src/core/cpu/arm/data_processing.zig | 91 ++++++++++++---------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index e2630b9..30ed279 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -75,73 +75,60 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins 0x8, 0x9, 0xA, 0xB => {}, // Test Operations else => { cpu.r[rd] = result; - if (rd == 0xF) cpu.pipe.reload(u32, cpu); + if (rd == 0xF) { + if (S) cpu.setCpsr(cpu.spsr.raw); + + cpu.pipe.reload(u32, cpu); + } }, } // Write Flags switch (kind) { - 0x0, 0x1, 0xC, 0xD, 0xE, 0xF => { + 0x0, 0x1, 0xC, 0xD, 0xE, 0xF => if (S and rd != 0xF) { // Logic Operation Flags - if (S) { - if (rd == 0xF) { - cpu.setCpsr(cpu.spsr.raw); - } else { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // C set by Barrel Shifter, V is unaffected - } - } + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + // C set by Barrel Shifter, V is unaffected + }, - 0x2, 0x3 => { + 0x2, 0x3 => if (S and rd != 0xF) { // SUB, RSB Flags - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); + 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); - } - - if (rd == 0xF) cpu.setCpsr(cpu.spsr.raw); + 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 => { + 0x4, 0x5 => if (S and rd != 0xF) { // ADD, ADC Flags - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); - cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - - if (rd == 0xF) cpu.setCpsr(cpu.spsr.raw); - } + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(didOverflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); }, - 0x6, 0x7 => { + 0x6, 0x7 => if (S and rd != 0xF) { // SBC, RSC Flags - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); + 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); - } - - if (rd == 0xF) cpu.setCpsr(cpu.spsr.raw); + 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 => { From a3996cbc58a7ac40a551562d6889af89109e1654 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 10:09:00 -0300 Subject: [PATCH 12/20] fix: don't flush pipeline when reloading CPSR in ARM Data Processing --- src/core/cpu/arm/data_processing.zig | 230 +-------------------------- src/core/cpu/thumb/alu.zig | 153 ++++++++---------- 2 files changed, 74 insertions(+), 309 deletions(-) diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 30ed279..5540d28 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -34,8 +34,8 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins 0x3 => result = op2 -% op1, // RSB 0x4 => result = newAdd(&didOverflow, op1, op2), // ADD 0x5 => result = newAdc(&didOverflow, op1, op2, old_carry), // ADC - 0x6 => result = newSbc(op1, op2, old_carry), // SBC - 0x7 => result = newSbc(op2, op1, old_carry), // RSC + 0x6 => result = sbc(op1, op2, old_carry), // SBC + 0x7 => result = sbc(op2, op1, old_carry), // RSC 0x8 => { // TST if (rd == 0xF) @@ -76,7 +76,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins else => { cpu.r[rd] = result; if (rd == 0xF) { - if (S) cpu.setCpsr(cpu.spsr.raw); + if (S) cpu.setCpsrNoFlush(cpu.spsr.raw); cpu.pipe.reload(u32, cpu); } @@ -145,7 +145,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins cpu.cpsr.c.write(didOverflow); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); } else { - // TEST, TEQ specific + // TST, TEQ specific // Barrel Shifter should always calc CPSR C in TST if (!S) _ = execute(true, cpu, opcode); } @@ -155,134 +155,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins }.inner; } -// pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: 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: What are these conditions? I can't remember -// 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) rotateRight(S, &cpu.cpsr, opcode & 0xFF, amount) else execute(S, cpu, opcode); - -// // Undo special condition from above -// if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; - -// switch (instrKind) { -// 0x0 => { -// // AND -// const result = op1 & op2; -// cpu.r[rd] = result; -// setArmLogicOpFlags(S, cpu, rd, result); -// }, -// 0x1 => { -// // EOR -// const result = op1 ^ op2; -// cpu.r[rd] = result; -// setArmLogicOpFlags(S, cpu, rd, result); -// }, -// 0x2 => { -// // SUB -// cpu.r[rd] = armSub(S, cpu, rd, op1, op2); -// }, -// 0x3 => { -// // RSB -// cpu.r[rd] = armSub(S, cpu, rd, op2, op1); -// }, -// 0x4 => { -// // ADD -// cpu.r[rd] = armAdd(S, cpu, rd, op1, op2); -// }, -// 0x5 => { -// // ADC -// cpu.r[rd] = armAdc(S, cpu, rd, op1, op2, old_carry); -// }, -// 0x6 => { -// // SBC -// cpu.r[rd] = armSbc(S, cpu, rd, op1, op2, old_carry); -// }, -// 0x7 => { -// // RSC -// cpu.r[rd] = armSbc(S, cpu, rd, op2, op1, old_carry); -// }, -// 0x8 => { -// // TST -// if (rd == 0xF) -// return undefinedTestBehaviour(cpu); - -// const result = op1 & op2; -// setTestOpFlags(S, cpu, opcode, result); -// }, -// 0x9 => { -// // TEQ -// if (rd == 0xF) -// return undefinedTestBehaviour(cpu); - -// const result = op1 ^ op2; -// setTestOpFlags(S, cpu, opcode, result); -// }, -// 0xA => { -// // CMP -// if (rd == 0xF) -// return undefinedTestBehaviour(cpu); - -// cmp(cpu, op1, op2); -// }, -// 0xB => { -// // CMN -// if (rd == 0xF) -// return undefinedTestBehaviour(cpu); - -// cmn(cpu, op1, op2); -// }, -// 0xC => { -// // ORR -// const result = op1 | op2; -// cpu.r[rd] = result; -// setArmLogicOpFlags(S, cpu, rd, result); -// }, -// 0xD => { -// // MOV -// cpu.r[rd] = op2; -// setArmLogicOpFlags(S, cpu, rd, op2); -// }, -// 0xE => { -// // BIC -// const result = op1 & ~op2; -// cpu.r[rd] = result; -// setArmLogicOpFlags(S, cpu, rd, result); -// }, -// 0xF => { -// // MVN -// const result = ~op2; -// cpu.r[rd] = result; -// setArmLogicOpFlags(S, cpu, rd, result); -// }, -// } - -// if (rd == 0xF) cpu.pipe.reload(u32, cpu); -// } -// }.inner; -// } - -fn armSbc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_carry: u1) u32 { - var result: u32 = undefined; - if (S and rd == 0xF) { - result = sbc(false, cpu, left, right, old_carry); - cpu.setCpsr(cpu.spsr.raw); - } else { - result = sbc(S, cpu, left, right, old_carry); - } - - return result; -} - -fn newSbc(left: u32, right: u32, old_carry: u1) u32 { +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); @@ -290,33 +163,6 @@ fn newSbc(left: u32, right: u32, old_carry: u1) u32 { return ret; } -pub fn sbc(comptime S: bool, cpu: *Arm7tdmi, 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 result = @truncate(u32, left -% subtrahend); - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(subtrahend <= left); - cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1); - } - - return result; -} - -fn armSub(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32) u32 { - var result: u32 = undefined; - if (S and rd == 0xF) { - result = sub(false, cpu, left, right); - cpu.setCpsr(cpu.spsr.raw); - } else { - result = sub(S, cpu, left, right); - } - - return result; -} - pub fn sub(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { const result = left -% right; @@ -330,18 +176,6 @@ pub fn sub(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { return result; } -fn armAdd(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32) u32 { - var result: u32 = undefined; - if (S and rd == 0xF) { - result = add(false, cpu, left, right); - cpu.setCpsr(cpu.spsr.raw); - } else { - result = add(S, cpu, left, right); - } - - return result; -} - fn newAdd(didOverflow: *bool, left: u32, right: u32) u32 { var ret: u32 = undefined; didOverflow.* = @addWithOverflow(u32, left, right, &ret); @@ -362,19 +196,7 @@ pub fn add(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { return result; } -fn armAdc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_carry: u1) u32 { - var result: u32 = undefined; - if (S and rd == 0xF) { - result = adc(false, cpu, left, right, old_carry); - cpu.setCpsr(cpu.spsr.raw); - } else { - result = adc(S, cpu, left, right, old_carry); - } - - return result; -} - -fn newAdc(didOverflow: *bool, left: u32, right: u32, old_carry: u1) u32 { +pub fn newAdc(didOverflow: *bool, left: u32, right: u32, old_carry: u1) u32 { var ret: u32 = undefined; const did = @addWithOverflow(u32, left, right, &ret); const overflow = @addWithOverflow(u32, ret, old_carry, &ret); @@ -383,21 +205,6 @@ fn newAdc(didOverflow: *bool, left: u32, right: u32, old_carry: u1) u32 { return ret; } -pub fn adc(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32, old_carry: u1) u32 { - var result: u32 = undefined; - const did = @addWithOverflow(u32, left, right, &result); - const overflow = @addWithOverflow(u32, result, old_carry, &result); - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(did or overflow); - cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1); - } - - return result; -} - pub fn cmp(cpu: *Arm7tdmi, left: u32, right: u32) void { const result = left -% right; @@ -407,24 +214,6 @@ pub fn cmp(cpu: *Arm7tdmi, left: u32, right: u32) void { cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1); } -pub fn cmn(cpu: *Arm7tdmi, left: u32, right: u32) void { - var result: u32 = undefined; - const didOverflow = @addWithOverflow(u32, left, right, &result); - - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); - cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1); -} - -fn setArmLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, rd: u4, result: u32) void { - if (S and rd == 0xF) { - cpu.setCpsr(cpu.spsr.raw); - } else { - setLogicOpFlags(S, cpu, result); - } -} - pub fn setLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, result: u32) void { if (S) { cpu.cpsr.n.write(result >> 31 & 1 == 1); @@ -433,13 +222,6 @@ pub fn setLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, result: u32) void { } } -fn setTestOpFlags(comptime S: bool, cpu: *Arm7tdmi, opcode: u32, result: u32) void { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // Barrel Shifter should always calc CPSR C in TST - if (!S) _ = execute(true, cpu, opcode); -} - fn undefinedTestBehaviour(cpu: *Arm7tdmi) void { @setCold(true); cpu.setCpsrNoFlush(cpu.spsr.raw); diff --git a/src/core/cpu/thumb/alu.zig b/src/core/cpu/thumb/alu.zig index 0113860..d16ae5d 100644 --- a/src/core/cpu/thumb/alu.zig +++ b/src/core/cpu/thumb/alu.zig @@ -2,18 +2,13 @@ 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 adc = @import("../arm/data_processing.zig").newAdc; const sbc = @import("../arm/data_processing.zig").sbc; -const sub = @import("../arm/data_processing.zig").sub; -const cmp = @import("../arm/data_processing.zig").cmp; -const cmn = @import("../arm/data_processing.zig").cmn; -const setTestOpFlags = @import("../arm/data_processing.zig").setTestOpFlags; -const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; -const logicalLeft = @import("../barrel_shifter.zig").logicalLeft; -const logicalRight = @import("../barrel_shifter.zig").logicalRight; -const arithmeticRight = @import("../barrel_shifter.zig").arithmeticRight; -const rotateRight = @import("../barrel_shifter.zig").rotateRight; +const lsl = @import("../barrel_shifter.zig").logicalLeft; +const lsr = @import("../barrel_shifter.zig").logicalRight; +const asr = @import("../barrel_shifter.zig").arithmeticRight; +const ror = @import("../barrel_shifter.zig").rotateRight; pub fn fmt4(comptime op: u4) InstrFn { return struct { @@ -22,96 +17,84 @@ pub fn fmt4(comptime op: u4) InstrFn { const rd = opcode & 0x7; const carry = @boolToInt(cpu.cpsr.c.read()); + var result: u32 = undefined; + var didOverflow: bool = undefined; switch (op) { - 0x0 => { - // AND - const result = cpu.r[rd] & cpu.r[rs]; - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); + 0x0 => result = cpu.r[rd] & cpu.r[rs], // AND + 0x1 => result = cpu.r[rd] ^ cpu.r[rs], // EOR + 0x2 => result = lsl(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // LSL + 0x3 => result = lsr(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // LSR + 0x4 => result = asr(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // ASR + 0x5 => result = adc(&didOverflow, cpu.r[rd], cpu.r[rs], carry), // ADC + 0x6 => result = sbc(cpu.r[rd], cpu.r[rs], carry), // SBC + 0x7 => result = ror(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // ROR + 0x8 => result = cpu.r[rd] & cpu.r[rs], // TST + 0x9 => result = 0 -% cpu.r[rs], // NEG + 0xA => result = cpu.r[rd] -% cpu.r[rs], // CMP + 0xB => didOverflow = @addWithOverflow(u32, cpu.r[rd], cpu.r[rs], &result), // CMN + 0xC => result = cpu.r[rd] | cpu.r[rs], // ORR + 0xD => result = @truncate(u32, @as(u64, cpu.r[rs]) * @as(u64, cpu.r[rd])), + 0xE => result = cpu.r[rd] & ~cpu.r[rs], + 0xF => result = ~cpu.r[rs], + } + + // 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 }, - 0x1 => { - // EOR - const result = cpu.r[rd] ^ cpu.r[rs]; - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); + 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(cpu.r[rs] <= cpu.r[rd]); + cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + } }, - 0x2 => { - // LSL - const result = logicalLeft(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])); - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); - }, - 0x3 => { - // LSR - const result = logicalRight(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])); - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); - }, - 0x4 => { - // ASR - const result = arithmeticRight(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])); - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); - }, - 0x5 => { - // ADC - cpu.r[rd] = adc(true, cpu, cpu.r[rd], cpu.r[rs], carry); + 0x5, 0xB => { + // ADC, CMN + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(didOverflow); + cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (cpu.r[rs] ^ result)) >> 31 & 1 == 1); + + // FIXME: Pretty sure CMN Is the same }, 0x6 => { // SBC - cpu.r[rd] = sbc(true, cpu, cpu.r[rd], cpu.r[rs], carry); - }, - 0x7 => { - // ROR - const result = rotateRight(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])); - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); - }, - 0x8 => { - // TST - const result = cpu.r[rd] & cpu.r[rs]; - setLogicOpFlags(true, cpu, result); + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + + const subtrahend = @as(u64, cpu.r[rs]) -% carry +% 1; + cpu.cpsr.c.write(subtrahend <= cpu.r[rd]); + cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); }, 0x9 => { // NEG - cpu.r[rd] = sub(true, cpu, 0, cpu.r[rs]); - }, - 0xA => { - // CMP - cmp(cpu, cpu.r[rd], cpu.r[rs]); - }, - 0xB => { - // CMN - cmn(cpu, cpu.r[rd], cpu.r[rs]); - }, - 0xC => { - // ORR - const result = cpu.r[rd] | cpu.r[rs]; - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); + cpu.cpsr.c.write(cpu.r[rs] <= 0); + cpu.cpsr.v.write(((0 ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); }, 0xD => { - // MUL - const temp = @as(u64, cpu.r[rs]) * @as(u64, cpu.r[rd]); - const result = @truncate(u32, temp); - cpu.r[rd] = result; - + // 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 }, - 0xE => { - // BIC - const result = cpu.r[rd] & ~cpu.r[rs]; - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); - }, - 0xF => { - // MVN - const result = ~cpu.r[rs]; - cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); - }, } } }.inner; From 5bb5bdf3896b20a80b82ff08c6ad2b7fe645dadc Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 12:46:32 -0300 Subject: [PATCH 13/20] chore: refactor ARM/THUMB data processing instructions --- src/core/cpu/arm/data_processing.zig | 68 +++--------- src/core/cpu/thumb/alu.zig | 57 +++++----- src/core/cpu/thumb/data_processing.zig | 137 ++++++++++++++++--------- 3 files changed, 132 insertions(+), 130 deletions(-) diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 5540d28..604921c 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -24,7 +24,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4; var result: u32 = undefined; - var didOverflow: bool = undefined; + var overflow: bool = undefined; // Perform Data Processing Logic switch (kind) { @@ -32,8 +32,8 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins 0x1 => result = op1 ^ op2, // EOR 0x2 => result = op1 -% op2, // SUB 0x3 => result = op2 -% op1, // RSB - 0x4 => result = newAdd(&didOverflow, op1, op2), // ADD - 0x5 => result = newAdc(&didOverflow, op1, op2, old_carry), // ADC + 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 => { @@ -62,7 +62,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins if (rd == 0xF) return undefinedTestBehaviour(cpu); - didOverflow = @addWithOverflow(u32, op1, op2, &result); + overflow = @addWithOverflow(u32, op1, op2, &result); }, 0xC => result = op1 | op2, // ORR 0xD => result = op2, // MOV @@ -111,7 +111,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins // ADD, ADC Flags cpu.cpsr.n.write(result >> 31 & 1 == 1); cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); + cpu.cpsr.c.write(overflow); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); }, 0x6, 0x7 => if (S and rd != 0xF) { @@ -142,7 +142,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); } else if (kind == 0xB) { // CMN specific - cpu.cpsr.c.write(didOverflow); + cpu.cpsr.c.write(overflow); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); } else { // TST, TEQ specific @@ -163,65 +163,21 @@ pub fn sbc(left: u32, right: u32, old_carry: u1) u32 { return ret; } -pub fn sub(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { - const result = left -% right; - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(right <= left); - cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1); - } - - return result; -} - -fn newAdd(didOverflow: *bool, left: u32, right: u32) u32 { +pub fn add(overflow: *bool, left: u32, right: u32) u32 { var ret: u32 = undefined; - didOverflow.* = @addWithOverflow(u32, left, right, &ret); + overflow.* = @addWithOverflow(u32, left, right, &ret); return ret; } -pub fn add(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 { - var result: u32 = undefined; - const didOverflow = @addWithOverflow(u32, left, right, &result); - - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(didOverflow); - cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1); - } - - return result; -} - -pub fn newAdc(didOverflow: *bool, left: u32, right: u32, old_carry: u1) u32 { +pub fn adc(overflow: *bool, left: u32, right: u32, old_carry: u1) u32 { var ret: u32 = undefined; - const did = @addWithOverflow(u32, left, right, &ret); - const overflow = @addWithOverflow(u32, ret, old_carry, &ret); + const first = @addWithOverflow(u32, left, right, &ret); + const second = @addWithOverflow(u32, ret, old_carry, &ret); - didOverflow.* = did or overflow; + overflow.* = first or second; return ret; } -pub fn cmp(cpu: *Arm7tdmi, left: u32, right: u32) void { - const result = left -% right; - - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - cpu.cpsr.c.write(right <= left); - cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1); -} - -pub fn setLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, result: u32) void { - if (S) { - cpu.cpsr.n.write(result >> 31 & 1 == 1); - cpu.cpsr.z.write(result == 0); - // C set by Barrel Shifter, V is unaffected - } -} - fn undefinedTestBehaviour(cpu: *Arm7tdmi) void { @setCold(true); cpu.setCpsrNoFlush(cpu.spsr.raw); diff --git a/src/core/cpu/thumb/alu.zig b/src/core/cpu/thumb/alu.zig index d16ae5d..a05b26b 100644 --- a/src/core/cpu/thumb/alu.zig +++ b/src/core/cpu/thumb/alu.zig @@ -2,7 +2,7 @@ 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").newAdc; +const adc = @import("../arm/data_processing.zig").adc; const sbc = @import("../arm/data_processing.zig").sbc; const lsl = @import("../barrel_shifter.zig").logicalLeft; @@ -17,25 +17,28 @@ pub fn fmt4(comptime op: u4) InstrFn { 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 didOverflow: bool = undefined; + var overflow: bool = undefined; switch (op) { - 0x0 => result = cpu.r[rd] & cpu.r[rs], // AND - 0x1 => result = cpu.r[rd] ^ cpu.r[rs], // EOR - 0x2 => result = lsl(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // LSL - 0x3 => result = lsr(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // LSR - 0x4 => result = asr(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // ASR - 0x5 => result = adc(&didOverflow, cpu.r[rd], cpu.r[rs], carry), // ADC - 0x6 => result = sbc(cpu.r[rd], cpu.r[rs], carry), // SBC - 0x7 => result = ror(true, &cpu.cpsr, cpu.r[rd], @truncate(u8, cpu.r[rs])), // ROR - 0x8 => result = cpu.r[rd] & cpu.r[rs], // TST - 0x9 => result = 0 -% cpu.r[rs], // NEG - 0xA => result = cpu.r[rd] -% cpu.r[rs], // CMP - 0xB => didOverflow = @addWithOverflow(u32, cpu.r[rd], cpu.r[rs], &result), // CMN - 0xC => result = cpu.r[rd] | cpu.r[rs], // ORR - 0xD => result = @truncate(u32, @as(u64, cpu.r[rs]) * @as(u64, cpu.r[rd])), - 0xE => result = cpu.r[rd] & ~cpu.r[rs], - 0xF => result = ~cpu.r[rs], + 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 => overflow = @addWithOverflow(u32, op1, op2, &result), // CMN + 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 @@ -60,16 +63,16 @@ pub fn fmt4(comptime op: u4) InstrFn { if (op == 0xA) { // CMP specific - cpu.cpsr.c.write(cpu.r[rs] <= cpu.r[rd]); - cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + 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(didOverflow); - cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (cpu.r[rs] ^ result)) >> 31 & 1 == 1); + cpu.cpsr.c.write(overflow); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); // FIXME: Pretty sure CMN Is the same }, @@ -78,16 +81,16 @@ pub fn fmt4(comptime op: u4) InstrFn { cpu.cpsr.n.write(result >> 31 & 1 == 1); cpu.cpsr.z.write(result == 0); - const subtrahend = @as(u64, cpu.r[rs]) -% carry +% 1; - cpu.cpsr.c.write(subtrahend <= cpu.r[rd]); - cpu.cpsr.v.write(((cpu.r[rd] ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + 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(cpu.r[rs] <= 0); - cpu.cpsr.v.write(((0 ^ result) & (~cpu.r[rs] ^ result)) >> 31 & 1 == 1); + cpu.cpsr.c.write(op2 <= 0); + cpu.cpsr.v.write(((0 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1); }, 0xD => { // Multiplication diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig index ebc897a..e0e6c50 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -3,12 +3,12 @@ const std = @import("std"); const Bus = @import("../../Bus.zig"); const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const InstrFn = @import("../../cpu.zig").thumb.InstrFn; -const shifter = @import("../barrel_shifter.zig"); const add = @import("../arm/data_processing.zig").add; -const sub = @import("../arm/data_processing.zig").sub; -const cmp = @import("../arm/data_processing.zig").cmp; -const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; + +const lsl = @import("../barrel_shifter.zig").logicalLeft; +const lsr = @import("../barrel_shifter.zig").logicalRight; +const asr = @import("../barrel_shifter.zig").arithmeticRight; pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { return struct { @@ -22,7 +22,7 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { if (offset == 0) { break :blk cpu.r[rs]; } else { - break :blk shifter.logicalLeft(true, &cpu.cpsr, cpu.r[rs], offset); + break :blk lsl(true, &cpu.cpsr, cpu.r[rs], offset); } }, 0b01 => blk: { @@ -31,7 +31,7 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); break :blk @as(u32, 0); } else { - break :blk shifter.logicalRight(true, &cpu.cpsr, cpu.r[rs], offset); + break :blk lsr(true, &cpu.cpsr, cpu.r[rs], offset); } }, 0b10 => blk: { @@ -40,7 +40,7 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); break :blk @bitCast(u32, @bitCast(i32, cpu.r[rs]) >> 31); } else { - break :blk shifter.arithmeticRight(true, &cpu.cpsr, cpu.r[rs], offset); + 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}), @@ -48,7 +48,10 @@ pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn { // Equivalent to an ARM MOVS cpu.r[rd] = result; - setLogicOpFlags(true, cpu, result); + + // Write Flags + cpu.cpsr.n.write(result >> 31 & 1 == 1); + cpu.cpsr.z.write(result == 0); } }.inner; } @@ -59,35 +62,49 @@ pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn { const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7); const rd = @as(u4, h1) << 3 | (opcode & 0x7); - const rs_value = if (rs == 0xF) cpu.r[rs] & ~@as(u32, 1) else cpu.r[rs]; - const rd_value = if (rd == 0xF) cpu.r[rd] & ~@as(u32, 1) else cpu.r[rd]; + const op1 = cpu.r[rd]; + const op2 = cpu.r[rs]; + var result: u32 = undefined; + var overflow: bool = undefined; switch (op) { - 0b00 => { - // ADD - const sum = add(false, cpu, rd_value, rs_value); - cpu.r[rd] = if (rd == 0xF) sum & ~@as(u32, 1) else sum; - }, - 0b01 => cmp(cpu, rd_value, rs_value), // CMP - 0b10 => { - // MOV - cpu.r[rd] = if (rd == 0xF) rs_value & ~@as(u32, 1) else rs_value; - }, + 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 thumb = rs_value & 1 == 1; - cpu.r[15] = rs_value & ~@as(u32, 1); + const is_thumb = op2 & 1 == 1; + cpu.r[15] = op2 & ~@as(u32, 1); - cpu.cpsr.t.write(thumb); - if (thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); - - // TODO: We shouldn't need to worry about the if statement - // below, because in BX, rd SBZ (and H1 is guaranteed to be 0) - return; + cpu.cpsr.t.write(is_thumb); + if (is_thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); + }, + else => { + cpu.r[rd] = result; + if (rd == 0xF) { + cpu.r[15] &= ~@as(u32, 1); + cpu.pipe.reload(u16, cpu); + } }, } - if (rd == 0xF) cpu.pipe.reload(u16, 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; } @@ -97,21 +114,28 @@ pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { 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 - cpu.r[rd] = if (I) blk: { - break :blk sub(true, cpu, cpu.r[rs], rn); - } else blk: { - break :blk sub(true, cpu, cpu.r[rs], cpu.r[rn]); - }; + 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 - cpu.r[rd] = if (I) blk: { - break :blk add(true, cpu, cpu.r[rs], rn); - } else blk: { - break :blk add(true, cpu, cpu.r[rs], cpu.r[rn]); - }; + var overflow: bool = 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); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); } } }.inner; @@ -120,17 +144,36 @@ pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { pub fn fmt3(comptime op: u2, comptime rd: u3) InstrFn { return struct { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { - const offset = @truncate(u8, opcode); + const op1 = cpu.r[rd]; + const op2: u32 = opcode & 0xFF; // Offset + + var overflow: bool = 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 - cpu.r[rd] = offset; - setLogicOpFlags(true, cpu, offset); + 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); + cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); }, - 0b01 => cmp(cpu, cpu.r[rd], offset), // CMP - 0b10 => cpu.r[rd] = add(true, cpu, cpu.r[rd], offset), // ADD - 0b11 => cpu.r[rd] = sub(true, cpu, cpu.r[rd], offset), // SUB } } }.inner; From 870e99186298be256f86f2f2d28778e1a363dd36 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 16:11:25 -0300 Subject: [PATCH 14/20] feat: working pipeline implementation --- src/core/cpu.zig | 39 ++++++++-------------- src/core/cpu/arm/block_data_transfer.zig | 4 +-- src/core/cpu/arm/branch.zig | 4 +-- src/core/cpu/arm/data_processing.zig | 7 ++-- src/core/cpu/arm/single_data_transfer.zig | 4 +-- src/core/cpu/arm/software_interrupt.zig | 2 +- src/core/cpu/thumb/block_data_transfer.zig | 4 +-- src/core/cpu/thumb/branch.zig | 6 ++-- src/core/cpu/thumb/data_processing.zig | 4 +-- src/core/cpu/thumb/software_interrupt.zig | 2 +- 10 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index d77b68d..a0b2717 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -323,21 +323,8 @@ pub const Arm7tdmi = struct { return self.bus.io.haltcnt == .Halt; } - pub fn setCpsrNoFlush(self: *Self, value: u32) void { - if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F)); - self.cpsr.raw = value; - } - pub fn setCpsr(self: *Self, value: u32) void { if (value & 0x1F != self.cpsr.raw & 0x1F) self.changeModeFromIdx(@truncate(u5, value & 0x1F)); - - const new: PSR = .{ .raw = value }; - if (self.cpsr.t.read() != new.t.read()) { - // If THUMB to ARM or ARM to THUMB, flush pipeline - self.r[15] &= if (new.t.read()) ~@as(u32, 1) else ~@as(u32, 3); - if (new.t.read()) self.pipe.reload(u16, self) else self.pipe.reload(u32, self); - } - self.cpsr.raw = value; } @@ -500,7 +487,8 @@ pub const Arm7tdmi = struct { // log.debug("Handling Interrupt!", .{}); self.bus.io.haltcnt = .Execute; - const ret_addr = self.r[15] - if (self.cpsr.t.read()) 2 else @as(u32, 4); + // 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); @@ -510,7 +498,7 @@ pub const Arm7tdmi = struct { self.r[14] = ret_addr; self.spsr.raw = new_spsr; self.r[15] = 0x0000_0018; - self.pipe.reload(u32, self); + self.pipe.reload(self); } inline fn fetch(self: *Self, comptime T: type) T { @@ -692,18 +680,17 @@ const Pipline = struct { return opcode; } - pub fn reload(self: *Self, comptime T: type, cpu: *Arm7tdmi) void { - comptime std.debug.assert(T == u32 or T == u16); + pub fn reload(self: *Self, cpu: *Arm7tdmi) void { + if (cpu.cpsr.t.read()) { + self.stage[0] = cpu.bus.read(u16, cpu.r[15]); + self.stage[1] = cpu.bus.read(u16, cpu.r[15] + 2); + cpu.r[15] += 4; + } else { + self.stage[0] = cpu.bus.read(u32, cpu.r[15]); + self.stage[1] = cpu.bus.read(u32, cpu.r[15] + 4); + cpu.r[15] += 8; + } - // Sometimes, the pipeline can be reloaded twice in the same instruction - // This can happen if: - // 1. R15 is written to - // 2. The CPSR is written to (and T changes), so R15 is written to again - - self.stage[0] = cpu.bus.read(T, cpu.r[15]); - self.stage[1] = cpu.bus.read(T, cpu.r[15] + if (T == u32) 4 else @as(u32, 2)); - - cpu.r[15] += if (T == u32) 8 else @as(u32, 4); self.flushed = true; } }; diff --git a/src/core/cpu/arm/block_data_transfer.zig b/src/core/cpu/arm/block_data_transfer.zig index 32aafba..f94cbf9 100644 --- a/src/core/cpu/arm/block_data_transfer.zig +++ b/src/core/cpu/arm/block_data_transfer.zig @@ -55,7 +55,7 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c if (L) { cpu.r[15] = bus.read(u32, und_addr); - cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); } else { // FIXME: Should r15 on write be +12 ahead? bus.write(u32, und_addr, cpu.r[15] + 4); @@ -92,7 +92,7 @@ pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, c cpu.r[i] = value; if (i == 0xF) { cpu.r[i] &= ~@as(u32, 3); // Align r15 - cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); if (S) cpu.setCpsr(cpu.spsr.raw); } diff --git a/src/core/cpu/arm/branch.zig b/src/core/cpu/arm/branch.zig index 368735a..c4f9594 100644 --- a/src/core/cpu/arm/branch.zig +++ b/src/core/cpu/arm/branch.zig @@ -12,7 +12,7 @@ pub fn branch(comptime L: bool) InstrFn { if (L) cpu.r[14] = cpu.r[15] - 4; cpu.r[15] +%= sext(u32, u24, opcode) << 2; - cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); } }.inner; } @@ -24,5 +24,5 @@ pub fn branchAndExchange(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { cpu.r[15] = cpu.r[rn] & if (thumb) ~@as(u32, 1) else ~@as(u32, 3); cpu.cpsr.t.write(thumb); - if (thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); } diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 604921c..65df83f 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -76,9 +76,8 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins else => { cpu.r[rd] = result; if (rd == 0xF) { - if (S) cpu.setCpsrNoFlush(cpu.spsr.raw); - - cpu.pipe.reload(u32, cpu); + if (S) cpu.setCpsr(cpu.spsr.raw); + cpu.pipe.reload(cpu); } }, } @@ -180,5 +179,5 @@ pub fn adc(overflow: *bool, left: u32, right: u32, old_carry: u1) u32 { fn undefinedTestBehaviour(cpu: *Arm7tdmi) void { @setCold(true); - cpu.setCpsrNoFlush(cpu.spsr.raw); + cpu.setCpsr(cpu.spsr.raw); } diff --git a/src/core/cpu/arm/single_data_transfer.zig b/src/core/cpu/arm/single_data_transfer.zig index 091e151..fcab5d7 100644 --- a/src/core/cpu/arm/single_data_transfer.zig +++ b/src/core/cpu/arm/single_data_transfer.zig @@ -47,13 +47,13 @@ pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, address = modified_base; if (W and P or !P) { cpu.r[rn] = address; - if (rn == 0xF) cpu.pipe.reload(u32, cpu); + 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(u32, cpu); + 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 index 50f6674..b44cf99 100644 --- a/src/core/cpu/arm/software_interrupt.zig +++ b/src/core/cpu/arm/software_interrupt.zig @@ -17,7 +17,7 @@ pub fn armSoftwareInterrupt() InstrFn { cpu.r[14] = ret_addr; // Resume Execution cpu.spsr.raw = cpsr; // Previous mode CPSR cpu.r[15] = 0x0000_0008; - cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); } }.inner; } diff --git a/src/core/cpu/thumb/block_data_transfer.zig b/src/core/cpu/thumb/block_data_transfer.zig index 2f3b2a3..014729f 100644 --- a/src/core/cpu/thumb/block_data_transfer.zig +++ b/src/core/cpu/thumb/block_data_transfer.zig @@ -34,7 +34,7 @@ pub fn fmt14(comptime L: bool, comptime R: bool) InstrFn { if (L) { const value = bus.read(u32, address); cpu.r[15] = value & ~@as(u32, 1); - cpu.pipe.reload(u16, cpu); + cpu.pipe.reload(cpu); } else { bus.write(u32, address, cpu.r[14]); } @@ -55,7 +55,7 @@ pub fn fmt15(comptime L: bool, comptime rb: u3) InstrFn { if (opcode & 0xFF == 0) { if (L) { cpu.r[15] = bus.read(u32, address); - cpu.pipe.reload(u16, cpu); + cpu.pipe.reload(cpu); } else { bus.write(u32, address, cpu.r[15] + 2); } diff --git a/src/core/cpu/thumb/branch.zig b/src/core/cpu/thumb/branch.zig index 4c8a076..4d00577 100644 --- a/src/core/cpu/thumb/branch.zig +++ b/src/core/cpu/thumb/branch.zig @@ -15,7 +15,7 @@ pub fn fmt16(comptime cond: u4) InstrFn { if (!checkCond(cpu.cpsr, cond)) return; cpu.r[15] +%= sext(u32, u8, opcode & 0xFF) << 1; - cpu.pipe.reload(u16, cpu); + cpu.pipe.reload(cpu); } }.inner; } @@ -25,7 +25,7 @@ pub fn fmt18() InstrFn { // B but conditional fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1; - cpu.pipe.reload(u16, cpu); + cpu.pipe.reload(cpu); } }.inner; } @@ -43,7 +43,7 @@ pub fn fmt19(comptime is_low: bool) InstrFn { cpu.r[15] = cpu.r[14] +% (offset << 1); cpu.r[14] = next_opcode | 1; - cpu.pipe.reload(u16, cpu); + cpu.pipe.reload(cpu); } else { // Instruction 1 const lr_offset = sext(u32, u11, offset) << 12; diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig index e0e6c50..bd27f48 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -83,13 +83,13 @@ pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn { cpu.r[15] = op2 & ~@as(u32, 1); cpu.cpsr.t.write(is_thumb); - if (is_thumb) cpu.pipe.reload(u16, cpu) else cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); }, else => { cpu.r[rd] = result; if (rd == 0xF) { cpu.r[15] &= ~@as(u32, 1); - cpu.pipe.reload(u16, cpu); + cpu.pipe.reload(cpu); } }, } diff --git a/src/core/cpu/thumb/software_interrupt.zig b/src/core/cpu/thumb/software_interrupt.zig index 96806d2..893f902 100644 --- a/src/core/cpu/thumb/software_interrupt.zig +++ b/src/core/cpu/thumb/software_interrupt.zig @@ -17,7 +17,7 @@ pub fn fmt17() InstrFn { cpu.r[14] = ret_addr; // Resume Execution cpu.spsr.raw = cpsr; // Previous mode CPSR cpu.r[15] = 0x0000_0008; - cpu.pipe.reload(u32, cpu); + cpu.pipe.reload(cpu); } }.inner; } From 06c60dad748f325d89022482a0619cd1ae441056 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 16:37:40 -0300 Subject: [PATCH 15/20] fix: rename Pipline to Pipeline --- src/core/cpu.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index a0b2717..a2b726b 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -242,7 +242,7 @@ pub const Arm7tdmi = struct { const Self = @This(); r: [16]u32, - pipe: Pipline, + pipe: Pipeline, sched: *Scheduler, bus: *Bus, cpsr: PSR, @@ -263,7 +263,7 @@ pub const Arm7tdmi = struct { pub fn init(sched: *Scheduler, bus: *Bus, log_file: ?std.fs.File) Self { return Self{ .r = [_]u32{0x00} ** 16, - .pipe = Pipline.init(), + .pipe = Pipeline.init(), .sched = sched, .bus = bus, .cpsr = .{ .raw = 0x0000_001F }, @@ -643,7 +643,7 @@ pub fn checkCond(cpsr: PSR, cond: u4) bool { }; } -const Pipline = struct { +const Pipeline = struct { const Self = @This(); stage: [2]?u32, flushed: bool, From d3514b14f3591831870e0ad208ba969a85460864 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 28 Sep 2022 17:25:21 -0300 Subject: [PATCH 16/20] fix: resolve timing regressions make sure to use fetch timings when fetching instructions --- src/core/cpu.zig | 62 +++++++++++++++----------------------- src/core/cpu/thumb/alu.zig | 2 -- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index a2b726b..fbde5ab 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -428,22 +428,24 @@ pub const Arm7tdmi = struct { } pub fn step(self: *Self) void { - if (self.cpsr.t.read()) blk: { - const opcode = @truncate(u16, self.pipe.step(self, u16) orelse break :blk); + 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 blk: { - const opcode = self.pipe.step(self, u32) orelse break :blk; + } 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); } } - - if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); - self.pipe.flushed = false; } pub fn stepDmaTransfer(self: *Self) bool { @@ -481,8 +483,8 @@ pub const Arm7tdmi = struct { // 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, return but reschedule the handling of the event - if (!self.pipe.isFull()) 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; @@ -501,23 +503,18 @@ pub const Arm7tdmi = struct { self.pipe.reload(self); } - inline fn fetch(self: *Self, comptime T: type) T { + 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) - defer self.r[15] += if (T == u32) 4 else 2; - // FIXME: You better hope this is optimized out + // 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, self.r[15] >> 24)]; + defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)]; - return self.bus.read(T, self.r[15]); - } - - fn debug_log(self: *const Self, file: *const File, opcode: u32) void { - if (self.binary_log) { - self.skyLog(file) catch unreachable; - } else { - self.mgbaLog(file, opcode) catch unreachable; - } + return self.bus.read(T, address); } pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn { @@ -655,15 +652,6 @@ const Pipeline = struct { }; } - pub fn flush(self: *Self) void { - for (self.stage) |*opcode| opcode.* = null; - self.flushed = true; - - // Note: If using this, add - // if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4); - // to the end of Arm7tdmi.step - } - pub fn isFull(self: *const Self) bool { return self.stage[0] != null and self.stage[1] != null; } @@ -672,22 +660,22 @@ const Pipeline = struct { comptime std.debug.assert(T == u32 or T == u16); // FIXME: https://github.com/ziglang/zig/issues/12642 - const opcode = self.stage[0..1][0]; + var opcode = self.stage[0]; self.stage[0] = self.stage[1]; - self.stage[1] = cpu.bus.read(T, cpu.r[15]); + 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.bus.read(u16, cpu.r[15]); - self.stage[1] = cpu.bus.read(u16, cpu.r[15] + 2); + 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.bus.read(u32, cpu.r[15]); - self.stage[1] = cpu.bus.read(u32, cpu.r[15] + 4); + self.stage[0] = cpu.fetch(u32, cpu.r[15]); + self.stage[1] = cpu.fetch(u32, cpu.r[15] + 4); cpu.r[15] += 8; } diff --git a/src/core/cpu/thumb/alu.zig b/src/core/cpu/thumb/alu.zig index a05b26b..c47e63c 100644 --- a/src/core/cpu/thumb/alu.zig +++ b/src/core/cpu/thumb/alu.zig @@ -73,8 +73,6 @@ pub fn fmt4(comptime op: u4) InstrFn { cpu.cpsr.z.write(result == 0); cpu.cpsr.c.write(overflow); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); - - // FIXME: Pretty sure CMN Is the same }, 0x6 => { // SBC From f5bd20bc2afb2e05122e0c76a24116d9039eac8b Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sat, 1 Oct 2022 13:17:57 -0300 Subject: [PATCH 17/20] style: code cleanup --- src/core/cpu/arm/data_processing.zig | 8 +++--- src/core/cpu/arm/single_data_transfer.zig | 2 +- src/core/cpu/barrel_shifter.zig | 34 +++++++++++------------ src/core/cpu/thumb/alu.zig | 8 +++--- src/core/cpu/thumb/data_processing.zig | 6 ++-- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/core/cpu/arm/data_processing.zig b/src/core/cpu/arm/data_processing.zig index 65df83f..7bc3f44 100644 --- a/src/core/cpu/arm/data_processing.zig +++ b/src/core/cpu/arm/data_processing.zig @@ -2,8 +2,8 @@ const Bus = @import("../../Bus.zig"); const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const InstrFn = @import("../../cpu.zig").arm.InstrFn; -const rotateRight = @import("../barrel_shifter.zig").rotateRight; -const execute = @import("../barrel_shifter.zig").execute; +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 { @@ -18,7 +18,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins const op1 = cpu.r[rn]; const amount = @truncate(u8, (opcode >> 8 & 0xF) << 1); - const op2 = if (I) rotateRight(S, &cpu.cpsr, opcode & 0xFF, amount) else execute(S, cpu, opcode); + 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; @@ -146,7 +146,7 @@ pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime kind: u4) Ins } else { // TST, TEQ specific // Barrel Shifter should always calc CPSR C in TST - if (!S) _ = execute(true, cpu, opcode); + if (!S) _ = exec(true, cpu, opcode); } }, } diff --git a/src/core/cpu/arm/single_data_transfer.zig b/src/core/cpu/arm/single_data_transfer.zig index fcab5d7..42bd217 100644 --- a/src/core/cpu/arm/single_data_transfer.zig +++ b/src/core/cpu/arm/single_data_transfer.zig @@ -17,7 +17,7 @@ pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, // rn is r15 and L is not set, the PC is 12 ahead const base = cpu.r[rn] + if (!L and rn == 0xF) 4 else @as(u32, 0); - const offset = if (I) shifter.immShift(false, cpu, opcode) else opcode & 0xFFF; + 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; diff --git a/src/core/cpu/barrel_shifter.zig b/src/core/cpu/barrel_shifter.zig index 71cb236..14578b4 100644 --- a/src/core/cpu/barrel_shifter.zig +++ b/src/core/cpu/barrel_shifter.zig @@ -5,31 +5,31 @@ const CPSR = @import("../cpu.zig").PSR; const rotr = @import("../../util.zig").rotr; -pub fn execute(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { +pub fn exec(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { var result: u32 = undefined; if (opcode >> 4 & 1 == 1) { - result = registerShift(S, cpu, opcode); + result = register(S, cpu, opcode); } else { - result = immShift(S, cpu, opcode); + result = immediate(S, cpu, opcode); } return result; } -fn registerShift(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { +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 => logicalLeft(S, &cpu.cpsr, rm, rs), - 0b01 => logicalRight(S, &cpu.cpsr, rm, rs), - 0b10 => arithmeticRight(S, &cpu.cpsr, rm, rs), - 0b11 => rotateRight(S, &cpu.cpsr, rm, rs), + 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 immShift(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { +pub fn immediate(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { const amount = @truncate(u8, opcode >> 7 & 0x1F); const rm = cpu.r[opcode & 0xF]; @@ -60,17 +60,17 @@ pub fn immShift(comptime S: bool, cpu: *Arm7tdmi, opcode: u32) u32 { } } else { switch (@truncate(u2, opcode >> 5)) { - 0b00 => result = logicalLeft(S, &cpu.cpsr, rm, amount), - 0b01 => result = logicalRight(S, &cpu.cpsr, rm, amount), - 0b10 => result = arithmeticRight(S, &cpu.cpsr, rm, amount), - 0b11 => result = rotateRight(S, &cpu.cpsr, rm, amount), + 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 logicalLeft(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 { +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; @@ -97,7 +97,7 @@ pub fn logicalLeft(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 return result; } -pub fn logicalRight(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u32) u32 { +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; @@ -121,7 +121,7 @@ pub fn logicalRight(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u32) u return result; } -pub fn arithmeticRight(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 { +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; @@ -138,7 +138,7 @@ pub fn arithmeticRight(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) return result; } -pub fn rotateRight(comptime S: bool, cpsr: *CPSR, rm: u32, total_amount: u8) u32 { +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) { diff --git a/src/core/cpu/thumb/alu.zig b/src/core/cpu/thumb/alu.zig index c47e63c..778da99 100644 --- a/src/core/cpu/thumb/alu.zig +++ b/src/core/cpu/thumb/alu.zig @@ -5,10 +5,10 @@ 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").logicalLeft; -const lsr = @import("../barrel_shifter.zig").logicalRight; -const asr = @import("../barrel_shifter.zig").arithmeticRight; -const ror = @import("../barrel_shifter.zig").rotateRight; +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 { diff --git a/src/core/cpu/thumb/data_processing.zig b/src/core/cpu/thumb/data_processing.zig index bd27f48..6d9a324 100644 --- a/src/core/cpu/thumb/data_processing.zig +++ b/src/core/cpu/thumb/data_processing.zig @@ -6,9 +6,9 @@ const InstrFn = @import("../../cpu.zig").thumb.InstrFn; const add = @import("../arm/data_processing.zig").add; -const lsl = @import("../barrel_shifter.zig").logicalLeft; -const lsr = @import("../barrel_shifter.zig").logicalRight; -const asr = @import("../barrel_shifter.zig").arithmeticRight; +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 { From b37a14900c214e6cf6ea9fee5019da091b67fc9d Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 9 Oct 2022 13:20:19 -0300 Subject: [PATCH 18/20] style(bus): cpu ptr doesn't need to be optional --- src/core/Bus.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/Bus.zig b/src/core/Bus.zig index 56b00de..d57bdf8 100644 --- a/src/core/Bus.zig +++ b/src/core/Bus.zig @@ -46,7 +46,7 @@ iwram: Iwram, ewram: Ewram, io: Io, -cpu: ?*Arm7tdmi, +cpu: *Arm7tdmi, sched: *Scheduler, pub fn init(self: *Self, allocator: Allocator, sched: *Scheduler, cpu: *Arm7tdmi, paths: FilePaths) !void { @@ -82,7 +82,7 @@ pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T { // General Internal Memory 0x00 => blk: { if (address < Bios.size) - break :blk self.bios.dbgRead(T, self.cpu.?.r[15], aligned_addr); + break :blk self.bios.dbgRead(T, self.cpu.r[15], aligned_addr); break :blk self.readOpenBus(T, address); }, @@ -119,11 +119,11 @@ fn readIo(self: *const Self, comptime T: type, unaligned_address: u32) T { } fn readOpenBus(self: *const Self, comptime T: type, address: u32) T { - const r15 = self.cpu.?.r[15]; + const r15 = self.cpu.r[15]; const word = blk: { // If u32 Open Bus, read recently fetched opcode (PC + 8) - if (!self.cpu.?.cpsr.t.read()) break :blk self.dbgRead(u32, r15 + 4); + if (!self.cpu.cpsr.t.read()) break :blk self.dbgRead(u32, r15 + 4); const page = @truncate(u8, r15 >> 24); switch (page) { @@ -167,7 +167,7 @@ pub fn read(self: *Self, comptime T: type, address: u32) T { // General Internal Memory 0x00 => blk: { if (address < Bios.size) - break :blk self.bios.read(T, self.cpu.?.r[15], aligned_addr); + break :blk self.bios.read(T, self.cpu.r[15], aligned_addr); break :blk self.readOpenBus(T, address); }, From 822eed1f3a1d43b7b81b2b1e04e4aa8767e4d1b0 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 9 Oct 2022 14:10:21 -0300 Subject: [PATCH 19/20] fix(bus): make open bus impl aware of CPU pipeline --- src/core/Bus.zig | 49 +++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/core/Bus.zig b/src/core/Bus.zig index d57bdf8..38f0f50 100644 --- a/src/core/Bus.zig +++ b/src/core/Bus.zig @@ -84,7 +84,7 @@ pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T { if (address < Bios.size) break :blk self.bios.dbgRead(T, self.cpu.r[15], aligned_addr); - break :blk self.readOpenBus(T, address); + break :blk self.openBus(T, address); }, 0x02 => self.ewram.read(T, aligned_addr), 0x03 => self.iwram.read(T, aligned_addr), @@ -109,48 +109,63 @@ pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T { break :blk @as(T, value) * multiplier; }, - else => self.readOpenBus(T, address), + else => self.openBus(T, address), }; } fn readIo(self: *const Self, comptime T: type, unaligned_address: u32) T { const maybe_value = io.read(self, T, forceAlign(T, unaligned_address)); - return if (maybe_value) |value| value else self.readOpenBus(T, unaligned_address); + return if (maybe_value) |value| value else self.openBus(T, unaligned_address); } -fn readOpenBus(self: *const Self, comptime T: type, address: u32) T { +fn openBus(self: *const Self, comptime T: type, address: u32) T { const r15 = self.cpu.r[15]; const word = blk: { - // If u32 Open Bus, read recently fetched opcode (PC + 8) - if (!self.cpu.cpsr.t.read()) break :blk self.dbgRead(u32, r15 + 4); + // If Arm, get the most recently fetched instruction (PC + 8) + if (!self.cpu.cpsr.t.read()) break :blk self.cpu.pipe.stage[1].?; + const page = @truncate(u8, r15 >> 24); + // PC + 2 = stage[0] + // PC + 4 = stage[1] + // PC + 6 = Need a Debug Read for this? + switch (page) { // EWRAM, PALRAM, VRAM, and Game ROM (16-bit) 0x02, 0x05, 0x06, 0x08...0x0D => { - // (PC + 4) - const halfword = self.dbgRead(u16, r15 + 2); - - break :blk @as(u32, halfword) << 16 | halfword; + const halfword: u32 = @truncate(u16, self.cpu.pipe.stage[1].?); + break :blk halfword << 16 | halfword; }, + // BIOS or OAM (32-bit) 0x00, 0x07 => { // Aligned: (PC + 6) | (PC + 4) // Unaligned: (PC + 4) | (PC + 2) - const offset: u32 = if (address & 3 == 0b00) 2 else 0; + const aligned = address & 3 == 0b00; - break :blk @as(u32, self.dbgRead(u16, r15 + 2 + offset)) << 16 | self.dbgRead(u16, r15 + offset); + // TODO: What to do on PC + 6? + const high: u32 = if (aligned) self.dbgRead(u16, r15 + 4) else @truncate(u16, self.cpu.pipe.stage[1].?); + const low: u32 = @truncate(u16, self.cpu.pipe.stage[@boolToInt(aligned)].?); + + break :blk high << 16 | low; }, + // IWRAM (16-bit but special) 0x03 => { // Aligned: (PC + 2) | (PC + 4) // Unaligned: (PC + 4) | (PC + 2) - const offset: u32 = if (address & 3 == 0b00) 2 else 0; + const aligned = address & 3 == 0b00; - break :blk @as(u32, self.dbgRead(u16, r15 + 2 - offset)) << 16 | self.dbgRead(u16, r15 + offset); + const high: u32 = @truncate(u16, self.cpu.pipe.stage[1 - @boolToInt(aligned)].?); + const low: u32 = @truncate(u16, self.cpu.pipe.stage[@boolToInt(aligned)].?); + + break :blk high << 16 | low; + }, + else => { + log.err("THUMB open bus read from 0x{X:0>2} page @0x{X:0>8}", .{ page, address }); + @panic("invariant most-likely broken"); }, - else => unreachable, } }; @@ -169,7 +184,7 @@ pub fn read(self: *Self, comptime T: type, address: u32) T { if (address < Bios.size) break :blk self.bios.read(T, self.cpu.r[15], aligned_addr); - break :blk self.readOpenBus(T, address); + break :blk self.openBus(T, address); }, 0x02 => self.ewram.read(T, aligned_addr), 0x03 => self.iwram.read(T, aligned_addr), @@ -194,7 +209,7 @@ pub fn read(self: *Self, comptime T: type, address: u32) T { break :blk @as(T, value) * multiplier; }, - else => self.readOpenBus(T, address), + else => self.openBus(T, address), }; } From 7b146ad7cae6443d4e5a4ff8c71fa4705e91ed64 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 9 Oct 2022 14:11:18 -0300 Subject: [PATCH 20/20] fix(bios): set addr_latch even if bios is skipped --- src/core/cpu.zig | 8 ++++++++ src/main.zig | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/cpu.zig b/src/core/cpu.zig index fbde5ab..6f15cae 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -412,6 +412,12 @@ pub const Arm7tdmi = struct { 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); @@ -425,6 +431,8 @@ pub const Arm7tdmi = struct { // self.cpsr.raw = 0x6000001F; self.cpsr.raw = 0x0000_001F; + + self.bus.bios.addr_latch = 0x0000_00DC + 8; } pub fn step(self: *Self) void { diff --git a/src/main.zig b/src/main.zig index a2c5ac0..e723949 100644 --- a/src/main.zig +++ b/src/main.zig @@ -60,11 +60,12 @@ pub fn main() anyerror!void { var bus: Bus = undefined; var cpu = Arm7tdmi.init(&scheduler, &bus, log_file); - if (paths.bios == null) cpu.fastBoot(); try bus.init(allocator, &scheduler, &cpu, paths); defer bus.deinit(); + if (paths.bios == null) cpu.fastBoot(); + var gui = Gui.init(&bus.pak.title, &bus.apu, width, height); defer gui.deinit();