Compare commits
11 Commits
00058f6094
...
8c248ffb11
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | 8c248ffb11 | |
Rekai Nyangadzayi Musuka | b0332e6eb8 | |
Rekai Nyangadzayi Musuka | dd632975f8 | |
Rekai Nyangadzayi Musuka | a459d4b433 | |
Rekai Nyangadzayi Musuka | 6c008ce950 | |
Rekai Nyangadzayi Musuka | 8d1df7ae43 | |
Rekai Nyangadzayi Musuka | 6ffaf12804 | |
Rekai Nyangadzayi Musuka | dc6931639f | |
Rekai Nyangadzayi Musuka | e18f10126e | |
Rekai Nyangadzayi Musuka | 0598ba402d | |
Rekai Nyangadzayi Musuka | b8a9aaee86 |
|
@ -12,14 +12,16 @@ pub const Io = struct {
|
|||
/// Read / Write
|
||||
ime: bool,
|
||||
ie: InterruptEnable,
|
||||
keyinput: KeyInput,
|
||||
|
||||
pub fn init() Self {
|
||||
return .{
|
||||
.dispcnt = .{ .raw = 0x0000_0000 },
|
||||
.dispstat = .{ .raw = 0x0000_0000 },
|
||||
.vcount = .{ .raw = 0x0000_0000 },
|
||||
.dispcnt = .{ .raw = 0x0000 },
|
||||
.dispstat = .{ .raw = 0x0000 },
|
||||
.vcount = .{ .raw = 0x0000 },
|
||||
.ime = false,
|
||||
.ie = .{ .raw = 0x0000_0000 },
|
||||
.ie = .{ .raw = 0x0000 },
|
||||
.keyinput = .{ .raw = 0x01FF },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -36,6 +38,7 @@ pub const Io = struct {
|
|||
|
||||
pub fn write32(self: *Self, addr: u32, word: u32) void {
|
||||
switch (addr) {
|
||||
0x0400_0000 => self.dispcnt.raw = @truncate(u16, word),
|
||||
0x0400_0200 => self.ie.raw = @truncate(u16, word),
|
||||
0x0400_0208 => self.ime = word & 1 == 1,
|
||||
else => std.debug.panic("[I/O:32] tried to write 0x{X:} to 0x{X:}", .{ word, addr }),
|
||||
|
@ -47,6 +50,7 @@ pub const Io = struct {
|
|||
0x0400_0000 => self.dispcnt.raw,
|
||||
0x0400_0004 => self.dispstat.raw,
|
||||
0x0400_0006 => self.vcount.raw,
|
||||
0x0400_0130 => self.keyinput.raw,
|
||||
0x0400_0200 => self.ie.raw,
|
||||
0x0400_0208 => @boolToInt(self.ime),
|
||||
else => std.debug.panic("[I/O:16] tried to read from {X:}", .{addr}),
|
||||
|
@ -128,3 +132,19 @@ const InterruptEnable = extern union {
|
|||
game_pak: Bit(u16, 13),
|
||||
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,
|
||||
};
|
||||
|
|
36
src/cpu.zig
36
src/cpu.zig
|
@ -17,15 +17,19 @@ const blockDataTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTr
|
|||
const branch = @import("cpu/arm/branch.zig").branch;
|
||||
const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange;
|
||||
const softwareInterrupt = @import("cpu/arm/software_interrupt.zig").softwareInterrupt;
|
||||
const multiply = @import("cpu/arm/multiply.zig").multiply;
|
||||
|
||||
// THUMB Instruction Groups
|
||||
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 format4 = @import("cpu/thumb/format4.zig").format4;
|
||||
const format2 = @import("cpu/thumb/format2.zig").format2;
|
||||
const format5 = @import("cpu/thumb/format5.zig").format5;
|
||||
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 format13 = @import("cpu/thumb/format13.zig").format13;
|
||||
const format14 = @import("cpu/thumb/format14.zig").format14;
|
||||
const format15 = @import("cpu/thumb/format15.zig").format15;
|
||||
const format16 = @import("cpu/thumb/format16.zig").format16;
|
||||
const format19 = @import("cpu/thumb/format19.zig").format19;
|
||||
|
@ -369,6 +373,14 @@ fn thumbPopulate() [0x400]ThumbInstrFn {
|
|||
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) {
|
||||
const isSP = i >> 5 & 1 == 1;
|
||||
const rd = i >> 2 & 0x7;
|
||||
|
@ -376,9 +388,22 @@ fn thumbPopulate() [0x400]ThumbInstrFn {
|
|||
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) {
|
||||
const L = i >> 5 & 1 == 1;
|
||||
const rb = i >> 2 & 0x3;
|
||||
const rb = i >> 2 & 0x7;
|
||||
|
||||
lut[i] = format15(L, rb);
|
||||
}
|
||||
|
@ -438,6 +463,13 @@ fn armPopulate() [0x1000]ArmInstrFn {
|
|||
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) {
|
||||
const I = i >> 9 & 1 == 1;
|
||||
const P = i >> 8 & 1 == 1;
|
||||
|
|
|
@ -58,7 +58,7 @@ pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I:
|
|||
// STRH
|
||||
bus.write16(address, @truncate(u16, cpu.r[rd]));
|
||||
} else {
|
||||
std.debug.panic("[CPU] TODO: Figure out if this is also SWP", .{});
|
||||
std.debug.print("[CPU|ARM|SignedDataTransfer] {X:0>8} was improperly decoded", .{opcode});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
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;
|
||||
}
|
|
@ -8,7 +8,7 @@ pub fn format12(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.fakePC() & 0xFFFF_FFFC;
|
||||
const left = if (isSP) cpu.r[13] else (cpu.r[15] + 2) & 0xFFFF_FFFD;
|
||||
const right = (opcode & 0xFF) << 2;
|
||||
const result = left + right; // TODO: What about overflows?
|
||||
cpu.r[rd] = result;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
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;
|
||||
}
|
|
@ -19,7 +19,7 @@ pub fn format16(comptime cond: u4) InstrFn {
|
|||
};
|
||||
|
||||
if (should_execute) {
|
||||
cpu.r[15] = (cpu.fakePC() & 0xFFFF_FFFC) +% offset;
|
||||
cpu.r[15] = (cpu.r[15] + 2) +% offset;
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
|
|
|
@ -9,7 +9,7 @@ pub fn format19(comptime is_low: bool) InstrFn {
|
|||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
|
||||
// BL
|
||||
const offset = opcode & 0x3FF;
|
||||
const offset = opcode & 0x7FF;
|
||||
|
||||
if (is_low) {
|
||||
// Instruction 2
|
||||
|
@ -19,7 +19,7 @@ pub fn format19(comptime is_low: bool) InstrFn {
|
|||
cpu.r[14] = old_pc | 1;
|
||||
} else {
|
||||
// Instruction 1
|
||||
cpu.r[14] = (cpu.fakePC() & 0xFFFF_FFFC) + (u32SignExtend(11, @as(u32, offset)) << 12);
|
||||
cpu.r[14] = (cpu.r[15] + 2) +% (u32SignExtend(11, @as(u32, offset)) << 12);
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
|
|
|
@ -92,7 +92,10 @@ pub fn format4(comptime op: u4) InstrFn {
|
|||
// MUL
|
||||
const result = cpu.r[rs] * cpu.r[rd];
|
||||
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 => {
|
||||
// BIC
|
||||
|
|
|
@ -11,7 +11,7 @@ pub fn format6(comptime rd: u3) InstrFn {
|
|||
const offset = (opcode & 0xFF) << 2;
|
||||
|
||||
// FIXME: Should this overflow?
|
||||
cpu.r[rd] = bus.read32((cpu.fakePC() & 0xFFFF_FFFC) + offset);
|
||||
cpu.r[rd] = bus.read32((cpu.r[15] + 2 & 0xFFFF_FFFD) + offset);
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
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;
|
||||
}
|
10
src/ppu.zig
10
src/ppu.zig
|
@ -117,8 +117,16 @@ const Vram = struct {
|
|||
alloc: Allocator,
|
||||
|
||||
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{
|
||||
.buf = try alloc.alloc(u8, 0x18000),
|
||||
.buf = buf,
|
||||
.alloc = alloc,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue