2017-05-14 21:46:41 +00:00
//
// Z80.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
2018-05-13 19:19:52 +00:00
// Copyright 2017 Thomas Harte. All rights reserved.
2017-05-14 21:46:41 +00:00
//
# ifndef Z80_hpp
# define Z80_hpp
2017-08-22 00:43:12 +00:00
# include <cassert>
2017-05-30 01:44:33 +00:00
# include <vector>
2017-09-02 00:50:24 +00:00
# include <cstdint>
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"
2019-07-17 19:09:26 +00:00
# include "../../ClockReceiver/ForceInline.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 .
*/
2019-09-22 16:13:56 +00:00
enum class Register {
2017-05-14 21:46:41 +00:00
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
} ;
/*
2017-09-02 00:50:24 +00:00
Flags as defined on the Z80 ; can be used to decode the result of getting or setting @ c Flags .
2017-05-14 21:46:41 +00:00
*/
enum Flag : uint8_t {
Sign = 0x80 ,
Zero = 0x40 ,
Bit5 = 0x20 ,
HalfCarry = 0x10 ,
Bit3 = 0x08 ,
Parity = 0x04 ,
Overflow = 0x04 ,
Subtract = 0x02 ,
Carry = 0x01
} ;
/*!
2017-09-02 00:50:24 +00:00
Subclasses will be given the task of performing partial machine cycles , allowing them to provide whatever interface they like
between a Z80 and the rest of the system . @ c PartialMachineCycle defines the information they will be handed for each unit
of execution .
2017-05-14 21:46:41 +00:00
*/
2017-06-22 00:38:08 +00:00
struct PartialMachineCycle {
2017-06-18 01:53:45 +00:00
enum Operation {
2021-04-02 11:34:06 +00:00
/// The final half cycle of the opcode fetch part of an M1 cycle.
2017-07-28 00:17:13 +00:00
ReadOpcode = 0 ,
2021-04-02 11:34:06 +00:00
/// The 1.5 cycles of a read cycle.
2017-06-22 00:32:08 +00:00
Read ,
2021-04-02 11:34:06 +00:00
/// The 1.5 cycles of a write cycle.
2017-06-22 00:32:08 +00:00
Write ,
2021-04-02 11:34:06 +00:00
/// The 1.5 cycles of an input cycle.
2017-06-22 00:32:08 +00:00
Input ,
2021-04-02 11:34:06 +00:00
/// The 1.5 cycles of an output cycle.
2017-06-22 00:32:08 +00:00
Output ,
2021-04-02 11:34:06 +00:00
/// The 1.5 cycles of an interrupt acknowledgment.
2017-06-18 01:53:45 +00:00
Interrupt ,
2017-06-22 00:32:08 +00:00
2021-04-02 11:34:06 +00:00
/// The two-cycle refresh part of an M1 cycle.
2017-06-22 00:32:08 +00:00
Refresh ,
2021-04-02 11:34:06 +00:00
/// A period with no changes in bus signalling.
2017-06-22 00:32:08 +00:00
Internal ,
2021-04-02 11:34:06 +00:00
/// A bus acknowledgement cycle.
2017-06-18 01:53:45 +00:00
BusAcknowledge ,
2017-06-22 00:32:08 +00:00
2021-04-02 11:34:06 +00:00
/// A wait state within an M1 cycle.
2017-07-28 00:17:13 +00:00
ReadOpcodeWait ,
2021-04-02 11:34:06 +00:00
/// A wait state within a read cycle.
2017-06-22 00:32:08 +00:00
ReadWait ,
2021-04-02 11:34:06 +00:00
/// A wait state within a write cycle.
2017-06-22 00:32:08 +00:00
WriteWait ,
2021-04-02 11:34:06 +00:00
/// A wait state within an input cycle.
2017-06-22 00:32:08 +00:00
InputWait ,
2021-04-02 11:34:06 +00:00
/// A wait state within an output cycle.
2017-06-23 00:07:47 +00:00
OutputWait ,
2021-04-02 11:34:06 +00:00
/// A wait state within an interrupt acknowledge cycle.
2017-06-23 00:07:47 +00:00
InterruptWait ,
2021-04-02 11:34:06 +00:00
/// The first 1.5 cycles of an M1 bus cycle, up to the sampling of WAIT.
2017-07-28 00:17:13 +00:00
ReadOpcodeStart ,
2021-04-02 11:34:06 +00:00
/// The first 1.5 cycles of a read cycle, up to the sampling of WAIT.
2017-06-23 00:07:47 +00:00
ReadStart ,
2021-04-02 11:34:06 +00:00
/// The first 1.5 cycles of a write cycle, up to the sampling of WAIT.
2017-06-23 00:07:47 +00:00
WriteStart ,
2021-04-02 11:34:06 +00:00
/// The first 1.5 samples of an input bus cycle, up to the sampling of WAIT.
2017-06-23 00:07:47 +00:00
InputStart ,
2021-04-02 11:34:06 +00:00
/// The first 1.5 samples of an output bus cycle, up to the sampling of WAIT.
2017-06-22 00:32:08 +00:00
OutputStart ,
2021-04-02 11:34:06 +00:00
/// The first portion of an interrupt acknowledgement — 2.5 or 3.5 cycles, depending on interrupt mode.
2017-07-28 00:17:13 +00:00
InterruptStart ,
2017-08-25 01:32:33 +00:00
} ;
2017-09-02 00:50:24 +00:00
/// The operation being carried out by the Z80. See the various getters below for better classification.
2020-05-30 04:37:06 +00:00
const Operation operation = Operation : : Internal ;
2017-09-02 00:50:24 +00:00
/// The length of this operation.
2017-08-25 01:32:33 +00:00
const HalfCycles length ;
2017-09-02 00:50:24 +00:00
/// The current value of the address bus.
2020-05-30 04:37:06 +00:00
const uint16_t * const address = nullptr ;
2019-04-19 21:44:52 +00:00
/// If the Z80 is outputting to the data bus, a pointer to that value. Otherwise, a pointer to the location where the current data bus value should be placed.
2020-05-30 04:37:06 +00:00
uint8_t * const value = nullptr ;
2017-09-02 00:50:24 +00:00
/// @c true if this operation is occurring only because of an external request; @c false otherwise.
2020-05-30 04:37:06 +00:00
const bool was_requested = false ;
2017-06-22 00:32:08 +00:00
2017-09-02 00:50:24 +00:00
/*!
@ returns @ c true if the processor believes that the bus handler should actually do something with
the content of this PartialMachineCycle ; @ c false otherwise .
*/
2019-07-17 19:09:26 +00:00
forceinline bool expects_action ( ) const {
2017-06-22 00:32:08 +00:00
return operation < = Operation : : Interrupt ;
}
2017-09-02 00:50:24 +00:00
/*!
@ returns @ c true if this partial machine cycle completes one of the documented full machine cycles ;
@ c false otherwise .
*/
2019-07-17 19:09:26 +00:00
forceinline bool is_terminal ( ) const {
2017-06-22 00:32:08 +00:00
return operation < = Operation : : BusAcknowledge ;
}
2017-09-02 00:50:24 +00:00
/*!
@ returns @ c true if this partial machine cycle is a wait cycle ; @ c false otherwise .
*/
2019-07-17 19:09:26 +00:00
forceinline 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
}
2021-06-27 20:27:26 +00:00
/*!
@ returns @ c true if this partial machine cycle is a memory access ; @ c false otherwise .
*/
forceinline bool is_memory_access ( ) const {
return operation < = Operation : : Write ;
}
2017-08-25 01:32:33 +00:00
2021-04-02 03:02:40 +00:00
enum Line {
CLK = 1 < < 0 ,
MREQ = 1 < < 1 ,
IOREQ = 1 < < 2 ,
RD = 1 < < 3 ,
WR = 1 < < 4 ,
RFSH = 1 < < 5 ,
M1 = 1 < < 6 ,
2021-04-02 11:34:06 +00:00
BUSACK = 1 < < 7 ,
2021-04-02 03:02:40 +00:00
} ;
/// @returns A C-style array of the bus state at the beginning of each half cycle in this
/// partial machine cycle. Each element is a combination of bit masks from the Line enum;
2021-04-02 11:34:06 +00:00
/// bit set means line active, bit clear means line inactive. For the CLK line set means high.
2021-04-08 23:21:35 +00:00
///
/// @discussion This discrete sampling is prone to aliasing errors. Beware.
2021-04-02 03:02:40 +00:00
const uint8_t * bus_state ( ) const {
switch ( operation ) {
//
// M1 cycle
//
case Operation : : ReadOpcodeStart : {
static constexpr uint8_t states [ ] = {
Line : : CLK | Line : : M1 ,
Line : : M1 | Line : : MREQ | Line : : RD ,
Line : : CLK | Line : : M1 | Line : : MREQ | Line : : RD ,
} ;
return states ;
}
case Operation : : ReadOpcode :
case Operation : : ReadOpcodeWait : {
static constexpr uint8_t states [ ] = {
Line : : M1 | Line : : MREQ | Line : : RD ,
Line : : CLK | Line : : M1 | Line : : MREQ | Line : : RD ,
} ;
return states ;
}
case Operation : : Refresh : {
static constexpr uint8_t states [ ] = {
Line : : CLK | Line : : RFSH | Line : : MREQ ,
Line : : RFSH ,
2021-04-08 23:21:35 +00:00
Line : : CLK | Line : : RFSH | Line : : MREQ ,
Line : : RFSH | Line : : MREQ ,
2021-04-02 03:02:40 +00:00
Line : : CLK | Line : : RFSH ,
Line : : RFSH ,
2021-04-09 02:15:03 +00:00
Line : : CLK | Line : : RFSH ,
Line : : RFSH ,
2021-04-02 03:02:40 +00:00
} ;
return states ;
}
//
2021-04-02 11:34:06 +00:00
// Read cycle.
2021-04-02 03:02:40 +00:00
//
case Operation : : ReadStart : {
static constexpr uint8_t states [ ] = {
Line : : CLK ,
Line : : RD | Line : : MREQ ,
Line : : CLK | Line : : RD | Line : : MREQ ,
} ;
return states ;
}
case Operation : : ReadWait : {
static constexpr uint8_t states [ ] = {
Line : : MREQ | Line : : RD ,
Line : : CLK | Line : : MREQ | Line : : RD ,
Line : : MREQ | Line : : RD ,
Line : : CLK | Line : : MREQ | Line : : RD ,
Line : : MREQ | Line : : RD ,
Line : : CLK | Line : : MREQ | Line : : RD ,
} ;
return states ;
}
case Operation : : Read : {
static constexpr uint8_t states [ ] = {
Line : : MREQ | Line : : RD ,
Line : : CLK | Line : : MREQ | Line : : RD ,
0 ,
} ;
return states ;
}
2021-04-02 11:34:06 +00:00
//
// Write cycle.
//
case Operation : : WriteStart : {
static constexpr uint8_t states [ ] = {
Line : : CLK ,
Line : : MREQ ,
Line : : CLK | Line : : MREQ ,
} ;
return states ;
}
case Operation : : WriteWait : {
static constexpr uint8_t states [ ] = {
Line : : MREQ ,
Line : : CLK | Line : : MREQ ,
Line : : MREQ ,
Line : : CLK | Line : : MREQ ,
Line : : MREQ ,
Line : : CLK | Line : : MREQ ,
} ;
return states ;
}
case Operation : : Write : {
static constexpr uint8_t states [ ] = {
Line : : MREQ | Line : : WR ,
Line : : CLK | Line : : MREQ | Line : : WR ,
0 ,
} ;
return states ;
}
//
// Input cycle.
//
case Operation : : InputStart : {
static constexpr uint8_t states [ ] = {
Line : : CLK ,
0 ,
Line : : CLK | Line : : IOREQ | Line : : RD ,
} ;
return states ;
}
case Operation : : InputWait : {
static constexpr uint8_t states [ ] = {
Line : : IOREQ | Line : : RD ,
Line : : CLK | Line : : IOREQ | Line : : RD ,
} ;
return states ;
}
case Operation : : Input : {
static constexpr uint8_t states [ ] = {
Line : : IOREQ | Line : : RD ,
Line : : CLK | Line : : IOREQ | Line : : RD ,
0 ,
} ;
return states ;
}
//
// Output cycle.
//
case Operation : : OutputStart : {
static constexpr uint8_t states [ ] = {
Line : : CLK ,
0 ,
Line : : CLK | Line : : IOREQ | Line : : WR ,
} ;
return states ;
}
case Operation : : OutputWait : {
static constexpr uint8_t states [ ] = {
Line : : IOREQ | Line : : WR ,
Line : : CLK | Line : : IOREQ | Line : : WR ,
} ;
return states ;
}
case Operation : : Output : {
static constexpr uint8_t states [ ] = {
Line : : IOREQ | Line : : WR ,
Line : : CLK | Line : : IOREQ | Line : : WR ,
0 ,
} ;
return states ;
}
//
// TODO: Interrupt acknowledge.
//
//
// Bus acknowldge.
//
case Operation : : BusAcknowledge : {
static constexpr uint8_t states [ ] = {
Line : : CLK | Line : : BUSACK ,
Line : : BUSACK ,
} ;
return states ;
}
//
// Internal.
//
case Operation : : Internal : {
static constexpr uint8_t states [ ] = {
Line : : CLK , 0 ,
Line : : CLK , 0 ,
Line : : CLK , 0 ,
Line : : CLK , 0 ,
Line : : CLK , 0 ,
} ;
return states ;
}
2021-04-02 03:02:40 +00:00
default : break ;
}
2021-04-04 01:04:44 +00:00
return nullptr ;
2021-04-02 03:02:40 +00:00
}
2017-09-02 00:50:24 +00:00
PartialMachineCycle ( const PartialMachineCycle & rhs ) noexcept ;
PartialMachineCycle ( Operation operation , HalfCycles length , uint16_t * address , uint8_t * value , bool was_requested ) noexcept ;
PartialMachineCycle ( ) noexcept ;
2017-05-16 02:25:52 +00:00
} ;
2017-05-15 02:08:15 +00:00
/*!
2017-09-02 00:50:24 +00:00
A class providing empty implementations of the methods a Z80 uses to access the bus . To wire the Z80 to a bus ,
2019-04-19 21:44:52 +00:00
machines should subclass BusHandler and then declare a realisation of the Z80 template , supplying their bus
2017-09-02 00:50:24 +00:00
handler .
2017-05-15 02:08:15 +00:00
*/
2017-09-02 00:50:24 +00:00
class BusHandler {
2017-05-15 11:59:21 +00:00
public :
2017-05-15 11:55:53 +00:00
/*!
2017-09-02 00:50:24 +00:00
Announces that the Z80 has performed the partial machine cycle defined by @ c cycle .
2017-05-15 11:55:53 +00:00
2017-09-02 00:50:24 +00:00
@ returns The number of additional HalfCycles that passed in objective time while this Z80 operation was ongoing .
On an archetypal machine this will be HalfCycles ( 0 ) but some architectures may choose not to clock the Z80
during some periods or may impose wait states so predictably that it ' s more efficient just to add them
via this mechanism .
2017-05-15 11:55:53 +00:00
*/
2020-05-30 04:37:06 +00:00
HalfCycles perform_machine_cycle ( [[maybe_unused]] const PartialMachineCycle & cycle ) {
2017-07-26 23:42:00 +00:00
return HalfCycles ( 0 ) ;
2017-05-16 02:25:52 +00:00
}
2017-05-15 11:55:53 +00:00
/*!
2019-04-19 21:44:52 +00:00
Announces completion of all the cycles supplied to a . run_for request on the Z80 . Intended to allow
2017-09-02 00:50:24 +00:00
bus handlers to perform any deferred output work .
2017-05-15 11:55:53 +00:00
*/
2017-09-02 00:50:24 +00:00
void flush ( ) { }
} ;
2017-05-15 11:55:53 +00:00
2017-09-02 00:50:24 +00:00
# include "Implementation/Z80Storage.hpp"
2017-05-15 11:55:53 +00:00
2017-09-02 00:50:24 +00:00
/*!
A base class from which the Z80 descends ; separated for implementation reasons only .
*/
class ProcessorBase : public ProcessorStorage {
public :
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 .
*/
2020-05-21 03:34:26 +00:00
uint16_t get_value_of_register ( Register r ) const ;
2017-05-15 11:55:53 +00:00
/*!
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 .
*/
2017-09-02 00:50:24 +00:00
void set_value_of_register ( Register r , uint16_t value ) ;
2017-05-29 15:54:27 +00:00
/*!
Gets the value of the HALT output line .
*/
2020-05-21 03:34:26 +00:00
inline bool get_halt_line ( ) const ;
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-09-02 00:50:24 +00:00
inline void set_interrupt_line ( bool value , HalfCycles offset = 0 ) ;
2017-06-01 02:51:32 +00:00
2017-09-02 00:50:24 +00:00
/*!
Gets the value of the interrupt line .
*/
2020-05-21 03:34:26 +00:00
inline bool get_interrupt_line ( ) const ;
2017-06-23 00:11:19 +00:00
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-11-08 03:54:22 +00:00
2017-06-11 17:31:02 +00:00
@ param offset See discussion in set_interrupt_line .
2017-06-01 02:51:32 +00:00
*/
2018-02-24 23:14:38 +00:00
inline void set_non_maskable_interrupt_line ( bool value , HalfCycles offset = 0 ) ;
2017-06-23 00:11:19 +00:00
2017-06-01 02:51:32 +00:00
/*!
2017-09-02 00:50:24 +00:00
Gets the value of the non - maskable interrupt line .
2017-06-01 02:51:32 +00:00
*/
2020-05-21 03:34:26 +00:00
inline bool get_non_maskable_interrupt_line ( ) const ;
2017-06-23 00:11:19 +00:00
2017-06-02 01:40:08 +00:00
/*!
Sets the logical value of the reset line .
*/
2017-09-02 00:50:24 +00:00
inline void set_reset_line ( bool value ) ;
2017-06-02 01:40:08 +00:00
2020-03-01 00:58:25 +00:00
/*!
Gets whether the Z80 would reset at the next opportunity .
@ returns @ c true if the line is logically active ; @ c false otherwise .
*/
2020-05-21 03:34:26 +00:00
bool get_is_resetting ( ) const ;
2020-03-01 00:58:25 +00:00
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 .
*/
2017-09-02 00:50:24 +00:00
void reset_power_on ( ) ;
2020-02-25 04:31:42 +00:00
/*!
@ returns @ c true if the Z80 is currently beginning to fetch a new instruction ; @ c false otherwise .
This is not a speedy operation .
*/
2020-05-21 03:34:26 +00:00
bool is_starting_new_instruction ( ) const ;
2017-09-02 00:50:24 +00:00
} ;
/*!
@ abstact Template providing emulation of a Z80 processor .
@ discussion Users should provide as the first template parameter a subclass of CPU : : Z80 : : BusHandler ; the Z80
will announce its activity via the bus handler , which is responsible for marrying it to a bus . Users
can also nominate whether the processor includes support for the bus request and / or wait lines . Declining to
support either can produce a minor runtime performance improvement .
*/
template < class T , bool uses_bus_request , bool uses_wait_line > class Processor : public ProcessorBase {
public :
Processor ( T & bus_handler ) ;
/*!
Runs the Z80 for a supplied number of cycles .
@ discussion Subclasses must implement @ c perform_machine_cycle ( const PartialMachineCycle & cycle ) .
If it is a read operation then @ c value will be seeded with the value 0xff .
@ param cycles The number of cycles to run for .
*/
void run_for ( const HalfCycles cycles ) ;
2017-06-02 02:31:04 +00:00
2017-06-22 00:32:08 +00:00
/*!
2017-09-02 00:50:24 +00:00
Sets the logical value of the bus request line , having asserted that this Z80 supports the bus request line .
2017-06-22 00:32:08 +00:00
*/
2017-09-02 00:50:24 +00:00
void set_bus_request_line ( bool value ) ;
2017-06-22 00:32:08 +00:00
2017-09-02 00:50:24 +00:00
/*!
Gets the logical value of the bus request line .
*/
2020-05-21 03:34:26 +00:00
bool get_bus_request_line ( ) const ;
2017-09-02 00:50:24 +00:00
/*!
Sets the logical value of the wait line , having asserted that this Z80 supports the wait line .
*/
void set_wait_line ( bool value ) ;
/*!
Gets the logical value of the bus request line .
*/
2020-05-21 03:34:26 +00:00
bool get_wait_line ( ) const ;
2017-09-02 00:50:24 +00:00
private :
T & bus_handler_ ;
void assemble_page ( InstructionPage & target , InstructionTable & table , bool add_offsets ) ;
void copy_program ( const MicroOp * source , std : : vector < MicroOp > & destination ) ;
2017-05-14 21:46:41 +00:00
} ;
2017-09-02 00:50:24 +00:00
# include "Implementation/Z80Implementation.hpp"
2017-05-15 02:15:16 +00:00
}
2017-05-14 21:46:41 +00:00
}
# endif /* Z80_hpp */