add alu operations and cpu features
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
@@ -47,4 +47,40 @@ pub const Model = enum {
|
|||||||
ricoh2a07,
|
ricoh2a07,
|
||||||
wdc65c02,
|
wdc65c02,
|
||||||
r65c02,
|
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,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user