diff --git a/src/cpu/m6502/alu.zig b/src/cpu/m6502/alu.zig new file mode 100644 index 0000000..784d974 --- /dev/null +++ b/src/cpu/m6502/alu.zig @@ -0,0 +1,102 @@ +const types = @import("types.zig"); +const Instruction = @import("instruction.zig"); + +// pure ALU arithmetic & bitwise operations monomorphized for Cpu +const Alu = @This(); + +pub inline fn updateNZ(cpu: anytype, value: u8) void { + cpu.registers.status.zero = (value == 0); + cpu.registers.status.negative = (value & 0x80) != 0; +} + +pub fn compare(cpu: anytype, register: u8, value: u8) void { + const result = register -% value; + cpu.registers.status.carry = register >= value; + updateNZ(cpu, result); +} + +pub fn adc(cpu: anytype, value: u8) void { + const accumulator = cpu.registers.a; + const carry: u16 = @intFromBool(cpu.registers.status.carry); + + const sum = @as(u16, accumulator) + @as(u16, value) + carry; + const binary_result: u8 = @truncate(sum); + + const overflow = ((accumulator ^ binary_result) & (value ^ binary_result) & 0x80) != 0; + const decimal = cpu.model.features().decimal_arithmetic and cpu.registers.status.decimal; + + if (decimal and cpu.model.features().cmos_instructions) cpu.tick(); + + const out: struct { value: u8, carry: bool } = if (decimal) decimal: { + const low_sum = @as(u16, accumulator & 0x0f) + @as(u16, value & 0x0f) + carry; + var adjusted = sum; + if (low_sum > 9) adjusted += 6; + const carry_out = adjusted > 0x99; + if (carry_out) adjusted += 0x60; + break :decimal .{ .value = @truncate(adjusted), .carry = carry_out }; + } else .{ .value = binary_result, .carry = sum > 0xff }; + + cpu.registers.status.carry = out.carry; + cpu.registers.status.overflow = overflow; + cpu.registers.a = out.value; + updateNZ(cpu, out.value); +} + +pub fn sbc(cpu: anytype, value: u8) void { + const accumulator = cpu.registers.a; + const borrow: u16 = if (cpu.registers.status.carry) 0 else 1; + const diff: i16 = @as(i16, accumulator) - @as(i16, value) - @as(i16, @intCast(borrow)); + const binary_result: u8 = @truncate(@as(u16, @bitCast(diff))); + + const overflow = ((accumulator ^ binary_result) & (accumulator ^ value) & 0x80) != 0; + const no_borrow = diff >= 0; + const decimal = cpu.model.features().decimal_arithmetic and cpu.registers.status.decimal; + + if (decimal and cpu.model.features().cmos_instructions) cpu.tick(); + + const result: u8 = if (decimal) decimal: { + var low: i16 = @as(i16, accumulator & 0x0f) - @as(i16, value & 0x0f) - @as(i16, @intCast(borrow)); + var high: i16 = @as(i16, accumulator >> 4) - @as(i16, value >> 4); + if (low < 0) { + low -= 6; + high -= 1; + } + if (high < 0) high -= 6; + break :decimal (@as(u8, @intCast(high & 0x0f)) << 4) | @as(u8, @intCast(low & 0x0f)); + } else binary_result; + + cpu.registers.status.carry = no_borrow; + cpu.registers.status.overflow = overflow; + cpu.registers.a = result; + updateNZ(cpu, result); +} + +pub fn asl(cpu: anytype, value: u8) u8 { + cpu.registers.status.carry = (value & 0x80) != 0; + const result = value << 1; + updateNZ(cpu, result); + return result; +} + +pub fn lsr(cpu: anytype, value: u8) u8 { + cpu.registers.status.carry = (value & 1) != 0; + const result = value >> 1; + updateNZ(cpu, result); + return result; +} + +pub fn rol(cpu: anytype, value: u8) u8 { + const carry: u8 = @intFromBool(cpu.registers.status.carry); + cpu.registers.status.carry = (value & 0x80) != 0; + const result = (value << 1) | carry; + updateNZ(cpu, result); + return result; +} + +pub fn ror(cpu: anytype, value: u8) u8 { + const carry: u8 = if (cpu.registers.status.carry) 0x80 else 0; + cpu.registers.status.carry = (value & 1) != 0; + const result = (value >> 1) | carry; + updateNZ(cpu, result); + return result; +} diff --git a/src/cpu/m6502/types.zig b/src/cpu/m6502/types.zig index b1a06a2..4ca5a34 100644 --- a/src/cpu/m6502/types.zig +++ b/src/cpu/m6502/types.zig @@ -47,4 +47,40 @@ pub const Model = enum { ricoh2a07, wdc65c02, r65c02, + + pub fn features(self: Model) CpuFeatures { + return switch (self) { + .mos6502, .mos6507, .mos6510 => .{ + .decimal_arithmetic = true, + .undocumented_opcodes = true, + .indirect_jump_bug = true, + }, + .ricoh2a03, .ricoh2a07 => .{ + .undocumented_opcodes = true, + .indirect_jump_bug = true, + }, + .wdc65c02 => .{ + .decimal_arithmetic = true, + .cmos_instructions = true, + .fixed_indirect_jump = true, + .wait_and_stop = true, + }, + .r65c02 => .{ + .decimal_arithmetic = true, + .cmos_instructions = true, + .fixed_indirect_jump = true, + }, + }; + } +}; + +pub const CpuFeatures = packed struct(u8) { + decimal_arithmetic: bool = false, + cmos_instructions: bool = false, + undocumented_opcodes: bool = false, + indirect_jump_bug: bool = false, + fixed_indirect_jump: bool = false, + wait_and_stop: bool = false, + + _reserved: u2 = 0, };