v0.2-alpha
This commit is contained in:
672
API/Z80.h
Normal file
672
API/Z80.h
Normal file
@@ -0,0 +1,672 @@
|
||||
/* Z80 API
|
||||
______ ______ ______
|
||||
/\___ \/\ __ \\ __ \
|
||||
____ \/__/ /\_\ __ \\ \/\ \ ________________________________________________
|
||||
| /\_____\\_____\\_____\ |
|
||||
| Zilog \/_____//_____//_____/ CPU Emulator |
|
||||
| Copyright (C) 1999-2022 Manuel Sainz de Baranda y Goñi. |
|
||||
| |
|
||||
| This emulator is free software: you can redistribute it and/or modify it |
|
||||
| under the terms of the GNU Lesser General Public License as published by |
|
||||
| the Free Software Foundation, either version 3 of the License, or (at your |
|
||||
| option) any later version. |
|
||||
| |
|
||||
| This emulator is distributed in the hope that it will be useful, but |
|
||||
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
|
||||
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
|
||||
| License for more details. |
|
||||
| |
|
||||
| You should have received a copy of the GNU Lesser General Public License |
|
||||
| along with this emulator. If not, see <http://www.gnu.org/licenses/>. |
|
||||
| |
|
||||
'=============================================================================*/
|
||||
|
||||
#ifndef Z80_H
|
||||
|
||||
/** @file Z80.h
|
||||
* @brief Zilog Z80 CPU emulator.
|
||||
*
|
||||
* @details The Z80 library implements a fast, small and accurate emulator
|
||||
* of the Zilog Z80. It emulates all that is known to date about this CPU,
|
||||
* including the undocumented flags and instructions, MEMPTR, Q, and the
|
||||
* special RESET.
|
||||
*
|
||||
* @version 0.2
|
||||
* @date 2022-05-29
|
||||
* @author Manuel Sainz de Baranda y Goñi. */
|
||||
|
||||
#ifdef Z80_DEPENDENCIES_HEADER
|
||||
# define Z80_H
|
||||
# include Z80_DEPENDENCIES_HEADER
|
||||
# undef Z80_H
|
||||
#else
|
||||
# include <Z/macros/language.h>
|
||||
# include <Z/types/bitwise.h>
|
||||
#endif
|
||||
|
||||
#ifndef Z80_API
|
||||
# if defined(Z80_STATIC) || defined(__DOXYGEN__)
|
||||
# define Z80_API
|
||||
# else
|
||||
# define Z80_API Z_API_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** @brief The major version number of the Z80 library. */
|
||||
|
||||
#define Z80_LIBRARY_VERSION_MAJOR 0
|
||||
|
||||
/** @brief The minor version number of the Z80 library. */
|
||||
|
||||
#define Z80_LIBRARY_VERSION_MINOR 2
|
||||
|
||||
/** @brief The micro version number of the Z80 library. */
|
||||
|
||||
#define Z80_LIBRARY_VERSION_MICRO 0
|
||||
|
||||
/** @brief A string literal with the version number of the Z80 library. */
|
||||
|
||||
#define Z80_LIBRARY_VERSION_STRING "0.2"
|
||||
|
||||
/** @brief The maximum number of clock cycles that the @ref z80_run and @ref
|
||||
* z80_execute functions can emulate per call. */
|
||||
|
||||
#define Z80_CYCLE_LIMIT (Z_USIZE_MAXIMUM - Z_USIZE(30))
|
||||
#define Z80_CYCLES_PER_RESET 5
|
||||
|
||||
/** @brief The 8-bit value interpreted as a hook by the Z80 emulator, which
|
||||
* corresponds to the <tt>ld h,h</tt> opcode in the Z80 ISA. */
|
||||
|
||||
#define Z80_HOOK Z_UINT8(0x64)
|
||||
|
||||
/** @brief Defines a pointer to a callback function used by the Z80 emulator
|
||||
* to read a byte.
|
||||
*
|
||||
* @param context The value of the @ref Z80.context member variable of the
|
||||
* object invoking the callback.
|
||||
* @param address The memory address or I/O port to read from.
|
||||
* @return The byte read. */
|
||||
|
||||
typedef zuint8 (* Z80Read)(void *context, zuint16 address);
|
||||
|
||||
/** @brief Defines a pointer to a callback function used by the Z80 emulator
|
||||
* to write a byte.
|
||||
*
|
||||
* @param context The value of the @ref Z80.context member variable of the
|
||||
* object invoking the callback.
|
||||
* @param address The memory address or I/O port to write to.
|
||||
* @param value The byte to write. */
|
||||
|
||||
typedef void (* Z80Write)(void *context, zuint16 address, zuint8 value);
|
||||
|
||||
/** @brief Defines a pointer to a callback function used by the Z80 emulator
|
||||
* to notify the change of state of the HALT line.
|
||||
*
|
||||
* @param context The value of the @ref Z80.context member variable of the
|
||||
* object invoking the callback.
|
||||
* @param state
|
||||
* @c TRUE if the HALT line goes low (the CPU enters the HALT state);
|
||||
* @c FALSE if the HALT line goes high (the CPU exits the HALT state). */
|
||||
|
||||
typedef void (* Z80HALT)(void *context, zboolean state);
|
||||
|
||||
/** @brief Defines a pointer to a callback function used by the Z80 emulator
|
||||
* to notify an event.
|
||||
*
|
||||
* @param context The value of the @ref Z80.context member variable of the
|
||||
* object invoking the callback. */
|
||||
|
||||
typedef void (* Z80Notify)(void *context);
|
||||
|
||||
|
||||
typedef zusize (* Z80Reset)(void *context, zuint16 address);
|
||||
|
||||
/** @struct Z80 Z80.h
|
||||
*
|
||||
* @brief A Z80 CPU emulator.
|
||||
*
|
||||
* @details @c Z80 contains the state of an emulated Z80 CPU
|
||||
* and the callback pointers needed to interconnect it with the external logic.
|
||||
* Before using an object of this type, some of its members
|
||||
* must be initialized. In particular the following (in alphabetical order):
|
||||
* @ref Z80.context, @ref Z80.fetch, @ref Z80.fetch_opcode, @ref Z80.halt_nop,
|
||||
* @ref Z80.in, @ref Z80.out, @ref Z80.read, @ref Z80.write, @ref Z80.read_bus,
|
||||
* @ref Z80.halt, @ref Z80.inta, @ref Z80.reti @ref Z80.hook and @ref
|
||||
* Z80.model.
|
||||
*
|
||||
* Callback | Mandatory
|
||||
* --------------------- | ---------
|
||||
* @ref Z80.fetch_opcode | yes
|
||||
* @ref Z80.fetch | yes
|
||||
* @ref Z80.read | yes
|
||||
* @ref Z80.write | yes
|
||||
* @ref Z80.in | yes
|
||||
* @ref Z80.out | yes
|
||||
* @ref Z80.halt | no
|
||||
* @ref Z80.halt_nop | no
|
||||
* @ref Z80.nmia | yes
|
||||
* @ref Z80.inta | yes
|
||||
* @ref Z80.int_fetch | no
|
||||
* @ref Z80.ld_i_a | no
|
||||
* @ref Z80.ld_r_a | no
|
||||
* @ref Z80.reti | no
|
||||
* @ref Z80.retn | no
|
||||
* @ref Z80.hook | no
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
|
||||
/** @brief Clock cycle counter.
|
||||
*
|
||||
* This clock cycle counter is updated as the emulator executes
|
||||
* instructions and responds to different signals. */
|
||||
|
||||
zusize cycles;
|
||||
|
||||
/** @brief Maximum number of clock cycles to be executed in the current
|
||||
* invokation of @ref z80_run. */
|
||||
|
||||
zusize cycle_limit;
|
||||
|
||||
/** @brief Pointer passed as the first argument to the callbacks.
|
||||
*
|
||||
* @details This member variable can be used to maintain a reference to
|
||||
* the object or context to which the Z80 emulator object belongs. */
|
||||
|
||||
void *context;
|
||||
|
||||
/** @brief Callback used to perform opcode fetch operations.
|
||||
*
|
||||
* @details This type of operation is used by the CPU to read the first
|
||||
* byte of an instruction or an instruction prefix. A succession of
|
||||
* @c DDh and/or @c FDh prefixes will cause multiple consecutive such
|
||||
* operations until an opcode is fetched. In the instructions with
|
||||
* double prefix (@c CBDDh or @c CBFDh), only the prefixes are fetched
|
||||
* with this operation, the remaining 2 bytes are accessed by using
|
||||
* normal memory read operations.
|
||||
*
|
||||
* The emulator always incrementents the R register @b before calling
|
||||
* this callback, but the value of its most-significant bit (R7) is not
|
||||
* preserved for speed optimization reasons. In the rare case that the
|
||||
* callback function needs to access that register, it must take R7
|
||||
* from the 7th bit of the @c r7 member variable.
|
||||
*
|
||||
* Implementations that do not distinguish between opcode fetch and
|
||||
* memory read operations should use the same callback function for
|
||||
* both.
|
||||
*
|
||||
* @attention This callback is mandatory, initializing it to @c Z_NULL
|
||||
* will cause the emulator to crash. */
|
||||
|
||||
Z80Read fetch_opcode;
|
||||
|
||||
/** @brief Callback used to perform memory read operations on instruction data.
|
||||
*
|
||||
* @attention This callback is mandatory, initializing it to @c Z_NULL
|
||||
* will cause the emulator to crash. */
|
||||
|
||||
Z80Read fetch;
|
||||
|
||||
/** @brief Callback used to perform a memory read operation.
|
||||
*
|
||||
* @details This type of operation is used by the CPU to read a byte
|
||||
* from a memory address.
|
||||
*
|
||||
* @attention This callback is mandatory, initializing it to @c Z_NULL
|
||||
* will cause the emulator to crash. */
|
||||
|
||||
Z80Read read;
|
||||
|
||||
/** @brief Callback used to perform a memory write operation.
|
||||
*
|
||||
* @details This type of operation is used by the CPU to write a byte
|
||||
* to a memory address.
|
||||
*
|
||||
* @attention This callback is mandatory, initializing it to @c Z_NULL
|
||||
* will cause the emulator to crash. */
|
||||
|
||||
Z80Write write;
|
||||
|
||||
/** @brief Callback used to perform an I/O read operation.
|
||||
*
|
||||
* @details This type of operation is used by the CPU to read a byte
|
||||
* from an I/O port.
|
||||
*
|
||||
* @attention This callback is mandatory, initializing it to @c Z_NULL
|
||||
* will cause the emulator to crash. */
|
||||
|
||||
Z80Read in;
|
||||
|
||||
/** @brief Callback used to perform an I/O write operation.
|
||||
*
|
||||
* @details This type of operation is used by the CPU to write a byte
|
||||
* to an I/O port.
|
||||
*
|
||||
* @attention This callback is mandatory, initializing it to @c Z_NULL
|
||||
* will cause the emulator to crash. */
|
||||
|
||||
Z80Write out;
|
||||
|
||||
/** @brief Callback used to notify that the state of the HALT output
|
||||
* line has changed.
|
||||
*
|
||||
* @details The @c HALT instruction halts the CPU by not incrementing
|
||||
* the PC register, so the instruction is re-executed until an
|
||||
* interrupt is accepted. Only then the PC register is incremented
|
||||
* again and the next instruction is executed. The HALT output line is
|
||||
* active (low) during the HALT state.
|
||||
*
|
||||
* The emulator invokes this callback after changing the value of the
|
||||
* @c halt_line member variable and, when exiting the HALT state,
|
||||
* immediately before executing the interrupt response.
|
||||
*
|
||||
* @attention This callback is optional and @b must be initialized to
|
||||
* @c Z_NULL if not used. */
|
||||
|
||||
Z80HALT halt;
|
||||
|
||||
/** @brief Callback invoked... */
|
||||
|
||||
Z80Read nop;
|
||||
|
||||
/** @brief Callback invoked... */
|
||||
|
||||
Z80Read nmia;
|
||||
|
||||
/** @brief Callback used to notify a maskable interrupt acknowledge
|
||||
* (INTA).
|
||||
*
|
||||
* @details When a maskable interrupt (INT) is accepted, the CPU
|
||||
* generates a special M1 cycle to indicate that the interrupting I/O
|
||||
* device can write to the data bus. Two wait clock cycles are added
|
||||
* to this M1 cycle, allowing sufficient time to identify which device
|
||||
* must insert the data.
|
||||
*
|
||||
* The emulator invokes this callback during the execution of the INT
|
||||
* response. It should be used to identify and prepare the context of
|
||||
* the interrupting device, so that subsequent invocations of the @c
|
||||
* read_bus callback can read the interrupt response vector or the
|
||||
* instruction to be executed.
|
||||
*
|
||||
* @note This callback is optional and @b must be initialized to @c
|
||||
* Z_NULL if not used.
|
||||
|
||||
* @details When a maskable interrupt (INT) is accepted, the CPU reads
|
||||
* the interrupt response data provided by the interrupting device via
|
||||
* the data bus. Usually only one data bus read operation is performed
|
||||
* during the INT acknowledge cycle, but when the CPU responds to the
|
||||
* interrupt in mode 0, it will perform as many such operations as
|
||||
* needed to read the byte sequence of the instruction to be executed.
|
||||
*
|
||||
* The emulator invokes this callback during the execution of the INT
|
||||
* response. The return value is ignored in interrupt mode 1. In mode
|
||||
* 0, the emulator invokes this callback as many times as needed to
|
||||
* read a complete instruction. Illegal instructions and instruction
|
||||
* prefix sequences are fully supported.
|
||||
*
|
||||
* @attention This callback is optional and @b must be initialized to
|
||||
* @c Z_NULL if not used, in which case the emulator will assume that
|
||||
* the byte read from the data bus is always @c FFh. */
|
||||
|
||||
Z80Read inta;
|
||||
|
||||
/** @brief Callback used to perform a data bus read operation during a
|
||||
* maskable interrupt (INT) response.
|
||||
**/
|
||||
|
||||
Z80Read int_fetch;
|
||||
|
||||
Z80Reset reset;
|
||||
|
||||
/** @brief Callback invoked before executing the <tt>ld i,a</tt>
|
||||
* instruction. */
|
||||
|
||||
Z80Notify ld_i_a;
|
||||
|
||||
/** @brief Callback invoked before executing the <tt>ld r,a</tt>
|
||||
* instruction.
|
||||
*
|
||||
* @attention This callback is optional and @b must be initialized to
|
||||
* @c Z_NULL if not used. */
|
||||
|
||||
Z80Notify ld_r_a;
|
||||
|
||||
/** @brief Callback invoked before executing the @c reti instruction.
|
||||
*
|
||||
* The Z80 Counter/Timer Circuit (CTC) detects the two-byte @c reti
|
||||
* opcode when it is fetched by the CPU. This instruction is used to
|
||||
* return from an ISR and signal that the computer should initialize
|
||||
* the daisy-chain enable lines for control of nested priority
|
||||
* interrupt handling.
|
||||
*
|
||||
* Although the Z80 CTC is not part of the CPU, the emulator can signal
|
||||
* the execution of a @c reti instruction by invoking this callback in
|
||||
* order to simplify the emulation of machines that use daisy-chain
|
||||
* interrupt servicing, thus avoiding having to implement the detection
|
||||
* of this instruction externally, which is not optimal and would cause
|
||||
* a speed penalty.
|
||||
*
|
||||
* @attention This callback is optional and @b must be initialized to
|
||||
* @c Z_NULL if not used. */
|
||||
|
||||
Z80Notify reti;
|
||||
|
||||
/** @brief Callback invoked before executing the @c retn instruction.
|
||||
|
||||
* @attention This callback is optional and @b must be initialized to
|
||||
* @c Z_NULL if not used. */
|
||||
|
||||
Z80Notify retn;
|
||||
|
||||
/** @brief Callback invoked...
|
||||
*
|
||||
* @attention This callback is optional and @b must be initialized to
|
||||
* @c Z_NULL if not used. */
|
||||
|
||||
Z80Read hook;
|
||||
|
||||
/** @brief Temporary storage used for instruction fetch. */
|
||||
|
||||
ZInt32 data;
|
||||
|
||||
/** @brief Temporay IX/IY register. */
|
||||
|
||||
ZInt16 xy;
|
||||
|
||||
ZInt16 memptr; /**< @brief MEMPTR register. */
|
||||
ZInt16 pc; /**< @brief PC register. */
|
||||
ZInt16 sp; /**< @brief SP register. */
|
||||
ZInt16 ix; /**< @brief IX register. */
|
||||
ZInt16 iy; /**< @brief IY register. */
|
||||
ZInt16 af; /**< @brief AF register. */
|
||||
ZInt16 bc; /**< @brief BC register. */
|
||||
ZInt16 de; /**< @brief DE register. */
|
||||
ZInt16 hl; /**< @brief HL register. */
|
||||
ZInt16 af_; /**< @brief AF' register. */
|
||||
ZInt16 bc_; /**< @brief BC' register. */
|
||||
ZInt16 de_; /**< @brief DE' register. */
|
||||
ZInt16 hl_; /**< @brief HL' register. */
|
||||
zuint8 r; /**< @brief R register. */
|
||||
zuint8 i; /**< @brief I register. */
|
||||
|
||||
/** @brief The most significant bit of the R register (R7).
|
||||
*
|
||||
* @details The R register is incremented during each M1 cycle, but its
|
||||
* most significant bit (R7) is not affected. For optimization reasons,
|
||||
* this behavior is not emulated in every M1 cycle, so successive
|
||||
* increments of R corrupt R7. @c z80_run keeps the value of R7 in this
|
||||
* temporary member variable while executing operations and copies it
|
||||
* back to R before returning. Since this variable is a snapshot of the
|
||||
* R register at a given time, the value of the 7 least significant
|
||||
* bits must be considered garbage. */
|
||||
|
||||
zuint8 r7;
|
||||
|
||||
/** @brief Maskable interrup mode.
|
||||
*
|
||||
* @details Contains the number of the maskable interrupt mode in use:
|
||||
* `0`, `1` or `2`. */
|
||||
|
||||
zuint8 im;
|
||||
|
||||
/** @brief Number of signals pending to be processed. */
|
||||
|
||||
zuint8 request;
|
||||
|
||||
/** @brief TODO */
|
||||
|
||||
zuint8 resume;
|
||||
|
||||
zuint8 iff1; /**< @brief Interrupt flip-flop 1 (IFF1). */
|
||||
zuint8 iff2; /**< @brief Interrupt flip-flop 2 (IFF2). */
|
||||
zuint8 q; /**< @brief Q register. */
|
||||
|
||||
/** @brief CPU model.
|
||||
*
|
||||
* @details todo... */
|
||||
|
||||
zuint8 options;
|
||||
|
||||
/** @brief State of the INT line.
|
||||
*
|
||||
* @details Contains @c TRUE if the INT line is active, or @c FALSE
|
||||
* otherwise. */
|
||||
|
||||
zuint8 int_line;
|
||||
|
||||
/** @brief State of the HALT line.
|
||||
*
|
||||
* @details Contains @c TRUE if the HALT line is active, or @c FALSE
|
||||
* otherwise. The emulator always modifies this variable before
|
||||
* invoking the @ref Z80.halt callback. */
|
||||
|
||||
zuint8 halt_line;
|
||||
} Z80;
|
||||
|
||||
#define Z80_MEMPTR(self) (self).memptr.uint16_value
|
||||
#define Z80_MEMPTRH(self) (self).memptr.uint8_values.at_1
|
||||
#define Z80_MEMPTRL(self) (self).memptr.uint8_values.at_0
|
||||
#define Z80_PC(self) (self).pc.uint16_value
|
||||
#define Z80_SP(self) (self).sp.uint16_value
|
||||
#define Z80_XY(self) (self).xy.uint16_value
|
||||
#define Z80_IX(self) (self).ix.uint16_value
|
||||
#define Z80_IY(self) (self).iy.uint16_value
|
||||
#define Z80_AF(self) (self).af.uint16_value
|
||||
#define Z80_BC(self) (self).bc.uint16_value
|
||||
#define Z80_DE(self) (self).de.uint16_value
|
||||
#define Z80_HL(self) (self).hl.uint16_value
|
||||
#define Z80_AF_(self) (self).af_.uint16_value
|
||||
#define Z80_BC_(self) (self).bc_.uint16_value
|
||||
#define Z80_DE_(self) (self).de_.uint16_value
|
||||
#define Z80_HL_(self) (self).hl_.uint16_value
|
||||
#define Z80_PCH(self) (self).pc.uint8_values.at_1
|
||||
#define Z80_PCL(self) (self).pc.uint8_values.at_0
|
||||
#define Z80_SPH(self) (self).sp.uint8_values.at_1
|
||||
#define Z80_SPL(self) (self).sp.uint8_values.at_0
|
||||
#define Z80_XYH(self) (self).xy.uint8_values.at_1
|
||||
#define Z80_XYL(self) (self).xy.uint8_values.at_0
|
||||
#define Z80_IXH(self) (self).ix.uint8_values.at_1
|
||||
#define Z80_IXL(self) (self).ix.uint8_values.at_0
|
||||
#define Z80_IYH(self) (self).iy.uint8_values.at_1
|
||||
#define Z80_IYL(self) (self).iy.uint8_values.at_0
|
||||
#define Z80_A(self) (self).af.uint8_values.at_1
|
||||
#define Z80_F(self) (self).af.uint8_values.at_0
|
||||
#define Z80_B(self) (self).bc.uint8_values.at_1
|
||||
#define Z80_C(self) (self).bc.uint8_values.at_0
|
||||
#define Z80_D(self) (self).de.uint8_values.at_1
|
||||
#define Z80_E(self) (self).de.uint8_values.at_0
|
||||
#define Z80_H(self) (self).hl.uint8_values.at_1
|
||||
#define Z80_L(self) (self).hl.uint8_values.at_0
|
||||
#define Z80_A_(self) (self).af_.uint8_values.at_1
|
||||
#define Z80_F_(self) (self).af_.uint8_values.at_0
|
||||
#define Z80_B_(self) (self).bc_.uint8_values.at_1
|
||||
#define Z80_C_(self) (self).bc_.uint8_values.at_0
|
||||
#define Z80_D_(self) (self).de_.uint8_values.at_1
|
||||
#define Z80_E_(self) (self).de_.uint8_values.at_0
|
||||
#define Z80_H_(self) (self).hl_.uint8_values.at_1
|
||||
#define Z80_L_(self) (self).hl_.uint8_values.at_0
|
||||
|
||||
#define Z80_SF 128
|
||||
#define Z80_ZF 64
|
||||
#define Z80_YF 32
|
||||
#define Z80_HF 16
|
||||
#define Z80_XF 8
|
||||
#define Z80_PF 4
|
||||
#define Z80_NF 2
|
||||
#define Z80_CF 1
|
||||
|
||||
#define Z80_OPTION_LD_A_IR_BUG 1
|
||||
#define Z80_OPTION_OUT_VC_255 2
|
||||
#define Z80_OPTION_XQ 8
|
||||
#define Z80_OPTION_HALT_SKIP 16
|
||||
#define Z80_OPTION_YQ 32
|
||||
|
||||
/** @brief Zilog Z80 NMOS version. */
|
||||
#define Z80_MODEL_ZILOG_NMOS \
|
||||
(Z80_OPTION_LD_A_IR_BUG | Z80_OPTION_XQ | Z80_OPTION_YQ)
|
||||
|
||||
/** @brief Zilog Z80 CMOS version. */
|
||||
#define Z80_MODEL_ZILOG_CMOS \
|
||||
(Z80_OPTION_OUT_VC_255 | Z80_OPTION_XQ | Z80_OPTION_YQ)
|
||||
|
||||
#define Z80_MODEL_NEC_NMOS \
|
||||
Z80_OPTION_LD_A_IR_BUG
|
||||
|
||||
#define Z80_MODEL_ST_CMOS \
|
||||
(Z80_OPTION_LD_A_IR_BUG | Z80_OPTION_YQ)
|
||||
|
||||
|
||||
#define Z80_REQUEST_RESET 3
|
||||
#define Z80_REQUEST_REJECT_NMI 4
|
||||
#define Z80_REQUEST_NMI 8 /**< NMI pending. */
|
||||
#define Z80_REQUEST_CLEAR_PC 16
|
||||
#define Z80_REQUEST_SPECIAL_RESET 32
|
||||
#define Z80_REQUEST_INT 64 /**< INT pending. */
|
||||
#define Z80_REQUEST_ANY_RESET 35
|
||||
|
||||
|
||||
#define Z80_REQUEST_RESET 3
|
||||
#define Z80_REQUEST_INTERRUPT 5
|
||||
|
||||
#define Z80_RESUME_HALT 1 /**< Resume the execution of the HALT state. */
|
||||
#define Z80_RESUME_XY 2 /**< Resume at opcode decode stage in INT mode 0. */
|
||||
#define Z80_RESUME_IM0_XY 3 /**< Resume at opcode decode stage. */
|
||||
#define Z80_RESUME_NORMAL_RESET 4
|
||||
#define Z80_RESUME_SPECIAL_RESET_XY 5
|
||||
#define Z80_RESUME_SPECIAL_RESET_NOP 6
|
||||
|
||||
Z_EXTERN_C_BEGIN
|
||||
|
||||
/** @brief Sets the power state of a Z80 emulator.
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @param state
|
||||
* @c TRUE = power ON;
|
||||
* @c FALSE = power OFF. */
|
||||
|
||||
Z80_API void z80_power(Z80 *self, zboolean state);
|
||||
|
||||
/** @brief Performs a normal RESET on a Z80 emulator.
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called. */
|
||||
|
||||
Z80_API void z80_instant_reset(Z80 *self);
|
||||
|
||||
/** @brief Sends a normal RESET signal to a Z80 emulator.
|
||||
*
|
||||
* @details todo
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called. */
|
||||
|
||||
Z80_API void z80_reset(Z80 *self);
|
||||
|
||||
/** @brief Sends a special RESET signal to a Z80 emulator.
|
||||
*
|
||||
* @details todo
|
||||
*
|
||||
* @sa
|
||||
* - http://www.primrosebank.net/computers/z80/z80_special_reset.htm
|
||||
* - US Patent 4486827
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called. */
|
||||
|
||||
Z80_API void z80_special_reset(Z80 *self);
|
||||
|
||||
/** @brief Sets the state of the INT line of a Z80 emulator.
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @param state
|
||||
* @c TRUE = ON (line low);
|
||||
* @c FALSE = OFF (line high). */
|
||||
|
||||
Z80_API void z80_int(Z80 *self, zboolean state);
|
||||
|
||||
/** @brief Sends a NMI signal to a Z80 emulator.
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called. */
|
||||
|
||||
Z80_API void z80_nmi(Z80 *self);
|
||||
|
||||
Z80_API void z80_busreq(Z80 *self, zboolean state);
|
||||
|
||||
/** @brief Runs a Z80 emulator for a given number of clock @p cycles, executing
|
||||
* only instructions without responding to signals.
|
||||
*
|
||||
* @details Given the fact that one Z80 instruction takes between 4 and 23
|
||||
* cycles to be executed, it is not always possible to run the CPU the exact
|
||||
* number of @p cycles specfified.
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @param cycles Number of clock cycles to be emulated.
|
||||
* @return The actual number of clock cycles emulated. */
|
||||
|
||||
Z80_API zusize z80_execute(Z80 *self, zusize cycles);
|
||||
|
||||
/** @brief Runs a Z80 emulator for a given number of clock @p cycles.
|
||||
*
|
||||
* @details Given the fact that one Z80 instruction takes between 4 and 23
|
||||
* cycles to be executed, it is not always possible to run the CPU the exact
|
||||
* number of @p cycles specfified.
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @param cycles Number of clock cycles to be emulated.
|
||||
* @return The actual number of clock cycles emulated. */
|
||||
|
||||
Z80_API zusize z80_run(Z80 *self, zusize cycles);
|
||||
|
||||
|
||||
/** @brief Obtains the refresh address of the M1 cycle being executed by a Z80
|
||||
* emulator.
|
||||
*
|
||||
* @details todo
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @return todo */
|
||||
|
||||
static Z_INLINE zuint16 z80_refresh_address(Z80 *self)
|
||||
{return ((zuint16)self->i << 8) | ((self->r - 1) & 127);}
|
||||
|
||||
|
||||
/** @brief Computes the clock cycle, relative to the start of the instruction,
|
||||
* at which the I/O read M-cycle being executed by a Z80 emulator begins.
|
||||
*
|
||||
* @details todo
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @return todo */
|
||||
|
||||
static Z_INLINE zuint8 z80_in_cycle(Z80 *self)
|
||||
{
|
||||
return self->data.uint8_array[0] == 0xDB
|
||||
? /* in a,(BYTE) : 4+3 */
|
||||
7
|
||||
: /* in J,(c) / in (c) : 4+4 */
|
||||
8
|
||||
+ /* ini / ind / inir / indr : 4+5 */
|
||||
(self->data.uint8_array[1] >> 7);
|
||||
}
|
||||
|
||||
|
||||
/** @brief Computes the clock cycle, relative to the start of the instruction,
|
||||
* at which the I/O write M-cycle being executed by a Z80 emulator begins.
|
||||
*
|
||||
* @details todo
|
||||
*
|
||||
* @param self Pointer to the object on which the function is called.
|
||||
* @return todo */
|
||||
|
||||
static Z_INLINE zuint8 z80_out_cycle(Z80 *self)
|
||||
{
|
||||
return self->data.uint8_array[0] == 0xD3
|
||||
? /* out (BYTE),a : 4+3 */
|
||||
7
|
||||
: /* out (c),J / out (c),0 : 4+4 */
|
||||
8
|
||||
+ /* outi / outd / otir / otdr : 4+5+3 */
|
||||
((self->data.uint8_array[1] >> 7) << 2);
|
||||
}
|
||||
|
||||
|
||||
Z_EXTERN_C_END
|
||||
|
||||
#endif /* Z80_H */
|
||||
@@ -1,201 +0,0 @@
|
||||
/* ______ ______ ______
|
||||
/\___ \ /\ __ \/\ __ \
|
||||
\/__/ /_\ \ __ \ \ \/\ \
|
||||
/\_____\ \_____\ \_____\
|
||||
Zilog \/_____/\/_____/\/_____/ CPU Emulator ----------------------------------
|
||||
Copyright (C) 1999-2018 Manuel Sainz de Baranda y Goñi.
|
||||
|
||||
This emulator is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This emulator is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
#ifndef _emulation_CPU_Z80_H_
|
||||
#define _emulation_CPU_Z80_H_
|
||||
|
||||
#ifdef CPU_Z80_DEPENDENCIES_H
|
||||
# include CPU_Z80_DEPENDENCIES_H
|
||||
#else
|
||||
# include <Z/hardware/CPU/architecture/Z80.h>
|
||||
#endif
|
||||
|
||||
/** Z80 emulator instance.
|
||||
* @details This structure contains the state of the emulated CPU and callback
|
||||
* pointers necessary to interconnect the emulator with external logic. There
|
||||
* is no constructor function, so, before using an object of this type, some
|
||||
* of its members must be initialized, in particular the following:
|
||||
* @c context, @c read, @c write, @c in, @c out, @c int_data and @c halt. */
|
||||
|
||||
typedef struct {
|
||||
|
||||
/** Number of cycles executed in the current call to @c z80_run.
|
||||
* @details @c z80run sets this variable to @c 0 before starting
|
||||
* to execute instructions and its value persists after returning.
|
||||
* The callbacks can use this variable to know during what cycle
|
||||
* they are being called. */
|
||||
|
||||
zusize cycles;
|
||||
|
||||
/** The value used as the first argument when calling a callback.
|
||||
* @details This variable should be initialized before using the
|
||||
* emulator and can be used to reference the context/instance of
|
||||
* the machine being emulated. */
|
||||
|
||||
void *context;
|
||||
|
||||
/** Callback: Called when the CPU needs to read 8 bits from memory.
|
||||
* @param context The value of the member @c context.
|
||||
* @param address The memory address to read from.
|
||||
* @return The 8 bits read from memory. */
|
||||
|
||||
zuint8 (* read)(void *context, zuint16 address);
|
||||
|
||||
/** Callback: Called when the CPU needs to write 8 bits to memory.
|
||||
* @param context The value of the member @c context.
|
||||
* @param address The memory address to write to.
|
||||
* @param value The value to write. */
|
||||
|
||||
void (* write)(void *context, zuint16 address, zuint8 value);
|
||||
|
||||
/** Callback: Called when the CPU needs to read 8 bits from an I/O port.
|
||||
* @param context The value of the member @c context.
|
||||
* @param port The number of the I/O port to read from.
|
||||
* @return The 8 bits read from the I/O port. */
|
||||
|
||||
zuint8 (* in)(void *context, zuint16 port);
|
||||
|
||||
/** Callback: Called when the CPU needs to write 8 bits to an I/O port.
|
||||
* @param context The value of the member @c context.
|
||||
* @param port The number of the I/O port to write to.
|
||||
* @param value The value to write. */
|
||||
|
||||
void (* out)(void *context, zuint16 port, zuint8 value);
|
||||
|
||||
/** Callback: Called when the CPU needs to read one instruction from
|
||||
* the data bus to service a maskable interrupt (INT) in mode 0.
|
||||
* @param context The value of the member @c context.
|
||||
* @return A 32-bit value containing the bytes of one instruction. The
|
||||
* instruction must begin at the most significant byte (big endian). */
|
||||
|
||||
zuint32 (* int_data)(void *context);
|
||||
|
||||
/** Callback: Called when the CPU enters or exits the halt state.
|
||||
* @param context The value of the member @c context.
|
||||
* @param state @c TRUE if halted; @c FALSE otherwise.
|
||||
* @note This callback is optional and must be set to @c NULL if not
|
||||
* used. */
|
||||
|
||||
void (* halt)(void *context, zboolean state);
|
||||
|
||||
/** CPU registers and internal bits.
|
||||
* @details It contains the state of the registers, as well as the
|
||||
* interrupt flip-flops, variables related to interrupts and other
|
||||
* necessary flags. This is what a debugger should use as its data
|
||||
* source. */
|
||||
|
||||
ZZ80State state;
|
||||
|
||||
/** Backup of the 7th bit of the R register.
|
||||
* @details The value of the R register is incremented as instructions
|
||||
* are executed, but its most significant bit remains unchanged. For
|
||||
* optimization reasons, this bit is saved at the beginning of the
|
||||
* execution of @c z80_run and restored before returning. If an
|
||||
* instruction directly affects the R register, this variable is also
|
||||
* updated accordingly. */
|
||||
|
||||
zuint8 r7;
|
||||
|
||||
/** Temporay IX/IY register for instructions with @c DDh/FDh prefix.
|
||||
* @details Since instructions with prefix @c DDh and @c FDh behave
|
||||
* similarly, differing only in the use of register IX or IY, for
|
||||
* reasons of size optimization, a single register is used that acts
|
||||
* as both. During opcode analysis, the IX or IY register is copied
|
||||
* to this variable and, once the instruction emulation is complete,
|
||||
* its contents are copied back to the appropriate register. */
|
||||
|
||||
Z16Bit xy;
|
||||
|
||||
/** Temporary storage for opcode fetching.
|
||||
* @details This is an internal private variable. */
|
||||
|
||||
Z32Bit data;
|
||||
} Z80;
|
||||
|
||||
Z_C_SYMBOLS_BEGIN
|
||||
|
||||
#ifndef CPU_Z80_API
|
||||
# ifdef CPU_Z80_STATIC
|
||||
# define CPU_Z80_API
|
||||
# else
|
||||
# define CPU_Z80_API Z_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** Changes the CPU power status.
|
||||
* @param object A pointer to a Z80 emulator instance.
|
||||
* @param state @c TRUE = power ON; @c FALSE = power OFF. */
|
||||
|
||||
CPU_Z80_API void z80_power(Z80 *object, zboolean state);
|
||||
|
||||
/** Resets the CPU.
|
||||
* @details This is equivalent to a pulse on the RESET line of a real Z80.
|
||||
* @param object A pointer to a Z80 emulator instance. */
|
||||
|
||||
CPU_Z80_API void z80_reset(Z80 *object);
|
||||
|
||||
/** Runs the CPU for a given number of @p cycles.
|
||||
* @param object A pointer to a Z80 emulator instance.
|
||||
* @param cycles The number of cycles to be executed.
|
||||
* @return The number of cycles executed.
|
||||
* @note Given the fact that one Z80 instruction needs between 4 and 23 cycles
|
||||
* to be executed, it's not always possible to run the CPU the exact number of
|
||||
* @p cycles specfified. */
|
||||
|
||||
CPU_Z80_API zusize z80_run(Z80 *object, zusize cycles);
|
||||
|
||||
/** Performs a non-maskable interrupt (NMI).
|
||||
* @details This is equivalent to a pulse on the NMI line of a real Z80.
|
||||
* @param object A pointer to a Z80 emulator instance. */
|
||||
|
||||
CPU_Z80_API void z80_nmi(Z80 *object);
|
||||
|
||||
/** Changes the state of the maskable interrupt (INT).
|
||||
* @details This is equivalent to a change on the INT line of a real Z80.
|
||||
* @param object A pointer to a Z80 emulator instance.
|
||||
* @param state @c TRUE = line high; @c FALSE = line low. */
|
||||
|
||||
CPU_Z80_API void z80_int(Z80 *object, zboolean state);
|
||||
|
||||
Z_C_SYMBOLS_END
|
||||
|
||||
#ifdef CPU_Z80_WITH_ABI
|
||||
|
||||
# ifndef CPU_Z80_DEPENDENCIES_H
|
||||
# include <Z/ABIs/generic/emulation.h>
|
||||
# endif
|
||||
|
||||
Z_C_SYMBOLS_BEGIN
|
||||
|
||||
# ifndef CPU_Z80_ABI
|
||||
# ifdef CPU_Z80_STATIC
|
||||
# define CPU_Z80_ABI
|
||||
# else
|
||||
# define CPU_Z80_ABI Z_API
|
||||
# endif
|
||||
# endif
|
||||
|
||||
CPU_Z80_ABI extern ZCPUEmulatorABI const abi_emulation_cpu_z80;
|
||||
|
||||
Z_C_SYMBOLS_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _emulation_CPU_Z80_H_ */
|
||||
Reference in New Issue
Block a user