From fee48f34fe83352b6e41cf5cd03ba5f586673ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C5=9Een?= Date: Sun, 26 Jul 2026 17:30:12 +0200 Subject: [PATCH] implement 6502 addressing modes and refactor utils --- src/cpu/m6502/addressing.zig | 246 +++++++++++++++++++++++++++++++++++ src/cpu/m6502/utils.zig | 12 +- 2 files changed, 247 insertions(+), 11 deletions(-) create mode 100644 src/cpu/m6502/addressing.zig diff --git a/src/cpu/m6502/addressing.zig b/src/cpu/m6502/addressing.zig new file mode 100644 index 0000000..f5fcb2d --- /dev/null +++ b/src/cpu/m6502/addressing.zig @@ -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); +} diff --git a/src/cpu/m6502/utils.zig b/src/cpu/m6502/utils.zig index a0e1ac6..1d869d0 100644 --- a/src/cpu/m6502/utils.zig +++ b/src/cpu/m6502/utils.zig @@ -1,16 +1,6 @@ +// common 6502 helper utilities 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 { return @as(u16, low) | (@as(u16, high) << 8); }