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); }