From 030e6c1d0a7adbc2e523abcc086d0bc95227e805 Mon Sep 17 00:00:00 2001 From: Sam M W Date: Mon, 28 Aug 2023 16:46:18 +0100 Subject: [PATCH] Variant with no decimal mode --- src/cpu.rs | 104 ++++++++++++++++++++++++++++++++++--- src/instruction.rs | 124 +++++++++++++++++++++++++++------------------ 2 files changed, 172 insertions(+), 56 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 0379f86..ac5d2a6 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -196,6 +196,15 @@ impl CPU { debug!("add with carry. address: {:?}. value: {}", addr, val); self.add_with_carry(val); } + (Instruction::ADCnd, OpInput::UseImmediate(val)) => { + debug!("add with carry immediate: {}", val); + self.add_with_no_decimal(val); + } + (Instruction::ADCnd, OpInput::UseAddress(addr)) => { + let val = self.memory.get_byte(addr); + debug!("add with carry. address: {:?}. value: {}", addr, val); + self.add_with_no_decimal(val); + } (Instruction::AND, OpInput::UseImmediate(val)) => { self.and(val); @@ -509,6 +518,16 @@ impl CPU { self.subtract_with_carry(val); } + (Instruction::SBCnd, OpInput::UseImmediate(val)) => { + debug!("subtract with carry immediate: {}", val); + self.subtract_with_no_decimal(val); + } + (Instruction::SBCnd, OpInput::UseAddress(addr)) => { + let val = self.memory.get_byte(addr); + debug!("subtract with carry. address: {:?}. value: {}", addr, val); + self.subtract_with_no_decimal(val); + } + (Instruction::SEC, OpInput::UseImplied) => { self.registers.status.or(Status::PS_CARRY); } @@ -700,7 +719,6 @@ impl CPU { } fn add_with_carry(&mut self, value: u8) { - #[cfg(feature = "decimal_mode")] fn decimal_adjust(result: u8) -> u8 { let bcd1: u8 = if (result & 0x0f) > 0x09 { 0x06 } else { 0x00 }; @@ -719,15 +737,43 @@ impl CPU { debug_assert_eq!(a_after, a_before.wrapping_add(c_before).wrapping_add(value)); - #[cfg(feature = "decimal_mode")] let result: u8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) { decimal_adjust(a_after) } else { a_after }; - #[cfg(not(feature = "decimal_mode"))] - let result: u8 = a_after; + let did_carry = (result) < (a_before) + || (a_after == 0 && c_before == 0x01) + || (value == 0xff && c_before == 0x01); + + let did_overflow = (a_before > 127 && value > 127 && a_after < 128) + || (a_before < 128 && value < 128 && a_after > 127); + + let mask = Status::PS_CARRY | Status::PS_OVERFLOW; + + self.registers.status.set_with_mask( + mask, + Status::new(StatusArgs { + carry: did_carry, + overflow: did_overflow, + ..StatusArgs::none() + }), + ); + + self.load_accumulator(result); + + debug!("accumulator: {}", self.registers.accumulator); + } + + fn add_with_no_decimal(&mut self, value: u8) { + let a_before: u8 = self.registers.accumulator; + let c_before: u8 = u8::from(self.registers.status.contains(Status::PS_CARRY)); + let a_after: u8 = a_before.wrapping_add(c_before).wrapping_add(value); + + debug_assert_eq!(a_after, a_before.wrapping_add(c_before).wrapping_add(value)); + + let result = a_after; let did_carry = (result) < (a_before) || (a_after == 0 && c_before == 0x01) @@ -757,6 +803,52 @@ impl CPU { self.load_accumulator(a_after); } + fn subtract_with_no_decimal(&mut self, value: u8) { + // A - M - (1 - C) + + // nc -- 'not carry' + let nc: u8 = if self.registers.status.contains(Status::PS_CARRY) { + 0 + } else { + 1 + }; + + let a_before = self.registers.accumulator; + + let a_after = a_before.wrapping_sub(value).wrapping_sub(nc); + + // The overflow flag is set on two's-complement overflow. + // + // range of A is -128 to 127 + // range of - M - (1 - C) is -128 to 128 + // -(127 + 1) to -(-128 + 0) + // + let over = (nc == 0 && value > 127) && a_before < 128 && a_after > 127; + + let under = + (a_before > 127) && (0u8.wrapping_sub(value).wrapping_sub(nc) > 127) && a_after < 128; + + let did_overflow = over || under; + + let mask = Status::PS_CARRY | Status::PS_OVERFLOW; + + let result = a_after; + + // The carry flag is set on unsigned overflow. + let did_carry = (result) > (a_before); + + self.registers.status.set_with_mask( + mask, + Status::new(StatusArgs { + carry: did_carry, + overflow: did_overflow, + ..StatusArgs::none() + }), + ); + + self.load_accumulator(result); + } + fn subtract_with_carry(&mut self, value: u8) { // A - M - (1 - C) @@ -798,16 +890,12 @@ impl CPU { 0x00 }; - #[cfg(feature = "decimal_mode")] let result: u8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) { a_after.wrapping_sub(bcd1).wrapping_sub(bcd2) } else { a_after }; - #[cfg(not(feature = "decimal_mode"))] - let result = a_after; - // The carry flag is set on unsigned overflow. let did_carry = (result) > (a_before); diff --git a/src/instruction.rs b/src/instruction.rs index 8b2cde4..94f17fd 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -44,39 +44,40 @@ #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Instruction { - ADC, // 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 + 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 @@ -88,21 +89,22 @@ pub enum Instruction { // 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) - 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 + 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 } #[derive(Copy, Clone)] @@ -418,6 +420,32 @@ impl crate::Variant for Nmos6502 { } } } + +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), + } + } +} pub struct RevisionA; impl crate::Variant for RevisionA {