Compare commits

...

9 Commits

Author SHA1 Message Date
omarandlorraine cde5c41213
Merge 536abef126 into 4847744518 2024-04-27 13:23:34 +00:00
Sam M W 536abef126 add immediate BIT instruction 2024-04-27 14:23:28 +01:00
Sam M W b62b6c74b0 fmt 2024-04-27 14:16:40 +01:00
Sam M W 8704fd55bf add the cmos addressing mode, indirect unindexed 2024-04-27 14:15:19 +01:00
Sam M W 747d620943 trb tsb 2024-04-27 14:04:54 +01:00
Sam M W d51ae3057b better commenting for Instruction enum 2024-04-27 11:56:58 +01:00
Sam M W 6a52e0e882 formatting 2024-04-27 11:45:20 +01:00
Sam M W ecf222f80b BRK on CMOS clears the decimal flag 2024-04-27 11:42:37 +01:00
Sam M W cfb956f6a2 phx phy plx ply 2024-04-27 02:55:49 +01:00
2 changed files with 320 additions and 80 deletions

View File

@ -201,6 +201,14 @@ impl<M: Bus, V: Variant> CPU<M, V> {
address_from_bytes(slice[0], slice[1]).wrapping_add(y.into()),
)
}
AddressingMode::ZeroPageIndirect => {
// Use [u8, ..1] from instruction
// This is where the absolute (16-bit) target address is stored.
// (Output: a 16-bit address)
let start = slice[0];
let slice = read_address(memory, u16::from(start));
OpInput::UseAddress(address_from_bytes(slice[0], slice[1]))
}
};
// Increment program counter
@ -274,6 +282,16 @@ impl<M: Bus, V: Variant> CPU<M, V> {
self.branch_if_not_equal(addr);
}
(Instruction::BIT, OpInput::UseImmediate(val)) => {
self.registers.status.set_with_mask(
Status::PS_ZERO,
Status::new(StatusArgs {
zero: 0 == (self.registers.accumulator & val),
..StatusArgs::none()
}),
);
}
(Instruction::BIT, OpInput::UseAddress(addr)) => {
let a: u8 = self.registers.accumulator;
let m: u8 = self.memory.get_byte(addr);
@ -326,6 +344,18 @@ impl<M: Bus, V: Variant> CPU<M, V> {
self.registers.status.or(Status::PS_DISABLE_INTERRUPTS);
}
(Instruction::BRKcld, OpInput::UseImplied) => {
for b in self.registers.program_counter.wrapping_sub(1).to_be_bytes() {
self.push_on_stack(b);
}
self.push_on_stack(self.registers.status.bits());
let pcl = self.memory.get_byte(0xfffe);
let pch = self.memory.get_byte(0xffff);
self.jump(((pch as u16) << 8) | pcl as u16);
self.registers.status.or(Status::PS_DISABLE_INTERRUPTS);
self.registers.status.and(!Status::PS_DECIMAL_MODE);
}
(Instruction::BVC, OpInput::UseRelative(rel)) => {
let addr = self.registers.program_counter.wrapping_add(rel);
self.branch_if_overflow_clear(addr);
@ -471,11 +501,47 @@ impl<M: Bus, V: Variant> CPU<M, V> {
let val = self.registers.accumulator;
self.push_on_stack(val);
}
(Instruction::PHX, OpInput::UseImplied) => {
// Push X
self.push_on_stack(self.registers.index_x);
}
(Instruction::PHY, OpInput::UseImplied) => {
// Push Y
self.push_on_stack(self.registers.index_y);
}
(Instruction::PHP, OpInput::UseImplied) => {
// Push status
let val = self.registers.status.bits() | 0x30;
self.push_on_stack(val);
}
(Instruction::PLX, OpInput::UseImplied) => {
// Pull accumulator
self.pull_from_stack();
let val: u8 = self.fetch_from_stack();
self.registers.index_x = val;
self.registers.status.set_with_mask(
Status::PS_ZERO | Status::PS_NEGATIVE,
Status::new(StatusArgs {
zero: val == 0,
negative: self.registers.accumulator > 127,
..StatusArgs::none()
}),
);
}
(Instruction::PLY, OpInput::UseImplied) => {
// Pull accumulator
self.pull_from_stack();
let val: u8 = self.fetch_from_stack();
self.registers.index_y = val;
self.registers.status.set_with_mask(
Status::PS_ZERO | Status::PS_NEGATIVE,
Status::new(StatusArgs {
zero: val == 0,
negative: self.registers.accumulator > 127,
..StatusArgs::none()
}),
);
}
(Instruction::PLA, OpInput::UseImplied) => {
// Pull accumulator
self.pull_from_stack();
@ -592,6 +658,38 @@ impl<M: Bus, V: Variant> CPU<M, V> {
let val = self.registers.accumulator;
self.load_y_register(val);
}
(Instruction::TRB, OpInput::UseAddress(addr)) => {
let val = self.memory.get_byte(addr);
// The zero flag is set based on the result of the 'and'.
self.registers.status.set_with_mask(
Status::PS_ZERO,
Status::new(StatusArgs {
zero: 0 == (self.registers.accumulator & val),
..StatusArgs::none()
}),
);
// The 1's in the accumulator set the corresponding bits in the operand
let res = self.registers.accumulator | val;
self.memory.set_byte(addr, res);
}
(Instruction::TSB, OpInput::UseAddress(addr)) => {
let val = self.memory.get_byte(addr);
// The zero flag is set based on the result of the 'and'.
self.registers.status.set_with_mask(
Status::PS_ZERO,
Status::new(StatusArgs {
zero: 0 == (self.registers.accumulator & val),
..StatusArgs::none()
}),
);
// The 1's in the accumulator clear the corresponding bits in the operand
let res = (self.registers.accumulator ^ 0xff) & val;
self.memory.set_byte(addr, res);
}
(Instruction::TSX, OpInput::UseImplied) => {
let StackPointer(val) = self.registers.stack_pointer;
self.load_x_register(val);

View File

@ -25,88 +25,208 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// Abbreviations
//
// General
//
// M | `Memory location`
//
// Registers
//
// A | accumulator
// X | general purpose register
// Y | general purpose register
// F | processor status flags, collectively
// NV-BDIZC | processor status flags, individually
// S | stack pointer
// PC | program counter
//
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Instruction {
ADC, // ADd with Carry................ | NV ...ZC A = A + M + C
ADCnd, // ADd with Carry................ | NV ...ZC A = A + M + C
AND, // logical AND (bitwise)......... | N. ...Z. A = A && M
ASL, // Arithmetic Shift Left......... | N. ...ZC A = M << 1
BCC, // Branch if Carry Clear......... | .. ..... PC = !C
BCS, // Branch if Carry Set........... | .. ..... PC = C
BEQ, // Branch if Equal (to zero?).... | .. ..... PC = Z
BIT, // BIT test...................... | NV ...Z. = A & M
BMI, // Branch if Minus............... | .. ..... PC = N
BNE, // Branch if Not Equal........... | .. ..... PC = !Z
BPL, // Branch if Positive............ | .. ..... PC = Z
BRA, // Unconditional BRAnch.......... | .. B.... S PC =
BRK, // BReaK......................... | .. B.... S PC =
BVC, // Branch if oVerflow Clear...... | .. ..... PC = !V
BVS, // Branch if oVerflow Set........ | .. ..... PC = V
CLC, // CLear Carry flag.............. | .. ....C = 0
CLD, // Clear Decimal Mode............ | .. .D... = 0
CLI, // Clear Interrupt Disable....... | .. ..I.. = 0
CLV, // Clear oVerflow flag........... | .V ..... = 0
CMP, // Compare....................... | N. ...ZC = A - M
CPX, // Compare X register............ | N. ...ZC = X - M
CPY, // Compare Y register............ | N. ...ZC = Y - M
DEC, // DECrement memory.............. | N. ...Z. M = M - 1
DEX, // DEcrement X register.......... | N. ...Z. X = X - 1
DEY, // DEcrement Y register.......... | N. ...Z. Y = Y - 1
EOR, // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M
INC, // INCrement memory.............. | N. ...Z. M = M + 1
INX, // INcrement X register.......... | N. ...Z. X = X + 1
INY, // INcrement Y register.......... | N. ...Z. Y = Y + 1
JMP, // JuMP.......................... | .. ..... S PC =
JSR, // Jump to SubRoutine............ | .. ..... S PC =
LDA, // LoaD Accumulator.............. | N. ...Z. A = M
LDX, // LoaD X register............... | N. ...Z. X = M
LDY, // LoaD Y register............... | N. ...Z. Y = M
LSR, // Logical Shift Right........... | N. ...ZC A = A/2
// or N. ...ZC M = M/2
NOP, // No OPeration.................. | .. ..... =
ORA, // inclusive OR (bitwise)........ | N. ...Z. A = A | M
PHA, // PusH Accumulator.............. | .. ..... S M = A
PHP, // PusH Processor status......... | .. ..... S M = F
PLA, // PuLl Accumulator.............. | N. ...Z. A S = M (stack)
PLP, // PuLl Processor status......... | NV BDIZC S = M (stack)
ROL, // ROtate Left................... | N. ...ZC A = C A rotated
// or N. ...ZC M = C M rotated
ROR, // ROtate Right.................. | N. ...ZC A = C A rotated
// or N. ...ZC M = C M rotated
RTI, // ReTurn from Interrupt......... | NV BDIZC PC = M (stack)
RTS, // ReTurn from Subroutine........ | .. ..... PC = M (stack)
SBC, // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C)
SBCnd, // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C)
SEC, // SEt Carry flag................ | .. ....C = 1
SED, // SEt Decimal flag.............. | .. .D... = 1
SEI, // SEt Interrupt disable......... | .. ..I.. = 1
STA, // STore Accumulator............. | .. ..... M = A
STX, // STore X register.............. | .. ..... M = X
STY, // STore Y register.............. | .. ..... M = Y
STZ, // STore Zero.................... | .. ..... M = Y
TAX, // Transfer Accumulator to X..... | N. ...Z. X = A
TAY, // Transfer Accumulator to Y..... | N. ...Z. Y = A
TSX, // Transfer Stack pointer to X... | N. ...Z. X = S
TXA, // Transfer X to Accumulator..... | N. ...Z. A = X
TXS, // Transfer X to Stack pointer... | .. ..... S = X
TYA, // Transfer Y to Accumulator..... | N. ...Z. A = Y
// ADd with Carry
ADC,
// ADd with Carry. This one has now decimal mode.
ADCnd,
// logical AND (bitwise)
AND,
// Arithmetic Shift Left
ASL,
// Branch if Carry Clear
BCC,
// Branch if Carry Set
BCS,
// Branch if Equal (to zero?)
BEQ,
// BIT test
BIT,
// Branch if Minus
BMI,
// Branch if Not Equal
BNE,
// Branch if Positive
BPL,
// Unconditional BRAnch
BRA,
// BReaK
BRK,
// BReaK, clearing decimal flag
BRKcld,
// Branch if oVerflow Clear
BVC,
// Branch if oVerflow Set
BVS,
// CLear Carry flag
CLC,
// Clear Decimal Mode
CLD,
// Clear Interrupt Disable
CLI,
// Clear oVerflow flag
CLV,
// Compare
CMP,
// Compare X register
CPX,
// Compare Y register
CPY,
// DECrement memory
DEC,
// DEcrement X register
DEX,
// DEcrement Y register
DEY,
// Exclusive OR (bitwise)
EOR,
// INCrement memory
INC,
// INcrement X register
INX,
// INcrement Y register
INY,
// JuMP
JMP,
// Jump to SubRoutine
JSR,
// LoaD Accumulator
LDA,
// LoaD X register
LDX,
// LoaD Y register
LDY,
// Logical Shift Right
LSR,
// No OPeration
NOP,
// inclusive OR (bitwise)
ORA,
// PusH Accumulator
PHA,
// PusH X
PHX,
// PusH Y
PHY,
// PusH Processor status
PHP,
// PuLl Accumulator
PLA,
// PuLl X
PLX,
// PuLl Y
PLY,
// PuLl Processor status
PLP,
// ROtate Left
ROL,
// ROtate Right
ROR,
// ReTurn from Interrupt
RTI,
// ReTurn from Subroutine
RTS,
// SuBtract with Carry
SBC,
// SuBtract with Carry. This one has now decimal mode.
SBCnd,
// SEt Carry flag
SEC,
// SEt Decimal flag
SED,
// SEt Interrupt disable
SEI,
// STore Accumulator
STA,
// STore X register
STX,
// STore Y register
STY,
// STore Zero
STZ,
// Transfer Accumulator to X
TAX,
// Transfer Accumulator to Y
TAY,
// Test and Reset Bits
TRB,
// Test and Set Bits
TSB,
// Transfer Stack pointer to X
TSX,
// Transfer X to Accumulator
TXA,
// Transfer X to Stack pointer
TXS,
// Transfer Y to Accumulator
TYA,
}
#[derive(Copy, Clone)]
@ -160,6 +280,9 @@ pub enum AddressingMode {
// load from (address stored at constant zero page address) plus Y register, e. g. `lda ($10),Y`.
IndirectIndexedY,
// Address stored at constant zero page address
ZeroPageIndirect,
}
impl AddressingMode {
@ -179,6 +302,7 @@ impl AddressingMode {
AddressingMode::BuggyIndirect => 2,
AddressingMode::IndexedIndirectX => 1,
AddressingMode::IndirectIndexedY => 1,
AddressingMode::ZeroPageIndirect => 1,
}
}
}
@ -491,6 +615,7 @@ impl crate::Variant for Cmos6502 {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
// TODO: We obviously need to add the other CMOS instructions here.
match opcode {
0x00 => Some((Instruction::BRKcld, AddressingMode::Implied)),
0x1a => Some((Instruction::INC, AddressingMode::Accumulator)),
0x3a => Some((Instruction::DEC, AddressingMode::Accumulator)),
0x6c => Some((Instruction::JMP, AddressingMode::Indirect)),
@ -499,6 +624,23 @@ impl crate::Variant for Cmos6502 {
0x74 => Some((Instruction::STZ, AddressingMode::ZeroPageX)),
0x9c => Some((Instruction::STZ, AddressingMode::Absolute)),
0x9e => Some((Instruction::STZ, AddressingMode::AbsoluteX)),
0x7a => Some((Instruction::PLY, AddressingMode::Implied)),
0xfa => Some((Instruction::PLX, AddressingMode::Implied)),
0x5a => Some((Instruction::PHY, AddressingMode::Implied)),
0xda => Some((Instruction::PHX, AddressingMode::Implied)),
0x04 => Some((Instruction::TSB, AddressingMode::ZeroPage)),
0x14 => Some((Instruction::TRB, AddressingMode::ZeroPage)),
0x0c => Some((Instruction::TSB, AddressingMode::Absolute)),
0x1c => Some((Instruction::TRB, AddressingMode::Absolute)),
0x12 => Some((Instruction::ORA, AddressingMode::ZeroPageIndirect)),
0x32 => Some((Instruction::AND, AddressingMode::ZeroPageIndirect)),
0x52 => Some((Instruction::EOR, AddressingMode::ZeroPageIndirect)),
0x72 => Some((Instruction::ADC, AddressingMode::ZeroPageIndirect)),
0x92 => Some((Instruction::STA, AddressingMode::ZeroPageIndirect)),
0xb2 => Some((Instruction::LDA, AddressingMode::ZeroPageIndirect)),
0xd2 => Some((Instruction::CMP, AddressingMode::ZeroPageIndirect)),
0xf2 => Some((Instruction::SBC, AddressingMode::ZeroPageIndirect)),
0x89 => Some((Instruction::BIT, AddressingMode::Immediate)),
_ => Nmos6502::decode(opcode),
}
}