Compare commits

..

No commits in common. "8c248ffb11fd54cc9d94859c28e16a4a80a4cb7d" and "00058f6094ef2a4526c6d2951853b2c722933557" have entirely different histories.

13 changed files with 14 additions and 201 deletions

View File

@ -12,16 +12,14 @@ pub const Io = struct {
/// Read / Write /// Read / Write
ime: bool, ime: bool,
ie: InterruptEnable, ie: InterruptEnable,
keyinput: KeyInput,
pub fn init() Self { pub fn init() Self {
return .{ return .{
.dispcnt = .{ .raw = 0x0000 }, .dispcnt = .{ .raw = 0x0000_0000 },
.dispstat = .{ .raw = 0x0000 }, .dispstat = .{ .raw = 0x0000_0000 },
.vcount = .{ .raw = 0x0000 }, .vcount = .{ .raw = 0x0000_0000 },
.ime = false, .ime = false,
.ie = .{ .raw = 0x0000 }, .ie = .{ .raw = 0x0000_0000 },
.keyinput = .{ .raw = 0x01FF },
}; };
} }
@ -38,7 +36,6 @@ pub const Io = struct {
pub fn write32(self: *Self, addr: u32, word: u32) void { pub fn write32(self: *Self, addr: u32, word: u32) void {
switch (addr) { switch (addr) {
0x0400_0000 => self.dispcnt.raw = @truncate(u16, word),
0x0400_0200 => self.ie.raw = @truncate(u16, word), 0x0400_0200 => self.ie.raw = @truncate(u16, word),
0x0400_0208 => self.ime = word & 1 == 1, 0x0400_0208 => self.ime = word & 1 == 1,
else => std.debug.panic("[I/O:32] tried to write 0x{X:} to 0x{X:}", .{ word, addr }), else => std.debug.panic("[I/O:32] tried to write 0x{X:} to 0x{X:}", .{ word, addr }),
@ -50,7 +47,6 @@ pub const Io = struct {
0x0400_0000 => self.dispcnt.raw, 0x0400_0000 => self.dispcnt.raw,
0x0400_0004 => self.dispstat.raw, 0x0400_0004 => self.dispstat.raw,
0x0400_0006 => self.vcount.raw, 0x0400_0006 => self.vcount.raw,
0x0400_0130 => self.keyinput.raw,
0x0400_0200 => self.ie.raw, 0x0400_0200 => self.ie.raw,
0x0400_0208 => @boolToInt(self.ime), 0x0400_0208 => @boolToInt(self.ime),
else => std.debug.panic("[I/O:16] tried to read from {X:}", .{addr}), else => std.debug.panic("[I/O:16] tried to read from {X:}", .{addr}),
@ -132,19 +128,3 @@ const InterruptEnable = extern union {
game_pak: Bit(u16, 13), game_pak: Bit(u16, 13),
raw: u16, raw: u16,
}; };
/// Read Only
/// 0 = Pressed, 1 = Released
const KeyInput = extern union {
a: Bit(u16, 0),
b: Bit(u16, 1),
select: Bit(u16, 2),
start: Bit(u16, 3),
right: Bit(u16, 4),
left: Bit(u16, 5),
up: Bit(u16, 6),
down: Bit(u16, 7),
shoulder_r: Bit(u16, 8),
shoulder_l: Bit(u16, 9),
raw: u16,
};

View File

@ -17,19 +17,15 @@ const blockDataTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTr
const branch = @import("cpu/arm/branch.zig").branch; const branch = @import("cpu/arm/branch.zig").branch;
const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange; const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange;
const softwareInterrupt = @import("cpu/arm/software_interrupt.zig").softwareInterrupt; const softwareInterrupt = @import("cpu/arm/software_interrupt.zig").softwareInterrupt;
const multiply = @import("cpu/arm/multiply.zig").multiply;
// THUMB Instruction Groups // THUMB Instruction Groups
const format1 = @import("cpu/thumb/format1.zig").format1; const format1 = @import("cpu/thumb/format1.zig").format1;
const format2 = @import("cpu/thumb/format2.zig").format2;
const format3 = @import("cpu/thumb/format3.zig").format3; const format3 = @import("cpu/thumb/format3.zig").format3;
const format4 = @import("cpu/thumb/format4.zig").format4; const format4 = @import("cpu/thumb/format4.zig").format4;
const format2 = @import("cpu/thumb/format2.zig").format2;
const format5 = @import("cpu/thumb/format5.zig").format5; const format5 = @import("cpu/thumb/format5.zig").format5;
const format6 = @import("cpu/thumb/format6.zig").format6; const format6 = @import("cpu/thumb/format6.zig").format6;
const format9 = @import("cpu/thumb/format9.zig").format9;
const format12 = @import("cpu/thumb/format12.zig").format12; const format12 = @import("cpu/thumb/format12.zig").format12;
const format13 = @import("cpu/thumb/format13.zig").format13;
const format14 = @import("cpu/thumb/format14.zig").format14;
const format15 = @import("cpu/thumb/format15.zig").format15; const format15 = @import("cpu/thumb/format15.zig").format15;
const format16 = @import("cpu/thumb/format16.zig").format16; const format16 = @import("cpu/thumb/format16.zig").format16;
const format19 = @import("cpu/thumb/format19.zig").format19; const format19 = @import("cpu/thumb/format19.zig").format19;
@ -373,14 +369,6 @@ fn thumbPopulate() [0x400]ThumbInstrFn {
lut[i] = format6(rd); lut[i] = format6(rd);
} }
if (i >> 7 & 0x7 == 0b011) {
const B = i >> 6 & 1 == 1;
const L = i >> 5 & 1 == 1;
const offset = i & 0x1F;
lut[i] = format9(B, L, offset);
}
if (i >> 6 & 0xF == 0b1010) { if (i >> 6 & 0xF == 0b1010) {
const isSP = i >> 5 & 1 == 1; const isSP = i >> 5 & 1 == 1;
const rd = i >> 2 & 0x7; const rd = i >> 2 & 0x7;
@ -388,22 +376,9 @@ fn thumbPopulate() [0x400]ThumbInstrFn {
lut[i] = format12(isSP, rd); lut[i] = format12(isSP, rd);
} }
if (i >> 2 & 0xFF == 0xB0) {
const S = i >> 1 & 1 == 1;
lut[i] = format13(S);
}
if (i >> 6 & 0xF == 0b1011 and i >> 3 & 0x3 == 0b10) {
const L = i >> 5 & 1 == 1;
const R = i >> 2 & 1 == 1;
lut[i] = format14(L, R);
}
if (i >> 6 & 0xF == 0b1100) { if (i >> 6 & 0xF == 0b1100) {
const L = i >> 5 & 1 == 1; const L = i >> 5 & 1 == 1;
const rb = i >> 2 & 0x7; const rb = i >> 2 & 0x3;
lut[i] = format15(L, rb); lut[i] = format15(L, rb);
} }
@ -463,13 +438,6 @@ fn armPopulate() [0x1000]ArmInstrFn {
lut[i] = halfAndSignedDataTransfer(P, U, I, W, L); lut[i] = halfAndSignedDataTransfer(P, U, I, W, L);
} }
if (i >> 6 & 0x3F == 0b000000 and i & 0xF == 0b1001) {
const A = i >> 5 & 1 == 1;
const S = i >> 4 & 1 == 1;
lut[i] = multiply(A, S);
}
if (i >> 10 & 0x3 == 0b01) { if (i >> 10 & 0x3 == 0b01) {
const I = i >> 9 & 1 == 1; const I = i >> 9 & 1 == 1;
const P = i >> 8 & 1 == 1; const P = i >> 8 & 1 == 1;

View File

@ -58,7 +58,7 @@ pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I:
// STRH // STRH
bus.write16(address, @truncate(u16, cpu.r[rd])); bus.write16(address, @truncate(u16, cpu.r[rd]));
} else { } else {
std.debug.print("[CPU|ARM|SignedDataTransfer] {X:0>8} was improperly decoded", .{opcode}); std.debug.panic("[CPU] TODO: Figure out if this is also SWP", .{});
} }
} }

View File

@ -1,25 +0,0 @@
const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
pub fn multiply(comptime A: bool, comptime S: bool) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
const rd = opcode >> 16 & 0xF;
const rn = opcode >> 12 & 0xF;
const rs = opcode >> 8 & 0xF;
const rm = opcode & 0xF;
const result = cpu.r[rm] * cpu.r[rs] + if (A) cpu.r[rn] else 0;
cpu.r[rd] = result;
if (S) {
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
// V is unaffected, C is *actually* undefined in ARMv4
}
}
}.inner;
}

View File

@ -8,7 +8,7 @@ pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn {
return struct { return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
// ADD // ADD
const left = if (isSP) cpu.r[13] else (cpu.r[15] + 2) & 0xFFFF_FFFD; const left = if (isSP) cpu.r[13] else cpu.fakePC() & 0xFFFF_FFFC;
const right = (opcode & 0xFF) << 2; const right = (opcode & 0xFF) << 2;
const result = left + right; // TODO: What about overflows? const result = left + right; // TODO: What about overflows?
cpu.r[rd] = result; cpu.r[rd] = result;

View File

@ -1,13 +0,0 @@
const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
pub fn format13(comptime _: bool) InstrFn {
return struct {
fn inner(_: *Arm7tdmi, _: *Bus, _: u16) void {
std.debug.panic("[CPU|THUMB|Fmt13] Implement Format 13 THUMB Instructions", .{});
}
}.inner;
}

View File

@ -1,50 +0,0 @@
const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
pub fn format14(comptime L: bool, comptime R: bool) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
var address: u32 = undefined;
if (L) {
// POP
address = cpu.r[13];
var i: usize = 0;
while (i < 8) : (i += 1) {
if ((opcode >> @truncate(u3, i)) & 1 == 1) {
cpu.r[i] = bus.read32(address);
address += 4;
}
}
if (R) {
const value = bus.read32(address);
cpu.r[15] = value & 0xFFFF_FFFE;
address += 4;
}
} else {
address = cpu.r[13] - 4;
if (R) {
bus.write32(address, cpu.r[14]);
address -= 4;
}
var i: usize = 8;
while (i > 0) : (i -= 1) {
const j = i - 1;
if ((opcode >> @truncate(u3, j)) & 1 == 1) {
bus.write32(address, cpu.r[j]);
address -= 4;
}
}
}
cpu.r[13] = address + if (!L) 4 else 0;
}
}.inner;
}

View File

@ -19,7 +19,7 @@ pub fn format16(comptime cond: u4) InstrFn {
}; };
if (should_execute) { if (should_execute) {
cpu.r[15] = (cpu.r[15] + 2) +% offset; cpu.r[15] = (cpu.fakePC() & 0xFFFF_FFFC) +% offset;
} }
} }
}.inner; }.inner;

View File

@ -9,7 +9,7 @@ pub fn format19(comptime is_low: bool) InstrFn {
return struct { return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
// BL // BL
const offset = opcode & 0x7FF; const offset = opcode & 0x3FF;
if (is_low) { if (is_low) {
// Instruction 2 // Instruction 2
@ -19,7 +19,7 @@ pub fn format19(comptime is_low: bool) InstrFn {
cpu.r[14] = old_pc | 1; cpu.r[14] = old_pc | 1;
} else { } else {
// Instruction 1 // Instruction 1
cpu.r[14] = (cpu.r[15] + 2) +% (u32SignExtend(11, @as(u32, offset)) << 12); cpu.r[14] = (cpu.fakePC() & 0xFFFF_FFFC) + (u32SignExtend(11, @as(u32, offset)) << 12);
} }
} }
}.inner; }.inner;

View File

@ -92,10 +92,7 @@ pub fn format4(comptime op: u4) InstrFn {
// MUL // MUL
const result = cpu.r[rs] * cpu.r[rd]; const result = cpu.r[rs] * cpu.r[rd];
cpu.r[rd] = result; cpu.r[rd] = result;
std.debug.panic("[CPU|THUMB|MUL] TODO: Set flags on ALU MUL", .{});
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 => { 0xE => {
// BIC // BIC

View File

@ -11,7 +11,7 @@ pub fn format6(comptime rd: u3) InstrFn {
const offset = (opcode & 0xFF) << 2; const offset = (opcode & 0xFF) << 2;
// FIXME: Should this overflow? // FIXME: Should this overflow?
cpu.r[rd] = bus.read32((cpu.r[15] + 2 & 0xFFFF_FFFD) + offset); cpu.r[rd] = bus.read32((cpu.fakePC() & 0xFFFF_FFFC) + offset);
} }
}.inner; }.inner;
} }

View File

@ -1,36 +0,0 @@
const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const rb = opcode >> 3 & 0x7;
const rd = opcode & 0x7;
if (L) {
if (B) {
// LDRB
const address = cpu.r[rb] + offset;
cpu.r[rd] = bus.read8(address);
} else {
// LDR
const address = cpu.r[rb] + (@as(u32, offset) << 2);
cpu.r[rd] = bus.read32(address & 0xFFFF_FFFC);
}
} else {
if (B) {
// STRB
const address = cpu.r[rb] + offset;
bus.write8(address, @truncate(u8, cpu.r[rd]));
} else {
// STR
const address = cpu.r[rb] + (@as(u32, offset) << 2);
bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]);
}
}
}
}.inner;
}

View File

@ -117,16 +117,8 @@ const Vram = struct {
alloc: Allocator, alloc: Allocator,
fn init(alloc: Allocator) !Self { fn init(alloc: Allocator) !Self {
// In Modes 3 and 4, parts of the VRAM are copied to the
// frame buffer, therefore we want to zero-initialize Vram
//
// some programs like Armwrestler assume that VRAM is zeroed-out.
const black = std.mem.zeroes([0x18000]u8);
const buf = try alloc.alloc(u8, 0x18000);
std.mem.copy(u8, buf, &black);
return Self{ return Self{
.buf = buf, .buf = try alloc.alloc(u8, 0x18000),
.alloc = alloc, .alloc = alloc,
}; };
} }