From 0e73ba4b3eda0d7424274e36e7ebde8de78518ef Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Mar 2018 09:47:00 -0500 Subject: [PATCH] Introduces proper 5/3 SCF/CCF behaviour for the Z80. While also `const`ing a bunch of things. --- .../Z80/Implementation/Z80Implementation.hpp | 210 ++++++++++-------- Processors/Z80/Implementation/Z80Storage.hpp | 4 + 2 files changed, 120 insertions(+), 94 deletions(-) diff --git a/Processors/Z80/Implementation/Z80Implementation.hpp b/Processors/Z80/Implementation/Z80Implementation.hpp index ea4d02859..3b8e76c3d 100644 --- a/Processors/Z80/Implementation/Z80Implementation.hpp +++ b/Processors/Z80/Implementation/Z80Implementation.hpp @@ -54,9 +54,12 @@ template < class T, } while(true) { - const MicroOp *operation = scheduled_program_counter_; + const MicroOp *const operation = scheduled_program_counter_; scheduled_program_counter_++; +#define set_did_compute_flags() \ + flag_adjustment_history_ |= 1; + #define set_parity(v) \ parity_overflow_result_ = static_cast(v^1);\ parity_overflow_result_ ^= parity_overflow_result_ >> 4;\ @@ -90,6 +93,7 @@ template < class T, ir_.bytes.low = (ir_.bytes.low & 0x80) | ((ir_.bytes.low + current_instruction_page_->r_step) & 0x7f); pc_.full += pc_increment_ & static_cast(halt_mask_); scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_]; + flag_adjustment_history_ <<= 1; break; case MicroOp::DecodeOperationNoRChange: refresh_addr_ = ir_; @@ -110,6 +114,7 @@ template < class T, case MicroOp::DisassembleAF: a_ = temp16_.bytes.high; set_flags(temp16_.bytes.low); + // break; // MARK: - Logical @@ -119,7 +124,8 @@ template < class T, set_parity(a_); \ half_carry_result_ = hf; \ subtract_flag_ = 0; \ - carry_result_ = 0; + carry_result_ = 0; \ + set_did_compute_flags(); case MicroOp::And: a_ &= *static_cast(operation->source); @@ -143,20 +149,31 @@ template < class T, subtract_flag_ = Flag::Subtract; half_carry_result_ = Flag::HalfCarry; bit53_result_ = a_; + set_did_compute_flags(); break; case MicroOp::CCF: half_carry_result_ = static_cast(carry_result_ << 4); carry_result_ ^= Flag::Carry; subtract_flag_ = 0; - bit53_result_ = a_; + if(flag_adjustment_history_&2) { + bit53_result_ = a_; + } else { + bit53_result_ |= a_; + } + set_did_compute_flags(); break; case MicroOp::SCF: carry_result_ = Flag::Carry; half_carry_result_ = 0; subtract_flag_ = 0; - bit53_result_ = a_; + if(flag_adjustment_history_&2) { + bit53_result_ = a_; + } else { + bit53_result_ |= a_; + } + set_did_compute_flags(); break; // MARK: - Flow control @@ -180,68 +197,69 @@ template < class T, half_carry_result_ = static_cast(half_result); \ parity_overflow_result_ = static_cast(overflow >> 5); \ subtract_flag_ = sub; \ - bit53_result_ = static_cast(b53); + bit53_result_ = static_cast(b53); \ + set_did_compute_flags(); case MicroOp::CP8: { - uint8_t value = *static_cast(operation->source); - int result = a_ - value; - int half_result = (a_&0xf) - (value&0xf); + const uint8_t value = *static_cast(operation->source); + const int result = a_ - value; + const int half_result = (a_&0xf) - (value&0xf); // overflow for a subtraction is when the signs were originally // different and the result is different again - int overflow = (value^a_) & (result^a_); + const int overflow = (value^a_) & (result^a_); // the 5 and 3 flags come from the operand, atypically set_arithmetic_flags(Flag::Subtract, value); } break; case MicroOp::SUB8: { - uint8_t value = *static_cast(operation->source); - int result = a_ - value; - int half_result = (a_&0xf) - (value&0xf); + const uint8_t value = *static_cast(operation->source); + const int result = a_ - value; + const int half_result = (a_&0xf) - (value&0xf); // overflow for a subtraction is when the signs were originally // different and the result is different again - int overflow = (value^a_) & (result^a_); + const int overflow = (value^a_) & (result^a_); a_ = static_cast(result); set_arithmetic_flags(Flag::Subtract, result); } break; case MicroOp::SBC8: { - uint8_t value = *static_cast(operation->source); - int result = a_ - value - (carry_result_ & Flag::Carry); - int half_result = (a_&0xf) - (value&0xf) - (carry_result_ & Flag::Carry); + const uint8_t value = *static_cast(operation->source); + const int result = a_ - value - (carry_result_ & Flag::Carry); + const int half_result = (a_&0xf) - (value&0xf) - (carry_result_ & Flag::Carry); // overflow for a subtraction is when the signs were originally // different and the result is different again - int overflow = (value^a_) & (result^a_); + const int overflow = (value^a_) & (result^a_); a_ = static_cast(result); set_arithmetic_flags(Flag::Subtract, result); } break; case MicroOp::ADD8: { - uint8_t value = *static_cast(operation->source); - int result = a_ + value; - int half_result = (a_&0xf) + (value&0xf); + const uint8_t value = *static_cast(operation->source); + const int result = a_ + value; + const int half_result = (a_&0xf) + (value&0xf); // overflow for addition is when the signs were originally // the same and the result is different - int overflow = ~(value^a_) & (result^a_); + const int overflow = ~(value^a_) & (result^a_); a_ = static_cast(result); set_arithmetic_flags(0, result); } break; case MicroOp::ADC8: { - uint8_t value = *static_cast(operation->source); - int result = a_ + value + (carry_result_ & Flag::Carry); - int half_result = (a_&0xf) + (value&0xf) + (carry_result_ & Flag::Carry); + const uint8_t value = *static_cast(operation->source); + const int result = a_ + value + (carry_result_ & Flag::Carry); + const int half_result = (a_&0xf) + (value&0xf) + (carry_result_ & Flag::Carry); // overflow for addition is when the signs were originally // the same and the result is different - int overflow = ~(value^a_) & (result^a_); + const int overflow = ~(value^a_) & (result^a_); a_ = static_cast(result); set_arithmetic_flags(0, result); @@ -250,9 +268,9 @@ template < class T, #undef set_arithmetic_flags case MicroOp::NEG: { - int overflow = (a_ == 0x80); - int result = -a_; - int halfResult = -(a_&0xf); + const int overflow = (a_ == 0x80); + const int result = -a_; + const int halfResult = -(a_&0xf); a_ = static_cast(result); bit53_result_ = sign_result_ = zero_result_ = a_; @@ -260,16 +278,17 @@ template < class T, subtract_flag_ = Flag::Subtract; carry_result_ = static_cast(result >> 8); half_carry_result_ = static_cast(halfResult); + set_did_compute_flags(); } break; case MicroOp::Increment8: { - uint8_t value = *static_cast(operation->source); - int result = value + 1; + const uint8_t value = *static_cast(operation->source); + const int result = value + 1; // with an increment, overflow occurs if the sign changes from // positive to negative - int overflow = (value ^ result) & ~value; - int half_result = (value&0xf) + 1; + const int overflow = (value ^ result) & ~value; + const int half_result = (value&0xf) + 1; *static_cast(operation->source) = static_cast(result); @@ -278,16 +297,17 @@ template < class T, half_carry_result_ = static_cast(half_result); parity_overflow_result_ = static_cast(overflow >> 5); subtract_flag_ = 0; + set_did_compute_flags(); } break; case MicroOp::Decrement8: { - uint8_t value = *static_cast(operation->source); - int result = value - 1; + const uint8_t value = *static_cast(operation->source); + const int result = value - 1; // with a decrement, overflow occurs if the sign changes from // negative to positive - int overflow = (value ^ result) & value; - int half_result = (value&0xf) - 1; + const int overflow = (value ^ result) & value; + const int half_result = (value&0xf) - 1; *static_cast(operation->source) = static_cast(result); @@ -296,28 +316,23 @@ template < class T, half_carry_result_ = static_cast(half_result); parity_overflow_result_ = static_cast(overflow >> 5); subtract_flag_ = Flag::Subtract; + set_did_compute_flags(); } break; case MicroOp::DAA: { - int lowNibble = a_ & 0xf; - int highNibble = a_ >> 4; + const int lowNibble = a_ & 0xf; + const int highNibble = a_ >> 4; int amountToAdd = 0; - if(carry_result_ & Flag::Carry) - { + if(carry_result_ & Flag::Carry) { amountToAdd = (lowNibble > 0x9 || (half_carry_result_ & Flag::HalfCarry)) ? 0x66 : 0x60; - } - else - { - if(half_carry_result_ & Flag::HalfCarry) - { + } else { + if(half_carry_result_ & Flag::HalfCarry) { if(lowNibble > 0x9) amountToAdd = (highNibble > 0x8) ? 0x66 : 0x06; else amountToAdd = (highNibble > 0x9) ? 0x66 : 0x06; - } - else - { + } else { if(lowNibble > 0x9) amountToAdd = (highNibble > 0x8) ? 0x66 : 0x06; else @@ -325,25 +340,18 @@ template < class T, } } - if(!(carry_result_ & Flag::Carry)) - { - if(lowNibble > 0x9) - { + if(!(carry_result_ & Flag::Carry)) { + if(lowNibble > 0x9) { if(highNibble > 0x8) carry_result_ = Flag::Carry; - } - else - { + } else { if(highNibble > 0x9) carry_result_ = Flag::Carry; } } - if(subtract_flag_) - { + if(subtract_flag_) { a_ -= amountToAdd; half_carry_result_ = ((half_carry_result_ & Flag::HalfCarry) && lowNibble < 0x6) ? Flag::HalfCarry : 0; - } - else - { + } else { a_ += amountToAdd; half_carry_result_ = (lowNibble > 0x9) ? Flag::HalfCarry : 0; } @@ -351,21 +359,23 @@ template < class T, sign_result_ = zero_result_ = bit53_result_ = a_; set_parity(a_); + set_did_compute_flags(); } break; // MARK: - 16-bit arithmetic case MicroOp::ADD16: { memptr_.full = *static_cast(operation->destination); - uint16_t sourceValue = *static_cast(operation->source); - uint16_t destinationValue = memptr_.full; - int result = sourceValue + destinationValue; - int halfResult = (sourceValue&0xfff) + (destinationValue&0xfff); + const uint16_t sourceValue = *static_cast(operation->source); + const uint16_t destinationValue = memptr_.full; + const int result = sourceValue + destinationValue; + const int halfResult = (sourceValue&0xfff) + (destinationValue&0xfff); bit53_result_ = static_cast(result >> 8); carry_result_ = static_cast(result >> 16); half_carry_result_ = static_cast(halfResult >> 8); subtract_flag_ = 0; + set_did_compute_flags(); *static_cast(operation->destination) = static_cast(result); memptr_.full++; @@ -373,12 +383,12 @@ template < class T, case MicroOp::ADC16: { memptr_.full = *static_cast(operation->destination); - uint16_t sourceValue = *static_cast(operation->source); - uint16_t destinationValue = memptr_.full; - int result = sourceValue + destinationValue + (carry_result_ & Flag::Carry); - int halfResult = (sourceValue&0xfff) + (destinationValue&0xfff) + (carry_result_ & Flag::Carry); + const uint16_t sourceValue = *static_cast(operation->source); + const uint16_t destinationValue = memptr_.full; + const int result = sourceValue + destinationValue + (carry_result_ & Flag::Carry); + const int halfResult = (sourceValue&0xfff) + (destinationValue&0xfff) + (carry_result_ & Flag::Carry); - int overflow = (result ^ destinationValue) & ~(destinationValue ^ sourceValue); + const int overflow = (result ^ destinationValue) & ~(destinationValue ^ sourceValue); bit53_result_ = sign_result_ = static_cast(result >> 8); @@ -387,6 +397,7 @@ template < class T, carry_result_ = static_cast(result >> 16); half_carry_result_ = static_cast(halfResult >> 8); parity_overflow_result_ = static_cast(overflow >> 13); + set_did_compute_flags(); *static_cast(operation->destination) = static_cast(result); memptr_.full++; @@ -394,15 +405,15 @@ template < class T, case MicroOp::SBC16: { memptr_.full = *static_cast(operation->destination); - uint16_t sourceValue = *static_cast(operation->source); - uint16_t destinationValue = memptr_.full; - int result = destinationValue - sourceValue - (carry_result_ & Flag::Carry); - int halfResult = (destinationValue&0xfff) - (sourceValue&0xfff) - (carry_result_ & Flag::Carry); + const uint16_t sourceValue = *static_cast(operation->source); + const uint16_t destinationValue = memptr_.full; + const int result = destinationValue - sourceValue - (carry_result_ & Flag::Carry); + const int halfResult = (destinationValue&0xfff) - (sourceValue&0xfff) - (carry_result_ & Flag::Carry); // subtraction, so parity rules are: // signs of operands were different, // sign of result is different - int overflow = (result ^ destinationValue) & (sourceValue ^ destinationValue); + const int overflow = (result ^ destinationValue) & (sourceValue ^ destinationValue); bit53_result_ = sign_result_ = static_cast(result >> 8); @@ -411,6 +422,7 @@ template < class T, carry_result_ = static_cast(result >> 16); half_carry_result_ = static_cast(halfResult >> 8); parity_overflow_result_ = static_cast(overflow >> 13); + set_did_compute_flags(); *static_cast(operation->destination) = static_cast(result); memptr_.full++; @@ -446,8 +458,8 @@ template < class T, } break; case MicroOp::ExAFAFDash: { - uint8_t a = a_; - uint8_t f = get_flags(); + const uint8_t a = a_; + const uint8_t f = get_flags(); set_flags(afDash_.bytes.low); a_ = afDash_.bytes.high; afDash_.bytes.high = a; @@ -476,11 +488,12 @@ template < class T, bc_.full--; \ de_.full += dir; \ hl_.full += dir; \ - uint8_t sum = a_ + temp8_; \ + const uint8_t sum = a_ + temp8_; \ bit53_result_ = static_cast((sum&0x8) | ((sum & 0x02) << 4)); \ subtract_flag_ = 0; \ half_carry_result_ = 0; \ - parity_overflow_result_ = bc_.full ? Flag::Parity : 0; + parity_overflow_result_ = bc_.full ? Flag::Parity : 0; \ + set_did_compute_flags(); case MicroOp::LDDR: { LDxR_STEP(-1); @@ -507,7 +520,7 @@ template < class T, bc_.full--; \ \ uint8_t result = a_ - temp8_; \ - uint8_t halfResult = (a_&0xf) - (temp8_&0xf); \ + const uint8_t halfResult = (a_&0xf) - (temp8_&0xf); \ \ parity_overflow_result_ = bc_.full ? Flag::Parity : 0; \ half_carry_result_ = halfResult; \ @@ -516,6 +529,7 @@ template < class T, \ result -= (halfResult >> 4)&1; \ bit53_result_ = static_cast((result&0x8) | ((result&0x2) << 4)); \ + set_did_compute_flags(); case MicroOp::CPDR: { CPxR_STEP(-1); @@ -546,7 +560,7 @@ template < class T, sign_result_ = zero_result_ = bit53_result_ = bc_.bytes.high; \ subtract_flag_ = (temp8_ >> 6) & Flag::Subtract; \ \ - int next_bc = bc_.bytes.low + dir; \ + const int next_bc = bc_.bytes.low + dir; \ int summation = temp8_ + (next_bc&0xff); \ \ if(summation > 0xff) { \ @@ -558,7 +572,8 @@ template < class T, } \ \ summation = (summation&7) ^ bc_.bytes.high; \ - set_parity(summation); + set_parity(summation); \ + set_did_compute_flags(); case MicroOp::INDR: { INxR_STEP(-1); @@ -598,7 +613,8 @@ template < class T, } \ \ summation = (summation&7) ^ bc_.bytes.high; \ - set_parity(summation); + set_parity(summation); \ + set_did_compute_flags(); case MicroOp::OUT_R: REPEAT(bc_.bytes.high); @@ -619,7 +635,7 @@ template < class T, // MARK: - Bit Manipulation case MicroOp::BIT: { - uint8_t result = *static_cast(operation->source) & (1 << ((operation_ >> 3)&7)); + const uint8_t result = *static_cast(operation->source) & (1 << ((operation_ >> 3)&7)); if(current_instruction_page_->is_indexed || ((operation_&0x07) == 6)) { bit53_result_ = memptr_.bytes.high; @@ -631,6 +647,7 @@ template < class T, half_carry_result_ = Flag::HalfCarry; subtract_flag_ = 0; parity_overflow_result_ = result ? 0 : Flag::Parity; + set_did_compute_flags(); } break; case MicroOp::RES: @@ -646,28 +663,29 @@ template < class T, #define set_rotate_flags() \ bit53_result_ = a_; \ carry_result_ = new_carry; \ - subtract_flag_ = half_carry_result_ = 0; + subtract_flag_ = half_carry_result_ = 0; \ + set_did_compute_flags(); case MicroOp::RLA: { - uint8_t new_carry = a_ >> 7; + const uint8_t new_carry = a_ >> 7; a_ = static_cast((a_ << 1) | (carry_result_ & Flag::Carry)); set_rotate_flags(); } break; case MicroOp::RRA: { - uint8_t new_carry = a_ & 1; + const uint8_t new_carry = a_ & 1; a_ = static_cast((a_ >> 1) | (carry_result_ << 7)); set_rotate_flags(); } break; case MicroOp::RLCA: { - uint8_t new_carry = a_ >> 7; + const uint8_t new_carry = a_ >> 7; a_ = static_cast((a_ << 1) | new_carry); set_rotate_flags(); } break; case MicroOp::RRCA: { - uint8_t new_carry = a_ & 1; + const uint8_t new_carry = a_ & 1; a_ = static_cast((a_ >> 1) | (new_carry << 7)); set_rotate_flags(); } break; @@ -678,7 +696,8 @@ template < class T, sign_result_ = zero_result_ = bit53_result_ = *static_cast(operation->source); \ set_parity(sign_result_); \ half_carry_result_ = 0; \ - subtract_flag_ = 0; + subtract_flag_ = 0; \ + set_did_compute_flags(); case MicroOp::RLC: carry_result_ = *static_cast(operation->source) >> 7; @@ -693,14 +712,14 @@ template < class T, break; case MicroOp::RL: { - uint8_t next_carry = *static_cast(operation->source) >> 7; + const uint8_t next_carry = *static_cast(operation->source) >> 7; *static_cast(operation->source) = static_cast((*static_cast(operation->source) << 1) | (carry_result_ & Flag::Carry)); carry_result_ = next_carry; set_shift_flags(); } break; case MicroOp::RR: { - uint8_t next_carry = *static_cast(operation->source); + const uint8_t next_carry = *static_cast(operation->source); *static_cast(operation->source) = static_cast((*static_cast(operation->source) >> 1) | (carry_result_ << 7)); carry_result_ = next_carry; set_shift_flags(); @@ -736,11 +755,12 @@ template < class T, subtract_flag_ = 0; \ half_carry_result_ = 0; \ set_parity(a_); \ - bit53_result_ = zero_result_ = sign_result_ = a_; + bit53_result_ = zero_result_ = sign_result_ = a_; \ + set_did_compute_flags(); case MicroOp::RRD: { memptr_.full = hl_.full + 1; - uint8_t low_nibble = a_ & 0xf; + const uint8_t low_nibble = a_ & 0xf; a_ = (a_ & 0xf0) | (temp8_ & 0xf); temp8_ = static_cast((temp8_ >> 4) | (low_nibble << 4)); set_decimal_rotate_flags(); @@ -748,7 +768,7 @@ template < class T, case MicroOp::RLD: { memptr_.full = hl_.full + 1; - uint8_t low_nibble = a_ & 0xf; + const uint8_t low_nibble = a_ & 0xf; a_ = (a_ & 0xf0) | (temp8_ >> 4); temp8_ = static_cast((temp8_ << 4) | low_nibble); set_decimal_rotate_flags(); @@ -784,12 +804,14 @@ template < class T, subtract_flag_ = half_carry_result_ = 0; sign_result_ = zero_result_ = bit53_result_ = *static_cast(operation->source); set_parity(sign_result_); + set_did_compute_flags(); break; case MicroOp::SetAFlags: subtract_flag_ = half_carry_result_ = 0; parity_overflow_result_ = iff2_ ? Flag::Parity : 0; sign_result_ = zero_result_ = bit53_result_ = a_; + set_did_compute_flags(); break; case MicroOp::SetZero: diff --git a/Processors/Z80/Implementation/Z80Storage.hpp b/Processors/Z80/Implementation/Z80Storage.hpp index acc147c90..0ef7244dd 100644 --- a/Processors/Z80/Implementation/Z80Storage.hpp +++ b/Processors/Z80/Implementation/Z80Storage.hpp @@ -134,6 +134,10 @@ class ProcessorStorage { uint8_t carry_result_; // the carry flag is set if bit 0 of carry_result_ is set uint8_t halt_mask_ = 0xff; + int flag_adjustment_history_ = 0; // a shifting record of whether each opcode set any flags; it turns out + // that knowledge of what the last opcode did is necessary to get bits 5 & 3 + // correct for SCF and CCF. + HalfCycles number_of_cycles_; enum Interrupt: uint8_t {