add 6502 disassembler
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
const Model = types.Model;
|
||||
const Instruction = @import("instruction.zig");
|
||||
const Operation = Instruction.Operation;
|
||||
const AddressingMode = Instruction.AddressingMode;
|
||||
|
||||
pub const DisassembledInstruction = struct {
|
||||
address: u16,
|
||||
opcode: u8,
|
||||
bytes: [3]u8,
|
||||
len: u8,
|
||||
operation: Operation,
|
||||
mode: AddressingMode,
|
||||
|
||||
pub fn format(self: DisassembledInstruction, writer: anytype) !void {
|
||||
try writer.print("{X:0>4}: ", .{self.address});
|
||||
|
||||
for (0..self.len) |i| {
|
||||
try writer.print("{X:0>2} ", .{self.bytes[i]});
|
||||
}
|
||||
for (self.len..3) |_| {
|
||||
try writer.writeAll(" ");
|
||||
}
|
||||
|
||||
try writer.writeAll(" ");
|
||||
|
||||
const op_name = @tagName(self.operation);
|
||||
var upper_buf: [8]u8 = undefined;
|
||||
for (op_name, 0..) |c, i| {
|
||||
upper_buf[i] = std.ascii.toUpper(c);
|
||||
}
|
||||
try writer.writeAll(upper_buf[0..op_name.len]);
|
||||
|
||||
switch (self.mode) {
|
||||
.implied => {},
|
||||
.accumulator => try writer.writeAll(" A"),
|
||||
.immediate => try writer.print(" #${X:0>2}", .{self.bytes[1]}),
|
||||
.zero_page => try writer.print(" ${X:0>2}", .{self.bytes[1]}),
|
||||
.zero_page_x => try writer.print(" ${X:0>2},X", .{self.bytes[1]}),
|
||||
.zero_page_y => try writer.print(" ${X:0>2},Y", .{self.bytes[1]}),
|
||||
.relative => {
|
||||
const rel = @as(i8, @bitCast(self.bytes[1]));
|
||||
const target = @as(u16, @bitCast(@as(i16, @intCast(self.address)) +% 2 +% rel));
|
||||
try writer.print(" ${X:0>4}", .{target});
|
||||
},
|
||||
.absolute => {
|
||||
const target = @as(u16, self.bytes[1]) | (@as(u16, self.bytes[2]) << 8);
|
||||
try writer.print(" ${X:0>4}", .{target});
|
||||
},
|
||||
.absolute_x => {
|
||||
const target = @as(u16, self.bytes[1]) | (@as(u16, self.bytes[2]) << 8);
|
||||
try writer.print(" ${X:0>4},X", .{target});
|
||||
},
|
||||
.absolute_y => {
|
||||
const target = @as(u16, self.bytes[1]) | (@as(u16, self.bytes[2]) << 8);
|
||||
try writer.print(" ${X:0>4},Y", .{target});
|
||||
},
|
||||
.indirect => {
|
||||
const target = @as(u16, self.bytes[1]) | (@as(u16, self.bytes[2]) << 8);
|
||||
try writer.print(" (${X:0>4})", .{target});
|
||||
},
|
||||
.indexed_indirect_x => try writer.print(" (${X:0>2},X)", .{self.bytes[1]}),
|
||||
.indirect_indexed_y => try writer.print(" (${X:0>2}),Y", .{self.bytes[1]}),
|
||||
.zero_page_indirect => try writer.print(" (${X:0>2})", .{self.bytes[1]}),
|
||||
.absolute_indexed_indirect => {
|
||||
const target = @as(u16, self.bytes[1]) | (@as(u16, self.bytes[2]) << 8);
|
||||
try writer.print(" (${X:0>4},X)", .{target});
|
||||
},
|
||||
.zero_page_relative => {
|
||||
const rel = @as(i8, @bitCast(self.bytes[2]));
|
||||
const target = @as(u16, @bitCast(@as(i16, @intCast(self.address)) +% 3 +% rel));
|
||||
try writer.print(" ${X:0>2},${X:0>4}", .{ self.bytes[1], target });
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn disassemble(model: Model, address: u16, bus: anytype) DisassembledInstruction {
|
||||
const opcode = bus.read(address & model.addressMask());
|
||||
const inst = model.instruction(opcode);
|
||||
const len: u8 = switch (inst.mode) {
|
||||
.implied, .accumulator => 1,
|
||||
.immediate, .zero_page, .zero_page_x, .zero_page_y, .relative, .zero_page_indirect, .indexed_indirect_x, .indirect_indexed_y => 2,
|
||||
.absolute, .absolute_x, .absolute_y, .indirect, .absolute_indexed_indirect, .zero_page_relative => 3,
|
||||
};
|
||||
|
||||
var bytes = [3]u8{ opcode, 0, 0 };
|
||||
if (len > 1) bytes[1] = bus.read((address +% 1) & model.addressMask());
|
||||
if (len > 2) bytes[2] = bus.read((address +% 2) & model.addressMask());
|
||||
|
||||
return .{
|
||||
.address = address,
|
||||
.opcode = opcode,
|
||||
.bytes = bytes,
|
||||
.len = len,
|
||||
.operation = inst.operation,
|
||||
.mode = inst.mode,
|
||||
};
|
||||
}
|
||||
|
||||
test "Zero-Allocation Disassembler Formatting" {
|
||||
const testing = std.testing;
|
||||
const TestBus = @import("conformance.zig").TestBus;
|
||||
|
||||
var ram = TestBus{};
|
||||
// LDA #$42 at 0x8000
|
||||
ram.write(0x8000, 0xA9);
|
||||
ram.write(0x8001, 0x42);
|
||||
|
||||
const disassembled = disassemble(.mos6502, 0x8000, &ram);
|
||||
try testing.expectEqual(@as(u8, 2), disassembled.len);
|
||||
try testing.expectEqual(Operation.lda, disassembled.operation);
|
||||
|
||||
var buf: [64]u8 = undefined;
|
||||
var pos: usize = 0;
|
||||
const BufferWriter = struct {
|
||||
buf: []u8,
|
||||
pos: *usize,
|
||||
pub fn writeAll(self: @This(), bytes: []const u8) !void {
|
||||
@memcpy(self.buf[self.pos.* .. self.pos.* + bytes.len], bytes);
|
||||
self.pos.* += bytes.len;
|
||||
}
|
||||
pub fn print(self: @This(), comptime fmt: []const u8, args: anytype) !void {
|
||||
const printed = try std.fmt.bufPrint(self.buf[self.pos.*..], fmt, args);
|
||||
self.pos.* += printed.len;
|
||||
}
|
||||
};
|
||||
|
||||
const bw = BufferWriter{ .buf = &buf, .pos = &pos };
|
||||
try disassembled.format(bw);
|
||||
|
||||
try testing.expectEqualSlices(u8, "8000: A9 42 LDA #$42", buf[0..pos]);
|
||||
}
|
||||
@@ -1 +1,5 @@
|
||||
pub const m6502 = @import("m6502/root.zig");
|
||||
|
||||
test {
|
||||
_ = @import("m6502/disassembler.zig");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user