1
0
mirror of https://github.com/mre/mos6502.git synced 2024-12-21 21:29:16 +00:00

Cmos support (#99)

This commit is contained in:
omarandlorraine 2024-06-07 15:29:40 +01:00 committed by GitHub
parent 467b3ff436
commit 265ef6941e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 352 additions and 104 deletions

View File

@ -212,6 +212,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
@ -286,6 +294,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);
@ -322,6 +340,11 @@ impl<M: Bus, V: Variant> CPU<M, V> {
self.branch_if_positive(addr);
}
(Instruction::BRA, OpInput::UseRelative(rel)) => {
let addr = self.registers.program_counter.wrapping_add(rel);
self.branch(addr);
}
(Instruction::BRK, OpInput::UseImplied) => {
for b in self.registers.program_counter.wrapping_sub(1).to_be_bytes() {
self.push_on_stack(b);
@ -333,6 +356,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((u16::from(pch) << 8) | u16::from(pcl));
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);
@ -478,11 +513,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();
@ -588,6 +659,9 @@ impl<M: Bus, V: Variant> CPU<M, V> {
(Instruction::STY, OpInput::UseAddress(addr)) => {
self.memory.set_byte(addr, self.registers.index_y);
}
(Instruction::STZ, OpInput::UseAddress(addr)) => {
self.memory.set_byte(addr, 0);
}
(Instruction::TAX, OpInput::UseImplied) => {
let val = self.registers.accumulator;
@ -597,6 +671,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);
@ -1011,6 +1117,10 @@ impl<M: Bus, V: Variant> CPU<M, V> {
}
}
fn branch(&mut self, addr: u16) {
self.registers.program_counter = addr;
}
fn branch_if_positive(&mut self, addr: u16) {
if !self.registers.status.contains(Status::PS_NEGATIVE) {
self.registers.program_counter = addr;

View File

@ -25,86 +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
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
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, Debug)]
@ -158,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 {
@ -178,6 +303,7 @@ impl AddressingMode {
AddressingMode::BuggyIndirect => 2,
AddressingMode::IndexedIndirectX => 1,
AddressingMode::IndirectIndexedY => 1,
AddressingMode::ZeroPageIndirect => 1,
}
}
}
@ -458,24 +584,15 @@ pub struct Ricoh2a03;
impl crate::Variant for Ricoh2a03 {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
match opcode {
0x61 => Some((Instruction::ADCnd, AddressingMode::IndexedIndirectX)),
0x65 => Some((Instruction::ADCnd, AddressingMode::ZeroPage)),
0x69 => Some((Instruction::ADCnd, AddressingMode::Immediate)),
0x6d => Some((Instruction::ADCnd, AddressingMode::Absolute)),
0x71 => Some((Instruction::ADCnd, AddressingMode::IndirectIndexedY)),
0x75 => Some((Instruction::ADCnd, AddressingMode::ZeroPageX)),
0x79 => Some((Instruction::ADCnd, AddressingMode::AbsoluteY)),
0x7d => Some((Instruction::ADCnd, AddressingMode::AbsoluteX)),
0xe1 => Some((Instruction::SBCnd, AddressingMode::IndexedIndirectX)),
0xe5 => Some((Instruction::SBCnd, AddressingMode::ZeroPage)),
0xe9 => Some((Instruction::SBCnd, AddressingMode::Immediate)),
0xed => Some((Instruction::SBCnd, AddressingMode::Absolute)),
0xf1 => Some((Instruction::SBCnd, AddressingMode::IndirectIndexedY)),
0xf5 => Some((Instruction::SBCnd, AddressingMode::ZeroPageX)),
0xf9 => Some((Instruction::SBCnd, AddressingMode::AbsoluteY)),
0xfd => Some((Instruction::SBCnd, AddressingMode::AbsoluteX)),
_ => Nmos6502::decode(opcode),
// It's the same as on NMOS, but doesn't support decimal mode.
match Nmos6502::decode(opcode) {
Some((Instruction::ADC, addressing_mode)) => {
Some((Instruction::ADCnd, addressing_mode))
}
Some((Instruction::SBC, addressing_mode)) => {
Some((Instruction::SBCnd, addressing_mode))
}
something_else => something_else,
}
}
}
@ -487,14 +604,10 @@ pub struct RevisionA;
impl crate::Variant for RevisionA {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
#[allow(clippy::match_same_arms)]
match opcode {
0x66 => None,
0x6a => None,
0x6e => None,
0x76 => None,
0x7e => None,
_ => Nmos6502::decode(opcode),
// It's the same as on NMOS, but has no ROR instruction.
match Nmos6502::decode(opcode) {
Some((Instruction::ROR, _)) => None,
something_else => something_else,
}
}
}
@ -507,7 +620,32 @@ 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)),
0x80 => Some((Instruction::BRA, AddressingMode::Relative)),
0x64 => Some((Instruction::STZ, AddressingMode::ZeroPage)),
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),
}
}