finish ch3.3
This commit is contained in:
parent
c8d9a02de1
commit
362e5b8fda
@ -4,4 +4,5 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitflags = "2.6.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
681
src/cpu.rs
681
src/cpu.rs
@ -1,6 +1,38 @@
|
|||||||
use crate::opcodes;
|
use crate::opcodes;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// # Status Register (P) http://wiki.nesdev.com/w/index.php/Status_flags
|
||||||
|
///
|
||||||
|
/// 7 6 5 4 3 2 1 0
|
||||||
|
/// N V _ B D I Z C
|
||||||
|
/// | | | | | | +--- Carry Flag
|
||||||
|
/// | | | | | +----- Zero Flag
|
||||||
|
/// | | | | +------- Interrupt Disable
|
||||||
|
/// | | | +--------- Decimal Mode (not used on NES)
|
||||||
|
/// | | +----------- Break Command
|
||||||
|
/// | +--------------- Overflow Flag
|
||||||
|
/// +----------------- Negative Flag
|
||||||
|
///
|
||||||
|
pub struct CpuFlags: u8 {
|
||||||
|
const CARRY = 0b00000001;
|
||||||
|
const ZERO = 0b00000010;
|
||||||
|
const INTERRUPT_DISABLE = 0b00000100;
|
||||||
|
const DECIMAL_MODE = 0b00001000;
|
||||||
|
const BREAK = 0b00010000;
|
||||||
|
const BREAK2 = 0b00100000;
|
||||||
|
const OVERFLOW = 0b01000000;
|
||||||
|
const NEGATIV = 0b10000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stack at page 2: 0x0100 - 0x01ff
|
||||||
|
const STACK: u16 = 0x0100;
|
||||||
|
|
||||||
|
// 0x01ff is for first function call
|
||||||
|
const STACK_RESET: u8 = 0xFD;
|
||||||
|
|
||||||
/// The `CPU` struct represents the central processing unit of the NES emulator.
|
/// The `CPU` struct represents the central processing unit of the NES emulator.
|
||||||
/// It contains the following fields:
|
/// It contains the following fields:
|
||||||
/// * `register_a`: The accumulator register, used for arithmetic operations.
|
/// * `register_a`: The accumulator register, used for arithmetic operations.
|
||||||
@ -11,8 +43,9 @@ pub struct CPU {
|
|||||||
pub register_a: u8,
|
pub register_a: u8,
|
||||||
pub register_x: u8,
|
pub register_x: u8,
|
||||||
pub register_y: u8,
|
pub register_y: u8,
|
||||||
pub status: u8,
|
pub status: CpuFlags,
|
||||||
pub program_counter: u16,
|
pub program_counter: u16,
|
||||||
|
pub stack_pointer: u8,
|
||||||
memory: [u8; 0xFFFF],
|
memory: [u8; 0xFFFF],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,12 +105,18 @@ impl CPU {
|
|||||||
register_a: 0,
|
register_a: 0,
|
||||||
register_x: 0,
|
register_x: 0,
|
||||||
register_y: 0,
|
register_y: 0,
|
||||||
status: 0,
|
status: CpuFlags::from_bits_truncate(0b0010_0100),
|
||||||
program_counter: 0,
|
program_counter: 0,
|
||||||
|
stack_pointer: STACK_RESET,
|
||||||
memory: [0; 0xFFFF],
|
memory: [0; 0xFFFF],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_register_a(&mut self, value: u8) {
|
||||||
|
self.register_a = value;
|
||||||
|
self.update_zero_and_negative_flags(self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_and_run(&mut self, program: Vec<u8>) {
|
pub fn load_and_run(&mut self, program: Vec<u8>) {
|
||||||
self.load(program);
|
self.load(program);
|
||||||
self.reset();
|
self.reset();
|
||||||
@ -100,6 +139,7 @@ impl CPU {
|
|||||||
let opcode = opcodes
|
let opcode = opcodes
|
||||||
.get(&code)
|
.get(&code)
|
||||||
.unwrap_or_else(|| panic!("opcode {:x} is not supported", code));
|
.unwrap_or_else(|| panic!("opcode {:x} is not supported", code));
|
||||||
|
|
||||||
match code {
|
match code {
|
||||||
0xa9 | 0xa5 | 0xb5 | 0xad | 0xbd | 0xb9 | 0xa1 | 0xb1 => {
|
0xa9 | 0xa5 | 0xb5 | 0xad | 0xbd | 0xb9 | 0xa1 | 0xb1 => {
|
||||||
self.lda(&opcode.mode);
|
self.lda(&opcode.mode);
|
||||||
@ -113,6 +153,274 @@ impl CPU {
|
|||||||
0xe8 => self.inx(),
|
0xe8 => self.inx(),
|
||||||
0x00 => return,
|
0x00 => return,
|
||||||
|
|
||||||
|
/* CLD */ 0xd8 => self.status.remove(CpuFlags::DECIMAL_MODE),
|
||||||
|
|
||||||
|
/* CLI */ 0x58 => self.status.remove(CpuFlags::INTERRUPT_DISABLE),
|
||||||
|
|
||||||
|
/* CLV */ 0xb8 => self.status.remove(CpuFlags::OVERFLOW),
|
||||||
|
|
||||||
|
/* CLC */ 0x18 => self.clear_carry_flag(),
|
||||||
|
|
||||||
|
/* SEC */ 0x38 => self.set_carry_flag(),
|
||||||
|
|
||||||
|
/* SEI */ 0x78 => self.status.insert(CpuFlags::INTERRUPT_DISABLE),
|
||||||
|
|
||||||
|
/* SED */ 0xf8 => self.status.insert(CpuFlags::DECIMAL_MODE),
|
||||||
|
|
||||||
|
/* PHA */ 0x48 => self.stack_push(self.register_a),
|
||||||
|
|
||||||
|
/* PLA */
|
||||||
|
0x68 => {
|
||||||
|
self.pla();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PHP */
|
||||||
|
0x08 => {
|
||||||
|
self.php();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PLP */
|
||||||
|
0x28 => {
|
||||||
|
self.plp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADC */
|
||||||
|
0x69 | 0x65 | 0x75 | 0x6d | 0x7d | 0x79 | 0x61 | 0x71 => {
|
||||||
|
self.adc(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SBC */
|
||||||
|
0xe9 | 0xe5 | 0xf5 | 0xed | 0xfd | 0xf9 | 0xe1 | 0xf1 => {
|
||||||
|
self.sbc(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AND */
|
||||||
|
0x29 | 0x25 | 0x35 | 0x2d | 0x3d | 0x39 | 0x21 | 0x31 => {
|
||||||
|
self.and(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EOR */
|
||||||
|
0x49 | 0x45 | 0x55 | 0x4d | 0x5d | 0x59 | 0x41 | 0x51 => {
|
||||||
|
self.eor(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ORA */
|
||||||
|
0x09 | 0x05 | 0x15 | 0x0d | 0x1d | 0x19 | 0x01 | 0x11 => {
|
||||||
|
self.ora(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LSR */ 0x4a => self.lsr_accumulator(),
|
||||||
|
|
||||||
|
/* LSR */
|
||||||
|
0x46 | 0x56 | 0x4e | 0x5e => {
|
||||||
|
self.lsr(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*ASL*/ 0x0a => self.asl_accumulator(),
|
||||||
|
|
||||||
|
/* ASL */
|
||||||
|
0x06 | 0x16 | 0x0e | 0x1e => {
|
||||||
|
self.asl(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*ROL*/ 0x2a => self.rol_accumulator(),
|
||||||
|
|
||||||
|
/* ROL */
|
||||||
|
0x26 | 0x36 | 0x2e | 0x3e => {
|
||||||
|
self.rol(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ROR */ 0x6a => self.ror_accumulator(),
|
||||||
|
|
||||||
|
/* ROR */
|
||||||
|
0x66 | 0x76 | 0x6e | 0x7e => {
|
||||||
|
self.ror(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INC */
|
||||||
|
0xe6 | 0xf6 | 0xee | 0xfe => {
|
||||||
|
self.inc(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INY */
|
||||||
|
0xc8 => self.iny(),
|
||||||
|
|
||||||
|
/* DEC */
|
||||||
|
0xc6 | 0xd6 | 0xce | 0xde => {
|
||||||
|
self.dec(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEX */
|
||||||
|
0xca => {
|
||||||
|
self.dex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEY */
|
||||||
|
0x88 => {
|
||||||
|
self.dey();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CMP */
|
||||||
|
0xc9 | 0xc5 | 0xd5 | 0xcd | 0xdd | 0xd9 | 0xc1 | 0xd1 => {
|
||||||
|
self.compare(&opcode.mode, self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CPY */
|
||||||
|
0xc0 | 0xc4 | 0xcc => {
|
||||||
|
self.compare(&opcode.mode, self.register_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CPX */
|
||||||
|
0xe0 | 0xe4 | 0xec => self.compare(&opcode.mode, self.register_x),
|
||||||
|
|
||||||
|
/* JMP Absolute */
|
||||||
|
0x4c => {
|
||||||
|
let mem_address = self.mem_read_u16(self.program_counter);
|
||||||
|
self.program_counter = mem_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JMP Indirect */
|
||||||
|
0x6c => {
|
||||||
|
let mem_address = self.mem_read_u16(self.program_counter);
|
||||||
|
// let indirect_ref = self.mem_read_u16(mem_address);
|
||||||
|
//6502 bug mode with with page boundary:
|
||||||
|
// if address $3000 contains $40, $30FF contains $80, and $3100 contains $50,
|
||||||
|
// the result of JMP ($30FF) will be a transfer of control to $4080 rather than $5080 as you intended
|
||||||
|
// i.e. the 6502 took the low byte of the address from $30FF and the high byte from $3000
|
||||||
|
|
||||||
|
let indirect_ref = if mem_address & 0x00FF == 0x00FF {
|
||||||
|
let lo = self.mem_read(mem_address);
|
||||||
|
let hi = self.mem_read(mem_address & 0xFF00);
|
||||||
|
(hi as u16) << 8 | (lo as u16)
|
||||||
|
} else {
|
||||||
|
self.mem_read_u16(mem_address)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.program_counter = indirect_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JSR */
|
||||||
|
0x20 => {
|
||||||
|
self.stack_push_u16(self.program_counter + 2 - 1);
|
||||||
|
let target_address = self.mem_read_u16(self.program_counter);
|
||||||
|
self.program_counter = target_address
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RTS */
|
||||||
|
0x60 => {
|
||||||
|
self.program_counter = self.stack_pop_u16() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RTI */
|
||||||
|
0x40 => {
|
||||||
|
self.status = CpuFlags::from_bits_truncate(self.stack_pop());
|
||||||
|
self.status.remove(CpuFlags::BREAK);
|
||||||
|
self.status.insert(CpuFlags::BREAK2);
|
||||||
|
|
||||||
|
self.program_counter = self.stack_pop_u16();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BNE */
|
||||||
|
0xd0 => {
|
||||||
|
self.branch(!self.status.contains(CpuFlags::ZERO));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BVS */
|
||||||
|
0x70 => {
|
||||||
|
self.branch(self.status.contains(CpuFlags::OVERFLOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BVC */
|
||||||
|
0x50 => {
|
||||||
|
self.branch(!self.status.contains(CpuFlags::OVERFLOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BPL */
|
||||||
|
0x10 => {
|
||||||
|
self.branch(!self.status.contains(CpuFlags::NEGATIV));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BMI */
|
||||||
|
0x30 => {
|
||||||
|
self.branch(self.status.contains(CpuFlags::NEGATIV));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BEQ */
|
||||||
|
0xf0 => {
|
||||||
|
self.branch(self.status.contains(CpuFlags::ZERO));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BCS */
|
||||||
|
0xb0 => {
|
||||||
|
self.branch(self.status.contains(CpuFlags::CARRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BCC */
|
||||||
|
0x90 => {
|
||||||
|
self.branch(!self.status.contains(CpuFlags::CARRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BIT */
|
||||||
|
0x24 | 0x2c => {
|
||||||
|
self.bit(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STX */
|
||||||
|
0x86 | 0x96 | 0x8e => {
|
||||||
|
let addr = self.get_operand_address(&opcode.mode);
|
||||||
|
self.mem_write(addr, self.register_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STY */
|
||||||
|
0x84 | 0x94 | 0x8c => {
|
||||||
|
let addr = self.get_operand_address(&opcode.mode);
|
||||||
|
self.mem_write(addr, self.register_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LDX */
|
||||||
|
0xa2 | 0xa6 | 0xb6 | 0xae | 0xbe => {
|
||||||
|
self.ldx(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LDY */
|
||||||
|
0xa0 | 0xa4 | 0xb4 | 0xac | 0xbc => {
|
||||||
|
self.ldy(&opcode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOP */
|
||||||
|
0xea => {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TAY */
|
||||||
|
0xa8 => {
|
||||||
|
self.register_y = self.register_a;
|
||||||
|
self.update_zero_and_negative_flags(self.register_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TSX */
|
||||||
|
0xba => {
|
||||||
|
self.register_x = self.stack_pointer;
|
||||||
|
self.update_zero_and_negative_flags(self.register_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TXA */
|
||||||
|
0x8a => {
|
||||||
|
self.register_a = self.register_x;
|
||||||
|
self.update_zero_and_negative_flags(self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TXS */
|
||||||
|
0x9a => {
|
||||||
|
self.stack_pointer = self.register_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TYA */
|
||||||
|
0x98 => {
|
||||||
|
self.register_a = self.register_y;
|
||||||
|
self.update_zero_and_negative_flags(self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,33 +434,41 @@ impl CPU {
|
|||||||
self.register_a = 0;
|
self.register_a = 0;
|
||||||
self.register_x = 0;
|
self.register_x = 0;
|
||||||
self.register_y = 0;
|
self.register_y = 0;
|
||||||
self.status = 0;
|
self.stack_pointer = STACK_RESET;
|
||||||
|
self.status = CpuFlags::from_bits_truncate(0b0010_0100);
|
||||||
self.program_counter = self.mem_read_u16(0xFFFC);
|
self.program_counter = self.mem_read_u16(0xFFFC);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_operand_address(&self, mode: &AddressingMode) -> u16 {
|
fn get_operand_address(&self, mode: &AddressingMode) -> u16 {
|
||||||
match mode {
|
match mode {
|
||||||
AddressingMode::Immediate => self.program_counter,
|
AddressingMode::Immediate => self.program_counter,
|
||||||
|
|
||||||
AddressingMode::ZeroPage => self.mem_read(self.program_counter) as u16,
|
AddressingMode::ZeroPage => self.mem_read(self.program_counter) as u16,
|
||||||
|
|
||||||
AddressingMode::Absolute => self.mem_read_u16(self.program_counter),
|
AddressingMode::Absolute => self.mem_read_u16(self.program_counter),
|
||||||
|
|
||||||
AddressingMode::ZeroPage_X => {
|
AddressingMode::ZeroPage_X => {
|
||||||
let pos = self.mem_read(self.program_counter);
|
let pos = self.mem_read(self.program_counter);
|
||||||
let addr = pos.wrapping_add(self.register_x);
|
let addr = pos.wrapping_add(self.register_x);
|
||||||
addr as u16
|
addr as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode::ZeroPage_Y => {
|
AddressingMode::ZeroPage_Y => {
|
||||||
let pos = self.mem_read(self.program_counter);
|
let pos = self.mem_read(self.program_counter);
|
||||||
let addr = pos.wrapping_add(self.register_y);
|
let addr = pos.wrapping_add(self.register_y);
|
||||||
addr as u16
|
addr as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode::Absolute_X => {
|
AddressingMode::Absolute_X => {
|
||||||
let base = self.mem_read_u16(self.program_counter);
|
let base = self.mem_read_u16(self.program_counter);
|
||||||
base.wrapping_add(self.register_x as u16)
|
base.wrapping_add(self.register_x as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode::Absolute_Y => {
|
AddressingMode::Absolute_Y => {
|
||||||
let base = self.mem_read_u16(self.program_counter);
|
let base = self.mem_read_u16(self.program_counter);
|
||||||
base.wrapping_add(self.register_y as u16)
|
base.wrapping_add(self.register_y as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode::Indirect_X => {
|
AddressingMode::Indirect_X => {
|
||||||
let base = self.mem_read(self.program_counter);
|
let base = self.mem_read(self.program_counter);
|
||||||
|
|
||||||
@ -161,6 +477,7 @@ impl CPU {
|
|||||||
let hi = self.mem_read(ptr.wrapping_add(1) as u16);
|
let hi = self.mem_read(ptr.wrapping_add(1) as u16);
|
||||||
(hi as u16) << 8 | (lo as u16)
|
(hi as u16) << 8 | (lo as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode::Indirect_Y => {
|
AddressingMode::Indirect_Y => {
|
||||||
let base = self.mem_read(self.program_counter);
|
let base = self.mem_read(self.program_counter);
|
||||||
|
|
||||||
@ -169,6 +486,7 @@ impl CPU {
|
|||||||
let deref_base = (hi as u16) << 8 | (lo as u16);
|
let deref_base = (hi as u16) << 8 | (lo as u16);
|
||||||
deref_base.wrapping_add(self.register_y as u16)
|
deref_base.wrapping_add(self.register_y as u16)
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode::NoneAddressing => {
|
AddressingMode::NoneAddressing => {
|
||||||
panic!("mode {:?} is not supported", mode);
|
panic!("mode {:?} is not supported", mode);
|
||||||
}
|
}
|
||||||
@ -182,8 +500,23 @@ impl CPU {
|
|||||||
|
|
||||||
let value = self.mem_read(addr);
|
let value = self.mem_read(addr);
|
||||||
|
|
||||||
self.register_a = value;
|
self.set_register_a(value);
|
||||||
self.update_zero_and_negative_flags(self.register_a);
|
}
|
||||||
|
|
||||||
|
/// LDX
|
||||||
|
fn ldx(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let value = self.mem_read(addr);
|
||||||
|
self.register_x = value;
|
||||||
|
self.update_zero_and_negative_flags(self.register_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LDY
|
||||||
|
fn ldy(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let value = self.mem_read(addr);
|
||||||
|
self.register_y = value;
|
||||||
|
self.update_zero_and_negative_flags(self.register_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// STA - Store Accumulator
|
/// STA - Store Accumulator
|
||||||
@ -193,6 +526,27 @@ impl CPU {
|
|||||||
self.mem_write(addr, self.register_a);
|
self.mem_write(addr, self.register_a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AND - Logical AND
|
||||||
|
fn and(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let value = self.mem_read(addr);
|
||||||
|
self.set_register_a(value & self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// EOR - Exclusive OR
|
||||||
|
fn eor(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let value = self.mem_read(addr);
|
||||||
|
self.set_register_a(value ^ self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ORA - Logical Inclusive OR
|
||||||
|
fn ora(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let value = self.mem_read(addr);
|
||||||
|
self.set_register_a(value | self.register_a);
|
||||||
|
}
|
||||||
|
|
||||||
/// TAX - Transfer Accumulator to X
|
/// TAX - Transfer Accumulator to X
|
||||||
/// Copies the current contents of the accumulator into the X register and sets the zero and negative flags as appropriate.
|
/// Copies the current contents of the accumulator into the X register and sets the zero and negative flags as appropriate.
|
||||||
fn tax(&mut self) {
|
fn tax(&mut self) {
|
||||||
@ -207,17 +561,302 @@ impl CPU {
|
|||||||
self.update_zero_and_negative_flags(self.register_x);
|
self.update_zero_and_negative_flags(self.register_x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// INY - Increment Y Register
|
||||||
|
fn iny(&mut self) {
|
||||||
|
self.register_y = self.register_y.wrapping_add(1);
|
||||||
|
self.update_zero_and_negative_flags(self.register_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_carry_flag(&mut self) {
|
||||||
|
self.status.insert(CpuFlags::CARRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_carry_flag(&mut self) {
|
||||||
|
self.status.remove(CpuFlags::CARRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_to_register_a(&mut self, data: u8) {
|
||||||
|
let sum = self.register_a as u16
|
||||||
|
+ data as u16
|
||||||
|
+ (if self.status.contains(CpuFlags::CARRY) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}) as u16;
|
||||||
|
|
||||||
|
let carry = sum > 0xff;
|
||||||
|
|
||||||
|
if carry {
|
||||||
|
self.status.insert(CpuFlags::CARRY);
|
||||||
|
} else {
|
||||||
|
self.status.remove(CpuFlags::CARRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = sum as u8;
|
||||||
|
|
||||||
|
if (data ^ result) & (result ^ self.register_a) & 0x80 != 0 {
|
||||||
|
self.status.insert(CpuFlags::OVERFLOW);
|
||||||
|
} else {
|
||||||
|
self.status.remove(CpuFlags::OVERFLOW)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_register_a(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SBC
|
||||||
|
fn sbc(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let data = self.mem_read(addr);
|
||||||
|
self.add_to_register_a(((data as i8).wrapping_neg().wrapping_sub(1)) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ADC - Add with Carry
|
||||||
|
fn adc(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let value = self.mem_read(addr);
|
||||||
|
self.add_to_register_a(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stack_pop(&mut self) -> u8 {
|
||||||
|
self.stack_pointer = self.stack_pointer.wrapping_add(1);
|
||||||
|
self.mem_read((STACK) + self.stack_pointer as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stack_push(&mut self, data: u8) {
|
||||||
|
self.mem_write((STACK) + self.stack_pointer as u16, data);
|
||||||
|
self.stack_pointer = self.stack_pointer.wrapping_sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stack_push_u16(&mut self, data: u16) {
|
||||||
|
let hi = (data >> 8) as u8;
|
||||||
|
let lo = (data & 0xff) as u8;
|
||||||
|
self.stack_push(hi);
|
||||||
|
self.stack_push(lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stack_pop_u16(&mut self) -> u16 {
|
||||||
|
let lo = self.stack_pop() as u16;
|
||||||
|
let hi = self.stack_pop() as u16;
|
||||||
|
|
||||||
|
hi << 8 | lo
|
||||||
|
}
|
||||||
|
|
||||||
|
fn asl_accumulator(&mut self) {
|
||||||
|
let mut data = self.register_a;
|
||||||
|
if data >> 7 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data <<= 1;
|
||||||
|
self.set_register_a(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn asl(&mut self, mode: &AddressingMode) -> u8 {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let mut data = self.mem_read(addr);
|
||||||
|
if data >> 7 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data <<= 1;
|
||||||
|
self.mem_write(addr, data);
|
||||||
|
self.update_zero_and_negative_flags(data);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lsr_accumulator(&mut self) {
|
||||||
|
let mut data = self.register_a;
|
||||||
|
if data & 1 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data >>= 1;
|
||||||
|
self.set_register_a(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lsr(&mut self, mode: &AddressingMode) -> u8 {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let mut data = self.mem_read(addr);
|
||||||
|
if data & 1 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data >>= 1;
|
||||||
|
self.mem_write(addr, data);
|
||||||
|
self.update_zero_and_negative_flags(data);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rol(&mut self, mode: &AddressingMode) -> u8 {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let mut data = self.mem_read(addr);
|
||||||
|
let old_carry = self.status.contains(CpuFlags::CARRY);
|
||||||
|
|
||||||
|
if data >> 7 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data <<= 1;
|
||||||
|
if old_carry {
|
||||||
|
data |= 1;
|
||||||
|
}
|
||||||
|
self.mem_write(addr, data);
|
||||||
|
self.update_zero_and_negative_flags(data);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rol_accumulator(&mut self) {
|
||||||
|
let mut data = self.register_a;
|
||||||
|
let old_carry = self.status.contains(CpuFlags::CARRY);
|
||||||
|
|
||||||
|
if data >> 7 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data <<= 1;
|
||||||
|
if old_carry {
|
||||||
|
data |= 1;
|
||||||
|
}
|
||||||
|
self.set_register_a(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ror(&mut self, mode: &AddressingMode) -> u8 {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let mut data = self.mem_read(addr);
|
||||||
|
let old_carry = self.status.contains(CpuFlags::CARRY);
|
||||||
|
|
||||||
|
if data & 1 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data >>= 1;
|
||||||
|
if old_carry {
|
||||||
|
data |= 0b10000000;
|
||||||
|
}
|
||||||
|
self.mem_write(addr, data);
|
||||||
|
self.update_zero_and_negative_flags(data);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ror_accumulator(&mut self) {
|
||||||
|
let mut data = self.register_a;
|
||||||
|
let old_carry = self.status.contains(CpuFlags::CARRY);
|
||||||
|
|
||||||
|
if data & 1 == 1 {
|
||||||
|
self.set_carry_flag();
|
||||||
|
} else {
|
||||||
|
self.clear_carry_flag();
|
||||||
|
}
|
||||||
|
data >>= 1;
|
||||||
|
if old_carry {
|
||||||
|
data |= 0b10000000;
|
||||||
|
}
|
||||||
|
self.set_register_a(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inc(&mut self, mode: &AddressingMode) -> u8 {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let mut data = self.mem_read(addr);
|
||||||
|
data = data.wrapping_add(1);
|
||||||
|
self.mem_write(addr, data);
|
||||||
|
self.update_zero_and_negative_flags(data);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dey(&mut self) {
|
||||||
|
self.register_y = self.register_y.wrapping_sub(1);
|
||||||
|
self.update_zero_and_negative_flags(self.register_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dex(&mut self) {
|
||||||
|
self.register_x = self.register_x.wrapping_sub(1);
|
||||||
|
self.update_zero_and_negative_flags(self.register_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dec(&mut self, mode: &AddressingMode) -> u8 {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let mut data = self.mem_read(addr);
|
||||||
|
data = data.wrapping_sub(1);
|
||||||
|
self.mem_write(addr, data);
|
||||||
|
self.update_zero_and_negative_flags(data);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pla(&mut self) {
|
||||||
|
let data = self.stack_pop();
|
||||||
|
self.set_register_a(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plp(&mut self) {
|
||||||
|
self.status = CpuFlags::from_bits_truncate(self.stack_pop());
|
||||||
|
self.status.remove(CpuFlags::BREAK);
|
||||||
|
self.status.insert(CpuFlags::BREAK2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn php(&mut self) {
|
||||||
|
//http://wiki.nesdev.com/w/index.php/CPU_status_flag_behavior
|
||||||
|
let mut flags = self.status.clone();
|
||||||
|
flags.insert(CpuFlags::BREAK);
|
||||||
|
flags.insert(CpuFlags::BREAK2);
|
||||||
|
self.stack_push(flags.bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bit(&mut self, mode: &AddressingMode) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let data = self.mem_read(addr);
|
||||||
|
let and = self.register_a & data;
|
||||||
|
if and == 0 {
|
||||||
|
self.status.insert(CpuFlags::ZERO);
|
||||||
|
} else {
|
||||||
|
self.status.remove(CpuFlags::ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.status.set(CpuFlags::NEGATIV, data & 0b10000000 > 0);
|
||||||
|
self.status.set(CpuFlags::OVERFLOW, data & 0b01000000 > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare(&mut self, mode: &AddressingMode, compare_with: u8) {
|
||||||
|
let addr = self.get_operand_address(mode);
|
||||||
|
let data = self.mem_read(addr);
|
||||||
|
if data <= compare_with {
|
||||||
|
self.status.insert(CpuFlags::CARRY);
|
||||||
|
} else {
|
||||||
|
self.status.remove(CpuFlags::CARRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_zero_and_negative_flags(compare_with.wrapping_sub(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch(&mut self, condition: bool) {
|
||||||
|
if condition {
|
||||||
|
let jump: i8 = self.mem_read(self.program_counter) as i8;
|
||||||
|
let jump_addr = self
|
||||||
|
.program_counter
|
||||||
|
.wrapping_add(1)
|
||||||
|
.wrapping_add(jump as u16);
|
||||||
|
|
||||||
|
self.program_counter = jump_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_zero_and_negative_flags(&mut self, result: u8) {
|
fn update_zero_and_negative_flags(&mut self, result: u8) {
|
||||||
if result == 0 {
|
if result == 0 {
|
||||||
self.status |= 0b0000_0010;
|
self.status.insert(CpuFlags::ZERO);
|
||||||
} else {
|
} else {
|
||||||
self.status &= 0b1111_1101;
|
self.status.remove(CpuFlags::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
if result & 0b1000_0000 != 0 {
|
if result & 0b1000_0000 != 0 {
|
||||||
self.status |= 0b1000_0000;
|
self.status.insert(CpuFlags::NEGATIV);
|
||||||
} else {
|
} else {
|
||||||
self.status &= 0b0111_1111;
|
self.status.remove(CpuFlags::NEGATIV);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,26 +868,19 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_0xa9_lda_immediate_load_data() {
|
fn test_0xa9_lda_immediate_load_data() {
|
||||||
let mut cpu = CPU::new();
|
let mut cpu = CPU::new();
|
||||||
cpu.reset();
|
|
||||||
cpu.load_and_run(vec![0xa9, 0x05, 0x00]);
|
cpu.load_and_run(vec![0xa9, 0x05, 0x00]);
|
||||||
assert_eq!(cpu.register_a, 5);
|
assert_eq!(cpu.register_a, 5);
|
||||||
assert_eq!(cpu.status & 0b0000_0010, 0);
|
assert!(cpu.status.bits() & 0b0000_0010 == 0b00);
|
||||||
assert_eq!(cpu.status & 0b1000_0000, 0);
|
assert!(cpu.status.bits() & 0b1000_0000 == 0);
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_0xa9_lda_zero_flag() {
|
|
||||||
let mut cpu = CPU::new();
|
|
||||||
cpu.reset();
|
|
||||||
cpu.load_and_run(vec![0xa9, 0x00, 0x00]);
|
|
||||||
assert_eq!(cpu.status & 0b0000_0010, 0b10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_0xaa_tax_move_a_to_x() {
|
fn test_0xaa_tax_move_a_to_x() {
|
||||||
let mut cpu = CPU::new();
|
let mut cpu = CPU::new();
|
||||||
|
cpu.load(vec![0xaa, 0x00]);
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
cpu.load_and_run(vec![0xa9, 0x0A, 0xaa, 0x00]);
|
cpu.register_a = 10;
|
||||||
|
cpu.run();
|
||||||
|
|
||||||
assert_eq!(cpu.register_x, 10)
|
assert_eq!(cpu.register_x, 10)
|
||||||
}
|
}
|
||||||
@ -256,7 +888,6 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_5_ops_working_together() {
|
fn test_5_ops_working_together() {
|
||||||
let mut cpu = CPU::new();
|
let mut cpu = CPU::new();
|
||||||
cpu.reset();
|
|
||||||
cpu.load_and_run(vec![0xa9, 0xc0, 0xaa, 0xe8, 0x00]);
|
cpu.load_and_run(vec![0xa9, 0xc0, 0xaa, 0xe8, 0x00]);
|
||||||
|
|
||||||
assert_eq!(cpu.register_x, 0xc1)
|
assert_eq!(cpu.register_x, 0xc1)
|
||||||
@ -265,8 +896,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_inx_overflow() {
|
fn test_inx_overflow() {
|
||||||
let mut cpu = CPU::new();
|
let mut cpu = CPU::new();
|
||||||
|
cpu.load(vec![0xe8, 0xe8, 0x00]);
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
cpu.load_and_run(vec![0xa9, 0xff, 0xaa, 0xe8, 0xe8, 0x00]);
|
cpu.register_x = 0xff;
|
||||||
|
cpu.run();
|
||||||
|
|
||||||
assert_eq!(cpu.register_x, 1)
|
assert_eq!(cpu.register_x, 1)
|
||||||
}
|
}
|
||||||
@ -274,8 +907,8 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_lda_from_memory() {
|
fn test_lda_from_memory() {
|
||||||
let mut cpu = CPU::new();
|
let mut cpu = CPU::new();
|
||||||
cpu.reset();
|
|
||||||
cpu.mem_write(0x10, 0x55);
|
cpu.mem_write(0x10, 0x55);
|
||||||
|
|
||||||
cpu.load_and_run(vec![0xa5, 0x10, 0x00]);
|
cpu.load_and_run(vec![0xa5, 0x10, 0x00]);
|
||||||
|
|
||||||
assert_eq!(cpu.register_a, 0x55);
|
assert_eq!(cpu.register_a, 0x55);
|
||||||
|
@ -4,6 +4,9 @@ pub mod opcodes;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate bitflags;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
}
|
}
|
||||||
|
260
src/opcodes.rs
260
src/opcodes.rs
@ -23,19 +23,231 @@ impl OpCode {
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CPU_OPS_CODES: Vec<OpCode> = vec![
|
pub static ref CPU_OPS_CODES: Vec<OpCode> = vec![
|
||||||
|
|
||||||
|
// ADC - Add with Carry
|
||||||
|
OpCode::new(0x69, "ADC", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0x65, "ADC", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x75, "ADC", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x6D, "ADC", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x7D, "ADC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0x79, "ADC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0x61, "ADC", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0x71, "ADC", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// AND
|
||||||
|
OpCode::new(0x29, "AND", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0x25, "AND", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x35, "AND", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x2D, "AND", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x3D, "AND", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0x39, "AND", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0x21, "AND", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0x31, "AND", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// ASL
|
||||||
|
OpCode::new(0x0A, "ASL", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
OpCode::new(0x06, "ASL", 2, 5, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x16, "ASL", 2, 6, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x0E, "ASL", 3, 6, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x1E, "ASL", 3, 7, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// BCC
|
||||||
|
OpCode::new(0x90, "BCC", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BCS
|
||||||
|
OpCode::new(0xB0, "BCS", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BEQ
|
||||||
|
OpCode::new(0xF0, "BEQ", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BIT
|
||||||
|
OpCode::new(0x24, "BIT", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x2C, "BIT", 3, 4, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BMI
|
||||||
|
OpCode::new(0x30, "BMI", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BNE
|
||||||
|
OpCode::new(0xD0, "BNE", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BPL
|
||||||
|
OpCode::new(0x10, "BPL", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// BRK
|
||||||
OpCode::new(0x00, "BRK", 1, 7, AddressingMode::NoneAddressing),
|
OpCode::new(0x00, "BRK", 1, 7, AddressingMode::NoneAddressing),
|
||||||
OpCode::new(0xaa, "TAX", 1, 2, AddressingMode::NoneAddressing),
|
|
||||||
OpCode::new(0xe8, "INX", 1, 2, AddressingMode::NoneAddressing),
|
|
||||||
|
|
||||||
OpCode::new(0xa9, "LDA", 2, 2, AddressingMode::Immediate),
|
// BVC
|
||||||
OpCode::new(0xa5, "LDA", 2, 3, AddressingMode::ZeroPage),
|
OpCode::new(0x50, "BVC", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
OpCode::new(0xb5, "LDA", 2, 4, AddressingMode::ZeroPage_X),
|
|
||||||
OpCode::new(0xad, "LDA", 3, 4, AddressingMode::Absolute),
|
|
||||||
OpCode::new(0xbd, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
|
||||||
OpCode::new(0xb9, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
|
||||||
OpCode::new(0xa1, "LDA", 2, 6, AddressingMode::Indirect_X),
|
|
||||||
OpCode::new(0xb1, "LDA", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
|
||||||
|
|
||||||
|
// BVS
|
||||||
|
OpCode::new(0x70, "BVS", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// CLC
|
||||||
|
OpCode::new(0x18, "CLC", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// CLD
|
||||||
|
OpCode::new(0xD8, "CLD", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// CLI
|
||||||
|
OpCode::new(0x58, "CLI", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// CLV
|
||||||
|
OpCode::new(0xB8, "CLV", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// CMP
|
||||||
|
OpCode::new(0xC9, "CMP", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xC5, "CMP", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xD5, "CMP", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0xCD, "CMP", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xDD, "CMP", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0xD9, "CMP", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0xC1, "CMP", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0xD1, "CMP", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// CPX
|
||||||
|
OpCode::new(0xE0, "CPX", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xE4, "CPX", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xEC, "CPX", 3, 4, AddressingMode::Absolute),
|
||||||
|
|
||||||
|
// CPY
|
||||||
|
OpCode::new(0xC0, "CPY", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xC4, "CPY", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xCC, "CPY", 3, 4, AddressingMode::Absolute),
|
||||||
|
|
||||||
|
// DEC
|
||||||
|
OpCode::new(0xC6, "DEC", 2, 5, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xD6, "DEC", 2, 6, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0xCE, "DEC", 3, 6, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xDE, "DEC", 3, 7, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// DEX
|
||||||
|
OpCode::new(0xCA, "DEX", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// EOR
|
||||||
|
OpCode::new(0x49, "EOR", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0x45, "EOR", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x55, "EOR", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x4D, "EOR", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x5D, "EOR", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0x59, "EOR", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0x41, "EOR", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0x51, "EOR", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// INC
|
||||||
|
OpCode::new(0xE6, "INC", 2, 5, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xF6, "INC", 2, 6, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0xEE, "INC", 3, 6, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xFE, "INC", 3, 7, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// INX
|
||||||
|
OpCode::new(0xE8, "INX", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// INY
|
||||||
|
OpCode::new(0xC8, "INY", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// JMP
|
||||||
|
OpCode::new(0x4C, "JMP", 3, 3, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x6C, "JMP", 3, 5, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// JSR
|
||||||
|
OpCode::new(0x20, "JSR", 3, 6, AddressingMode::Absolute),
|
||||||
|
|
||||||
|
// LDA
|
||||||
|
OpCode::new(0xA9, "LDA", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xA5, "LDA", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xB5, "LDA", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0xAD, "LDA", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xBD, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0xB9, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0xA1, "LDA", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0xB1, "LDA", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// LDX
|
||||||
|
OpCode::new(0xA2, "LDX", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xA6, "LDX", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xB6, "LDX", 2, 4, AddressingMode::ZeroPage_Y),
|
||||||
|
OpCode::new(0xAE, "LDX", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xBE, "LDX", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
|
||||||
|
// LDY
|
||||||
|
OpCode::new(0xA0, "LDY", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xA4, "LDY", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xB4, "LDY", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0xAC, "LDY", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xBC, "LDY", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// LSR
|
||||||
|
OpCode::new(0x4A, "LSR", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
OpCode::new(0x46, "LSR", 2, 5, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x56, "LSR", 2, 6, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x4E, "LSR", 3, 6, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x5E, "LSR", 3, 7, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// NOP
|
||||||
|
OpCode::new(0xEA, "NOP", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// ORA
|
||||||
|
OpCode::new(0x09, "ORA", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0x05, "ORA", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x15, "ORA", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x0D, "ORA", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x1D, "ORA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0x19, "ORA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0x01, "ORA", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0x11, "ORA", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// PHA
|
||||||
|
OpCode::new(0x48, "PHA", 1, 3, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// PHP
|
||||||
|
OpCode::new(0x08, "PHP", 1, 3, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// PLA
|
||||||
|
OpCode::new(0x68, "PLA", 1, 4, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// PLP
|
||||||
|
OpCode::new(0x28, "PLP", 1, 4, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// ROL
|
||||||
|
OpCode::new(0x2A, "ROL", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
OpCode::new(0x26, "ROL", 2, 5, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x36, "ROL", 2, 6, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x2E, "ROL", 3, 6, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x3E, "ROL", 3, 7, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// ROR
|
||||||
|
OpCode::new(0x6A, "ROR", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
OpCode::new(0x66, "ROR", 2, 5, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x76, "ROR", 2, 6, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x6E, "ROR", 3, 6, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0x7E, "ROR", 3, 7, AddressingMode::Absolute_X),
|
||||||
|
|
||||||
|
// RTI
|
||||||
|
OpCode::new(0x40, "RTI", 1, 6, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// RTS
|
||||||
|
OpCode::new(0x60, "RTS", 1, 6, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// SBC
|
||||||
|
OpCode::new(0xE9, "SBC", 2, 2, AddressingMode::Immediate),
|
||||||
|
OpCode::new(0xE5, "SBC", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0xF5, "SBC", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0xED, "SBC", 3, 4, AddressingMode::Absolute),
|
||||||
|
OpCode::new(0xFD, "SBC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X),
|
||||||
|
OpCode::new(0xF9, "SBC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y),
|
||||||
|
OpCode::new(0xE1, "SBC", 2, 6, AddressingMode::Indirect_X),
|
||||||
|
OpCode::new(0xF1, "SBC", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// SEC
|
||||||
|
OpCode::new(0x38, "SEC", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// SED
|
||||||
|
OpCode::new(0xF8, "SED", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// SEI
|
||||||
|
OpCode::new(0x78, "SEI", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// STA
|
||||||
OpCode::new(0x85, "STA", 2, 3, AddressingMode::ZeroPage),
|
OpCode::new(0x85, "STA", 2, 3, AddressingMode::ZeroPage),
|
||||||
OpCode::new(0x95, "STA", 2, 4, AddressingMode::ZeroPage_X),
|
OpCode::new(0x95, "STA", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
OpCode::new(0x8d, "STA", 3, 4, AddressingMode::Absolute),
|
OpCode::new(0x8d, "STA", 3, 4, AddressingMode::Absolute),
|
||||||
@ -44,6 +256,34 @@ lazy_static! {
|
|||||||
OpCode::new(0x81, "STA", 2, 6, AddressingMode::Indirect_X),
|
OpCode::new(0x81, "STA", 2, 6, AddressingMode::Indirect_X),
|
||||||
OpCode::new(0x91, "STA", 2, 6, AddressingMode::Indirect_Y),
|
OpCode::new(0x91, "STA", 2, 6, AddressingMode::Indirect_Y),
|
||||||
|
|
||||||
|
// STX
|
||||||
|
OpCode::new(0x86, "STX", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x96, "STX", 2, 4, AddressingMode::ZeroPage_Y),
|
||||||
|
OpCode::new(0x8E, "STX", 3, 4, AddressingMode::Absolute),
|
||||||
|
|
||||||
|
// STY
|
||||||
|
OpCode::new(0x84, "STY", 2, 3, AddressingMode::ZeroPage),
|
||||||
|
OpCode::new(0x94, "STY", 2, 4, AddressingMode::ZeroPage_X),
|
||||||
|
OpCode::new(0x8C, "STY", 3, 4, AddressingMode::Absolute),
|
||||||
|
|
||||||
|
// TAX
|
||||||
|
OpCode::new(0xAA, "TAX", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// TAY
|
||||||
|
OpCode::new(0xA8, "TAY", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// TSX
|
||||||
|
OpCode::new(0xBA, "TSX", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// TXA
|
||||||
|
OpCode::new(0x8A, "TXA", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// TXS
|
||||||
|
OpCode::new(0x9A, "TXS", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
|
// TYA
|
||||||
|
OpCode::new(0x98, "TYA", 1, 2, AddressingMode::NoneAddressing),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user