2017-05-14 21:46:41 +00:00
//
// Z80.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
# ifndef Z80_hpp
# define Z80_hpp
2017-08-22 00:43:12 +00:00
# include <cassert>
2017-05-14 21:46:41 +00:00
# include <cstdint>
2017-05-21 23:15:52 +00:00
# include <cstring>
2017-05-16 02:25:52 +00:00
# include <cstdio>
2017-05-30 01:44:33 +00:00
# include <vector>
2017-05-14 21:46:41 +00:00
2017-05-15 02:15:16 +00:00
# include "../RegisterSizes.hpp"
2017-07-26 00:20:55 +00:00
# include "../../ClockReceiver/ClockReceiver.hpp"
2017-05-15 02:15:16 +00:00
namespace CPU {
2017-05-14 21:46:41 +00:00
namespace Z80 {
/*
The list of registers that can be accessed via @ c set_value_of_register and @ c set_value_of_register .
*/
enum Register {
ProgramCounter ,
StackPointer ,
A , Flags , AF ,
B , C , BC ,
D , E , DE ,
H , L , HL ,
ADash , FlagsDash , AFDash ,
BDash , CDash , BCDash ,
DDash , EDash , DEDash ,
HDash , LDash , HLDash ,
IXh , IXl , IX ,
IYh , IYl , IY ,
2017-06-04 22:32:23 +00:00
R , I , Refresh ,
2017-05-22 23:14:46 +00:00
2017-07-22 02:31:42 +00:00
IFF1 , IFF2 , IM ,
MemPtr
2017-05-14 21:46:41 +00:00
} ;
/*
Flags as defined on the Z80 ; can be used to decode the result of @ c get_flags or to form a value for @ c set_flags .
*/
enum Flag : uint8_t {
Sign = 0x80 ,
Zero = 0x40 ,
Bit5 = 0x20 ,
HalfCarry = 0x10 ,
Bit3 = 0x08 ,
Parity = 0x04 ,
Overflow = 0x04 ,
Subtract = 0x02 ,
Carry = 0x01
} ;
/*!
Subclasses will be given the task of performing bus operations , allowing them to provide whatever interface they like
between a Z80 and the rest of the system . @ c BusOperation lists the types of bus operation that may be requested .
*/
2017-06-22 00:38:08 +00:00
struct PartialMachineCycle {
2017-06-18 01:53:45 +00:00
enum Operation {
2017-07-28 00:17:13 +00:00
ReadOpcode = 0 ,
2017-06-22 00:32:08 +00:00
Read ,
Write ,
Input ,
Output ,
2017-06-18 01:53:45 +00:00
Interrupt ,
2017-06-22 00:32:08 +00:00
Refresh ,
Internal ,
2017-06-18 01:53:45 +00:00
BusAcknowledge ,
2017-06-22 00:32:08 +00:00
2017-07-28 00:17:13 +00:00
ReadOpcodeWait ,
2017-06-22 00:32:08 +00:00
ReadWait ,
WriteWait ,
InputWait ,
2017-06-23 00:07:47 +00:00
OutputWait ,
InterruptWait ,
2017-07-28 00:17:13 +00:00
ReadOpcodeStart ,
2017-06-23 00:07:47 +00:00
ReadStart ,
WriteStart ,
InputStart ,
2017-06-22 00:32:08 +00:00
OutputStart ,
2017-07-28 00:17:13 +00:00
InterruptStart ,
2017-08-25 01:32:33 +00:00
} ;
const Operation operation ;
const HalfCycles length ;
const uint16_t * const address ;
uint8_t * const value ;
const bool was_requested ;
2017-06-22 00:32:08 +00:00
inline bool expects_action ( ) const {
return operation < = Operation : : Interrupt ;
}
inline bool is_terminal ( ) const {
return operation < = Operation : : BusAcknowledge ;
}
2017-06-23 00:07:47 +00:00
inline bool is_wait ( ) const {
2017-07-28 01:10:14 +00:00
return operation > = Operation : : ReadOpcodeWait & & operation < = Operation : : InterruptWait ;
2017-06-23 00:07:47 +00:00
}
2017-08-25 01:32:33 +00:00
2017-08-25 02:00:21 +00:00
PartialMachineCycle ( const PartialMachineCycle & rhs ) noexcept :
2017-08-25 01:32:33 +00:00
operation ( rhs . operation ) ,
length ( rhs . length ) ,
address ( rhs . address ) ,
value ( rhs . value ) ,
was_requested ( rhs . was_requested ) { }
2017-08-25 02:00:21 +00:00
PartialMachineCycle ( Operation operation , HalfCycles length , uint16_t * address , uint8_t * value , bool was_requested ) noexcept :
operation ( operation ) , length ( length ) , address ( address ) , value ( value ) , was_requested ( was_requested ) { }
PartialMachineCycle ( ) noexcept :
2017-08-25 01:32:33 +00:00
operation ( Internal ) , length ( 0 ) , address ( nullptr ) , value ( nullptr ) , was_requested ( false ) { }
2017-05-16 02:25:52 +00:00
} ;
2017-08-03 02:09:59 +00:00
class BusHandler {
public :
void flush ( ) { }
HalfCycles perform_machine_cycle ( const PartialMachineCycle & cycle ) {
return HalfCycles ( 0 ) ;
}
} ;
2017-06-18 21:08:50 +00:00
// Elemental bus operations
2017-08-25 01:32:33 +00:00
# define ReadOpcodeStart() PartialMachineCycle(PartialMachineCycle::ReadOpcodeStart, HalfCycles(3), &pc_.full, &operation_, false)
# define ReadOpcodeWait(f) PartialMachineCycle(PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f)
# define ReadOpcodeEnd() PartialMachineCycle(PartialMachineCycle::ReadOpcode, HalfCycles(1), &pc_.full, &operation_, false)
2017-07-28 00:17:13 +00:00
2017-08-25 01:32:33 +00:00
# define Refresh(len) PartialMachineCycle(PartialMachineCycle::Refresh, HalfCycles(len), &refresh_addr_.full, nullptr, false)
2017-06-18 16:21:27 +00:00
2017-08-25 01:32:33 +00:00
# define ReadStart(addr, val) PartialMachineCycle(PartialMachineCycle::ReadStart, HalfCycles(3), &addr.full, &val, false)
# define ReadWait(l, addr, val, f) PartialMachineCycle(PartialMachineCycle::ReadWait, HalfCycles(l), &addr.full, &val, f)
# define ReadEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Read, HalfCycles(3), &addr.full, &val, false)
2017-06-18 16:21:27 +00:00
2017-08-25 01:32:33 +00:00
# define WriteStart(addr, val) PartialMachineCycle(PartialMachineCycle::WriteStart,HalfCycles(3), &addr.full, &val, false)
# define WriteWait(l, addr, val, f) PartialMachineCycle(PartialMachineCycle::WriteWait, HalfCycles(l), &addr.full, &val, f)
# define WriteEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Write, HalfCycles(3), &addr.full, &val, false)
2017-06-18 16:21:27 +00:00
2017-08-25 01:32:33 +00:00
# define InputStart(addr, val) PartialMachineCycle(PartialMachineCycle::InputStart, HalfCycles(3), &addr.full, &val, false)
# define InputWait(addr, val, f) PartialMachineCycle(PartialMachineCycle::InputWait, HalfCycles(2), &addr.full, &val, f)
# define InputEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Input, HalfCycles(3), &addr.full, &val, false)
2017-06-18 16:21:27 +00:00
2017-08-25 01:32:33 +00:00
# define OutputStart(addr, val) PartialMachineCycle(PartialMachineCycle::OutputStart, HalfCycles(3), &addr.full, &val, false)
# define OutputWait(addr, val, f) PartialMachineCycle(PartialMachineCycle::OutputWait, HalfCycles(2), &addr.full, &val, f)
# define OutputEnd(addr, val) PartialMachineCycle(PartialMachineCycle::Output, HalfCycles(3), &addr.full, &val, false)
2017-06-18 16:21:27 +00:00
2017-08-25 01:32:33 +00:00
# define IntAckStart(length, val) PartialMachineCycle(PartialMachineCycle::InterruptStart, HalfCycles(length), nullptr, &val, false)
# define IntWait(val) PartialMachineCycle(PartialMachineCycle::InterruptWait, HalfCycles(2), nullptr, &val, true)
# define IntAckEnd(val) PartialMachineCycle(PartialMachineCycle::Interrupt, HalfCycles(3), nullptr, &val, false)
2017-07-28 00:17:13 +00:00
2017-06-21 22:47:00 +00:00
2017-06-18 21:08:50 +00:00
// A wrapper to express a bus operation as a micro-op
2017-06-18 21:25:15 +00:00
# define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op}
2017-06-18 16:21:27 +00:00
2017-06-18 21:08:50 +00:00
// Compound bus operations, as micro-ops
2017-07-26 23:42:00 +00:00
# define Read3(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadEnd(addr, val))
# define Read4(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(2, addr, val, false)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadEnd(addr, val))
# define Read5(addr, val) BusOp(ReadStart(addr, val)), BusOp(ReadWait(4, addr, val, false)), BusOp(ReadWait(2, addr, val, true)), BusOp(ReadEnd(addr, val))
2017-06-18 21:08:50 +00:00
2017-07-26 23:42:00 +00:00
# define Write3(addr, val) BusOp(WriteStart(addr, val)), BusOp(WriteWait(2, addr, val, true)), BusOp(WriteEnd(addr, val))
# define Write5(addr, val) BusOp(WriteStart(addr, val)), BusOp(WriteWait(4, addr, val, false)), BusOp(WriteWait(2, addr, val, true)), BusOp(WriteEnd(addr, val))
2017-06-18 21:08:50 +00:00
# define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val))
# define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val))
2017-07-26 23:42:00 +00:00
# define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {PartialMachineCycle::Internal, HalfCycles(len), nullptr, nullptr, false}}
2017-06-18 16:21:27 +00:00
2017-06-18 21:08:50 +00:00
/// A sequence is a series of micro-ops that ends in a move-to-next-program operation.
2017-06-18 16:21:27 +00:00
# define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} }
2017-06-18 21:08:50 +00:00
/// An instruction is the part of an instruction that follows instruction fetch; it should include two or more refresh cycles and then the work of the instruction.
2017-06-18 16:21:27 +00:00
# define Instr(r, ...) Sequence(BusOp(Refresh(r)), __VA_ARGS__)
2017-06-18 21:08:50 +00:00
/// A standard instruction is one with the most normal timing: two cycles of refresh, then the work.
2017-07-26 23:42:00 +00:00
# define StdInstr(...) Instr(4, __VA_ARGS__)
2017-06-18 16:21:27 +00:00
2017-06-18 21:08:50 +00:00
// Assumption made: those instructions that are rated with an opcode fetch greater than four cycles spend the extra time
// providing a lengthened refresh cycle. I assume this because the CPU doesn't have foresight and presumably spends the
// normal refresh time decoding. So if it gets to cycle four and realises it has two more cycles of work, I have assumed
// it simply maintains the refresh state for an extra two cycles.
2017-05-15 02:08:15 +00:00
/*!
2017-05-30 23:24:58 +00:00
@ abstact An abstract base class for emulation of a Z80 processor via the curiously recurring template pattern / f - bounded polymorphism .
2017-05-15 02:08:15 +00:00
2017-05-30 23:24:58 +00:00
@ discussion Subclasses should implement @ c perform_machine_cycle in
order to provide the bus on which the Z80 operates and @ c flush ( ) , which is called upon completion of a continuous run
2017-05-15 02:08:15 +00:00
of cycles to allow a subclass to bring any on - demand activities up to date .
*/
2017-08-27 03:08:57 +00:00
template < class T , bool uses_bus_request , bool uses_wait_line > class Processor {
2017-05-15 02:08:15 +00:00
private :
2017-08-03 02:09:59 +00:00
T & bus_handler_ ;
2017-06-17 22:18:28 +00:00
uint8_t a_ ;
2017-05-15 11:55:53 +00:00
RegisterPair bc_ , de_ , hl_ ;
RegisterPair afDash_ , bcDash_ , deDash_ , hlDash_ ;
RegisterPair ix_ , iy_ , pc_ , sp_ ;
2017-06-22 01:11:00 +00:00
RegisterPair ir_ , refresh_addr_ ;
2017-05-21 16:53:17 +00:00
bool iff1_ , iff2_ ;
2017-05-22 23:14:46 +00:00
int interrupt_mode_ ;
2017-06-03 21:07:05 +00:00
uint16_t pc_increment_ ;
2017-05-30 02:23:19 +00:00
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
2017-05-31 02:15:43 +00:00
uint8_t halt_mask_ ;
2017-05-15 11:55:53 +00:00
2017-07-26 23:42:00 +00:00
HalfCycles number_of_cycles_ ;
2017-05-16 02:25:52 +00:00
2017-06-02 00:34:52 +00:00
enum Interrupt : uint8_t {
IRQ = 0x01 ,
NMI = 0x02 ,
2017-06-03 23:09:47 +00:00
Reset = 0x04 ,
PowerOn = 0x08
2017-06-02 00:34:52 +00:00
} ;
uint8_t request_status_ ;
2017-06-02 01:40:08 +00:00
uint8_t last_request_status_ ;
2017-06-23 00:11:19 +00:00
bool irq_line_ , nmi_line_ ;
2017-06-03 23:09:47 +00:00
bool bus_request_line_ ;
2017-06-22 00:32:08 +00:00
bool wait_line_ ;
2017-06-02 00:34:52 +00:00
2017-05-16 02:25:52 +00:00
uint8_t operation_ ;
2017-06-04 19:07:07 +00:00
RegisterPair temp16_ , memptr_ ;
2017-05-21 13:17:30 +00:00
uint8_t temp8_ ;
2017-05-16 02:25:52 +00:00
2017-06-03 23:17:34 +00:00
struct MicroOp {
enum Type {
BusOperation ,
DecodeOperation ,
DecodeOperationNoRChange ,
MoveToNextProgram ,
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 ,
DJNZ ,
DAA ,
CPL ,
SCF ,
CCF ,
RES ,
BIT ,
SET ,
CalculateRSTDestination ,
SetAFlags ,
SetInFlags ,
SetZero ,
IndexedPlaceHolder ,
Reset
} ;
Type type ;
void * source ;
void * destination ;
2017-06-22 00:38:08 +00:00
PartialMachineCycle machine_cycle ;
2017-06-03 23:17:34 +00:00
} ;
const MicroOp * scheduled_program_counter_ ;
2017-05-21 23:15:52 +00:00
struct InstructionPage {
2017-05-30 01:44:33 +00:00
std : : vector < MicroOp * > instructions ;
std : : vector < MicroOp > all_operations ;
std : : vector < MicroOp > fetch_decode_execute ;
2017-06-01 22:28:04 +00:00
MicroOp * fetch_decode_execute_data ;
2017-06-04 18:13:38 +00:00
uint8_t r_step ;
bool is_indexed ;
2017-05-30 02:23:19 +00:00
2017-06-04 18:13:38 +00:00
InstructionPage ( ) : r_step ( 1 ) , is_indexed ( false ) { }
2017-05-21 23:15:52 +00:00
} ;
2017-06-21 00:57:23 +00:00
std : : vector < MicroOp > conditional_call_untaken_program_ ;
2017-06-02 02:16:22 +00:00
std : : vector < MicroOp > reset_program_ ;
std : : vector < MicroOp > irq_program_ [ 3 ] ;
std : : vector < MicroOp > nmi_program_ ;
2017-05-28 01:06:56 +00:00
InstructionPage * current_instruction_page_ ;
2017-05-21 23:15:52 +00:00
InstructionPage base_page_ ;
InstructionPage ed_page_ ;
InstructionPage fd_page_ ;
2017-05-21 23:26:40 +00:00
InstructionPage dd_page_ ;
2017-05-20 21:04:25 +00:00
2017-05-27 19:39:22 +00:00
InstructionPage cb_page_ ;
InstructionPage fdcb_page_ ;
InstructionPage ddcb_page_ ;
2017-06-03 16:16:21 +00:00
2017-06-18 21:08:50 +00:00
/* The following are helper macros that define common parts of instructions */
# define Inc16(r) {(&r == &pc_) ? MicroOp::IncrementPC : MicroOp::Increment16, &r.full}
2017-05-19 23:18:35 +00:00
2017-06-18 21:48:54 +00:00
# define ReadInc(addr, val) Read3(addr, val), Inc16(addr)
2017-06-20 02:20:23 +00:00
# define Read4Inc(addr, val) Read4(addr, val), Inc16(addr)
2017-06-21 02:20:58 +00:00
# define Read5Inc(addr, val) Read5(addr, val), Inc16(addr)
2017-06-18 21:48:54 +00:00
# define WriteInc(addr, val) Write3(addr, val), {MicroOp::Increment16, &addr.full}
2017-05-19 23:18:35 +00:00
2017-06-18 21:08:50 +00:00
# define Read16Inc(addr, val) ReadInc(addr, val.bytes.low), ReadInc(addr, val.bytes.high)
2017-06-19 01:00:44 +00:00
# define Read16(addr, val) ReadInc(addr, val.bytes.low), Read3(addr, val.bytes.high)
2017-05-19 23:18:35 +00:00
2017-06-18 21:48:54 +00:00
# define Write16(addr, val) WriteInc(addr, val.bytes.low), Write3(addr, val.bytes.high)
2017-05-21 14:18:43 +00:00
2017-07-26 23:42:00 +00:00
# define INDEX() {MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &index}
2017-06-20 02:20:23 +00:00
# define FINDEX() {MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), {MicroOp::CalculateIndexAddress, &index}
2017-06-18 21:08:50 +00:00
# define INDEX_ADDR() (add_offsets ? memptr_ : index)
2017-05-28 18:20:05 +00:00
2017-06-18 21:25:15 +00:00
# define Push(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.bytes.high), {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.bytes.low)
# define Pop(x) Read3(sp_, x.bytes.low), {MicroOp::Increment16, &sp_.full}, Read3(sp_, x.bytes.high), {MicroOp::Increment16, &sp_.full}
2017-05-28 18:20:05 +00:00
2017-06-19 23:25:53 +00:00
# define Push8(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.bytes.high), {MicroOp::Decrement16, &sp_.full}, Write5(sp_, x.bytes.low)
# define Pop7(x) Read3(sp_, x.bytes.low), {MicroOp::Increment16, &sp_.full}, Read4(sp_, x.bytes.high), {MicroOp::Increment16, &sp_.full}
2017-06-18 21:08:50 +00:00
/* The following are actual instructions */
2017-07-26 23:42:00 +00:00
# define NOP Sequence(BusOp(Refresh(4)))
2017-05-19 23:18:35 +00:00
2017-06-21 22:47:00 +00:00
# define JP(cc) StdInstr(Read16Inc(pc_, temp16_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &temp16_.full, &pc_.full})
2017-06-21 00:57:23 +00:00
# define CALL(cc) StdInstr(ReadInc(pc_, temp16_.bytes.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, temp16_.bytes.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full})
2017-07-26 23:42:00 +00:00
# define RET(cc) Instr(6, {MicroOp::cc, nullptr}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full})
# define JR(cc) StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full})
# define RST() Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
2017-06-18 21:25:15 +00:00
# define LD(a, b) StdInstr({MicroOp::Move8, &b, &a})
2017-05-20 02:57:43 +00:00
2017-05-27 03:37:17 +00:00
# define LD_GROUP(r, ri) \
LD ( r , bc_ . bytes . high ) , LD ( r , bc_ . bytes . low ) , LD ( r , de_ . bytes . high ) , LD ( r , de_ . bytes . low ) , \
2017-06-21 02:25:00 +00:00
LD ( r , index . bytes . high ) , LD ( r , index . bytes . low ) , \
StdInstr ( INDEX ( ) , Read3 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : Move8 , & temp8_ , & ri } ) , \
LD ( r , a_ )
2017-05-20 02:57:43 +00:00
2017-05-27 19:54:24 +00:00
# define READ_OP_GROUP(op) \
2017-06-18 21:25:15 +00:00
StdInstr ( { MicroOp : : op , & bc_ . bytes . high } ) , StdInstr ( { MicroOp : : op , & bc_ . bytes . low } ) , \
StdInstr ( { MicroOp : : op , & de_ . bytes . high } ) , StdInstr ( { MicroOp : : op , & de_ . bytes . low } ) , \
StdInstr ( { MicroOp : : op , & index . bytes . high } ) , StdInstr ( { MicroOp : : op , & index . bytes . low } ) , \
StdInstr ( INDEX ( ) , Read3 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( { MicroOp : : op , & a_ } )
2017-05-20 03:03:34 +00:00
2017-06-13 02:22:00 +00:00
# define READ_OP_GROUP_D(op) \
2017-06-18 21:18:01 +00:00
StdInstr ( { MicroOp : : op , & bc_ . bytes . high } ) , StdInstr ( { MicroOp : : op , & bc_ . bytes . low } ) , \
StdInstr ( { MicroOp : : op , & de_ . bytes . high } ) , StdInstr ( { MicroOp : : op , & de_ . bytes . low } ) , \
StdInstr ( { MicroOp : : op , & index . bytes . high } ) , StdInstr ( { MicroOp : : op , & index . bytes . low } ) , \
StdInstr ( INDEX ( ) , Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( { MicroOp : : op , & a_ } )
2017-06-13 02:22:00 +00:00
2017-06-18 21:18:01 +00:00
# define RMW(x, op, ...) StdInstr(INDEX(), Read4(INDEX_ADDR(), x), {MicroOp::op, &x}, Write3(INDEX_ADDR(), x))
2017-06-20 00:51:28 +00:00
# define RMWI(x, op, ...) StdInstr(Read4(INDEX_ADDR(), x), {MicroOp::op, &x}, Write3(INDEX_ADDR(), x))
2017-05-28 01:06:56 +00:00
2017-05-27 19:54:24 +00:00
# define MODIFY_OP_GROUP(op) \
2017-06-18 21:18:01 +00:00
StdInstr ( { MicroOp : : op , & bc_ . bytes . high } ) , StdInstr ( { MicroOp : : op , & bc_ . bytes . low } ) , \
StdInstr ( { MicroOp : : op , & de_ . bytes . high } ) , StdInstr ( { MicroOp : : op , & de_ . bytes . low } ) , \
StdInstr ( { MicroOp : : op , & index . bytes . high } ) , StdInstr ( { MicroOp : : op , & index . bytes . low } ) , \
2017-05-28 01:06:56 +00:00
RMW ( temp8_ , op ) , \
2017-06-18 21:18:01 +00:00
StdInstr ( { MicroOp : : op , & a_ } )
2017-05-27 19:54:24 +00:00
2017-05-28 03:54:06 +00:00
# define IX_MODIFY_OP_GROUP(op) \
2017-05-28 01:06:56 +00:00
RMWI ( bc_ . bytes . high , op ) , \
RMWI ( bc_ . bytes . low , op ) , \
RMWI ( de_ . bytes . high , op ) , \
RMWI ( de_ . bytes . low , op ) , \
RMWI ( hl_ . bytes . high , op ) , \
RMWI ( hl_ . bytes . low , op ) , \
RMWI ( temp8_ , op ) , \
RMWI ( a_ , op )
2017-05-28 03:54:06 +00:00
# define IX_READ_OP_GROUP(op) \
2017-06-21 01:15:56 +00:00
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } ) , \
StdInstr ( Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : op , & temp8_ } )
2017-05-28 03:54:06 +00:00
2017-07-26 23:42:00 +00:00
# define ADD16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::ADD16, &s.full, &d.full})
# define ADC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::ADC16, &s.full, &d.full})
# define SBC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full})
2017-05-20 21:04:25 +00:00
2017-06-03 21:07:05 +00:00
# define isTerminal(n) (n == MicroOp::MoveToNextProgram || n == MicroOp::DecodeOperation || n == MicroOp::DecodeOperationNoRChange)
2017-06-03 16:19:25 +00:00
2017-06-19 23:25:53 +00:00
typedef MicroOp InstructionTable [ 256 ] [ 30 ] ;
2017-05-19 23:18:35 +00:00
2017-05-27 03:23:33 +00:00
void assemble_page ( InstructionPage & target , InstructionTable & table , bool add_offsets ) {
2017-05-21 23:15:52 +00:00
size_t number_of_micro_ops = 0 ;
size_t lengths [ 256 ] ;
// Count number of micro-ops required.
for ( int c = 0 ; c < 256 ; c + + ) {
size_t length = 0 ;
2017-06-03 16:19:25 +00:00
while ( ! isTerminal ( table [ c ] [ length ] . type ) ) length + + ;
2017-05-21 23:15:52 +00:00
length + + ;
lengths [ c ] = length ;
number_of_micro_ops + = length ;
}
// Allocate a landing area.
2017-08-25 01:32:33 +00:00
std : : vector < size_t > operation_indices ;
2017-08-27 03:08:57 +00:00
target . all_operations . reserve ( number_of_micro_ops ) ;
2017-05-30 01:44:33 +00:00
target . instructions . resize ( 256 , nullptr ) ;
2017-05-21 23:15:52 +00:00
2017-08-25 01:32:33 +00:00
// Copy in all programs, recording where they go.
2017-05-21 23:15:52 +00:00
size_t destination = 0 ;
2017-06-04 21:55:19 +00:00
for ( size_t c = 0 ; c < 256 ; c + + ) {
2017-08-25 01:32:33 +00:00
operation_indices . push_back ( target . all_operations . size ( ) ) ;
2017-07-22 01:19:46 +00:00
for ( size_t t = 0 ; t < lengths [ c ] ; ) {
2017-05-29 15:40:56 +00:00
// Skip zero-length bus cycles.
2017-07-25 00:10:05 +00:00
if ( table [ c ] [ t ] . type = = MicroOp : : BusOperation & & table [ c ] [ t ] . machine_cycle . length . as_int ( ) = = 0 ) {
2017-05-29 15:40:56 +00:00
t + + ;
continue ;
}
2017-08-27 03:08:57 +00:00
// Skip optional waits if this instance doesn't use the wait line.
if ( table [ c ] [ t ] . machine_cycle . was_requested & & ! uses_wait_line ) {
t + + ;
continue ;
}
2017-05-27 03:23:33 +00:00
// If an index placeholder is hit then drop it, and if offsets aren't being added,
2017-05-28 01:06:56 +00:00
// then also drop the indexing that follows, which is assumed to be everything
// up to and including the next ::CalculateIndexAddress. Coupled to the INDEX() macro.
2017-05-27 03:23:33 +00:00
if ( table [ c ] [ t ] . type = = MicroOp : : IndexedPlaceHolder ) {
t + + ;
2017-05-28 01:06:56 +00:00
if ( ! add_offsets ) {
while ( table [ c ] [ t ] . type ! = MicroOp : : CalculateIndexAddress ) t + + ;
t + + ;
}
2017-05-27 03:23:33 +00:00
}
2017-08-25 01:32:33 +00:00
target . all_operations . emplace_back ( table [ c ] [ t ] ) ;
2017-05-27 03:23:33 +00:00
destination + + ;
t + + ;
}
2017-05-20 21:04:25 +00:00
}
2017-08-25 01:32:33 +00:00
// Since the vector won't change again, it's now safe to set pointers.
size_t c = 0 ;
for ( size_t index : operation_indices ) {
target . instructions [ c ] = & target . all_operations [ index ] ;
c + + ;
}
2017-05-20 21:04:25 +00:00
}
2017-05-21 23:15:52 +00:00
void assemble_ed_page ( InstructionPage & target ) {
2017-06-19 00:31:12 +00:00
# define IN_C(r) StdInstr(Input(bc_, r), {MicroOp::SetInFlags, &r})
# define OUT_C(r) StdInstr(Output(bc_, r))
2017-05-28 18:50:51 +00:00
# define IN_OUT(r) IN_C(r), OUT_C(r)
2017-05-29 16:25:10 +00:00
# define NOP_ROW() NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP
2017-05-21 23:15:52 +00:00
InstructionTable ed_program_table = {
2017-05-20 21:29:30 +00:00
NOP_ROW ( ) , /* 0x00 */
NOP_ROW ( ) , /* 0x10 */
NOP_ROW ( ) , /* 0x20 */
NOP_ROW ( ) , /* 0x30 */
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( bc_ . bytes . high ) ,
2017-07-22 15:39:13 +00:00
/* 0x42 SBC HL, BC */ SBC16 ( hl_ , bc_ ) , /* 0x43 LD (nn), BC */ StdInstr ( Read16Inc ( pc_ , memptr_ ) , Write16 ( memptr_ , bc_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x44 NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x45 RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
2017-07-26 23:42:00 +00:00
/* 0x46 IM 0 */ StdInstr ( { MicroOp : : IM } ) , /* 0x47 LD I, A */ Instr ( 6 , { MicroOp : : Move8 , & a_ , & ir_ . bytes . high } ) ,
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( bc_ . bytes . low ) ,
2017-06-18 21:48:54 +00:00
/* 0x4a ADC HL, BC */ ADC16 ( hl_ , bc_ ) , /* 0x4b LD BC, (nn) */ StdInstr ( Read16Inc ( pc_ , temp16_ ) , Read16 ( temp16_ , bc_ ) ) ,
/* 0x4c NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x4d RETI */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
2017-07-26 23:42:00 +00:00
/* 0x4e IM 0/1 */ StdInstr ( { MicroOp : : IM } ) , /* 0x4f LD R, A */ Instr ( 6 , { MicroOp : : Move8 , & a_ , & ir_ . bytes . low } ) ,
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( de_ . bytes . high ) ,
2017-07-22 15:39:13 +00:00
/* 0x52 SBC HL, DE */ SBC16 ( hl_ , de_ ) , /* 0x53 LD (nn), DE */ StdInstr ( Read16Inc ( pc_ , memptr_ ) , Write16 ( memptr_ , de_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x54 NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x55 RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
2017-07-26 23:42:00 +00:00
/* 0x56 IM 1 */ StdInstr ( { MicroOp : : IM } ) , /* 0x57 LD A, I */ Instr ( 6 , { MicroOp : : Move8 , & ir_ . bytes . high , & a_ } , { MicroOp : : SetAFlags } ) ,
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( de_ . bytes . low ) ,
2017-06-21 01:32:23 +00:00
/* 0x5a ADC HL, DE */ ADC16 ( hl_ , de_ ) , /* 0x5b LD DE, (nn) */ StdInstr ( Read16Inc ( pc_ , temp16_ ) , Read16 ( temp16_ , de_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x5c NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x5d RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
2017-07-26 23:42:00 +00:00
/* 0x5e IM 2 */ StdInstr ( { MicroOp : : IM } ) , /* 0x5f LD A, R */ Instr ( 6 , { MicroOp : : Move8 , & ir_ . bytes . low , & a_ } , { MicroOp : : SetAFlags } ) ,
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( hl_ . bytes . high ) ,
2017-07-22 15:39:13 +00:00
/* 0x62 SBC HL, HL */ SBC16 ( hl_ , hl_ ) , /* 0x63 LD (nn), HL */ StdInstr ( Read16Inc ( pc_ , memptr_ ) , Write16 ( memptr_ , hl_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x64 NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x65 RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
2017-07-26 23:42:00 +00:00
/* 0x66 IM 0 */ StdInstr ( { MicroOp : : IM } ) , /* 0x67 RRD */ StdInstr ( Read3 ( hl_ , temp8_ ) , InternalOperation ( 8 ) , { MicroOp : : RRD } , Write3 ( hl_ , temp8_ ) ) ,
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( hl_ . bytes . low ) ,
2017-06-21 01:32:23 +00:00
/* 0x6a ADC HL, HL */ ADC16 ( hl_ , hl_ ) , /* 0x6b LD HL, (nn) */ StdInstr ( Read16Inc ( pc_ , temp16_ ) , Read16 ( temp16_ , hl_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x6c NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x6d RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
2017-07-26 23:42:00 +00:00
/* 0x6e IM 0/1 */ StdInstr ( { MicroOp : : IM } ) , /* 0x6f RLD */ StdInstr ( Read3 ( hl_ , temp8_ ) , InternalOperation ( 8 ) , { MicroOp : : RLD } , Write3 ( hl_ , temp8_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0x70 IN (C) */ IN_C ( temp8_ ) , /* 0x71 OUT (C), 0 */ StdInstr ( { MicroOp : : SetZero } , Output ( bc_ , temp8_ ) ) ,
2017-07-22 15:39:13 +00:00
/* 0x72 SBC HL, SP */ SBC16 ( hl_ , sp_ ) , /* 0x73 LD (nn), SP */ StdInstr ( Read16Inc ( pc_ , memptr_ ) , Write16 ( memptr_ , sp_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x74 NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x75 RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
/* 0x76 IM 1 */ StdInstr ( { MicroOp : : IM } ) , /* 0x77 XX */ NOP ,
2017-05-28 18:50:51 +00:00
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT ( a_ ) ,
2017-06-21 01:32:23 +00:00
/* 0x7a ADC HL, SP */ ADC16 ( hl_ , sp_ ) , /* 0x7b LD SP, (nn) */ StdInstr ( Read16Inc ( pc_ , temp16_ ) , Read16 ( temp16_ , sp_ ) ) ,
2017-06-18 21:48:54 +00:00
/* 0x7c NEG */ StdInstr ( { MicroOp : : NEG } ) , /* 0x7d RETN */ StdInstr ( Pop ( pc_ ) , { MicroOp : : RETN } ) ,
/* 0x7e IM 2 */ StdInstr ( { MicroOp : : IM } ) , /* 0x7f XX */ NOP ,
2017-05-20 21:29:30 +00:00
NOP_ROW ( ) , /* 0x80 */
NOP_ROW ( ) , /* 0x90 */
2017-06-18 21:56:48 +00:00
/* 0xa0 LDI */ StdInstr ( Read3 ( hl_ , temp8_ ) , Write5 ( de_ , temp8_ ) , { MicroOp : : LDI } ) ,
2017-07-26 23:42:00 +00:00
/* 0xa1 CPI */ StdInstr ( Read3 ( hl_ , temp8_ ) , InternalOperation ( 10 ) , { MicroOp : : CPI } ) ,
/* 0xa2 INI */ Instr ( 6 , Input ( bc_ , temp8_ ) , Write3 ( hl_ , temp8_ ) , { MicroOp : : INI } ) ,
/* 0xa3 OTI */ Instr ( 6 , Read3 ( hl_ , temp8_ ) , { MicroOp : : OUTI } , Output ( bc_ , temp8_ ) ) ,
2017-05-29 16:25:10 +00:00
NOP , NOP , NOP , NOP ,
2017-06-18 21:56:48 +00:00
/* 0xa8 LDD */ StdInstr ( Read3 ( hl_ , temp8_ ) , Write5 ( de_ , temp8_ ) , { MicroOp : : LDD } ) ,
2017-07-26 23:42:00 +00:00
/* 0xa9 CPD */ StdInstr ( Read3 ( hl_ , temp8_ ) , InternalOperation ( 10 ) , { MicroOp : : CPD } ) ,
/* 0xaa IND */ Instr ( 6 , Input ( bc_ , temp8_ ) , Write3 ( hl_ , temp8_ ) , { MicroOp : : IND } ) ,
/* 0xab OTD */ Instr ( 6 , Read3 ( hl_ , temp8_ ) , { MicroOp : : OUTD } , Output ( bc_ , temp8_ ) ) ,
2017-05-29 16:25:10 +00:00
NOP , NOP , NOP , NOP ,
2017-07-26 23:42:00 +00:00
/* 0xb0 LDIR */ StdInstr ( Read3 ( hl_ , temp8_ ) , Write5 ( de_ , temp8_ ) , { MicroOp : : LDIR } , InternalOperation ( 10 ) ) ,
/* 0xb1 CPIR */ StdInstr ( Read3 ( hl_ , temp8_ ) , InternalOperation ( 10 ) , { MicroOp : : CPIR } , InternalOperation ( 10 ) ) ,
/* 0xb2 INIR */ Instr ( 6 , Input ( bc_ , temp8_ ) , Write3 ( hl_ , temp8_ ) , { MicroOp : : INIR } , InternalOperation ( 10 ) ) ,
/* 0xb3 OTIR */ Instr ( 6 , Read3 ( hl_ , temp8_ ) , { MicroOp : : OUTI } , Output ( bc_ , temp8_ ) , { MicroOp : : OUT_R } , InternalOperation ( 10 ) ) ,
2017-05-29 16:25:10 +00:00
NOP , NOP , NOP , NOP ,
2017-07-26 23:42:00 +00:00
/* 0xb8 LDDR */ StdInstr ( Read3 ( hl_ , temp8_ ) , Write5 ( de_ , temp8_ ) , { MicroOp : : LDDR } , InternalOperation ( 10 ) ) ,
/* 0xb9 CPDR */ StdInstr ( Read3 ( hl_ , temp8_ ) , InternalOperation ( 10 ) , { MicroOp : : CPDR } , InternalOperation ( 10 ) ) ,
/* 0xba INDR */ Instr ( 6 , Input ( bc_ , temp8_ ) , Write3 ( hl_ , temp8_ ) , { MicroOp : : INDR } , InternalOperation ( 10 ) ) ,
/* 0xbb OTDR */ Instr ( 6 , Read3 ( hl_ , temp8_ ) , { MicroOp : : OUTD } , Output ( bc_ , temp8_ ) , { MicroOp : : OUT_R } , InternalOperation ( 10 ) ) ,
2017-05-29 16:25:10 +00:00
NOP , NOP , NOP , NOP ,
2017-05-20 21:29:30 +00:00
NOP_ROW ( ) , /* 0xc0 */
NOP_ROW ( ) , /* 0xd0 */
NOP_ROW ( ) , /* 0xe0 */
NOP_ROW ( ) , /* 0xf0 */
} ;
2017-05-27 03:23:33 +00:00
assemble_page ( target , ed_program_table , false ) ;
2017-05-20 21:29:30 +00:00
# undef NOP_ROW
}
2017-05-27 19:41:26 +00:00
void assemble_cb_page ( InstructionPage & target , RegisterPair & index , bool add_offsets ) {
2017-05-28 01:06:56 +00:00
# define OCTO_OP_GROUP(m, x) m(x), m(x), m(x), m(x), m(x), m(x), m(x), m(x)
2017-05-28 03:54:06 +00:00
# define CB_PAGE(m, p) m(RLC), m(RRC), m(RL), m(RR), m(SLA), m(SRA), m(SLL), m(SRL), OCTO_OP_GROUP(p, BIT), OCTO_OP_GROUP(m, RES), OCTO_OP_GROUP(m, SET)
2017-05-28 01:06:56 +00:00
2017-05-27 19:39:22 +00:00
InstructionTable cb_program_table = {
/* 0x00 RLC B; 0x01 RLC C; 0x02 RLC D; 0x03 RLC E; 0x04 RLC H; 0x05 RLC L; 0x06 RLC (HL); 0x07 RLC A */
/* 0x08 RRC B; 0x09 RRC C; 0x0a RRC D; 0x0b RRC E; 0x0c RRC H; 0x0d RRC L; 0x0e RRC (HL); 0x0f RRC A */
/* 0x10 RL B; 0x11 RL C; 0x12 RL D; 0x13 RL E; 0x14 RL H; 0x15 RL L; 0x16 RL (HL); 0x17 RL A */
/* 0x18 RR B; 0x99 RR C; 0x1a RR D; 0x1b RR E; 0x1c RR H; 0x1d RR L; 0x1e RR (HL); 0x1f RR A */
/* 0x20 SLA B; 0x21 SLA C; 0x22 SLA D; 0x23 SLA E; 0x24 SLA H; 0x25 SLA L; 0x26 SLA (HL); 0x27 SLA A */
/* 0x28 SRA B; 0x29 SRA C; 0x2a SRA D; 0x2b SRA E; 0x2c SRA H; 0x2d SRA L; 0x2e SRA (HL); 0x2f SRA A */
/* 0x30 SLL B; 0x31 SLL C; 0x32 SLL D; 0x33 SLL E; 0x34 SLL H; 0x35 SLL L; 0x36 SLL (HL); 0x37 SLL A */
/* 0x38 SRL B; 0x39 SRL C; 0x3a SRL D; 0x3b SRL E; 0x3c SRL H; 0x3d SRL L; 0x3e SRL (HL); 0x3f SRL A */
/* 0x40 – 0x7f: BIT */
/* 0x80 – 0xcf: RES */
/* 0xd0 – 0xdf: SET */
2017-06-13 02:22:00 +00:00
CB_PAGE ( MODIFY_OP_GROUP , READ_OP_GROUP_D )
2017-05-28 01:06:56 +00:00
} ;
InstructionTable offsets_cb_program_table = {
2017-05-28 03:54:06 +00:00
CB_PAGE ( IX_MODIFY_OP_GROUP , IX_READ_OP_GROUP )
2017-05-27 19:39:22 +00:00
} ;
2017-05-28 01:06:56 +00:00
assemble_page ( target , add_offsets ? offsets_cb_program_table : cb_program_table , add_offsets ) ;
# undef OCTO_OP_GROUP
# undef CB_PAGE
2017-05-27 19:39:22 +00:00
}
void assemble_base_page ( InstructionPage & target , RegisterPair & index , bool add_offsets , InstructionPage & cb_page ) {
2017-05-21 13:17:30 +00:00
# define INC_DEC_LD(r) \
2017-06-18 21:18:01 +00:00
StdInstr ( { MicroOp : : Increment8 , & r } ) , \
StdInstr ( { MicroOp : : Decrement8 , & r } ) , \
StdInstr ( ReadInc ( pc_ , r ) )
2017-05-21 13:17:30 +00:00
# define INC_INC_DEC_LD(rf, r) \
2017-07-26 23:42:00 +00:00
Instr ( 8 , { MicroOp : : Increment16 , & rf . full } ) , INC_DEC_LD ( r )
2017-05-21 13:17:30 +00:00
# define DEC_INC_DEC_LD(rf, r) \
2017-07-26 23:42:00 +00:00
Instr ( 8 , { MicroOp : : Decrement16 , & rf . full } ) , INC_DEC_LD ( r )
2017-05-21 13:17:30 +00:00
2017-05-21 23:15:52 +00:00
InstructionTable base_program_table = {
2017-06-19 23:47:00 +00:00
/* 0x00 NOP */ NOP , /* 0x01 LD BC, nn */ StdInstr ( Read16Inc ( pc_ , bc_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0x02 LD (BC), A */ StdInstr ( { MicroOp : : Move16 , & bc_ . full , & memptr_ . full } , Write3 ( memptr_ , a_ ) ) ,
2017-05-21 13:17:30 +00:00
/* 0x03 INC BC; 0x04 INC B; 0x05 DEC B; 0x06 LD B, n */
INC_INC_DEC_LD ( bc_ , bc_ . bytes . high ) ,
2017-06-19 00:31:12 +00:00
/* 0x07 RLCA */ StdInstr ( { MicroOp : : RLCA } ) ,
/* 0x08 EX AF, AF' */ StdInstr ( { MicroOp : : ExAFAFDash } ) , /* 0x09 ADD HL, BC */ ADD16 ( index , bc_ ) ,
2017-07-22 15:20:21 +00:00
/* 0x0a LD A, (BC) */ StdInstr ( { MicroOp : : Move16 , & bc_ . full , & memptr_ . full } , Read3 ( memptr_ , a_ ) , Inc16 ( memptr_ ) ) ,
2017-05-21 13:17:30 +00:00
/* 0x0b DEC BC; 0x0c INC C; 0x0d DEC C; 0x0e LD C, n */
DEC_INC_DEC_LD ( bc_ , bc_ . bytes . low ) ,
2017-06-19 00:31:12 +00:00
/* 0x0f RRCA */ StdInstr ( { MicroOp : : RRCA } ) ,
2017-07-26 23:42:00 +00:00
/* 0x10 DJNZ */ Instr ( 6 , ReadInc ( pc_ , temp8_ ) , { MicroOp : : DJNZ } , InternalOperation ( 10 ) , { MicroOp : : CalculateIndexAddress , & pc_ . full } , { MicroOp : : Move16 , & memptr_ . full , & pc_ . full } ) ,
2017-06-19 00:31:12 +00:00
/* 0x11 LD DE, nn */ StdInstr ( Read16Inc ( pc_ , de_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0x12 LD (DE), A */ StdInstr ( { MicroOp : : Move16 , & de_ . full , & memptr_ . full } , Write3 ( memptr_ , a_ ) ) ,
2017-05-21 13:17:30 +00:00
/* 0x13 INC DE; 0x14 INC D; 0x15 DEC D; 0x16 LD D, n */
INC_INC_DEC_LD ( de_ , de_ . bytes . high ) ,
2017-06-19 00:31:12 +00:00
/* 0x17 RLA */ StdInstr ( { MicroOp : : RLA } ) ,
2017-07-26 23:42:00 +00:00
/* 0x18 JR */ StdInstr ( ReadInc ( pc_ , temp8_ ) , InternalOperation ( 10 ) , { MicroOp : : CalculateIndexAddress , & pc_ . full } , { MicroOp : : Move16 , & memptr_ . full , & pc_ . full } ) ,
2017-05-26 01:48:28 +00:00
/* 0x19 ADD HL, DE */ ADD16 ( index , de_ ) ,
2017-07-22 15:20:21 +00:00
/* 0x1a LD A, (DE) */ StdInstr ( { MicroOp : : Move16 , & de_ . full , & memptr_ . full } , Read3 ( memptr_ , a_ ) , Inc16 ( memptr_ ) ) ,
2017-05-21 13:17:30 +00:00
/* 0x1b DEC DE; 0x1c INC E; 0x1d DEC E; 0x1e LD E, n */
DEC_INC_DEC_LD ( de_ , de_ . bytes . low ) ,
2017-06-19 00:31:12 +00:00
/* 0x1f RRA */ StdInstr ( { MicroOp : : RRA } ) ,
/* 0x20 JR NZ */ JR ( TestNZ ) , /* 0x21 LD HL, nn */ StdInstr ( Read16Inc ( pc_ , index ) ) ,
2017-07-22 15:39:13 +00:00
/* 0x22 LD (nn), HL */ StdInstr ( Read16Inc ( pc_ , memptr_ ) , Write16 ( memptr_ , index ) ) ,
2017-05-21 13:17:30 +00:00
/* 0x23 INC HL; 0x24 INC H; 0x25 DEC H; 0x26 LD H, n */
2017-05-21 23:26:40 +00:00
INC_INC_DEC_LD ( index , index . bytes . high ) ,
2017-05-21 13:17:30 +00:00
2017-06-19 00:31:12 +00:00
/* 0x27 DAA */ StdInstr ( { MicroOp : : DAA } ) ,
2017-05-26 01:51:30 +00:00
/* 0x28 JR Z */ JR ( TestZ ) , /* 0x29 ADD HL, HL */ ADD16 ( index , index ) ,
2017-06-19 00:31:12 +00:00
/* 0x2a LD HL, (nn) */ StdInstr ( Read16Inc ( pc_ , temp16_ ) , Read16 ( temp16_ , index ) ) ,
2017-05-21 13:17:30 +00:00
/* 0x2b DEC HL; 0x2c INC L; 0x2d DEC L; 0x2e LD L, n */
2017-05-21 23:26:40 +00:00
DEC_INC_DEC_LD ( index , index . bytes . low ) ,
2017-05-21 13:17:30 +00:00
2017-06-19 00:31:12 +00:00
/* 0x2f CPL */ StdInstr ( { MicroOp : : CPL } ) ,
/* 0x30 JR NC */ JR ( TestNC ) , /* 0x31 LD SP, nn */ StdInstr ( Read16Inc ( pc_ , sp_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0x32 LD (nn), A */ StdInstr ( Read16Inc ( pc_ , temp16_ ) , Write3 ( temp16_ , a_ ) ) ,
2017-07-26 23:42:00 +00:00
/* 0x33 INC SP */ Instr ( 8 , { MicroOp : : Increment16 , & sp_ . full } ) ,
2017-06-19 00:31:12 +00:00
/* 0x34 INC (HL) */ StdInstr ( INDEX ( ) , Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : Increment8 , & temp8_ } , Write3 ( INDEX_ADDR ( ) , temp8_ ) ) ,
/* 0x35 DEC (HL) */ StdInstr ( INDEX ( ) , Read4 ( INDEX_ADDR ( ) , temp8_ ) , { MicroOp : : Decrement8 , & temp8_ } , Write3 ( INDEX_ADDR ( ) , temp8_ ) ) ,
2017-06-21 02:20:58 +00:00
/* 0x36 LD (HL), n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , Write3 ( INDEX_ADDR ( ) , temp8_ ) ) ,
2017-06-19 00:31:12 +00:00
/* 0x37 SCF */ StdInstr ( { MicroOp : : SCF } ) ,
2017-05-26 01:51:30 +00:00
/* 0x38 JR C */ JR ( TestC ) ,
2017-05-21 23:26:40 +00:00
/* 0x39 ADD HL, SP */ ADD16 ( index , sp_ ) ,
2017-07-22 03:01:35 +00:00
/* 0x3a LD A, (nn) */ StdInstr ( Read16Inc ( pc_ , memptr_ ) , Read3 ( memptr_ , a_ ) , Inc16 ( memptr_ ) ) ,
2017-07-26 23:42:00 +00:00
/* 0x3b DEC SP */ Instr ( 8 , { MicroOp : : Decrement16 , & sp_ . full } ) ,
2017-05-21 13:17:30 +00:00
/* 0x3c INC A; 0x3d DEC A; 0x3e LD A, n */
INC_DEC_LD ( a_ ) ,
2017-06-19 00:31:12 +00:00
/* 0x3f CCF */ StdInstr ( { MicroOp : : CCF } ) ,
2017-05-21 13:17:30 +00:00
/* 0x40 LD B, B; 0x41 LD B, C; 0x42 LD B, D; 0x43 LD B, E; 0x44 LD B, H; 0x45 LD B, L; 0x46 LD B, (HL); 0x47 LD B, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( bc_ . bytes . high , bc_ . bytes . high ) ,
2017-05-21 13:17:30 +00:00
/* 0x48 LD C, B; 0x49 LD C, C; 0x4a LD C, D; 0x4b LD C, E; 0x4c LD C, H; 0x4d LD C, L; 0x4e LD C, (HL); 0x4f LD C, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( bc_ . bytes . low , bc_ . bytes . low ) ,
2017-05-21 13:17:30 +00:00
/* 0x50 LD D, B; 0x51 LD D, C; 0x52 LD D, D; 0x53 LD D, E; 0x54 LD D, H; 0x55 LD D, L; 0x56 LD D, (HL); 0x57 LD D, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( de_ . bytes . high , de_ . bytes . high ) ,
2017-05-21 13:17:30 +00:00
/* 0x58 LD E, B; 0x59 LD E, C; 0x5a LD E, D; 0x5b LD E, E; 0x5c LD E, H; 0x5d LD E, L; 0x5e LD E, (HL); 0x5f LD E, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( de_ . bytes . low , de_ . bytes . low ) ,
2017-05-21 13:17:30 +00:00
/* 0x60 LD H, B; 0x61 LD H, C; 0x62 LD H, D; 0x63 LD H, E; 0x64 LD H, H; 0x65 LD H, L; 0x66 LD H, (HL); 0x67 LD H, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( index . bytes . high , hl_ . bytes . high ) ,
2017-05-21 13:17:30 +00:00
/* 0x68 LD L, B; 0x69 LD L, C; 0x6a LD L, D; 0x6b LD L, E; 0x6c LD L, H; 0x6d LD H, L; 0x6e LD L, (HL); 0x6f LD L, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( index . bytes . low , hl_ . bytes . low ) ,
2017-05-21 13:17:30 +00:00
2017-06-19 01:00:44 +00:00
/* 0x70 LD (HL), B */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , bc_ . bytes . high ) ) ,
/* 0x71 LD (HL), C */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , bc_ . bytes . low ) ) ,
/* 0x72 LD (HL), D */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , de_ . bytes . high ) ) ,
/* 0x73 LD (HL), E */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , de_ . bytes . low ) ) ,
/* 0x74 LD (HL), H */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , hl_ . bytes . high ) ) , // neither of these stores parts of the index register;
/* 0x75 LD (HL), L */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , hl_ . bytes . low ) ) , // they always store exactly H and L.
2017-06-19 00:31:12 +00:00
/* 0x76 HALT */ StdInstr ( { MicroOp : : HALT } ) ,
2017-06-19 01:00:44 +00:00
/* 0x77 LD (HL), A */ StdInstr ( INDEX ( ) , Write3 ( INDEX_ADDR ( ) , a_ ) ) ,
2017-05-21 13:43:17 +00:00
/* 0x78 LD A, B; 0x79 LD A, C; 0x7a LD A, D; 0x7b LD A, E; 0x7c LD A, H; 0x7d LD A, L; 0x7e LD A, (HL); 0x7f LD A, A */
2017-05-27 03:37:17 +00:00
LD_GROUP ( a_ , a_ ) ,
2017-05-21 13:43:17 +00:00
/* 0x80 ADD B; 0x81 ADD C; 0x82 ADD D; 0x83 ADD E; 0x84 ADD H; 0x85 ADD L; 0x86 ADD (HL); 0x87 ADD A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( ADD8 ) ,
2017-05-21 13:43:17 +00:00
/* 0x88 ADC B; 0x89 ADC C; 0x8a ADC D; 0x8b ADC E; 0x8c ADC H; 0x8d ADC L; 0x8e ADC (HL); 0x8f ADC A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( ADC8 ) ,
2017-05-21 13:43:17 +00:00
/* 0x90 SUB B; 0x91 SUB C; 0x92 SUB D; 0x93 SUB E; 0x94 SUB H; 0x95 SUB L; 0x96 SUB (HL); 0x97 SUB A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( SUB8 ) ,
2017-05-21 13:43:17 +00:00
/* 0x98 SBC B; 0x99 SBC C; 0x9a SBC D; 0x9b SBC E; 0x9c SBC H; 0x9d SBC L; 0x9e SBC (HL); 0x9f SBC A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( SBC8 ) ,
2017-05-21 13:43:17 +00:00
/* 0xa0 AND B; 0xa1 AND C; 0xa2 AND D; 0xa3 AND E; 0xa4 AND H; 0xa5 AND L; 0xa6 AND (HL); 0xa7 AND A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( And ) ,
2017-05-21 13:43:17 +00:00
/* 0xa8 XOR B; 0xa9 XOR C; 0xaa XOR D; 0xab XOR E; 0xac XOR H; 0xad XOR L; 0xae XOR (HL); 0xaf XOR A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( Xor ) ,
2017-05-21 13:43:17 +00:00
/* 0xb0 OR B; 0xb1 OR C; 0xb2 OR D; 0xb3 OR E; 0xb4 OR H; 0xb5 OR L; 0xb6 OR (HL); 0xb7 OR A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( Or ) ,
2017-05-21 13:43:17 +00:00
/* 0xb8 CP B; 0xb9 CP C; 0xba CP D; 0xbb CP E; 0xbc CP H; 0xbd CP L; 0xbe CP (HL); 0xbf CP A */
2017-05-27 19:54:24 +00:00
READ_OP_GROUP ( CP8 ) ,
2017-05-21 13:43:17 +00:00
2017-06-19 00:34:46 +00:00
/* 0xc0 RET NZ */ RET ( TestNZ ) , /* 0xc1 POP BC */ StdInstr ( Pop ( bc_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0xc2 JP NZ */ JP ( TestNZ ) , /* 0xc3 JP nn */ StdInstr ( Read16 ( pc_ , temp16_ ) , { MicroOp : : Move16 , & temp16_ . full , & pc_ . full } ) ,
2017-07-26 23:42:00 +00:00
/* 0xc4 CALL NZ */ CALL ( TestNZ ) , /* 0xc5 PUSH BC */ Instr ( 6 , Push ( bc_ ) ) ,
2017-06-19 00:34:46 +00:00
/* 0xc6 ADD A, n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : ADD8 , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xc7 RST 00h */ RST ( ) ,
2017-06-19 00:34:46 +00:00
/* 0xc8 RET Z */ RET ( TestZ ) , /* 0xc9 RET */ StdInstr ( Pop ( pc_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0xca JP Z */ JP ( TestZ ) , /* 0xcb [CB page] */ StdInstr ( FINDEX ( ) , { MicroOp : : SetInstructionPage , & cb_page } ) ,
2017-06-20 02:20:23 +00:00
/* 0xcc CALL Z */ CALL ( TestZ ) , /* 0xcd CALL */ StdInstr ( ReadInc ( pc_ , temp16_ . bytes . low ) , Read4Inc ( pc_ , temp16_ . bytes . high ) , Push ( pc_ ) , { MicroOp : : Move16 , & temp16_ . full , & pc_ . full } ) ,
2017-06-19 00:34:46 +00:00
/* 0xce ADC A, n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : ADC8 , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xcf RST 08h */ RST ( ) ,
2017-06-19 00:34:46 +00:00
/* 0xd0 RET NC */ RET ( TestNC ) , /* 0xd1 POP DE */ StdInstr ( Pop ( de_ ) ) ,
2017-06-19 01:00:44 +00:00
/* 0xd2 JP NC */ JP ( TestNC ) , /* 0xd3 OUT (n), A */ StdInstr ( ReadInc ( pc_ , temp16_ . bytes . low ) , { MicroOp : : Move8 , & a_ , & temp16_ . bytes . high } , Output ( temp16_ , a_ ) ) ,
2017-07-26 23:42:00 +00:00
/* 0xd4 CALL NC */ CALL ( TestNC ) , /* 0xd5 PUSH DE */ Instr ( 6 , Push ( de_ ) ) ,
2017-06-19 00:34:46 +00:00
/* 0xd6 SUB n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : SUB8 , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xd7 RST 10h */ RST ( ) ,
2017-06-19 00:34:46 +00:00
/* 0xd8 RET C */ RET ( TestC ) , /* 0xd9 EXX */ StdInstr ( { MicroOp : : EXX } ) ,
2017-06-19 01:00:44 +00:00
/* 0xda JP C */ JP ( TestC ) , /* 0xdb IN A, (n) */ StdInstr ( ReadInc ( pc_ , temp16_ . bytes . low ) , { MicroOp : : Move8 , & a_ , & temp16_ . bytes . high } , Input ( temp16_ , a_ ) ) ,
2017-06-19 00:34:46 +00:00
/* 0xdc CALL C */ CALL ( TestC ) , /* 0xdd [DD page] */ StdInstr ( { MicroOp : : SetInstructionPage , & dd_page_ } ) ,
/* 0xde SBC A, n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : SBC8 , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xdf RST 18h */ RST ( ) ,
2017-06-19 00:34:46 +00:00
/* 0xe0 RET PO */ RET ( TestPO ) , /* 0xe1 POP HL */ StdInstr ( Pop ( index ) ) ,
2017-06-19 23:25:53 +00:00
/* 0xe2 JP PO */ JP ( TestPO ) , /* 0xe3 EX (SP), HL */ StdInstr ( Pop7 ( memptr_ ) , Push8 ( index ) , { MicroOp : : Move16 , & memptr_ . full , & index . full } ) ,
2017-07-26 23:42:00 +00:00
/* 0xe4 CALL PO */ CALL ( TestPO ) , /* 0xe5 PUSH HL */ Instr ( 6 , Push ( index ) ) ,
2017-06-19 00:34:46 +00:00
/* 0xe6 AND n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : And , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xe7 RST 20h */ RST ( ) ,
2017-06-19 00:34:46 +00:00
/* 0xe8 RET PE */ RET ( TestPE ) , /* 0xe9 JP (HL) */ StdInstr ( { MicroOp : : Move16 , & index . full , & pc_ . full } ) ,
/* 0xea JP PE */ JP ( TestPE ) , /* 0xeb EX DE, HL */ StdInstr ( { MicroOp : : ExDEHL } ) ,
/* 0xec CALL PE */ CALL ( TestPE ) , /* 0xed [ED page] */ StdInstr ( { MicroOp : : SetInstructionPage , & ed_page_ } ) ,
/* 0xee XOR n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : Xor , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xef RST 28h */ RST ( ) ,
2017-06-19 00:34:46 +00:00
/* 0xf0 RET p */ RET ( TestP ) , /* 0xf1 POP AF */ StdInstr ( Pop ( temp16_ ) , { MicroOp : : DisassembleAF } ) ,
/* 0xf2 JP P */ JP ( TestP ) , /* 0xf3 DI */ StdInstr ( { MicroOp : : DI } ) ,
2017-07-26 23:42:00 +00:00
/* 0xf4 CALL P */ CALL ( TestP ) , /* 0xf5 PUSH AF */ Instr ( 6 , { MicroOp : : AssembleAF } , Push ( temp16_ ) ) ,
2017-06-19 00:34:46 +00:00
/* 0xf6 OR n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : Or , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xf7 RST 30h */ RST ( ) ,
2017-07-26 23:42:00 +00:00
/* 0xf8 RET M */ RET ( TestM ) , /* 0xf9 LD SP, HL */ Instr ( 8 , { MicroOp : : Move16 , & index . full , & sp_ . full } ) ,
2017-06-19 00:34:46 +00:00
/* 0xfa JP M */ JP ( TestM ) , /* 0xfb EI */ StdInstr ( { MicroOp : : EI } ) ,
/* 0xfc CALL M */ CALL ( TestM ) , /* 0xfd [FD page] */ StdInstr ( { MicroOp : : SetInstructionPage , & fd_page_ } ) ,
/* 0xfe CP n */ StdInstr ( ReadInc ( pc_ , temp8_ ) , { MicroOp : : CP8 , & temp8_ } ) ,
2017-05-26 11:29:19 +00:00
/* 0xff RST 38h */ RST ( ) ,
2017-05-18 01:45:23 +00:00
} ;
2017-06-21 02:20:58 +00:00
if ( add_offsets ) {
// The indexed version of 0x36 differs substantially from the non-indexed by building index calculation into
// the cycle that fetches the final operand. So patch in a different microprogram if building an indexed table.
InstructionTable copy_table = {
StdInstr ( FINDEX ( ) , Read5Inc ( pc_ , temp8_ ) , Write3 ( INDEX_ADDR ( ) , temp8_ ) )
} ;
memcpy ( & base_program_table [ 0x36 ] , & copy_table [ 0 ] , sizeof ( copy_table [ 0 ] ) ) ;
}
2017-05-27 19:39:22 +00:00
assemble_cb_page ( cb_page , index , add_offsets ) ;
2017-05-27 03:23:33 +00:00
assemble_page ( target , base_program_table , add_offsets ) ;
2017-05-20 21:04:25 +00:00
}
2017-05-28 03:54:06 +00:00
void assemble_fetch_decode_execute ( InstructionPage & target , int length ) {
2017-06-19 01:00:44 +00:00
const MicroOp normal_fetch_decode_execute [ ] = {
2017-06-21 22:47:00 +00:00
BusOp ( ReadOpcodeStart ( ) ) ,
2017-07-26 23:42:00 +00:00
BusOp ( ReadOpcodeWait ( true ) ) ,
2017-07-28 00:17:13 +00:00
BusOp ( ReadOpcodeEnd ( ) ) ,
2017-06-03 16:19:25 +00:00
{ MicroOp : : DecodeOperation }
2017-05-23 01:50:34 +00:00
} ;
2017-06-19 01:00:44 +00:00
const MicroOp short_fetch_decode_execute [ ] = {
2017-06-21 22:47:00 +00:00
BusOp ( ReadOpcodeStart ( ) ) ,
2017-07-26 23:42:00 +00:00
BusOp ( ReadOpcodeWait ( false ) ) ,
BusOp ( ReadOpcodeWait ( true ) ) ,
2017-07-28 00:17:13 +00:00
BusOp ( ReadOpcodeEnd ( ) ) ,
2017-06-19 01:00:44 +00:00
{ MicroOp : : DecodeOperation }
} ;
copy_program ( ( length = = 4 ) ? normal_fetch_decode_execute : short_fetch_decode_execute , target . fetch_decode_execute ) ;
2017-06-01 22:28:04 +00:00
target . fetch_decode_execute_data = target . fetch_decode_execute . data ( ) ;
2017-05-23 01:50:34 +00:00
}
2017-06-03 16:16:21 +00:00
void copy_program ( const MicroOp * source , std : : vector < MicroOp > & destination ) {
2017-06-03 15:30:12 +00:00
size_t length = 0 ;
2017-06-03 16:19:25 +00:00
while ( ! isTerminal ( source [ length ] . type ) ) length + + ;
2017-06-03 15:30:12 +00:00
size_t pointer = 0 ;
while ( true ) {
2017-08-27 03:08:57 +00:00
// TODO: This test is duplicated from assemble_page; can a better factoring be found?
// Skip optional waits if this instance doesn't use the wait line.
if ( source [ pointer ] . machine_cycle . was_requested & & ! uses_wait_line ) {
pointer + + ;
continue ;
}
2017-08-25 01:32:33 +00:00
destination . emplace_back ( source [ pointer ] ) ;
2017-06-03 16:19:25 +00:00
if ( isTerminal ( source [ pointer ] . type ) ) break ;
2017-06-03 15:30:12 +00:00
pointer + + ;
}
}
2017-05-15 11:59:21 +00:00
public :
2017-08-03 02:09:59 +00:00
Processor ( T & bus_handler ) :
2017-05-31 02:15:43 +00:00
halt_mask_ ( 0xff ) ,
2017-06-21 22:47:00 +00:00
interrupt_mode_ ( 0 ) ,
2017-06-22 00:32:08 +00:00
wait_line_ ( false ) ,
2017-06-02 01:40:08 +00:00
request_status_ ( Interrupt : : PowerOn ) ,
2017-06-02 02:31:04 +00:00
last_request_status_ ( Interrupt : : PowerOn ) ,
2017-06-03 21:07:05 +00:00
irq_line_ ( false ) ,
2017-06-23 00:11:19 +00:00
nmi_line_ ( false ) ,
2017-06-03 23:09:47 +00:00
bus_request_line_ ( false ) ,
2017-06-03 23:17:34 +00:00
pc_increment_ ( 1 ) ,
2017-08-03 02:09:59 +00:00
scheduled_program_counter_ ( nullptr ) ,
bus_handler_ ( bus_handler ) {
2017-05-29 23:25:08 +00:00
set_flags ( 0xff ) ;
2017-06-21 00:57:23 +00:00
MicroOp conditional_call_untaken_program [ ] = Sequence ( ReadInc ( pc_ , temp16_ . bytes . high ) ) ;
copy_program ( conditional_call_untaken_program , conditional_call_untaken_program_ ) ;
2017-05-27 19:39:22 +00:00
assemble_base_page ( base_page_ , hl_ , false , cb_page_ ) ;
assemble_base_page ( dd_page_ , ix_ , true , ddcb_page_ ) ;
assemble_base_page ( fd_page_ , iy_ , true , fdcb_page_ ) ;
2017-05-20 21:29:30 +00:00
assemble_ed_page ( ed_page_ ) ;
2017-05-28 01:06:56 +00:00
2017-06-04 18:13:38 +00:00
fdcb_page_ . r_step = 0 ;
fd_page_ . is_indexed = true ;
fdcb_page_ . is_indexed = true ;
ddcb_page_ . r_step = 0 ;
dd_page_ . is_indexed = true ;
ddcb_page_ . is_indexed = true ;
2017-05-28 03:54:06 +00:00
assemble_fetch_decode_execute ( base_page_ , 4 ) ;
assemble_fetch_decode_execute ( dd_page_ , 4 ) ;
assemble_fetch_decode_execute ( fd_page_ , 4 ) ;
assemble_fetch_decode_execute ( ed_page_ , 4 ) ;
assemble_fetch_decode_execute ( cb_page_ , 4 ) ;
assemble_fetch_decode_execute ( fdcb_page_ , 3 ) ;
assemble_fetch_decode_execute ( ddcb_page_ , 3 ) ;
2017-06-02 02:31:04 +00:00
2017-07-26 23:42:00 +00:00
MicroOp reset_program [ ] = Sequence ( InternalOperation ( 6 ) , { MicroOp : : Reset } ) ;
2017-06-23 01:09:26 +00:00
// Justification for NMI timing: per Wilf Rigter on the ZX81 (http://www.user.dccnet.com/wrigter/index_files/ZX81WAIT.htm),
// wait cycles occur between T2 and T3 during NMI; extending the refresh cycle is also consistent with my guess
// for the action of other non-four-cycle opcode fetches
2017-06-03 15:30:12 +00:00
MicroOp nmi_program [ ] = {
{ MicroOp : : BeginNMI } ,
2017-06-21 22:47:00 +00:00
BusOp ( ReadOpcodeStart ( ) ) ,
2017-07-26 23:42:00 +00:00
BusOp ( ReadOpcodeWait ( true ) ) ,
2017-07-28 00:17:13 +00:00
BusOp ( ReadOpcodeEnd ( ) ) ,
2017-07-26 23:42:00 +00:00
BusOp ( Refresh ( 6 ) ) ,
2017-06-19 01:00:44 +00:00
Push ( pc_ ) ,
2017-06-03 21:41:45 +00:00
{ MicroOp : : JumpTo66 , nullptr , nullptr } ,
2017-06-03 15:30:12 +00:00
{ MicroOp : : MoveToNextProgram }
} ;
2017-06-03 21:07:05 +00:00
MicroOp irq_mode0_program [ ] = {
{ MicroOp : : BeginIRQMode0 } ,
2017-07-28 00:17:13 +00:00
BusOp ( IntAckStart ( 5 , operation_ ) ) ,
2017-06-23 00:07:47 +00:00
BusOp ( IntWait ( operation_ ) ) ,
2017-07-28 00:17:13 +00:00
BusOp ( IntAckEnd ( operation_ ) ) ,
2017-06-03 21:07:05 +00:00
{ MicroOp : : DecodeOperationNoRChange }
} ;
MicroOp irq_mode1_program [ ] = {
{ MicroOp : : BeginIRQ } ,
2017-08-24 02:25:31 +00:00
BusOp ( IntAckStart ( 7 , operation_ ) ) , // 7 half cycles (including +
BusOp ( IntWait ( operation_ ) ) , // [potentially 2 half cycles] +
BusOp ( IntAckEnd ( operation_ ) ) , // Implicitly 3 half cycles +
BusOp ( Refresh ( 4 ) ) , // 4 half cycles +
Push ( pc_ ) , // 12 half cycles = 26 half cycles = 13 cycles
2017-06-03 21:07:05 +00:00
{ MicroOp : : Move16 , & temp16_ . full , & pc_ . full } ,
{ MicroOp : : MoveToNextProgram }
} ;
MicroOp irq_mode2_program [ ] = {
{ MicroOp : : BeginIRQ } ,
2017-07-28 00:17:13 +00:00
BusOp ( IntAckStart ( 7 , temp16_ . bytes . low ) ) ,
2017-06-23 00:07:47 +00:00
BusOp ( IntWait ( temp16_ . bytes . low ) ) ,
2017-07-28 00:17:13 +00:00
BusOp ( IntAckEnd ( temp16_ . bytes . low ) ) ,
2017-07-26 23:42:00 +00:00
BusOp ( Refresh ( 4 ) ) ,
2017-06-19 01:00:44 +00:00
Push ( pc_ ) ,
2017-06-17 22:18:28 +00:00
{ MicroOp : : Move8 , & ir_ . bytes . high , & temp16_ . bytes . high } ,
2017-06-21 23:08:48 +00:00
Read16 ( temp16_ , pc_ ) ,
2017-06-03 21:07:05 +00:00
{ MicroOp : : MoveToNextProgram }
} ;
2017-06-03 15:30:12 +00:00
copy_program ( reset_program , reset_program_ ) ;
copy_program ( nmi_program , nmi_program_ ) ;
2017-06-03 21:07:05 +00:00
copy_program ( irq_mode0_program , irq_program_ [ 0 ] ) ;
copy_program ( irq_mode1_program , irq_program_ [ 1 ] ) ;
copy_program ( irq_mode2_program , irq_program_ [ 2 ] ) ;
2017-05-18 01:45:23 +00:00
}
2017-05-15 11:59:21 +00:00
/*!
Runs the Z80 for a supplied number of cycles .
2017-06-22 00:38:08 +00:00
@ discussion Subclasses must implement @ c perform_machine_cycle ( const PartialMachineCycle & cycle ) .
2017-05-15 11:59:21 +00:00
If it is a read operation then @ c value will be seeded with the value 0xff .
2017-07-24 02:15:04 +00:00
@ param cycles The number of cycles to run for .
2017-05-15 11:59:21 +00:00
*/
2017-07-28 02:05:29 +00:00
void run_for ( const HalfCycles cycles ) {
2017-05-16 02:25:52 +00:00
2017-06-01 00:15:56 +00:00
# define advance_operation() \
2017-06-03 21:07:05 +00:00
pc_increment_ = 1 ; \
2017-06-02 02:16:22 +00:00
if ( last_request_status_ ) { \
halt_mask_ = 0xff ; \
if ( last_request_status_ & ( Interrupt : : PowerOn | Interrupt : : Reset ) ) { \
request_status_ & = ~ Interrupt : : PowerOn ; \
scheduled_program_counter_ = reset_program_ . data ( ) ; \
} else if ( last_request_status_ & Interrupt : : NMI ) { \
request_status_ & = ~ Interrupt : : NMI ; \
scheduled_program_counter_ = nmi_program_ . data ( ) ; \
} else if ( last_request_status_ & Interrupt : : IRQ ) { \
scheduled_program_counter_ = irq_program_ [ interrupt_mode_ ] . data ( ) ; \
} \
} else { \
current_instruction_page_ = & base_page_ ; \
scheduled_program_counter_ = base_page_ . fetch_decode_execute_data ; \
}
2017-05-18 01:45:23 +00:00
2017-07-25 00:10:05 +00:00
number_of_cycles_ + = cycles ;
2017-06-01 00:15:56 +00:00
if ( ! scheduled_program_counter_ ) {
advance_operation ( ) ;
}
2017-05-18 01:45:23 +00:00
2017-05-16 02:25:52 +00:00
while ( 1 ) {
2017-06-03 23:09:47 +00:00
2017-08-20 16:39:45 +00:00
do_bus_acknowledge :
2017-08-22 00:43:12 +00:00
while ( uses_bus_request & & bus_request_line_ ) {
2017-07-26 23:42:00 +00:00
static PartialMachineCycle bus_acknowledge_cycle = { PartialMachineCycle : : BusAcknowledge , HalfCycles ( 2 ) , nullptr , nullptr , false } ;
2017-08-03 02:09:59 +00:00
number_of_cycles_ - = bus_handler_ . perform_machine_cycle ( bus_acknowledge_cycle ) + HalfCycles ( 1 ) ;
2017-06-04 22:08:35 +00:00
if ( ! number_of_cycles_ ) {
2017-08-03 02:09:59 +00:00
bus_handler_ . flush ( ) ;
2017-06-04 22:08:35 +00:00
return ;
}
2017-06-03 23:09:47 +00:00
}
2017-08-20 16:39:45 +00:00
while ( true ) {
2017-06-03 23:09:47 +00:00
const MicroOp * operation = scheduled_program_counter_ ;
scheduled_program_counter_ + + ;
2017-05-18 01:45:23 +00:00
2017-05-26 02:41:05 +00:00
# define set_parity(v) \
2017-06-04 22:40:59 +00:00
parity_overflow_result_ = ( uint8_t ) ( v ^ 1 ) ; \
2017-05-30 02:23:19 +00:00
parity_overflow_result_ ^ = parity_overflow_result_ > > 4 ; \
parity_overflow_result_ ^ = parity_overflow_result_ < < 2 ; \
parity_overflow_result_ ^ = parity_overflow_result_ > > 1 ;
2017-05-26 02:41:05 +00:00
2017-06-03 23:09:47 +00:00
switch ( operation - > type ) {
case MicroOp : : BusOperation :
2017-06-06 21:52:44 +00:00
if ( number_of_cycles_ < operation - > machine_cycle . length ) {
scheduled_program_counter_ - - ;
2017-08-03 02:09:59 +00:00
bus_handler_ . flush ( ) ;
2017-06-06 21:52:44 +00:00
return ;
}
2017-08-27 03:08:57 +00:00
if ( uses_wait_line & & operation - > machine_cycle . was_requested ) {
2017-06-22 00:32:08 +00:00
if ( wait_line_ ) {
scheduled_program_counter_ - - ;
} else {
continue ;
}
}
2017-06-03 23:09:47 +00:00
number_of_cycles_ - = operation - > machine_cycle . length ;
last_request_status_ = request_status_ ;
2017-08-03 02:09:59 +00:00
number_of_cycles_ - = bus_handler_ . perform_machine_cycle ( operation - > machine_cycle ) ;
2017-08-22 00:43:12 +00:00
if ( uses_bus_request & & bus_request_line_ ) goto do_bus_acknowledge ;
2017-06-03 23:09:47 +00:00
break ;
case MicroOp : : MoveToNextProgram :
advance_operation ( ) ;
break ;
case MicroOp : : DecodeOperation :
2017-06-22 01:11:00 +00:00
refresh_addr_ = ir_ ;
2017-06-17 22:18:28 +00:00
ir_ . bytes . low = ( ir_ . bytes . low & 0x80 ) | ( ( ir_ . bytes . low + current_instruction_page_ - > r_step ) & 0x7f ) ;
2017-06-05 14:35:03 +00:00
pc_ . full + = pc_increment_ & ( uint16_t ) halt_mask_ ;
2017-06-22 01:11:00 +00:00
scheduled_program_counter_ = current_instruction_page_ - > instructions [ operation_ & halt_mask_ ] ;
break ;
2017-06-03 23:09:47 +00:00
case MicroOp : : DecodeOperationNoRChange :
2017-06-22 01:11:00 +00:00
refresh_addr_ = ir_ ;
pc_ . full + = pc_increment_ & ( uint16_t ) halt_mask_ ;
2017-06-03 23:09:47 +00:00
scheduled_program_counter_ = current_instruction_page_ - > instructions [ operation_ & halt_mask_ ] ;
break ;
case MicroOp : : Increment16 : ( * ( uint16_t * ) operation - > source ) + + ; break ;
case MicroOp : : IncrementPC : pc_ . full + = pc_increment_ ; break ;
case MicroOp : : Decrement16 : ( * ( uint16_t * ) operation - > source ) - - ; break ;
case MicroOp : : Move8 : * ( uint8_t * ) operation - > destination = * ( uint8_t * ) operation - > source ; break ;
case MicroOp : : Move16 : * ( uint16_t * ) operation - > destination = * ( uint16_t * ) operation - > source ; break ;
case MicroOp : : AssembleAF :
temp16_ . bytes . high = a_ ;
temp16_ . bytes . low = get_flags ( ) ;
break ;
case MicroOp : : DisassembleAF :
a_ = temp16_ . bytes . high ;
set_flags ( temp16_ . bytes . low ) ;
break ;
2017-05-19 23:28:38 +00:00
2017-05-21 13:43:17 +00:00
# pragma mark - Logical
2017-05-28 19:09:58 +00:00
# define set_logical_flags(hf) \
2017-05-30 02:23:19 +00:00
sign_result_ = zero_result_ = bit53_result_ = a_ ; \
2017-05-28 19:09:58 +00:00
set_parity ( a_ ) ; \
2017-05-30 02:23:19 +00:00
half_carry_result_ = hf ; \
2017-05-28 19:09:58 +00:00
subtract_flag_ = 0 ; \
2017-05-30 02:23:19 +00:00
carry_result_ = 0 ;
2017-05-28 19:09:58 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : And :
a_ & = * ( uint8_t * ) operation - > source ;
set_logical_flags ( Flag : : HalfCarry ) ;
break ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : Or :
a_ | = * ( uint8_t * ) operation - > source ;
set_logical_flags ( 0 ) ;
break ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : Xor :
a_ ^ = * ( uint8_t * ) operation - > source ;
set_logical_flags ( 0 ) ;
break ;
2017-05-21 13:43:17 +00:00
2017-05-28 19:09:58 +00:00
# undef set_logical_flags
2017-06-03 23:09:47 +00:00
case MicroOp : : CPL :
a_ ^ = 0xff ;
subtract_flag_ = Flag : : Subtract ;
half_carry_result_ = Flag : : HalfCarry ;
bit53_result_ = a_ ;
break ;
case MicroOp : : CCF :
2017-06-04 22:40:59 +00:00
half_carry_result_ = ( uint8_t ) ( carry_result_ < < 4 ) ;
2017-06-03 23:09:47 +00:00
carry_result_ ^ = Flag : : Carry ;
subtract_flag_ = 0 ;
bit53_result_ = a_ ;
break ;
case MicroOp : : SCF :
carry_result_ = Flag : : Carry ;
half_carry_result_ = 0 ;
subtract_flag_ = 0 ;
bit53_result_ = a_ ;
break ;
2017-05-26 02:51:08 +00:00
2017-05-26 11:29:19 +00:00
# pragma mark - Flow control
2017-05-26 01:44:24 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : DJNZ :
bc_ . bytes . high - - ;
if ( ! bc_ . bytes . high ) {
advance_operation ( ) ;
}
break ;
2017-05-26 01:44:24 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : CalculateRSTDestination :
2017-06-04 19:39:37 +00:00
memptr_ . full = operation_ & 0x38 ;
2017-06-03 23:09:47 +00:00
break ;
2017-05-26 11:29:19 +00:00
2017-05-21 13:43:17 +00:00
# pragma mark - 8-bit arithmetic
2017-06-01 02:51:32 +00:00
# define set_arithmetic_flags(sub, b53) \
sign_result_ = zero_result_ = ( uint8_t ) result ; \
2017-06-04 22:40:59 +00:00
carry_result_ = ( uint8_t ) ( result > > 8 ) ; \
half_carry_result_ = ( uint8_t ) half_result ; \
parity_overflow_result_ = ( uint8_t ) ( overflow > > 5 ) ; \
2017-06-01 02:51:32 +00:00
subtract_flag_ = sub ; \
bit53_result_ = ( uint8_t ) b53 ;
2017-06-03 23:09:47 +00:00
case MicroOp : : CP8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
int result = a_ - value ;
int half_result = ( a_ & 0xf ) - ( value & 0xf ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
// overflow for a subtraction is when the signs were originally
// different and the result is different again
int overflow = ( value ^ a_ ) & ( result ^ a_ ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
// the 5 and 3 flags come from the operand, atypically
set_arithmetic_flags ( Flag : : Subtract , value ) ;
} break ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : SUB8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
int result = a_ - value ;
int half_result = ( a_ & 0xf ) - ( value & 0xf ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
// overflow for a subtraction is when the signs were originally
// different and the result is different again
int overflow = ( value ^ a_ ) & ( result ^ a_ ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
a_ = ( uint8_t ) result ;
set_arithmetic_flags ( Flag : : Subtract , result ) ;
} break ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : SBC8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
int result = a_ - value - ( carry_result_ & Flag : : Carry ) ;
int half_result = ( a_ & 0xf ) - ( value & 0xf ) - ( carry_result_ & Flag : : Carry ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
// overflow for a subtraction is when the signs were originally
// different and the result is different again
int overflow = ( value ^ a_ ) & ( result ^ a_ ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
a_ = ( uint8_t ) result ;
set_arithmetic_flags ( Flag : : Subtract , result ) ;
} break ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : ADD8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
int result = a_ + value ;
int half_result = ( a_ & 0xf ) + ( value & 0xf ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
// overflow for addition is when the signs were originally
// the same and the result is different
int overflow = ~ ( value ^ a_ ) & ( result ^ a_ ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
a_ = ( uint8_t ) result ;
set_arithmetic_flags ( 0 , result ) ;
} break ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : ADC8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
int result = a_ + value + ( carry_result_ & Flag : : Carry ) ;
int half_result = ( a_ & 0xf ) + ( value & 0xf ) + ( carry_result_ & Flag : : Carry ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
// overflow for addition is when the signs were originally
// the same and the result is different
int overflow = ~ ( value ^ a_ ) & ( result ^ a_ ) ;
2017-05-21 13:43:17 +00:00
2017-06-03 23:09:47 +00:00
a_ = ( uint8_t ) result ;
set_arithmetic_flags ( 0 , result ) ;
} break ;
2017-05-21 13:43:17 +00:00
2017-06-01 02:51:32 +00:00
# undef set_arithmetic_flags
2017-06-03 23:09:47 +00:00
case MicroOp : : NEG : {
int overflow = ( a_ = = 0x80 ) ;
int result = - a_ ;
int halfResult = - ( a_ & 0xf ) ;
a_ = ( uint8_t ) result ;
bit53_result_ = sign_result_ = zero_result_ = a_ ;
parity_overflow_result_ = overflow ? Flag : : Overflow : 0 ;
subtract_flag_ = Flag : : Subtract ;
2017-06-04 22:40:59 +00:00
carry_result_ = ( uint8_t ) ( result > > 8 ) ;
half_carry_result_ = ( uint8_t ) halfResult ;
2017-06-03 23:09:47 +00:00
} break ;
case MicroOp : : Increment8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
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 ;
* ( uint8_t * ) operation - > source = ( uint8_t ) result ;
// sign, zero and 5 & 3 are set directly from the result
bit53_result_ = sign_result_ = zero_result_ = ( uint8_t ) result ;
2017-06-04 22:40:59 +00:00
half_carry_result_ = ( uint8_t ) half_result ;
parity_overflow_result_ = ( uint8_t ) ( overflow > > 5 ) ;
2017-06-03 23:09:47 +00:00
subtract_flag_ = 0 ;
} break ;
case MicroOp : : Decrement8 : {
uint8_t value = * ( uint8_t * ) operation - > source ;
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 ;
* ( uint8_t * ) operation - > source = ( uint8_t ) result ;
// sign, zero and 5 & 3 are set directly from the result
bit53_result_ = sign_result_ = zero_result_ = ( uint8_t ) result ;
2017-06-04 22:40:59 +00:00
half_carry_result_ = ( uint8_t ) half_result ;
parity_overflow_result_ = ( uint8_t ) ( overflow > > 5 ) ;
2017-06-03 23:09:47 +00:00
subtract_flag_ = Flag : : Subtract ;
} break ;
case MicroOp : : DAA : {
int lowNibble = a_ & 0xf ;
int highNibble = a_ > > 4 ;
int amountToAdd = 0 ;
if ( carry_result_ & Flag : : Carry )
2017-05-26 02:17:48 +00:00
{
2017-06-03 23:09:47 +00:00
amountToAdd = ( lowNibble > 0x9 | | ( half_carry_result_ & Flag : : HalfCarry ) ) ? 0x66 : 0x60 ;
2017-05-26 02:17:48 +00:00
}
else
2017-06-03 23:09:47 +00:00
{
if ( half_carry_result_ & Flag : : HalfCarry )
{
if ( lowNibble > 0x9 )
amountToAdd = ( highNibble > 0x8 ) ? 0x66 : 0x06 ;
else
amountToAdd = ( highNibble > 0x9 ) ? 0x66 : 0x06 ;
}
else
{
if ( lowNibble > 0x9 )
amountToAdd = ( highNibble > 0x8 ) ? 0x66 : 0x06 ;
else
amountToAdd = ( highNibble > 0x9 ) ? 0x60 : 0x00 ;
}
}
if ( ! ( carry_result_ & Flag : : Carry ) )
2017-05-26 02:17:48 +00:00
{
if ( lowNibble > 0x9 )
2017-06-03 23:09:47 +00:00
{
if ( highNibble > 0x8 ) carry_result_ = Flag : : Carry ;
}
2017-05-26 02:17:48 +00:00
else
2017-06-03 23:09:47 +00:00
{
if ( highNibble > 0x9 ) carry_result_ = Flag : : Carry ;
}
2017-05-26 02:17:48 +00:00
}
2017-06-03 23:09:47 +00:00
if ( subtract_flag_ )
2017-05-26 02:17:48 +00:00
{
2017-06-03 23:09:47 +00:00
a_ - = amountToAdd ;
half_carry_result_ = ( ( half_carry_result_ & Flag : : HalfCarry ) & & lowNibble < 0x6 ) ? Flag : : HalfCarry : 0 ;
2017-05-26 02:17:48 +00:00
}
else
{
2017-06-03 23:09:47 +00:00
a_ + = amountToAdd ;
half_carry_result_ = ( lowNibble > 0x9 ) ? Flag : : HalfCarry : 0 ;
2017-05-26 02:17:48 +00:00
}
2017-06-03 23:09:47 +00:00
sign_result_ = zero_result_ = bit53_result_ = a_ ;
2017-05-26 02:17:48 +00:00
2017-06-03 23:09:47 +00:00
set_parity ( a_ ) ;
} break ;
2017-05-20 02:57:43 +00:00
2017-05-21 13:43:17 +00:00
# pragma mark - 16-bit arithmetic
2017-05-20 02:57:43 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : ADD16 : {
2017-07-26 23:04:52 +00:00
memptr_ . full = * ( uint16_t * ) operation - > destination ;
uint16_t sourceValue = * ( uint16_t * ) operation - > source ;
uint16_t destinationValue = memptr_ . full ;
2017-06-03 23:09:47 +00:00
int result = sourceValue + destinationValue ;
int halfResult = ( sourceValue & 0xfff ) + ( destinationValue & 0xfff ) ;
bit53_result_ = ( uint8_t ) ( result > > 8 ) ;
2017-06-04 22:40:59 +00:00
carry_result_ = ( uint8_t ) ( result > > 16 ) ;
half_carry_result_ = ( uint8_t ) ( halfResult > > 8 ) ;
2017-06-03 23:09:47 +00:00
subtract_flag_ = 0 ;
* ( uint16_t * ) operation - > destination = ( uint16_t ) result ;
2017-07-26 23:04:52 +00:00
memptr_ . full + + ;
2017-06-03 23:09:47 +00:00
} break ;
case MicroOp : : ADC16 : {
2017-07-26 23:04:52 +00:00
memptr_ . full = * ( uint16_t * ) operation - > destination ;
uint16_t sourceValue = * ( uint16_t * ) operation - > source ;
uint16_t destinationValue = memptr_ . full ;
2017-06-03 23:09:47 +00:00
int result = sourceValue + destinationValue + ( carry_result_ & Flag : : Carry ) ;
int halfResult = ( sourceValue & 0xfff ) + ( destinationValue & 0xfff ) + ( carry_result_ & Flag : : Carry ) ;
int overflow = ( result ^ destinationValue ) & ~ ( destinationValue ^ sourceValue ) ;
bit53_result_ =
sign_result_ = ( uint8_t ) ( result > > 8 ) ;
zero_result_ = ( uint8_t ) ( result | sign_result_ ) ;
subtract_flag_ = 0 ;
2017-06-04 22:40:59 +00:00
carry_result_ = ( uint8_t ) ( result > > 16 ) ;
half_carry_result_ = ( uint8_t ) ( halfResult > > 8 ) ;
parity_overflow_result_ = ( uint8_t ) ( overflow > > 13 ) ;
2017-06-03 23:09:47 +00:00
* ( uint16_t * ) operation - > destination = ( uint16_t ) result ;
2017-07-26 23:04:52 +00:00
memptr_ . full + + ;
2017-06-03 23:09:47 +00:00
} break ;
case MicroOp : : SBC16 : {
2017-07-26 23:04:52 +00:00
memptr_ . full = * ( uint16_t * ) operation - > destination ;
uint16_t sourceValue = * ( uint16_t * ) operation - > source ;
uint16_t destinationValue = memptr_ . full ;
2017-06-03 23:09:47 +00:00
int result = destinationValue - sourceValue - ( carry_result_ & Flag : : Carry ) ;
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 ) ;
bit53_result_ =
sign_result_ = ( uint8_t ) ( result > > 8 ) ;
zero_result_ = ( uint8_t ) ( result | sign_result_ ) ;
subtract_flag_ = Flag : : Subtract ;
2017-06-04 22:40:59 +00:00
carry_result_ = ( uint8_t ) ( result > > 16 ) ;
half_carry_result_ = ( uint8_t ) ( halfResult > > 8 ) ;
parity_overflow_result_ = ( uint8_t ) ( overflow > > 13 ) ;
2017-06-03 23:09:47 +00:00
* ( uint16_t * ) operation - > destination = ( uint16_t ) result ;
2017-07-26 23:04:52 +00:00
memptr_ . full + + ;
2017-06-03 23:09:47 +00:00
} break ;
2017-05-21 13:43:17 +00:00
2017-05-28 18:55:14 +00:00
# pragma mark - Conditionals
2017-06-21 00:57:23 +00:00
# define decline_conditional() \
if ( operation - > source ) { \
scheduled_program_counter_ = ( MicroOp * ) operation - > source ; \
} else { \
advance_operation ( ) ; \
}
case MicroOp : : TestNZ : if ( ! zero_result_ ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestZ : if ( zero_result_ ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestNC : if ( carry_result_ & Flag : : Carry ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestC : if ( ! ( carry_result_ & Flag : : Carry ) ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestPO : if ( parity_overflow_result_ & Flag : : Parity ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestPE : if ( ! ( parity_overflow_result_ & Flag : : Parity ) ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestP : if ( sign_result_ & Flag : : Sign ) { decline_conditional ( ) ; } break ;
case MicroOp : : TestM : if ( ! ( sign_result_ & Flag : : Sign ) ) { decline_conditional ( ) ; } break ;
# undef decline_conditional
2017-05-20 02:57:43 +00:00
2017-05-28 18:55:14 +00:00
# pragma mark - Exchange
# define swap(a, b) temp = a.full; a.full = b.full; b.full = temp;
2017-06-03 23:09:47 +00:00
case MicroOp : : ExDEHL : {
uint16_t temp ;
swap ( de_ , hl_ ) ;
} break ;
case MicroOp : : ExAFAFDash : {
uint8_t a = a_ ;
uint8_t f = get_flags ( ) ;
set_flags ( afDash_ . bytes . low ) ;
a_ = afDash_ . bytes . high ;
afDash_ . bytes . high = a ;
afDash_ . bytes . low = f ;
} break ;
case MicroOp : : EXX : {
uint16_t temp ;
swap ( de_ , deDash_ ) ;
swap ( bc_ , bcDash_ ) ;
swap ( hl_ , hlDash_ ) ;
} break ;
2017-05-28 18:55:14 +00:00
# undef swap
2017-05-27 19:39:22 +00:00
# pragma mark - Repetition
2017-05-21 13:52:19 +00:00
2017-05-29 14:12:33 +00:00
# define REPEAT(test) \
if ( test ) { \
2017-05-29 12:54:00 +00:00
pc_ . full - = 2 ; \
} else { \
2017-06-01 00:15:56 +00:00
advance_operation ( ) ; \
2017-05-29 12:54:00 +00:00
}
2017-05-31 02:12:10 +00:00
# define LDxR_STEP(dir) \
2017-05-29 12:24:10 +00:00
bc_ . full - - ; \
2017-05-31 02:12:10 +00:00
de_ . full + = dir ; \
hl_ . full + = dir ; \
2017-06-04 18:18:04 +00:00
uint8_t sum = a_ + temp8_ ; \
2017-06-04 22:40:59 +00:00
bit53_result_ = ( uint8_t ) ( ( sum & 0x8 ) | ( ( sum & 0x02 ) < < 4 ) ) ; \
2017-05-29 12:24:10 +00:00
subtract_flag_ = 0 ; \
2017-05-30 02:23:19 +00:00
half_carry_result_ = 0 ; \
parity_overflow_result_ = bc_ . full ? Flag : : Parity : 0 ;
2017-05-29 12:24:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : LDDR : {
LDxR_STEP ( - 1 ) ;
REPEAT ( bc_ . full ) ;
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : LDIR : {
LDxR_STEP ( 1 ) ;
REPEAT ( bc_ . full ) ;
} break ;
2017-05-21 01:58:24 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : LDD : {
LDxR_STEP ( - 1 ) ;
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : LDI : {
LDxR_STEP ( 1 ) ;
} break ;
2017-05-29 03:55:54 +00:00
2017-05-29 12:24:10 +00:00
# undef LDxR_STEP
2017-05-31 02:12:10 +00:00
# define CPxR_STEP(dir) \
hl_ . full + = dir ; \
2017-05-29 12:54:00 +00:00
bc_ . full - - ; \
\
uint8_t result = a_ - temp8_ ; \
uint8_t halfResult = ( a_ & 0xf ) - ( temp8_ & 0xf ) ; \
\
2017-05-30 02:23:19 +00:00
parity_overflow_result_ = bc_ . full ? Flag : : Parity : 0 ; \
half_carry_result_ = halfResult ; \
2017-05-29 12:54:00 +00:00
subtract_flag_ = Flag : : Subtract ; \
2017-06-04 18:59:18 +00:00
sign_result_ = zero_result_ = result ; \
\
result - = ( halfResult > > 4 ) & 1 ; \
2017-05-30 02:23:19 +00:00
bit53_result_ = ( uint8_t ) ( ( result & 0x8 ) | ( ( result & 0x2 ) < < 4 ) ) ; \
2017-05-29 12:54:00 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : CPDR : {
CPxR_STEP ( - 1 ) ;
REPEAT ( bc_ . full & & sign_result_ ) ;
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : CPIR : {
CPxR_STEP ( 1 ) ;
REPEAT ( bc_ . full & & sign_result_ ) ;
} break ;
2017-05-29 03:55:54 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : CPD : {
2017-06-04 19:39:37 +00:00
memptr_ . full - - ;
2017-06-03 23:09:47 +00:00
CPxR_STEP ( - 1 ) ;
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : CPI : {
2017-06-04 19:39:37 +00:00
memptr_ . full + + ;
2017-06-03 23:09:47 +00:00
CPxR_STEP ( 1 ) ;
} break ;
2017-05-29 03:55:54 +00:00
2017-05-29 12:54:00 +00:00
# undef CPxR_STEP
2017-05-29 03:55:54 +00:00
2017-05-31 02:12:10 +00:00
# define INxR_STEP(dir) \
2017-05-29 14:12:33 +00:00
bc_ . bytes . high - - ; \
2017-05-31 02:12:10 +00:00
hl_ . full + = dir ; \
2017-05-29 14:12:33 +00:00
\
2017-05-30 02:23:19 +00:00
sign_result_ = zero_result_ = bit53_result_ = bc_ . bytes . high ; \
2017-05-29 14:12:33 +00:00
subtract_flag_ = ( temp8_ > > 6 ) & Flag : : Subtract ; \
\
2017-05-31 02:12:10 +00:00
int next_bc = bc_ . bytes . low + dir ; \
2017-05-29 14:12:33 +00:00
int summation = temp8_ + ( next_bc & 0xff ) ; \
\
if ( summation > 0xff ) { \
2017-05-30 02:23:19 +00:00
carry_result_ = Flag : : Carry ; \
half_carry_result_ = Flag : : HalfCarry ; \
2017-05-29 14:12:33 +00:00
} else { \
2017-05-30 02:23:19 +00:00
carry_result_ = 0 ; \
half_carry_result_ = 0 ; \
2017-05-29 14:12:33 +00:00
} \
\
summation = ( summation & 7 ) ^ bc_ . bytes . high ; \
set_parity ( summation ) ;
2017-06-03 23:09:47 +00:00
case MicroOp : : INDR : {
INxR_STEP ( - 1 ) ;
REPEAT ( bc_ . bytes . high ) ;
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : INIR : {
INxR_STEP ( 1 ) ;
REPEAT ( bc_ . bytes . high ) ;
} break ;
2017-05-29 14:12:33 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : IND : {
2017-06-04 19:39:37 +00:00
memptr_ . full = bc_ . full - 1 ;
2017-06-03 23:09:47 +00:00
INxR_STEP ( - 1 ) ;
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : INI : {
2017-06-04 19:39:37 +00:00
memptr_ . full = bc_ . full + 1 ;
2017-06-03 23:09:47 +00:00
INxR_STEP ( 1 ) ;
} break ;
2017-05-29 14:12:33 +00:00
# undef INxR_STEP
2017-05-31 02:12:10 +00:00
# define OUTxR_STEP(dir) \
2017-05-29 14:28:04 +00:00
bc_ . bytes . high - - ; \
2017-05-31 02:12:10 +00:00
hl_ . full + = dir ; \
2017-05-29 14:28:04 +00:00
\
2017-05-30 02:23:19 +00:00
sign_result_ = zero_result_ = bit53_result_ = bc_ . bytes . high ; \
2017-05-29 14:28:04 +00:00
subtract_flag_ = ( temp8_ > > 6 ) & Flag : : Subtract ; \
\
int summation = temp8_ + hl_ . bytes . low ; \
if ( summation > 0xff ) { \
2017-05-30 02:23:19 +00:00
carry_result_ = Flag : : Carry ; \
half_carry_result_ = Flag : : HalfCarry ; \
2017-05-29 14:28:04 +00:00
} else { \
2017-05-30 02:23:19 +00:00
carry_result_ = half_carry_result_ = 0 ; \
2017-05-29 14:28:04 +00:00
} \
\
summation = ( summation & 7 ) ^ bc_ . bytes . high ; \
set_parity ( summation ) ;
2017-06-03 23:09:47 +00:00
case MicroOp : : OUT_R :
REPEAT ( bc_ . bytes . high ) ;
break ;
2017-05-29 14:28:04 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : OUTD : {
OUTxR_STEP ( - 1 ) ;
2017-06-04 19:39:37 +00:00
memptr_ . full = bc_ . full - 1 ;
2017-06-03 23:09:47 +00:00
} break ;
2017-05-31 02:12:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : OUTI : {
OUTxR_STEP ( 1 ) ;
2017-06-04 19:39:37 +00:00
memptr_ . full = bc_ . full + 1 ;
2017-06-03 23:09:47 +00:00
} break ;
2017-05-29 14:28:04 +00:00
# undef OUTxR_STEP
2017-05-27 19:39:22 +00:00
# pragma mark - Bit Manipulation
2017-06-03 23:09:47 +00:00
case MicroOp : : BIT : {
uint8_t result = * ( uint8_t * ) operation - > source & ( 1 < < ( ( operation_ > > 3 ) & 7 ) ) ;
2017-05-27 19:39:22 +00:00
2017-06-04 19:07:07 +00:00
if ( current_instruction_page_ - > is_indexed | | ( ( operation_ & 0x08 ) = = 7 ) ) {
bit53_result_ = memptr_ . bytes . high ;
2017-06-04 18:13:38 +00:00
} else {
bit53_result_ = * ( uint8_t * ) operation - > source ;
}
2017-06-03 23:09:47 +00:00
sign_result_ = zero_result_ = result ;
half_carry_result_ = Flag : : HalfCarry ;
subtract_flag_ = 0 ;
parity_overflow_result_ = result ? 0 : Flag : : Parity ;
} break ;
2017-05-27 19:39:22 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : RES :
* ( uint8_t * ) operation - > source & = ~ ( 1 < < ( ( operation_ > > 3 ) & 7 ) ) ;
break ;
2017-05-27 19:39:22 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : SET :
* ( uint8_t * ) operation - > source | = ( 1 < < ( ( operation_ > > 3 ) & 7 ) ) ;
break ;
2017-05-27 19:39:22 +00:00
# pragma mark - Rotation and shifting
2017-05-21 13:52:19 +00:00
2017-05-28 04:12:42 +00:00
# define set_rotate_flags() \
2017-05-30 02:23:19 +00:00
bit53_result_ = a_ ; \
carry_result_ = new_carry ; \
subtract_flag_ = half_carry_result_ = 0 ;
2017-05-28 04:12:42 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : RLA : {
uint8_t new_carry = a_ > > 7 ;
a_ = ( uint8_t ) ( ( a_ < < 1 ) | ( carry_result_ & Flag : : Carry ) ) ;
set_rotate_flags ( ) ;
} break ;
case MicroOp : : RRA : {
uint8_t new_carry = a_ & 1 ;
a_ = ( uint8_t ) ( ( a_ > > 1 ) | ( carry_result_ < < 7 ) ) ;
set_rotate_flags ( ) ;
} break ;
case MicroOp : : RLCA : {
uint8_t new_carry = a_ > > 7 ;
a_ = ( uint8_t ) ( ( a_ < < 1 ) | new_carry ) ;
set_rotate_flags ( ) ;
} break ;
case MicroOp : : RRCA : {
uint8_t new_carry = a_ & 1 ;
a_ = ( uint8_t ) ( ( a_ > > 1 ) | ( new_carry < < 7 ) ) ;
set_rotate_flags ( ) ;
} break ;
2017-05-21 13:52:19 +00:00
2017-05-28 04:12:42 +00:00
# undef set_rotate_flags
# define set_shift_flags() \
2017-05-30 02:23:19 +00:00
sign_result_ = zero_result_ = bit53_result_ = * ( uint8_t * ) operation - > source ; \
2017-05-28 04:12:42 +00:00
set_parity ( sign_result_ ) ; \
2017-05-30 02:23:19 +00:00
half_carry_result_ = 0 ; \
2017-05-28 04:12:42 +00:00
subtract_flag_ = 0 ;
2017-06-03 23:09:47 +00:00
case MicroOp : : RLC :
carry_result_ = * ( uint8_t * ) operation - > source > > 7 ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( ( * ( uint8_t * ) operation - > source < < 1 ) | carry_result_ ) ;
set_shift_flags ( ) ;
break ;
case MicroOp : : RRC :
carry_result_ = * ( uint8_t * ) operation - > source ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( ( * ( uint8_t * ) operation - > source > > 1 ) | ( carry_result_ < < 7 ) ) ;
set_shift_flags ( ) ;
break ;
case MicroOp : : RL : {
uint8_t next_carry = * ( uint8_t * ) operation - > source > > 7 ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( ( * ( uint8_t * ) operation - > source < < 1 ) | ( carry_result_ & Flag : : Carry ) ) ;
carry_result_ = next_carry ;
set_shift_flags ( ) ;
} break ;
case MicroOp : : RR : {
uint8_t next_carry = * ( uint8_t * ) operation - > source ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( ( * ( uint8_t * ) operation - > source > > 1 ) | ( carry_result_ < < 7 ) ) ;
carry_result_ = next_carry ;
set_shift_flags ( ) ;
} break ;
case MicroOp : : SLA :
carry_result_ = * ( uint8_t * ) operation - > source > > 7 ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( * ( uint8_t * ) operation - > source < < 1 ) ;
set_shift_flags ( ) ;
break ;
case MicroOp : : SRA :
carry_result_ = * ( uint8_t * ) operation - > source ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( ( * ( uint8_t * ) operation - > source > > 1 ) | ( * ( uint8_t * ) operation - > source & 0x80 ) ) ;
set_shift_flags ( ) ;
break ;
case MicroOp : : SLL :
carry_result_ = * ( uint8_t * ) operation - > source > > 7 ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( * ( uint8_t * ) operation - > source < < 1 ) | 1 ;
set_shift_flags ( ) ;
break ;
case MicroOp : : SRL :
carry_result_ = * ( uint8_t * ) operation - > source ;
* ( uint8_t * ) operation - > source = ( uint8_t ) ( ( * ( uint8_t * ) operation - > source > > 1 ) ) ;
set_shift_flags ( ) ;
break ;
2017-05-27 19:39:22 +00:00
2017-05-28 04:12:42 +00:00
# undef set_shift_flags
2017-05-27 19:39:22 +00:00
2017-05-28 20:46:27 +00:00
# define set_decimal_rotate_flags() \
subtract_flag_ = 0 ; \
2017-05-30 02:23:19 +00:00
half_carry_result_ = 0 ; \
2017-05-28 20:46:27 +00:00
set_parity ( a_ ) ; \
2017-05-30 02:23:19 +00:00
bit53_result_ = zero_result_ = sign_result_ = a_ ;
2017-05-28 20:46:27 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : RRD : {
2017-06-04 19:39:37 +00:00
memptr_ . full = hl_ . full + 1 ;
2017-06-03 23:09:47 +00:00
uint8_t low_nibble = a_ & 0xf ;
a_ = ( a_ & 0xf0 ) | ( temp8_ & 0xf ) ;
2017-06-04 22:40:59 +00:00
temp8_ = ( uint8_t ) ( ( temp8_ > > 4 ) | ( low_nibble < < 4 ) ) ;
2017-06-03 23:09:47 +00:00
set_decimal_rotate_flags ( ) ;
} break ;
2017-05-28 20:46:27 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : RLD : {
2017-06-04 19:39:37 +00:00
memptr_ . full = hl_ . full + 1 ;
2017-06-03 23:09:47 +00:00
uint8_t low_nibble = a_ & 0xf ;
a_ = ( a_ & 0xf0 ) | ( temp8_ > > 4 ) ;
2017-06-04 22:40:59 +00:00
temp8_ = ( uint8_t ) ( ( temp8_ < < 4 ) | low_nibble ) ;
2017-06-03 23:09:47 +00:00
set_decimal_rotate_flags ( ) ;
} break ;
2017-05-28 20:46:27 +00:00
# undef set_decimal_rotate_flags
2017-05-21 16:53:17 +00:00
# pragma mark - Interrupt state
2017-06-03 23:09:47 +00:00
case MicroOp : : EI :
iff1_ = iff2_ = true ;
if ( irq_line_ ) request_status_ | = Interrupt : : IRQ ;
break ;
case MicroOp : : DI :
iff1_ = iff2_ = false ;
request_status_ & = ~ Interrupt : : IRQ ;
break ;
case MicroOp : : IM :
switch ( operation_ & 0x18 ) {
case 0x00 : interrupt_mode_ = 0 ; break ;
case 0x08 : interrupt_mode_ = 0 ; break ; // IM 0/1
case 0x10 : interrupt_mode_ = 1 ; break ;
case 0x18 : interrupt_mode_ = 2 ; break ;
}
break ;
2017-05-28 19:55:21 +00:00
2017-05-28 18:50:51 +00:00
# pragma mark - Input
2017-06-03 23:09:47 +00:00
case MicroOp : : SetInFlags :
subtract_flag_ = half_carry_result_ = 0 ;
sign_result_ = zero_result_ = bit53_result_ = * ( uint8_t * ) operation - > source ;
set_parity ( sign_result_ ) ;
break ;
2017-05-28 18:50:51 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : SetAFlags :
subtract_flag_ = half_carry_result_ = 0 ;
parity_overflow_result_ = iff2_ ? Flag : : Parity : 0 ;
sign_result_ = zero_result_ = bit53_result_ = a_ ;
break ;
2017-05-28 20:32:10 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : SetZero :
temp8_ = 0 ;
break ;
2017-05-28 18:50:51 +00:00
2017-05-29 15:54:27 +00:00
# pragma mark - Special-case Flow
2017-05-28 20:07:25 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : BeginIRQMode0 :
pc_increment_ = 0 ; // deliberate fallthrough
case MicroOp : : BeginIRQ :
iff2_ = iff1_ = false ;
request_status_ & = ~ Interrupt : : IRQ ;
temp16_ . full = 0x38 ;
break ;
case MicroOp : : BeginNMI :
iff2_ = iff1_ ;
iff1_ = false ;
request_status_ & = ~ Interrupt : : IRQ ;
break ;
case MicroOp : : JumpTo66 :
pc_ . full = 0x66 ;
break ;
case MicroOp : : RETN :
iff1_ = iff2_ ;
if ( irq_line_ & & iff1_ ) request_status_ | = Interrupt : : IRQ ;
break ;
case MicroOp : : HALT :
halt_mask_ = 0x00 ;
break ;
2017-05-29 15:54:27 +00:00
2017-06-02 02:31:04 +00:00
# pragma mark - Interrupt handling
2017-06-03 23:09:47 +00:00
case MicroOp : : Reset :
iff1_ = iff2_ = false ;
interrupt_mode_ = 0 ;
pc_ . full = 0 ;
sp_ . full = 0xffff ;
a_ = 0xff ;
set_flags ( 0xff ) ;
2017-06-17 22:18:28 +00:00
ir_ . full = 0 ;
2017-06-03 23:09:47 +00:00
break ;
2017-06-02 02:31:04 +00:00
2017-05-21 13:52:19 +00:00
# pragma mark - Internal bookkeeping
2017-06-03 23:09:47 +00:00
case MicroOp : : SetInstructionPage :
current_instruction_page_ = ( InstructionPage * ) operation - > source ;
scheduled_program_counter_ = current_instruction_page_ - > fetch_decode_execute_data ;
break ;
2017-05-20 21:04:25 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : CalculateIndexAddress :
2017-06-04 22:40:59 +00:00
memptr_ . full = ( uint16_t ) ( * ( uint16_t * ) operation - > source + ( int8_t ) temp8_ ) ;
2017-06-03 23:09:47 +00:00
break ;
2017-05-21 23:26:40 +00:00
2017-06-03 23:09:47 +00:00
case MicroOp : : IndexedPlaceHolder :
printf ( " Hit placeholder!!! \n " ) ;
return ;
}
2017-05-26 02:41:05 +00:00
# undef set_parity
2017-06-03 23:09:47 +00:00
}
2017-05-26 02:41:05 +00:00
2017-05-16 02:25:52 +00:00
}
2017-05-15 11:59:21 +00:00
}
2017-05-15 11:55:53 +00:00
/*!
2017-07-24 02:16:00 +00:00
Called to announce the end of a run_for period , allowing deferred work to take place .
2017-05-15 11:55:53 +00:00
2017-05-15 11:59:21 +00:00
Users of the Z80 template may override this .
2017-05-15 11:55:53 +00:00
*/
void flush ( ) { }
2017-07-26 23:42:00 +00:00
HalfCycles perform_machine_cycle ( const PartialMachineCycle & cycle ) {
return HalfCycles ( 0 ) ;
2017-05-16 02:25:52 +00:00
}
2017-05-15 11:55:53 +00:00
/*!
Gets the flags register .
@ see set_flags
@ returns The current value of the flags register .
*/
uint8_t get_flags ( ) {
2017-05-26 01:23:38 +00:00
uint8_t result =
2017-05-15 11:55:53 +00:00
( sign_result_ & Flag : : Sign ) |
2017-05-20 02:57:43 +00:00
( zero_result_ ? 0 : Flag : : Zero ) |
2017-05-30 02:23:19 +00:00
( bit53_result_ & ( Flag : : Bit5 | Flag : : Bit3 ) ) |
( half_carry_result_ & Flag : : HalfCarry ) |
( parity_overflow_result_ & Flag : : Parity ) |
2017-05-20 02:57:43 +00:00
subtract_flag_ |
2017-05-30 02:23:19 +00:00
( carry_result_ & Flag : : Carry ) ;
2017-05-26 01:23:38 +00:00
return result ;
2017-05-15 11:55:53 +00:00
}
/*!
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 ;
2017-05-20 02:57:43 +00:00
zero_result_ = ( flags & Flag : : Zero ) ^ Flag : : Zero ;
2017-05-30 02:23:19 +00:00
bit53_result_ = flags ;
half_carry_result_ = flags ;
parity_overflow_result_ = flags ;
2017-05-15 11:55:53 +00:00
subtract_flag_ = flags & Flag : : Subtract ;
2017-05-30 02:23:19 +00:00
carry_result_ = flags ;
2017-05-15 11:55:53 +00:00
}
/*!
Gets the value of a register .
@ see set_value_of_register
@ param r The register to set .
@ returns The value of the register . 8 - bit registers will be returned as unsigned .
*/
uint16_t get_value_of_register ( Register r ) {
switch ( r ) {
case Register : : ProgramCounter : return pc_ . full ;
case Register : : StackPointer : return sp_ . full ;
case Register : : A : return a_ ;
case Register : : Flags : return get_flags ( ) ;
case Register : : AF : return ( uint16_t ) ( ( a_ < < 8 ) | get_flags ( ) ) ;
case Register : : B : return bc_ . bytes . high ;
case Register : : C : return bc_ . bytes . low ;
case Register : : BC : return bc_ . full ;
case Register : : D : return de_ . bytes . high ;
case Register : : E : return de_ . bytes . low ;
case Register : : DE : return de_ . full ;
case Register : : H : return hl_ . bytes . high ;
case Register : : L : return hl_ . bytes . low ;
case Register : : HL : return hl_ . full ;
case Register : : ADash : return afDash_ . bytes . high ;
case Register : : FlagsDash : return afDash_ . bytes . low ;
case Register : : AFDash : return afDash_ . full ;
case Register : : BDash : return bcDash_ . bytes . high ;
case Register : : CDash : return bcDash_ . bytes . low ;
case Register : : BCDash : return bcDash_ . full ;
case Register : : DDash : return deDash_ . bytes . high ;
case Register : : EDash : return deDash_ . bytes . low ;
case Register : : DEDash : return deDash_ . full ;
case Register : : HDash : return hlDash_ . bytes . high ;
case Register : : LDash : return hlDash_ . bytes . low ;
case Register : : HLDash : return hlDash_ . full ;
case Register : : IXh : return ix_ . bytes . high ;
case Register : : IXl : return ix_ . bytes . low ;
case Register : : IX : return ix_ . full ;
case Register : : IYh : return iy_ . bytes . high ;
case Register : : IYl : return iy_ . bytes . low ;
case Register : : IY : return iy_ . full ;
2017-06-17 22:18:28 +00:00
case Register : : R : return ir_ . bytes . low ;
case Register : : I : return ir_ . bytes . high ;
case Register : : Refresh : return ir_ . full ;
2017-05-15 11:55:53 +00:00
2017-05-22 23:14:46 +00:00
case Register : : IFF1 : return iff1_ ? 1 : 0 ;
case Register : : IFF2 : return iff2_ ? 1 : 0 ;
2017-06-04 22:32:23 +00:00
case Register : : IM : return ( uint16_t ) interrupt_mode_ ;
2017-05-22 23:14:46 +00:00
2017-07-22 02:31:42 +00:00
case Register : : MemPtr : return memptr_ . full ;
2017-05-15 11:55:53 +00:00
default : return 0 ;
}
}
/*!
Sets the value of a register .
@ see get_value_of_register
@ param r The register to set .
@ param value The value to set . If the register is only 8 bit , the value will be truncated .
*/
void set_value_of_register ( Register r , uint16_t value ) {
switch ( r ) {
case Register : : ProgramCounter : pc_ . full = value ; break ;
case Register : : StackPointer : sp_ . full = value ; break ;
case Register : : A : a_ = ( uint8_t ) value ; break ;
case Register : : AF : a_ = ( uint8_t ) ( value > > 8 ) ; // deliberate fallthrough...
case Register : : Flags : set_flags ( ( uint8_t ) value ) ; break ;
case Register : : B : bc_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : C : bc_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : BC : bc_ . full = value ; break ;
case Register : : D : de_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : E : de_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : DE : de_ . full = value ; break ;
case Register : : H : hl_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : L : hl_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : HL : hl_ . full = value ; break ;
case Register : : ADash : afDash_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : FlagsDash : afDash_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : AFDash : afDash_ . full = value ; break ;
case Register : : BDash : bcDash_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : CDash : bcDash_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : BCDash : bcDash_ . full = value ; break ;
case Register : : DDash : deDash_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : EDash : deDash_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : DEDash : deDash_ . full = value ; break ;
case Register : : HDash : hlDash_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : LDash : hlDash_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : HLDash : hlDash_ . full = value ; break ;
case Register : : IXh : ix_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : IXl : ix_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : IX : ix_ . full = value ; break ;
case Register : : IYh : iy_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : IYl : iy_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : IY : iy_ . full = value ; break ;
2017-06-17 22:18:28 +00:00
case Register : : R : ir_ . bytes . low = ( uint8_t ) value ; break ;
case Register : : I : ir_ . bytes . high = ( uint8_t ) value ; break ;
case Register : : Refresh : ir_ . full = ( uint16_t ) value ; break ;
2017-05-15 11:55:53 +00:00
2017-05-22 23:14:46 +00:00
case Register : : IFF1 : iff1_ = ! ! value ; break ;
case Register : : IFF2 : iff2_ = ! ! value ; break ;
2017-06-03 22:58:13 +00:00
case Register : : IM : interrupt_mode_ = value % 3 ; break ;
2017-05-22 23:14:46 +00:00
2017-07-22 02:31:42 +00:00
case Register : : MemPtr : memptr_ . full = value ; break ;
2017-05-15 11:55:53 +00:00
default : break ;
}
}
2017-05-29 15:54:27 +00:00
/*!
Gets the value of the HALT output line .
*/
bool get_halt_line ( ) {
2017-05-31 02:15:43 +00:00
return halt_mask_ = = 0x00 ;
2017-05-29 15:54:27 +00:00
}
2017-06-01 02:51:32 +00:00
/*!
2017-06-02 00:34:52 +00:00
Sets the logical value of the interrupt line .
2017-06-11 17:31:02 +00:00
@ param offset If called while within perform_machine_cycle this may be a value indicating
how many cycles before now the line changed state . The value may not be longer than the
current machine cycle . If called at any other time , this must be zero .
2017-06-01 02:51:32 +00:00
*/
2017-07-28 01:10:14 +00:00
void set_interrupt_line ( bool value , HalfCycles offset = 0 ) {
2017-06-05 13:37:19 +00:00
if ( irq_line_ = = value ) return ;
2017-06-02 00:34:52 +00:00
// IRQ requests are level triggered and masked.
irq_line_ = value ;
if ( irq_line_ & & iff1_ ) {
request_status_ | = Interrupt : : IRQ ;
} else {
request_status_ & = ~ Interrupt : : IRQ ;
}
2017-06-11 17:31:02 +00:00
// If this change happened at least one cycle ago then: (i) we're promised that this is a machine
// cycle per the contract on supplying an offset; and (ii) that means it happened before the lines
// were sampled. So adjust the most recent sample.
2017-07-28 01:10:14 +00:00
if ( offset < = HalfCycles ( - 2 ) ) {
2017-06-11 17:31:02 +00:00
last_request_status_ = ( last_request_status_ & ~ Interrupt : : IRQ ) | ( request_status_ & Interrupt : : IRQ ) ;
}
2017-06-01 02:51:32 +00:00
}
2017-06-23 00:11:19 +00:00
bool get_interrupt_line ( ) {
return irq_line_ ;
}
2017-06-01 02:51:32 +00:00
/*!
2017-06-02 00:34:52 +00:00
Sets the logical value of the non - maskable interrupt line .
2017-06-11 17:31:02 +00:00
@ param offset See discussion in set_interrupt_line .
2017-06-01 02:51:32 +00:00
*/
2017-06-11 17:31:02 +00:00
void set_non_maskable_interrupt_line ( bool value , int offset = 0 ) {
2017-06-02 00:34:52 +00:00
// NMIs are edge triggered and cannot be masked.
2017-06-23 00:11:19 +00:00
nmi_line_ = value ;
2017-06-11 17:31:02 +00:00
if ( value ) {
request_status_ | = Interrupt : : NMI ;
2017-06-11 20:41:34 +00:00
if ( offset < 0 ) {
2017-06-11 17:31:02 +00:00
last_request_status_ | = Interrupt : : NMI ;
}
}
2017-06-01 02:51:32 +00:00
}
2017-06-23 00:11:19 +00:00
bool get_non_maskable_interrupt_line ( ) {
return nmi_line_ ;
}
2017-06-01 02:51:32 +00:00
/*!
2017-06-02 00:34:52 +00:00
Sets the logical value of the bus request line .
2017-06-01 02:51:32 +00:00
*/
2017-06-02 00:34:52 +00:00
void set_bus_request_line ( bool value ) {
2017-08-22 00:43:12 +00:00
assert ( uses_bus_request ) ;
2017-06-03 23:09:47 +00:00
bus_request_line_ = value ;
2017-06-01 02:51:32 +00:00
}
2017-06-23 00:11:19 +00:00
bool get_bus_request_line ( ) {
return bus_request_line_ ;
}
2017-06-02 01:40:08 +00:00
/*!
Sets the logical value of the reset line .
*/
void set_reset_line ( bool value ) {
// Reset requests are level triggered and cannot be masked.
if ( value ) request_status_ | = Interrupt : : Reset ;
else request_status_ & = ~ Interrupt : : Reset ;
}
2017-06-02 02:31:04 +00:00
/*!
This emulation automatically sets itself up in power - on state at creation , which has the effect of triggering a
reset at the first opportunity . Use @ c reset_power_on to disable that behaviour .
*/
inline void reset_power_on ( ) {
request_status_ & = ~ Interrupt : : PowerOn ;
last_request_status_ & = ~ Interrupt : : PowerOn ;
}
2017-06-22 00:32:08 +00:00
/*!
Sets the logical value of the wait line .
*/
inline void set_wait_line ( bool value ) {
2017-08-27 03:08:57 +00:00
assert ( uses_wait_line ) ;
2017-06-22 00:32:08 +00:00
wait_line_ = value ;
}
2017-06-23 00:11:19 +00:00
bool get_wait_line ( ) {
return wait_line_ ;
}
2017-05-14 21:46:41 +00:00
} ;
2017-05-15 02:15:16 +00:00
}
2017-05-14 21:46:41 +00:00
}
# endif /* Z80_hpp */