diff --git a/.editorconfig b/.editorconfig index af0a0c1..f68b68c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,3 +6,13 @@ end_of_line = lf indent_size = 8 indent_style = tab trim_trailing_whitespace = true + +[*.{md,rst}] +trim_trailing_whitespace = false + +[*.rc] +charset = utf-8-bom + +[*.{cff,yml}] +indent_size = 2 +indent_style = space diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7eafc02 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +github: redcode +ko_fi: redcode +liberapay: redcode +patreon: redcode diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..d5629bf --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,7 @@ +Thank you for your contribution to the Z80 library. It is required that contributors assign copyright to the original author so that he retains full ownership of the project. + +This makes it easier for other entities to use the software because they only have to deal with one copyright holder. It also gives the original author assurance that he will be able to make decisions in the future without gathering and consulting all contributors. + +By submitting this PR, you agree to the following: + +> You hereby assign copyright in this PR's code to the Z80 library and its copyright holder, Manuel Sainz de Baranda y Goñi, to be licensed under the same terms as the rest of the code. You agree to relinquish any and all copyright interest in the software, to the detriment of your heirs and successors. diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml new file mode 100644 index 0000000..74e8c72 --- /dev/null +++ b/.github/workflows/documentation-ci.yml @@ -0,0 +1,72 @@ +name: Documentation CI + +on: + push: + paths: + - '.github/**' + - 'API/**' + - 'CMake/FindBreathe.cmake' + - 'CMake/FindSphinx.cmake' + - 'documentation/**' + - 'CMakeLists.txt' + pull_request: + paths: + - '.github/**' + - 'API/**' + - 'CMake/FindBreathe.cmake' + - 'CMake/FindSphinx.cmake' + - 'documentation/**' + - 'CMakeLists.txt' + +env: + BUILD_SHARED_LIBS: NO + CMAKE_BUILD_TYPE: Release + CMAKE_VERBOSE_MAKEFILE: YES + Z80_DOWNLOAD_TEST_FILES: NO + Z80_WITH_CMAKE_SUPPORT: NO + Z80_WITH_HTML_DOCUMENTATION: YES + Z80_WITH_PDF_DOCUMENTATION: YES + Z80_WITH_PKGCONFIG_SUPPORT: NO + Z80_WITH_STANDARD_DOCUMENTS: NO + Z80_WITH_TESTS: NO + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install Build Tools + run: | + sudo apt-get update + sudo apt-get -y install doxygen + sudo apt-get -y install python3-pip + sudo apt-get -y install texlive-full + pip3 install --user sphinx + pip3 install --user breathe + + - name: Install Dependencies + run: | + mkdir -p ${{github.workspace}}/build + curl -L http://zeta.st/downloads/Zeta-latest.tar.xz | xz -cd | tar -C ${{github.workspace}}/build --strip-components=2 -xvf - Zeta/API/Z + + - name: Configure CMake + run: > + cmake -B ${{github.workspace}}/build + -DBUILD_SHARED_LIBS=${{env.BUILD_SHARED_LIBS}} + -DCMAKE_BUILD_TYPE=${{env.CMAKE_BUILD_TYPE}} + -DCMAKE_VERBOSE_MAKEFILE=${{env.CMAKE_VERBOSE_MAKEFILE}} + -DZ80_DOWNLOAD_TEST_FILES=${{env.Z80_DOWNLOAD_TEST_FILES}} + -DZ80_WITH_CMAKE_SUPPORT=${{env.Z80_WITH_CMAKE_SUPPORT}} + -DZ80_WITH_HTML_DOCUMENTATION=${{env.Z80_WITH_HTML_DOCUMENTATION}} + -DZ80_WITH_PDF_DOCUMENTATION=${{env.Z80_WITH_PDF_DOCUMENTATION}} + -DZ80_WITH_PKGCONFIG_SUPPORT=${{env.Z80_WITH_PKGCONFIG_SUPPORT}} + -DZ80_WITH_STANDARD_DOCUMENTS=${{env.Z80_WITH_STANDARD_DOCUMENTS}} + -DZ80_WITH_TESTS=${{env.Z80_WITH_TESTS}} + + - name: Build HTML Documentation + run: cmake --build ${{github.workspace}}/build --config ${{env.CMAKE_BUILD_TYPE}} --target Documentation-HTML + + - name: Build PDF Documentation + run: cmake --build ${{github.workspace}}/build --config ${{env.CMAKE_BUILD_TYPE}} --target Documentation-PDF diff --git a/.github/workflows/library-ci.yml b/.github/workflows/library-ci.yml new file mode 100644 index 0000000..a831830 --- /dev/null +++ b/.github/workflows/library-ci.yml @@ -0,0 +1,92 @@ +name: Library CI + +on: + push: + paths: + - '.github/**' + - 'API/**' + - 'CMake/FindZeta.cmake' + - 'sources/**' + - 'support/*.sha512sum' + - 'CMakeLists.txt' + pull_request: + paths: + - '.github/**' + - 'API/**' + - 'CMake/FindZeta.cmake' + - 'sources/**' + - 'support/*.sha512sum' + - 'CMakeLists.txt' + +env: + BUILD_SHARED_LIBS: YES + CMAKE_BUILD_TYPE: Release + CMAKE_VERBOSE_MAKEFILE: YES + Z80_DOWNLOAD_TEST_FILES: YES + Z80_NOSTDLIB_FLAGS: Auto + Z80_WITH_CMAKE_SUPPORT: NO + Z80_WITH_HTML_DOCUMENTATION: NO + Z80_WITH_PDF_DOCUMENTATION: NO + Z80_WITH_PKGCONFIG_SUPPORT: NO + Z80_WITH_STANDARD_DOCUMENTS: NO + Z80_WITH_TESTS: YES + Z80_WITH_EXECUTE: YES + Z80_WITH_FULL_IM0: YES + Z80_WITH_Q: YES + Z80_WITH_RESET_SIGNAL: YES + Z80_WITH_SPECIAL_RESET_SIGNAL: NO + Z80_WITH_UNOFFICIAL_RETI: NO + Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG: YES + +jobs: + build: + runs-on: ${{matrix.os}} + + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + + steps: + - uses: actions/checkout@v3 + + - name: Install Dependencies (POSIX) + if: startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu') + run: | + mkdir -p ${{github.workspace}}/build + curl -L http://zeta.st/downloads/Zeta-latest.tar.xz | xz -cd | tar -C ${{github.workspace}}/build --strip-components=2 -xvf - Zeta/API/Z + + - name: Install Dependencies (Windows) + if: startsWith(matrix.os, 'windows') + shell: cmd + run: | + mkdir ${{github.workspace}}\build + curl -L http://zeta.st/downloads/Zeta-latest.tar.xz | 7z x -txz -si -so | 7z x -si -ttar -o${{github.workspace}}\build Zeta/API/Z + + - name: Configure CMake + run: > + cmake -B ${{github.workspace}}/build + -DBUILD_SHARED_LIBS=${{env.BUILD_SHARED_LIBS}} + -DCMAKE_BUILD_TYPE=${{env.CMAKE_BUILD_TYPE}} + -DCMAKE_VERBOSE_MAKEFILE=${{env.CMAKE_VERBOSE_MAKEFILE}} + -DZ80_DOWNLOAD_TEST_FILES=${{env.Z80_DOWNLOAD_TEST_FILES}} + -DZ80_NOSTDLIB_FLAGS=${{env.Z80_NOSTDLIB_FLAGS}} + -DZ80_WITH_CMAKE_SUPPORT=${{env.Z80_WITH_CMAKE_SUPPORT}} + -DZ80_WITH_HTML_DOCUMENTATION=${{env.Z80_WITH_HTML_DOCUMENTATION}} + -DZ80_WITH_PDF_DOCUMENTATION=${{env.Z80_WITH_PDF_DOCUMENTATION}} + -DZ80_WITH_PKGCONFIG_SUPPORT=${{env.Z80_WITH_PKGCONFIG_SUPPORT}} + -DZ80_WITH_STANDARD_DOCUMENTS=${{env.Z80_WITH_STANDARD_DOCUMENTS}} + -DZ80_WITH_TESTS=${{env.Z80_WITH_TESTS}} + -DZ80_WITH_EXECUTE=${{env.Z80_WITH_EXECUTE}} + -DZ80_WITH_FULL_IM0=${{env.Z80_WITH_FULL_IM0}} + -DZ80_WITH_Q=${{env.Z80_WITH_Q}} + -DZ80_WITH_RESET_SIGNAL=${{env.Z80_WITH_RESET_SIGNAL}} + -DZ80_WITH_SPECIAL_RESET_SIGNAL=${{env.Z80_WITH_SPECIAL_RESET_SIGNAL}} + -DZ80_WITH_UNOFFICIAL_RETI=${{env.Z80_WITH_UNOFFICIAL_RETI}} + -DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=${{env.Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.CMAKE_BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + run: ctest --output-on-failure -C ${{env.CMAKE_BUILD_TYPE}} diff --git a/.gitignore b/.gitignore index 5773d84..1615f24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -.directory .DS_Store +.directory Thumbs.db +build/ diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000..361ddcf --- /dev/null +++ b/.vimrc @@ -0,0 +1,4 @@ +set noexpandtab +set shiftwidth=8 +set tabstop=8 +autocmd BufWritePre * %s/\s\+$//e diff --git a/API/Z80.h b/API/Z80.h new file mode 100644 index 0000000..0f05e88 --- /dev/null +++ b/API/Z80.h @@ -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 . | +| | +'=============================================================================*/ + +#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 +# include +#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 ld h,h 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 ld i,a + * instruction. */ + + Z80Notify ld_i_a; + + /** @brief Callback invoked before executing the ld r,a + * 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 */ diff --git a/API/emulation/CPU/Z80.h b/API/emulation/CPU/Z80.h deleted file mode 100644 index 77ee87a..0000000 --- a/API/emulation/CPU/Z80.h +++ /dev/null @@ -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 . ---------------------------------------------------------------------------- */ - -#ifndef _emulation_CPU_Z80_H_ -#define _emulation_CPU_Z80_H_ - -#ifdef CPU_Z80_DEPENDENCIES_H -# include CPU_Z80_DEPENDENCIES_H -#else -# include -#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 -# 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_ */ diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..8ca1867 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,22 @@ +abstract: "Zilog Z80 CPU emulator." +authors: + - family-names: "Sainz de Baranda y Goñi" + given-names: "Manuel" + orcid: "https://orcid.org/0000-0001-6326-3519" + email: "manuel@zxe.io" + website: "https://zxe.io" +cff-version: 1.2.0 +date-released: "2022-05-29" +keywords: + - CPU + - LLE + - Z80 + - Zilog + - emulator +license: LGPL-3.0-or-later +message: "If you use this software, please cite it using these metadata." +repository-code: "https://github.com/redcode/Z80" +title: "Z80" +type: software +version: 0.2 +url: "https://zxe.io/software/Z80" diff --git a/CMake/FindBreathe.cmake b/CMake/FindBreathe.cmake new file mode 100644 index 0000000..0ea2ed9 --- /dev/null +++ b/CMake/FindBreathe.cmake @@ -0,0 +1,29 @@ +# FindBreathe.cmake +# Copyright (C) 2021 Manuel Sainz de Baranda y Goñi. +# This "find module" is DISTRIBUTED AS PUBLIC DOMAIN. No restrictions apply. + +include(FindPackageHandleStandardArgs) + +find_program( + BREATHE_APIDOC_EXECUTABLE + NAMES breathe-apidoc + DOC "Breathe extension for Sphinx") + +if(BREATHE_APIDOC_EXECUTABLE) + execute_process( + COMMAND ${BREATHE_APIDOC_EXECUTABLE} --version + OUTPUT_VARIABLE _output) + + if("${_output}" MATCHES ".* ([^\n]+)\n") + set(BREATHE_APIDOC_VERSION "${CMAKE_MATCH_1}") + endif() +endif() + +find_package_handle_standard_args( + Breathe + REQUIRED_VARS BREATHE_APIDOC_EXECUTABLE + VERSION_VAR BREATHE_APIDOC_VERSION) + +mark_as_advanced(BREATHE_APIDOC_EXECUTABLE) + +# FindBreathe.cmake EOF diff --git a/CMake/FindSphinx.cmake b/CMake/FindSphinx.cmake new file mode 100644 index 0000000..f22cd50 --- /dev/null +++ b/CMake/FindSphinx.cmake @@ -0,0 +1,29 @@ +# FindSphinx.cmake +# Copyright (C) 2021 Manuel Sainz de Baranda y Goñi. +# This "find module" is DISTRIBUTED AS PUBLIC DOMAIN. No restrictions apply. + +include(FindPackageHandleStandardArgs) + +find_program( + SPHINX_BUILD_EXECUTABLE + NAMES sphinx-build + DOC "Sphinx Documentation Builder") + +if(SPHINX_BUILD_EXECUTABLE) + execute_process( + COMMAND ${SPHINX_BUILD_EXECUTABLE} --version + OUTPUT_VARIABLE _output) + + if("${_output}" MATCHES ".* ([^\n]+)\n") + set(SPHINX_BUILD_VERSION "${CMAKE_MATCH_1}") + endif() +endif() + +find_package_handle_standard_args( + Sphinx + REQUIRED_VARS SPHINX_BUILD_EXECUTABLE + VERSION_VAR SPHINX_BUILD_VERSION) + +mark_as_advanced(SPHINX_BUILD_EXECUTABLE) + +# FindSphinx.cmake EOF diff --git a/CMake/FindZ80.cmake b/CMake/FindZ80.cmake new file mode 100644 index 0000000..1ebc956 --- /dev/null +++ b/CMake/FindZ80.cmake @@ -0,0 +1,50 @@ +# Z80 - FindZ80.cmake +# ______ ______ ______ +# /\___ \/\ __ \\ __ \ +# \/__/ /\_\ __ \\ \/\ \ +# /\_____\\_____\\_____\ +# Zilog \/_____//_____//_____/ CPU Emulator +# Copyright (C) 1999-2022 Manuel Sainz de Baranda y Goñi. +# Released under the terms of the GNU Lesser General Public License v3. +# This "find module" is DISTRIBUTED AS PUBLIC DOMAIN. No restrictions apply. + +include(FindPackageHandleStandardArgs) + +find_path( + Z80_INCLUDE_DIR + "Z80.h" + HINTS ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR} + PATH_SUFFIXES + "deps/Z80/API" + "dependencies/Z80/API" + "extern/Z80/API" + "external/Z80/API" + "externals/Z80/API" + "kits/Z80/API/" + "Z80/API" + "../Z80/API" +) + +if(Z80_INCLUDE_DIR) + file(READ "${Z80_INCLUDE_DIR}/Z80.h" _h) + + if(${_h} MATCHES ".*Z80_LIBRARY_VERSION_STRING \"([^\n]*)\".*") + set(Z80_VERSION ${CMAKE_MATCH_1}) + endif() +endif() + +find_package_handle_standard_args( + Z80 + REQUIRED_VARS Z80_INCLUDE_DIR + VERSION_VAR Z80_VERSION +) + +if(Z80_FOUND AND NOT TARGET Z80) + add_library(Z80 IMPORTED) + target_include_directories(Z80 INTERFACE ${Z80_INCLUDE_DIR}) +endif() + +mark_as_advanced(Z80_INCLUDE_DIR) + +# FindZ80.cmake EOF diff --git a/CMake/FindZeta.cmake b/CMake/FindZeta.cmake new file mode 100644 index 0000000..52565ef --- /dev/null +++ b/CMake/FindZeta.cmake @@ -0,0 +1,60 @@ +# Zeta - FindZeta.cmake +# ______ ____________ ___ +# |__ /| ___|__ __|/ \ +# / /_| __| | | / * \ +# /_____|_____| |__|/__/ \__\ +# Copyright (C) 2006-2022 Manuel Sainz de Baranda y Goñi. +# # Released under the terms of the GNU Lesser General Public License v3. +# This "find module" is DISTRIBUTED AS PUBLIC DOMAIN. No restrictions apply. + +include(FindPackageHandleStandardArgs) + +find_path( + Zeta_INCLUDE_DIR "Z/version.h" + PATHS "${PROJECT_BINARY_DIR}/Zeta/API" + "${PROJECT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/Zeta/API" + "${PROJECT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/dependencies/Zeta/API" + "${PROJECT_SOURCE_DIR}/dependencies" + "${PROJECT_SOURCE_DIR}/deps/Zeta/API" + "${PROJECT_SOURCE_DIR}/deps" + "${PROJECT_SOURCE_DIR}/extern/Zeta/API" + "${PROJECT_SOURCE_DIR}/extern" + "${PROJECT_SOURCE_DIR}/external/Zeta/API" + "${PROJECT_SOURCE_DIR}/external" + "${PROJECT_SOURCE_DIR}/externals/Zeta/API" + "${PROJECT_SOURCE_DIR}/externals" + "${PROJECT_SOURCE_DIR}/kits/Zeta/API" + "${PROJECT_SOURCE_DIR}/kits" + "${PROJECT_SOURCE_DIR}/../Zeta/API" + "${PROJECT_SOURCE_DIR}/.." + ENV CPATH + ENV C_INCLUDE_PATH + ENV CPLUS_INCLUDE_PATH + ENV OBJC_INCLUDE_PATH) + +if(Zeta_INCLUDE_DIR AND EXISTS "${Zeta_INCLUDE_DIR}/Z/version.h") + file(READ "${Zeta_INCLUDE_DIR}/Z/version.h" _) + + if(_ MATCHES ".*Z_LIBRARY_VERSION_STRING \"([^\n]*)\".*") + set(Zeta_VERSION ${CMAKE_MATCH_1}) + endif() + + unset(_) +endif() + +find_package_handle_standard_args( + Zeta + FOUND_VAR Zeta_FOUND + REQUIRED_VARS Zeta_INCLUDE_DIR Zeta_VERSION + VERSION_VAR Zeta_VERSION) + +if(Zeta_FOUND AND NOT (TARGET Zeta)) + add_library(Zeta INTERFACE IMPORTED) + target_include_directories(Zeta INTERFACE ${Zeta_INCLUDE_DIR}) +endif() + +mark_as_advanced(Zeta_INCLUDE_DIR) + +# FindZeta.cmake EOF diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8f33b27 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,373 @@ +# Z80 - CMakeLists.txt +# ______ ______ ______ +# /\___ \/\ __ \\ __ \ +# \/__/ /\_\ __ \\ \/\ \ +# /\_____\\_____\\_____\ +# Zilog \/_____//_____//_____/ CPU Emulator +# Copyright (C) 1999-2022 Manuel Sainz de Baranda y Goñi. +# Released under the terms of the GNU Lesser General Public License v3. + +cmake_minimum_required(VERSION 3.14) + +if( CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR AND + NOT CMAKE_BUILD_TYPE AND + NOT CMAKE_CONFIGURATION_TYPES +) + set(CMAKE_BUILD_TYPE Release) +endif() + +file(READ "${CMAKE_CURRENT_SOURCE_DIR}/API/Z80.h" _) +string(REGEX MATCH ".*Z80_LIBRARY_VERSION_STRING \"([^\n]*)\".*" _ ${_}) + +project(Z80 + VERSION ${CMAKE_MATCH_1} + LANGUAGES C + DESCRIPTION "Zilog Z80 CPU emulator") + +unset(_) +message("${PROJECT_NAME} v${PROJECT_VERSION}") + +include(GNUInstallDirs) + +if(DEFINED ${PROJECT_NAME}_SHARED_LIBS) + set(BUILD_SHARED_LIBS "${${PROJECT_NAME}_SHARED_LIBS}") +endif() + +option(${PROJECT_NAME}_DOWNLOAD_TEST_FILES +"Download the firmware and software used by the testing tool." +NO) + +set(${PROJECT_NAME}_INSTALL_CMAKEDIR +"${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING +"Specify the directory in which to install the CMake config-file package.") + +set(${PROJECT_NAME}_INSTALL_PKGCONFIGDIR +"${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE STRING +"Specify the directory in which to install the pkg-config file.") + +set(${PROJECT_NAME}_NOSTDLIB_FLAGS +"Auto" CACHE STRING +"Specify the linker flags used to avoid linking against system libraries.") + +set(${PROJECT_NAME}_SPHINX_HTML_THEME +"" CACHE STRING +"Specify the Sphinx theme for the documentation in HTML format.") + +option(${PROJECT_NAME}_WITH_CMAKE_SUPPORT +"Generate and install the CMake config-file package." +NO) + +option(${PROJECT_NAME}_WITH_HTML_DOCUMENTATION +"Build and install the documentation in HTML format." +NO) + +option(${PROJECT_NAME}_WITH_PDF_DOCUMENTATION +"Build and install the documentation in PDF format." +NO) + +option(${PROJECT_NAME}_WITH_PKGCONFIG_SUPPORT +"Generate and install the pkg-config file." +NO) + +option(${PROJECT_NAME}_WITH_STANDARD_DOCUMENTS +"Install the standard text documents distributed with the package: \ +AUTHORS, COPYING, COPYING.LESSER, HISTORY, README and THANKS." +NO) + +option(${PROJECT_NAME}_WITH_TESTS +"Build the testing tool." +NO) + +option(${PROJECT_NAME}_WITH_EXECUTE +"Build the implementation of the z80_execute function." +NO) + +option(${PROJECT_NAME}_WITH_FULL_IM0 +"Build the full implementation of the interrupt mode 0 rather than the \ +reduced one." +NO) + +option(${PROJECT_NAME}_WITH_Q +"Build the implementation of the Q \"register\"." +NO) + +option(${PROJECT_NAME}_WITH_RESET_SIGNAL +"Build the implementation of the normal RESET signal." +NO) + +option(${PROJECT_NAME}_WITH_SPECIAL_RESET_SIGNAL +"Build the implementation of the special RESET signal." +NO) + +option(${PROJECT_NAME}_WITH_UNOFFICIAL_RETI +"Configure the ED5Dh, ED6Dh and ED7Dh undocumented instructions as \"reti\" \ +instead of \"retn\"." +NO) + +option(${PROJECT_NAME}_WITH_ZILOG_NMOS_LD_A_IR_BUG +"Build the implementation of the bug affecting the Zilog Z80 NMOS, which \ +causes the P/V flag to be reset when a maskable interrupt is accepted \ +during the execution of the \"ld a,{i|r}\" instructions." +NO) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") +find_package(Zeta REQUIRED) + +add_library( + ${PROJECT_NAME} + "${CMAKE_CURRENT_SOURCE_DIR}/API/Z80.h" + "${CMAKE_CURRENT_SOURCE_DIR}/sources/Z80.c") + +set_target_properties( + ${PROJECT_NAME} PROPERTIES + C_STANDARD 99 + C_STANDARD_REQUIRED NO + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + DEBUG_POSTFIX "-debug" + PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/API/Z80.h") + +target_link_libraries(${PROJECT_NAME} PUBLIC Zeta) + +target_include_directories( + ${PROJECT_NAME} PUBLIC + "$" + "$") + +if(BUILD_SHARED_LIBS) + if(WIN32) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/sources/Z80.rc.in" + "${PROJECT_BINARY_DIR}/Z80.rc" + @ONLY) + + target_sources(${PROJECT_NAME} PRIVATE "${PROJECT_BINARY_DIR}/Z80.rc") + endif() + + if (${PROJECT_NAME}_NOSTDLIB_FLAGS STREQUAL "Auto") + if(MSVC) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_WINDOWS_DLL_MAIN) + target_link_options(${PROJECT_NAME} PRIVATE "/NODEFAULTLIB") + elseif(APPLE) + if(CMAKE_C_COMPILER_ID MATCHES "^(AppleClang|Clang)$") + target_link_options(${PROJECT_NAME} PRIVATE "LINKER:-dead_strip_dylibs") + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU") + target_link_options(${PROJECT_NAME} PRIVATE "LINKER:-dead_strip_dylibs;-nolibc") + endif() + elseif(CMAKE_C_COMPILER_ID MATCHES "^(ARMClang|Clang|GNU|Intel|TinyCC|XL|XLClang)$") + target_link_options(${PROJECT_NAME} PRIVATE "-nostdlib") + elseif(CMAKE_C_COMPILER_ID MATCHES "PGI") + target_link_options(${PROJECT_NAME} PRIVATE "-Mnostdlib") + elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro") + target_link_options(${PROJECT_NAME} PRIVATE "-xnolib") + endif() + elseif(NOT ${PROJECT_NAME}_NOSTDLIB_FLAGS STREQUAL "") + target_link_options(${PROJECT_NAME} PRIVATE ${${PROJECT_NAME}_NOSTDLIB_FLAGS}) + endif() +else() + target_compile_definitions(${PROJECT_NAME} PUBLIC Z80_STATIC) +endif() + +if(${PROJECT_NAME}_WITH_EXECUTE) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_EXECUTE) +endif() + +if(${PROJECT_NAME}_WITH_FULL_IM0) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_FULL_IM0) +endif() + +if(${PROJECT_NAME}_WITH_Q) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_Q) +endif() + +if(${PROJECT_NAME}_WITH_RESET_SIGNAL) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_RESET_SIGNAL) +endif() + +if(${PROJECT_NAME}_WITH_SPECIAL_RESET_SIGNAL) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_SPECIAL_RESET_SIGNAL) +endif() + +if(${PROJECT_NAME}_WITH_UNOFFICIAL_RETI) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_UNOFFICIAL_RETI) +endif() + +if(${PROJECT_NAME}_WITH_ZILOG_NMOS_LD_A_IR_BUG) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG) +endif() + +install(TARGETS ${PROJECT_NAME} + EXPORT "${PROJECT_NAME}_Targets" + RUNTIME COMPONENT "${PROJECT_NAME}_Runtime" + LIBRARY COMPONENT "${PROJECT_NAME}_Runtime" + DESTINATION "${CMAKE_INSTALL_LIBDIR}" + NAMELINK_COMPONENT "${PROJECT_NAME}_Development" + ARCHIVE COMPONENT "${PROJECT_NAME}_Development" + PUBLIC_HEADER + COMPONENT "${PROJECT_NAME}_Development" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +if(${PROJECT_NAME}_WITH_CMAKE_SUPPORT OR ${PROJECT_NAME}_WITH_PKGCONFIG_SUPPORT) + include(CMakePackageConfigHelpers) + + if(${PROJECT_NAME}_WITH_CMAKE_SUPPORT) + if (BUILD_SHARED_LIBS) + set(_type Shared) + else() + set(_type Static) + endif() + + install(EXPORT "${PROJECT_NAME}_Targets" + DESTINATION ${${PROJECT_NAME}_INSTALL_CMAKEDIR} + FILE ${PROJECT_NAME}${_type}Targets.cmake + COMPONENT ${PROJECT_NAME}_Development) + + unset(_type) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + + configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/support/${PROJECT_NAME}Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${${PROJECT_NAME}_INSTALL_CMAKEDIR}) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${${PROJECT_NAME}_INSTALL_CMAKEDIR} + COMPONENT ${PROJECT_NAME}_Development) + endif() + + if(${PROJECT_NAME}_WITH_PKGCONFIG_SUPPORT) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/support/${PROJECT_NAME}.pc.in" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" + @ONLY) + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION ${${PROJECT_NAME}_INSTALL_PKGCONFIGDIR} + COMPONENT ${PROJECT_NAME}_Development) + endif() +endif() + +if(${PROJECT_NAME}_WITH_STANDARD_DOCUMENTS) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/AUTHORS" + "${CMAKE_CURRENT_SOURCE_DIR}/COPYING" + "${CMAKE_CURRENT_SOURCE_DIR}/COPYING.LESSER" + "${CMAKE_CURRENT_SOURCE_DIR}/HISTORY" + "${CMAKE_CURRENT_SOURCE_DIR}/README" + "${CMAKE_CURRENT_SOURCE_DIR}/THANKS" + DESTINATION ${CMAKE_INSTALL_DOCDIR}) +endif() + +if(${PROJECT_NAME}_WITH_HTML_DOCUMENTATION OR ${PROJECT_NAME}_WITH_PDF_DOCUMENTATION) + add_subdirectory(documentation) +endif() + +if(${PROJECT_NAME}_WITH_TESTS) + include(CTest) + + find_package(ZLIB QUIET) + find_package(libzip QUIET) + + add_executable("test-${PROJECT_NAME}" "${CMAKE_CURRENT_SOURCE_DIR}/sources/test-Z80.c") + target_link_libraries("test-${PROJECT_NAME}" PRIVATE ${PROJECT_NAME}) + + if(ZLIB_FOUND AND libzip_FOUND) + target_link_libraries("test-${PROJECT_NAME}" PRIVATE z libzip::zip) + target_compile_definitions("test-${PROJECT_NAME}" PRIVATE TEST_Z80_WITH_ARCHIVE_EXTRACTION) + endif() + + if(${PROJECT_NAME}_WITH_EXECUTE) + target_compile_definitions("test-${PROJECT_NAME}" PRIVATE TEST_Z80_WITH_EXECUTE) + endif() + + if(${PROJECT_NAME}_DOWNLOAD_TEST_FILES) + function(_download_files sha512sum_file base_url downloads_dir) + file(STRINGS "${sha512sum_file}" _lines) + + foreach(_line ${_lines}) + string(STRIP ${_line} _line) + string(SUBSTRING ${_line} 0 128 _sha512) + string(SUBSTRING ${_line} 130 -1 _file_path) + get_filename_component(_file_full_name ${_file_path} NAME) + + set(_file_url "${base_url}/${_file_path}") + message(STATUS "Downloading \"${_file_full_name}\"") + string(REPLACE " " "%20" _file_url ${_file_url}) + string(REPLACE "!" "%21" _file_url ${_file_url}) + string(REPLACE "(" "%28" _file_url ${_file_url}) + string(REPLACE ")" "%29" _file_url ${_file_url}) + string(REPLACE "[" "%5B" _file_url ${_file_url}) + string(REPLACE "]" "%5D" _file_url ${_file_url}) + + file( DOWNLOAD + "${_file_url}" + "${downloads_dir}/${_file_path}" + EXPECTED_HASH SHA512=${_sha512}) + + if ( NOT "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 3.18 AND + (NOT ZLIB_FOUND OR NOT libzip_FOUND) + ) + get_filename_component(_file_extension ${_file_full_name} LAST_EXT) + + if(_file_extension STREQUAL ".gz" OR _file_extension STREQUAL ".zip") + get_filename_component(_subdirectory ${_file_path} DIRECTORY) + + if(_file_extension STREQUAL ".gz") + set(_extract_pattern "*zex*.com") + else() + set(_extract_pattern "*.tap") + endif() + + file( ARCHIVE_EXTRACT + INPUT "${downloads_dir}/${_file_path}" + DESTINATION "${downloads_dir}/${_subdirectory}" + PATTERNS ${_extract_pattern} + VERBOSE) + endif() + endif() + endforeach() + endfunction() + + _download_files( + "${CMAKE_CURRENT_SOURCE_DIR}/support/firmware.sha512sum" + "http://zxe.io/downloads/support/firmware" + "${CMAKE_CURRENT_BINARY_DIR}/downloads/firmware") + + _download_files( + "${CMAKE_CURRENT_SOURCE_DIR}/support/software.sha512sum" + "http://zxe.io/downloads/support/software" + "${CMAKE_CURRENT_BINARY_DIR}/downloads/software") + + if ( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 3.18 AND + (NOT ZLIB_FOUND OR NOT libzip_FOUND) + ) + message(WARNING + "libzip or zlib was not found on your system, which will cause the " + "test-Z80 tool to be built without archive extraction support. When " + "this happens, the build script extracts those downloaded tests that " + "are compressed so that test-Z80 can use them later, but this has " + "failed because the version of CMake you are using is too old and " + "does not support archive extraction either.\n" + "To fix this, extract all files with \".tar.gz\" or \".zip\" " + "extension located in " + "\"${CMAKE_CURRENT_BINARY_DIR}/downloads/software/**\" " + "to the same directory.") + endif() + endif() + + add_test( + NAME "test-${PROJECT_NAME}" + COMMAND "test-${PROJECT_NAME}" + --path "${CMAKE_CURRENT_BINARY_DIR}/downloads/firmware" + --path "${CMAKE_CURRENT_BINARY_DIR}/downloads/software/POSIX" + --path "${CMAKE_CURRENT_BINARY_DIR}/downloads/software/ZX Spectrum" + --all + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") +endif() + +# CMakeLists.txt EOF diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/HISTORY b/HISTORY index c45cc71..351f6c9 100644 --- a/HISTORY +++ b/HISTORY @@ -1,3 +1,100 @@ -Z80 v0.1 (2018-11-10) +Z80 v0.2 (2022-XX-XX) +===================== - + Initial release. +This is an important update that addresses a number of issues and also includes +new features. Please note that the changes introduced in this release break the +binary compatibility with the previous version. + +Changes: + 1. Changed the license from GPL to LGPL (by popular request). + 2. Moved the public header from to . + 3. Removed the Xcode project. + 4. Switched the build system from Premake to CMake. + 5. Switched to Zeta v0.1. + 6. Added pkg-config support. + 7. Added the .vimrc dotfile. + 8. Added the CITATION.cff file. + 9. Added the file_id.diz file. +10. Added the THANKS file. +11. Added detailed documentation. +12. Added tests. +13. Added public macros for checking the library version. +14. Added public macros with bit masks for working with flags. +15. Added public macros for accessing the 16-bit registers. +16. Added the z80_execute function to run a simplified emulation without RESET + and interrupts. +17. Added the z80_refresh_address function to get the refresh address of the + current M1 cycle. +18. Added the z80_in_cycle and z80_out_cycle functions to get the clock cycle on + which the I/O M-cycle occurs, relative to the start of the instruction. +19. Fixed a bug in the "sll" instruction. +20. Fixed a bug in the INX and OUTX macros affecting the S and N flags. +21. Fixed a bug in the OUTX macro affecting the MSByte of the port number. +22. Fixed the clock cycles of the "dec XY" and "in (c)" instructions. +23. Fixed the read_16 function so that the order in which the compiler evaluates + expressions does not affect the order of the memory read operations. +24. Fixed the order in which the memory write operations are performed when the + SP register is involved. This affects the NMI response, the INT response in + modes 1 and 2, and the following instructions: "ex (sp),{hl|XY}", "push TT", + "push XY", "call WORD", "call Z,WORD" and "rst N". +25. Fixed the handling of illegal instructions to avoid stack overflows in long + sequences of DDh/FDh prefixes. +26. Fixed several implicit conversions to avoid warnings about loss of sign and + precision. +27. Fixed some bitwise operations to avoid undefined behavior and arithmetic + right shifts on signed integers. +28. Fixed violations of the C standard in several identifiers. +29. Renamed the 8-bit register lists: X/Y to J/K; J/K and P/Q to O/P. +30. Replaced all P/V overflow computation functions with a single, faster macro. +31. Replaced all register resolution functions with macros. +32. Replaced all "ld {J,K|O,P}" instructions that have the same destination and + source register with NOPs. +33. Reimplemented the HALT state. The emulation should now be fully accurate. + HALTskip is also supported. +34. Added optional emulation of the normal and special RESET signals, along with + the new z80_reset and z80_special_reset functions to emit them. The old + z80_reset function is now called z80_instant_reset. +35. Added emulation of the NMI acknowledge M-cycle through the new Z80::nmia + callback. +36. Added emulation of the INT acknowledge M-cycle through the new Z80::inta + callback, which replaces Z80::int_data. +37. Added optional full emulation of the interrupt mode 0, along with the new + Z80::int_fetch callback to perform bus read operations on instruction data. + If not enabled at compile-time, the old simplified emulation is built, which + supports only the most typical instructions. +38. Added accurate flag behavior in the following instructions: "ldir", "lddr", + "cpir", "cpdr", "inir", "indr", "otir" and "otdr". +39. Added emulation of the interrupt acceptance deferral that occurs during the + "reti" and "retn" instructions. +40. Added MEMPTR emulation. The "bit N,(hl)" instruction now produces a correct + value of F. +41. Added optional emulation of the Q "register". If enabled at compile-time, + the "ccf" and "scf" instructions produce a correct value of F. +42. Added emulation options that can be configured at runtime. +43. Added emulation of the "out (c),255" instruction (Zilog Z80 CMOS). +44. Added optional emulation of the bug affecting the "ld a,{i|r}" instructions + (Zilog Z80 NMOS). If enabled at compile-time, the P/V flag is reset when an + INT is accepted during the execution of these instructions. +45. Added the Z80::fetch_opcode and Z80::fetch callbacks to perform opcode fetch + operations and memory read operations on instruction data respectively. +46. Added hooking functionality through the "ld h,h" instruction and the new + Z80::hook callback. +47. Added the Z80::nop callback to perform disregarded opcode fetch operations + during internal NOP M-cycles. +48. Added four callbacks to notify the execution of important instructions: + Z80::ld_i_a, Z80::ld_r_a, Z80::reti and Z80::retn. +49. Removed Z80::state. Replaced with individual members for the registers, the + interrupt enable flip-flops and the interrupt mode. +50. Removed the superfluous EI flag. The previous opcode is checked instead, + which is faster and makes the Z80 object smaller. +51. Removed all module-related stuff. +52. Optimizations in flag computation and condition evaluation. +53. New source code comments and improvements to existing ones. +54. Improved code aesthetics. +55. Other improvements, optimizations and minor changes. + + +Z80 v0.1 (2018-11-10) +===================== + +Initial public release. diff --git a/README b/README index a4cf839..8c813e0 100644 --- a/README +++ b/README @@ -1,84 +1,319 @@ - ______ ______ ______ - /\___ \ /\ __ \/\ __ \ - \/__/ /_\ \ __ \ \ \/\ \ - /\_____\ \_____\ \_____\ -Zilog \/_____/\/_____/\/_____/ CPU Emulator v0.1 -Copyright (C) 1999-2018 Manuel Sainz de Baranda y Goñi -This is a Zilog Z80 CPU emulator that I wrote many years ago. It is fast, small, -easy to understand, and the code is profusely commented. + ________________ ________________ _________________ + | /\ | || | + |_______ / / | ____ || ____ | + \_____/ / / | |____| || |___/| | + / / / | || | | | | + / / / | ____ || | | | | + / /_/_____| |____| || |__|_| | + / || || | + /_______________||________________||________________| + \________________\\______________//________________/ + + ______________________________________________________________________________ +| | +| Zilog Z80 CPU Emulator | +| version 0.2 | +| | +| Copyright (C) 1999-2022 Manuel Sainz de Baranda y Goñi | +| Released under the terms of the GNU Lesser General Public License v3 | +| | +| https://zxe.io/software/Z80 | +| | +'==============================================================================' -Building -======== +1. Introduction +================ - You must first install Z , a header-only library that provides - types and macros. This is the only dependency, the emulator does not use the C - standard library or its headers. Then add Z80.h and Z80.c to your project and - configure its build system so that CPU_Z80_STATIC and CPU_Z80_USE_LOCAL_HEADER - are predefined when compiling the sources. +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 +behaviour, MEMPTR, Q and the special RESET. It also has the honor of having been +the first open source project to provide full emulation of the interrupt mode 0. - If you preffer to build the emulator as a library, you can use premake4: - - $ cd building - $ premake4 gmake # generate Makefile - $ make help # list available targets - $ make [config=] [target] # build the emulator - - There is also an Xcode project in "development/Xcode" with several targets: - - Z80 (dynamic) - Shared library. - - Z80 (dynamic module) - Shared library with a generic module ABI to be used in modular multi-machine - emulators. - - Z80 (static) - Static library. - - Z80 (static module) - Static library with a generic CPU emulator ABI to be used in monolithic - multi-machine emulators. +The source code is written in ANSI C for maximum portability and is extensively +commented. The aim has been to write a well-structured, easy to understand piece +of software; something solid and elegant that can stand the test of time with no +need for major changes. -Code configuration -================== +2. Emulation accuracy +====================== - There are some predefined macros that control the compilation: +The Zilog Z80 emulator has a classic design with instruction-level granularity. +This provides the best performance when speed is a critical factor, while still +offering a reasonable flexibility to achieve precision down to the T-state level +when accuracy is imperative. - CPU_Z80_DEPENDENCIES_H - If defined, it replaces the inclusion of any external header with this one. - If you don't want to use Z, you can provide your own header with the types - and macros used by the emulator. +Instruction-level granularity implies that, except in a few well-defined cases, +the execution of a given instruction cannot stop until all its internal M-cycles +are completed. This kind of emulation is also carried out in an efficient way: +the pertinent registers are modified only once per instruction and the T-state +counter is updated only after a whole instruction is executed. - CPU_Z80_HIDE_ABI - Makes the generic CPU emulator ABI private. - - CPU_Z80_HIDE_API - Makes the public functions private. - - CPU_Z80_STATIC - You need to define this to compile or use the emulator as a static library - or if you have added Z80.h and Z80.c to your project. - - CPU_Z80_USE_LOCAL_HEADER - Use this if you have imported Z80.h and Z80.c to your project. Z80.c will - #include "Z80.h" instead of . - - CPU_Z80_WITH_ABI - Builds the generic CPU emulator ABI and declares its prototype in Z80.h. - - CPU_Z80_WITH_MODULE_ABI - Builds the generic module ABI. This macro also enables CPU_Z80_WITH_ABI, so - the generic CPU emulator ABI will be built too. This option is intended to - be used when building a true module loadable at runtime with dlopen(), - LoadLibrary() or similar. The ABI module can be accessed via the weak symbol - __module_abi__. +That said, instructions, flags, memory accesses, interrupt and reset responses, +clock cycles, etc. are accurately emulated as far as is known, according to the +technical documentation available, the findings made after decades of research +on the Z80 and electronic simulations. And, of course, the emulator passes the +most exhaustive tests written to date such as Patrik Rak's "Zilog Z80 CPU Test +Suite", Frank D. Cringle's "Z80 Instruction Set Exerciser", Mark Woodmass' "Z80 +Test Suite" and Peter Helcmanovsky's "Z80 Block Flags Test", to name a few. -Use in Proprietary Software -=========================== +3. Installation +================ - This library is released under the terms of the GNU General Public License v3, - but I can license it for non-free/propietary projects if you contact me. +You will need CMake v3.14 or later to build the package and, optionally, recent +versions of Doxygen, Sphinx and Breathe to compile the documentation. Also make +sure you have LaTeX with PDF support installed on your system in case you want +to generate the documentation in PDF format. + +The emulator requires some types and macros included in Zeta (https://zeta.st), +a dependency-free, header-only library used to retain compatibility with most C +compilers. Install Zeta or extract its official source code package to the same +directory of this README or its parent directory. Zeta is the sole dependency; +the emulator is a freestanding implementation and as such does not depend on the +C standard library. + +Once all requirements are met, create a directory and run cmake from there to +prepare the build system: + + $ mkdir build + $ cd build + $ cmake [options] + +The resulting build files can be configured by passing options to cmake. To show +a complete list of those available along with their current settings, type the +following: + + $ cmake -LAH + +If in doubt, read the CMake documentation for more information on configuration +options. The following are some of the most relevant standard options of CMake: + + -DBUILD_SHARED_LIBS=(YES|NO) + Build the project as a shared library rather than a static one. + The default is NO. + + -DCMAKE_BUILD_TYPE=(Debug|Release|RelWithDebInfo) + Choose the type of build (configuration) to generate. + The default is Release. + + -DCMAKE_INSTALL_PREFIX="" + Specify the installation prefix on UNIX and UNIX-like operating systems. + The default is "/usr/local". + +Package-specific options are prefixed with "Z80_" and can be divided into two +groups. The first one controls aspects not related to the source code of the +library: + + -DZ80_DOWNLOAD_TEST_FILES=(YES|NO) + Download the firmware and software used by the testing tool. + The default is NO. + + -DZ80_INSTALL_CMAKEDIR="" + Specify the directory in which to install the CMake config-file package. + The default is "${CMAKE_INSTALL_LIBDIR}/cmake/Z80". + + -DZ80_INSTALL_PKGCONFIGDIR="" + Specify the directory in which to install the pkg-config file. + The default is "${CMAKE_INSTALL_LIBDIR}/pkgconfig". + + -DZ80_NOSTDLIB_FLAGS=(Auto|[[;...]]) + Specify the linker flags used to avoid linking against system libraries. + The default is Auto (autoconfigure flags). If you get linker errors, set + this option to "". + + -DZ80_SHARED_LIBS=(YES|NO) + Build the project as a shared library rather than a static one. + This option takes precedence over "BUILD_SHARED_LIBS". + Not defined by default. + + -DZ80_SPHINX_HTML_THEME="[]" + Specify the Sphinx theme for the documentation in HTML format. + The default is "" (use the default theme). + + -DZ80_WITH_CMAKE_SUPPORT=(YES|NO) + Generate and install the CMake config-file package. + The default is NO. + + -DZ80_WITH_HTML_DOCUMENTATION=(YES|NO) + Build and install the documentation in HTML format. + It requires Doxygen, Sphinx and Breathe. + The default is NO. + + -DZ80_WITH_PDF_DOCUMENTATION=(YES|NO) + Build and install the documentation in PDF format. + It requires Doxygen, Sphinx, Breathe and LaTeX with PDF support. + The default is NO. + + -DZ80_WITH_PKGCONFIG_SUPPORT=(YES|NO) + Generate and install the pkg-config file. + The default is NO. + + -DZ80_WITH_STANDARD_DOCUMENTS=(YES|NO) + Install the standard text documents distributed with the package: + AUTHORS, COPYING, COPYING.LESSER, HISTORY, README and THANKS. + The default is NO. + + -DZ80_WITH_TESTS=(YES|NO) + Build the testing tool. + The default is NO. + +The second group of package-specific options configures the source code of the +library by predefining macros that enable optional implementations: + + -DZ80_WITH_EXECUTE=(YES|NO) + Build the implementation of the z80_execute function. + The default is NO. + + -DZ80_WITH_FULL_IM0=(YES|NO) + Build the full implementation of the interrupt mode 0 rather than the + reduced one. + The default is NO. + + -DZ80_WITH_Q=(YES|NO) + Build the implementation of the Q "register". + The default is NO. + + -DZ80_WITH_RESET_SIGNAL=(YES|NO) + Build the implementation of the normal RESET signal. + The default is NO. + + -DZ80_WITH_SPECIAL_RESET_SIGNAL=(YES|NO) + Build the implementation of the special RESET signal. + The default is NO. + + -DZ80_WITH_UNOFFICIAL_RETI=(YES|NO) + Configure the ED5Dh, ED6Dh and ED7Dh undocumented instructions as "reti" + instead of "retn". + The default is NO. + + -DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=(YES|NO) + Build the implementation of the bug affecting the Zilog Z80 NMOS, which + causes the P/V flag to be reset when a maskable interrupt is accepted + during the execution of the "ld a,{i|r}" instructions. + The default is NO. + +Package maintainers should use at least the following options for both shared and +static library targets: + + -DZ80_WITH_EXECUTE=YES + -DZ80_WITH_FULL_IM0=YES + -DZ80_WITH_Q=YES + -DZ80_WITH_RESET_SIGNAL=YES + -DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=YES + +Finally, once the build system is configured according to your needs, build and +install the package: + + $ make + # make install/strip + + +4. Integration +=============== + +4.1. As an external dependency in CMake-based projects + +The Z80 library includes find-modules and a config-file package for integration +into CMake-based projects. It is recommended to always copy the FindZ80.cmake +and FindZeta.cmake files into the CMake modules directory of projects that use +the library as an external dependency. This will allow CMake to find the library +if the necessary config-file packages are not installed on the system. + +Both the config-file package and the find-module support dual installations of +the shared and static versions of the Z80 library. You can specify the linking +method by using the component mechanism of "find_package". + +Example: + + find_package(Z80 REQUIRED [Shared|Static]) + target_link_libraries(your-target Z80) + +Omitting the linking method will select the "Shared" version of the library or, +if not installed, the "Static" version instead. + +4.2. As a CMake subproject + +To embed the library as a CMake subproject, just place its entire source tree +into a subdirectory of your project. + +It is advisable to configure the library in the CMakeLists.txt of your project. +This will prevent the user from having to specify configuration options of the +Z80 subproject through the CMake command line when building the main project. +As noted in the "Installation" section of this document, all package-specific +options are prefixed with "Z80_", so, in a normal scenario, there should be no +risk of name collision with the options and variables of the parent project. + +Example: + + set(Z80_SHARED_LIBS NO CACHE BOOL "") + set(Z80_WITH_Q YES CACHE BOOL "") + set(Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG YES CACHE BOOL "") + + add_subdirectory(dependencies/Z80) + target_link_libraries(your-target Z80) + +It is important to set the "Z80_SHARED_LIBS" option. Otherwise CMake will build +the library type indicated by "BUILD_SHARED_LIBS", which may not be the desired +one. + +4.3. Manual integration + +There are several macros that can be used to configure the source code of the +library. You can define those you need in your build system or at the beginning +of the Z80.c file. The following ones allow you to configure the integration of +Z80.h and Z80.c into the project: + + #define Z80_DEPENDENCIES_HEADER "header name.h" + Specifies the only external header to #include, replacing those of Zeta. + If used, it must also be defined before including the Z80.h header. + + #define Z80_STATIC + Needed for compiling and/or using the emulator as a static library or as + an internal part of other project. + If used, it must also be defined before including the Z80.h header. + + #define Z80_WITH_LOCAL_HEADER + Tells Z80.c to #include "Z80.h" instead of . + +The second group of package-specific options, explained in the "Installation" +section of this document, activates various optional implementations in the +source code by predefining the following macros: + + #define Z80_WITH_EXECUTE + #define Z80_WITH_FULL_IM0 + #define Z80_WITH_Q + #define Z80_WITH_RESET_SIGNAL + #define Z80_WITH_SPECIAL_RESET_SIGNAL + #define Z80_WITH_UNOFFICIAL_RETI + #define Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG + +Please note that the activation of some these optional implementations affects +the speed of the emulator due to various factors (read the documentation for +more details). + +As a final note, except for "Z80_DEPENDENCIES_HEADER", the above macros do not +need to be set to a particular token when used, as the source code only checks +whether or not they are defined. + + +5. License +=========== + +This library 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 library 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 library. If not, see . + + +-------------------------------------------------------------------------------- +Last update: 2022-05-16 README EOF diff --git a/README.md b/README.md index 07d6ef6..15c9859 100644 --- a/README.md +++ b/README.md @@ -1,229 +1,633 @@ -![al-tag](http://upload.wikimedia.org/wikipedia/commons/1/19/Zilog_Z80.jpg) +

+
+ Zilog Z80 CPU Emulator +

+

+ + + + + + + Discord +

-
+# Introduction -# Zilog Z80 CPU Emulator -Copyright © 1999-2018 Manuel Sainz de Baranda y Goñi. -Released under the terms of the [GNU General Public License v3](http://www.gnu.org/copyleft/gpl.html). +The [Z80 library](https://zxe.io/software/Z80) implements a fast, small and accurate [emulator](https://en.wikipedia.org/wiki/Emulator) of the [Zilog Z80](https://en.wikipedia.org/wiki/Zilog_Z80). It emulates all that is known to date about this CPU, including the undocumented behaviour, [MEMPTR](https://zxpress.ru/zxnet/zxnet.pc/5909), [Q](https://worldofspectrum.org/forums/discussion/41704) and the [special RESET](http://www.primrosebank.net/computers/z80/z80_special_reset.htm). It also has the honor of having been the first open source project to provide full emulation of the interrupt mode 0. -This is a very accurate [Z80](http://en.wikipedia.org/wiki/Zilog_Z80) [emulator](http://en.wikipedia.org/wiki/Emulator) I wrote many years ago. It has been used in several machine emulators by other people and it has been extensivelly tested. It is fast, small (33 KB when compiled as a x86-64 dynamic library), easy to understand, and **the code is profusely commented**. +The source code is written in [ANSI C](https://en.wikipedia.org/wiki/ANSI_C) for maximum portability and is extensively commented. The aim has been to write a well-structured, easy to understand piece of software; something solid and elegant that can stand the test of time with no need for major changes. -If you are looking for a Zilog Z80 CPU emulator for your project maybe you have found the correct one. I use this core in the [ZX Spectrum emulator](http://github.com/redcode/MicroZX) I started as hobby. -
+# Emulation Accuracy -## Building +The Zilog Z80 emulator has a classic design with instruction-level granularity. This provides the best performance when speed is a critical factor, while still offering a reasonable flexibility to achieve precision down to the T-state level when accuracy is imperative. -You must first install [Z](http://zeta.st), a **header-only** library that provides types and macros. This is the only dependency, the emulator does not use the C standard library or its headers. Then add `Z80.h` and `Z80.c` to your project and configure its build system so that `CPU_Z80_STATIC` and `CPU_Z80_USE_LOCAL_HEADER` are predefined when compiling the sources. +Instruction-level granularity implies that, except in a few well-defined cases, the execution of a given instruction cannot stop until all its internal M-cycles are completed. This kind of emulation is also carried out in an efficient way: the pertinent registers are modified only once per instruction and the T-state counter is updated only after a whole instruction is executed. -If you preffer to build the emulator as a library, you can use [premake4](http://premake.github.io): -```console -$ cd building -$ premake4 gmake # generate Makefile -$ make help # list available targets -$ make [config=] [target] # build the emulator +That said, instructions, flags, memory accesses, interrupt and reset responses, clock cycles, etc. are accurately emulated as far as is known, according to the technical documentation available, the findings made after decades of research on the Z80 and electronic simulations. And, of course, the emulator passes the most exhaustive tests written to date: + + +### Zilog Z80 CPU Test Suite, by [Patrik Rak](https://github.com/raxoft) + +[This set of programs](https://github.com/raxoft/z80test) is intended to help the emulator authors to reach the desired level of the CPU emulation authenticity. Each of the included programs performs an exhaustive computation using each of the tested Z80 instructions, compares the results with values obtained from a real [Sinclair ZX Spectrum 48K](https://en.wikipedia.org/wiki/ZX_Spectrum) with Zilog Z80 CPU, and reports any deviations detected. + +
+ Results + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ z80full.tap +
+
+ Tests all flags and registers. +
+ + +
+ z80doc.tap +
+
+ Tests all registers, but only officially documented flags. +
+ + + +
+ + +
+ z80flags.tap +
+
+ Tests all flags, ignores registers. +
+ + +
+ z80docflags.tap +
+
+ Tests documented flags only, ignores registers. +
+ + + +
+ + +
+ z80ccf.tap +
+
+ Tests all flags after executing ccf after each instruction tested. +
+ + +
+ z80memptr.tap +
+
+ Tests all flags after executing bit n,(hl) after each instruction tested. +
+ + + +
+
+ +### Z80 Test Suite, by Mark Woodmass + +This suite performs a series of tests to verify the MEMPTR documents _([English](http://zx.pk.ru/attachment.php?attachmentid=2989), [Russian](http://zx.pk.ru/attachment.php?attachmentid=2984))_, which are spot on, as well as a brief run through several of the `CBh`/`DDh`/`FDh` opcode ranges. The test results in the program are compared against those from a [NEC D780C-1](https://www.cpu-world.com/CPUs/Z80/NEC-D780C-1.html) CPU, but Simon Conway kindly tested several other Z80 clones, confirming the same results. + +
+ Results + + + + + + + + +
+ + +
+ z80tests.tap +
+
+ + + +
+
+ +### Z80 Instruction Set Exerciser, by Frank D. Cringle + +Frank Cringle's _Z80 Instruction Set Exerciser_ attempts to execute every Z80 opcode, putting them through a cycle of tests and comparing the results to actual results from running the code on a real Z80. The exerciser is supplied with Frank's [YAZE](ftp://ftp.ping.de/pub/misc/emulators/yaze-1.10.tar.gz) (Yet Another Z80 Emulator). It is often difficult to track down, so [Jonathan Graham Harston](http://mdfs.net/User/JGH/) put it together [here](http://mdfs.net/Software/Z80/Exerciser/), as well as [some conversions](http://mdfs.net/Software/Z80/Exerciser/Spectrum/). The latest release of YAZE is available at [Andreas Gerlich's website](http://www.mathematik.uni-ulm.de/users/ag/yaze-ag/). + +
+ Results + + + + + + + + + + + + + + + + + + +
+ + +
+ zexdoc.tap +
+
+ Tests officially documented flag effects. +
+ + +
+ zexall.tap +
+
+ Tests all flags changes. +
+ + + +
+ + +
+ zexall2.tap +
+
+ Patrik Rak's modification with MEMPTR testing. +
+ +
+
+ + +# Installation + +You will need [CMake](https://cmake.org) v3.14 or later to build the package and, optionally, recent versions of [Doxygen](https://www.doxygen.nl), [Sphinx](https://www.sphinx-doc.org) and [Breathe](https://github.com/michaeljones/breathe) to compile the documentation. Also make sure you have [LaTeX](https://www.latex-project.org) with PDF support installed on your system in case you want to generate the documentation in PDF format. + +The emulator requires some types and macros included in [Zeta](https://github.com/redcode/Zeta), a dependency-free, [header-only](https://en.wikipedia.org/wiki/Header-only) library used to retain compatibility with most C compilers. Install Zeta or extract its official source code package to the same directory of this `README.md` or its parent directory. Zeta is the sole dependency; the emulator is a freestanding implementation and as such does not depend on the [C standard library](https://en.wikipedia.org/wiki/C_standard_library). + +Once all requirements are met, create a directory and run `cmake` from there to prepare the build system: + +```shell +mkdir build +cd build +cmake [options] ``` -There is also an Xcode project in `development/Xcode` with several targets: +The resulting build files can be configured by passing options to `cmake`. To show a complete list of those available along with their current settings, type the following: -Target | Description ---- | --- -Z80 (dynamic) | Shared library. -Z80 (dynamic module) | Shared library with a generic module ABI to be used in modular multi-machine emulators. -Z80 (static) | Static library. -Z80 (static module) | Static library with a generic CPU emulator ABI to be used in monolithic multi-machine emulators. - -
- -## Code configuration - -There are some predefined macros that control the compilation: - -Name | Description ---- | --- -`CPU_Z80_DEPENDENCIES_H` | If defined, it replaces the inclusion of any external header with this one. If you don't want to use Z, you can provide your own header with the types and macros used by the emulator. -`CPU_Z80_HIDE_ABI` | Makes the generic CPU emulator ABI private. -`CPU_Z80_HIDE_API` | Makes the public functions private. -`CPU_Z80_STATIC` | You need to define this to compile or use the emulator as a static library or if you have added `Z80.h` and `Z80.c` to your project. -`CPU_Z80_USE_LOCAL_HEADER` | Use this if you have imported `Z80.h` and `Z80.c` to your project. `Z80.c` will `#include "Z80.h"` instead of ``. -`CPU_Z80_WITH_ABI` | Builds the generic CPU emulator ABI and declares its prototype in `Z80.h`. -`CPU_Z80_WITH_MODULE_ABI` | Builds the generic module ABI. This macro also enables `CPU_Z80_WITH_ABI`, so the generic CPU emulator ABI will be built too. This option is intended to be used when building a true module loadable at runtime with `dlopen()`, `LoadLibrary()` or similar. The ABI module can be accessed via the [weak symbol](http://en.wikipedia.org/wiki/Weak_symbol) `__module_abi__`. - -
- -## API: `Z80` emulator instance - -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: `context`, `read`, `write`, `in`, `out`, `int_data` and `halt`. - -```C -zusize cycles; +```shell +cmake -LAH ``` -**Description** -Number of cycles executed in the current call to `z80_run`. -**Details** -`z80run` sets this variable to `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. -```C -void *context; +If in doubt, read the [CMake documentation](https://cmake.org/documentation/) for more information on configuration options. The following are some of the most relevant standard options of CMake: + +* **`-DBUILD_SHARED_LIBS=(YES|NO)`** + Build the project as a shared library rather than a static one. + The default is `NO`. + +* **`-DCMAKE_BUILD_TYPE=(Debug|Release|RelWithDebInfo)`** + Choose the type of build (configuration) to generate. + The default is `Release`. + +* **`-DCMAKE_INSTALL_PREFIX=""`** + Specify the installation prefix on [UNIX](https://en.wikipedia.org/wiki/Unix) and [UNIX-like](https://en.wikipedia.org/wiki/Unix-like) operating systems. + The default is `"/usr/local"`. + +Package-specific options are prefixed with `Z80_` and can be divided into two groups. The first one controls aspects not related to the source code of the library: + +* **`-DZ80_DOWNLOAD_TEST_FILES=(YES|NO)`** + Download the firmware and software used by the testing tool. + The default is `NO`. + +* **`-DZ80_INSTALL_CMAKEDIR=""`** + Specify the directory in which to install the CMake [config-file package](https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#config-file-packages). + The default is `"${CMAKE_INSTALL_LIBDIR}/cmake/Z80"`. + +* **`-DZ80_INSTALL_PKGCONFIGDIR=""`** + Specify the directory in which to install the [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config) [file](https://people.freedesktop.org/~dbn/pkg-config-guide.html). + The default is `"${CMAKE_INSTALL_LIBDIR}/pkgconfig"`. + +* **`-DZ80_NOSTDLIB_FLAGS=(Auto|[[;...]])`** + Specify the linker flags used to avoid linking against system libraries. + The default is `Auto` (autoconfigure flags). If you get linker errors, set this option to `""`. + +* **`-DZ80_SHARED_LIBS=(YES|NO)`** + Build the project as a shared library rather than a static one. + This option takes precedence over `BUILD_SHARED_LIBS`. + Not defined by default. + +* **`-DZ80_SPHINX_HTML_THEME="[]"`** + Specify the Sphinx theme for the documentation in HTML format. + The default is `""` (use the default theme). + +* **`-DZ80_WITH_CMAKE_SUPPORT=(YES|NO)`** + Generate and install the CMake [config-file package](https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#config-file-packages). + The default is `NO`. + +* **`-DZ80_WITH_HTML_DOCUMENTATION=(YES|NO)`** + Build and install the documentation in HTML format. + It requires Doxygen, Sphinx and Breathe. + The default is `NO`. + +* **`-DZ80_WITH_PDF_DOCUMENTATION=(YES|NO)`** + Build and install the documentation in PDF format. + It requires Doxygen, Sphinx, Breathe and LaTeX with PDF support. + The default is `NO`. + +* **`-DZ80_WITH_PKGCONFIG_SUPPORT=(YES|NO)`** + Generate and install the [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config) [file](https://people.freedesktop.org/~dbn/pkg-config-guide.html). + The default is `NO`. + +* **`-DZ80_WITH_STANDARD_DOCUMENTS=(YES|NO)`** + Install the standard text documents distributed with the package: `AUTHORS`, `COPYING`, `COPYING.LESSER`, `HISTORY`, `README` and `THANKS`. + The default is `NO`. + +* **`-DZ80_WITH_TESTS=(YES|NO)`** + Build the testing tool. + The default is `NO`. + +The second group of package-specific options configures the source code of the library by predefining macros that enable optional implementations: + +* **`-DZ80_WITH_EXECUTE=(YES|NO)`** + Build the implementation of the `z80_execute` function. + The default is `NO`. + +* **`-DZ80_WITH_FULL_IM0=(YES|NO)`** + Build the full implementation of the interrupt mode 0 rather than the reduced one. + The default is `NO`. + +* **`-DZ80_WITH_Q=(YES|NO)`** + Build the implementation of the [Q "register"](https://worldofspectrum.org/forums/discussion/41704). + The default is `NO`. + +* **`-DZ80_WITH_RESET_SIGNAL=(YES|NO)`** + Build the implementation of the normal RESET signal. + The default is `NO`. + +* **`-DZ80_WITH_SPECIAL_RESET_SIGNAL=(YES|NO)`** + Build the implementation of the [special RESET](http://www.primrosebank.net/computers/z80/z80_special_reset.htm) signal. + The default is `NO`. + +* **`-DZ80_WITH_UNOFFICIAL_RETI=(YES|NO)`** + Configure the `ED5Dh`, `ED6Dh` and `ED7Dh` undocumented instructions as `reti` instead of `retn`. + The default is `NO`. + +* **`-DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=(YES|NO)`** + Build the implementation of the bug affecting the Zilog Z80 NMOS, which causes the P/V flag to be reset when a maskable interrupt is accepted during the execution of the `ld a,{i|r}` instructions. + The default is `NO`. + +Package maintainers should use at least the following options for both shared and static library targets: + +```shell +-DZ80_WITH_EXECUTE=YES +-DZ80_WITH_FULL_IM0=YES +-DZ80_WITH_Q=YES +-DZ80_WITH_RESET_SIGNAL=YES +-DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=YES ``` -**Description** -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. -```C -zuint8 (* read)(void *context, zuint16 address); +Finally, once the build system is configured according to your needs, build and install the package: + +```shell +make +make install/strip ``` -**Description** -Callback: Called when the CPU needs to read 8 bits from memory. -**Parameters** -`context` → The value of the member `context`. -`address` → The memory address to read from. -**Returns** -The 8 bits read from memory. -```C -void (* write)(void *context, zuint16 address, zuint8 value); +# Integration + +### As an external dependency in CMake-based projects + +The Z80 library includes find-modules and a config-file package for integration into CMake-based projects. It is recommended to always copy the `FindZ80.cmake` and `FindZeta.cmake` files into the CMake modules directory of projects that use the library as an external dependency. This will allow CMake to find the library if the necessary config-file packages are not installed on the system. + +Both the config-file package and the find-module support dual installations of the shared and static versions of the Z80 library. You can specify the linking method by using the component mechanism of `find_package`. + +Example: + +```cmake +find_package(Z80 REQUIRED [Shared|Static]) +target_link_libraries(your-target Z80) ``` -**Description** -Callback: Called when the CPU needs to write 8 bits to memory. -**Parameters** -`context` → The value of the member `context`. -`address` → The memory address to write to. -`value` → The value to write. -```C -zuint8 (* in)(void *context, zuint16 port); +Omitting the linking method will select the `Shared` version of the library or, if not installed, the `Static` version instead. + +### As a CMake subproject + +To embed the library as a CMake subproject, just place its entire source tree into a subdirectory of your project. + +It is advisable to configure the library in the CMakeLists.txt of your project. This will prevent the user from having to specify configuration options of the Z80 subproject through the CMake command line when building the main project. As noted in the "Installation" section of this document, all package-specific options are prefixed with `Z80_`, so, in a normal scenario, there should be no risk of name collision with the options and variables of the parent project. + +Example: + +```cmake +set(Z80_SHARED_LIBS NO CACHE BOOL "") +set(Z80_WITH_Q YES CACHE BOOL "") +set(Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG YES CACHE BOOL "") + +add_subdirectory(dependencies/Z80) +target_link_libraries(your-target Z80) ``` -**Description** -Callback: Called when the CPU needs to read 8 bits from an I/O port. -**Parameters** -`context` → The value of the member `context`. -`port` → The number of the I/O port to read from. -**Returns** -The 8 bits read from the I/O port. -```C -void (* out)(void *context, zuint16 port, zuint8 value); -``` -**Description** -Callback: Called when the CPU needs to write 8 bits to an I/O port. -**Parameters** -`context` → The value of the member `context`. -`port` → The number of the I/O port to write to. -`value` → The value to write. +It is important to set the `Z80_SHARED_LIBS` option. Otherwise CMake will build the library type indicated by `BUILD_SHARED_LIBS`, which may not be the desired one. -```C -zuint32 (* int_data)(void *context); -``` -**Description** -Callback: Called when the CPU needs to read one instruction from the data bus to service a maskable interrupt (`INT`) in mode 0. -**Parameters** -`context` → The value of the member `context`. -**Returns** -A 32-bit value containing the bytes of an instruction. The instruction must begin at the most significant byte (big endian). +### Manual integration -```C -void (* halt)(void *context, zboolean state); -``` -**Description** -Callback: Called when the CPU enters or exits the halt state. -**Note** -This callback is **optional** and must be set to `NULL` if not used. -**Parameters** -`context` → The value of the member `context`. -`state` → `TRUE` if halted; `FALSE` otherwise. +There are several macros that can be used to configure the source code of the library. You can define those you need in your build system or at the beginning of the `Z80.c` file. The following ones allow you to configure the integration of `Z80.h` and `Z80.c` into the project: -```C -ZZ80State state; -``` -**Description** -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 data source. +* **`#define Z80_DEPENDENCIES_HEADER "header name.h"`** + Specifies the only external header to `#include`, replacing those of Zeta. + If used, it must also be defined before including the `Z80.h` header. -```C -zuint8 r7; -``` -**Description** -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 `z80_run` and restored before returning. If an instruction directly affects the R register, this variable is also updated accordingly. +* **`#define Z80_STATIC`** + Needed for compiling and/or using the emulator as a static library or as an internal part of other project. + If used, it must also be defined before including the `Z80.h` header. -```C -Z16Bit xy; -``` -**Description** -Temporay IX/IY register for instructions with `DDh`/`FDh` prefix. -**Details** -Since instructions with prefix `DDh` and `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. +* **`#define Z80_WITH_LOCAL_HEADER`** + Tells `Z80.c` to `#include "Z80.h"` instead of ``. -```C -Z32Bit data; -``` -**Description** -Temporary storage for opcode fetching. -**Details** -This is an internal private variable. +The second group of package-specific options, explained in the "Installation" section of this document, activates various optional implementations in the source code by predefining the following macros: -
+* **`#define Z80_WITH_EXECUTE`** +* **`#define Z80_WITH_FULL_IM0`** +* **`#define Z80_WITH_Q`** +* **`#define Z80_WITH_RESET_SIGNAL`** +* **`#define Z80_WITH_SPECIAL_RESET_SIGNAL`** +* **`#define Z80_WITH_UNOFFICIAL_RETI`** +* **`#define Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG`** -## API: Public Functions +Please note that the activation of some these optional implementations affects the speed of the emulator due to various factors (read the documentation for more details). -```C -void z80_power(Z80 *object, zboolean state); -``` -**Description** -Changes the CPU power status. -**Parameters** -`object` → A pointer to a Z80 emulator instance. -`state` → `TRUE` = power ON; `FALSE` = power OFF. +As a final note, except for `Z80_DEPENDENCIES_HEADER`, the above macros do not need to be set to a particular token when used, as the source code only checks whether or not they are defined. -```C -void z80_reset(Z80 *object); -``` -**Description** -Resets the CPU. -**Details** -This is equivalent to a pulse on the `RESET` line of a real Z80. -**Parameters** -`object` → A pointer to a Z80 emulator instance. +# Showcase -```C -zusize z80_run(Z80 *object, zusize cycles); -``` -**Description** -Runs the CPU for a given number of `cycles`. -**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 `cycles` specfified. -**Parameters** -`object` → A pointer to a Z80 emulator instance. -`cycles` → The number of cycles to be executed. -**Returns** -The number of cycles executed. +This emulator has been used by the following projects (listed in alphabetical order): -```C -void z80_nmi(Z80 *object); -``` -**Description** -Performs a non-maskable interrupt (NMI). -**Details** -This is equivalent to a pulse on the `NMI` line of a real Z80. -**Parameters** -`object` → A pointer to a Z80 emulator instance. +* [Augmentinel](https://simonowen.com/spectrum/augmentinel/), _by [Simon Owen](https://simonowen.com/)_ - [GitHub](https://github.com/simonowen/augmentinel) +* CPM-Emulator, _by [Marc Sibert](https://github.com/Marcussacapuces91)_ - [GitHub](https://github.com/Marcussacapuces91/CPM-Emulator) +* [tihle: a unique TI calculator emulator](https://www.taricorp.net/2020/introducing-tihle/), _by [Peter Marheine](https://www.taricorp.net/about/)_ - [GitHub](https://github.com/tari/tihle), [GitLab](https://gitlab.com/taricorp/tihle) +* [TileMap](https://simonowen.com/spectrum/tilemap/), _by [Simon Owen](https://simonowen.com/)_ - [GitHub](https://github.com/simonowen/tilemap) +* [Zemu](https://github.com/jayvalentine/zemu), _by [Jay Valentine](https://jayvalentine.github.io/)_ - [GitHub](https://github.com/jayvalentine/zemu), [RubyGems](https://rubygems.org/gems/zemu) +* Others... -```C -void z80_int(Z80 *object, zboolean state); -``` -**Description** -Changes the state of the maskable interrupt (INT). -**Details** -This is equivalent to a change on the `INT` line of a real Z80. -**Parameters** -`object` → A pointer to a Z80 emulator instance. -`state` → `TRUE` = line high; `FALSE` = line low. +# Thanks -
+Many thanks to the following individuals (in alphabetical order): -## Use in Proprietary Software -This library is released under the terms of the [GNU General Public License v3](http://www.gnu.org/copyleft/gpl.html), but I can license it for non-free/propietary projects if you contact [me](mailto:contact@zxe.io?subject=Z80%20non-free%20licensing). +* **Akimov, Vadim (lvd)** + * For testing the library on many different platforms and CPU architectures. +* **azesmbog** + 1. For validating tests on real hardware [[1](#r1)]. + 2. For his research on the unstable flag behavior of the `ccf/scf` instructions. + 3. For his invaluable help. +* **Banks, David (hoglet)** + 1. For cracking the flag behavior of the block instructions [[2](#r2), [3](#r3)]. + 2. For his research on the flag behavior of the `ccf/scf` instructions [[3](#r3)]. +* **Beliansky, Anatoly (Tolik_Trek)** + * For validating tests on real hardware [[4](#r4)]. +* **Bobrowski, Jan** + * For fixing the _"Z80 Full Instruction Set Exerciser for Spectrum"_ [[5](#r5)]. +* **boo_boo** + * For cracking the behavior of the MEMPTR register [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Brady, Stuart** + * For his research on the flag behavior of the `ccf/scf` instructions [[10](#r10)]. +* **Brewer, Tony** + 1. For his research on the special RESET [[11](#r11)]. + 2. For sharing information about the RESET signal [[12](#r12)]. + 3. For helping to crack the flag behavior of the block instructions [[2](#r2)]. + 4. For performing low-level tests on real hardware [[2](#r2)]. + 5. For helping me to test different undocumented behaviors of the Zilog Z80. +* **Bystrov, Dmitry (Alone Coder)** + * For validating tests on real hardware [[4](#r4)]. +* **Chunin, Roman (CHRV)** + * For testing the behavior of the MEMPTR register on real Z80 chips [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Conway, Simon (BadBeard)** + * For validating the _"Z80 Test Suite"_ on several Z80 clones [[13](#r13)]. +* **Cooke, Simon** + * For finding out how the `out (c),0` instruction behaves on the Zilog Z80 CMOS [[14](#r14)]. +* **Cringle, Frank D.** + * For writing the _"Z80 Instruction Set Exerciser"_ [[15](#r15)]. +* **Devic, Goran** + * For his research on undocumented behaviors of the Z80 CPU [[16](#r16)]. +* **Flammenkamp, Achim** + * For his article on Z80 interrupts [[17](#r17)]. +* **Gimeno Fortea, Pedro** + 1. For his research work [[18](#r18)]. + 2. For writing the first-ever ZX Spectrum emulator [[19](#r19), [20](#r20)]. +* **goodboy** + * For testing the behavior of the MEMPTR register on real Z80 chips [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Greenway, Ian** + * For testing the flag behavior of the `ccf/scf` instructions on real hardware [[10](#r10), [21](#r21)]. +* **Harston, Jonathan Graham** + 1. For his technical documents about the Zilog Z80 [[22](#r22), [23](#r23), [24](#r24)]. + 2. For porting the _"Z80 Instruction Set Exerciser"_ to the ZX Spectrum [[25](#r25)]. +* **Helcmanovsky, Peter (Ped7g)** + 1. For helping me to write the _"IN-MEMPTR"_ test. + 2. For writing the _"Z80 Block Flags Test"_ [[26](#r26), [27](#r27)]. + 3. For writing the _"Z80 CCF SCF Outcome Stability"_ test [[27](#r27)]. + 4. For writing the _"Z80 INT Skip"_ test [[27](#r27)]. + 5. For his research on the unstable flag behavior of the `ccf/scf` instructions. + 6. For his invaluable help. +* **icebear** + * For testing the behavior of the MEMPTR register on real Z80 chips [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Kladov, Vladimir** + * For cracking the behavior of the MEMPTR register [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Krook, Magnus** + * For validating tests on real hardware [[28](#r28)]. +* **London, Matthew** + * For validating tests on real hardware. +* **Molodtsov, Aleksandr** + * For testing the behavior of the MEMPTR register on real Z80 chips [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Nair, Arjun** + * For validating tests on real hardware [[26](#r26)]. +* **Nicolás-González, César** + * For helping me to research the unstable flag behavior of the `ccf/scf` instructions. +* **Ortega Sosa, Sofía** + * For her support. +* **Owen, Simon** + * For the idea of the hooking method used in this emulator. +* **Rak, Patrik** + 1. For improving the _"Z80 Instruction Set Exerciser for Spectrum"_ [[29](#r29)]. + 2. For cracking the flag behavior of the `ccf/scf` instructions [[13](#r13), [29](#r29)]. + 3. For writing the _"Zilog Z80 CPU Test Suite"_ [[29](#r29), [30](#r30)]. + 4. For his research on the unstable flag behavior of the `ccf/scf` instructions. +* **Rodríguez Jódar, Miguel Ángel (mcleod_ideafix)** + * For his reseach on the state of the registers after POWER/RESET [[31](#r31)]. +* **Rodríguez Palomino, Mario (r-lyeh)** + * For teaching me how emulators work. +* **Sainz de Baranda y Romero, Manuel** + * For teaching me programming and giving me my first computer. +* **Sánchez Ordiñana, José Ismael (Vaporatorius)** + * For validating tests on real hardware [[32](#r32), [33](#r33)]. +* **Stevenson, Dave** + 1. For testing the special RESET on real hardware [[11](#r11)]. + 2. For performing low-level tests on real hardware [[34](#r34)]. +* **Weissflog, Andre (Floh)** + 1. For finding out that the "reti/retn" instructions defer the acceptance of the maskable interrupt [[35](#r35)]. + 2. For writing the "Visual Z80 Remix" simulator [[36](#r36)]. +* **Wilkinson, Oli (evolutional)** + * For validating tests on real hardware [[26](#r26)]. +* **Wlodek** + * For testing the behavior of the MEMPTR register on real Z80 chips [[6](#r6), [7](#r7), [8](#r8), [9](#r9)]. +* **Woodmass, Mark (Woody)** + 1. For his invaluable contributions to the emuscene. + 2. For writing the _"Z80 Test Suite"_ [[13](#r13)]. + 3. For his research on the flag behavior of the `ccf/scf` instructions [[37](#r37)]. + 4. For writing the _"HALT2INT"_ test. + 5. For writing the _"EIHALT"_ test. +* **Young, Sean** + 1. For his research work. + 2. For his technical documents about the Zilog Z80 [[18](#r18), [38](#r38)]. +* **ZXGuesser** + * For validating tests on real hardware. + +### References + +1. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83384#p83384 +2. https://stardot.org.uk/forums/viewtopic.php?t=15464 + * https://stardot.org.uk/forums/viewtopic.php?p=211042#p211042 + * https://stardot.org.uk/forums/viewtopic.php?p=212021#p212021 +3. Banks, David (2018-08-21). _"Undocumented Z80 Flags"_. + * https://github.com/hoglet67/Z80Decoder/wiki/Undocumented-Flags + * https://stardot.org.uk/forums/download/file.php?id=39831 +4. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83041#p83041 +5. http://wizard.ae.krakow.pl/~jb/qaop/tests.html +6. https://zxpress.ru/zxnet/zxnet.pc/5909 +7. https://zx-pk.ru/threads/2506-komanda-bit-n-(hl).html +8. https://zx-pk.ru/threads/2586-prosba-realshchikam-ot-emulyatorshchikov.html +9. boo_boo; Kladov, Vladimir (2006-03-29). _"MEMPTR: esoteric register of the Zilog Z80 CPU"_. + * http://zx.pk.ru/showpost.php?p=43688 + * http://zx.pk.ru/attachment.php?attachmentid=2984 + * http://zx.pk.ru/showpost.php?p=43800 + * http://zx.pk.ru/attachment.php?attachmentid=2989 +10. https://sourceforge.net/p/fuse-emulator/mailman/message/6929573 +11. Brewer, Tony (2014-12). _"Z80 Special Reset"_. + * http://primrosebank.net/computers/z80/z80_special_reset.htm +12. https://mtxworld.dk/memorum/viewtopic.php?p=1352#p1352 +13. https://worldofspectrum.org/forums/discussion/20345 +14. https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJ +15. Cringle, Frank D. (1998-01-28). _"Yaze - Yet Another Z80 Emulator"_ v1.10. + * ftp://ftp.ping.de/pub/misc/emulators/yaze-1.10.tar.gz +16. https://baltazarstudios.com/zilog-z80-undocumented-behavior +17. Flammenkamp, Achim. _"Interrupt Behaviour of the Z80 CPU"_. + * http://z80.info/interrup.htm +18. Young, Sean (1998-10). _"Z80 Undocumented Features (in Software Behaviour)"_. +19. https://elmundodelspectrum.com/desenterrando-el-primer-emulador-de-spectrum +20. https://elmundodelspectrum.com/con-vosotros-el-emulador-de-pedro-gimeno-1989 +21. https://sourceforge.net/p/fuse-emulator/mailman/message/4502844 +22. Harston, Jonathan Graham (1997-09-09). _"Z80 Opcode Map"_. + * https://mdfs.net/Docs/Comp/Z80/OpCodeMap +23. Harston, Jonathan Graham (1997-12-18). _"Z80 Microprocessor Undocumented Instructions"_. + * https://mdfs.net/Docs/Comp/Z80/UnDocOps +24. Harston, Jonathan Graham (1998-04-15). _"Full Z80 Opcode List Including Undocumented Opcodes"_. + * https://mdfs.net/Docs/Comp/Z80/OpList +25. https://mdfs.net/Software/Z80/Exerciser/Spectrum +26. https://spectrumcomputing.co.uk/forums/viewtopic.php?t=6102 +27. https://github.com/MrKWatkins/ZXSpectrumNextTests +28. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83157#p83157 +29. https://worldofspectrum.org/forums/discussion/41704 + * http://zxds.raxoft.cz/taps/misc/zexall2.zip +30. https://worldofspectrum.org/forums/discussion/41834 + * http://zxds.raxoft.cz/taps/misc/z80test-1.0.zip + * https://github.com/raxoft/z80test +31. https://worldofspectrum.org/forums/discussion/34574 +32. https://worldofspectrum.org/forums/discussion/comment/668760/#Comment_668760 +33. https://jisanchez.com/test-a-dos-placas-de-zx-spectrum +34. https://stardot.org.uk/forums/viewtopic.php?p=212360#p212360 +35. https://floooh.github.io/2021/12/17/cycle-stepped-z80.html +36. https://github.com/floooh/v6502r +37. http://groups.google.co.uk/group/comp.sys.sinclair/msg/56dd1fd4ccb5fb3b +38. Young, Sean (2005-09-18). _"Undocumented Z80 Documented, The"_. + * http://www.myquest.nl/z80undocumented + * http://www.myquest.nl/z80undocumented/z80-documented-v0.91.pdf + +# Thanks + +Alexander Molodtsov, +boo_boo, +CHRV, +Dave Stevenson, +Frank D. Cringle, +goodboy, +Goran Devic, +[Ian Greenway](http://www.lasernet.plus.com), +icebear, +Manuel Sainz de Baranda y Romero, +[Mario Rodríguez Palomino](https://github.com/r-lyeh), +Mark Woodmass, +[Miguel Ángel Rodríguez Jódar](https://github.com/mcleod-ideafix), +[Patrik Rak](https://github.com/raxoft), +[Pedro Gimeno Fortea](http://www.formauri.es/personal/pgimeno/), +Sean Young, +Simon Conway, +Simon Cooke, +Simon Owen, +[Sofía Ortega Sosa](https://github.com/agaxia), +Stuart Brady, +Tony Brewer, +Vladimir Kladov, +Wlodek + +Read the [THANKS](THANKS) file for details. + +# License + +Copyright © 1999-2022 Manuel Sainz de Baranda y Goñi. + + + +This emulator is [free software](https://www.gnu.org/philosophy/free-sw.html): you can redistribute it and/or modify it under the terms of the **[GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.en.html)** as published by the [Free Software Foundation](https://www.fsf.org), 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](COPYING.LESSER) of the GNU Lesser General Public License along with this emulator. If not, see . + +# Special licensing + +Projects where the terms of the GNU Lesser General Public License prevent the use of this library, or require unwanted publication of the source code of commercial products, may [apply for a special license](mailto:manuel@zxe.io?subject=Z80). diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..f06edd4 --- /dev/null +++ b/THANKS @@ -0,0 +1,180 @@ +Many thanks to the following individuals (in alphabetical order): + +* Akimov, Vadim (lvd) + For testing the library on many different platforms and CPU architectures. +* azesmbog + 1. For validating tests on real hardware [1]. + 2. For his research on the unstable flag behavior of the "ccf/scf" + instructions. + 3. For his invaluable help. +* Banks, David (hoglet) + 1. For cracking the flag behavior of the block instructions [2,3]. + 2. For his research on the flag behavior of the "ccf/scf" instructions [3]. +* Beliansky, Anatoly (Tolik_Trek) + For validating tests on real hardware [4]. +* Bobrowski, Jan + For fixing the "Z80 Full Instruction Set Exerciser for Spectrum" [5]. +* boo_boo + For cracking the behavior of the MEMPTR register [6,7,8,9]. +* Brady, Stuart + For his research on the flag behavior of the "ccf/scf" instructions [10]. +* Brewer, Tony + 1. For his research on the special RESET [11]. + 2. For sharing information about the RESET signal [12]. + 3. For helping to crack the flag behavior of the block instructions [2]. + 4. For performing low-level tests on real hardware [2]. + 5. For helping me to test different undocumented behaviors of the Zilog Z80. +* Bystrov, Dmitry (Alone Coder) + For validating tests on real hardware [4]. +* Chunin, Roman (CHRV) + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. +* Conway, Simon (BadBeard) + For validating the "Z80 Test Suite" on several Z80 clones [13]. +* Cooke, Simon + For finding out how the "out (c),0" instruction behaves on the Zilog Z80 + CMOS [14]. +* Cringle, Frank D. + For writing the "Z80 Instruction Set Exerciser" [15]. +* Devic, Goran + For his research on undocumented behaviors of the Z80 CPU [16]. +* Flammenkamp, Achim + * For his article on Z80 interrupts [17]. +* Gimeno Fortea, Pedro + 1. For his research work [18]. + 2. For writing the first-ever ZX Spectrum emulator [19,20]. +* goodboy + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. +* Greenway, Ian + For testing the flag behavior of the "ccf/scf" instructions on real hardware + [10,21]. +* Harston, Jonathan Graham + 1. For his technical documents about the Zilog Z80 [22,23,24]. + 2. For porting the "Z80 Instruction Set Exerciser" to the ZX Spectrum [25]. +* Helcmanovsky, Peter (Ped7g) + 1. For helping me to write the "IN-MEMPTR" test. + 2. For writing the "Z80 Block Flags Test" [26,27]. + 3. For writing the "Z80 CCF SCF Outcome Stability" test [27]. + 4. For writing the "Z80 INT Skip" test [27]. + 5. For his research on the unstable flag behavior of the "ccf/scf" + instructions. + 6. For his invaluable help. +* icebear + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. +* Kladov, Vladimir + For cracking the behavior of the MEMPTR register [6,7,8,9]. +* Krook, Magnus + For validating tests on real hardware [28]. +* London, Matthew + For validating tests on real hardware. +* Molodtsov, Aleksandr + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. +* Nair, Arjun + For validating tests on real hardware [26]. +* Nicolás-González, César + For helping me to research the unstable flag behavior of the "ccf/scf" + instructions. +* Ortega Sosa, Sofía + For her support. +* Owen, Simon + For the idea of the hooking method used in this emulator. +* Rak, Patrik + 1. For improving the "Z80 Instruction Set Exerciser for Spectrum" [29]. + 2. For cracking the flag behavior of the "ccf/scf" instructions [13,29]. + 3. For writing the "Zilog Z80 CPU Test Suite" [29,30]. + 4. For his research on the unstable flag behavior of the "ccf/scf" + instructions. +* Rodríguez Jódar, Miguel Ángel (mcleod_ideafix) + For his reseach on the state of the registers after POWER/RESET [31]. +* Rodríguez Palomino, Mario (r-lyeh) + For teaching me how emulators work. +* Sainz de Baranda y Romero, Manuel + For teaching me programming and giving me my first computer. +* Sánchez Ordiñana, José Ismael (Vaporatorius) + For validating tests on real hardware [32,33]. +* Stevenson, Dave + 1. For testing the special RESET on real hardware [11]. + 2. For performing low-level tests on real hardware [34]. +* Weissflog, Andre (Floh) + 1. For finding out that the "reti/retn" instructions defer the acceptance of + the maskable interrupt [35]. + 2. For writing the "Visual Z80 Remix" simulator [36]. +* Wilkinson, Oli (evolutional) + For validating tests on real hardware [26]. +* Wlodek + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. +* Woodmass, Mark (Woody) + 1. For his invaluable contributions to the emuscene. + 2. For writing the "Z80 Test Suite" [13]. + 3. For his research on the flag behavior of the "ccf/scf" instructions [37]. + 4. For writing the "HALT2INT" test. + 5. For writing the "EIHALT" test. +* Young, Sean + 1. For his research work. + 2. For his technical documents about the Zilog Z80 [18,38]. +* ZXGuesser + For validating tests on real hardware. + + +References +========== + + 1. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83384#p83384 + 2. https://stardot.org.uk/forums/viewtopic.php?t=15464 + * https://stardot.org.uk/forums/viewtopic.php?p=211042#p211042 + * https://stardot.org.uk/forums/viewtopic.php?p=212021#p212021 + 3. Banks, David (2018-08-21). "Undocumented Z80 Flags". + * https://github.com/hoglet67/Z80Decoder/wiki/Undocumented-Flags + * https://stardot.org.uk/forums/download/file.php?id=39831 + 4. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83041#p83041 + 5. http://wizard.ae.krakow.pl/~jb/qaop/tests.html + 6. https://zxpress.ru/zxnet/zxnet.pc/5909 + 7. https://zx-pk.ru/threads/2506-komanda-bit-n-(hl).html + 8. https://zx-pk.ru/threads/2586-prosba-realshchikam-ot-emulyatorshchikov.html + 9. boo_boo; Kladov, Vladimir (2006-03-29). "MEMPTR: esoteric register of the + Zilog Z80 CPU". + * http://zx.pk.ru/showpost.php?p=43688 + * http://zx.pk.ru/attachment.php?attachmentid=2984 + * http://zx.pk.ru/showpost.php?p=43800 + * http://zx.pk.ru/attachment.php?attachmentid=2989 +10. https://sourceforge.net/p/fuse-emulator/mailman/message/6929573 +11. Brewer, Tony (2014-12). "Z80 Special Reset". + * http://primrosebank.net/computers/z80/z80_special_reset.htm +12. https://mtxworld.dk/memorum/viewtopic.php?p=1352#p1352 +13. https://worldofspectrum.org/forums/discussion/20345 +14. https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJ +15. Cringle, Frank D. (1998-01-28). "Yaze - Yet Another Z80 Emulator" v1.10. + * ftp://ftp.ping.de/pub/misc/emulators/yaze-1.10.tar.gz +16. https://baltazarstudios.com/zilog-z80-undocumented-behavior +17. Flammenkamp, Achim. "Interrupt Behaviour of the Z80 CPU". + * http://z80.info/interrup.htm +18. Young, Sean (1998-10). "Z80 Undocumented Features (in Software Behaviour)". +19. https://elmundodelspectrum.com/desenterrando-el-primer-emulador-de-spectrum +20. https://elmundodelspectrum.com/con-vosotros-el-emulador-de-pedro-gimeno-1989 +21. https://sourceforge.net/p/fuse-emulator/mailman/message/4502844 +22. Harston, Jonathan Graham (1997-09-09). "Z80 Opcode Map". + * https://mdfs.net/Docs/Comp/Z80/OpCodeMap +23. Harston, Jonathan Graham (1997-12-18). "Z80 Microprocessor Undocumented + Instructions". + * https://mdfs.net/Docs/Comp/Z80/UnDocOps +24. Harston, Jonathan Graham (1998-04-15). "Full Z80 Opcode List Including + Undocumented Opcodes". + * https://mdfs.net/Docs/Comp/Z80/OpList +25. https://mdfs.net/Software/Z80/Exerciser/Spectrum +26. https://spectrumcomputing.co.uk/forums/viewtopic.php?t=6102 +27. https://github.com/MrKWatkins/ZXSpectrumNextTests +28. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83157#p83157 +29. https://worldofspectrum.org/forums/discussion/41704 + * http://zxds.raxoft.cz/taps/misc/zexall2.zip +30. https://worldofspectrum.org/forums/discussion/41834 + * http://zxds.raxoft.cz/taps/misc/z80test-1.0.zip + * https://github.com/raxoft/z80test +31. https://worldofspectrum.org/forums/discussion/34574 +32. https://worldofspectrum.org/forums/discussion/comment/668760/#Comment_668760 +33. https://jisanchez.com/test-a-dos-placas-de-zx-spectrum +34. https://stardot.org.uk/forums/viewtopic.php?p=212360#p212360 +35. https://floooh.github.io/2021/12/17/cycle-stepped-z80.html +36. https://github.com/floooh/v6502r +37. http://groups.google.co.uk/group/comp.sys.sinclair/msg/56dd1fd4ccb5fb3b +38. Young, Sean (2005-09-18). "Undocumented Z80 Documented, The". + * http://www.myquest.nl/z80undocumented + * http://www.myquest.nl/z80undocumented/z80-documented-v0.91.pdf diff --git a/building/.gitignore b/building/.gitignore deleted file mode 100644 index 1d2223e..0000000 --- a/building/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -** -!.gitignore -!premake4.lua -!premake5.lua diff --git a/building/premake4.lua b/building/premake4.lua deleted file mode 100644 index bdf799f..0000000 --- a/building/premake4.lua +++ /dev/null @@ -1,33 +0,0 @@ -solution "Z80" - configurations { - "release-dynamic", "release-dynamic-module", "release-static", "release-static-module", - "debug-dynamic", "debug-dynamic-module", "debug-static", "debug-static-module" - } - - project "Z80" - language "C" - flags {"ExtraWarnings"} - files {"../sources/**.c"} - includedirs {"../API"} - --buildoptions {"-std=c89 -pedantic -Wall -Weverything"} - - configuration "release*" - targetdir "lib/release" - flags {"Optimize"} - - configuration "debug*" - targetdir "lib/debug" - flags {"Symbols"} - - configuration "*dynamic*" - kind "SharedLib" - - configuration "*dynamic-module" - defines {"CPU_Z80_WITH_MODULE_ABI"} - - configuration "*static*" - kind "StaticLib" - defines {"CPU_Z80_STATIC"} - - configuration "*static-module" - defines {"CPU_Z80_WITH_ABI"} diff --git a/development/Xcode/Z80.xcodeproj/.gitignore b/development/Xcode/Z80.xcodeproj/.gitignore deleted file mode 100644 index 256476d..0000000 --- a/development/Xcode/Z80.xcodeproj/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -** -!.gitignore -!project.pbxproj diff --git a/development/Xcode/Z80.xcodeproj/project.pbxproj b/development/Xcode/Z80.xcodeproj/project.pbxproj deleted file mode 100644 index e03e5b0..0000000 --- a/development/Xcode/Z80.xcodeproj/project.pbxproj +++ /dev/null @@ -1,520 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 6428A5751AAFC6DF00634F5D /* Z80.h in Headers */ = {isa = PBXBuildFile; fileRef = 6428A5741AAFC6DF00634F5D /* Z80.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6428A5771AAFC72900634F5D /* Z80.c in Sources */ = {isa = PBXBuildFile; fileRef = 6428A5761AAFC72900634F5D /* Z80.c */; }; - 6428A5801AAFEF1D00634F5D /* Z80.c in Sources */ = {isa = PBXBuildFile; fileRef = 6428A5761AAFC72900634F5D /* Z80.c */; settings = {COMPILER_FLAGS = "-DCPU_Z80_STATIC"; }; }; - 648C13AF1CCDFA8D00C8DCE1 /* Z80.c in Sources */ = {isa = PBXBuildFile; fileRef = 6428A5761AAFC72900634F5D /* Z80.c */; settings = {COMPILER_FLAGS = "-DCPU_Z80_WITH_MODULE_ABI"; }; }; - 648C13B21CCDFA8D00C8DCE1 /* Z80.h in Headers */ = {isa = PBXBuildFile; fileRef = 6428A5741AAFC6DF00634F5D /* Z80.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 648C13C31CCDFBEF00C8DCE1 /* Z80.c in Sources */ = {isa = PBXBuildFile; fileRef = 6428A5761AAFC72900634F5D /* Z80.c */; settings = {COMPILER_FLAGS = "-DCPU_Z80_STATIC -DCPU_Z80_WITH_ABI"; }; }; - 64F8E3361CD656DA0083A613 /* Z80.h in Headers */ = {isa = PBXBuildFile; fileRef = 6428A5741AAFC6DF00634F5D /* Z80.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 64F8E3371CD656DB0083A613 /* Z80.h in Headers */ = {isa = PBXBuildFile; fileRef = 6428A5741AAFC6DF00634F5D /* Z80.h */; settings = {ATTRIBUTES = (Public, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 6428A5691AAFC68700634F5D /* libZ80.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libZ80.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; - 6428A5741AAFC6DF00634F5D /* Z80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Z80.h; sourceTree = ""; }; - 6428A5761AAFC72900634F5D /* Z80.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Z80.c; sourceTree = ""; }; - 6428A57C1AAFEDED00634F5D /* libZ80.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libZ80.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 648C13B61CCDFA8D00C8DCE1 /* libZ80.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libZ80.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; - 648C13C91CCDFBEF00C8DCE1 /* libZ80.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libZ80.a; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 6428A5661AAFC68700634F5D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6428A5791AAFEDED00634F5D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 648C13B01CCDFA8D00C8DCE1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 648C13C41CCDFBEF00C8DCE1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 6428A5601AAFC68700634F5D = { - isa = PBXGroup; - children = ( - 6428A5711AAFC69A00634F5D /* API */, - 6428A5701AAFC69100634F5D /* Sources */, - 6428A56A1AAFC68700634F5D /* Products */, - ); - sourceTree = ""; - }; - 6428A56A1AAFC68700634F5D /* Products */ = { - isa = PBXGroup; - children = ( - 6428A5691AAFC68700634F5D /* libZ80.dylib */, - 6428A57C1AAFEDED00634F5D /* libZ80.a */, - 648C13B61CCDFA8D00C8DCE1 /* libZ80.dylib */, - 648C13C91CCDFBEF00C8DCE1 /* libZ80.a */, - ); - name = Products; - sourceTree = ""; - }; - 6428A5701AAFC69100634F5D /* Sources */ = { - isa = PBXGroup; - children = ( - 6428A5761AAFC72900634F5D /* Z80.c */, - ); - name = Sources; - path = ../../sources; - sourceTree = ""; - }; - 6428A5711AAFC69A00634F5D /* API */ = { - isa = PBXGroup; - children = ( - 6428A5741AAFC6DF00634F5D /* Z80.h */, - ); - name = API; - path = ../../API/emulation/CPU; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 6428A5671AAFC68700634F5D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 6428A5751AAFC6DF00634F5D /* Z80.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6428A57A1AAFEDED00634F5D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 64F8E3361CD656DA0083A613 /* Z80.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 648C13B11CCDFA8D00C8DCE1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 648C13B21CCDFA8D00C8DCE1 /* Z80.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 648C13C51CCDFBEF00C8DCE1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 64F8E3371CD656DB0083A613 /* Z80.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 6428A5681AAFC68700634F5D /* Z80 (dynamic) */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6428A56D1AAFC68700634F5D /* Build configuration list for PBXNativeTarget "Z80 (dynamic)" */; - buildPhases = ( - 6428A5651AAFC68700634F5D /* Sources */, - 6428A5661AAFC68700634F5D /* Frameworks */, - 6428A5671AAFC68700634F5D /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Z80 (dynamic)"; - productName = Z80; - productReference = 6428A5691AAFC68700634F5D /* libZ80.dylib */; - productType = "com.apple.product-type.library.dynamic"; - }; - 6428A57B1AAFEDED00634F5D /* Z80 (static) */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6428A57D1AAFEDED00634F5D /* Build configuration list for PBXNativeTarget "Z80 (static)" */; - buildPhases = ( - 6428A5781AAFEDED00634F5D /* Sources */, - 6428A5791AAFEDED00634F5D /* Frameworks */, - 6428A57A1AAFEDED00634F5D /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Z80 (static)"; - productName = "Z80-Static"; - productReference = 6428A57C1AAFEDED00634F5D /* libZ80.a */; - productType = "com.apple.product-type.library.static"; - }; - 648C13AD1CCDFA8D00C8DCE1 /* Z80 (dynamic module) */ = { - isa = PBXNativeTarget; - buildConfigurationList = 648C13B31CCDFA8D00C8DCE1 /* Build configuration list for PBXNativeTarget "Z80 (dynamic module)" */; - buildPhases = ( - 648C13AE1CCDFA8D00C8DCE1 /* Sources */, - 648C13B01CCDFA8D00C8DCE1 /* Frameworks */, - 648C13B11CCDFA8D00C8DCE1 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Z80 (dynamic module)"; - productName = Z80; - productReference = 648C13B61CCDFA8D00C8DCE1 /* libZ80.dylib */; - productType = "com.apple.product-type.library.dynamic"; - }; - 648C13C11CCDFBEF00C8DCE1 /* Z80 (static module) */ = { - isa = PBXNativeTarget; - buildConfigurationList = 648C13C61CCDFBEF00C8DCE1 /* Build configuration list for PBXNativeTarget "Z80 (static module)" */; - buildPhases = ( - 648C13C21CCDFBEF00C8DCE1 /* Sources */, - 648C13C41CCDFBEF00C8DCE1 /* Frameworks */, - 648C13C51CCDFBEF00C8DCE1 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Z80 (static module)"; - productName = "Z80-Static"; - productReference = 648C13C91CCDFBEF00C8DCE1 /* libZ80.a */; - productType = "com.apple.product-type.library.static"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 6428A5611AAFC68700634F5D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0610; - ORGANIZATIONNAME = "Manuel Sainz de Baranda y Goñi"; - TargetAttributes = { - 6428A5681AAFC68700634F5D = { - CreatedOnToolsVersion = 6.1.1; - }; - 6428A57B1AAFEDED00634F5D = { - CreatedOnToolsVersion = 6.1.1; - }; - }; - }; - buildConfigurationList = 6428A5641AAFC68700634F5D /* Build configuration list for PBXProject "Z80" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 6428A5601AAFC68700634F5D; - productRefGroup = 6428A56A1AAFC68700634F5D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 6428A5681AAFC68700634F5D /* Z80 (dynamic) */, - 648C13AD1CCDFA8D00C8DCE1 /* Z80 (dynamic module) */, - 6428A57B1AAFEDED00634F5D /* Z80 (static) */, - 648C13C11CCDFBEF00C8DCE1 /* Z80 (static module) */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 6428A5651AAFC68700634F5D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6428A5771AAFC72900634F5D /* Z80.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6428A5781AAFEDED00634F5D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6428A5801AAFEF1D00634F5D /* Z80.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 648C13AE1CCDFA8D00C8DCE1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 648C13AF1CCDFA8D00C8DCE1 /* Z80.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 648C13C21CCDFBEF00C8DCE1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 648C13C31CCDFBEF00C8DCE1 /* Z80.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 6428A56B1AAFC68700634F5D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_ASSIGN_ENUM = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_FLOAT_CONVERSION = YES; - CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = c99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_BUILTIN_FUNCTIONS = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; - GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_SHADOW = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_PARAMETER = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /usr/local/include, - ../../API, - ); - LINK_WITH_STANDARD_LIBRARIES = NO; - LLVM_LTO = YES; - MACOSX_DEPLOYMENT_TARGET = 10.6; - ONLY_ACTIVE_ARCH = YES; - STRIP_STYLE = "non-global"; - }; - name = Debug; - }; - 6428A56C1AAFC68700634F5D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_ASSIGN_ENUM = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_FLOAT_CONVERSION = YES; - CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = c99; - GCC_ENABLE_BUILTIN_FUNCTIONS = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; - GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_SHADOW = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_PARAMETER = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /usr/local/include, - ../../API, - ); - LINK_WITH_STANDARD_LIBRARIES = NO; - LLVM_LTO = YES; - MACOSX_DEPLOYMENT_TARGET = 10.6; - STRIP_STYLE = "non-global"; - }; - name = Release; - }; - 6428A56E1AAFC68700634F5D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 0; - DYLIB_CURRENT_VERSION = 0.1.0; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Debug; - }; - 6428A56F1AAFC68700634F5D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 0; - DYLIB_CURRENT_VERSION = 0.1.0; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Release; - }; - 6428A57E1AAFEDED00634F5D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Debug; - }; - 6428A57F1AAFEDED00634F5D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Release; - }; - 648C13B41CCDFA8D00C8DCE1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 0; - DYLIB_CURRENT_VERSION = 0.1.0; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Debug; - }; - 648C13B51CCDFA8D00C8DCE1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 0; - DYLIB_CURRENT_VERSION = 0.1.0; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Release; - }; - 648C13C71CCDFBEF00C8DCE1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Debug; - }; - 648C13C81CCDFBEF00C8DCE1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = Z80; - PUBLIC_HEADERS_FOLDER_PATH = /usr/local/include/emulation/CPU; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 6428A5641AAFC68700634F5D /* Build configuration list for PBXProject "Z80" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6428A56B1AAFC68700634F5D /* Debug */, - 6428A56C1AAFC68700634F5D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6428A56D1AAFC68700634F5D /* Build configuration list for PBXNativeTarget "Z80 (dynamic)" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6428A56E1AAFC68700634F5D /* Debug */, - 6428A56F1AAFC68700634F5D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6428A57D1AAFEDED00634F5D /* Build configuration list for PBXNativeTarget "Z80 (static)" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6428A57E1AAFEDED00634F5D /* Debug */, - 6428A57F1AAFEDED00634F5D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 648C13B31CCDFA8D00C8DCE1 /* Build configuration list for PBXNativeTarget "Z80 (dynamic module)" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 648C13B41CCDFA8D00C8DCE1 /* Debug */, - 648C13B51CCDFA8D00C8DCE1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 648C13C61CCDFBEF00C8DCE1 /* Build configuration list for PBXNativeTarget "Z80 (static module)" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 648C13C71CCDFBEF00C8DCE1 /* Debug */, - 648C13C81CCDFBEF00C8DCE1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 6428A5611AAFC68700634F5D /* Project object */; -} diff --git a/documentation/APIReference.rst b/documentation/APIReference.rst new file mode 100644 index 0000000..b838188 --- /dev/null +++ b/documentation/APIReference.rst @@ -0,0 +1,54 @@ +============= +API reference +============= + +Macros +------ + +.. doxygendefine:: Z80_LIBRARY_VERSION_MAJOR +.. doxygendefine:: Z80_LIBRARY_VERSION_MINOR +.. doxygendefine:: Z80_LIBRARY_VERSION_MICRO +.. doxygendefine:: Z80_LIBRARY_VERSION_STRING +.. doxygendefine:: Z80_CYCLE_LIMIT +.. doxygendefine:: Z80_HOOK + +.. doxygendefine:: Z80_MODEL_ZILOG_NMOS +.. doxygendefine:: Z80_MODEL_ZILOG_CMOS + +.. doxygendefine:: Z80_REQUEST_SPECIAL_RESET +.. doxygendefine:: Z80_REQUEST_RESET +.. doxygendefine:: Z80_REQUEST_NMI +.. doxygendefine:: Z80_REQUEST_INT + +.. doxygendefine:: Z80_RESUME_HALT +.. doxygendefine:: Z80_RESUME_XY +.. doxygendefine:: Z80_RESUME_IM0_XY + +Callback Types +-------------- + +.. doxygentypedef:: Z80Read +.. doxygentypedef:: Z80Write +.. doxygentypedef:: Z80HALT +.. doxygentypedef:: Z80Notify + +Objects +------- + +.. doxygenstruct:: Z80 + :members: + +Functions +--------- + +.. doxygenfunction:: z80_in_offset +.. doxygenfunction:: z80_instant_reset +.. doxygenfunction:: z80_out_offset +.. doxygenfunction:: z80_execute +.. doxygenfunction:: z80_int +.. doxygenfunction:: z80_nmi +.. doxygenfunction:: z80_normal_reset +.. doxygenfunction:: z80_power +.. doxygenfunction:: z80_refresh_address +.. doxygenfunction:: z80_run +.. doxygenfunction:: z80_special_reset diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt new file mode 100644 index 0000000..53897d7 --- /dev/null +++ b/documentation/CMakeLists.txt @@ -0,0 +1,100 @@ +# Z80 - documentation/CMakeLists.txt +# ______ ______ ______ +# /\___ \/\ __ \\ __ \ +# \/__/ /\_\ __ \\ \/\ \ +# /\_____\\_____\\_____\ +# Zilog \/_____//_____//_____/ CPU Emulator +# Copyright (C) 1999-2022 Manuel Sainz de Baranda y Goñi. +# Released under the terms of the GNU Lesser General Public License v3. + +find_package(Doxygen REQUIRED) +find_package(Sphinx REQUIRED) +find_package(Breathe REQUIRED) + +set(DOXYGEN_INPUT_DIR "${PROJECT_SOURCE_DIR}/API" ) +set(DOXYGEN_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}" ) +set(DOXYGEN_XML_OUTPUT "API-XML" ) +set(_doxyfile_in "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in") +set(_doxyfile "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" ) + +get_target_property(DOXYGEN_INCLUDE_PATH Zeta INTERFACE_INCLUDE_DIRECTORIES) +configure_file(${_doxyfile_in} ${_doxyfile} @ONLY) + +file(GLOB_RECURSE _public_headers ${DOXYGEN_INPUT_DIR}/*.h) +set(_api_xml_output "${DOXYGEN_OUTPUT_DIR}/${DOXYGEN_XML_OUTPUT}") + +add_custom_command( + OUTPUT ${_api_xml_output} + DEPENDS ${_public_headers} + COMMAND ${DOXYGEN_EXECUTABLE} ${_doxyfile} + MAIN_DEPENDENCY ${_doxyfile} + COMMENT "Extracting API reference") + +add_custom_target(API-XML ALL DEPENDS ${_api_xml_output}) + +if(${PROJECT_NAME}_WITH_HTML_DOCUMENTATION) + set(_html_documentation_output "${CMAKE_CURRENT_BINARY_DIR}/HTML") + + if(${PROJECT_NAME}_SPHINX_HTML_THEME STREQUAL "") + set(_html_theme_option "") + else() + set(_html_theme_option "-Dhtml_theme=${${PROJECT_NAME}_SPHINX_HTML_THEME}") + endif() + + add_custom_command( + OUTPUT ${_html_documentation_output} + COMMAND ${SPHINX_BUILD_EXECUTABLE} + -b html + ${_html_theme_option} + "-Dbreathe_projects.${PROJECT_NAME}=${_api_xml_output}" + ${CMAKE_CURRENT_SOURCE_DIR} + ${_html_documentation_output} + MAIN_DEPENDENCY ${_api_xml_output} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating documentation in HTML format") + + add_custom_target(Documentation-HTML ALL DEPENDS ${_html_documentation_output}) + add_dependencies(Documentation-HTML API-XML) + + install(DIRECTORY "${_html_documentation_output}/" + DESTINATION "${CMAKE_INSTALL_DOCDIR}/documentation") +endif() + +if(${PROJECT_NAME}_WITH_PDF_DOCUMENTATION) + find_package(LATEX REQUIRED COMPONENTS PDFLATEX) + + set(_latex_documentation_output "${CMAKE_CURRENT_BINARY_DIR}/LaTeX") + + add_custom_command( + OUTPUT ${_latex_documentation_output} + COMMAND ${SPHINX_BUILD_EXECUTABLE} + -b latex + "-Dbreathe_projects.${PROJECT_NAME}=${_api_xml_output}" + ${CMAKE_CURRENT_SOURCE_DIR} + ${_latex_documentation_output} + MAIN_DEPENDENCY ${_api_xml_output} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating documentation in LaTeX format") + + add_custom_target(Documentation-LaTeX ALL DEPENDS ${_latex_documentation_output}) + add_dependencies(Documentation-LaTeX API-XML) + + string(TOLOWER ${PROJECT_NAME} _pdf_documentation_output) + set(_pdf_documentation_output "${_latex_documentation_output}/${_pdf_documentation_output}.pdf") + + add_custom_command( + OUTPUT ${_pdf_documentation_output} + COMMAND make + MAIN_DEPENDENCY ${_latex_documentation_output} + WORKING_DIRECTORY ${_latex_documentation_output} + COMMENT "Converting documentation to PDF format") + + add_custom_target(Documentation-PDF ALL DEPENDS ${_pdf_documentation_output}) + add_dependencies(Documentation-PDF Documentation-LaTeX) + + install(FILES ${_pdf_documentation_output} + DESTINATION ${CMAKE_INSTALL_DOCDIR} + RENAME "${PROJECT_NAME} v${PROJECT_VERSION}.pdf") +endif() + +# documentation/CMakeLists.txt EOF diff --git a/documentation/Doxyfile.in b/documentation/Doxyfile.in new file mode 100644 index 0000000..610e1d5 --- /dev/null +++ b/documentation/Doxyfile.in @@ -0,0 +1,376 @@ +# Doxyfile 1.8.15 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "@PROJECT_NAME@" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +OUTPUT_TEXT_DIRECTION = None +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@DOXYGEN_INPUT_DIR@" +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = "overline=" +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 0 +AUTOLINK_SUPPORT = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +EXTRACT_ALL = NO +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = "@DOXYGEN_INPUT_DIR@" +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = NO +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_MENUS = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = YES +DOCSET_FEEDNAME = "Zilog Z80 CPU Emulator" +DOCSET_BUNDLE_ID = com.redcode.Z80 +DOCSET_PUBLISHER_ID = com.redcode +DOCSET_PUBLISHER_NAME = "Manuel Sainz de Baranda y Goñi" +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = +MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = YES +XML_OUTPUT = "@DOXYGEN_XML_OUTPUT@" +XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = "@DOXYGEN_INCLUDE_PATH@" +INCLUDE_FILE_PATTERNS = +PREDEFINED = Z80_API= +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/documentation/EmulationAccuracy.rst b/documentation/EmulationAccuracy.rst new file mode 100644 index 0000000..0aae1d1 --- /dev/null +++ b/documentation/EmulationAccuracy.rst @@ -0,0 +1,3 @@ +================== +Emulation accuracy +================== diff --git a/documentation/Installation.rst b/documentation/Installation.rst new file mode 100644 index 0000000..b8d26ff --- /dev/null +++ b/documentation/Installation.rst @@ -0,0 +1,170 @@ +============ +Installation +============ + +.. only:: html + + .. |br| raw:: html + +
+ +.. only:: latex + + .. |nl| raw:: latex + + \newline + +You will need `CMake `_ v3.14 or later to build the package and, optionally, recent versions of `Doxygen `_, `Sphinx `_ and `Breathe `_ to compile the documentation. Also make sure you have `LaTeX `_ with PDF support installed on your system in case you want to generate the documentation in PDF format. + +The emulator requires some types and macros included in `Zeta `_, a dependency-free, `header-only `_ library used to retain compatibility with most C compilers. Install Zeta or extract its official source code package to the directory of the Z80 project or its parent directory. Zeta is the sole dependency; the emulator is a freestanding implementation and as such does not depend on the +`C standard library `_. + +Once all requirements are met, create a directory and run ``cmake`` from there to prepare the build system: + +.. code-block:: sh + + mkdir build + cd build + cmake [options] + +The resulting build files can be configured by passing options to ``cmake``. To show a complete list of those available along with their current settings, type the following: + +.. code-block:: sh + + cmake -LAH + +If in doubt, read the `CMake documentation `_ for more information on configuration options. The following are some of the most relevant standard options of CMake: + +.. option:: -DBUILD_SHARED_LIBS=(YES|NO) + + Build the project as a shared library rather than a static one. |br| |nl| + The default is ``NO``. + +.. option:: -DCMAKE_BUILD_TYPE=(Debug|Release|RelWithDebInfo) + + Choose the type of build (configuration) to generate. |br| |nl| + The default is ``Release``. + +.. option:: -DCMAKE_INSTALL_PREFIX="" + + Specify the installation prefix on `UNIX `_ and `UNIX-like `_ operating systems. |br| |nl| + The default is ``"/usr/local"``. + +Package-specific options are prefixed with ``Z80_`` and can be divided into two groups. The first one controls aspects not related to the source code of the library: + +.. option:: -DZ80_DOWNLOAD_TEST_FILES=(YES|NO) + + Download the firmware and software used by the testing tool. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_INSTALL_CMAKEDIR="" + + Specify the directory in which to install the CMake `config-file package `_. |br| |nl| + The default is ``"${CMAKE_INSTALL_LIBDIR}/cmake/Z80"``. + +.. option:: -DZ80_INSTALL_PKGCONFIGDIR="" + + Specify the directory in which to install the `pkg-config `_ `file `_. |br| |nl| + The default is ``"${CMAKE_INSTALL_LIBDIR}/pkgconfig"``. + +.. option:: -DZ80_NOSTDLIB_FLAGS=(Auto|[[;...]]) + + Specify the linker flags used to avoid linking against system libraries. |br| |nl| + The default is ``Auto`` (autoconfigure flags). If you get linker errors, set this option to ``""``. + +.. option:: -DZ80_SHARED_LIBS=(YES|NO) + + Build the project as a shared library rather than a static one. |br| |nl| + This option takes precedence over ``BUILD_SHARED_LIBS``. |br| |nl| + Not defined by default. + +.. option:: -DZ80_SPHINX_HTML_THEME="[]" + + Specify the Sphinx theme for the documentation in HTML format. |br| |nl| + The default is ``""`` (use the default theme). + +.. option:: -DZ80_WITH_CMAKE_SUPPORT=(YES|NO) + + Generate and install the CMake `config-file package `_. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_HTML_DOCUMENTATION=(YES|NO) + + Build and install the documentation in HTML format. |br| |nl| + It requires Doxygen, Sphinx and Breathe. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_PDF_DOCUMENTATION=(YES|NO) + + Build and install the documentation in PDF format. |br| |nl| + It requires Doxygen, Sphinx, Breathe and LaTeX with PDF support. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_PKGCONFIG_SUPPORT=(YES|NO) + + Generate and install the `pkg-config `_ `file `_. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_STANDARD_DOCUMENTS=(YES|NO) + + Install the standard text documents distributed with the package: ``AUTHORS``, ``COPYING``, ``COPYING.LESSER``, ``HISTORY``, ``README`` and ``THANKS``. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_TESTS=(YES|NO) + + Build the testing tool. |br| |nl| + The default is ``NO``. + +The second group of package-specific options configures the source code of the library by predefining macros that enable optional implementations: + +.. option:: -DZ80_WITH_EXECUTE=(YES|NO) + + Build the implementation of the ``z80_execute`` function. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_FULL_IM0=(YES|NO) + + Build the full implementation of the interrupt mode 0 rather than the reduced one. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_Q=(YES|NO) + + Build the implementation of the `Q "register" `_. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_RESET_SIGNAL=(YES|NO) + + Build the implementation of the normal RESET signal. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_SPECIAL_RESET_SIGNAL=(YES|NO) + + Build the implementation of the `special RESET `_ signal. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_UNOFFICIAL_RETI=(YES|NO) + + Configure the ``ED5Dh``, ``ED6Dh`` and ``ED7Dh`` undocumented instructions as ``reti`` instead of ``retn``. |br| |nl| + The default is ``NO``. + +.. option:: -DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=(YES|NO) + + Build the implementation of the bug affecting the Zilog Z80 NMOS, which causes the P/V flag to be reset when a maskable interrupt is accepted during the execution of the ``ld a,{i|r}`` instructions. |br| |nl| + The default is ``NO``. + +Package maintainers should use at least the following options for both shared and static library targets: + +.. code-block:: sh + + -DZ80_WITH_EXECUTE=YES + -DZ80_WITH_FULL_IM0=YES + -DZ80_WITH_Q=YES + -DZ80_WITH_RESET_SIGNAL=YES + -DZ80_WITH_ZILOG_NMOS_LD_A_IR_BUG=YES + +Finally, once the build system is configured according to your needs, build and install the package: + +.. code-block:: sh + + make + make install/strip diff --git a/documentation/Integration.rst b/documentation/Integration.rst new file mode 100644 index 0000000..6ed5d07 --- /dev/null +++ b/documentation/Integration.rst @@ -0,0 +1,82 @@ +=========== +Integration +=========== + +.. only:: html + + .. |br| raw:: html + +
+ +.. only:: latex + + .. |nl| raw:: latex + + \newline + +As an external dependency in CMake-based projects +------------------------------------------------- + +The Z80 library includes find-modules and a config-file package for integration into CMake-based projects. It is recommended to always copy the ``FindZ80.cmake`` and ``FindZeta.cmake`` files into the CMake modules directory of projects that use the library as an external dependency. This will allow CMake to find the library if the necessary config-file packages are not installed on the system. + +Both the config-file package and the find-module support dual installations of the shared and static versions of the Z80 library. You can specify the linking method by using the component mechanism of ``find_package``. + +Example: + +.. code-block:: cmake + + find_package(Z80 REQUIRED [Shared|Static]) + target_link_libraries(your-target Z80) + +Omitting the linking method will select the ``Shared`` version of the library or, if not installed, the ``Static`` version instead. + +As a CMake subproject +--------------------- + +To embed the library as a CMake subproject, just place its entire source tree into a subdirectory of your project. + +It is advisable to configure the library in the ``CMakeLists.txt`` of your project. This will prevent the user from having to specify configuration options of the Z80 subproject through the CMake command line when building the main project. As noted in the Installation section of this document, all package-specific options are prefixed with ``Z80_``, so, in a normal scenario, there should be no risk of name collision with the options and variables of the parent project. + +Example: + +.. code-block:: cmake + + set(Z80_SHARED_LIBS NO CACHE BOOL "") + set(Z80_WITH_Q YES CACHE BOOL "") + set(Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG YES CACHE BOOL "") + + add_subdirectory(dependencies/Z80) + target_link_libraries(your-target Z80) + +It is important to set the ``Z80_SHARED_LIBS`` option. Otherwise CMake will build the library type indicated by ``BUILD_SHARED_LIBS``, which may not be the desired one. + +Manual integration +------------------ + +There are several macros that can be used to configure the source code of the library. You can define those you need in your build system or at the beginning of the ``Z80.c`` file. The following ones allow you to configure the integration of ``Z80.h`` and ``Z80.c`` into the project: + +.. c:macro:: Z80_DEPENDENCIES_HEADER + + Specifies the only external header to ``#include``, replacing those of Zeta. |br| |nl| + If used, it must also be defined before including the ``Z80.h`` header. + +.. c:macro:: Z80_STATIC + + Needed for compiling and/or using the emulator as a static library or as an internal part of other project. |br| |nl| + If used, it must also be defined before including the ``Z80.h`` header. + +.. c:macro:: Z80_WITH_LOCAL_HEADER + + Tells ``Z80.c`` to ``#include Z80.h`` instead of ````. + +The second group of package-specific options, explained in the "Installation" section of this document, activates various optional implementations in the source code by predefining the following macros: + +.. c:macro:: Z80_WITH_EXECUTE +.. c:macro:: Z80_WITH_FULL_IM0 +.. c:macro:: Z80_WITH_Q +.. c:macro:: Z80_WITH_RESET_SIGNAL +.. c:macro:: Z80_WITH_SPECIAL_RESET_SIGNAL +.. c:macro:: Z80_WITH_UNOFFICIAL_RETI +.. c:macro:: Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG + +Except for ``Z80_DEPENDENCIES_HEADER``, the above macros do not need to be set to a particular token when used, as the source code only checks whether or not they are defined. diff --git a/documentation/License.rst b/documentation/License.rst new file mode 100644 index 0000000..c283d29 --- /dev/null +++ b/documentation/License.rst @@ -0,0 +1,9 @@ +======= +License +======= + +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 . diff --git a/documentation/Makefile b/documentation/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/documentation/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/documentation/TODO.txt b/documentation/TODO.txt new file mode 100644 index 0000000..bd3b82a --- /dev/null +++ b/documentation/TODO.txt @@ -0,0 +1,2 @@ +ST CMOS Z80 no se comporta igual en los flags de las instrucciones de bloque? +https://stardot.org.uk/forums/viewtopic.php?p=212100#p212100 diff --git a/documentation/Testing.rst b/documentation/Testing.rst new file mode 100644 index 0000000..3fe0b96 --- /dev/null +++ b/documentation/Testing.rst @@ -0,0 +1,3 @@ +======= +Testing +======= diff --git a/documentation/Thanks.rst b/documentation/Thanks.rst new file mode 100644 index 0000000..5c08c35 --- /dev/null +++ b/documentation/Thanks.rst @@ -0,0 +1,214 @@ +====== +Thanks +====== + +Many thanks to the following individuals (in alphabetical order): + +Akimov, Vadim (lvd) + For testing the library on many different platforms and CPU architectures. + +azesmbog + 1. For validating tests on real hardware [1]. + 2. For his research on the unstable flag behavior of the "ccf/scf" instructions. + 3. For his invaluable help. + +Banks, David (hoglet) + 1. For cracking the flag behavior of the block instructions [2,3]. + 2. For his research on the flag behavior of the "ccf/scf" instructions [3]. + +Beliansky, Anatoly (Tolik_Trek) + For validating tests on real hardware [4]. + +Bobrowski, Jan + For fixing the "Z80 Full Instruction Set Exerciser for Spectrum" [5]. + +boo_boo + For cracking the behavior of the MEMPTR register [6,7,8,9]. + +Brady, Stuart + For his research on the flag behavior of the "ccf/scf" instructions [10]. + +Brewer, Tony + 1. For his research on the special RESET [11]. + 2. For sharing information about the RESET signal [12]. + 3. For helping to crack the flag behavior of the block instructions [2]. + 4. For performing low-level tests on real hardware [2]. + 5. For helping me to test different undocumented behaviors of the Zilog Z80. + +Bystrov, Dmitry (Alone Coder) + For validating tests on real hardware [4]. + +Chunin, Roman (CHRV) + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. + +Conway, Simon (BadBeard) + For validating the "Z80 Test Suite" on several Z80 clones [13]. + +Cooke, Simon + For finding out how the "out (c),0" instruction behaves on the Zilog Z80 CMOS [14]. + +Cringle, Frank D. + For writing the "Z80 Instruction Set Exerciser" [15]. + +Devic, Goran + For his research on undocumented behaviors of the Z80 CPU [16]. + +Flammenkamp, Achim + For his article on Z80 interrupts [17]. + +Gimeno Fortea, Pedro + 1. For his research work [18]. + 2. For writing the first-ever ZX Spectrum emulator [19,20]. + +goodboy + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. + +Greenway, Ian + For testing the flag behavior of the "ccf/scf" instructions on real hardware [10,21]. + +Harston, Jonathan Graham + 1. For his technical documents about the Zilog Z80 [22,23,24]. + 2. For porting the "Z80 Instruction Set Exerciser" to the ZX Spectrum [25]. + +Helcmanovsky, Peter (Ped7g) + 1. For helping me to write the "IN-MEMPTR" test. + 2. For writing the "Z80 Block Flags Test" [26,27]. + 3. For writing the "Z80 CCF SCF Outcome Stability" test [27]. + 4. For writing the "Z80 INT Skip" test [27]. + 5. For his research on the unstable flag behavior of the "ccf/scf" instructions. + 6. For his invaluable help. + +icebear + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. + +Kladov, Vladimir + For cracking the behavior of the MEMPTR register [6,7,8,9]. + +Krook, Magnus + For validating tests on real hardware [28]. + +London, Matthew + For validating tests on real hardware. + +Molodtsov, Aleksandr + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. + +Nair, Arjun + For validating tests on real hardware [26]. + +Nicolás-González, César + For helping me to research the unstable flag behavior of the "ccf/scf" instructions. + +Ortega Sosa, Sofía + For her support. + +Owen, Simon + For the idea of the hooking method used in this emulator. + +Rak, Patrik + 1. For improving the "Z80 Instruction Set Exerciser for Spectrum" [29]. + 2. For cracking the flag behavior of the "ccf/scf" instructions [13,29]. + 3. For writing the "Zilog Z80 CPU Test Suite" [29,30]. + 4. For his research on the unstable flag behavior of the "ccf/scf" instructions. + +Rodríguez Jódar, Miguel Ángel (mcleod_ideafix) + For his reseach on the state of the registers after POWER/RESET [31]. + +Rodríguez Palomino, Mario (r-lyeh) + For teaching me how emulators work. + +Sainz de Baranda y Romero, Manuel + For teaching me programming and giving me my first computer. + +Sánchez Ordiñana, José Ismael (Vaporatorius) + For validating tests on real hardware [32,33]. + +Stevenson, Dave + 1. For testing the special RESET on real hardware [11]. + 2. For performing low-level tests on real hardware [34]. + +Weissflog, Andre (Floh) + 1. For finding out that the "reti/retn" instructions defer the acceptance of the maskable interrupt [35]. + 2. For writing the "Visual Z80 Remix" simulator [36]. + +Wilkinson, Oli (evolutional) + For validating tests on real hardware [26]. + +Wlodek + For testing the behavior of the MEMPTR register on real Z80 chips [6,7,8,9]. + +Woodmass, Mark (Woody) + 1. For his invaluable contributions to the emuscene. + 2. For writing the "Z80 Test Suite" [13]. + 3. For his research on the flag behavior of the "ccf/scf" instructions [37]. + 4. For writing the "HALT2INT" test. + 5. For writing the "EIHALT" test. + +Young, Sean + 1. For his research work. + 2. For his technical documents about the Zilog Z80 [18,38]. + +ZXGuesser + For validating tests on real hardware. + + +References +========== + +1. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83384#p83384 +2. https://stardot.org.uk/forums/viewtopic.php?t=15464 + * https://stardot.org.uk/forums/viewtopic.php?p=211042#p211042 + * https://stardot.org.uk/forums/viewtopic.php?p=212021#p212021 +3. Banks, David (2018-08-21). "Undocumented Z80 Flags". + * https://github.com/hoglet67/Z80Decoder/wiki/Undocumented-Flags + * https://stardot.org.uk/forums/download/file.php?id=39831 +4. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83041#p83041 +5. http://wizard.ae.krakow.pl/~jb/qaop/tests.html +6. https://zxpress.ru/zxnet/zxnet.pc/5909 +7. https://zx-pk.ru/threads/2506-komanda-bit-n-(hl).html +8. https://zx-pk.ru/threads/2586-prosba-realshchikam-ot-emulyatorshchikov.html +9. boo_boo; Kladov, Vladimir (2006-03-29). "MEMPTR: esoteric register of the Zilog Z80 CPU". + * http://zx.pk.ru/showpost.php?p=43688 + * http://zx.pk.ru/attachment.php?attachmentid=2984 + * http://zx.pk.ru/showpost.php?p=43800 + * http://zx.pk.ru/attachment.php?attachmentid=2989 +10. https://sourceforge.net/p/fuse-emulator/mailman/message/6929573 +11. Brewer, Tony (2014-12). "Z80 Special Reset". + * http://primrosebank.net/computers/z80/z80_special_reset.htm +12. https://mtxworld.dk/memorum/viewtopic.php?p=1352#p1352 +13. https://worldofspectrum.org/forums/discussion/20345 +14. https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJ +15. Cringle, Frank D. (1998-01-28). "Yaze - Yet Another Z80 Emulator" v1.10. + * ftp://ftp.ping.de/pub/misc/emulators/yaze-1.10.tar.gz +16. https://baltazarstudios.com/zilog-z80-undocumented-behavior +17. Flammenkamp, Achim. "Interrupt Behaviour of the Z80 CPU". + * http://z80.info/interrup.htm +18. Young, Sean (1998-10). "Z80 Undocumented Features (in Software Behaviour)". +19. https://elmundodelspectrum.com/desenterrando-el-primer-emulador-de-spectrum +20. https://elmundodelspectrum.com/con-vosotros-el-emulador-de-pedro-gimeno-1989 +21. https://sourceforge.net/p/fuse-emulator/mailman/message/4502844 +22. Harston, Jonathan Graham (1997-09-09). "Z80 Opcode Map". + * https://mdfs.net/Docs/Comp/Z80/OpCodeMap +23. Harston, Jonathan Graham (1997-12-18). "Z80 Microprocessor Undocumented Instructions". + * https://mdfs.net/Docs/Comp/Z80/UnDocOps +24. Harston, Jonathan Graham (1998-04-15). "Full Z80 Opcode List Including Undocumented Opcodes". + * https://mdfs.net/Docs/Comp/Z80/OpList +25. https://mdfs.net/Software/Z80/Exerciser/Spectrum +26. https://spectrumcomputing.co.uk/forums/viewtopic.php?t=6102 +27. https://github.com/MrKWatkins/ZXSpectrumNextTests +28. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=83157#p83157 +29. https://worldofspectrum.org/forums/discussion/41704 + * http://zxds.raxoft.cz/taps/misc/zexall2.zip +30. https://worldofspectrum.org/forums/discussion/41834 + * http://zxds.raxoft.cz/taps/misc/z80test-1.0.zip + * https://github.com/raxoft/z80test +31. https://worldofspectrum.org/forums/discussion/34574 +32. https://worldofspectrum.org/forums/discussion/comment/668760/#Comment_668760 +33. https://jisanchez.com/test-a-dos-placas-de-zx-spectrum +34. https://stardot.org.uk/forums/viewtopic.php?p=212360#p212360 +35. https://floooh.github.io/2021/12/17/cycle-stepped-z80.html +36. https://github.com/floooh/v6502r +37. http://groups.google.co.uk/group/comp.sys.sinclair/msg/56dd1fd4ccb5fb3b +38. Young, Sean (2005-09-18). "Undocumented Z80 Documented, The". + * http://www.myquest.nl/z80undocumented + * http://www.myquest.nl/z80undocumented/z80-documented-v0.91.pdf diff --git a/documentation/VersionHistory.rst b/documentation/VersionHistory.rst new file mode 100644 index 0000000..bca150a --- /dev/null +++ b/documentation/VersionHistory.rst @@ -0,0 +1,71 @@ +=============== +Version History +=============== + +v0.2 (2022-XX-XX) +================= + +This is an important update that addresses a number of issues and also includes new features. Please note that the changes introduced in this release break the binary compatibility with the previous version. + +Changes: + +1. Changed the license from GPL to LGPL (by popular request). +2. Moved the public header from ```` to ````. +3. Removed the Xcode project. +4. Switched the build system from Premake to CMake. +5. Switched to Zeta v0.1. +6. Added pkg-config support. +7. Added the ``.vimrc`` dotfile. +8. Added the ``CITATION.cff`` file. +9. Added the ``file_id.diz`` file. +10. Added the ``THANKS`` file. +11. Added detailed documentation. +12. Added tests. +13. Added public macros for checking the library version. +14. Added public macros with bit masks for working with flags. +15. Added public macros for accessing the 16-bit registers. +16. Added the ``z80_execute`` function to run a simplified emulation without RESET and interrupts. +17. Added the ``z80_refresh_address`` function to get the refresh address of the current M1 cycle. +18. Added the ``z80_in_cycle`` and ``z80_out_cycle`` functions to get the clock cycle on which the I/O M-cycle occurs, relative to the start of the instruction. +19. Fixed a bug in the ``sll`` instruction. +20. Fixed a bug in the ``INX`` and ``OUTX`` macros affecting the S and N flags. +21. Fixed a bug in the ``OUTX`` macro affecting the MSByte of the port number. +22. Fixed the clock cycles of the ``dec XY`` and ``in (c)`` instructions. +23. Fixed the ``read_16`` function so that the order in which the compiler evaluates expressions does not affect the order of the memory read operations. +24. Fixed the order in which the memory write operations are performed when the SP register is involved. This affects the NMI response, the INT response in modes 1 and 2, and the following instructions: ``ex (sp),{hl|XY}``, ``push TT``, ``push XY``, ``call WORD``, ``call Z,WORD`` and ``rst N``. +25. Fixed the handling of illegal instructions to avoid stack overflows in long sequences of ``DDh`` / ``FDh`` prefixes. +26. Fixed several implicit conversions to avoid warnings about loss of sign and precision. +27. Fixed some bitwise operations to avoid undefined behavior and arithmetic right shifts on signed integers. +28. Fixed violations of the C standard in several identifiers. +29. Renamed the 8-bit register lists: ``X/Y`` to ``J/K``; ``J/K`` and ``P/Q`` to ``O/P``. +30. Replaced all P/V overflow computation functions with a single, faster macro. +31. Replaced all register resolution functions with macros. +32. Replaced all ``ld {J,K|O,P}`` instructions that have the same destination and source register with NOPs. +33. Reimplemented the HALT state. The emulation should now be fully accurate. HALTskip is also supported. +34. Added optional emulation of the normal and special RESET signals, along with the new ``z80_reset`` and ``z80_special_reset`` functions to emit them. The old ``z80_reset`` function is now called ``z80_instant_reset``. +35. Added emulation of the NMI acknowledge M-cycle through the new ``Z80::nmia`` callback. +36. Added emulation of the INT acknowledge M-cycle through the new ``Z80::inta`` callback, which replaces ``Z80::int_data``. +37. Added optional full emulation of the interrupt mode 0, along with the new ``Z80::int_fetch`` callback to perform bus read operations on instruction data. If not enabled at compile-time, the old simplified emulation is built, which supports only the most typical instructions. +38. Added accurate flag behavior in the following instructions: ``ldir``, ``lddr``, ``cpir``, ``cpdr``, ``inir``, ``indr``, ``otir`` and ``otdr``. +39. Added emulation of the interrupt acceptance deferral that occurs during the ``reti`` and ``retn`` instructions. +40. Added MEMPTR emulation. The ``bit N,(hl)`` instruction now produces a correct value of F. +41. Added optional emulation of the Q "register". If enabled at compile-time, the ``ccf`` and ``scf`` instructions produce a correct value of F. +42. Added emulation options that can be configured at runtime. +43. Added emulation of the ``out (c),255`` instruction (Zilog Z80 CMOS). +44. Added optional emulation of the bug affecting the ``ld a,{i|r}`` instructions (Zilog Z80 NMOS). If enabled at compile-time, the P/V flag is reset when an INT is accepted during the execution of these instructions. +45. Added the ``Z80::fetch_opcode`` and ``Z80::fetch`` callbacks to perform opcode fetch operations and memory read operations on instruction data respectively. +46. Added hooking functionality through the ``ld h,h`` instruction and the new ``Z80::hook`` callback. +47. Added the ``Z80::nop`` callback to perform disregarded opcode fetch operations during internal NOP M-cycles. +48. Added four callbacks to notify the execution of important instructions: ``Z80::ld_i_a``, ``Z80::ld_r_a``, ``Z80::reti`` and ``Z80::retn``. +49. Removed ``Z80::state``. Replaced with individual members for the registers, the interrupt enable flip-flops and the interrupt mode. +50. Removed the superfluous EI flag. The previous opcode is checked instead, which is faster and makes the ``Z80`` object smaller. +51. Removed all module-related stuff. +52. Optimizations in flag computation and condition evaluation. +53. New source code comments and improvements to existing ones. +54. Improved code aesthetics. +55. Other improvements, optimizations and minor changes. + +v0.1 (2018-11-10) +================= + +Initial public release. diff --git a/documentation/conf.py b/documentation/conf.py new file mode 100644 index 0000000..67eeeb0 --- /dev/null +++ b/documentation/conf.py @@ -0,0 +1,55 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'Z80' +copyright = '1999-2022 Manuel Sainz de Baranda y Goñi' +author = 'Manuel Sainz de Baranda y Goñi' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['breathe'] + +# Breathe Configuration. +breathe_default_project = 'Z80' +breathe_domain_by_extension = {"h": "c"} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +#html_theme = 'haiku' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/documentation/index.rst b/documentation/index.rst new file mode 100644 index 0000000..704c67e --- /dev/null +++ b/documentation/index.rst @@ -0,0 +1,27 @@ +.. Z80 documentation master file, created by + sphinx-quickstart on Wed Feb 24 06:49:20 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Zilog Z80 CPU Emulator +====================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + EmulationAccuracy + Installation + Integration + APIReference + Testing + Thanks + VersionHistory + License + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + diff --git a/documentation/make.bat b/documentation/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/documentation/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/file_id.diz b/file_id.diz new file mode 100644 index 0000000..b6080ca --- /dev/null +++ b/file_id.diz @@ -0,0 +1,5 @@ +Z80 v0.2 - Fast, small and accurate emulator +of the Zilog Z80. Version 0.2 implements all +that is known to date about this CPU, passes +all test suites and is carefully designed to +fit most use cases. diff --git a/sources/Z80.c b/sources/Z80.c index 7bdd3d6..9c16254 100644 --- a/sources/Z80.c +++ b/sources/Z80.c @@ -1,145 +1,220 @@ -/* ______ ______ ______ - /\___ \ /\ __ \/\ __ \ - \/__/ /_\ \ __ \ \ \/\ \ - /\_____\ \_____\ \_____\ -Zilog \/_____/\/_____/\/_____/ CPU Emulator ---------------------------------- -Copyright (C) 1999-2018 Manuel Sainz de Baranda y Goñi. +/* Z80 v0.2 + ______ ______ ______ + /\___ \/\ __ \\ __ \ + ____ \/__/ /\_\ __ \\ \/\ \ ________________________________________________ +| /\_____\\_____\\_____\ | +| 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 . | +| | +| -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | +| | +| A NOTE FROM THE ORIGINAL AUTHOR | +| | +| Those familiar with the official documentation of the Zilog Z80 CPU will | +| find this source code quite intuitive. The purpose has not been to write | +| the fastest possible emulator, although the speed aspect is not neglected, | +| but a portable, hackable and well | +| .----._.----. structured piece of software; | +| A11 <-01--|1 o|--40-> A10 something small, solid and elegant | +| A12 <-02--| |--39-> A09 that can stand the test of time | +| A13 <-03--| |--38-> A08 with no need for major changes. | +| A14 <-04--| |--37-> A07 | +| A15 <-05--| |--36-> A06 Everything that is known about the | +| CLK --06->| |--35-> A05 Z80 is emulated here, including | +| D4 <-07->| |--34-> A04 the interrupt mode 0, which has | +| D3 <-08->|.---------.|--33-> A03 not been fully implemented to date | +| D5 <-09->|| ZILOG ||--32-> A02 in any other known open source Z80 | +| D6 <-10->|| Z80 ||--31-> A01 emulator. This is, therefore, the | +| +5V --11--|| CPU ||--30-> A00 first complete and free emulator. | +| D2 <-12->|| ||--29-- GND | +| D7 <-13->|'---------'|--28-> RFSH Some of the main design decisions | +| D0 <-14->| |--27-> M1 have been the following: | +| D1 <-15->| |<-26-- RESET | +| INT --16->| |<-25-- BUSREQ 1. The logic of the opcodes has | +| NMI --17->| |<-24-- WAIT been separated from that of the | +| HALT <-18--| |--23-> BUSACK arguments to reduce the size of | +| MREQ <-19--| |--22-> WR the emulator, thus increasing the | +| IORQ <-20--| |--21-> RD possibility that the CPU loads all | +| '-----------' its code in the L1 cache. | +| Zilog Z80 CPU, May 1976 version | +| 40-pin ceramic DIP pinout 2. The use of function pointer | +| tables has been chosen over large | +| switch statements for opcode selection. This allows easy reuse of almost | +| all instruction emulation code in the interrupt mode 0. | +| | +| 3. Special attention has been given to avoid conditional statements as | +| much as possible to reduce the branch penalty in pipelined processors. | +| | +'=============================================================================*/ -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 . ---------------------------------------------------------------------------- */ - -#ifndef CPU_Z80_DEPENDENCIES_H -# include -# include +#ifndef Z80_DEPENDENCIES_HEADER +# include +# include #endif -#if defined(CPU_Z80_HIDE_API) -# define CPU_Z80_API static -#elif defined(CPU_Z80_STATIC) -# define CPU_Z80_API +#ifdef Z80_STATIC +# define Z80_API #else -# define CPU_Z80_API Z_API_EXPORT +# define Z80_API Z_API_EXPORT #endif -#if defined(CPU_Z80_WITH_MODULE_ABI) && !defined(CPU_Z80_WITH_ABI) -# define CPU_Z80_WITH_ABI -#endif - -#ifdef CPU_Z80_WITH_ABI -# if defined(CPU_Z80_HIDE_ABI) -# define CPU_Z80_ABI static -# elif defined(CPU_Z80_STATIC) -# define CPU_Z80_ABI -# else -# define CPU_Z80_ABI Z_API_EXPORT -# endif -#endif - -#ifdef CPU_Z80_USE_LOCAL_HEADER +#ifdef Z80_WITH_LOCAL_HEADER # include "Z80.h" #else -# include +# include #endif /* MARK: - Types */ -typedef zuint8 (* Instruction)(Z80 *object); +typedef zuint8 (* Instruction)(Z80 *self); + +#ifdef Z80_WITH_FULL_IM0 + typedef struct { + Z80* z80; + void* context; + Z80Read fetch; + Z80Read read; + Z80Write write; + Z80Read in; + Z80Write out; + Z80Notify ld_i_a; + Z80Notify ld_r_a; + Z80Notify reti; + Z80Notify retn; + zuint16 pc; + } IM0; +#endif -/* MARK: - Macros: External */ +/* MARK: - External Macros */ -#define O(member) Z_OFFSET_OF(Z80, member) -#define ROL(value) value = (zuint8)Z_8BIT_ROTATE_LEFT( value, 1) -#define ROR(value) value = (zuint8)Z_8BIT_ROTATE_RIGHT(value, 1) +#define REGISTER_OFFSET(member) (zusize)Z_MEMBER_OFFSET(Z80, member) +#define ROL(value) value = (zuint8)Z_UINT8_ROTATE_LEFT( value, 1) +#define ROR(value) value = (zuint8)Z_UINT8_ROTATE_RIGHT(value, 1) -/* MARK: - Macros & Functions: Callback */ +/* MARK: - Instance Variable and Callback Shortcuts */ -#define READ_8(address) object->read (object->context, (zuint16)(address)) -#define WRITE_8(address, value) object->write (object->context, (zuint16)(address), (zuint8)(value)) -#define IN(port) object->in (object->context, (zuint16)(port )) -#define OUT(port, value) object->out (object->context, (zuint16)(port ), (zuint8)(value)) -#define INT_DATA object->int_data(object->context) -#define READ_OFFSET(address) ((zsint8)READ_8(address)) -#define SET_HALT if (object->halt != NULL) object->halt(object->context, TRUE ) -#define CLEAR_HALT if (object->halt != NULL) object->halt(object->context, FALSE) +#define MEMPTR self->memptr.uint16_value +#define MEMPTRH self->memptr.uint8_values.at_1 +#define MEMPTRL self->memptr.uint8_values.at_0 +#define PC self->pc.uint16_value +#define SP self->sp.uint16_value +#define IX self->ix.uint16_value +#define IY self->iy.uint16_value +#define AF self->af.uint16_value +#define BC self->bc.uint16_value +#define DE self->de.uint16_value +#define HL self->hl.uint16_value +#define AF_ self->af_.uint16_value +#define BC_ self->bc_.uint16_value +#define DE_ self->de_.uint16_value +#define HL_ self->hl_.uint16_value +#define A self->af.uint8_values.at_1 +#define F self->af.uint8_values.at_0 +#define B self->bc.uint8_values.at_1 +#define C self->bc.uint8_values.at_0 +#define E self->de.uint8_values.at_0 +#define L self->hl.uint8_values.at_0 +#define I self->i +#define R self->r +#define R7 self->r7 +#define Q self->q +#define IFF1 self->iff1 +#define IFF2 self->iff2 +#define IM self->im +#define HALT_LINE self->halt_line +#define INT_LINE self->int_line +#define XY self->xy.uint16_value +#define DATA self->data.uint8_array +#define REQUEST self->request +#define RESUME self->resume +#define OPTIONS self->options +#define CONTEXT self->context + +#define FETCH_OPCODE(address) self->fetch_opcode(CONTEXT, address) +#define FETCH(address) self->fetch(CONTEXT, address) +#define READ(address) self->read (CONTEXT, address) +#define WRITE(address, value) self->write(CONTEXT, address, value) +#define IN(port) self->in (CONTEXT, port) +#define OUT(port, value) self->out(CONTEXT, port, value) +#define NOTIFY(callback) if (self->callback != Z_NULL) self->callback(CONTEXT) +#define INTA self->inta(CONTEXT, PC) -static Z_INLINE zuint16 read_16bit(Z80 *object, zuint16 address) - {return (zuint16)(READ_8(address) | (zuint16)READ_8(address + 1) << 8);} +/* MARK: - Callbacks: 16-bit Operations */ - -static Z_INLINE void write_16bit(Z80 *object, zuint16 address, zuint16 value) +static Z_INLINE zuint16 fetch_16(Z80 const *self, zuint16 address) { - WRITE_8(address, (zuint8)value); - WRITE_8(address + 1, value >> 8); + zuint8 t = FETCH(address); + return (zuint16)(t | ((zuint16)FETCH(address + 1) << 8)); } -#define READ_16(address) read_16bit (object, (zuint16)(address)) -#define WRITE_16(address, value) write_16bit(object, (zuint16)(address), (zuint16)(value)) +static Z_INLINE zuint16 read_16(Z80 const *self, zuint16 address) + { + zuint8 t = READ(address); + return (zuint16)(t | ((zuint16)READ(address + 1) << 8)); + } -/* MARK: - Macros: Registers */ - -#define AF object->state.Z_Z80_STATE_MEMBER_AF -#define BC object->state.Z_Z80_STATE_MEMBER_BC -#define DE object->state.Z_Z80_STATE_MEMBER_DE -#define HL object->state.Z_Z80_STATE_MEMBER_HL -#define IX object->state.Z_Z80_STATE_MEMBER_IX -#define IY object->state.Z_Z80_STATE_MEMBER_IY -#define PC object->state.Z_Z80_STATE_MEMBER_PC -#define SP object->state.Z_Z80_STATE_MEMBER_SP -#define AF_ object->state.Z_Z80_STATE_MEMBER_AF_ -#define BC_ object->state.Z_Z80_STATE_MEMBER_BC_ -#define DE_ object->state.Z_Z80_STATE_MEMBER_DE_ -#define HL_ object->state.Z_Z80_STATE_MEMBER_HL_ -#define A object->state.Z_Z80_STATE_MEMBER_A -#define F object->state.Z_Z80_STATE_MEMBER_F -#define B object->state.Z_Z80_STATE_MEMBER_B -#define C object->state.Z_Z80_STATE_MEMBER_C -#define L object->state.Z_Z80_STATE_MEMBER_L -#define I object->state.Z_Z80_STATE_MEMBER_I -#define R object->state.Z_Z80_STATE_MEMBER_R -#define R_ALL ((R & 127) | (R7 & 128)) +static Z_INLINE void write_16f(Z80 const *self, zuint16 address, zuint16 value) + { + WRITE(address, (zuint8)value); + WRITE(address + 1, (zuint8)(value >> 8)); + } -/* MARK: - Macros: Internal Bits */ - -#define HALT object->state.Z_Z80_STATE_MEMBER_HALT -#define IFF1 object->state.Z_Z80_STATE_MEMBER_IFF1 -#define IFF2 object->state.Z_Z80_STATE_MEMBER_IFF2 -#define EI object->state.Z_Z80_STATE_MEMBER_EI -#define IM object->state.Z_Z80_STATE_MEMBER_IM -#define NMI object->state.Z_Z80_STATE_MEMBER_NMI -#define INT object->state.Z_Z80_STATE_MEMBER_IRQ +static Z_INLINE void write_16b(Z80 const *self, zuint16 address, zuint16 value) + { + WRITE(address + 1, (zuint8)(value >> 8)); + WRITE(address, (zuint8)value); + } -/* MARK: - Macros: Temporal Data */ - -#define CYCLES object->cycles -#define R7 object->r7 -#define BYTE(index) object->data.array_uint8[index] -#define BYTE0 BYTE(0) -#define BYTE1 BYTE(1) -#define BYTE2 BYTE(2) -#define BYTE3 BYTE(3) -#define XY object->xy.value_uint16 -#define XY_ADDRESS ((zuint16)(XY + object->data.array_sint8[2])) +#ifndef Z80_WITH_FULL_IM0 + static Z_INLINE zuint16 int_fetch_16(Z80 const *self) + { + zuint8 t = self->int_fetch(CONTEXT, PC); + return (zuint16)(t | ((zuint16)self->int_fetch(CONTEXT, PC) << 8)); + } +#endif -/* MARK: - Macros: Flags */ +#define FETCH_16(address) fetch_16 (self, address) +#define READ_16(address) read_16 (self, address) +#define WRITE_16F(address, value) write_16f(self, address, value) +#define WRITE_16B(address, value) write_16b(self, address, value) + + +/* MARK: - Flags */ + +/*----------------. +| 7 6 5 4 3 2 1 0 | +| S Z Y H X P N C | +'-|-|-|-|-|-|-|-|-' + | | | | | | | '-- carry / borrow + | | | | | | '---- addition / subtraction + | | | | | '------ parity (P) / two's complement signed overflow (V) + | | | | '-------- result's bit 3 (undocumented) + | | | '---------- half carry / half borrow + | | '------------ result's bit 5 (undocumented) + | '-------------- zero + '---------------- sign */ #define SF 128 #define ZF 64 @@ -150,23 +225,32 @@ static Z_INLINE void write_16bit(Z80 *object, zuint16 address, zuint16 value) #define NF 2 #define CF 1 -#define SZPF (SF | ZF | PF) -#define SYXF (SF | YF | XF) -#define ZPF (ZF | PF ) -#define YXCF (YF | XF | CF) -#define YXF (YF | XF ) -#define PNF (PF | NF ) -#define HCF (HF | CF ) +#define SZPCF (SF | ZF | PF | CF) +#define SZPF (SF | ZF | PF ) +#define SZCF (SF | ZF | CF ) +#define SYXF (SF | YF | XF ) +#define ZPF (ZF | PF ) +#define YXCF (YF | XF | CF ) +#define YXF (YF | XF ) +#define HCF (HF | CF ) -#define F_H (F & HF) -#define F_N (F & NF) -#define F_C (F & CF) -#define F_SZP (F & SZPF) -#define A_SYX (A & SYXF) -#define A_YX (A & YXF) +#define F_SZPC (F & SZPCF) +#define F_SZP (F & SZPF) +#define F_SZC (F & SZCF) +#define F_C (F & CF) +#define A_SYX (A & SYXF) +#define A_YX (A & YXF) #define ZF_ZERO(value) (!(value) << 6) +#ifdef Z80_WITH_Q +# define FLAGS Q = F +# define Q_0 Q = 0; +#else +# define FLAGS F +# define Q_0 +#endif + /* MARK: - P/V Flag Computation */ @@ -192,1468 +276,2361 @@ static zuint8 const pf_parity_table[256] = { #define PF_PARITY(value) pf_parity_table[value] -#define VF(function, operand) \ -static Z_INLINE zuint8 pf_overflow_##function##8(zuint8 a, zuint8 b) \ - { \ - zsint total = ((zsint)((zsint8)a)) operand ((zsint)((zsint8)b)); \ - \ - return total < -128 || total > 127 ? PF : 0; \ - } - -VF(add, +) -VF(sub, -) - -#undef VF -#define VF(function, bits, type, operand, minimum, maximum) \ -static Z_INLINE zuint8 pf_overflow_##function##bits(zuint##bits a, zuint##bits b, zuint8 carry) \ - { \ - type total = ((type)((zsint##bits)a)) operand ((type)((zsint##bits)b)) operand carry; \ - \ - return total < minimum || total > maximum ? PF : 0; \ - } - -VF(adc, 8, zsint, +, -128, 127) -VF(sbc, 8, zsint, -, -128, 127) -VF(adc, 16, zsint32, +, -32768, 32767) -VF(sbc, 16, zsint32, -, -32768, 32767) +#define PF_OVERFLOW(bits, result, lhs, rhs) \ + (((zuint##bits)((lhs ^ rhs) & (lhs ^ result)) >> (bits - 3)) & PF) -/* MARK: - 8-Bit Register Resolution +/* MARK: - 8-Bit Register Resolution */ - .----------. .---------. .-----------. .-----------. - | 76543210 | | X / Y | | J / K | | P / Q | - |----------| |---------| |-----------| |-----------| - | __xxx___ | | 000 = b | | 000 = b | | 000 = b | - | _____yyy | | 001 = c | | 001 = c | | 001 = c | - | __jjj___ | | 010 = d | | 010 = d | | 010 = d | - | _____kkk | | 011 = e | | 011 = e | | 011 = e | - | __ppp___ | | 100 = h | | 100 = ixh | | 100 = iyh | - | _____qqq | | 101 = l | | 101 = ixl | | 101 = iyl | - '----------' | 111 = a | | 111 = a | | 111 = a | - '---------' '-----------' '----------*/ +/*---------. .---------------------. +| 76543210 | | J / K | O / P | +|----------| |---------+-----------| +| __jjj___ | | 000 = b | 000 = b | +| _____kkk | | 001 = c | 001 = c | +| __ooo___ | | 010 = d | 010 = d | +| _____ppp | | 011 = e | 011 = e | +'----------' | 100 = h | 100 = XYh | + | 101 = l | 101 = XYl | + | 111 = a | 111 = a | + '--------------------*/ -static zuint8 const x_y_table[8] = { - O(state.Z_Z80_STATE_MEMBER_B), - O(state.Z_Z80_STATE_MEMBER_C), - O(state.Z_Z80_STATE_MEMBER_D), - O(state.Z_Z80_STATE_MEMBER_E), - O(state.Z_Z80_STATE_MEMBER_H), - O(state.Z_Z80_STATE_MEMBER_L), +static zusize const j_k_table[8] = { + REGISTER_OFFSET(bc.uint8_values.at_1), + REGISTER_OFFSET(bc.uint8_values.at_0), + REGISTER_OFFSET(de.uint8_values.at_1), + REGISTER_OFFSET(de.uint8_values.at_0), + REGISTER_OFFSET(hl.uint8_values.at_1), + REGISTER_OFFSET(hl.uint8_values.at_0), 0, - O(state.Z_Z80_STATE_MEMBER_A) + REGISTER_OFFSET(af.uint8_values.at_1) }; -static zuint8 const j_k_p_q_table[8] = { - O(state.Z_Z80_STATE_MEMBER_B), - O(state.Z_Z80_STATE_MEMBER_C), - O(state.Z_Z80_STATE_MEMBER_D), - O(state.Z_Z80_STATE_MEMBER_E), - O(xy.values_uint8.index1 ), - O(xy.values_uint8.index0 ), +static zusize const o_p_table[8] = { + REGISTER_OFFSET(bc.uint8_values.at_1), + REGISTER_OFFSET(bc.uint8_values.at_0), + REGISTER_OFFSET(de.uint8_values.at_1), + REGISTER_OFFSET(de.uint8_values.at_0), + REGISTER_OFFSET(xy.uint8_values.at_1), + REGISTER_OFFSET(xy.uint8_values.at_0), 0, - O(state.Z_Z80_STATE_MEMBER_A) + REGISTER_OFFSET(af.uint8_values.at_1) }; -#define R_8(name, table, offset, mask, shift) \ -static Z_INLINE zuint8 *name(Z80 *object) \ - {return ((zuint8 *)object) + table[(BYTE(offset) & mask) shift];} +#define REGISTER_8(table, offset, shift) \ + *((zuint8 *)self + table[(DATA[offset] shift) & 7]) -R_8(__xxx___0, x_y_table, 0, 56, >> 3 ) -R_8(__xxx___1, x_y_table, 1, 56, >> 3 ) -R_8(_____yyy0, x_y_table, 0, 7, Z_EMPTY) -R_8(_____yyy1, x_y_table, 1, 7, Z_EMPTY) -R_8(_____yyy3, x_y_table, 3, 7, Z_EMPTY) -R_8(__jjj___ , j_k_p_q_table, 1, 56, >> 3 ) -R_8(_____kkk , j_k_p_q_table, 1, 7, Z_EMPTY) +#define J0 REGISTER_8(j_k_table, 0, >> 3 ) +#define J1 REGISTER_8(j_k_table, 1, >> 3 ) +#define K0 REGISTER_8(j_k_table, 0, Z_EMPTY) +#define K1 REGISTER_8(j_k_table, 1, Z_EMPTY) +#define K3 REGISTER_8(j_k_table, 3, Z_EMPTY) +#define O REGISTER_8(o_p_table, 1, >> 3 ) +#define P REGISTER_8(o_p_table, 1, Z_EMPTY) -/* MARK: - 16-Bit Register Resolution +/* MARK: - 16-Bit Register Resolution */ - .----------. .---------. .---------. .---------. - | 76543210 | | S | | T | | W | - |----------| |---------| |---------| |---------| - | __ss____ | | 00 = bc | | 00 = bc | | 00 = bc | - | __tt____ | | 01 = de | | 01 = de | | 01 = de | - '----------' | 10 = hl | | 10 = hl | | 10 = XY | - | 11 = sp | | 11 = af | | 11 = sp | - '---------' '---------' '--------*/ +/*---------. .-----------------------------. +| 76543210 | | S | T | W | +|----------| |---------+---------+---------| +| __ss____ | | 00 = bc | 00 = bc | 00 = bc | +| __tt____ | | 01 = de | 01 = de | 01 = de | +| __ww____ | | 10 = hl | 10 = hl | 10 = XY | +'----------' | 11 = sp | 11 = af | 11 = sp | + '----------------------------*/ -static zuint8 const s_table[4] = { - O(state.Z_Z80_STATE_MEMBER_BC), - O(state.Z_Z80_STATE_MEMBER_DE), - O(state.Z_Z80_STATE_MEMBER_HL), - O(state.Z_Z80_STATE_MEMBER_SP) +static zusize const s_table[4] = { + REGISTER_OFFSET(bc.uint16_value), + REGISTER_OFFSET(de.uint16_value), + REGISTER_OFFSET(hl.uint16_value), + REGISTER_OFFSET(sp) }; -static zuint8 const t_table[4] = { - O(state.Z_Z80_STATE_MEMBER_BC), - O(state.Z_Z80_STATE_MEMBER_DE), - O(state.Z_Z80_STATE_MEMBER_HL), - O(state.Z_Z80_STATE_MEMBER_AF) +static zusize const t_table[4] = { + REGISTER_OFFSET(bc.uint16_value), + REGISTER_OFFSET(de.uint16_value), + REGISTER_OFFSET(hl.uint16_value), + REGISTER_OFFSET(af.uint16_value) }; -static zuint8 const w_table[4] = { - O(state.Z_Z80_STATE_MEMBER_BC), - O(state.Z_Z80_STATE_MEMBER_DE), - O(xy ), - O(state.Z_Z80_STATE_MEMBER_SP) +static zusize const w_table[4] = { + REGISTER_OFFSET(bc.uint16_value), + REGISTER_OFFSET(de.uint16_value), + REGISTER_OFFSET(xy.uint16_value), + REGISTER_OFFSET(sp) }; -#define R_16(name, table, offset) \ -static Z_INLINE zuint16 *name(Z80 *object) \ - {return Z_BOP(zuint16 *, object, table[(BYTE(offset) & 48) >> 4]);} +#define REGISTER_16(table, offset) \ + *(zuint16 *)(void *)((zchar *)self + table[(DATA[offset] >> 4) & 3]) -R_16(__ss____0, s_table, 0) -R_16(__ss____1, s_table, 1) -R_16(__tt____ , t_table, 0) +#define SS0 REGISTER_16(s_table, 0) +#define SS1 REGISTER_16(s_table, 1) +#define TT REGISTER_16(t_table, 0) +#define WW REGISTER_16(w_table, 1) -/* MARK: - Condition Resolution +/* MARK: - Condition Evaluation */ - .----------. .----------. - | 76543210 | | Z | - |----------| |----------| - | __zzz___ | | 000 = nz | - | ___zz___ | | 001 = z | - '----------' | 010 = nc | - | 011 = c | - | 100 = po | - | 101 = pe | - | 110 = p | - | 111 = m | - '---------*/ +/*---------. .----------. +| 76543210 | | Z | +|----------| |----------| +| __zzz___ | | 000 = nz | +| ___zz___ | | 001 = z | +'----------' | 010 = nc | + | 011 = c | + | 100 = po | + | 101 = pe | + | 110 = p | + | 111 = m | + '---------*/ static zuint8 const z_table[8] = {ZF, ZF, CF, CF, PF, PF, SF, SF}; -static Z_INLINE zboolean __zzz___(Z80 *object) - { - zuint8 z = (BYTE0 & 56) >> 3; - return (F & (z_table[z])) - ? (z & 1) /* Flag is 1 */ - : !(z & 1); /* Flag is 0 */ +static Z_INLINE zsint zzz(Z80 const *self, zuint8 mask) + { + zsint z = (DATA[0] >> 3) & mask; + + return !(F & z_table[z]) ^ (z & 1); } -/* MARK: - 8-Bit Arithmetic and Logical Operation Resolution and Execution +/* MARK: - 8-Bit Arithmetic and Logical Operations */ - .----------. .-----------. .-------------------------------. - | 76543210 | | U | | S | Z | Y | H | X | P | N | C | - |----------| |-----------| .-----+---+---+---+---+---+---+---+---| - | __uuu___ | | 000 = add | | add | S | Z | 5 | H | 3 | V | 0 | C | - | _____vvv | | 001 = adc | |-----+---+---+---+---+---+---+---+---| - '----------' | 010 = sub | | adc | S | Z | 5 | H | 3 | V | 0 | C | - | 011 = sbc | |-----+---+---+---+---+---+---+---+---| - | 100 = and | | sub | S | Z | 5 | H | 3 | V | 1 | C | - | 101 = xor | |-----+---+---+---+---+---+---+---+---| - | 110 = or | | sbc | S | Z | 5 | H | 3 | V | 1 | C | - | 111 = cp | |-----+---+---+---+---+---+---+---+---| - |-----------| | and | S | Z | 5 | 1 | 3 | P | 0 | 0 | - | V | |-----+---+---+---+---+---+---+---+---| - |-----------| | xor | S | Z | 5 | 0 | 3 | P | 0 | 0 | - | 100 = inc | |-----+---+---+---+---+---+---+---+---| - | 101 = dec | | or | S | Z | 5 | 0 | 3 | P | 0 | 0 | - '-----------' |-----+---+---+---+---+---+---+---+---| - | cp | S | Z |v.5| H |v.3| V | 1 | C | - |-----+---+---+---+---+---+---+---+---| - | inc | S | Z |v.5| H |v.3| V | 0 | . | - |-----+---+---+---+---+---+---+---+---| - | dec | S | Z |v.5| H |v.3| V | 1 | . | - '------------------------------------*/ +/*---------. .---------------------. +| 76543210 | | U SZYHXPNC | +|----------| |---------------------| +| __uuu___ | | 000 = add szycxv0c | +| _____10v | | 001 = adc szycxv0c | +'----------' | 010 = sub szybxv1b | + | 011 = sbc szybxv1b | + | 100 = and szy1xp00 | + | 101 = xor szy0xp00 | + | 110 = or szy0xp00 | + | 111 = cp sz*b*v1b | + |---------------------| + | V SZYHXPNC | + |---------------------| + | 100 = inc szycxv0. | + | 101 = dec szybxv1. | + '--------------------*/ -static void __uuu___(Z80 *object, zuint8 offset, zuint8 value) +static void uuu(Z80 *self, zuint8 offset, zuint8 value) { - zuint8 t; + zuint8 t, f; - switch ((object->data.array_uint8[offset] >> 3) & 7) + switch ((DATA[offset] >> 3) & 7) { - case 0: /* ADD */ + case 0: /* add */ t = A + value; - F = ((zuint)A + value > 255) /* CF = Carry */ - | pf_overflow_add8(A, value) /* PF = Overflow */ - | ((A ^ value ^ t) & HF); /* HF = Half-carry */ - A = t; /* NF = 0 */ + f = ((zuint)A + value > 255) | /* CF = carry */ + PF_OVERFLOW(8, t, A, ~value) | /* PF = overflow */ + ((A ^ value ^ t) & HF); /* HF = half-carry */ + A = t; /* NF = 0 */ break; - case 1: /* ADC */ - t = F_C; + case 1: /* adc */ + t = A + value + (f = F_C); - F = ((zuint)A + value + t > 255) /* CF = Carry */ - | pf_overflow_adc8(A, value, t) /* PF = Overflow */ - | (((A & 0xF) + (value & 0xF) + t) & HF); /* HF = Half-carry */ - /* NF = 0 */ - A += value + t; - break; - - case 2: /* SUB */ - t = A - value; - - F = (A < value) /* CF = Borrow */ - | NF /* NF = 1 */ - | pf_overflow_sub8(A, value) /* PF = Overflow */ - | ((A ^ value ^ t) & HF); /* HF = Half-Borrow */ + f = ((zuint)A + value + f > 255) | /* CF = carry */ + PF_OVERFLOW(8, t, A, ~value) | /* PF = overflow */ + ((A ^ value ^ t) & HF); /* HF = half-carry */ + /* NF = 0 */ A = t; break; - case 3: /* SBC */ - t = F_C; - - F = ((zsint)A - (zsint)value - (zsint)t < 0) /* CF = Borrow */ - | NF /* NF = 1 */ - | pf_overflow_sbc8(A, value, t) /* PF = Overflow */ - | (((A & 0xF) - (value & 0xF) - t) & HF); /* HF = Half-Borrow */ - - A -= value + t; - break; - - case 4: /* AND */ - A &= value; - F = HF | PF_PARITY(A); /* HF = 1; PF = Parity */ - break; /* NF, CF = 0 */ - - case 5: /* XOR */ - A ^= value; - F = PF_PARITY(A); /* PF = Parity */ - break; /* HF, NF, CF = 0 */ - - case 6: /* OR */ - A |= value; - F = PF_PARITY(A); /* PF = Parity */ - break; /* HF, NF, CF = 0 */ - - case 7: /* CP */ + case 2: /* sub */ t = A - value; - F = (zuint8) - ((A < value) /* CF = Borrow */ - | NF /* NF = 1 */ - | pf_overflow_sub8(A, value) /* PF = Overflow */ - | ((A ^ value ^ t) & HF) /* HF = Half-Borrow */ - | (value & YXF) /* YF = v.5, XF = v.3 */ - | ZF_ZERO(t) /* ZF = !(A - v) */ - | (t & SF)); /* SF = (A - v).7 */ + f = (A < value) | /* CF = borrow */ + NF | /* NF = 1 */ + PF_OVERFLOW(8, t, A, value) | /* PF = overflow */ + ((A ^ value ^ t) & HF); /* HF = half-borrow */ + A = t; + break; + + case 3: /* sbc */ + t = A - value - (f = F_C); + + f = ((zsint)A - value - f < 0) | /* CF = borrow */ + NF | /* NF = 1 */ + PF_OVERFLOW(8, t, A, value) | /* PF = overflow */ + ((A ^ value ^ t) & HF); /* HF = half-borrow */ + + A = t; + break; + + case 4: /* and */ + A &= value; + f = HF | PF_PARITY(A); /* HF = 1; PF = parity */ + break; /* NF, CF = 0 */ + + case 5: /* xor */ + A ^= value; + f = PF_PARITY(A); /* PF = parity */ + break; /* HF, NF, CF = 0 */ + + case 6: /* or */ + A |= value; + f = PF_PARITY(A); /* PF = parity */ + break; /* HF, NF, CF = 0 */ + + case 7: /* cp */ + t = A - value; + + FLAGS = (zuint8)( + (t & SF) | /* SF = sign */ + ZF_ZERO(t) | /* ZF = zero */ + ((A ^ value ^ t) & HF) | /* HF = half-borrow */ + PF_OVERFLOW(8, t, A, value) | /* PF = overflow */ + (A < value) | /* CF = borrow */ + (value & YXF) | /* YF = rhs.5; XF = rhs.3 */ + NF); /* NF = 1 */ + return; } - F = (zuint8) - ((F & (HF | PF | NF | CF)) /* CF, NF, PF and HF already changed */ - | A_SYX /* SF = A.7; YF = A.5; XF = A.3 */ - | ZF_ZERO(A)); /* ZF = !A */ + FLAGS = (zuint8)( + f | /* HF, PF, NF and CF already computed */ + A_SYX | /* SF = sign; YF = Y; XF = X */ + ZF_ZERO(A)); /* ZF = zero */ } -static zuint8 _____vvv(Z80 *object, zuint8 offset, zuint8 value) +static zuint8 vvv(Z80 *self, zuint8 offset, zuint8 value) { - zuint8 t, pn; + zuint8 t, pnf; - /* DEC */ - if (object->data.array_uint8[offset] & 1) - { /* PF = Overflow */ - pn = value == 128 ? PNF : NF; /* NF = 1 */ + /* dec */ + if (DATA[offset] & 1) + { + pnf = (zuint8)(((value == 128) << 2) | NF); /* PF = overflow; NF = 1 */ t = value - 1; } - /* INC */ - else { /* PF = Overflow */ - pn = value == 127 ? PF : 0; /* NF = 0 */ + /* inc */ + else { + pnf = (zuint8)((value == 127) << 2); /* PF = overflow; NF = 0 */ t = value + 1; } - F = (zuint8) - ((F & CF) /* CF unchanged */ - | pn /* PF and NF already calculated */ - | (t & SYXF) /* SF = v.7; YF = v.5; XF = v.3 */ - | ((value ^ 1 ^ t) & HF) /* HF = Half-Borrow */ - | ZF_ZERO(t)); /* ZF = !v */ + FLAGS = (zuint8)( + pnf | /* PF and NF already computed */ + (t & SYXF) | /* SF = sign; YF = Y; XF = X */ + ZF_ZERO(t) | /* ZF = zero */ + ((value ^ t) & HF) | /* HF = half-carry/borrow */ + F_C); /* CF unchanged */ return t; } -/* MARK: - Rotation and Shift Operation Resolution and Execution +/* MARK: - Rotation and Shift Operations */ - .----------. .-----------. - | 76543210 | | G | - |----------| |-----------| - | __ggg___ | | 000 = rlc | - '----------' | 001 = rrc | - | 010 = rl | - | 011 = rr | - | 100 = sla | - | 101 = sra | - | 110 = sll | - | 111 = srl | - '----------*/ +/*---------. .-----------. +| 76543210 | | G | +|----------| |-----------| +| __ggg___ | | 000 = rlc | +'----------' | 001 = rrc | + | 010 = rl | + | 011 = rr | + | 100 = sla | + | 101 = sra | + | 110 = sll | + | 111 = srl | + '----------*/ -static zuint8 __ggg___(Z80 *object, zuint8 offset, zuint8 value) +static zuint8 ggg(Z80 *self, zuint8 offset, zuint8 value) { - zuint8 c; + zuint8 cf; - switch ((object->data.array_uint8[offset] >> 3) & 7) + switch ((DATA[offset] >> 3) & 7) { - /* RLC .----------------. - .----. | .---------. | + /* rlc .----------------. + .----. | .---------. | | CF |<-----| 7 <-- 0 |<--' '----' '--------*/ case 0: ROL(value); - c = value & CF; + cf = value & 1; break; - /* RRC .----------------. + /* rrc .----------------. | .---------. | .----. '-->| 7 --> 0 |----->| CF | '---------' '---*/ case 1: - c = value & CF; + cf = value & 1; ROR(value); break; - /* RL .-------------------------. + /* rl .-------------------------. | .----. .---------. | '--| CF |<--| 7 <-- 0 |<--' '----' '--------*/ case 2: - c = value >> 7; + cf = value >> 7; value = (zuint8)((value << 1) | F_C); break; - /* RR .-------------------------. + /* rr .-------------------------. | .---------. .----. | '-->| 7 --> 0 |-->| CF |--' '---------' '---*/ case 3: - c = value & CF; - value = (zuint8)((value >> 1) | (F_C << 7)); + cf = value & 1; + value = (zuint8)((value >> 1) | ((zuint8)F_C << 7)); break; - /* SLA .----. .---------. + /* sla .----. .---------. | CF |<--| 7 <-- 0 |<-- 0 '----' '--------*/ case 4: - c = value >> 7; + cf = value >> 7; value <<= 1; break; - /* SRA .---------. .----. + /* sra .---------. .----. .-->| 7 --> 0 |-->| CF | | '---------' '----' | | '----*/ case 5: - c = value & CF; - value = (value & 128) | (value >> 1); + cf = value & 1; + value = (zuint8)((value & 128) | (value >> 1)); break; - /* SLL .----. .---------. + /* sll .----. .---------. | CF |<--| 7 <-- 0 |<-- 1 '----' '--------*/ case 6: - c = value >> 7; - value = (value << 1) & 1; + cf = value >> 7; + value = (zuint8)((value << 1) | 1); break; - /* SRL .---------. .----. + /* srl .---------. .----. 0 -->| 7 --> 0 |-->| CF | '---------' '---*/ case 7: - c = value & CF; + cf = value & 1; value >>= 1; break; - /* Uncoment to Avoid a compiler warning */ - /*default: c = 0; break;*/ + /* Uncoment to avoid compiler warnings */ + /*default: cf = 0;*/ } - F = (zuint8)((value & SYXF) | ZF_ZERO(value) | PF_PARITY(value) | c); + FLAGS = (zuint8)( + (value & SYXF) | /* SF = sign; YF = Y; XF = X */ + ZF_ZERO(value) | /* ZF = zero */ + PF_PARITY(value) | /* PF = parity */ + cf); /* CF already computed */ + /* HF, NF = 0 */ return value; } -/* MARK: - Bit Set and Reset Operation Resolution and Execution +/* MARK: - Bit Set and Reset Operations */ - .----------. .---------. - | 76543210 | | M | - |----------| |---------| - | _m______ | | 0 = res | - '----------' | 1 = set | - '--------*/ +/*---------. .---------. +| 76543210 | | M | +|----------| |---------| +| _m______ | | 0 = res | +'----------' | 1 = set | + '--------*/ -static Z_INLINE zuint8 _m______(Z80 *object, zuint8 offset, zuint8 value) +static Z_INLINE zuint8 m(Z80 *self, zuint8 offset, zuint8 value) { - zuint8 t = object->data.array_uint8[offset]; + zuint8 t; + + Q_0 + t = DATA[offset]; return (zuint8)((t & 64) - ? value | (1 << ((t & 56) >> 3)) /* SET */ - : value & ~(1 << ((t & 56) >> 3))); /* RES */ + ? value | (1U << ((t >> 3) & 7)) + : value & ~(1U << ((t >> 3) & 7))); } -/* MARK: - Macros: Shortening */ +/* MARK: - Function Shortcuts and Reusable Code */ -#define N(x) ((BYTE##x & 56) >> 3) -#define X0 (*__xxx___0(object)) -#define X1 (*__xxx___1(object)) -#define Y0 (*_____yyy0(object)) -#define Y1 (*_____yyy1(object)) -#define Y3 (*_____yyy3(object)) -#define JP (*__jjj___ (object)) -#define KQ (*_____kkk (object)) -#define SS0 (*__ss____0(object)) -#define SS1 (*__ss____1(object)) -#define TT (*__tt____ (object)) -#define Z __zzz___ (object) -#define U0(value) __uuu___ (object, 0, value) -#define U1(value) __uuu___ (object, 1, value) -#define V0(value) _____vvv (object, 0, value) -#define V1(value) _____vvv (object, 1, value) -#define G1(value) __ggg___ (object, 1, value) -#define G3(value) __ggg___ (object, 3, value) -#define M1(value) _m______ (object, 1, value) -#define M3(value) _m______ (object, 3, value) -#define WW (*Z_BOP(zuint16 *, object, w_table[(BYTE1 >> 4) & 3])) +#define N(index) ((DATA[index] >> 3) & 7) +#define Z(mask) zzz(self, mask) +#define U0(value) uuu(self, 0, value) +#define U1(value) uuu(self, 1, value) +#define V0(value) vvv(self, 0, value) +#define V1(value) vvv(self, 1, value) +#define G1(value) ggg(self, 1, value) +#define G3(value) ggg(self, 3, value) +#define M1(value) m (self, 1, value) +#define M3(value) m (self, 3, value) +#define PUSH(value) WRITE_16B(SP -= 2, value) +#define R_ALL ((R & 127) | (R7 & 128)) +#define RET MEMPTR = PC = READ_16(SP); SP += 2 +#define FETCH_XY_EA(address) MEMPTR = (zuint16)(XY + (zsint8)FETCH(address)) +#define INSTRUCTION(name) static zuint8 name(Z80 *self) +#define IS_XY_PREFIX(opcode) ((opcode) & 0xDF) == 0xDD +#define EX(a, b) t = a; a = b; b = t -/* MARK: - Macros & Functions: Reusable Code */ - -#define INSTRUCTION(name) static zuint8 name(Z80 *object) -#define EXIT_HALT if (HALT) {PC++; HALT = FALSE; CLEAR_HALT;} -#define PUSH(value) WRITE_16(SP -= 2, value) +#define LD_A_IR(rhs) \ + A = rhs; \ + \ + FLAGS = (zuint8)( /* HF, NF = 0 */ \ + A_SYX | /* SF = sign; YF = Y; XF = X */ \ + ZF_ZERO(A) | /* ZF = zero */ \ + (IFF2 << 2) | /* PF = IFF2 */ \ + F_C); /* CF unchanged */ \ + \ + PC += 2; \ + return 9 -#define LD_A_I_LD_A_R \ - F = (zuint8) /* HF = 0 / NF = 0 */ \ - (A_SYX /* SF = A.7; YF = A.5; XF = A.3 */ \ - | ZF_ZERO(A) /* ZF = !A */ \ - | (IFF2 << 2) /* PF = IFF2 */ \ - | F_C); /* CF unchanged */ +#define LDX(operator) \ + zuint8 t = READ(HL operator); \ + \ + WRITE(DE operator, t); \ + t += A; \ + \ + FLAGS = (zuint8)( /* HF, NF = 0 */ \ + F_SZC | /* SF, ZF, CF unchanged */ \ + ((t & 2) << 4) | /* YF = (A + [HLi]).1 */ \ + (t & XF) | /* XF = (A + [HLi]).3 */ \ + (!!(--BC) << 2)); /* PF = !!BCo */ \ + \ + PC += 2; \ + return 16 -#define EX(a, b) t = a; a = b; b = t; - - -#define EX_VSP_X(register) \ - t = READ_16(SP); \ - WRITE_16(SP, register); \ - register = t; - - -#define LDX(operator) \ - zuint8 n; \ +#define LDXR(operator) \ + zuint8 t = READ(HL operator); \ + \ + WRITE(DE operator, t); \ + t += A; \ + \ + if (--BC) \ + { /* HF, NF = 0 */ \ + FLAGS = F_SZC | /* SF, ZF, CF unchanged */ \ + ((PC >> 8) & YXF) | /* YF = PCi.13; XF = PCi.11 */ \ + PF; /* PF = 1 */ \ + \ + MEMPTR = PC + 1; \ + return 21; \ + } \ + \ + FLAGS = (zuint8)( /* HF, PF, NF = 0 */ \ + F_SZC | /* SF, ZF, CF unchanged */ \ + ((t & 2) << 4) | /* YF = (A + [HLi]).1 */ \ + (t & XF)); /* XF = (A + [HLi]).3 */ \ \ PC += 2; \ - WRITE_8(DE operator, n = READ_8(HL operator)); \ - n += A; \ + return 16 + + +#define CPX(operator) \ + zuint8 n = READ(HL operator); \ + zuint8 t0 = A - n; \ + zuint8 hf = (A ^ n ^ t0) & HF; \ + zuint8 t1 = t0 - (hf >> 4); \ + \ + FLAGS = (zuint8)( \ + (t0 & SF) | /* SF = sign */ \ + ZF_ZERO(t0) | /* ZF = zero */ \ + hf | /* HF = half-borrow */ \ + ((t1 & 2) << 4) | /* YF = (A - [HLi] - HFo).1 */ \ + (t1 & XF) | /* XF = (A - [HLi] - HFo).3 */ \ + (!!(--BC) << 2) | /* PF = !!BCo */ \ + NF | /* NF = 1 */ \ + F_C); /* CF unchanged */ \ + \ + MEMPTR operator; \ + PC += 2; \ + return 16 + + +#define CPXR(operator) \ + zuint8 n = READ(HL operator); \ + zuint8 t0 = A - n; \ + zuint8 hf = (A ^ n ^ t0) & HF; \ + zuint8 t1 = t0 - (hf >> 4); \ + \ + zuint8 f = (zuint8)( \ + (t0 & SF) | /* SF = sign */ \ + ZF_ZERO(t0) | /* ZF = zero */ \ + hf | /* HF = half-borrow */ \ + (!!(--BC) << 2) | /* PF = !!BCo */ \ + NF | /* NF = 1 */ \ + F_C); /* CF unchanged */ \ + \ + if (t0 && BC) \ + { \ + /* YF = PCi.13; XF = PCi.11 */ \ + FLAGS = f | ((PC >> 8) & YXF); \ + MEMPTR = PC + 1; \ + return 21; \ + } \ + \ + FLAGS = (zuint8)( \ + f | \ + ((t1 & 2) << 4) | /* YF = (A - [HLi] - HFo).1 */ \ + (t1 & XF)); /* XF = (A - [HLi] - HFo).3 */ \ + \ + MEMPTR operator; \ + PC += 2; \ + return 16 + + +#define ADD_RR_NN(RR, NN) \ + zuint16 nn = NN; \ + zuint16 t = RR + nn; \ + \ + FLAGS = F_SZP | /* SF, ZF, PF unchanged */ \ + ((t >> 8) & YXF) | /* YF = high-Y; XF = high-X */ \ + (((zuint16)(RR ^ nn ^ t) >> 8) & HF) | /* HF = high-half-carry */ \ + ((zuint32)RR + nn > 65535); /* CF = carry */ \ + /* NF = 0 */ \ + MEMPTR = RR + 1; \ + RR = t + + +#define ADC_SBC_HL_SS(operator, pf_test_rhs, cf_test, set_nf) \ + zuint8 fc = F_C; \ + zuint16 ss = SS1; \ + zuint16 t = HL operator ss operator fc; \ + \ + FLAGS = (zuint8)( \ + ((t >> 8) & SYXF) /* SF = sign; YF = high-Y; XF = high-X */ \ + | ZF_ZERO(t) /* ZF = zero */ \ + /* HF = high-half-carry (adc), high-half-borrow (sbc) */ \ + | (((zuint16)(HL ^ ss ^ t) >> 8) & HF) \ + | PF_OVERFLOW(16, t, HL, pf_test_rhs) /* PF = overflow */ \ + | ((zuint32)cf_test) /* CF = carry (adc), borrow (sbc) */ \ + set_nf); /* NF = 0 (adc), 1 (sbc) */ \ + \ + MEMPTR = HL + 1; \ + HL = t; \ + PC += 2; \ + return 15 + + +#define RXA(a_to_cf, operator, fc_to_a) \ + zuint8 cf = a_to_cf; \ + \ + A = (zuint8)((A operator 1) | fc_to_a); \ + \ + FLAGS = F_SZP | /* SF, ZF, PF unchanged */ \ + A_YX | /* YF = Y; XF = X */ \ + cf; /* CF = Ai.7 (rla), Ai.0 (rra) */ \ + /* HF, NF = 0 */ \ + PC++; \ + return 4 + + +#define RXD(vhl_to_vhl, a_to_vhl, vhl_to_a) \ + zuint8 t = READ(HL); \ + \ + MEMPTR = HL + 1; \ + WRITE(HL, (zuint8)((t vhl_to_vhl) | (A a_to_vhl))); \ + A = (A & 0xF0) | (t vhl_to_a); \ + \ + FLAGS = (zuint8)( /* HF, NF = 0; */ \ + A_SYX | /* SF = sign; YF = Y; XF = X */ \ + ZF_ZERO(A) | /* ZF = zero */ \ + PF_PARITY(A) | /* PF = parity */ \ + F_C); /* CF unchanged */ \ + \ + PC += 2; \ + return 18 + + +#define DJNZ_JR_Z_OFFSET(condition, cycles_if_true, cycles_if_false) \ + zsint8 offset; \ + \ + Q_0 \ + offset = (zsint8)FETCH(PC + 1); /* always */ \ + \ + if (condition) \ + { \ + MEMPTR = (PC += 2 + offset); \ + return cycles_if_true; \ + } \ + \ + PC += 2; \ + return cycles_if_false + + +#define RETX(mnemonic) \ + NOTIFY(mnemonic); \ + Q_0 \ + DATA[2] = IFF1; \ + if ((IFF1 = IFF2) && INT_LINE) REQUEST |= Z80_REQUEST_INT; \ + RET; \ + return 14 + + +#define IN_VC \ + zuint8 t; \ + \ + MEMPTR = BC + 1; \ + t = IN(BC); \ + \ + FLAGS = (zuint8)( /* HF, NF = 0 */ \ + (t & SYXF) | /* SF = sign; YF = Y; XF = X */ \ + ZF_ZERO(t) | /* ZF = zero */ \ + PF_PARITY(t) | /* PF = parity */ \ + F_C) /* CF unchanged */ + + +#define INX_OUTX(io) \ + FLAGS = (zuint8)( \ + (B & SYXF) | /* SF = Bo.7; YF = Bo.5; XF = Bo.3 */ \ + ZF_ZERO(B) | /* ZF = !Bo */ \ + PF_PARITY((t & 7) ^ B) | /* PF = ((T & 7) ^ Bo).parity */ \ + ((t > 255) ? HCF : 0) | /* HF, CF = T > 255 */ \ + ((io >> 6) & NF)); /* NF = IO.7 */ \ + \ + PC += 2; \ + return 16 + + +#define INX(hl_operator, memptr_operator) \ + zuint8 in = IN(BC); \ + zuint t = (zuint)in + (zuint8)(C memptr_operator 1); \ + \ + WRITE(HL hl_operator, in); \ + MEMPTR = BC memptr_operator 1; \ + B--; \ + INX_OUTX(in) \ + + +#define OUTX(hl_operator, memptr_operator) \ + zuint8 out = READ(HL hl_operator); \ + zuint t = (zuint)out + L; \ + \ + B--; \ + MEMPTR = BC memptr_operator 1; \ + OUT(BC, out); \ + INX_OUTX(out) + + +#define INXR_OTXR(io) \ + if (B) { \ + FLAGS = (zuint8)( /* ZF = 0 */ \ + (B & SF) | /* SF = Bo.7 */ \ + ((PC >> 8) & YXF) | /* YF = PCi.13; XF = PCi.11 */ \ + nf | /* NF = IO.7 */ \ + (hcf ? \ + CF | \ + (nf ? \ + (!(B & 0xF) << 4) | \ + PF_PARITY(p ^ ((B - 1) & 7)) \ + : \ + (((B & 0xF) == 0xF) << 4) | \ + PF_PARITY(p ^ ((B + 1) & 7))) \ + : PF_PARITY(p ^ (B & 7)))); \ \ - F = (zuint8) /* HF = 0, NF = 0 */ \ - ((F & (SF | ZF | CF)) /* SF, ZF, CF unchanged */ \ - | ((n & 2) << 4) /* YF = ([HL] + A).1 */ \ - | (n & XF) /* XF = ([HL] + A).3 */ \ - | (!!(--BC) << 2)); /* PF = 1 if BC != 0, else PF = 0 */ + return 21; \ + } \ + \ + FLAGS = ZF | /* ZF = 1; SF, YF, XF = 0 */ \ + hcf | /* HF, CF = T > 255 */ \ + PF_PARITY(p) | /* PF = ((T & 7) ^ Bo).parity */ \ + nf; /* NF = IO.7 */ \ + \ + PC += 2; \ + return 16 -#define LDXR(operator) \ - LDX(operator) \ - if (!BC) return 16; \ - PC -= 2; return 21; +#define INXR(hl_operator, memptr_operator) \ + zuint8 in = IN(BC); \ + zuint8 nf = (in >> 6) & NF; \ + zuint t = (zuint)in + (zuint8)(C memptr_operator 1); \ + zuint8 hcf = (t > 255) ? HCF : 0; \ + zuint8 p; \ + \ + WRITE(HL hl_operator, in); \ + MEMPTR = BC memptr_operator 1; \ + p = (t & 7) ^ --B; \ + INXR_OTXR(in) -#define CPX(operator) \ - zuint8 v, n0, n1; \ - \ - PC += 2; \ - n1 = (n0 = A - (v = READ_8(HL operator))) - !!F_H; \ - \ - F = (zuint8) \ - ((n0 & SF) /* SF = (A - [HL]).7 */ \ - | ZF_ZERO(n0) /* ZF = !(A - [HL]) */ \ - | ((A ^ v ^ n0) & HF) /* HF = borrow from bit 5 */ \ - | ((n1 & 2) << 4) /* YF = (A - [HL] - HF).1 */ \ - | (n1 & XF) /* XF = (A - [HL] - HF).3 */ \ - | (!!(--BC) << 2) /* PF = !!BC */ \ - | NF /* NF = 1 */ \ - | F_C); /* CF unchanged */ +#define OTXR(hl_operator, memptr_operator) \ + zuint8 out = READ(HL hl_operator); \ + zuint8 nf = (out & 128) >> 6; \ + zuint t = (zuint)out + L; \ + zuint8 hcf = (t > 255) ? HCF : 0; \ + zuint8 p = (t & 7) ^ --B; \ + \ + MEMPTR = BC memptr_operator 1; \ + OUT(BC, out); \ + INXR_OTXR(out) -#define CPXR(operator) \ - CPX(operator) \ - if (!BC || !n0) return 16; \ - PC -= 2; return 21; +#define EXIT_HALT_STATE \ + HALT_LINE = FALSE; \ + if (self->halt != Z_NULL) self->halt(CONTEXT, FALSE) -static Z_INLINE void add_RR_NN(Z80 *object, zuint16 *r, zuint16 v) +/* MARK: - Instructions: 8-Bit Load Group */ +/*----------------------------------------------------------------------------. +| 0 1 2 3 Flags T-states | +| Assembly 76543210765432107654321076543210 SZYHXPNC 12345 | +| ------------------- -------------------------------- -------- -------- | +| ld J,K 01jjjkkk ........ 4:4 | +|* ld O,P <--XY-->01oooppp ........ 8:44 | +| ld J,BYTE 00jjj110<-BYTE-> ........ 7:43 | +|* ld O,BYTE <--XY-->00ooo110<-BYTE-> ........ 11:443 | +| ld J,(hl) 01jjj110 ........ 7:43 | +| ld J,(XY+OFFSET) <--XY-->01jjj110 ........ 19:44353 | +| ld (hl),K 01110kkk ........ 7:43 | +| ld (XY+OFFSET),K <--XY-->01110kkk ........ 19:44353 | +| ld (hl),BYTE <--36--><-BYTE-> ........ 10:433 | +| ld (XY+OFFSET),BYTE <--XY--><--36--><-BYTE-> ........ 19:44353 | +| ld a,(bc) <--0A--> ........ 7:43 | +| ld a,(de) <--1A--> ........ 7:43 | +| ld a,(WORD) <--3A--><-----WORD-----> ........ 13:4333 | +| ld (bc),a <--02--> ........ 7:43 | +| ld (de),a <--12--> ........ 7:43 | +| ld (WORD),a <--32--><-----WORD-----> ........ 13:4333 | +| ld a,i <--ED--><--57--> szy0x*0. 9:45 | +| ld a,r <--ED--><--5F--> szy0x*0. 9:45 | +| ld i,a <--ED--><--47--> ........ 9:45 | +| ld r,a <--ED--><--4F--> ........ 9:45 | +|-----------------------------------------------------------------------------| +| (*) Undocumented instruction. | +'============================================================================*/ + +INSTRUCTION(ld_J_K ) {Q_0 J0 = K0; PC++; return 4;} +INSTRUCTION(ld_O_P ) {Q_0 O = P; PC += 2; return 8;} +INSTRUCTION(ld_J_BYTE ) {Q_0 J0 = FETCH((PC += 2) - 1); return 7;} +INSTRUCTION(ld_O_BYTE ) {Q_0 O = FETCH((PC += 3) - 1); return 11;} +INSTRUCTION(ld_J_vhl ) {Q_0 J0 = READ(HL); PC++; return 7;} +INSTRUCTION(ld_J_vXYpOFFSET) {Q_0 J1 = READ(FETCH_XY_EA((PC += 3) - 1)); return 19;} +INSTRUCTION(ld_vhl_K ) {Q_0 WRITE(HL, K0); PC++; return 7;} +INSTRUCTION(ld_vXYpOFFSET_K) {Q_0 WRITE(FETCH_XY_EA((PC += 3) - 1), K1); return 19;} +INSTRUCTION(ld_vhl_BYTE ) {Q_0 WRITE(HL, FETCH((PC += 2) - 1)); return 10;} +INSTRUCTION(ld_a_vbc ) {Q_0 MEMPTR = BC + 1; A = READ(BC); PC++; return 7;} +INSTRUCTION(ld_a_vde ) {Q_0 MEMPTR = DE + 1; A = READ(DE); PC++; return 7;} +INSTRUCTION(ld_a_vWORD ) {Q_0 MEMPTR = FETCH_16((PC += 3) - 2); A = READ(MEMPTR++); return 13;} +INSTRUCTION(ld_vbc_a ) {Q_0 MEMPTRL = C + 1; WRITE(BC, MEMPTRH = A); PC++; return 7;} +INSTRUCTION(ld_vde_a ) {Q_0 MEMPTRL = E + 1; WRITE(DE, MEMPTRH = A); PC++; return 7;} +INSTRUCTION(ld_a_i ) {LD_A_IR(I); } +INSTRUCTION(ld_a_r ) {LD_A_IR(R_ALL); } +INSTRUCTION(ld_i_a ) {NOTIFY(ld_i_a); Q_0 I = A; PC += 2; return 9;} +INSTRUCTION(ld_r_a ) {NOTIFY(ld_r_a); Q_0 R = R7 = A; PC += 2; return 9;} + + +INSTRUCTION(ld_vXYpOFFSET_BYTE) { - zuint16 t = *r + v; + zuint16 ea; - F = F_SZP /* SF, ZF, PF unchanged */ - | ((t >> 8) & YXF) /* YF = RR.13; XF = RR.11 */ - | (((*r ^ v ^ t) >> 8) & HF) /* HF = RRh half-carry */ - | ((zuint32)*r + v > 65535); /* CF = Carry */ - *r = t; /* NF = 0 */ + Q_0 + ea = FETCH_XY_EA((PC += 4) - 2); + WRITE(ea, FETCH(PC - 1)); + return 19; } -#define ADD_RR_NN(register, value) \ - add_RR_NN(object, (zuint16 *)®ister, value); +INSTRUCTION(ld_vWORD_a) + { + zuint16 ea; + + Q_0 + MEMPTRL = (zuint8)((ea = FETCH_16((PC += 3) - 2)) + 1); + WRITE(ea, MEMPTRH = A); + return 13; + } -#define ADC_SBC_HL_SS(function, sign, cf_test, set_nf) \ - zuint8 c = F_C; \ - zuint16 v = SS1, t = HL sign v sign c; \ - \ - F = (zuint8) \ - (((t >> 8) & SYXF) /* SF = HL.15; YF = HL.13; XF = HL.11 */ \ - | ZF_ZERO(t) /* ZF = !HL */ \ - | ((((HL & 0xFFF) sign (v & 0xFFF) sign c) >> 8) & HF) /* HF = Half-carry of H */ \ - | pf_overflow_##function##16(HL, v, c) /* PF = Overflow */ \ - | !!(cf_test) /* CF = Carry */ \ - set_nf); /* ADC: NF = 0; SBC: NF = 1 */ \ - \ - HL = t; \ - PC += 2; \ - return 15; +/* MARK: - Instructions: 16-Bit Load Group */ +/*----------------------------------------------------------------------. +| 0 1 2 3 Flags T-states | +| Assembly 76543210765432107654321076543210 SZYHXPNC 123456 | +| ------------ -------------------------------- -------- --------- | +| ld SS,WORD 00ss0001<-----WORD-----> ........ 10:433 | +| ld XY,WORD <--XY--><--21--><-----WORD-----> ........ 14:4433 | +| ld hl,(WORD) <--2A--><-----WORD-----> ........ 16:43333 | +| ld SS,(WORD) <--ED-->01ss1011<-----WORD-----> ........ 20:443333 | +| ld XY,(WORD) <--XY--><--2A--><-----WORD-----> ........ 20:443333 | +| ld (WORD),hl <--22--><-----WORD-----> ........ 16:43333 | +| ld (WORD),SS <--ED-->01ss0011<-----WORD-----> ........ 20:443333 | +| ld (WORD),XY <--XY--><--22--><-----WORD-----> ........ 20:443333 | +| ld sp,hl <--F9--> ........ 6:6 | +| ld sp,XY <--XY--><--F9--> ........ 10:46 | +| push TT 11tt0101 ........ 11:533 | +| push XY <--XY--><--E5--> ........ 15:4533 | +| pop TT 11tt0001 ........ 10:433 | +| pop XY <--XY--><--E1--> ........ 14:4433 | +'======================================================================*/ + +INSTRUCTION(ld_SS_WORD ) {Q_0 SS0 = FETCH_16((PC += 3) - 2); return 10;} +INSTRUCTION(ld_XY_WORD ) {Q_0 XY = FETCH_16((PC += 4) - 2); return 14;} +INSTRUCTION(ld_hl_vWORD) {Q_0 MEMPTR = FETCH_16((PC += 3) - 2); HL = READ_16(MEMPTR++); return 16;} +INSTRUCTION(ld_SS_vWORD) {Q_0 MEMPTR = FETCH_16((PC += 4) - 2); SS1 = READ_16(MEMPTR++); return 20;} +INSTRUCTION(ld_XY_vWORD) {Q_0 MEMPTR = FETCH_16((PC += 4) - 2); XY = READ_16(MEMPTR++); return 20;} +INSTRUCTION(ld_vWORD_hl) {Q_0 MEMPTR = FETCH_16((PC += 3) - 2); WRITE_16F(MEMPTR++, HL ); return 16;} +INSTRUCTION(ld_vWORD_SS) {Q_0 MEMPTR = FETCH_16((PC += 4) - 2); WRITE_16F(MEMPTR++, SS1); return 20;} +INSTRUCTION(ld_vWORD_XY) {Q_0 MEMPTR = FETCH_16((PC += 4) - 2); WRITE_16F(MEMPTR++, XY ); return 20;} +INSTRUCTION(ld_sp_hl ) {Q_0 SP = HL; PC++; return 6;} +INSTRUCTION(ld_sp_XY ) {Q_0 SP = XY; PC += 2; return 10;} +INSTRUCTION(push_TT ) {Q_0 WRITE_16B(SP -= 2, TT); PC++; return 11;} +INSTRUCTION(push_XY ) {Q_0 WRITE_16B(SP -= 2, XY); PC += 2; return 15;} +INSTRUCTION(pop_TT ) {Q_0 TT = READ_16(SP); SP += 2; PC++; return 10;} +INSTRUCTION(pop_XY ) {Q_0 XY = READ_16(SP); SP += 2; PC += 2; return 14;} -#define RXA \ - F = F_SZP /* SF, ZF, PF unchanged */ \ - | A_YX /* YF = A.5; XF = A.3 */ \ - | c; /* CF = Ai.7 (rla) / Ai.0 (rra) */ +/* MARK: - Instructions: Exchange, Block Transfer and Search Groups */ +/*-------------------------------------------------------------. +| 0 1 Flags T-states | +| Assembly 7654321076543210 SZYHXPNC !0 123456 =0 1234 | +| ---------- ---------------- -------- ------------------ | +| ex de,hl <--EB--> ........ 4:4 | +| ex af,af' <--08--> ........ 4:4 | +| exx <--D9--> ........ 4:4 | +| ex (sp),hl <--E3--> ........ 19:43435 | +| ex (sp),XY <--XY--><--E3--> ........ 23:443435 | +| ldi <--ED--><--A0--> ..*0**0. 16:4435 | +| ldir <--ED--><--B0--> ..*0*00. 21:44355 16:4435 | +| ldd <--ED--><--A8--> ..*0**0. 16:4435 | +| lddr <--ED--><--B8--> ..*0*00. 21:44355 16:4435 | +| cpi <--ED--><--A1--> sz*b**1. 16:4435 | +| cpir <--ED--><--B1--> sz*b**1. 21:44355 16:4435 | +| cpd <--ED--><--A9--> sz*b**1. 16:4435 | +| cpdr <--ED--><--B9--> sz*b**1. 21:44355 16:4435 | +'=============================================================*/ + +INSTRUCTION(ex_de_hl ) {zuint16 t; Q_0 EX(DE, HL ); PC++; return 4;} +INSTRUCTION(ex_af_af_) {zuint16 t; Q_0 EX(AF, AF_); PC++; return 4;} +INSTRUCTION(exx ) {zuint16 t; Q_0 EX(BC, BC_); EX(DE, DE_); EX(HL, HL_); PC++; return 4;} +INSTRUCTION(ex_vsp_hl) {Q_0 MEMPTR = READ_16(SP); WRITE_16B(SP, HL); HL = MEMPTR; PC++; return 19;} +INSTRUCTION(ex_vsp_XY) {Q_0 MEMPTR = READ_16(SP); WRITE_16B(SP, XY); XY = MEMPTR; PC += 2; return 23;} +INSTRUCTION(ldi ) {LDX (++); } +INSTRUCTION(ldir ) {LDXR(++); } +INSTRUCTION(ldd ) {LDX (--); } +INSTRUCTION(lddr ) {LDXR(--); } +INSTRUCTION(cpi ) {CPX (++); } +INSTRUCTION(cpir ) {CPXR(++); } +INSTRUCTION(cpd ) {CPX (--); } +INSTRUCTION(cpdr ) {CPXR(--); } -#define RXD(a, b, c) \ - zuint8 t; \ - \ - PC += 2; \ - WRITE_8(HL, ((t = READ_8(HL)) a 4) | (A b)); \ - A = (A & 0xF0) | (t c); \ - \ - F = (zuint8) \ - (A_SYX /* SF = A.7; YF = A.5; XF = A.3 */ \ - | ZF_ZERO(A) /* ZF = !A */ \ - | PF_PARITY(A) /* PF = Parity of A */ \ - | F_C); /* CF unchanged */ \ - /* HF = 0, NF = 0; */ +/* MARK: - Instructions: 8-Bit Arithmetic and Logical Group */ +/*-------------------------------------------------------------------. +| 0 1 2 Flags T-states | +| Assembly 765432107654321076543210 SZYHXPNC 123456 | +| ----------------- ------------------------ -------- --------- | +| U [a,]K 10uuukkk sz|||||| 4:4 | +|* U [a,]P <--XY-->10uuuppp sz|||||| 8:44 | +| U [a,]BYTE 11uuu110<-BYTE-> sz|||||| 7:43 | +| U [a,](hl) 10uuu110 sz|||||| 7:43 | +| U [a,](XY+OFFSET) <--XY-->10uuu110 sz|||||| 19:44353 | +| V J 00jjj10v szy|xv|. 4:4 | +|* V O <--XY-->00ooo10v szy|xv|. 8:44 | +| V (hl) 0011010v szy|xv|. 11:443 | +| V (XY+OFFSET) <--XY-->0011010v szy|xv|. 23:443543 | +|--------------------------------------------------------------------| +| (*) Undocumented instruction. | +| (|) The flag is explained in a previous table. | +'===================================================================*/ + +INSTRUCTION(U_a_K ) {U0(K0); PC++; return 4;} +INSTRUCTION(U_a_P ) {U1(P); PC += 2; return 8;} +INSTRUCTION(U_a_BYTE ) {U0(FETCH((PC += 2) - 1)); return 7;} +INSTRUCTION(U_a_vhl ) {U0(READ(HL)); PC++; return 7;} +INSTRUCTION(U_a_vXYpOFFSET) {U1(READ(FETCH_XY_EA((PC += 3) - 1))); return 19;} +INSTRUCTION(V_J ) {zuint8 *j = &J0; *j = V0(*j); PC++; return 4;} +INSTRUCTION(V_O ) {zuint8 *o = &O; *o = V1(*o); PC += 2; return 8;} +INSTRUCTION(V_vhl ) {WRITE(HL, V0(READ(HL))); PC++; return 11;} +INSTRUCTION(V_vXYpOFFSET ) {zuint16 ea = FETCH_XY_EA((PC += 3) - 1); WRITE(ea, V1(READ(ea))); return 23;} -#define BIT_N_VALUE(value) \ - zuint8 n = value & (1 << N(1)); /* SF = value.N && N == 7 */ \ - /* ZF, PF = !value.N */ \ - F = (n ? n & SYXF : ZPF) /* YF = value.N && N == 5 */ \ - | HF /* HF = 1; NF = 0; CF unchanged */ \ - | F_C; /* XF = value.N && N == 3 */ +/* MARK: - Instructions: General-Purpose Arithmetic and CPU Control Groups */ +/*-------------------------------------------------. +| 0 1 Flags T-states | +| Assembly 7654321076543210 SZYHXPNC 12 | +| -------- ---------------- -------- -------- | +| daa <--27--> szy^xp.* 4:4 | +| cpl <--2F--> ..y1x.1. 4:4 | +|+ neg <--ED-->01***100 szybxv1b 8:44 | +| ccf <--3F--> ..***.0~ 4:4 | +| scf <--37--> ..*0*.01 4:4 | +| nop <--00--> ........ 4:4 | +| halt <--76--> ........ 4:4 | +| di <--F3--> ........ 4:4 | +| ei <--FB--> ........ 4:4 | +|+ im 0 <--ED-->01*0*110 ........ 8:44 | +|+ im 1 <--ED-->01*10110 ........ 8:44 | +|+ im 2 <--ED-->01*11110 ........ 8:44 | +|--------------------------------------------------| +| (+) The instruction has undocumented opcodes. | +'=================================================*/ - -#define BIT_N_VADDRESS(address) \ - Z16Bit a; \ - zuint8 n = READ_8(a.value_uint16 = address) & (1 << N(3)); \ - \ - F = (n ? (n & SF) : ZPF) \ - | (a.values_uint8.index1 & YXF) \ - | HF \ - | F_C; - - -#define IN_VC \ - zuint8 t; \ - \ - PC += 2; \ - t = IN(BC); \ - \ - F = (zuint8) \ - ((t & SYXF) \ - | ZF_ZERO(t) \ - | PF_PARITY(t) \ - | F_C); - - -#define INX(hl_operator, c_operator) \ - zuint8 v; \ - zuint t; \ - \ - PC += 2; \ - t = (zuint)(v = IN(BC)) + ((C c_operator 1) & 255); \ - WRITE_8(HL, v); \ - HL hl_operator; \ - B--; \ - \ - F = (zuint8) \ - ((B & SYXF) /* SF = (B - 1).7; YF = (B - 1).5; XF = (B - 1).3 */ \ - | ZF_ZERO(B) /* ZF = !(B - 1) */ \ - | PF_PARITY((t & 7) ^ B) /* PF = Parity of (([HL] + ((C +/- 1) & 255)) and 7) xor B */ \ - | (v & 128)); /* NF = IN(BC).7 */ \ - /* if (([HL] + ((C +/- 1) & 255)) > 255) HF = 1; else HF = 0 */ \ - if (t > 255) F |= HCF; /* if (([HL] + ((C +/- 1) & 255)) > 255) HF = 1; else HF = 0 */ - - -#define INXR(hl_operator, c_operator) \ - INX(hl_operator, c_operator); \ - if (!B) return 16; \ - PC -= 2; return 21; - - -#define OUTX(operator) \ - zuint8 t; \ - \ - PC += 2; \ - OUT(BC, t = READ_8(HL)); \ - HL operator; \ - B--; \ - \ - F = (zuint8) \ - ((B & SYXF) /* SF = (B - 1).7; YF = (B - 1).5; XF = (B - 1).3 */ \ - | ZF_ZERO(B) /* ZF = !(B - 1) */ \ - | PF_PARITY(((L + t) & 7) ^ B) /* PF = Parity of ((L + [HL]) and 7) xor B */ \ - | (t & 128)); /* NF = IN(BC).7 */ \ - /* if (L + [HL] > 255) HF = 1; else HF = 0 */ \ - if ((zuint)t + L > 255) F |= HCF; /* if (L + [HL] > 255) CF = 1; else CF = 0 */ - - -#define OTXR(operator) \ - OUTX(operator); \ - if (!B) return 16; \ - PC -= 2; return 21; - - -#define RET PC = READ_16(SP); SP += 2; - - -/* MARK: - Instructions: 8-Bit Load Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| ld X,Y 01xxxyyy ........ 1 / 4 | -| ld J,K < DD >01jjjkkk ........ 2 / 8 | -| ld P,Q < FD >01pppqqq ........ 2 / 8 | -| ld X,BYTE 00xxx110< BYTE > ........ 2 / 7 | -| ld J,BYTE < DD >00jjj110< BYTE > ........ 3 / 11 | -| ld P,BYTE < FD >00ppp110< BYTE > ........ 3 / 11 | -| ld X,(hl) 01xxx110 ........ 2 / 7 | -| ld X,(ix+OFFSET) < DD >01xxx110 ........ 5 / 19 | -| ld X,(iy+OFFSET) < FD >01xxx110 ........ 5 / 19 | -| ld (hl),Y 01110yyy ........ 2 / 7 | -| ld (ix+OFFSET),Y < DD >01110yyy ........ 5 / 19 | -| ld (iy+OFFSET),Y < FD >01110yyy ........ 5 / 19 | -| ld (hl),BYTE < 36 >< BYTE > ........ 3 / 10 | -| ld (ix+OFFSET),BYTE < DD >< 36 >< BYTE > ........ 5 / 19 | -| ld (iy+OFFSET),BYTE < FD >< 36 >< BYTE > ........ 5 / 19 | -| ld a,(bc) < 0A > ........ 2 / 7 | -| ld a,(de) < 1A > ........ 2 / 7 | -| ld a,(WORD) < 3A >< WORD > ........ 4 / 13 | -| ld (bc),a < 02 > ........ 2 / 7 | -| ld (de),a < 12 > ........ 2 / 7 | -| ld (WORD),a < 32 >< WORD > ........ 4 / 13 | -| ld a,i < ED >< 57 > szy0xi0. 2 / 9 | -| ld a,r < ED >< 5F > szy0xi0. 2 / 9 | -| ld i,a < ED >< 47 > ........ 2 / 9 | -| ld r,a < ED >< 4F > ........ 2 / 9 | -'--------------------------------------------------------------------------*/ - -INSTRUCTION(ld_X_Y) {PC++; X0 = Y0; return 4;} -INSTRUCTION(ld_JP_KQ) {PC += 2; JP = KQ; return 8;} -INSTRUCTION(ld_X_BYTE) {X0 = READ_8((PC += 2) - 1); return 7;} -INSTRUCTION(ld_JP_BYTE) {JP = READ_8((PC += 3) - 1); return 11;} -INSTRUCTION(ld_X_vhl) {PC++; X0 = READ_8(HL); return 7;} -INSTRUCTION(ld_X_vXYOFFSET) {X1 = READ_8(XY + READ_OFFSET((PC += 3) - 1)); return 19;} -INSTRUCTION(ld_vhl_Y) {PC++; WRITE_8(HL, Y0); return 7;} -INSTRUCTION(ld_vXYOFFSET_Y) {WRITE_8(XY + READ_OFFSET((PC += 3) - 1), Y1); return 19;} -INSTRUCTION(ld_vhl_BYTE) {WRITE_8(HL, READ_8((PC += 2) - 1)); return 10;} -INSTRUCTION(ld_vXYOFFSET_BYTE) {PC += 4; WRITE_8(XY + READ_OFFSET(PC - 2), READ_8(PC - 1)); return 19;} -INSTRUCTION(ld_a_vbc) {PC++; A = READ_8(BC); return 7;} -INSTRUCTION(ld_a_vde) {PC++; A = READ_8(DE); return 7;} -INSTRUCTION(ld_a_vWORD) {A = READ_8(READ_16((PC += 3) - 2)); return 13;} -INSTRUCTION(ld_vbc_a) {PC++; WRITE_8(BC, A); return 7;} -INSTRUCTION(ld_vde_a) {PC++; WRITE_8(DE, A); return 7;} -INSTRUCTION(ld_vWORD_a) {WRITE_8(READ_16((PC += 3) - 2), A); return 13;} -INSTRUCTION(ld_a_i) {PC += 2; A = I; LD_A_I_LD_A_R; return 9;} -INSTRUCTION(ld_a_r) {PC += 2; A = R_ALL; LD_A_I_LD_A_R; return 9;} -INSTRUCTION(ld_i_a) {PC += 2; I = A; return 9;} -INSTRUCTION(ld_r_a) {PC += 2; R = R7 = A; return 9;} - - -/* MARK: - Instructions: 16-Bit Load Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| ld SS,WORD 00ss0001< WORD > ........ 3 / 10 | -| ld ix,WORD < DD >< 21 >< WORD > ........ 4 / 14 | -| ld iy,WORD < FD >< 21 >< WORD > ........ 4 / 14 | -| ld hl,(WORD) < 2A >< WORD > ........ 5 / 16 | -| ld SS,(WORD) < ED >01ss1011< WORD > ........ 6 / 20 | -| ld ix,(WORD) < DD >< 2A >< WORD > ........ 6 / 20 | -| ld iy,(WORD) < FD >< 2A >< WORD > ........ 6 / 20 | -| ld (WORD),hl < 22 >< WORD > ........ 5 / 16 | -| ld (WORD),SS < ED >01ss0011< WORD > ........ 6 / 20 | -| ld (WORD),ix < DD >< 22 >< WORD > ........ 6 / 20 | -| ld (WORD),iy < FD >< 22 >< WORD > ........ 6 / 20 | -| ld sp,hl < F9 > ........ 1 / 6 | -| ld sp,ix < DD >< F9 > ........ 2 / 10 | -| ld sp,iy < FD >< F9 > ........ 2 / 10 | -| push TT 11tt0101 ........ 3 / 11 | -| push ix < DD >< E5 > ........ 4 / 15 | -| push iy < FD >< E5 > ........ 4 / 15 | -| pop TT 11tt0001 ........ 3 / 10 | -| pop ix < DD >< E1 > ........ 4 / 14 | -| pop iy < FD >< E1 > ........ 4 / 14 | -'--------------------------------------------------------------------------*/ - -INSTRUCTION(ld_SS_WORD) {SS0 = READ_16((PC += 3) - 2); return 10;} -INSTRUCTION(ld_XY_WORD) {XY = READ_16((PC += 4) - 2); return 14;} -INSTRUCTION(ld_hl_vWORD) {HL = READ_16(READ_16((PC += 3) - 2)); return 16;} -INSTRUCTION(ld_SS_vWORD) {SS1 = READ_16(READ_16((PC += 4) - 2)); return 20;} -INSTRUCTION(ld_XY_vWORD) {XY = READ_16(READ_16((PC += 4) - 2)); return 20;} -INSTRUCTION(ld_vWORD_hl) {WRITE_16(READ_16((PC += 3) - 2), HL); return 16;} -INSTRUCTION(ld_vWORD_SS) {WRITE_16(READ_16((PC += 4) - 2), SS1); return 20;} -INSTRUCTION(ld_vWORD_XY) {WRITE_16(READ_16((PC += 4) - 2), XY); return 20;} -INSTRUCTION(ld_sp_hl) {PC++; SP = HL; return 6;} -INSTRUCTION(ld_sp_XY) {PC += 2; SP = XY; return 10;} -INSTRUCTION(push_TT) {PC++; WRITE_16(SP -= 2, TT); return 11;} -INSTRUCTION(push_XY) {PC += 2; WRITE_16(SP -= 2, XY); return 15;} -INSTRUCTION(pop_TT) {PC++; TT = READ_16(SP); SP += 2; return 10;} -INSTRUCTION(pop_XY) {PC += 2; XY = READ_16(SP); SP += 2; return 14;} - - -/* MARK: - Instructions: Exchange, Block Transfer and Search Groups -.--------------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ---------------------------------------------------------------------------- | -| ex de,hl < EB > ........ 1 / 4 | -| ex af,af' < 08 > ........ 1 / 4 | -| exx < D9 > ........ 1 / 4 | -| ex (sp),hl < E3 > ........ 5 / 19 | -| ex (sp),ix < DD >< E3 > ........ 6 / 23 | -| ex (sp),iy < FD >< E3 > ........ 6 / 23 | -| ldi < ED >< A0 > ..*0**0. 4 / 16 | -| ldir < ED >< B0 > ..*0*00. 5,4 / 21,16 | -| ldd < ED >< A8 > ..*0**0. 4 / 16 | -| lddr < ED >< B8 > ..*0*00. 5,4 / 21,16 | -| cpi < ED >< A1 > ******1. 4 / 16 | -| cpir < ED >< B1 > ******1. 5,4 / 21,16 | -| cpd < ED >< A9 > ******1. 4 / 16 | -| cpdr < ED >< B9 > ******1. 5,4 / 21,16 | -'-------------------------------------------------------------------------------*/ - -INSTRUCTION(ex_de_hl) {zuint16 t; PC++; EX(DE, HL) return 4;} -INSTRUCTION(ex_af_af_) {zuint16 t; PC++; EX(AF, AF_) return 4;} -INSTRUCTION(exx) {zuint16 t; PC++; EX(BC, BC_) EX(DE, DE_) EX(HL, HL_) return 4;} -INSTRUCTION(ex_vsp_hl) {zuint16 t; PC++; EX_VSP_X(HL) return 19;} -INSTRUCTION(ex_vsp_XY) {zuint16 t; PC += 2; EX_VSP_X(XY) return 23;} -INSTRUCTION(ldi) {LDX (++) return 16;} -INSTRUCTION(ldir) {LDXR(++) } -INSTRUCTION(ldd) {LDX (--) return 16;} -INSTRUCTION(lddr) {LDXR(--) } -INSTRUCTION(cpi) {CPX (++) return 16;} -INSTRUCTION(cpir) {CPXR(++) } -INSTRUCTION(cpd) {CPX (--) return 16;} -INSTRUCTION(cpdr) {CPXR(--) } - - -/* MARK: - Instructions: 8-Bit Arithmetic and Logical Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| U [a,]Y 10uuuyyy sz5h3*** 1 / 4 | -| U [a,]K < DD >10uuukkk sz5h3*** 2 / 8 | -| U [a,]Q < FD >10uuuqqq sz5h3*** 2 / 8 | -| U [a,]BYTE 11uuu110< BYTE > sz5h3*** 2 / 7 | -| U [a,](hl) 10uuu110 sz5h3*** 2 / 7 | -| U [a,](ix+OFFSET) < DD >10uuu110 sz5h3*** 5 / 19 | -| U [a,](iy+OFFSET) < FD >10uuu110 sz5h3*** 5 / 19 | -| V X 00xxxvvv sz5h3v0. 1 / 4 | -| V J < DD >00jjjvvv sz5h3v0. 2 / 8 | -| V P < FD >00pppvvv sz5h3v0. 2 / 8 | -| V (hl) 00110vvv sz5h3v*. 3 / 11 | -| V (ix+OFFSET) < DD >00110vvv sz5h3v*. 6 / 23 | -| V (iy+OFFSET) < FD >00110vvv sz5h3v*. 6 / 23 | -'--------------------------------------------------------------------------*/ - -INSTRUCTION(U_a_Y) {PC++; U0(Y0); return 4;} -INSTRUCTION(U_a_KQ) {PC += 2; U1(KQ); return 8;} -INSTRUCTION(U_a_BYTE) {U0(READ_8((PC += 2) - 1)); return 7;} -INSTRUCTION(U_a_vhl) {PC++; U0(READ_8(HL)); return 7;} -INSTRUCTION(U_a_vXYOFFSET) {U1(READ_8(XY + READ_OFFSET((PC += 3) - 1))); return 19;} -INSTRUCTION(V_X) {zuint8 *r; PC++; r = __xxx___0(object); *r = V0(*r); return 4;} -INSTRUCTION(V_JP) {zuint8 *r; PC += 2; r = __jjj___ (object); *r = V1(*r); return 8;} -INSTRUCTION(V_vhl) {PC++; WRITE_8(HL, V0(READ_8(HL))); return 11;} -INSTRUCTION(V_vXYOFFSET) {zuint16 a = (zuint16)(XY + READ_OFFSET((PC += 3) - 1)); - WRITE_8(a, V1(READ_8(a))); return 23;} - - -/* MARK: - Instructions: General-Purpose Arithmetic and CPU Control Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| nop < 00 > ........ 1 / 4 | -| halt < 76 > ........ 1 / 4 | -| di < F3 > ........ 1 / 4 | -| ei < FB > ........ 1 / 4 | -| im 0 < ED >< 46 > ........ 2 / 8 | -| < ED >< 4E > | -| < ED >< 66 > | -| < ED >< 6E > | -| im 1 < ED >< 56 > ........ 2 / 8 | -| < ED >< 76 > | -| im 2 < ED >< 5E > ........ 2 / 8 | -| < ED >< 7E > | -| daa < 27 > szyhxp.c 1 / 4 | -| cpl < 2F > ..y1x.1. 1 / 4 | -| neg < ED >< 44 > szyhxv1c 2 / 8 | -| < ED >< 4C > | -| < ED >< 54 > | -| < ED >< 5C > | -| < ED >< 64 > | -| < ED >< 6C > | -| < ED >< 74 > | -| < ED >< 7C > | -| ccf < 3F > ..***.0c 1 / 4 | -| scf < 37 > ..*0*.01 1 / 4 | -'--------------------------------------------------------------------------*/ - -INSTRUCTION(nop) {PC++; return 4;} -INSTRUCTION(halt) {HALT = 1; SET_HALT; return 4;} -INSTRUCTION(di) {PC++; IFF1 = IFF2 = 0; EI = TRUE; return 4;} -INSTRUCTION(ei) {PC++; IFF1 = IFF2 = 1; EI = TRUE; return 4;} -INSTRUCTION(im_0) {PC += 2; IM = 0; return 8;} -INSTRUCTION(im_1) {PC += 2; IM = 1; return 8;} -INSTRUCTION(im_2) {PC += 2; IM = 2; return 8;} +INSTRUCTION(nop ) {Q_0 PC++; return 4;} +INSTRUCTION(im_0) {Q_0 IM = 0; PC += 2; return 8;} +INSTRUCTION(im_1) {Q_0 IM = 1; PC += 2; return 8;} +INSTRUCTION(im_2) {Q_0 IM = 2; PC += 2; return 8;} INSTRUCTION(daa) { - zuint8 t = (F_H || (A & 0xF ) > 9) ? 6 : 0; + zuint8 cf = A > 0x99, t = ((F & HF) || (A & 0xF) > 9) ? 6 : 0; - if (F_C || A > 0x99) t |= 0x60; - t = (F_N) ? A - t : A + t; - PC++; + if (F_C || cf) t |= 0x60; + t = (F & NF) ? A - t : A + t; - F = (zuint8) - ((F_N) /* NF unchanged */ - | (t & SYXF) /* SF = A.7; YF = A.5; XF = A.3 */ - | ZF_ZERO(t) /* ZF = !Af */ - | ((A & HF) ^ (t & HF)) /* HF = Ai.4 xor Af.4 */ - | PF_PARITY(t) /* PF = Parity of Af */ - | (F_C | (A > 0x99))); /* CF = CF | (Ai > 0x99) */ + FLAGS = (zuint8)( + (F & (NF | CF)) | /* NF unchanged; CF dominant */ + (t & SYXF) | /* SF = sign; YF = Y; XF = X */ + ZF_ZERO(t) | /* ZF = zero */ + ((A ^ t) & HF) | /* HF = Ai.4 != Ao.4 */ + PF_PARITY(t) | /* PF = parity */ + cf); /* CF |= 1 (if BCD carry) */ A = t; + PC++; return 4; } INSTRUCTION(cpl) { - PC++; A = ~A; - - F = (F & (SF | ZF | PF | CF)) /* SF, ZF, PF, CF unchanged */ - | HF /* HF = 1 */ - | NF /* NF = 1 */ - | A_YX; /* YF = A.5; XF = A.3 */ + FLAGS = F_SZPC | /* SF, ZF, PF, CF unchanged */ + ((A = ~A) & YXF) | /* YF = Y; XF = X */ + HF | NF; /* HF, NF = 1 */ + PC++; return 4; } INSTRUCTION(neg) { - zuint8 t = -A; PC += 2; + zuint8 t = (zuint8)-A; - F = (zuint8) - ((t & SYXF) /* SF = -A.7; YF = -A.5; XF = -A.3 */ - | ZF_ZERO(t) /* ZF = !-A */ - | ((0 ^ A ^ t) & HF) /* HF = Half-borrow */ - | ((t == 128) << 2) /* PF = Overflow */ - | NF /* NF = 1 */ - | !!A); /* CF = !!A */ + FLAGS = (zuint8)( + (t & SYXF) | /* SF = sign; YF = Y; XF = X */ + ZF_ZERO(t) | /* ZF = zero */ + ((A ^ t) & HF) | /* HF = half-borrow */ + ((t == 128) << 2) | /* PF = overflow */ + NF | /* NF = 1 */ + !!A); /* CF = borrow (not 0) */ A = t; + PC += 2; return 8; } +/*---------------------------------------------------------------------------. +| "ccf" and "scf" are the only instructions in which Q affects the flags. | +| Patrik Rak cracked the behavior of YF and XF in 2012, confirming that they | +| are taken, respectively, from bits 5 and 3 of the result of "(Q ^ F) | A" | +| [1,2]. This applies to all Zilog Z80 models, both NMOS and CMOS. In 2018, | +| David Banks (AKA hoglet) found that ST CMOS models do not set XF according | +| to this formula, but take this flag from bit 3 of A, while NEC NMOS models | +| take both flags from A [3]. | +| | +| References: | +| 1. https://worldofspectrum.org/forums/discussion/20345 | +| 2. https://worldofspectrum.org/forums/discussion/41704 | +| 3. Banks, David (2018-08-21). "Undocumented Z80 Flags". | +| * https://github.com/hoglet67/Z80Decoder/wiki/Undocumented-Flags | +| * https://stardot.org.uk/forums/download/file.php?id=39831 | +'===========================================================================*/ + INSTRUCTION(ccf) { + FLAGS = (zuint8)( + (F_SZPC ^ CF) | /* SF, ZF, PF unchanged; CF = ~CFi */ + /* Zilog: YF = A.5 | (YFi ^ YQi); XF = A.3 | (XFi ^ XQi) */ + /* ST CMOS: YF = A.5 | (YFi ^ YQi); XF = A.3 */ + /* NEC NMOS: YF = A.5; XF = A.3 */ +# ifdef Z80_WITH_Q + ((((F ^ Q) & OPTIONS) | A) & YXF) | +# else + (A & YXF) | +# endif + (F_C << 4)); /* HF = CFi */ + /* NF = 0 */ PC++; - - F = (zuint8) - (F_SZP /* SF, ZF, PF unchanged */ - | A_YX /* YF = A.5; XF = A.3 */ - | (F_C << 4) /* HF = CF */ - | (~F & CF)); /* CF = ~CF */ - /* NF = 0 */ return 4; } INSTRUCTION(scf) { + FLAGS = F_SZP | /* SF, ZF, PF unchanged */ + /* Zilog: YF = A.5 | (YFi ^ YQi); XF = A.3 | (XFi ^ XQi) */ + /* ST CMOS: YF = A.5 | (YFi ^ YQi); XF = A.3 */ + /* NEC NMOS: YF = A.5; XF = A.3 */ +# ifdef Z80_WITH_Q + ((((F ^ Q) & OPTIONS) | A) & YXF) | +# else + (A & YXF) | +# endif + CF; /* CF = 1 */ + /* HF, NF = 0 */ PC++; - - F = F_SZP /* SF, ZF, PF unchanged */ - | A_YX /* YF = A.5; XF = A.3 */ - | CF; /* CF = 1 */ - /* HF = 0; NF = 0 */ return 4; } -/* MARK: - Instructions: 16-Bit Arithmetic Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| add hl,SS 00ss1001 ..***.0* 3 / 11 | -| adc hl,SS < ED >01ss1010 *****v0* 4 / 15 | -| sbc hl,SS < ED >01ss0010 *****v0* 4 / 15 | -| add ix,WW < DD >00ww1001 ..***.0* 4 / 15 | -| add iy,WW < FD >00ww1001 ..***.0* 4 / 15 | -| inc SS 00ss0011 ........ 1 / 6 | -| inc ix < DD >< 23 > ........ 2 / 10 | -| inc iy < FD >< 23 > ........ 2 / 10 | -| dec SS 00ss1011 ........ 1 / 6 | -| dec ix < DD >< 2B > ........ 2 / 10 | -| dec iy < FD >< 2B > ........ 2 / 10 | -'--------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------. +| The "halt" instruction enables the HALT state after PC is incremented during | +| the opcode fetch. The CPU neither decrements nor avoids incrementing PC "so | +| that the instruction is re-executed" as Sean Young writes in section 5.4 of | +| "The Undocumented Z80 Documented". During the HALT state, the CPU repeatedly | +| executes an internal NOP operation. Each NOP consists of 1 M1 cycle of 4 | +| T-states that fetches and disregards the next opcode after "halt" without | +| incrementing PC. This opcode is read again and again until an exit condition | +| occurs (i.e., INT, NMI or RESET). | +| | +| This was first documented by Tony Brewer in 2014, and was later re-confirmed | +| by the HALT2INT test written by Mark Woodmass (AKA Woody) in 2021. | +| | +| References: | +| * Brewer, Tony (2014-12). "Z80 Special Reset". | +| * http://primrosebank.net/computers/z80/z80_special_reset.htm | +'=============================================================================*/ -INSTRUCTION(add_hl_SS) {PC++; ADD_RR_NN(HL, SS0) return 11;} -INSTRUCTION(adc_hl_SS) {ADC_SBC_HL_SS(adc, +, (zuint32)v + c + HL > 65535, Z_EMPTY)} -INSTRUCTION(sbc_hl_SS) {ADC_SBC_HL_SS(sbc, -, (zuint32)v + c > HL, | NF) } -INSTRUCTION(add_XY_WW) {PC += 2; ADD_RR_NN(XY, WW) return 15;} -INSTRUCTION(inc_SS) {PC++; SS0++; return 6;} -INSTRUCTION(inc_XY) {PC += 2; XY++; return 10;} -INSTRUCTION(dec_SS) {PC++; SS0--; return 6;} -INSTRUCTION(dec_XY) {PC += 2; XY--; return 15;} +INSTRUCTION(halt) + { + if (!HALT_LINE) + { + if (!RESUME) + { + Q_0 + PC++; + if (REQUEST & Z80_REQUEST_INTERRUPT) return 4; + RESUME = Z80_RESUME_HALT; + if ((self->cycles += 4) >= self->cycle_limit) return 0; + } + + HALT_LINE = TRUE; + if (self->halt != Z_NULL) self->halt(CONTEXT, TRUE); + } + + if (self->nop == Z_NULL || (OPTIONS & Z80_OPTION_HALT_SKIP)) + { + if (REQUEST) RESUME = FALSE; + + else if (self->cycles < self->cycle_limit) + { + zusize nop_cycles = self->cycle_limit - self->cycles; + + nop_cycles += (4 - (nop_cycles & 3)) & 3; + R += (zuint8)(nop_cycles >> 2); + self->cycles += nop_cycles; + } + } + + else for (; self->cycles < self->cycle_limit; self->cycles += 4) + { + if (REQUEST) + { + RESUME = FALSE; + return 0; + } + + R++; /* M1 */ + (void)self->nop(CONTEXT, PC); + } + + return 0; + } -/* MARK: - Instructions: Rotate and Shift Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| rlca < 07 > ..y0x.0c 1 / 4 | -| rla < 17 > ..y0x.0c 1 / 4 | -| rrca < 0F > ..y0x.0c 1 / 4 | -| rra < 1F > ..y0x.0c 1 / 4 | -| G Y < CB >00gggyyy szy0xp0c 2 / 8 | -| G (hl) < CB >00ggg110 szy0xp0c 4 / 15 | -| G (ix+OFFSET) < DD >< CB >00ggg110 szy0xp0c 6 / 23 | -| G (iy+OFFSET) < FD >< CB >00ggg110 szy0xp0c 6 / 23 | -| G (ix+OFFSET),Y < DD >< CB >00gggyyy szy0xp0c 6 / 23 | -| G (iy+OFFSET),Y < FD >< CB >00gggyyy szy0xp0c 6 / 23 | -| rld < ED >< 6F > szy0xp0. 5 / 18 | -| rrd < ED >< 67 > szy0xp0. 5 / 18 | -'--------------------------------------------------------------------------*/ - -INSTRUCTION(rlca) {PC++; ROL(A); F = F_SZP | (A & YXCF); return 4;} -INSTRUCTION(rla) {zuint8 c; PC++; c = A >> 7; A = (zuint8)((A << 1) | F_C); RXA return 4;} -INSTRUCTION(rrca) {PC++; ROR(A); F = F_SZP | A_YX | (A >> 7); return 4;} -INSTRUCTION(rra) {zuint8 c; PC++; c = A & 1; A = (zuint8)((A >> 1) | (F << 7)); RXA return 4;} -INSTRUCTION(G_Y) {zuint8 *r = _____yyy1(object); *r = G1(*r); return 8;} -INSTRUCTION(G_vhl) {WRITE_8(HL, G1(READ_8(HL))); return 15;} -INSTRUCTION(G_vXYOFFSET) {zuint16 a = XY_ADDRESS; WRITE_8(a, G3(READ_8(a))); return 23;} -INSTRUCTION(G_vXYOFFSET_Y) {zuint16 a = XY_ADDRESS; WRITE_8(a, Y3 = G3(READ_8(a))); return 23;} -INSTRUCTION(rld) {RXD(<<, & 0xF, >> 4) return 18;} -INSTRUCTION(rrd) {RXD(>>, << 4, & 0xF) return 18;} +INSTRUCTION(di) + { + Q_0 + IFF1 = IFF2 = 0; + REQUEST &= ~(zuint8)Z80_REQUEST_INT; + PC++; + return 4; + } -/* MARK: - Instructions: Bit Set, Reset and Test Group -.---------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ----------------------------------------------------------------------- | -| bit N,Y < CB >01nnnyyy szy1xz0. 2 / 8 | -| bit N,(hl) < CB >01nnn110 sz?1?z0. 3 / 12 | -| bit N,(ix+OFFSET) < DD >< CB >01nnn*** sz*1*z0. 5 / 20 | -| bit N,(iy+OFFSET) < FD >< CB >01nnn*** sz*1*z0. 5 / 20 | -| M N,Y < CB >1mnnnyyy ........ 2 / 8 | -| M N,(hl) < CB >1mnnn110 ........ 4 / 15 | -| M N,(ix+OFFSET) < DD >< CB >1mnnn110 ........ 6 / 23 | -| M N,(iy+OFFSET) < FD >< CB >1mnnn110 ........ 6 / 23 | -| M N,(ix+OFFSET),Y < DD >< CB >1mnnnyyy ........ 6 / 23 | -| M N,(iy+OFFSET),Y < FD >< CB >1mnnnyyy ........ 6 / 23 | -'--------------------------------------------------------------------------*/ - -INSTRUCTION(bit_N_Y) {BIT_N_VALUE(Y1) return 8;} -INSTRUCTION(bit_N_vhl) {BIT_N_VALUE(READ_8(HL)) return 12;} -INSTRUCTION(bit_N_vXYOFFSET) {BIT_N_VADDRESS(XY_ADDRESS) return 20;} -INSTRUCTION(M_N_Y) {zuint8 *t = _____yyy1(object); *t = M1(*t); return 8;} -INSTRUCTION(M_N_vhl) {WRITE_8(HL, M1(READ_8(HL))); return 15;} -INSTRUCTION(M_N_vXYOFFSET) {zuint16 a = XY_ADDRESS; WRITE_8(a, M3(READ_8(a))); return 23;} -INSTRUCTION(M_N_vXYOFFSET_Y) {zuint16 a = XY_ADDRESS; WRITE_8(a, Y3 = M3(READ_8(a))); return 23;} +INSTRUCTION(ei) + { + Q_0 + IFF1 = IFF2 = 1; + if (INT_LINE) REQUEST |= Z80_REQUEST_INT; + PC++; + return 4; + } -/* MARK: - Instructions: Jump Group -.-------------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| --------------------------------------------------------------------------- | -| jp WORD < C3 >< WORD > ........ 3 / 10 | -| jp Z,WORD 11zzz010< WORD > ........ 3 / 10 | -| jr OFFSET < 18 > ........ 3 / 12 | -| jr Z,OFFSET 001zz000 ........ 3,2 / 12,7 | -| jp (hl) < E9 > ........ 1 / 4 | -| jp (ix) < DD >< E9 > ........ 2 / 8 | -| jp (iy) < FD >< E9 > ........ 2 / 8 | -| djnz OFFSET < 10 > ........ 3,2 / 13,8 | -'------------------------------------------------------------------------------*/ +/* MARK: - Instructions: 16-Bit Arithmetic Group */ +/*--------------------------------------------------. +| 0 1 Flags T-states | +| Assembly 7654321076543210 SZYHXPNC 1234 | +| --------- ---------------- -------- -------- | +| add hl,SS 00ss1001 ..YCX.0c 11:443 | +| adc hl,SS <--ED-->01ss1010 szYCXv0c 15:4443 | +| sbc hl,SS <--ED-->01ss0010 szYBXv1b 15:4443 | +| add XY,WW <--XY-->00ww1001 ..YCX.0c 15:4443 | +| inc SS 00ss0011 ........ 6:6 | +| inc XY <--XY--><--23--> ........ 10:46 | +| dec SS 00ss1011 ........ 6:6 | +| dec XY <--XY--><--2B--> ........ 10:46 | +'==================================================*/ -INSTRUCTION(jp_WORD) {PC = READ_16(PC + 1); return 10;} -INSTRUCTION(jp_Z_WORD) {PC = Z ? READ_16(PC + 1) : PC + 3; return 10;} -INSTRUCTION(jr_OFFSET) {PC += (2 + READ_OFFSET(PC + 1)); return 12;} -INSTRUCTION(jr_Z_OFFSET) {BYTE0 &= 223; PC += 2; if (Z) {PC += READ_OFFSET(PC - 1); return 12;} return 7;} -INSTRUCTION(jp_hl) {PC = HL; return 4;} -INSTRUCTION(jp_XY) {PC = XY; return 8;} -INSTRUCTION(djnz_OFFSET) {PC += 2; if (--B) {PC += READ_OFFSET(PC - 1); return 13;} return 8;} +INSTRUCTION(add_hl_SS) {ADD_RR_NN(HL, SS0); PC++; return 11;} +INSTRUCTION(adc_hl_SS) {ADC_SBC_HL_SS(+, ~ss, ss + fc + HL > 65535, Z_EMPTY);} +INSTRUCTION(sbc_hl_SS) {ADC_SBC_HL_SS(-, ss, ss + fc > HL, | NF );} +INSTRUCTION(add_XY_WW) {ADD_RR_NN(XY, WW); PC += 2; return 15;} +INSTRUCTION(inc_SS ) {Q_0 (SS0)++; PC++; return 6;} +INSTRUCTION(inc_XY ) {Q_0 XY++; PC += 2; return 10;} +INSTRUCTION(dec_SS ) {Q_0 (SS0)--; PC++; return 6;} +INSTRUCTION(dec_XY ) {Q_0 XY--; PC += 2; return 10;} -/* MARK: - Instructions: Call and Return Group -.--------------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ---------------------------------------------------------------------------- | -| call WORD < CD >< WORD > ........ 5 / 17 | -| call Z,WORD 11zzz100< WORD > ........ 3,5 / 10,17 | -| ret < C9 > ........ 3 / 10 | -| ret Z 11zzz000 ........ 1,3 / 5,11 | -| reti < ED >< 4D > ........ 4 / 14 | -| retn < ED >< 45 > ........ 4 / 14 | -| < ED >< 55 > | -| < ED >< 5D > | -| < ED >< 65 > | -| < ED >< 6D > | -| < ED >< 75 > | -| < ED >< 7D > | -| rst N 11nnn111 ........ 3 / 11 | -'-------------------------------------------------------------------------------*/ +/* MARK: - Instructions: Rotate and Shift Group */ +/*-------------------------------------------------------------------------. +| 0 1 2 3 Flags T-states | +| Assembly 76543210765432107654321076543210 SZYHXPNC 123456 | +| --------------- -------------------------------- -------- --------- | +| rlca <--07--> ..y0x.0= 4:4 | +| rla <--17--> ..y0x.0= 4:4 | +| rrca <--0F--> ..y0x.0= 4:4 | +| rra <--1F--> ..y0x.0= 4:4 | +|+ G K <--CB-->00gggkkk szy0xp0= 8:44 | +|+ G (hl) <--CB-->00ggg110 szy0xp0= 15:4443 | +|+ G (XY+OFFSET) <--XY--><--CB-->00ggg110 szy0xp0= 23:443543 | +|* G (XY+OFFSET),K <--XY--><--CB-->00gggkkk szy0xp0= 23:443543 | +| rld <--ED--><--6F--> szy0xp0. 18:44343 | +| rrd <--ED--><--67--> szy0xp0. 18:44343 | +|--------------------------------------------------------------------------| +| (+) Some forms of the instruction are undocumented. | +| (*) Undocumented instruction. | +'=========================================================================*/ -INSTRUCTION(call_WORD) {PUSH(PC + 3); PC = READ_16(PC + 1); return 17;} -INSTRUCTION(call_Z_WORD) {if (Z) return call_WORD(object); PC += 3; return 10;} -INSTRUCTION(ret) {RET; return 10;} -INSTRUCTION(ret_Z) {if (Z) {RET; return 11;} PC++; return 5;} -INSTRUCTION(reti) {IFF1 = IFF2; RET; return 14;} -INSTRUCTION(retn) {IFF1 = IFF2; RET; return 14;} -INSTRUCTION(rst_N) {PUSH(PC + 1); PC = BYTE0 & 56; return 11;} +INSTRUCTION(rlca ) {ROL(A); FLAGS = F_SZP | (A & YXCF); PC++; return 4;} +INSTRUCTION(rla ) {RXA(A >> 7, <<, F_C); } +INSTRUCTION(rrca ) {ROR(A); FLAGS = F_SZP | A_YX | (A >> 7); PC++; return 4;} +INSTRUCTION(rra ) {RXA(A & 1, >>, (F << 7)); } +INSTRUCTION(G_K ) {zuint8 *k = &K1; *k = G1(*k); return 8;} +INSTRUCTION(G_vhl ) {WRITE(HL, G1(READ(HL))); return 15;} +INSTRUCTION(G_vXYpOFFSET ) {zuint16 ea = MEMPTR; WRITE(ea, G3(READ(ea))); return 23;} +INSTRUCTION(G_vXYpOFFSET_K) {zuint16 ea = MEMPTR; WRITE(ea, K3 = G3(READ(ea))); return 23;} +INSTRUCTION(rld ) {RXD(<< 4, & 0xF, >> 4); } +INSTRUCTION(rrd ) {RXD(>> 4, << 4, & 0xF); } -/* MARK: - Instructions: Input and Output Group -.--------------------------------------------------------------------------------. -| 0 1 2 3 Flags | -| Assembly 76543210765432107654321076543210 szyhxpnc Cycles | -| ---------------------------------------------------------------------------- | -| in a,(BYTE) < DB >< BYTE > ........ 3 / 11 | -| in X,(c) < ED >01xxx000 szy0xp0. 3 / 12 | -| in 0,(c) < ED >< 70 > szy0xp0. 3 / 12 | -| ini < ED >< A2 > ******** 4 / 16 | -| inir < ED >< B2 > 010*0*** 5,4 / 21,16 | -| ind < ED >< AA > ******** 4 / 16 | -| indr < ED >< BA > 010*0*** 5,4 / 21,16 | -| out (BYTE),a < D3 >< BYTE > ........ 3 / 11 | -| out (c),X < ED >01xxx001 ........ 3 / 12 | -| out (c),0 < ED >< 71 > ........ 3 / 12 | -| outi < ED >< A3 > ******** 4 / 16 | -| otir < ED >< B3 > 010*0_** 5,4 / 21,16 | -| outd < ED >< AB > ******** 4 / 16 | -| otdr < ED >< BB > 010*0*** 5,4 / 21,16 | -'-------------------------------------------------------------------------------*/ +/* MARK: - Instructions: Bit Set, Reset and Test Group */ +/*---------------------------------------------------------------------------. +| 0 1 2 3 Flags T-states | +| Assembly 76543210765432107654321076543210 SZYHXPNC 123456 | +| ----------------- -------------------------------- -------- --------- | +| bit N,K <--CB-->01nnnkkk sz*1*z0. 8:44 | +| bit N,(hl) <--CB-->01nnn110 sz*1*z0. 12:444 [1] +|+ bit N,(XY+OFFSET) <--XY--><--CB-->01nnn*** sz*1*z0. 20:44354 | +| M N,K <--CB-->1mnnnkkk ........ 8:44 | +| M N,(hl) <--CB-->1mnnn110 ........ 15:4443 | +| M N,(XY+OFFSET) <--XY--><--CB-->1mnnn110 ........ 23:443543 | +|* M N,(XY+OFFSET),K <--XY--><--CB-->1mnnnkkk ........ 23:443543 | +|----------------------------------------------------------------------------| +| (+) Some forms of the instruction are undocumented. | +| (*) Undocumented instruction. | +|----------------------------------------------------------------------------| +| 1. All versions of Zilog's "Z80 Family: CPU User Manual" contain a typo in | +| the M-cycles of this instruction: an additional M-cycle of 4 T-states. | +'===========================================================================*/ -INSTRUCTION(in_a_BYTE) {A = IN((A << 8) | READ_8((PC += 2) - 1)); return 11;} -INSTRUCTION(in_X_vc) {IN_VC; X1 = t; return 12;} -INSTRUCTION(in_0_vc) {IN_VC; return 16;} -INSTRUCTION(ini) {INX (++, +) return 16;} -INSTRUCTION(inir) {INXR(++, +) } -INSTRUCTION(ind) {INX (--, -) return 16;} -INSTRUCTION(indr) {INXR(--, -) } -INSTRUCTION(out_vBYTE_a) {OUT((A << 8) | READ_8((PC += 2) - 1), A); return 11;} -INSTRUCTION(out_vc_X) {PC += 2; OUT(BC, X1); return 12;} -INSTRUCTION(out_vc_0) {PC += 2; OUT(BC, 0); return 12;} -INSTRUCTION(outi) {OUTX(++) return 16;} -INSTRUCTION(otir) {OTXR(++) } -INSTRUCTION(outd) {OUTX(--) return 16;} -INSTRUCTION(otdr) {OTXR(--) } +INSTRUCTION(M_N_K ) {zuint8 *k = &K1; *k = M1(*k); return 8;} +INSTRUCTION(M_N_vhl ) {WRITE(HL, M1(READ(HL))); return 15;} +INSTRUCTION(M_N_vXYpOFFSET ) {zuint16 ea = MEMPTR; WRITE(ea, M3(READ(ea))); return 23;} +INSTRUCTION(M_N_vXYpOFFSET_K) {zuint16 ea = MEMPTR; WRITE(ea, K3 = M3(READ(ea))); return 23;} -/* MARK: - Opcode Selector Prototypes */ +INSTRUCTION(bit_N_K) + { + zuint8 k = K1; + zuint8 t = k & (1U << N(1)); -INSTRUCTION(CB); -INSTRUCTION(DD); -INSTRUCTION(ED); -INSTRUCTION(FD); -INSTRUCTION(XY_CB); -INSTRUCTION(ED_illegal); -INSTRUCTION(XY_illegal); + /*----------------------------------------------------------. + | In section 4.1 of "The Undocumented Z80 Documented" (all | + | versions), Sean Young says that YF and XF are taken from | + | the value resulting from the bit test operation, but this | + | seems not to be true. They are copies of bits 5 and 3 of | + | the register containing the value to be tested (K). | + '==========================================================*/ + FLAGS = (t ? t & SF : ZPF) | /* SF = sign; ZF, PF = zero */ + (k & YXF) | /* YF = K.5; XF = K.3 */ + HF | /* HF = 1 */ + F_C; /* CF unchanged */ + /* NF = 0 */ + return 8; + } + + +INSTRUCTION(bit_N_vhl) + { + zuint8 t = READ(HL) & (1U << N(1)); + + /*----------------------------------------------------------------. + | This is the only instruction in which MEMPTR affects the flags. | + | YF and XF are taken, respectively, from bits 13 and 11 of this | + | internal register whose behavior was cracked in 2006 by boo_boo | + | and Vladimir Kladov. Official schematics refer to this register | + | as WZ, but this emulator uses the name "MEMPTR" to honour those | + | who cracked it. | + | | + | References: | + | * https://zxpress.ru/zxnet/zxnet.pc/5909 | + | * boo_boo; Kladov, Vladimir (2006-03-29). "MEMPTR, Esoteric | + | Register of the Zilog Z80 CPU". | + | * http://zx.pk.ru/showpost.php?p=43688 | + | * http://zx.pk.ru/attachment.php?attachmentid=2984 | + | * http://zx.pk.ru/showpost.php?p=43800 | + | * http://zx.pk.ru/attachment.php?attachmentid=2989 | + '================================================================*/ + FLAGS = (t ? t & SF : ZPF) | /* SF = sign; ZF, PF = zero */ + (MEMPTRH & YXF) | /* YF = MEMPTRH.5; XF = MEMPTRH.3 */ + HF | /* HF = 1 */ + F_C; /* CF unchanged */ + /* NF = 0 */ + return 12; + } + + +INSTRUCTION(bit_N_vXYpOFFSET) + { + zuint8 t = READ(MEMPTR) & (1U << N(3)); + + FLAGS = (t ? t & SF : ZPF) | /* SF sign; ZF, PF = zero */ + (MEMPTRH & YXF) | /* YF = EA.13; XF = EA.11 */ + HF | /* HF = 1 */ + F_C; /* CF unchanged */ + /* NF = 0 */ + return 20; + } + + +/* MARK: - Instructions: Jump Group */ +/*----------------------------------------------------------------. +| 0 1 2 Flags T-states | +| Assembly 765432107654321076543210 SZYHXPNC Y 123 N 12 | +| ----------- ------------------------ -------- ------------ | +| jp WORD <--C3--><-----WORD-----> ........ 10:433 | +| jp Z,WORD 11zzz010<-----WORD-----> ........ 10:433 | +| jr OFFSET <--18--> ........ 12:435 | +| jr Z,OFFSET 001zz000 ........ 12:435 7:43 | +| jp (hl) <--E9--> ........ 4:4 | +| jp (XY) <--XY--><--E9--> ........ 8:44 | +| djnz OFFSET <--10--> ........ 13:535 8:53 | +'================================================================*/ + +INSTRUCTION(jp_WORD ) {Q_0 MEMPTR = PC = FETCH_16(PC + 1); return 10;} +INSTRUCTION(jp_Z_WORD ) {Q_0 MEMPTR = FETCH_16(PC + 1); PC = Z(7) ? MEMPTR : PC + 3; return 10;} +INSTRUCTION(jr_OFFSET ) {Q_0 MEMPTR = (PC += 2 + (zsint8)FETCH(PC + 1)); return 12;} +INSTRUCTION(jr_Z_OFFSET) {DJNZ_JR_Z_OFFSET(Z(3), 12, 7); } +INSTRUCTION(jp_hl ) {Q_0 PC = HL; return 4;} +INSTRUCTION(jp_XY ) {Q_0 PC = XY; return 8;} +INSTRUCTION(djnz_OFFSET) {DJNZ_JR_Z_OFFSET(--B, 13, 8); } + + +/* MARK: - Instructions: Call and Return Group */ +/*--------------------------------------------------------------------. +| 0 1 2 Flags T-states | +| Assembly 765432107654321076543210 SZYHXPNC Y 123 N 123 | +| ----------- ------------------------ -------- ---------------- | +| call WORD <--CD--><-----WORD-----> ........ 17:43433 | +| call Z,WORD 11zzz100<-----WORD-----> ........ 17:43433 10:433 | +| ret <--C9--> ........ 10:433 | +| ret Z 11zzz000 ........ 11:533 5:5 | +|+ reti/retn <--ED-->01***101 ........ 14:4433 | +| rst N 11nnn111 ........ 11:533 | +|---------------------------------------------------------------------| +| (+) The instruction has undocumented opcodes. The mnemonic "reti" | +| is used to represent the ED4Dh opcode, which is recognized by | +| the Z80 CTC chip. All others opcodes are represented as "retn". | +'====================================================================*/ + +INSTRUCTION(call_WORD) {Q_0 MEMPTR = FETCH_16(PC + 1); PUSH(PC + 3); PC = MEMPTR; return 17;} +INSTRUCTION(ret ) {Q_0 RET; return 10;} +INSTRUCTION(ret_Z ) {Q_0 if (Z(7)) {RET; return 11;} PC++; return 5;} +INSTRUCTION(reti ) {RETX(reti); } +INSTRUCTION(retn ) {RETX(retn); } +INSTRUCTION(rst_N ) {Q_0 PUSH(PC + 1); MEMPTR = PC = DATA[0] & 56; return 11;} + + +INSTRUCTION(call_Z_WORD) + { + Q_0 + MEMPTR = FETCH_16(PC + 1); /* always */ + + if (Z(7)) + { + PUSH(PC + 3); + PC = MEMPTR; + return 17; + } + + PC += 3; + return 10; + } + + +/* MARK: - Instructions: Input and Output Group */ +/*--------------------------------------------------------------. +| 0 1 Flags T-states | +| Assembly 7654321076543210 SZYHXPNC !0 12345 =0 1234 | +| ------------ ---------------- -------- ----------------- | +| in a,(BYTE) <--DB--><-BYTE-> ........ 11:434 | +| in J,(c) <--ED-->01jjj000 szy0xp0. 12:444 | +|* in (c) <--ED--><--70--> szy0xp0. 12:444 | +| ini <--ED--><--A2--> ******** 16:4543 [1] +| inir <--ED--><--B2--> ******** 21:45435 16:4543 [1] +| ind <--ED--><--AA--> ******** 16:4543 [1] +| indr <--ED--><--BA--> ******** 21:45435 16:4543 [1] +| out (BYTE),a <--D3--><-BYTE-> ........ 11:434 | +| out (c),J <--ED-->01jjj001 ........ 12:444 | +|* out (c),0 <--ED--><--71--> ........ 12:444 | +| outi <--ED--><--A3--> ******** 16:4534 | +| otir <--ED--><--B3--> ******** 21:45345 16:4534 | +| outd <--ED--><--AB--> ******** 16:4534 | +| otdr <--ED--><--BB--> ******** 21:45345 16:4534 | +|---------------------------------------------------------------| +| (*) Undocumented instruction. | +|---------------------------------------------------------------| +| 1. All versions of Zilog's "Z80 Family: CPU User Manual" have | +| the same typo in the M-cycles of these instructions: the | +| T-states of the 3rd and 4th M-cycles are swapped. | +'==============================================================*/ + +INSTRUCTION(in_J_vc ) {IN_VC; J1 = t; PC += 2; return 12;} +INSTRUCTION(in_vc ) {IN_VC; PC += 2; return 12;} +INSTRUCTION(ini ) {INX (++, +); } +INSTRUCTION(inir ) {INXR(++, +); } +INSTRUCTION(ind ) {INX (--, -); } +INSTRUCTION(indr ) {INXR(--, -); } +INSTRUCTION(out_vc_J) {Q_0 MEMPTR = BC + 1; OUT(BC, J1); PC += 2; return 12;} +INSTRUCTION(outi ) {OUTX(++, +); } +INSTRUCTION(otir ) {OTXR(++, +); } +INSTRUCTION(outd ) {OUTX(--, -); } +INSTRUCTION(otdr ) {OTXR(--, -); } + + +INSTRUCTION(in_a_vBYTE) + { + zuint16 t; + + Q_0 + + /*--------------------------------------------------------------------. + | In "MEMPTR, Esoteric Register of the Zilog Z80 CPU", boo_boo says | + | that MEMPTR is set to "((A << 8) | BYTE) + 1", which causes a carry | + | from the LSbyte of the resulting port number if BYTE is 255. This | + | differs from all other instructions where MEMPTRH is set to A, but | + | it has been confirmed to be correct by the IN-MEMPTR test. | + '====================================================================*/ + MEMPTR = (t = (zuint16)(((zuint16)A << 8) | FETCH((PC += 2) - 1))) + 1; + + A = IN(t); + return 11; + } + + +INSTRUCTION(out_vBYTE_a) + { + zuint8 t; + + Q_0 + MEMPTRL = (t = FETCH((PC += 2) - 1)) + 1; + MEMPTRH = A; + OUT((zuint16)(((zuint16)A << 8) | t), A); + return 11; + } + + +/*----------------------------------------------------------------------------. +| The "out (c),0" instruction behaves as "out (c),255" on the Zilog Z80 CMOS. | +| This was first discovered by Simon Cooke, who reported it on Usenet in 1996 | +| [1]. Later, in 2004, Colin Piggot rediscovered it with his SAM Coupé when | +| running a demo for SCPDU 6, coincidentally written by Simon Cooke [2]. In | +| 2008, this was once again rediscovered by the MSX community [3]. | +| | +| References: | +| 1. https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJ | +| 2. https://sinclair.wiki.zxnet.co.uk/wiki/Z80 | +| 3. https://msx.org/forum/development/msx-development/bug-z80-emulation-or- | +| tr-hw | +'============================================================================*/ + +INSTRUCTION(out_vc_0) + { + Q_0 + PC += 2; + MEMPTR = BC + 1; + OUT(BC, (OPTIONS & Z80_OPTION_OUT_VC_255) ? 255 : 0); + return 12; + } /* MARK: - Instruction Function Tables */ +INSTRUCTION(cb_prefix ); +INSTRUCTION(ed_prefix ); +INSTRUCTION(dd_prefix ); +INSTRUCTION(fd_prefix ); +INSTRUCTION(xy_cb_prefix); +INSTRUCTION(xy_xy ); +INSTRUCTION(ed_illegal ); +INSTRUCTION(xy_illegal ); +INSTRUCTION(hook ); + +#define nop_nop ed_illegal + +#ifdef Z80_WITH_UNOFFICIAL_RETI +# define reti_retn reti +#else +# define reti_retn retn +#endif + static Instruction const instruction_table[256] = { -/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ -/* 0 */ nop, ld_SS_WORD, ld_vbc_a, inc_SS, V_X, V_X, ld_X_BYTE, rlca, ex_af_af_, add_hl_SS, ld_a_vbc, dec_SS, V_X, V_X, ld_X_BYTE, rrca, -/* 1 */ djnz_OFFSET, ld_SS_WORD, ld_vde_a, inc_SS, V_X, V_X, ld_X_BYTE, rla, jr_OFFSET, add_hl_SS, ld_a_vde, dec_SS, V_X, V_X, ld_X_BYTE, rra, -/* 2 */ jr_Z_OFFSET, ld_SS_WORD, ld_vWORD_hl, inc_SS, V_X, V_X, ld_X_BYTE, daa, jr_Z_OFFSET, add_hl_SS, ld_hl_vWORD, dec_SS, V_X, V_X, ld_X_BYTE, cpl, -/* 3 */ jr_Z_OFFSET, ld_SS_WORD, ld_vWORD_a, inc_SS, V_vhl, V_vhl, ld_vhl_BYTE, scf, jr_Z_OFFSET, add_hl_SS, ld_a_vWORD, dec_SS, V_X, V_X, ld_X_BYTE, ccf, -/* 4 */ ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, -/* 5 */ ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, -/* 6 */ ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, -/* 7 */ ld_vhl_Y, ld_vhl_Y, ld_vhl_Y, ld_vhl_Y, ld_vhl_Y, ld_vhl_Y, halt, ld_vhl_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_Y, ld_X_vhl, ld_X_Y, -/* 8 */ U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, -/* 9 */ U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, -/* A */ U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, -/* B */ U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_Y, U_a_vhl, U_a_Y, -/* C */ ret_Z, pop_TT, jp_Z_WORD, jp_WORD, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, ret, jp_Z_WORD, CB, call_Z_WORD, call_WORD, U_a_BYTE, rst_N, -/* D */ ret_Z, pop_TT, jp_Z_WORD, out_vBYTE_a, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, exx, jp_Z_WORD, in_a_BYTE, call_Z_WORD, DD, U_a_BYTE, rst_N, -/* E */ ret_Z, pop_TT, jp_Z_WORD, ex_vsp_hl, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, jp_hl, jp_Z_WORD, ex_de_hl, call_Z_WORD, ED, U_a_BYTE, rst_N, -/* F */ ret_Z, pop_TT, jp_Z_WORD, di, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, ld_sp_hl, jp_Z_WORD, ei, call_Z_WORD, FD, U_a_BYTE, rst_N +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +/* 0 */ nop, ld_SS_WORD, ld_vbc_a, inc_SS, V_J, V_J, ld_J_BYTE, rlca, ex_af_af_, add_hl_SS, ld_a_vbc, dec_SS, V_J, V_J, ld_J_BYTE, rrca, +/* 1 */ djnz_OFFSET, ld_SS_WORD, ld_vde_a, inc_SS, V_J, V_J, ld_J_BYTE, rla, jr_OFFSET, add_hl_SS, ld_a_vde, dec_SS, V_J, V_J, ld_J_BYTE, rra, +/* 2 */ jr_Z_OFFSET, ld_SS_WORD, ld_vWORD_hl, inc_SS, V_J, V_J, ld_J_BYTE, daa, jr_Z_OFFSET, add_hl_SS, ld_hl_vWORD, dec_SS, V_J, V_J, ld_J_BYTE, cpl, +/* 3 */ jr_Z_OFFSET, ld_SS_WORD, ld_vWORD_a, inc_SS, V_vhl, V_vhl, ld_vhl_BYTE, scf, jr_Z_OFFSET, add_hl_SS, ld_a_vWORD, dec_SS, V_J, V_J, ld_J_BYTE, ccf, +/* 4 */ nop, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_vhl, ld_J_K, ld_J_K, nop, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_vhl, ld_J_K, +/* 5 */ ld_J_K, ld_J_K, nop, ld_J_K, ld_J_K, ld_J_K, ld_J_vhl, ld_J_K, ld_J_K, ld_J_K, ld_J_K, nop, ld_J_K, ld_J_K, ld_J_vhl, ld_J_K, +/* 6 */ ld_J_K, ld_J_K, ld_J_K, ld_J_K, hook, ld_J_K, ld_J_vhl, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_K, nop, ld_J_vhl, ld_J_K, +/* 7 */ ld_vhl_K, ld_vhl_K, ld_vhl_K, ld_vhl_K, ld_vhl_K, ld_vhl_K, halt, ld_vhl_K, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_K, ld_J_vhl, nop, +/* 8 */ U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, +/* 9 */ U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, +/* A */ U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, +/* B */ U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_K, U_a_vhl, U_a_K, +/* C */ ret_Z, pop_TT, jp_Z_WORD, jp_WORD, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, ret, jp_Z_WORD, cb_prefix, call_Z_WORD, call_WORD, U_a_BYTE, rst_N, +/* D */ ret_Z, pop_TT, jp_Z_WORD, out_vBYTE_a, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, exx, jp_Z_WORD, in_a_vBYTE, call_Z_WORD, dd_prefix, U_a_BYTE, rst_N, +/* E */ ret_Z, pop_TT, jp_Z_WORD, ex_vsp_hl, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, jp_hl, jp_Z_WORD, ex_de_hl, call_Z_WORD, ed_prefix, U_a_BYTE, rst_N, +/* F */ ret_Z, pop_TT, jp_Z_WORD, di, call_Z_WORD, push_TT, U_a_BYTE, rst_N, ret_Z, ld_sp_hl, jp_Z_WORD, ei, call_Z_WORD, fd_prefix, U_a_BYTE, rst_N }; -static Instruction const instruction_table_CB[256] = { +static Instruction const cb_instruction_table[256] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ -/* 0 */ G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, -/* 1 */ G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, -/* 2 */ G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, -/* 3 */ G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_Y, G_vhl, G_Y, -/* 4 */ bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, -/* 5 */ bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, -/* 6 */ bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, -/* 7 */ bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_Y, bit_N_vhl, bit_N_Y, -/* 8 */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* 9 */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* A */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* B */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* C */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* D */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* E */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, -/* F */ M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_Y, M_N_vhl, M_N_Y +/* 0 */ G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, +/* 1 */ G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, +/* 2 */ G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, +/* 3 */ G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, G_K, G_K, G_K, G_K, G_K, G_K, G_vhl, G_K, +/* 4 */ bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, +/* 5 */ bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, +/* 6 */ bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, +/* 7 */ bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_K, bit_N_vhl, bit_N_K, +/* 8 */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* 9 */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* A */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* B */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* C */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* D */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* E */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, +/* F */ M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_K, M_N_vhl, M_N_K }; -static Instruction const instruction_table_XY_CB[256] = { -/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ -/* 0 */ G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, -/* 1 */ G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, -/* 2 */ G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, -/* 3 */ G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET_Y, G_vXYOFFSET, G_vXYOFFSET_Y, -/* 4 */ bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, -/* 5 */ bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, -/* 6 */ bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, -/* 7 */ bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, bit_N_vXYOFFSET, -/* 8 */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* 9 */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* A */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* B */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* C */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* D */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* E */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, -/* F */ M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET_Y, M_N_vXYOFFSET, M_N_vXYOFFSET_Y -}; - -static Instruction const instruction_table_XY[256] = { -/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ -/* 0 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, add_XY_WW, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, -/* 1 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, add_XY_WW, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, -/* 2 */ XY_illegal, ld_XY_WORD, ld_vWORD_XY, inc_XY, V_JP, V_JP, ld_JP_BYTE, XY_illegal, XY_illegal, add_XY_WW, ld_XY_vWORD, dec_XY, V_JP, V_JP, ld_JP_BYTE, XY_illegal, -/* 3 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, V_vXYOFFSET, V_vXYOFFSET, ld_vXYOFFSET_BYTE, XY_illegal, XY_illegal, add_XY_WW, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, -/* 4 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, XY_illegal, -/* 5 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, XY_illegal, -/* 6 */ ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, ld_JP_KQ, -/* 7 */ ld_vXYOFFSET_Y, ld_vXYOFFSET_Y, ld_vXYOFFSET_Y, ld_vXYOFFSET_Y, ld_vXYOFFSET_Y, ld_vXYOFFSET_Y, XY_illegal, ld_vXYOFFSET_Y, XY_illegal, XY_illegal, XY_illegal, XY_illegal, ld_JP_KQ, ld_JP_KQ, ld_X_vXYOFFSET, XY_illegal, -/* 8 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, -/* 9 */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, -/* A */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, -/* B */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, U_a_KQ, U_a_KQ, U_a_vXYOFFSET, XY_illegal, -/* C */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_CB, XY_illegal, XY_illegal, XY_illegal, XY_illegal, -/* D */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, -/* E */ XY_illegal, pop_XY, XY_illegal, ex_vsp_XY, XY_illegal, push_XY, XY_illegal, XY_illegal, XY_illegal, jp_XY, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, -/* F */ XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, ld_sp_XY, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal, XY_illegal -}; - -static Instruction const instruction_table_ED[256] = { +static Instruction const ed_instruction_table[256] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ -/* 0 */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* 1 */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* 2 */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* 3 */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* 4 */ in_X_vc, out_vc_X, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_0, ld_i_a, in_X_vc, out_vc_X, adc_hl_SS, ld_SS_vWORD, neg, reti, im_0, ld_r_a, -/* 5 */ in_X_vc, out_vc_X, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_1, ld_a_i, in_X_vc, out_vc_X, adc_hl_SS, ld_SS_vWORD, neg, retn, im_2, ld_a_r, -/* 6 */ in_X_vc, out_vc_X, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_0, rrd, in_X_vc, out_vc_X, adc_hl_SS, ld_SS_vWORD, neg, retn, im_0, rld, -/* 7 */ in_0_vc, out_vc_0, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_1, ED_illegal, in_X_vc, out_vc_X, adc_hl_SS, ld_SS_vWORD, neg, retn, im_2, ED_illegal, -/* 8 */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* 9 */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* A */ ldi, cpi, ini, outi, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ldd, cpd, ind, outd, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* B */ ldir, cpir, inir, otir, ED_illegal, ED_illegal, ED_illegal, ED_illegal, lddr, cpdr, indr, otdr, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* C */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* D */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* E */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, -/* F */ ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal, ED_illegal +/* 0 */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* 1 */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* 2 */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* 3 */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* 4 */ in_J_vc, out_vc_J, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_0, ld_i_a, in_J_vc, out_vc_J, adc_hl_SS, ld_SS_vWORD, neg, reti, im_0, ld_r_a, +/* 5 */ in_J_vc, out_vc_J, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_1, ld_a_i, in_J_vc, out_vc_J, adc_hl_SS, ld_SS_vWORD, neg, reti_retn, im_2, ld_a_r, +/* 6 */ in_J_vc, out_vc_J, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_0, rrd, in_J_vc, out_vc_J, adc_hl_SS, ld_SS_vWORD, neg, reti_retn, im_0, rld, +/* 7 */ in_vc, out_vc_0, sbc_hl_SS, ld_vWORD_SS, neg, retn, im_1, ed_illegal, in_J_vc, out_vc_J, adc_hl_SS, ld_SS_vWORD, neg, reti_retn, im_2, ed_illegal, +/* 8 */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* 9 */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* A */ ldi, cpi, ini, outi, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ldd, cpd, ind, outd, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* B */ ldir, cpir, inir, otir, ed_illegal, ed_illegal, ed_illegal, ed_illegal, lddr, cpdr, indr, otdr, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* C */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* D */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* E */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, +/* F */ ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal, ed_illegal +}; + +static Instruction const xy_instruction_table[256] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +/* 0 */ nop_nop, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, add_XY_WW, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, +/* 1 */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, add_XY_WW, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, +/* 2 */ xy_illegal, ld_XY_WORD, ld_vWORD_XY, inc_XY, V_O, V_O, ld_O_BYTE, xy_illegal, xy_illegal, add_XY_WW, ld_XY_vWORD, dec_XY, V_O, V_O, ld_O_BYTE, xy_illegal, +/* 3 */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, V_vXYpOFFSET, V_vXYpOFFSET, ld_vXYpOFFSET_BYTE, xy_illegal, xy_illegal, add_XY_WW, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, +/* 4 */ nop_nop, xy_illegal, xy_illegal, xy_illegal, ld_O_P, ld_O_P, ld_J_vXYpOFFSET, xy_illegal, xy_illegal, nop_nop, xy_illegal, xy_illegal, ld_O_P, ld_O_P, ld_J_vXYpOFFSET, xy_illegal, +/* 5 */ xy_illegal, xy_illegal, nop_nop, xy_illegal, ld_O_P, ld_O_P, ld_J_vXYpOFFSET, xy_illegal, xy_illegal, xy_illegal, xy_illegal, nop_nop, ld_O_P, ld_O_P, ld_J_vXYpOFFSET, xy_illegal, +/* 6 */ ld_O_P, ld_O_P, ld_O_P, ld_O_P, nop_nop, ld_O_P, ld_J_vXYpOFFSET, ld_O_P, ld_O_P, ld_O_P, ld_O_P, ld_O_P, ld_O_P, nop_nop, ld_J_vXYpOFFSET, ld_O_P, +/* 7 */ ld_vXYpOFFSET_K, ld_vXYpOFFSET_K, ld_vXYpOFFSET_K, ld_vXYpOFFSET_K, ld_vXYpOFFSET_K, ld_vXYpOFFSET_K, xy_illegal, ld_vXYpOFFSET_K, xy_illegal, xy_illegal, xy_illegal, xy_illegal, ld_O_P, ld_O_P, ld_J_vXYpOFFSET, nop_nop, +/* 8 */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, +/* 9 */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, +/* A */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, +/* B */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, U_a_P, U_a_P, U_a_vXYpOFFSET, xy_illegal, +/* C */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_cb_prefix, xy_illegal, xy_illegal, xy_illegal, xy_illegal, +/* D */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_xy, xy_illegal, xy_illegal, +/* E */ xy_illegal, pop_XY, xy_illegal, ex_vsp_XY, xy_illegal, push_XY, xy_illegal, xy_illegal, xy_illegal, jp_XY, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, +/* F */ xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, xy_illegal, ld_sp_XY, xy_illegal, xy_illegal, xy_illegal, xy_xy, xy_illegal, xy_illegal +}; + +static Instruction const xy_cb_instruction_table[256] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +/* 0 */ G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, +/* 1 */ G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, +/* 2 */ G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, +/* 3 */ G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET_K, G_vXYpOFFSET, G_vXYpOFFSET_K, +/* 4 */ bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, +/* 5 */ bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, +/* 6 */ bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, +/* 7 */ bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, bit_N_vXYpOFFSET, +/* 8 */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* 9 */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* A */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* B */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* C */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* D */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* E */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, +/* F */ M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET_K, M_N_vXYpOFFSET, M_N_vXYpOFFSET_K }; -/* MARK: - Prefixed Instruction Set Selection and Execution */ +/* MARK: - Instructions: Prefix Handling */ -#define DD_FD(register) \ - zuint8 cycles; \ - \ - XY = register; \ - R++; \ - cycles = instruction_table_XY[BYTE1 = READ_8(PC + 1)](object); \ - register = XY; \ +INSTRUCTION(cb_prefix) + { + R++; + return cb_instruction_table[DATA[1] = FETCH_OPCODE((PC += 2) - 1)](self); + } + + +INSTRUCTION(ed_prefix) + { + R++; + return ed_instruction_table[DATA[1] = FETCH_OPCODE(PC + 1)](self); + } + + +#define XY_PREFIX(register) \ + zuint8 cycles; \ + \ + XY = register; \ + R++; \ + cycles = xy_instruction_table[DATA[1] = FETCH_OPCODE(PC + 1)](self); \ + register = XY; \ return cycles; -INSTRUCTION(DD) {DD_FD(IX)} -INSTRUCTION(FD) {DD_FD(IY)} -INSTRUCTION(CB) {R++; return instruction_table_CB[BYTE1 = READ_8((PC += 2) - 1)](object);} -INSTRUCTION(ED) {R++; return instruction_table_ED[BYTE1 = READ_8( PC + 1)](object);} +INSTRUCTION(dd_prefix) {XY_PREFIX(IX)} +INSTRUCTION(fd_prefix) {XY_PREFIX(IY)} -INSTRUCTION(XY_CB) +/*------------------------------------------------------------------------. +| Instructions with DDCBh or FDCBh prefix increment R by 2, as only the | +| bytes of the prefix are fetched by opcode fetch operations (M1 cycles). | +| The remaining 2 bytes are fetched by normal memory read operations. | +'========================================================================*/ + +INSTRUCTION(xy_cb_prefix) { - PC += 4; - BYTE2 = READ_8(PC - 2); - return instruction_table_XY_CB[BYTE3 = READ_8(PC - 1)](object); + FETCH_XY_EA((PC += 4) - 2); + return xy_cb_instruction_table[DATA[3] = FETCH(PC - 1)](self); } -/* MARK: - Illegal Instruction Handling */ +/*-----------------------------------------------------------------------------. +| In a sequence of DDh and/or FDh prefixes, it is the last one that counts, as | +| each prefix disables and replaces the previous one. No matter how long the | +| sequence is, interrupts can only be responded after all prefixes are fetched | +| and the final instruction is executed. Each prefix consumes 4 T-states. | +'=============================================================================*/ -INSTRUCTION(XY_illegal) {PC += 1; return instruction_table[BYTE0 = BYTE1](object) + 4;} -INSTRUCTION(ED_illegal) {PC += 2; return 8;} +// TODO: RESET - -/* MARK: - Main Functions */ - -CPU_Z80_API void z80_power(Z80 *object, zboolean state) +INSTRUCTION(xy_xy) { - if (state) - { -# ifdef Z_Z80_RESET_IS_EQUAL_TO_POWER_ON - z80_reset(object); -# else - PC = Z_Z80_VALUE_AFTER_POWER_ON_PC; - SP = Z_Z80_VALUE_AFTER_POWER_ON_SP; - IX = Z_Z80_VALUE_AFTER_POWER_ON_IX; - IY = Z_Z80_VALUE_AFTER_POWER_ON_IY; - AF = Z_Z80_VALUE_AFTER_POWER_ON_AF; - BC = Z_Z80_VALUE_AFTER_POWER_ON_BC; - DE = Z_Z80_VALUE_AFTER_POWER_ON_DE; - HL = Z_Z80_VALUE_AFTER_POWER_ON_HL; - AF_ = Z_Z80_VALUE_AFTER_POWER_ON_AF_; - BC_ = Z_Z80_VALUE_AFTER_POWER_ON_BC_; - DE_ = Z_Z80_VALUE_AFTER_POWER_ON_DE_; - HL_ = Z_Z80_VALUE_AFTER_POWER_ON_HL_; - R = Z_Z80_VALUE_AFTER_POWER_ON_R; - I = Z_Z80_VALUE_AFTER_POWER_ON_I; - IFF1 = Z_Z80_VALUE_AFTER_POWER_ON_IFF1; - IFF2 = Z_Z80_VALUE_AFTER_POWER_ON_IFF2; - IM = Z_Z80_VALUE_AFTER_POWER_ON_IM; + zuint8 cycles, first_prefix = DATA[0]; - EI = HALT = INT = NMI = 0; + do { + DATA[0] = DATA[1]; + PC++; + + if ((self->cycles += 4) >= self->cycle_limit) + { + RESUME = Z80_RESUME_XY; + return 0; + } + + R++; + } + while (IS_XY_PREFIX(DATA[1] = FETCH_OPCODE(PC + 1))); + + if (DATA[0] == first_prefix) return xy_instruction_table[DATA[1]](self); + + if (first_prefix == 0xFD) + { + XY = IX; + cycles = xy_instruction_table[DATA[1]](self); + IX = XY; + XY = IY; + } + + else { + XY = IY; + cycles = xy_instruction_table[DATA[1]](self); + IY = XY; + XY = IX; + } + + return cycles; + } + + +/* MARK: - Instructions: Illegal */ + +/*------------------------------------------------------------------. +| The CPU ignores illegal instructions with EDh prefix; in practice | +| they are all equivalent to two "nop" instructions (8 T-states). | +'==================================================================*/ + +INSTRUCTION(ed_illegal) + { + Q_0 + PC += 2; + return 8; + } + + +/*------------------------------------------------------------------------. +| Illegal instructions with DDh or FDh prefix cause the CPU to ignore the | +| prefix, that is, the byte immediately following the prefix is treated | +| as the first byte of a new instruction. The prefix consumes 4 T-states. | +'========================================================================*/ + +INSTRUCTION(xy_illegal) + { + PC++; + DATA[0] = DATA[1]; + + if ((self->cycles += 4) >= self->cycle_limit) + { + RESUME = Z80_RESUME_XY; + return 0; + } + + return instruction_table[DATA[0]](self); + } + + +/* MARK: - Instructions: Hooking */ + +INSTRUCTION(hook) + { + if (self->hook == Z_NULL) + { + Q_0 + PC++; + return 4; + } + + if ((DATA[0] = self->hook(CONTEXT, PC)) != Z80_HOOK) + return instruction_table[DATA[0]](self); + + R--; + return 0; + } + + +/* MARK: - Interrupt Mode 0: PC Decrements for Unprefixed Instructions */ + +#ifdef Z80_WITH_FULL_IM0 + static zuint8 const im0_pc_decrement_table[256] = { + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 1 */ 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + /* 2 */ 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + /* 3 */ 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + /* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 6 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* A */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* B */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* C */ 1, 0, 3, 0, 3, 0, 0, 1, 1, 0, 3, 0, 3, 3, 0, 1, + /* D */ 1, 0, 3, 0, 3, 0, 0, 1, 1, 0, 3, 0, 3, 0, 0, 1, + /* E */ 1, 0, 3, 0, 3, 0, 0, 1, 1, 0, 3, 0, 3, 0, 0, 1, + /* F */ 1, 0, 3, 0, 3, 0, 0, 1, 1, 0, 3, 0, 3, 0, 0, 1 + }; +#endif + + +/* MARK: - Interrupt Mode 0: Callback Trampolines */ + +#ifdef Z80_WITH_FULL_IM0 + static zuint8 im0_fetch(IM0 *self, zuint16 address) + { + Z_UNUSED(address) + return self->z80->int_fetch(CONTEXT, self->pc); + } + + + static zuint8 im0_read(IM0 *self, zuint16 address) + {return READ(address);} + + + static void im0_write(IM0 *self, zuint16 address, zuint8 value) + {WRITE(address, value);} + + + static zuint8 im0_in(IM0 *self, zuint16 port) + {return IN(port);} + + + static void im0_out(IM0 *self, zuint16 port, zuint8 value) + {OUT(port, value);} + + + static void im0_ld_i_a(IM0 *self) {NOTIFY(ld_i_a);} + static void im0_ld_r_a(IM0 *self) {NOTIFY(ld_r_a);} + static void im0_reti (IM0 *self) {NOTIFY(reti );} + static void im0_retn (IM0 *self) {NOTIFY(retn );} +#endif + + +/* MARK: - Public Functions */ + +/*----------------------------------------------------------------------. +| On POWER-ON, the CPU clears PC, I, and R, sets SP, IX, IY, AF, BC, | +| DE, HL, AF', BC', DE' and HL' to to FFFFh [1,2], resets the interrupt | +| enable flip-flops (IFF1 and IFF2) [2] and selects the interrupt mode | +| 0. On Zilog NMOS models, F is sometimes set to FDh (NF reset) [2]. | +| | +| There is no information about the initial state of MEMPTR and Q, so | +| they are assumed to be 0. | +| | +| References: | +| 1. https://worldofspectrum.org/forums/discussion/34574 | +| 2. https://baltazarstudios.com/webshare/Z80-undocumented-behavior.htm | +'======================================================================*/ + +Z80_API void z80_power(Z80 *self, zboolean state) + { + MEMPTR = PC = R = I = IFF1 = IFF2 = IM = Q = + DATA[0] = HALT_LINE = INT_LINE = RESUME = REQUEST = 0; + + SP = IX = IY = AF = BC = DE = HL = AF_ = BC_ = DE_ = HL_ = + state ? Z_UINT16(0xFFFF) : 0; + } + + +Z80_API void z80_instant_reset(Z80 *self) + { + if (HALT_LINE) {EXIT_HALT_STATE;} + PC = R = I = IFF1 = IFF2 = IM = + DATA[0] = HALT_LINE = RESUME = REQUEST = 0; + } + + +#ifdef Z80_WITH_RESET_SIGNAL + Z80_API void z80_reset(Z80 *self) + {REQUEST |= Z80_REQUEST_RESET;} +#endif + + +#ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + Z80_API void z80_special_reset(Z80 *self) + {REQUEST |= Z80_REQUEST_SPECIAL_RESET;} +#endif + + +Z80_API void z80_int(Z80 *self, zboolean state) + { + if (!(INT_LINE = state)) REQUEST &= ~(zuint8)Z80_REQUEST_INT; + else if (IFF1) REQUEST |= Z80_REQUEST_INT; + } + + +Z80_API void z80_nmi(Z80 *self) + {REQUEST |= Z80_REQUEST_NMI;} + + +Z80_API void z80_busreq(Z80 *self, zboolean state) + {} + + +#ifdef Z80_WITH_EXECUTE + Z80_API zusize z80_execute(Z80 *self, zusize cycles) + { + R7 = R; + self->cycles = 0; + self->cycle_limit = cycles; + + if (RESUME && cycles) switch (RESUME) + { + case Z80_RESUME_HALT: + (void)halt(self); + break; + + case Z80_RESUME_XY: + RESUME = FALSE; + self->cycles += instruction_table[DATA[0]](self); + break; + } + + while (self->cycles < cycles) + { + R++; /* M1 */ + self->cycles += instruction_table[DATA[0] = FETCH_OPCODE(PC)](self); + } + + R = R_ALL; /* restore R7 bit */ + return self->cycles; /* return consumed cycles */ + } +#endif + + +Z80_API zusize z80_run(Z80 *self, zusize cycles) + { + /*---------------------------------------------------------------------. + | The CPU increments R during each M1 cycle without altering the most | + | significant bit, commonly known as R7. This behavior is not emulated | + | in every increment for obvious speed reasons. Instead, a copy of R | + | is used to preserve R7, which is restored before returning from this | + | function. The emulation of "ld {a,r|r,a}" takes this into account. | + '=====================================================================*/ + R7 = R; + + self->cycles = 0; + self->cycle_limit = cycles; + + if (RESUME && cycles) switch (RESUME) + { + /*----------------------------------------------------------------. + | The CPU is halted. In order to avoid affecting the speed of the | + | main execution loop, this state is executed by a dedicated loop | + | within the function that emulates the "halt" instruction. | + '================================================================*/ + case Z80_RESUME_HALT: + (void)halt(self); + break; + + /*--------------------------------------------------------------------. + | The CPU is in normal operation state and the first byte of the next | + | instruction has already been fetched. This resumption is scheduled | + | if the emulator runs out of cycles while fetching a prefix sequence | + | or an illegal instruction with DDh or FDh prefix. | + '====================================================================*/ + case Z80_RESUME_XY: + RESUME = FALSE; + /*resume_opcode:*/ + self->cycles += instruction_table[DATA[0]](self); + break; + + /*--------------------------------------------------------------------. + | The CPU is responding to an INT in mode 0 and the first byte of the | + | instruction has already been fetched. This resumption if scheduled | + | if the emulator runs out of cycles while fetching a prefix sequence | + | or an illegal instruction with DDh or FDh prefix. | + '====================================================================*/ +# ifdef Z80_WITH_FULL_IM0 + case Z80_RESUME_IM0_XY: + RESUME = FALSE; + goto im0_begin; +# endif + +# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + case Z80_RESUME_SPECIAL_RESET_XY: + RESUME = FALSE; + goto special_reset_execute; + + case Z80_RESUME_SPECIAL_RESET_NOP: + RESUME = FALSE; + goto special_reset_nop; # endif } - else PC = SP = IX = IY = AF = BC = DE = HL = AF_ = BC_ = DE_ = HL_ = I = R = - IFF1 = IFF2 = IM = EI = HALT = INT = NMI = 0; - } - - -CPU_Z80_API void z80_reset(Z80 *object) - { - PC = Z_Z80_VALUE_AFTER_RESET_PC; - SP = Z_Z80_VALUE_AFTER_RESET_SP; - IX = Z_Z80_VALUE_AFTER_RESET_IX; - IY = Z_Z80_VALUE_AFTER_RESET_IY; - AF = Z_Z80_VALUE_AFTER_RESET_AF; - BC = Z_Z80_VALUE_AFTER_RESET_BC; - DE = Z_Z80_VALUE_AFTER_RESET_DE; - HL = Z_Z80_VALUE_AFTER_RESET_HL; - AF_ = Z_Z80_VALUE_AFTER_RESET_AF_; - BC_ = Z_Z80_VALUE_AFTER_RESET_BC_; - DE_ = Z_Z80_VALUE_AFTER_RESET_DE_; - HL_ = Z_Z80_VALUE_AFTER_RESET_HL_; - R = Z_Z80_VALUE_AFTER_RESET_R; - I = Z_Z80_VALUE_AFTER_RESET_I; - IFF1 = Z_Z80_VALUE_AFTER_RESET_IFF1; - IFF2 = Z_Z80_VALUE_AFTER_RESET_IFF2; - IM = Z_Z80_VALUE_AFTER_RESET_IM; - - EI = HALT = INT = NMI = 0; - } - - -CPU_Z80_API zusize z80_run(Z80 *object, zusize cycles) - { - zuint32 data; - - /*-------------. - | Clear cycles | - '-------------*/ - CYCLES = 0; - - /*--------------. - | Backup R7 bit | - '--------------*/ - R7 = R; - - /*------------------------------. - | Execute until cycles consumed | - '------------------------------*/ - while (CYCLES < cycles) + while (self->cycles < cycles) /* Main execution loop. */ { - /*--------------------------------------. - | Jump to NMI handler if NMI pending... | - '--------------------------------------*/ - if (NMI) + if (REQUEST) { - EXIT_HALT; /* Resume CPU if halted. */ - R++; /* Consume memory refresh. */ - NMI = FALSE; /* Clear the NMI pulse. */ - /*IFF2 = IFF1;*/ /* Backup IFF1 (it doesn't occur, acording to Sean Young). */ - IFF1 = 0; /* Reset IFF1 to don't bother the NMI routine. */ - PUSH(PC); /* Save return addres in the stack. */ - PC = Z_Z80_ADDRESS_NMI_POINTER; /* Make PC point to the NMI routine. */ - CYCLES += 11; /* Accepting a NMI consumes 11 cycles. */ - continue; - } - - /*--------------------------. - | Execute INT if pending... | - '--------------------------*/ - if (INT && IFF1 && !EI) - { - EXIT_HALT; /* Resume CPU on halt. */ - R++; /* Consume memory refresh. */ - IFF1 = IFF2 = 0; /* Clear interrupt flip-flops. */ - - switch (IM) - { - /*------------------------------. - | IM 0: Execute bus instruction | - '------------------------------*/ - case 0: - - if ((data = INT_DATA)) switch (data & Z_UINT32(0xFF000000)) + /*-------------------------------------------------------------------------. + | Normal RESET Response | T-states: 3 | + |--------------------------------------------------------------------------| + | The normal RESET clears PC, I, and R [1,2,3,4,5], resets the interrupt | + | enable flip-flops (IFF1 and IFF2) [1,2,4,5] and sets the interrupt mode | + | 0 [1,2,5]. Once /RESET goes inactive, the CPU consumes 3 T-states before | + | resuming normal processing operation at address 0000h [2,4,6,7]. | + | | + | References: | + | 1. Zilog (2005-02). "Z80 Family: CPU User Manual" v5. pp. 9,10. | + | 2. Flammenkamp, Achim. "Interrupt Behaviour of the Z80 CPU". | + | * http://z80.info/interrup.htm | + | 3. https://worldofspectrum.org/forums/discussion/34574 | + | 4. https://baltazarstudios.com/webshare/Z80-undocumented-behavior.htm | + | 5. Brewer, Tony (2014-12). "Z80 Special Reset". | + | * http://primrosebank.net/computers/z80/z80_special_reset.htm | + | 6. SGS-Thomson (1990-01). "Z80 Microprocessor Family Databook". p. 40. | + '=========================================================================*/ +# ifdef Z80_WITH_RESET_SIGNAL + if (REQUEST & Z80_REQUEST_RESET) { - case Z_UINT32(0xC3000000): /* JP */ - PC = (zuint16)(data >> 8); - CYCLES += 10; - break; + if (HALT_LINE) {EXIT_HALT_STATE;} - case Z_UINT32(0xCD000000): /* CALL */ - PUSH(PC); - PC = (zuint16)(data >> 8); - CYCLES += 17; - break; + reset: + self->cycles += (self->reset != Z_NULL) + ? self->reset(CONTEXT, PC) + : Z80_CYCLES_PER_RESET; - default: /* RST (and possibly others) */ - PUSH(PC); - PC = (zuint16)((data >> 8) & 0x38); - CYCLES += 11; + PC = R = I = IFF1 = IFF2 = IM = + DATA[0] = HALT_LINE = RESUME = 0; + REQUEST = Z80_REQUEST_REJECT_NMI; + continue; + } +# endif + + /*-------------------------------------------------------------------------. + | NMI Acknowledge/Response | T-states: 11:533 | + |--------------------------------------------------------------------------| + | The non-maskable interrupt takes priority over the maskable interrupt | + | and cannot be disabled under software control. Its usual function is to | + | provide immediate response to important signals. The CPU responds to an | + | NMI by storing PC on the stack and jumping to the ISR located at address | + | 0066h. The interrupt enable flip-flop 1 (IFF1) is reset to prevent any | + | INT from being accepted during the execution of this routine, which is | + | usually exited by using a "reti" or "retn" instruction to restore the | + | original state of IFF1. | + | | + | Some technical documents from Zilog include an erroneous timing diagram | + | showing an NMI acknowledge cycle of 4 T-states. In particular, "The Z80 | + | Family Program Interrupt Structure" and all versions of "Z80 Family: CPU | + | User Manual". Other documents from Zilog, as well as practically all the | + | existing literature about the Z80 from different manufacturers and third | + | parties, specify that this M-cycle has 5 T-states, as has been confirmed | + | by electronic simulations [1]. | + | | + | References: | + | 1. Tested on "Visual Z80 Remix" | + | * https://floooh.github.io/visualz80remix | + | * https://github.com/floooh/v6502r | + '=========================================================================*/ + if (REQUEST & Z80_REQUEST_REJECT_NMI) +# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + REQUEST &= ~(zuint8)Z80_REQUEST_REJECT_NMI; +# else + REQUEST = 0; +# endif + + else if (REQUEST & Z80_REQUEST_NMI) + { + DATA[0] = IFF1 = 0; + + if (HALT_LINE) + { + HALT_LINE = FALSE; + + if (self->halt != Z_NULL) + { + self->halt(CONTEXT, FALSE); + +# ifdef Z80_WITH_RESET_SIGNAL + if (REQUEST & Z80_REQUEST_RESET) goto reset; +# endif + } } - CYCLES += 2; - break; + R++; /* M1 */ + if (self->nmia != Z_NULL) (void)self->nmia(CONTEXT, PC); + Q_0 - /*----------------------. - | IM 1: Execute rst 38h | - '----------------------*/ - case 1: - PUSH(PC); - PC = 0x38; - CYCLES += (11 + 2); - break; +# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + PC >>= REQUEST & Z80_REQUEST_CLEAR_PC; + + /*----------------------------. + | RESET -> RESET | + | NMI -> REJECT_NMI | + | SPECIAL_RESET -> CLEAR_PC | + '----------------------------*/ + REQUEST = (REQUEST & (Z80_REQUEST_ANY_RESET | Z80_REQUEST_NMI)) >> 1; +# else + REQUEST = +# ifdef Z80_WITH_RESET_SIGNAL + (REQUEST & Z80_REQUEST_RESET) | +# endif + Z80_REQUEST_REJECT_NMI; +# endif - /*---------------------------. - | IM 2: Execute rst [i:byte] | - '---------------------------*/ - case 2: PUSH(PC); - PC = READ_16(((zuint16)(I << 8)) | (INT_DATA & 0xFF)); - CYCLES += (17 + 2); - break; + MEMPTR = PC = 0x66; + self->cycles += 11; + continue; } - continue; + /*-------------------------------------------------------------------------. + | INT Acknowledge/Response | + |--------------------------------------------------------------------------| + | The maskable interrupt can be enabled and disabled by using the "ei" and | + | "di" instructions respectively, which control the state of the interrupt | + | enable flip-flops (IFF1 and IFF2). The CPU does not accept this kind of | + | interrupt directly after an "ei" instruction, but only after the one | + | following "ei" is executed. This is so that ISRs can return without the | + | danger of being interrupted immediately after re-enabling interrupts if | + | the /INT line is still active, which could cause a stack overflow. | + | | + | This behavior is also present in all forms of the the "reti" and "retn" + | instructions. + | interrupt acceptance for one instruction, but this only occurs when IFF1 | + | and IFF2 have different states prior to the execution of either of these | + | instructions. This was first documented by Andre Weissflog (AKA Floh) in | + | 2021 [1], and was later rediscovered by Manuel Sainz de Baranda y Goñi | + | (AKA ZjoyKiLer) in 2022 [2,3]. | + | | + | References: | + | 1. https://floooh.github.io/2021/12/17/cycle-stepped-z80.html + | 2. https://spectrumcomputing.co.uk/forums/viewtopic.php?p=91342#p91342 | + | 3. https://stardot.org.uk/forums/viewtopic.php?t=24662 | + '=========================================================================*/ + else if ( +# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + (REQUEST & Z80_REQUEST_INT) && +# endif + /* if the previous instruction is not "ei" */ + DATA[0] != 0xFB && + /* if the previous instruction is not "reti/retn" or IFF1 has not changed */ + (self->data.uint32_value & Z_UINT32_BIG_ENDIAN(Z_UINT32(0xFFC7FF00))) + != Z_UINT32_BIG_ENDIAN(Z_UINT32(0xED450000)) + ) + { +# ifdef Z80_WITH_FULL_IM0 + Z80Read hook; + IM0 im0; +# endif + zuint8 byte; + + DATA[0] = IFF1 = IFF2 = 0; + + if (HALT_LINE) + { + HALT_LINE = FALSE; + + if (self->halt != Z_NULL) + { + self->halt(CONTEXT, FALSE); + +# ifdef Z80_WITH_RESET_SIGNAL + if (REQUEST & Z80_REQUEST_RESET) goto reset; +# endif + } + } + + /*-----------------------------------------------------. + | Due to a bug, the Zilog Z80 NMOS resets PF if an INT | + | is accepted during the execution of "ld a,{i|r}". | + '=====================================================*/ +# ifdef Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG + else if ( + (OPTIONS & Z80_OPTION_LD_A_IR_BUG) && + (self->data.uint16_array[0] & Z_UINT16_BIG_ENDIAN(Z_UINT16(0xFFF7))) + == Z_UINT16_BIG_ENDIAN(Z_UINT16(0xED57)) + ) + FLAGS = F & ~(zuint8)PF; +# endif + + /*----------------------------------------------------------------------. + | The INT acknowledge cycle indicates that the interrupting I/O device | + | can write to the data bus. 2 wait T-states are added to this M-cycle, | + | allowing sufficient time to identify which device must insert the | + | interrupt response data. The first and perhaps only byte of this data | + | is read from the data bus during this special M1 cycle. | + | | + | The value FFh is assumed when the "inta" callback is not used. This | + | is the most desirable behavior, since the "rst 38h" instruction will | + | be executed if the interrupt mode is 0. | + '======================================================================*/ + R++; /* M1 */ + byte = (self->inta != Z_NULL) ? INTA : 0xFF; + +# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + PC >>= REQUEST & Z80_REQUEST_CLEAR_PC; + REQUEST = (REQUEST & Z80_REQUEST_NMI) | ((REQUEST & Z80_REQUEST_ANY_RESET) >> 1); +# else + REQUEST &= +# ifdef Z80_WITH_RESET_SIGNAL + Z80_REQUEST_RESET | +# endif + Z80_REQUEST_NMI; +# endif + + switch (IM) /* Response */ + { + /*-------------------------------------------------------------------------. + | Interrupt Mode 0: Execute Instruction | T-states: 2*n + instruction | + |--------------------------------------------------------------------------| + | An instruction supplied via the data bus is executed. Its first byte is | + | read during the INT acknowledge cycle (INTA). If it is an opcode prefix, | + | additional M-cycles of this kind are produced until the final opcode of | + | the instruction is fetched [1]. Each INT acknowledge cycle consumes as | + | many T-states as its normal M1 counterpart (the opcode fech M-cycle) | + | plus the 2 wait T-states mentioned above [1]. Subsequent bytes of the | + | instruction are fetched by using normal memory read M-cycles [1,2], | + | during which the interrupting I/O device must still supply the data [2]. | + | The PC register, however, remains at its pre-interrupt state, not being | + | incremented as a result of the instruction fetch [1,2]. | + | | + | References: | + | 1. Tested on "Visual Z80 Remix" | + | * https://floooh.github.io/visualz80remix | + | * https://github.com/floooh/v6502r | + | 2. Zilog (1978-05). "Z80 Family Program Interrupt Structure, The". | + | pp. 6,8. | + '=========================================================================*/ + case 0: + DATA[0] = byte; + +# ifdef Z80_WITH_FULL_IM0 + im0_begin: + hook = self->hook; + im0.z80 = self; + im0.context = self->context; + im0.fetch = self->fetch; + im0.read = self->read; + im0.write = self->write; + im0.in = self->in; + im0.out = self->out; + im0.pc = PC; + byte = DATA[0]; + self->context = &im0; + self->fetch = (Z80Read )im0_fetch; + self->read = (Z80Read )im0_read; + self->write = (Z80Write)im0_write; + self->in = (Z80Read )im0_in; + self->out = (Z80Write)im0_out; + self->hook = Z_NULL; + + im0_execute: + + if (im0_pc_decrement_table[byte]) + { + PC -= im0_pc_decrement_table[byte]; + self->cycles += 2 + instruction_table[byte](self); + } + + /* halt */ + else if (byte == 0x76) HALT_LINE = TRUE; + + /* instructions with CBh prefix */ + else if (byte == 0xCB) + { + R++; + self->cycles += 4 + cb_instruction_table[DATA[1] = INTA](self); + } + + /* instructions with EDh prefix */ + else if (byte == 0xED) + { + im0.ld_i_a = self->ld_i_a; + im0.ld_r_a = self->ld_r_a; + im0.reti = self->reti; + im0.retn = self->retn; + self->ld_i_a = (Z80Notify)im0_ld_i_a; + self->ld_r_a = (Z80Notify)im0_ld_r_a; + self->reti = (Z80Notify)im0_reti; + self->retn = (Z80Notify)im0_retn; + + R++; + self->cycles += 4 + ed_instruction_table[DATA[1] = INTA](self); + + self->ld_i_a = im0.ld_i_a; + self->ld_r_a = im0.ld_r_a; + self->reti = im0.reti; + self->retn = im0.retn; + + /* All except: reti / retn */ + if ((DATA[1] & 0xC7) != 0x45) PC -= ((DATA[1] & 0xC7) == 0x43) + ? 4 /* ld SS,(WORD) / ld (WORD),SS */ + : 2 /* all others */; + } + + /* instructions with DDh, FDh, DDCBh or FDCBh prefix */ + else if (IS_XY_PREFIX(byte)) + { + Instruction instruction; + + im0_xy: + R++; + + if (IS_XY_PREFIX(byte = INTA)) + { + DATA[0] = byte; + if ((self->cycles += 6) < cycles) goto im0_xy; + RESUME = Z80_RESUME_IM0_XY; + goto im0_finalize; + } + + if ((instruction = xy_instruction_table[byte]) == xy_illegal) + { + DATA[0] = byte; + if ((self->cycles += 6) < cycles) goto im0_execute; + RESUME = Z80_RESUME_IM0_XY; + goto im0_finalize; + } + + if (DATA[0] == 0xDD) + { + XY = IX; + self->cycles += 4 + instruction(self); + IX = XY; + } + + else { + XY = IY; + self->cycles += 4 + instruction(self); + IY = XY; + } + + /* all except: jp (XY) */ + if (byte != 0xE9) PC = im0.pc; + } + + else { + cycles += 2 + instruction_table[byte](self); + + /* all except: jp WORD / jp (hl)> / ret */ + if (byte != 0xC3 && (byte & 0xDF) != 0xC9) PC = im0.pc; + } + + im0_finalize: + self->context = im0.context; + self->fetch = im0.fetch; + self->read = im0.read; + self->write = im0.write; + self->in = im0.in; + self->out = im0.out; + self->hook = hook; + + if (HALT_LINE) + { + if (self->halt != Z_NULL) self->halt(im0.context, TRUE); + RESUME = Z80_RESUME_HALT; + Q_0 + self->cycles += 6; + (void)halt(self); + } + + continue; + +# else + switch (byte) + { + /* jp WORD */ + case 0xC3: + Q_0 + MEMPTR = PC = int_fetch_16(self); + self->cycles += 2 + 10; + continue; + + /* call WORD */ + case 0xCD: + Q_0 + MEMPTR = int_fetch_16(self); + PUSH(PC); + PC = MEMPTR; + self->cycles += 2 + 17; + continue; + + /* "rst N" is assumed for other instructions */ + default: + Q_0 + PUSH(PC); + MEMPTR = PC = DATA[0] & 56; + self->cycles += 2 + 11; + continue; + } +# endif + + /*----------------------------------------------------------. + | Interrupt Mode 1: Execute "rst 38h" | T-states: 13:733 | + |-----------------------------------------------------------| + | An internal "rst 38h" is executed. The interrupt response | + | data read from the data bus is disregarded. | + '==========================================================*/ + case 1: + Q_0 + PUSH(PC); + MEMPTR = PC = 0x38; + self->cycles += 13; + continue; + + /*---------------------------------------------------------------------. + | Interrupt Mode 2: Execute "call (i:BYTE)" | T-states: 19:73333 | + |----------------------------------------------------------------------| + | An indirect call is executed. The pointer to the ISR is loaded from | + | the memory address formed by taking the I register as the most | + | significant byte, and the interrupt response vector read from the | + | data bus as the least significant byte. | + | | + | Zilog's official documentation states that the least significant bit | + | of the interrupt response vector "must be a zero", since the address | + | formed "is used to get two adjacent bytes to form a complete 16-bit | + | service routine starting address and the addresses must always start | + | in even locations" [1]. However, Sean Young's experiments confirmed | + | that there is no such limitation [2]; any vector works whether it is | + | odd or even. | + | | + | References: | + | 1. Zilog (2005-02). Z80 "Family: CPU User Manual" v5. pp. 25,26. | + | 2. Young, Sean (2005-09-18). "Undocumented Z80 Documented, The". | + | v0.91 p. 20. | + '=====================================================================*/ + case 2: + Q_0 + PUSH(PC); + MEMPTR = PC = READ_16((zuint16)(((zuint16)I << 8) | byte)); + self->cycles += 19; /* M1(5+2w), M2(3), M3(3), M4(3), M5(3) */ + continue; + } + } + + /*----------------------------------------------------------------------. + | Special RESET Response | T-states: 4:4 | + |-----------------------------------------------------------------------| + | 3T reset pulse, low at rising edges of M1T4 & M1T1 & M1T2 | + | N.B. /MREQ low during M1T1 of opcode fetch from address 0000h | + | | + | Reference: | + | * Brewer, Tony (2014-12). "Z80 Special Reset". | + | * http://primrosebank.net/computers/z80/z80_special_reset.htm | + | * US Patent 4486827 | + | * https://worldofspectrum.org/forums/discussion/34574 | + '======================================================================*/ +# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL + if (REQUEST & Z80_REQUEST_CLEAR_PC) + { + if (self->nop != Z_NULL) (void)self->nop(CONTEXT, PC); + PC = 0; + } + + if (REQUEST & Z80_REQUEST_SPECIAL_RESET) + { + if (HALT_LINE) + { + zuint8 opcode = DATA[0] = FETCH_OPCODE(PC); + if (self->halt != Z_NULL) self->halt(CONTEXT, FALSE); + PC--; + self->cycles += instruction_table[opcode](); + } + } +# endif } - /*---------------------------------------. - | Consume memory refresh and update bits | - '---------------------------------------*/ - R++; - EI = FALSE; - - /*-----------------------------------------------. - | Execute instruction and update consumed cycles | - '-----------------------------------------------*/ - CYCLES += instruction_table[BYTE0 = READ_8(PC)](object); + R++; /* M1 */ + self->cycles += instruction_table[DATA[0] = FETCH_OPCODE(PC)](self); } - /*---------------. - | Restore R7 bit | - '---------------*/ - R = R_ALL; - - /*-----------------------. - | Return consumed cycles | - '-----------------------*/ - return CYCLES; + R = R_ALL; /* restore R7 bit */ + return self->cycles; /* return consumed cycles */ } -CPU_Z80_API void z80_nmi(Z80 *object) {NMI = TRUE ;} -CPU_Z80_API void z80_int(Z80 *object, zboolean state) {INT = state;} - - -/* MARK: - ABI */ - -#ifdef CPU_Z80_WITH_ABI - - static void will_read_state(Z80 *object) {R = R_ALL;} - static void did_write_state(Z80 *object) {R7 = R; } - - static ZCPUEmulatorExport const exports[7] = { - {Z_EMULATOR_FUNCTION_POWER, {(void (*)(void))z80_power }}, - {Z_EMULATOR_FUNCTION_RESET, {(void (*)(void))z80_reset }}, - {Z_EMULATOR_FUNCTION_RUN, {(void (*)(void))z80_run }}, - {Z_EMULATOR_FUNCTION_WILL_READ_STATE, {(void (*)(void))will_read_state}}, - {Z_EMULATOR_FUNCTION_DID_WRITE_STATE, {(void (*)(void))did_write_state}}, - {Z_EMULATOR_FUNCTION_NMI, {(void (*)(void))z80_nmi }}, - {Z_EMULATOR_FUNCTION_IRQ, {(void (*)(void))z80_int }} - }; - - static ZCPUEmulatorInstanceImport const instance_imports[6] = { - {Z_EMULATOR_FUNCTION_READ_8BIT, O(read )}, - {Z_EMULATOR_FUNCTION_WRITE_8BIT, O(write )}, - {Z_EMULATOR_FUNCTION_IN_8BIT, O(in )}, - {Z_EMULATOR_FUNCTION_OUT_8BIT, O(out )}, - {Z_EMULATOR_FUNCTION_IRQ_DATA, O(int_data)}, - {Z_EMULATOR_FUNCTION_HALT, O(halt )} - }; - - CPU_Z80_ABI ZCPUEmulatorABI const abi_emulation_cpu_z80 = { - /* dependency_count */ 0, - /* dependencies */ NULL, - /* export_count */ 7, - /* exports */ exports, - /* instance_size */ sizeof(Z80), - /* instance_state_offset */ O(state), - /* instance_state_size */ sizeof(ZZ80State), - /* instance_import_count */ 6, - /* instance_imports */ instance_imports - }; - -#endif - -#ifdef CPU_Z80_WITH_MODULE_ABI - -# ifndef CPU_Z80_DEPENDENCIES_H -# include -# endif - - static ZModuleUnit const unit = {"Z80", "Z80", Z_VERSION(0, 1, 0), &abi_emulation_cpu_z80}; - static ZModuleDomain const domain = {"Emulation.CPU", Z_VERSION(1, 0, 0), 1, &unit}; - Z_API_WEAK_EXPORT ZModuleABI const __module_abi__ = {1, &domain}; - +#ifdef Z80_WITH_WINDOWS_DLL_MAIN + int Z_MICROSOFT_STD_CALL _DllMainCRTStartup(void *hDllHandle, unsigned long dwReason, void *lpReserved) + {return TRUE;} #endif diff --git a/sources/Z80.rc.in b/sources/Z80.rc.in new file mode 100644 index 0000000..64dc063 --- /dev/null +++ b/sources/Z80.rc.in @@ -0,0 +1,32 @@ +1 VERSIONINFO + FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,0,0 + PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,0,0 + FILEFLAGSMASK 0x3FL +#ifdef NDEBUG + FILEFLAGS 0x0L +#else + FILEFLAGS 0x1L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Manuel Sainz de Baranda y Goñi" + VALUE "FileDescription", "@PROJECT_DESCRIPTION@" + VALUE "FileVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@" + VALUE "InternalName", "@PROJECT_NAME@" + VALUE "LegalCopyright", "Copyright © 1999-2022 Manuel Sainz de Baranda y Goñi" + VALUE "OriginalFilename", "@PROJECT_NAME@.dll" + VALUE "ProductName", "@PROJECT_NAME@" + VALUE "ProductVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/sources/test-Z80.c b/sources/test-Z80.c new file mode 100644 index 0000000..2cb6cc3 --- /dev/null +++ b/sources/test-Z80.c @@ -0,0 +1,812 @@ +/* test-Z80 + ______ ______ ______ + /\___ \/\ __ \\ __ \ + ____ \/__/ /\_\ __ \\ \/\ \ ________________________________________________ +| /\_____\\_____\\_____\ | +| Zilog \/_____//_____//_____/ CPU Emulator Test | +| Copyright (C) 2021-2022 Manuel Sainz de Baranda y Goñi. | +| | +| This program 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 program 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 program. If not, see . | +| | +'=============================================================================*/ + +#ifdef TEST_Z80_WITH_ARCHIVE_EXTRACTION +# include +# include + + /*-----------------------------------------. + | Z_NULL is also defined by Zeta, so it is | + | undefined to avoid conflicts with zlib. | + '=========================================*/ +# ifdef Z_NULL +# undef Z_NULL +# endif + +# include +#endif + +#include +#include +#include +#include +#include +#include + + +/* MARK: - Constants */ + +#define OPCODE_NOP 0x00 +#define OPCODE_RET 0xC9 +#define OPCODE_HALT 0x76 +#define OPCODE_CALL_WORD 0xCD +#define OPCODE_JP_WORD 0xC3 + + +/* MARK: - Types */ + +typedef struct { + /* Name of the archive if the test is compressed; Z_NULL otherwise. */ + char const* archive_name; + + /* Name of the test program file, or the path to the file inside the + * archive if the file is compressed. */ + char const* file_path; + + /* Size of the program file. */ + zuint16 file_size; + + /* Offset of the program code inside the file. */ + zuint16 code_offset; + + /* Size of the program code. */ + zuint16 code_size; + + /* Memory address where to jump to start executing the test code. */ + zuint16 start_address; /* */ + + /* Value of the PC register once the test ends. */ + zuint16 exit_address; + + /* Format of the program file. */ + zuint8 format; + + /* Total number of lines printed by the test when it passes. */ + zuint8 lines_expected; +} Test; + +/* CP/M program in COM format. */ +#define TEST_FORMAT_CPM 0 + +/* ZX Spectrum TAP image. Different versions of the Z80 Instruction Set + * Exerciser adapted and improved by Jonathan Graham Harston and others. */ +#define TEST_FORMAT_HARSTON 1 + +/* ZX Spectrum TAP image. Tapes of the Zilog Z80 CPU Test Suite written by + * Patrik Rak. */ +#define TEST_FORMAT_RAK 2 + +/* ZX Spectrum TAP image. Z80 Test Suite, written by Mark Woodmass. */ +#define TEST_FORMAT_WOODMASS 3 + + +/* MARK: - Global Variables */ + +static Test const tests[22] = { + {"Yaze v1.14 (2004-04-23)(Cringle, Frank D.)(Sources)[!].tar.gz", "yaze-1.14/test/zexdoc.com", 8704, 0, 8704, 0x0100, 0, TEST_FORMAT_CPM, 68}, + {Z_NULL, "Z80 Documented Instruction Set Exerciser for Spectrum (2018)(Harston, Jonathan Graham)[!].tap", 8716, 91, 8624, 0x8000, 0x803D, TEST_FORMAT_HARSTON, 69}, + {"Yaze v1.14 (2004-04-23)(Cringle, Frank D.)(Sources)[!].tar.gz", "yaze-1.14/test/zexall.com", 8704, 0, 8704, 0x0100, 0, TEST_FORMAT_CPM, 68}, + {Z_NULL, "Z80 Full Instruction Set Exerciser for Spectrum (2009)(Bobrowski, Jan)[!].tap", 8656, 108, 8547, 0x8000, 0x803D, TEST_FORMAT_HARSTON, 69}, + {Z_NULL, "Z80 Full Instruction Set Exerciser for Spectrum (2011)(Bobrowski, Jan)(Narrowed to BIT Instructions)[!].tap", 8656, 108, 8547, 0x8000, 0x803D, TEST_FORMAT_HARSTON, 4}, + {Z_NULL, "Z80 Full Instruction Set Exerciser for Spectrum (2017-0x)(Harston, Jonathan Graham)[!].tap", 8704, 91, 8612, 0x8000, 0x803D, TEST_FORMAT_HARSTON, 69}, + {Z_NULL, "Z80 Full Instruction Set Exerciser for Spectrum (2018)(Harston, Jonathan Graham)[!].tap", 8716, 91, 8624, 0x8000, 0x803D, TEST_FORMAT_HARSTON, 69}, + {"Z80 Instruction Set Exerciser for Spectrum 2 v0.1 (2012-11-27)(Rak, Patrik)[!].zip", "zexall2-0.1/zexall2.tap", 9316, 87, 9228, 0x8000, 0x8040, TEST_FORMAT_HARSTON, 76}, + {"Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip", "z80test-1.0/z80full.tap", 13758, 91, 13666, 0x8000, 0x7003, TEST_FORMAT_RAK, 156}, + {"Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip", "z80test-1.0/z80doc.tap", 13758, 91, 13666, 0x8000, 0x7003, TEST_FORMAT_RAK, 156}, + {"Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip", "z80test-1.0/z80flags.tap", 13758, 91, 13666, 0x8000, 0x7003, TEST_FORMAT_RAK, 156}, + {"Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip", "z80test-1.0/z80docflags.tap", 13758, 91, 13666, 0x8000, 0x7003, TEST_FORMAT_RAK, 156}, + {"Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip", "z80test-1.0/z80ccf.tap", 14219, 91, 14127, 0x8000, 0x7003, TEST_FORMAT_RAK, 156}, + {"Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip", "z80test-1.0/z80memptr.tap", 13758, 91, 13666, 0x8000, 0x7003, TEST_FORMAT_RAK, 156}, + {"Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip", "z80test-1.2/z80full.tap", 14390, 91, 14298, 0x8000, 0x7003, TEST_FORMAT_RAK, 164}, + {"Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip", "z80test-1.2/z80doc.tap", 14390, 91, 14298, 0x8000, 0x7003, TEST_FORMAT_RAK, 164}, + {"Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip", "z80test-1.2/z80flags.tap", 14390, 91, 14298, 0x8000, 0x7003, TEST_FORMAT_RAK, 164}, + {"Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip", "z80test-1.2/z80docflags.tap", 14390, 91, 14298, 0x8000, 0x7003, TEST_FORMAT_RAK, 164}, + {"Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip", "z80test-1.2/z80ccf.tap", 14875, 91, 14783, 0x8000, 0x7003, TEST_FORMAT_RAK, 164}, + {"Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip", "z80test-1.2/z80memptr.tap", 14390, 91, 14298, 0x8000, 0x7003, TEST_FORMAT_RAK, 164}, + {Z_NULL, "Z80 Test Suite (2008)(Woodmass, Mark)[!].tap", 5573, 120, 5452, 0x8057, 0x80E6, TEST_FORMAT_WOODMASS, 50}, + {Z_NULL, "Z80 Test Suite (2008)(Woodmass, Mark)[!].tap", 5573, 120, 5452, 0x8049, 0x80E6, TEST_FORMAT_WOODMASS, 61} +}; + +static char const *const cpu_model_identifiers[4] = { + "zilog-nmos", + "zilog-cmos", + "nec-nmos", + "st-cmos" +}; + +static char const cpu_model_keys[4] = { + Z80_MODEL_ZILOG_NMOS, + Z80_MODEL_ZILOG_CMOS, + Z80_MODEL_NEC_NMOS, + Z80_MODEL_ST_CMOS +}; + +/* Instance of the Z80 Emulator. */ +static Z80 cpu; + +/* 64 KB of memory needed by the emulator. */ +static zuint8 memory[65536]; + +/* TRUE if the test is completed, or FALSE otherwise. */ +static zboolean test_completed; + +static zboolean zx_spectrum_tab; + +/* X position of the cursor inside ZX Spectrum screen paper + * (in characters). */ +static zuint zx_spectrum_column; + +/* Number of text lines printed by the current/latest test. + * It is used to know whether the test passed or produced errors. */ +static zuint lines; + +/* Address where to place a CPU trap to intercept the PRINT */ +static zuint16 print_hook_address; + +/* */ +static zuint8 in_values[2]; + +static char* path_buffer = Z_NULL; +static char** search_paths = Z_NULL; +static zuint search_path_count = 0; + + +/* MARK: - CPU Callbacks: Common */ + + +static zuint8 cpu_read(void *context, zuint16 address) + { + Z_UNUSED(context) + return memory[address]; + } + + +static zuint8 cpu_in(void *context, zuint16 port) + { + Z_UNUSED(context) + return in_values[port & 1]; + } + + +static void cpu_out(void *context, zuint16 port, zuint8 value) + {Z_UNUSED(context) Z_UNUSED(port) Z_UNUSED(value)} + + +static void cpu_halt(void *context, zboolean state) + { + Z_UNUSED(context) Z_UNUSED(state) + cpu.cycles = Z80_CYCLE_LIMIT; + test_completed = TRUE; + } + + +/* MARK: - CPU Callbacks: CP/M */ + + +static void cpm_cpu_write(void *context, zuint16 address, zuint8 value) + { + Z_UNUSED(context) + memory[address] = value; + } + + +static zuint8 cpm_cpu_hook(void *context, zuint16 address) + { + Z_UNUSED(context) + + if (address != 5) return OPCODE_NOP; + + if (Z80_C(cpu) == 2) switch (Z80_E(cpu)) + { + case 0x0D: + break; + + case 0x0A: + putchar('\n'); + lines++; + break; + + default: + putchar(Z80_E(cpu)); + } + + else if (Z80_C(cpu) == 9) + { + zuint16 i = Z80_DE(cpu); + zuint c = 0; + + while (memory[i] != '$') + { + zuint8 character; + + if (c++ > 100) + { + putchar('\n'); + fputs("Error: String to print is too long!\n", stderr); + exit(EXIT_FAILURE); + } + + switch ((character = memory[i++])) + { + case 0x0D: + break; + + case 0x0A: + putchar('\n'); + lines++; + break; + + default: + putchar(character); + } + } + } + + return OPCODE_RET; + } + + +/* MARK: - CPU Callbacks: ZX Spectrum */ + + +static void zx_spectrum_cpu_write(void *context, zuint16 address, zuint8 value) + { + Z_UNUSED(context) + if (address > 0x3FFF) memory[address] = value; + } + + +static zuint8 zx_spectrum_cpu_hook(void *context, zuint16 address) + { + Z_UNUSED(context) + + if (address != print_hook_address) return OPCODE_NOP; + + if (zx_spectrum_tab) + { + zuint c = (Z80_A(cpu) % 32) - (zx_spectrum_column % 32); + + while (c--) putchar(' '); + zx_spectrum_tab = FALSE; + } + + else switch (Z80_A(cpu)) + { + case 0x0D: /* CR */ + putchar('\n'); + lines++; + zx_spectrum_column = 0; + break; + + case 0x7F: /* © */ + printf("©"); + zx_spectrum_column++; + break; + + case 0x17: /* TAB */ + zx_spectrum_tab = TRUE; + break; + + default: + if (Z80_A(cpu) >= 32 && Z80_A(cpu) < 127) + { + putchar(Z80_A(cpu)); + zx_spectrum_column++; + } + } + + return OPCODE_RET; + } + + +/* Only needed for Woody's Z80 Test Suite. */ +static zuint8 zx_spectrum_cpu_fetch_opcode(void *context, zuint16 address) + { + Z_UNUSED(context) + + return (address == 0x0D6B /* 0D6B: THE 'CLS' COMMAND ROUTINE */ || + address == 0x1601 /* 1601: THE 'CHAN_OPEN' SUBROUTINE */ + ) + ? OPCODE_RET : memory[address]; + } + + +static char const *compose_path(char const *base_path, const char *file_path) + { + zusize base_path_size; + + if (base_path == Z_NULL) return file_path; + base_path_size = strlen(base_path); + memcpy(path_buffer, base_path, base_path_size); + path_buffer[base_path_size] = '/'; + strcpy(path_buffer + base_path_size + 1, file_path); + return path_buffer; + } + + +static zboolean load_file( + char const* path, + char const* file_path, + zuint32 file_size, + zuint16 offset, + zuint16 size, + void* buffer +) + { + zboolean status = FALSE; + FILE *file = fopen(compose_path(path, file_path), "rb"); + + if (file != Z_NULL) + { + fseek(file, 0, SEEK_END); + + if (ftell(file) == file_size) + { + fseek(file, offset, SEEK_SET); + status = fread(buffer, size, 1, file) == 1; + } + + fclose(file); + } + + return status; + } + + +static zboolean load_test(const char *path, Test const *test, void *buffer) + { +# ifdef TEST_Z80_WITH_ARCHIVE_EXTRACTION + zboolean status = load_file( + path, test->file_path, test->file_size, + test->code_offset, test->code_size, buffer); + + if (!status && test->archive_name != Z_NULL) + { + path = compose_path(path, test->archive_name); + + /* .tar.gz */ + if (strrchr(test->archive_name, '.')[1] == 'g') + { + union {zuint8 data[Z_TAR_BLOCK_SIZE]; Z_TARHeader fields;} header; + gzFile gz = gzopen(path, "rb"); + + if (gz != Z_NULL) + { + while (!gzeof(gz)) + { + char *end; + zulong file_size, block_tail_size; + + if (gzread(gz, header.data, Z_TAR_BLOCK_SIZE) != Z_TAR_BLOCK_SIZE) break; + file_size = strtoul((char *)header.fields.size, &end, 8); + + if (!strcmp(test->file_path, (char *)header.fields.name)) + { + status = + file_size == test->file_size && + gzseek(gz, test->code_offset, SEEK_CUR) != -1 && + gzread(gz, buffer, test->code_size) == test->code_size; + + break; + } + + if ( (zuint8 *)end == header.fields.size || *end || + -1 == gzseek(gz, (block_tail_size = (file_size % Z_TAR_BLOCK_SIZE)) + ? file_size + (Z_TAR_BLOCK_SIZE - block_tail_size) + : file_size, SEEK_CUR) + ) + break; + } + + gzclose(gz); + } + } + + /* .zip */ + else { + int error; + zip_t *zip = zip_open(path, ZIP_RDONLY | ZIP_CHECKCONS, &error); + + if (zip != Z_NULL) + { + zip_file_t *file; + zip_stat_t stat; + + if ( !zip_stat(zip, test->file_path, ZIP_FL_ENC_STRICT, &stat) && + (stat.valid & ZIP_STAT_SIZE) && stat.size == test->file_size && + (file = zip_fopen(zip, test->file_path, 0)) != Z_NULL + ) + { + if ( zip_fread(file, buffer, test->code_offset) == test->code_offset && + zip_fread(file, buffer, test->code_size) == test->code_size + ) + status = TRUE; + + zip_fclose(file); + } + + zip_close(zip); + } + } + } + + return status; + +# else + return load_file( + path, test->file_path, test->file_size, + test->code_offset, test->code_size, buffer); +# endif + } + + +static zboolean run_test(zuint test_index) + { + Test const *test = &tests[test_index]; + zuint16 start_address = test->start_address; + zuint i = 0; + + if (test->archive_name == Z_NULL) printf( + "[%02u] %s\n* Loading program...", + test_index, test->file_path); + + else printf( + "[%02u] %s/%s\n* Loading program...", + test_index, test->archive_name, test->file_path); + + memset(memory, 0, 65536); + + for (; i < search_path_count && + !load_test(search_paths[i], test, memory + (start_address & 0xFF00)); + i++ + ); + + if ( i == search_path_count && + !load_test(Z_NULL, test, memory + (start_address & 0xFF00)) + ) + { + puts(" ERROR\nTest skipped.\n"); + return FALSE; + } + + puts(" OK"); + z80_power(&cpu, TRUE); + + if (test->format == TEST_FORMAT_CPM) + { + cpu.fetch_opcode = cpu_read; + cpu.write = cpm_cpu_write; + cpu.hook = cpm_cpu_hook; + memory[0] = OPCODE_HALT; + memory[5] = Z80_HOOK; /* PRINT */ + } + + else { + cpu.write = zx_spectrum_cpu_write; + cpu.hook = zx_spectrum_cpu_hook; + cpu.im = 1; + cpu.i = 0x3F; + + if (test->format == TEST_FORMAT_WOODMASS) + { + printf("* Loading firmware..."); + + for ( i = 0; + i < search_path_count && + !load_file(search_paths[i], "ZX Spectrum.rom", 16384, 0, 16384, memory); + i++ + ); + + if ( i == search_path_count && + !load_file(Z_NULL, "ZX Spectrum.rom", 16384, 0, 16384, memory) + ) + { + puts(" ERROR\nTest skipped.\n"); + return FALSE; + } + + puts(" OK"); + Z80_SP(cpu) = 0x7FE8; + Z80_AF(cpu) = 0x3222; + + cpu.fetch_opcode = zx_spectrum_cpu_fetch_opcode; + + /* 0010: THE 'PRINT A CHARACTER' RESTART */ + memory[0x0010] = OPCODE_JP_WORD; /* jp PRINT */ + memory[0x0011] = 0xF2; + memory[0x0012] = 0x70; + + /* 70F2: PRINT */ + memory[print_hook_address = 0x70F2] = Z80_HOOK; + } + + else { + cpu.fetch_opcode = cpu_read; + + /* 0010: THE 'PRINT A CHARACTER' RESTART */ + memory[print_hook_address = 0x0010] = Z80_HOOK; + + /* 0D6B: THE 'CLS' COMMAND ROUTINE */ + memory[0x0D6B] = OPCODE_RET; + + /* 1601: THE 'CHAN_OPEN' SUBROUTINE */ + memory[0x1601] = OPCODE_RET; + + if (test->format == TEST_FORMAT_RAK) + { + /* 7000: START */ + memory[0x7000] = OPCODE_CALL_WORD; + memory[0x7001] = (zuint8)start_address; + memory[0x7002] = (zuint8)(start_address >> 8); + /*memory[0x7003] = HALT;*/ + start_address = 0x7000; + } + } + } + + memory[test->exit_address] = OPCODE_HALT; + Z80_PC(cpu) = start_address; + lines = 0; + zx_spectrum_column = 0; + zx_spectrum_tab = FALSE; + test_completed = FALSE; + + puts("* Running program...\n"); + + do +# ifdef TEST_Z80_WITH_EXECUTE + z80_execute(&cpu, Z80_CYCLE_LIMIT); +# else + z80_run(&cpu, Z80_CYCLE_LIMIT); +# endif + while (!test_completed); + + puts(test->format == TEST_FORMAT_RAK ? "" : "\n"); + return lines == test->lines_expected; + } + + +static zboolean is_option( + char const* string, + char const* short_option, + char const* long_option +) + { + return !strcmp(string, short_option) || + !strcmp(string, long_option); + } + + +static zboolean byte_value(char const* string, zuint8 maximum_value, zuint8 *byte) + { + char *end; + zulong value = strtoul(string, &end, 0); + + if (end == string || *end || value > maximum_value) return FALSE; + if (byte != Z_NULL) *byte = (zuint8)value; + return TRUE; + } + + +int main(int argc, char **argv) + { + zboolean all = FALSE; + zuint32 tests_run = 0; + zusize longest_search_path_size = 0; + int ii, i = 0, status = 0; + + cpu.options = Z80_MODEL_ZILOG_NMOS; + in_values[0] = 191; + in_values[1] = 255; + + while (++i < argc && *argv[i] == '-') + { + if (is_option(argv[i], "-h", "--help")) + { + puts( "Usage: test-Z80 [options] (--all | ...)\n" + "\n" + "Options:\n" + " -a, --all Run all tests.\n" + " -h, --help Show this help message.\n" + " -e, --in-even Set the 8-bit value to be read from even I/O ports.\n" + " -o, --in-odd Set the 8-bit value to be read from odd I/O ports.\n" + " -m, --model Specify the CPU model to emulate.\n" + " -p, --path Specify a directory where to look for test files.\n" + " -v, --version Show version and copyright.\n" + "\n" + "CPU models:\n" + " zilog-nmos Zilog NMOS (default)\n" + " zilog-cmos Zilog CMOS\n" + " nec-nmos NEC NMOS\n" + " st-cmos SGS-Thomson CMOS\n" + "\n" + "Tests:\n" + " Versions of the Z80 Documented Instruction Set Exerciser:\n" + " 00 CP/M ~ Cringle, Frank D. (2004-04-23).\n" + " 01 ZX Spectrum ~ Harston, Jonathan Graham (2018).\n" + " Versions of the Z80 Full Instruction Set Exerciser:\n" + " 02 CP/M ~ Cringle, Frank D. (2004-04-23).\n" + " 03 ZX Spectrum ~ Bobrowski, Jan (2009).\n" + " 04 ZX Spectrum ~ Bobrowski, Jan (2011). Narrowed to BIT instructions.\n" + " 05 ZX Spectrum ~ Harston, Jonathan Graham (2017).\n" + " 06 ZX Spectrum ~ Harston, Jonathan Graham (2018).\n" + " 07 ZX Spectrum ~ Rak, Patrik (2012-11-27).\n" + " Zilog Z80 CPU Test Suite v1.0 ~ ZX Spectrum ~ Rak, Patrik (2012-12-08):\n" + " 08 Tests all flags and registers.\n" + " 09 Tests all registers, but only officially documented flags.\n" + " 10 Tests all flags, ignores registers.\n" + " 11 Tests documented flags only, ignores registers.\n" + " 12 Tests all flags after executing CCF after each instruction tested.\n" + " 13 Tests all flags after executing BIT N,(HL) after each instruction tested.\n" + " Zilog Z80 CPU Test Suite v1.2 ~ ZX Spectrum ~ Rak, Patrik (2022-01-26):\n" + " 14 Tests all flags and registers.\n" + " 15 Tests all registers, but only officially documented flags.\n" + " 16 Tests all flags, ignores registers.\n" + " 17 Tests documented flags only, ignores registers.\n" + " 18 Tests all flags after executing CCF after each instruction tested.\n" + " 19 Tests all flags after executing BIT N,(HL) after each instruction tested.\n" + " Z80 Test Suite ~ ZX Spectrum ~ Woodmass, Mark (2008):\n" + " 20 Tests flags.\n" + " 21 Tests MEMPTR.\n" + "\n" + "Email bug reports and questions to \n" + "Open issues at "); + + goto exit_without_error; + } + + else if (is_option(argv[i], "-v", "--version")) + { + puts( "test-Z80 v" Z80_LIBRARY_VERSION_STRING "\n" + "Copyright (C) 2021-2022 Manuel Sainz de Baranda y Goñi.\n" + "Released under the terms of the GNU General Public License v3."); + + goto exit_without_error; + } + + else if (is_option(argv[i], "-e", "--in-even")) + { + if (++i == argc) goto incomplete_option; + if (!byte_value(argv[i], 255, &in_values[0])) goto invalid_io_value; + } + + else if (is_option(argv[i], "-o", "--in-odd")) + { + if (++i == argc) goto incomplete_option; + if (!byte_value(argv[i], 255, &in_values[1])) goto invalid_io_value; + } + + else if (is_option(argv[i], "-m", "--model")) + { + if (++i == argc) goto incomplete_option; + for (ii = 0; ii < 4; ii++) if (!strcmp(argv[i], cpu_model_identifiers[ii])) break; + + if (ii == 4) + { + fprintf(stderr, "Invalid CPU model: '%s'\n", argv[i]); + goto bad_syntax; + } + + cpu.options = cpu_model_keys[ii]; + } + + else if (is_option(argv[i], "-p", "--path")) + { + zusize s; + char **p; + + if (++i == argc || !(s = strlen(argv[i]))) goto incomplete_option; + if (s > longest_search_path_size) longest_search_path_size = s; + + if ((p = realloc(search_paths, (search_path_count + 1) * sizeof(char *))) == Z_NULL) + { + fputs("Error: Not enough memory available.", stderr); + goto exit_with_error; + } + + search_paths = p; + search_paths[search_path_count++] = argv[i]; + } + + else if (is_option(argv[i], "-a", "--all")) + all = TRUE; + + else { + fprintf(stderr, "Invalid option: '%s'\n", argv[i]); + goto bad_syntax; + } + } + + if (i == argc && !all) + { + fputs("No test specified.\n", stderr); + goto bad_syntax; + } + + if (search_path_count) path_buffer = malloc(longest_search_path_size + 110); + + /* Configure stdout as unbuffered. */ + setvbuf(stdout, Z_NULL, _IONBF, 0); + + /* Configure the Z80 emulator */ + cpu.context = Z_NULL; + cpu.fetch = + cpu.read = + cpu.nop = cpu_read; + cpu.in = cpu_in; + cpu.out = cpu_out; + cpu.halt = cpu_halt; + cpu.nmia = + cpu.inta = + cpu.int_fetch = Z_NULL; + cpu.reset = Z_NULL; + cpu.ld_i_a = + cpu.ld_r_a = + cpu.reti = + cpu.retn = Z_NULL; + + /* Ensure that all specified test numbers are valid. */ + for (ii = i; i < argc; i++) + { + char const *string = argv[i]; + char *end; + + if (strtoul(string, &end, 10) >= Z_ARRAY_SIZE(tests) || end == string || *end) + { + fprintf(stderr, "Invalid test number: '%s'\n", string); + goto bad_syntax; + } + } + + while (ii < argc) + { + tests_run |= Z_UINT32(1) << (i = atoi(argv[ii++])); + if (!run_test(i)) status = -1; + } + + if (all) for (i = 0; i < Z_ARRAY_SIZE(tests); i++) + if (!(tests_run & (Z_UINT32(1) << i)) && !run_test(i)) status = -1; + + exit_without_error: + free(search_paths); + free(path_buffer); + return status; + + incomplete_option: + fprintf(stderr, "Incomplete option: '%s'\n", argv[i - 1]); + goto bad_syntax; + + invalid_io_value: + fprintf(stderr, "Invalid I/O value: '%s'\n", argv[i]); + + bad_syntax: + fputs("Type 'test-Z80 -h' for help.\n", stderr); + + exit_with_error: + free(search_paths); + free(path_buffer); + return -1; + } + + +/* test-Z80.c EOF */ diff --git a/support/Z80.pc.in b/support/Z80.pc.in new file mode 100644 index 0000000..d28e086 --- /dev/null +++ b/support/Z80.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@. +URL: https://zxe.io/software/@PROJECT_NAME@ +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -l@PROJECT_NAME@ +Cflags: -I${includedir} diff --git a/support/Z80Config.cmake.in b/support/Z80Config.cmake.in new file mode 100644 index 0000000..1222622 --- /dev/null +++ b/support/Z80Config.cmake.in @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.19) + +set(@PROJECT_NAME@_known_components Static Shared) +set(@PROJECT_NAME@_component_Static NO) +set(@PROJECT_NAME@_component_Shared NO) + +foreach (@PROJECT_NAME@_component IN LISTS ${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS) + if (@PROJECT_NAME@_component IN_LIST @PROJECT_NAME@_known_components) + set(@PROJECT_NAME@_component_${@PROJECT_NAME@_component} YES) + else() + set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE + "@PROJECT_NAME@ does not recognize component `${@PROJECT_NAME@_component}`.") + set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE) + return() + endif() +endforeach() + +if (@PROJECT_NAME@_component_Static AND @PROJECT_NAME@_component_Shared) + set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE + "@PROJECT_NAME@ `Static` and `Shared` components are mutually exclusive.") + set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE) + return() +endif() + +set(@PROJECT_NAME@_Static_targets "${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@StaticTargets.cmake") +set(@PROJECT_NAME@_Shared_targets "${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@SharedTargets.cmake") + +macro(@PROJECT_NAME@_load_targets type) + if (NOT EXISTS "${@PROJECT_NAME@_${type}_targets}") + set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE + "@PROJECT_NAME@ `${type}` libraries were requested but not found.") + set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE) + return() + endif() + + include(CMakeFindDependencyMacro) + find_dependency(Zeta) + include("${@PROJECT_NAME@_${type}_targets}") +endmacro() + +if(@PROJECT_NAME@_component_Static) + @PROJECT_NAME@_load_targets(Static) +elseif(@PROJECT_NAME@_component_Shared) + @PROJECT_NAME@_load_targets(Shared) +elseif(DEFINED @PROJECT_NAME@_SHARED_LIBS AND @PROJECT_NAME@_SHARED_LIBS) + @PROJECT_NAME@_load_targets(Shared) +elseif(DEFINED @PROJECT_NAME@_SHARED_LIBS AND NOT @PROJECT_NAME@_SHARED_LIBS) + @PROJECT_NAME@_load_targets(Static) +elseif(BUILD_SHARED_LIBS) + if(EXISTS "${@PROJECT_NAME@_Shared_targets}") + @PROJECT_NAME@_load_targets(Shared) + else() + @PROJECT_NAME@_load_targets(Static) + endif() +else() + if(EXISTS "${@PROJECT_NAME@_Static_targets}") + @PROJECT_NAME@_load_targets(Static) + else() + @PROJECT_NAME@_load_targets(Shared) + endif() +endif() diff --git a/support/firmware.sha512sum b/support/firmware.sha512sum new file mode 100644 index 0000000..1020058 --- /dev/null +++ b/support/firmware.sha512sum @@ -0,0 +1 @@ +D5394DC919B1820E1956CB227E8DFFDA7887CB3D3A78E802EA690EE6B47EDF35DB3B29EA72E5E824B32B36FB18D79C72688AB7469A3D383A39EA01BA49AFC261 ZX Spectrum.rom diff --git a/support/software.sha512sum b/support/software.sha512sum new file mode 100644 index 0000000..aeb4116 --- /dev/null +++ b/support/software.sha512sum @@ -0,0 +1,10 @@ +2254439627B0A513C2E87266C5F57573C1607744FDD72C634329F8F79CAEAE2C4475DDD4C5945434DBF8F2B189BA0BEBA12ADA3C12A34FAE2DDD44E8DDAA528B POSIX/Yaze v1.14 (2004-04-23)(Cringle, Frank D.)(Sources)[!].tar.gz +FD6DF9FF65ED2F6502DE8892601CF803FBBB1D91F3BE43CF314464AADCB6B05E635F1A83A25C72E309EABEADC69975055EFDA6085A9D95B4815C7512801AC00E ZX Spectrum/Z80 Documented Instruction Set Exerciser for Spectrum (2018)(Harston, Jonathan Graham)[!].tap +C8BC407C874D305CA9BB2647A83D726ACB6969FE7525EEAB0BA2C3270FFF94B3D90CFA186F3059F468CF07F651FD8D4D9465C32989B33A5F4FEF1FABCC2DBA23 ZX Spectrum/Z80 Full Instruction Set Exerciser for Spectrum (2009)(Bobrowski, Jan)[!].tap +E4CD187FA712418863A85BD9FF2C2CC11717C0CCE4510686E3BBF367F38DB0A987BADC7CBC6AD22E88859587747FA818CCB04858B163502A870F45847FF9FE69 ZX Spectrum/Z80 Full Instruction Set Exerciser for Spectrum (2011)(Bobrowski, Jan)(Narrowed to BIT Instructions)[!].tap +CD9B552F3F56298FA7EC75B4B7A4A115CA326CBD3B9DED2BE01EC31534B572216E7A95B7AC5EF920E5E28343CE048F33DC163F47B5CE8BB8DED5232CF41F6D91 ZX Spectrum/Z80 Full Instruction Set Exerciser for Spectrum (2017-0x)(Harston, Jonathan Graham)[!].tap +3B4D93D5BEE0F58E187350E5EA90A92EA7F4874F5A7AABA91A944D92DA63B8EBC0B4B9F4AED264863B256C761307EA4EF3D5FD4A2896469A117EF1C676E6F2A6 ZX Spectrum/Z80 Full Instruction Set Exerciser for Spectrum (2018)(Harston, Jonathan Graham)[!].tap +3F8306CC7EDB49DEFC8C2D8A792D970C4681983D46A0B37982432531D5F56F8CDC1850C41038C3EA1C5AF2C4E98994BBD57C039496C783D9D9626F2D2C0B88B2 ZX Spectrum/Z80 Instruction Set Exerciser for Spectrum 2 v0.1 (2012-11-27)(Rak, Patrik)[!].zip +668542FC9965199659F03044F16A9943223A190B8D1BD9CC4699F58B4FF99FA304B8B3281DBD8B179C9AE0B047DA82D741D187F619B5293241BC1185657F7687 ZX Spectrum/Z80 Test Suite (2008)(Woodmass, Mark)[!].tap +28C1F2BB5E523E4F03A23A5BBB70136F01BC06E12C2EC2AF8074FBF4C31218C9E8235BEEACC8A892128204738EC4C0548ACC0C825D6EA30BD393B1AA28C734F6 ZX Spectrum/Zilog Z80 CPU Test Suite v1.0 (2012-12-08)(Rak, Patrik)[!].zip +5DD2F8335C4543D5D920318A526024CD7442E0CC15E208622D466A0CF57579908652A5301FD286A9AC61BA73341E4D8ACA1583635FC9D4204693FCB2C7364386 ZX Spectrum/Zilog Z80 CPU Test Suite v1.2 (2022-01-26)(Rak, Patrik)[!].zip