// // Z80Storage.hpp // Clock Signal // // Created by Thomas Harte on 01/09/2017. // Copyright 2017 Thomas Harte. All rights reserved. // /*! A repository for all the internal state of a CPU::Z80::Processor; extracted into a separate base class in order to remove it from visibility within the main Z80.hpp. */ class ProcessorStorage { protected: struct MicroOp { enum Type { BusOperation, IncrementR, DecodeOperation, MoveToNextProgram, Increment8NoFlags, Increment8, Increment16, Decrement8, Decrement16, Move8, Move16, IncrementPC, AssembleAF, DisassembleAF, And, Or, Xor, TestNZ, TestZ, TestNC, TestC, TestPO, TestPE, TestP, TestM, ADD16, ADC16, SBC16, CP8, SUB8, SBC8, ADD8, ADC8, NEG, ExDEHL, ExAFAFDash, EXX, EI, DI, IM, LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR, INI, INIR, IND, INDR, OUTI, OUTD, OUT_R, RLA, RLCA, RRA, RRCA, RLC, RRC, RL, RR, SLA, SRA, SLL, SRL, RLD, RRD, SetInstructionPage, CalculateIndexAddress, BeginNMI, BeginIRQ, BeginIRQMode0, RETN, JumpTo66, HALT, /// Decrements BC; if BC is 0 then moves to the next instruction. Otherwise allows this instruction to finish. DJNZ, DAA, CPL, SCF, CCF, /// Resets the bit in @c source implied by @c operation_ . RES, /// Tests the bit in @c source implied by @c operation_ . BIT, /// Sets the bit in @c source implied by @c operation_ . SET, /// Sets @c memptr_ to the target address implied by @c operation_ . CalculateRSTDestination, /// Resets subtract and carry, sets sign, zero, five and three according to the value of @c a_ and sets parity to the value of @c IFF2 . SetAFlags, /// Resets subtract and carry, sets sign, zero, five and three according to the value of @c operation and sets parity the same as sign. SetInFlags, /// Sets @c memptr_ to @c bc_.full+1 . SetOutFlags, /// Sets @c temp8_ to 0. SetZero, /// A no-op; used in instruction lists to indicate where an index calculation should go if this is an I[X/Y]+d operation. IndexedPlaceHolder, /// Sets @c memptr_ to (a_ << 8) + ((source_ + 1) & 0xff) SetAddrAMemptr, /// Resets: IFF1, IFF2, interrupt mode, the PC, I and R; sets all flags, the SP to 0xffff and a_ to 0xff. Reset }; Type type = Type::Reset; void *source = nullptr; void *destination = nullptr; PartialMachineCycle machine_cycle{}; }; static constexpr bool is_terminal(const MicroOp::Type type) { return type == MicroOp::MoveToNextProgram || type == MicroOp::DecodeOperation; } struct InstructionPage { std::vector instructions; std::vector all_operations; std::vector fetch_decode_execute; MicroOp *fetch_decode_execute_data = nullptr; bool is_indexed = false; }; ProcessorStorage(); void install_default_instruction_set(); uint8_t a_; RegisterPair16 bc_, de_, hl_; RegisterPair16 af_dash_, bc_dash_, de_dash_, hl_dash_; RegisterPair16 ix_, iy_, pc_, sp_; RegisterPair16 ir_, refresh_addr_; bool iff1_ = false, iff2_ = false; int interrupt_mode_ = 0; uint16_t pc_increment_ = 1; uint8_t sign_result_; // the sign flag is set if the value in sign_result_ is negative uint8_t zero_result_; // the zero flag is set if the value in zero_result_ is zero uint8_t half_carry_result_; // the half-carry flag is set if bit 4 of half_carry_result_ is set uint8_t bit53_result_; // the bit 3 and 5 flags are set if the corresponding bits of bit53_result_ are set uint8_t parity_overflow_result_; // the parity/overflow flag is set if the corresponding bit of parity_overflow_result_ is set uint8_t subtract_flag_; // contains a copy of the subtract flag in isolation uint8_t carry_result_; // the carry flag is set if bit 0 of carry_result_ is set uint8_t halt_mask_ = 0xff; unsigned 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. uint16_t last_address_bus_ = 0; // The value most recently put out on the address bus. HalfCycles number_of_cycles_; enum Interrupt: uint8_t { IRQ = 0x01, NMI = 0x02, Reset = 0x04, PowerOn = 0x08 }; uint8_t request_status_ = Interrupt::PowerOn; uint8_t last_request_status_ = Interrupt::PowerOn; bool irq_line_ = false, nmi_line_ = false; bool bus_request_line_ = false; bool wait_line_ = false; uint8_t operation_; RegisterPair16 temp16_, memptr_; uint8_t temp8_; const MicroOp *scheduled_program_counter_ = nullptr; std::vector conditional_call_untaken_program_; std::vector reset_program_; std::vector irq_program_[3]; std::vector nmi_program_; InstructionPage *current_instruction_page_; InstructionPage base_page_; InstructionPage ed_page_; InstructionPage fd_page_; InstructionPage dd_page_; InstructionPage cb_page_; InstructionPage fdcb_page_; InstructionPage ddcb_page_; /*! Gets the flags register. @see set_flags @returns The current value of the flags register. */ uint8_t get_flags() const { return (sign_result_ & Flag::Sign) | (zero_result_ ? 0 : Flag::Zero) | (bit53_result_ & (Flag::Bit5 | Flag::Bit3)) | (half_carry_result_ & Flag::HalfCarry) | (parity_overflow_result_ & Flag::Parity) | subtract_flag_ | (carry_result_ & Flag::Carry); } /*! Sets the flags register. @see set_flags @param flags The new value of the flags register. */ void set_flags(uint8_t flags) { sign_result_ = flags; zero_result_ = (flags & Flag::Zero) ^ Flag::Zero; bit53_result_ = flags; half_carry_result_ = flags; parity_overflow_result_ = flags; subtract_flag_ = flags & Flag::Subtract; carry_result_ = flags; } typedef MicroOp InstructionTable[256][30]; virtual void assemble_page(InstructionPage &target, InstructionTable &table, bool add_offsets) = 0; virtual void copy_program(const MicroOp *source, std::vector &destination) = 0; void assemble_fetch_decode_execute(InstructionPage &target, int length); void assemble_ed_page(InstructionPage &target); void assemble_cb_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets); void assemble_base_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets, InstructionPage &cb_page); // Allow state objects to capture and apply state. friend struct State; };