Compare commits

..

No commits in common. "fd57703939371bdbeb00b87f6fffdf24045d8040" and "7d8fbbb086a93b30d1c76a2a9dfa0cbe7d18b57b" have entirely different histories.

2 changed files with 163 additions and 145 deletions

View File

@ -28,45 +28,31 @@ Finally it's worth noting that ZBA uses a TOML config file it'll store in your O
## Tests
GBA Tests | [jsmolka](https://github.com/jsmolka/)
--- | ---
`arm.gba`, `thumb.gba` | PASS
`memory.gba`, `bios.gba` | PASS
`flash64.gba`, `flash128.gba` | PASS
`sram.gba` | PASS
`none.gba` | PASS
`hello.gba`, `shades.gba`, `stripes.gba` | PASS
`nes.gba` | PASS
GBARoms | [DenSinH](https://github.com/DenSinH/)
--- | ---
`eeprom-test`, `flash-test` | PASS
`midikey2freq` | PASS
`swi-tests-random` | FAIL
gba_tests | [destoer](https://github.com/destoer/)
--- | ---
`cond_invalid.gba` | PASS
`dma_priority.gba` | PASS
`hello_world.gba` | PASS
`if_ack.gba` | PASS
`line_timing.gba` | FAIL
`lyc_midline.gba` | FAIL
`window_midframe.gba` | FAIL
GBA Test Collection | [ladystarbreeze](https://github.com/ladystarbreeze)
--- | ---
`retAddr.gba` | PASS
`helloWorld.gba` | PASS
`helloAudio.gba` | PASS
FuzzARM | [DenSinH](https://github.com/DenSinH/)
--- | ---
`main.gba` | PASS
arm7wrestler GBA Fixed | [destoer](https://github.com/destoer)
--- | ---
`armwrestler-gba-fixed.gba` | PASS
- [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`
- [x] `nes.gba`
- [ ] [DenSinH's GBA ROMs](https://github.com/DenSinH/GBARoms)
- [x] `eeprom-test` and `flash-test`
- [x] `midikey2freq`
- [ ] `swi-tests-random`
- [ ] [destoer's GBA Tests](https://github.com/destoer/gba_tests)
- [x] `cond_invalid.gba`
- [x] `dma_priority.gba`
- [x] `hello_world.gba`
- [x] `if_ack.gba`
- [ ] `line_timing.gba`
- [ ] `lyc_midline.gba`
- [ ] `window_midframe.gba`
- [x] [ladystarbreeze's GBA Test Collection](https://github.com/ladystarbreeze/GBA-Test-Collection)
- [x] `retAddr.gba`
- [x] `helloWorld.gba`
- [x] `helloAudio.gba`
- [x] [`armwrestler-gba-fixed.gba`](https://github.com/destoer/armwrestler-gba-fixed)
- [x] [FuzzARM](https://github.com/DenSinH/FuzzARM)
## Resources

View File

@ -7,7 +7,6 @@ const Scheduler = @import("scheduler.zig").Scheduler;
const Logger = @import("../util.zig").Logger;
const File = std.fs.File;
const log = std.log.scoped(.Arm7Tdmi);
// ARM Instructions
pub const arm = struct {
@ -235,6 +234,8 @@ pub const thumb = struct {
}
};
const log = std.log.scoped(.Arm7Tdmi);
pub const Arm7tdmi = struct {
const Self = @This();
@ -245,64 +246,18 @@ pub const Arm7tdmi = struct {
cpsr: PSR,
spsr: PSR,
bank: Bank,
/// Storage for R8_fiq -> R12_fiq and their normal counterparts
/// e.g [r[0 + 8], fiq_r[0 + 8], r[1 + 8], fiq_r[1 + 8]...]
banked_fiq: [2 * 5]u32,
/// Storage for r13_<mode>, r14_<mode>
/// e.g. [r13, r14, r13_svc, r14_svc]
banked_r: [2 * 6]u32,
banked_spsr: [5]PSR,
logger: ?Logger,
/// Bank of Registers from other CPU Modes
const Bank = struct {
/// Storage for r13_<mode>, r14_<mode>
/// e.g. [r13, r14, r13_svc, r14_svc]
r: [2 * 6]u32,
/// Storage for R8_fiq -> R12_fiq and their normal counterparts
/// e.g [r[0 + 8], fiq_r[0 + 8], r[1 + 8], fiq_r[1 + 8]...]
fiq: [2 * 5]u32,
spsr: [5]PSR,
const Kind = enum(u1) {
R13 = 0,
R14,
};
pub fn create() Bank {
return .{
.r = [_]u32{0x00} ** 12,
.fiq = [_]u32{0x00} ** 10,
.spsr = [_]PSR{.{ .raw = 0x0000_0000 }} ** 5,
};
}
inline fn regIdx(mode: Mode, kind: Kind) usize {
const idx: usize = switch (mode) {
.User, .System => 0,
.Supervisor => 1,
.Abort => 2,
.Undefined => 3,
.Irq => 4,
.Fiq => 5,
};
return (idx * 2) + if (kind == .R14) @as(usize, 1) else 0;
}
inline fn spsrIdx(mode: Mode) usize {
return switch (mode) {
.Supervisor => 0,
.Abort => 1,
.Undefined => 2,
.Irq => 3,
.Fiq => 4,
else => std.debug.panic("[CPU/Mode] {} does not have a SPSR Register", .{mode}),
};
}
inline fn fiqIdx(i: usize, mode: Mode) usize {
return (i * 2) + if (mode == .Fiq) @as(usize, 1) else 0;
}
};
pub fn init(sched: *Scheduler, bus: *Bus, log_file: ?std.fs.File) Self {
return Self{
.r = [_]u32{0x00} ** 16,
@ -311,11 +266,41 @@ pub const Arm7tdmi = struct {
.bus = bus,
.cpsr = .{ .raw = 0x0000_001F },
.spsr = .{ .raw = 0x0000_0000 },
.bank = Bank.create(),
.banked_fiq = [_]u32{0x00} ** 10,
.banked_r = [_]u32{0x00} ** 12,
.banked_spsr = [_]PSR{.{ .raw = 0x0000_0000 }} ** 5,
.logger = if (log_file) |file| Logger.init(file) else null,
};
}
inline fn bankedIdx(mode: Mode, kind: BankedKind) usize {
const idx: usize = switch (mode) {
.User, .System => 0,
.Supervisor => 1,
.Abort => 2,
.Undefined => 3,
.Irq => 4,
.Fiq => 5,
};
return (idx * 2) + if (kind == .R14) @as(usize, 1) else 0;
}
inline fn bankedSpsrIndex(mode: Mode) usize {
return switch (mode) {
.Supervisor => 0,
.Abort => 1,
.Undefined => 2,
.Irq => 3,
.Fiq => 4,
else => std.debug.panic("[CPU/Mode] {} does not have a SPSR Register", .{mode}),
};
}
inline fn bankedFiqIdx(i: usize, mode: Mode) usize {
return (i * 2) + if (mode == .Fiq) @as(usize, 1) else 0;
}
pub inline fn hasSPSR(self: *const Self) bool {
const mode = getModeChecked(self, self.cpsr.mode.read());
return switch (mode) {
@ -351,14 +336,14 @@ pub const Arm7tdmi = struct {
switch (idx) {
8...12 => {
if (current == .Fiq) {
self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] = value;
self.banked_fiq[bankedFiqIdx(idx - 8, .User)] = value;
} else self.r[idx] = value;
},
13, 14 => switch (current) {
.User, .System => self.r[idx] = value,
else => {
const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable;
self.bank.r[Bank.regIdx(.User, kind)] = value;
const kind = std.meta.intToEnum(BankedKind, idx - 13) catch unreachable;
self.banked_r[bankedIdx(.User, kind)] = value;
},
},
else => self.r[idx] = value, // R0 -> R7 and R15
@ -369,12 +354,12 @@ pub const Arm7tdmi = struct {
const current = getModeChecked(self, self.cpsr.mode.read());
return switch (idx) {
8...12 => if (current == .Fiq) self.bank.fiq[Bank.fiqIdx(idx - 8, .User)] else self.r[idx],
8...12 => if (current == .Fiq) self.banked_fiq[bankedFiqIdx(idx - 8, .User)] else self.r[idx],
13, 14 => switch (current) {
.User, .System => self.r[idx],
else => blk: {
const kind = std.meta.intToEnum(Bank.Kind, idx - 13) catch unreachable;
break :blk self.bank.r[Bank.regIdx(.User, kind)];
const kind = std.meta.intToEnum(BankedKind, idx - 13) catch unreachable;
break :blk self.banked_r[bankedIdx(.User, kind)];
},
},
else => self.r[idx], // R0 -> R7 and R15
@ -387,38 +372,38 @@ pub const Arm7tdmi = struct {
// Bank R8 -> r12
var i: usize = 0;
while (i < 5) : (i += 1) {
self.bank.fiq[Bank.fiqIdx(i, now)] = self.r[8 + i];
self.banked_fiq[bankedFiqIdx(i, now)] = self.r[8 + i];
}
// Bank r13, r14, SPSR
switch (now) {
.User, .System => {
self.bank.r[Bank.regIdx(now, .R13)] = self.r[13];
self.bank.r[Bank.regIdx(now, .R14)] = self.r[14];
self.banked_r[bankedIdx(now, .R13)] = self.r[13];
self.banked_r[bankedIdx(now, .R14)] = self.r[14];
},
else => {
self.bank.r[Bank.regIdx(now, .R13)] = self.r[13];
self.bank.r[Bank.regIdx(now, .R14)] = self.r[14];
self.bank.spsr[Bank.spsrIdx(now)] = self.spsr;
self.banked_r[bankedIdx(now, .R13)] = self.r[13];
self.banked_r[bankedIdx(now, .R14)] = self.r[14];
self.banked_spsr[bankedSpsrIndex(now)] = self.spsr;
},
}
// Grab R8 -> R12
i = 0;
while (i < 5) : (i += 1) {
self.r[8 + i] = self.bank.fiq[Bank.fiqIdx(i, next)];
self.r[8 + i] = self.banked_fiq[bankedFiqIdx(i, next)];
}
// Grab r13, r14, SPSR
switch (next) {
.User, .System => {
self.r[13] = self.bank.r[Bank.regIdx(next, .R13)];
self.r[14] = self.bank.r[Bank.regIdx(next, .R14)];
self.r[13] = self.banked_r[bankedIdx(next, .R13)];
self.r[14] = self.banked_r[bankedIdx(next, .R14)];
},
else => {
self.r[13] = self.bank.r[Bank.regIdx(next, .R13)];
self.r[14] = self.bank.r[Bank.regIdx(next, .R14)];
self.spsr = self.bank.spsr[Bank.spsrIdx(next)];
self.r[13] = self.banked_r[bankedIdx(next, .R13)];
self.r[14] = self.banked_r[bankedIdx(next, .R14)];
self.spsr = self.banked_spsr[bankedSpsrIndex(next)];
},
}
@ -439,8 +424,8 @@ pub const Arm7tdmi = struct {
self.r[13] = 0x0300_7F00;
self.r[15] = 0x0800_0000;
self.bank.r[Bank.regIdx(.Irq, .R13)] = 0x0300_7FA0;
self.bank.r[Bank.regIdx(.Supervisor, .R13)] = 0x0300_7FE0;
self.banked_r[bankedIdx(.Irq, .R13)] = 0x0300_7FA0;
self.banked_r[bankedIdx(.Supervisor, .R13)] = 0x0300_7FE0;
// self.cpsr.raw = 0x6000001F;
self.cpsr.raw = 0x0000_001F;
@ -530,10 +515,10 @@ pub const Arm7tdmi = struct {
std.debug.print("R{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\tR{}: 0x{X:0>8}\n", .{ i, self.r[i], i_1, self.r[i_1], i_2, self.r[i_2], i_3, self.r[i_3] });
}
std.debug.print("cpsr: 0x{X:0>8} ", .{self.cpsr.raw});
self.cpsr.toString();
prettyPrintPsr(&self.cpsr);
std.debug.print("spsr: 0x{X:0>8} ", .{self.spsr.raw});
self.spsr.toString();
prettyPrintPsr(&self.spsr);
std.debug.print("pipeline: {??X:0>8}\n", .{self.pipe.stage});
@ -551,6 +536,76 @@ pub const Arm7tdmi = struct {
std.debug.panic(format, args);
}
fn prettyPrintPsr(psr: *const PSR) void {
std.debug.print("[", .{});
if (psr.n.read()) std.debug.print("N", .{}) else std.debug.print("-", .{});
if (psr.z.read()) std.debug.print("Z", .{}) else std.debug.print("-", .{});
if (psr.c.read()) std.debug.print("C", .{}) else std.debug.print("-", .{});
if (psr.v.read()) std.debug.print("V", .{}) else std.debug.print("-", .{});
if (psr.i.read()) std.debug.print("I", .{}) else std.debug.print("-", .{});
if (psr.f.read()) std.debug.print("F", .{}) else std.debug.print("-", .{});
if (psr.t.read()) std.debug.print("T", .{}) else std.debug.print("-", .{});
std.debug.print("|", .{});
if (getMode(psr.mode.read())) |mode| std.debug.print("{s}", .{modeString(mode)}) else std.debug.print("---", .{});
std.debug.print("]\n", .{});
}
fn modeString(mode: Mode) []const u8 {
return switch (mode) {
.User => "usr",
.Fiq => "fiq",
.Irq => "irq",
.Supervisor => "svc",
.Abort => "abt",
.Undefined => "und",
.System => "sys",
};
}
fn mgbaLog(self: *const Self, file: *const File, opcode: u32) !void {
const thumb_fmt = "{X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} cpsr: {X:0>8} | {X:0>4}:\n";
const arm_fmt = "{X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} {X:0>8} cpsr: {X:0>8} | {X:0>8}:\n";
var buf: [0x100]u8 = [_]u8{0x00} ** 0x100; // this is larger than it needs to be
const r0 = self.r[0];
const r1 = self.r[1];
const r2 = self.r[2];
const r3 = self.r[3];
const r4 = self.r[4];
const r5 = self.r[5];
const r6 = self.r[6];
const r7 = self.r[7];
const r8 = self.r[8];
const r9 = self.r[9];
const r10 = self.r[10];
const r11 = self.r[11];
const r12 = self.r[12];
const r13 = self.r[13];
const r14 = self.r[14];
const r15 = self.r[15] -| if (self.cpsr.t.read()) 2 else @as(u32, 4);
const c_psr = self.cpsr.raw;
var log_str: []u8 = undefined;
if (self.cpsr.t.read()) {
if (opcode >> 11 == 0x1E) {
// Instruction 1 of a BL Opcode, print in ARM mode
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 });
} else {
log_str = try std.fmt.bufPrint(&buf, thumb_fmt, .{ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, c_psr, opcode });
}
} else {
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, opcode });
}
_ = try file.writeAll(log_str);
}
};
const condition_lut = [_]u16{
@ -629,22 +684,6 @@ pub const PSR = extern union {
z: Bit(u32, 30),
n: Bit(u32, 31),
raw: u32,
fn toString(self: PSR) void {
std.debug.print("[", .{});
if (self.n.read()) std.debug.print("N", .{}) else std.debug.print("-", .{});
if (self.z.read()) std.debug.print("Z", .{}) else std.debug.print("-", .{});
if (self.c.read()) std.debug.print("C", .{}) else std.debug.print("-", .{});
if (self.v.read()) std.debug.print("V", .{}) else std.debug.print("-", .{});
if (self.i.read()) std.debug.print("I", .{}) else std.debug.print("-", .{});
if (self.f.read()) std.debug.print("F", .{}) else std.debug.print("-", .{});
if (self.t.read()) std.debug.print("T", .{}) else std.debug.print("-", .{});
std.debug.print("|", .{});
if (getMode(self.mode.read())) |m| std.debug.print("{s}", .{m.toString()}) else std.debug.print("---", .{});
std.debug.print("]\n", .{});
}
};
const Mode = enum(u5) {
@ -655,18 +694,11 @@ const Mode = enum(u5) {
Abort = 0b10111,
Undefined = 0b11011,
System = 0b11111,
};
fn toString(self: Mode) []const u8 {
return switch (self) {
.User => "usr",
.Fiq => "fiq",
.Irq => "irq",
.Supervisor => "svc",
.Abort => "abt",
.Undefined => "und",
.System => "sys",
};
}
const BankedKind = enum(u1) {
R13 = 0,
R14,
};
fn getMode(bits: u5) ?Mode {