implement 6502 addressing modes and refactor utils
This commit is contained in:
@@ -0,0 +1,246 @@
|
|||||||
|
const Instruction = @import("instruction.zig");
|
||||||
|
const AddressingMode = Instruction.AddressingMode;
|
||||||
|
|
||||||
|
const utils = @import("utils.zig");
|
||||||
|
|
||||||
|
// addressing mode operations monomorphized for Cpu
|
||||||
|
pub const Addressing = @This();
|
||||||
|
|
||||||
|
pub const addRelative = utils.addRelative;
|
||||||
|
pub const pageCrossed = utils.pageCrossed;
|
||||||
|
pub const bitNumber = utils.bitNumber;
|
||||||
|
|
||||||
|
pub fn readOperand(cpu: anytype, mode: AddressingMode) u8 {
|
||||||
|
return switch (mode) {
|
||||||
|
.immediate => cpu.fetchByte(),
|
||||||
|
|
||||||
|
.zero_page => blk: {
|
||||||
|
const address = cpu.fetchByte();
|
||||||
|
break :blk cpu.readCycle(address);
|
||||||
|
},
|
||||||
|
|
||||||
|
.zero_page_x => blk: {
|
||||||
|
const base = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(base);
|
||||||
|
break :blk cpu.readCycle(base +% cpu.registers.x);
|
||||||
|
},
|
||||||
|
|
||||||
|
.zero_page_y => blk: {
|
||||||
|
const base = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(base);
|
||||||
|
break :blk cpu.readCycle(base +% cpu.registers.y);
|
||||||
|
},
|
||||||
|
|
||||||
|
.zero_page_indirect => blk: {
|
||||||
|
const pointer = cpu.fetchByte();
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
break :blk cpu.readCycle(utils.makeWord(low, high));
|
||||||
|
},
|
||||||
|
|
||||||
|
.absolute => blk: {
|
||||||
|
const address = cpu.fetchWord();
|
||||||
|
break :blk cpu.readCycle(address);
|
||||||
|
},
|
||||||
|
|
||||||
|
.absolute_x => readAbsoluteIndexed(cpu, cpu.registers.x),
|
||||||
|
.absolute_y => readAbsoluteIndexed(cpu, cpu.registers.y),
|
||||||
|
.indexed_indirect_x => readIndexedIndirectX(cpu),
|
||||||
|
.indirect_indexed_y => readIndirectIndexedY(cpu),
|
||||||
|
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readAbsoluteIndexed(cpu: anytype, index: u8) u8 {
|
||||||
|
const low = cpu.fetchByte();
|
||||||
|
const high = cpu.fetchByte();
|
||||||
|
const base = utils.makeWord(low, high);
|
||||||
|
const address = base +% index;
|
||||||
|
|
||||||
|
if (pageCrossed(base, address)) {
|
||||||
|
if (@TypeOf(cpu.*).features.cmos_instructions) {
|
||||||
|
_ = cpu.readCycle(cpu.registers.pc -% 1);
|
||||||
|
} else {
|
||||||
|
const dummy = (base & 0xff00) | (address & 0x00ff);
|
||||||
|
_ = cpu.readCycle(dummy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu.readCycle(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readIndexedIndirectX(cpu: anytype) u8 {
|
||||||
|
const operand = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(operand);
|
||||||
|
const pointer = operand +% cpu.registers.x;
|
||||||
|
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
return cpu.readCycle(utils.makeWord(low, high));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readIndirectIndexedY(cpu: anytype) u8 {
|
||||||
|
const pointer = cpu.fetchByte();
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
|
||||||
|
const base = utils.makeWord(low, high);
|
||||||
|
const address = base +% cpu.registers.y;
|
||||||
|
|
||||||
|
if (pageCrossed(base, address)) {
|
||||||
|
if (@TypeOf(cpu.*).features.cmos_instructions) {
|
||||||
|
_ = cpu.readCycle(cpu.registers.pc -% 1);
|
||||||
|
} else {
|
||||||
|
const dummy = (base & 0xff00) | (address & 0x00ff);
|
||||||
|
_ = cpu.readCycle(dummy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu.readCycle(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeOperand(cpu: anytype, mode: AddressingMode, value: u8) void {
|
||||||
|
switch (mode) {
|
||||||
|
.zero_page => {
|
||||||
|
const address = cpu.fetchByte();
|
||||||
|
cpu.writeCycle(address, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.zero_page_x => {
|
||||||
|
const base = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(base);
|
||||||
|
cpu.writeCycle(base +% cpu.registers.x, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.zero_page_y => {
|
||||||
|
const base = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(base);
|
||||||
|
cpu.writeCycle(base +% cpu.registers.y, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.zero_page_indirect => {
|
||||||
|
const pointer = cpu.fetchByte();
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
cpu.writeCycle(utils.makeWord(low, high), value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.absolute => {
|
||||||
|
const address = cpu.fetchWord();
|
||||||
|
cpu.writeCycle(address, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.absolute_x => writeAbsoluteIndexed(cpu, cpu.registers.x, value),
|
||||||
|
.absolute_y => writeAbsoluteIndexed(cpu, cpu.registers.y, value),
|
||||||
|
.indexed_indirect_x => writeIndexedIndirectX(cpu, value),
|
||||||
|
.indirect_indexed_y => writeIndirectIndexedY(cpu, value),
|
||||||
|
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeAbsoluteIndexed(cpu: anytype, index: u8, value: u8) void {
|
||||||
|
const low = cpu.fetchByte();
|
||||||
|
const high = cpu.fetchByte();
|
||||||
|
const base = utils.makeWord(low, high);
|
||||||
|
const address = base +% index;
|
||||||
|
|
||||||
|
const dummy = (base & 0xff00) | (address & 0x00ff);
|
||||||
|
_ = cpu.readCycle(dummy);
|
||||||
|
|
||||||
|
cpu.writeCycle(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeIndexedIndirectX(cpu: anytype, value: u8) void {
|
||||||
|
const operand = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(operand);
|
||||||
|
const pointer = operand +% cpu.registers.x;
|
||||||
|
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
cpu.writeCycle(utils.makeWord(low, high), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeIndirectIndexedY(cpu: anytype, value: u8) void {
|
||||||
|
const pointer = cpu.fetchByte();
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
|
||||||
|
const base = utils.makeWord(low, high);
|
||||||
|
const address = base +% cpu.registers.y;
|
||||||
|
|
||||||
|
const dummy = (base & 0xff00) | (address & 0x00ff);
|
||||||
|
_ = cpu.readCycle(dummy);
|
||||||
|
|
||||||
|
cpu.writeCycle(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifyAddress(
|
||||||
|
cpu: anytype,
|
||||||
|
address: u16,
|
||||||
|
comptime operation: fn (*@TypeOf(cpu.*), u8) u8,
|
||||||
|
) void {
|
||||||
|
const old = cpu.readCycle(address);
|
||||||
|
|
||||||
|
if (@TypeOf(cpu.*).features.cmos_instructions) {
|
||||||
|
_ = cpu.readCycle(address);
|
||||||
|
} else {
|
||||||
|
cpu.writeCycle(address, old);
|
||||||
|
}
|
||||||
|
|
||||||
|
const new = operation(cpu, old);
|
||||||
|
cpu.writeCycle(address, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifyAbsoluteIndexed(
|
||||||
|
cpu: anytype,
|
||||||
|
index: u8,
|
||||||
|
comptime operation: fn (*@TypeOf(cpu.*), u8) u8,
|
||||||
|
) void {
|
||||||
|
const low = cpu.fetchByte();
|
||||||
|
const high = cpu.fetchByte();
|
||||||
|
const base = utils.makeWord(low, high);
|
||||||
|
const address = base +% index;
|
||||||
|
|
||||||
|
if (@TypeOf(cpu.*).features.cmos_instructions) {
|
||||||
|
if (pageCrossed(base, address)) {
|
||||||
|
_ = cpu.readCycle(cpu.registers.pc -% 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const dummy = (base & 0xff00) | (address & 0x00ff);
|
||||||
|
_ = cpu.readCycle(dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyAddress(cpu, address, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifyIndexedIndirectX(
|
||||||
|
cpu: anytype,
|
||||||
|
comptime operation: fn (*@TypeOf(cpu.*), u8) u8,
|
||||||
|
) void {
|
||||||
|
const operand = cpu.fetchByte();
|
||||||
|
_ = cpu.readCycle(operand);
|
||||||
|
const pointer = operand +% cpu.registers.x;
|
||||||
|
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
modifyAddress(cpu, utils.makeWord(low, high), operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifyIndirectIndexedY(
|
||||||
|
cpu: anytype,
|
||||||
|
comptime operation: fn (*@TypeOf(cpu.*), u8) u8,
|
||||||
|
) void {
|
||||||
|
const pointer = cpu.fetchByte();
|
||||||
|
const low = cpu.readCycle(pointer);
|
||||||
|
const high = cpu.readCycle(pointer +% 1);
|
||||||
|
|
||||||
|
const base = utils.makeWord(low, high);
|
||||||
|
const address = base +% cpu.registers.y;
|
||||||
|
|
||||||
|
const dummy = (base & 0xff00) | (address & 0x00ff);
|
||||||
|
_ = cpu.readCycle(dummy);
|
||||||
|
|
||||||
|
modifyAddress(cpu, address, operation);
|
||||||
|
}
|
||||||
+1
-11
@@ -1,16 +1,6 @@
|
|||||||
|
// common 6502 helper utilities
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub inline fn packAddress(low: u8, high: u8) u16 {
|
|
||||||
return @as(u16, low) | (@as(u16, high) << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn unpackAddress(addr: u16) struct { low: u8, high: u8 } {
|
|
||||||
return .{
|
|
||||||
.low = @truncate(addr),
|
|
||||||
.high = @truncate(addr >> 8),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn makeWord(low: u8, high: u8) u16 {
|
pub inline fn makeWord(low: u8, high: u8) u16 {
|
||||||
return @as(u16, low) | (@as(u16, high) << 8);
|
return @as(u16, low) | (@as(u16, high) << 8);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user