From 314edff52dc3372c07184c12a89f82dd56ee4464 Mon Sep 17 00:00:00 2001 From: redcode Date: Sun, 24 Jul 2022 21:24:18 +0200 Subject: [PATCH] Special RESET WIP; Removed the normal RESET signal; Documentation/comments; Other improvements. --- .github/workflows/library-ci.yml | 6 +- API/Z80.h | 66 +++----- CMakeLists.txt | 16 +- HISTORY | 50 +++--- README | 14 +- README.md | 12 +- documentation/APIReference.rst | 1 - documentation/Installation.rst | 10 +- documentation/Integration.rst | 3 +- documentation/VersionHistory.rst | 47 +++--- sources/Z80.c | 265 +++++++++++++------------------ sources/test-Z80.c | 1 - 12 files changed, 198 insertions(+), 293 deletions(-) diff --git a/.github/workflows/library-ci.yml b/.github/workflows/library-ci.yml index 2c7750a..d577b2e 100644 --- a/.github/workflows/library-ci.yml +++ b/.github/workflows/library-ci.yml @@ -33,8 +33,7 @@ env: 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_SPECIAL_RESET: NO Z80_WITH_UNOFFICIAL_RETI: NO Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG: YES @@ -79,8 +78,7 @@ jobs: -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_SPECIAL_RESET=${{env.Z80_WITH_SPECIAL_RESET}} -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}} diff --git a/API/Z80.h b/API/Z80.h index 5270020..1705180 100644 --- a/API/Z80.h +++ b/API/Z80.h @@ -147,11 +147,11 @@ typedef zusize (* Z80Reset)(void *context, zuint16 address); typedef struct { - /** @brief Number of clock cycles already executed. */ + /** @brief Number of clock cycles (T-states) already executed. */ zusize cycles; - /** @brief Maximum number of clock cycles to be executed. */ + /** @brief Maximum number of clock cycles (T-states) to be executed. */ zusize cycle_limit; @@ -227,14 +227,13 @@ typedef struct { /** @brief Invoked to perform an opcode fetch that corresponds to an * internal NOP. * - * This callback indicates the beginning of an internal NOP, which is - * an opcode fetch M-cycle that is generated in two situations: + * This callback indicates the beginning of an opcode fetch M-cycle + * that is generated in the following cases: * * - During the HALT state, the CPU repeatedly executes an internal NOP * that fetches the next opcode after @c halt without incrementing * the PC register. This opcode is read again and again until an exit * condition occurs. - * * - After detecting a special reset signal, the CPU completes the * ongoing instruction and then executes an internal NOP during which * it zeroes the PC register. @@ -273,45 +272,50 @@ typedef struct { Z80Read int_fetch; - /** @brief Callback invoked to query the duration of a RESET signal. - * - * @attention This callback is optional and must be set to @c Z_NULL - * when not used. */ - - Z80Reset reset; - /** @brief Invoked when an ld i,a instruction is fetched. * - * @attention This callback is optional and must be set to @c Z_NULL - * when not used. */ + * This callback is invoked before the ld i,a instruction + * modifies the I register. + * + * This callback is optional and must be set to @c Z_NULL when not + * used. */ Z80Notify ld_i_a; /** @brief Invoked when an ld r,a instruction is fetched. * - * @attention This callback is optional and must be set to @c Z_NULL - * when not used. */ + * This callback is invoked before the ld r,a instruction + * modifies the R register. + * + * This callback is optional and must be set to @c Z_NULL when not + * used. */ Z80Notify ld_r_a; /** @brief Invoked when a @c reti instruction is fetched. * - * @attention This callback is optional and must be set to @c Z_NULL - * when not used. */ + * This callback is invoked before executing a @c reti instruction. + * + * This callback is optional and must be set to @c Z_NULL when not + * used. */ Z80Notify reti; /** @brief Callback invoked when a @c retn instruction is fetched. * - * @attention This callback is optional and must be set to @c Z_NULL - * when not used. */ + * This callback is invoked before executing a @c retn instruction. + * + * This callback is optional and must be set to @c Z_NULL when not + * used. */ Z80Notify retn; /** @brief Invoked when a trap is fecthed. * - * @attention This callback is optional and must be set to @c Z_NULL - * when not used. */ + * This callback is invoked before the @c retn instruction is executed. + * + * This callback is optional and must be set to @c Z_NULL when not + * used. */ Z80Read hook; @@ -469,10 +473,6 @@ typedef struct { #define Z80_MODEL_ST_CMOS \ (Z80_OPTION_LD_A_IR_BUG | Z80_OPTION_YQ) -/** @brief @ref Z80 request flag anouncing an incoming RESET signal. */ - -#define Z80_REQUEST_RESET 3 - /** @brief @ref Z80 request flag that prevents the NMI signal from being * accepted. */ @@ -483,16 +483,11 @@ typedef struct { #define Z80_REQUEST_NMI 8 -#define Z80_REQUEST_CLEAR_PC 16 - /** @brief @ref Z80 request flag anouncing an incoming special RESET signal. */ #define Z80_REQUEST_SPECIAL_RESET 32 #define Z80_REQUEST_INT 64 -#define Z80_REQUEST_ANY_RESET 35 -#define Z80_REQUEST_RESET 3 -#define Z80_REQUEST_INTERRUPT 5 /** @brief @ref Z80 resume code that is set when the emulator runs out of clock * cycles during the HALT state. */ @@ -511,9 +506,6 @@ typedef struct { #define Z80_RESUME_IM0_XY 3 -#define Z80_RESUME_SPECIAL_RESET_XY 5 -#define Z80_RESUME_SPECIAL_RESET_NOP 6 - /** @brief Accesses the MEMPTR register of a @ref Z80 @p object. */ #define Z80_MEMPTR(object) (object).memptr.uint16_value @@ -699,12 +691,6 @@ Z80_API void z80_power(Z80 *self, zboolean state); Z80_API void z80_instant_reset(Z80 *self); -/** @brief Sends a normal RESET signal to a @ref Z80 object. - * - * @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 @ref Z80 object. * * @sa diff --git a/CMakeLists.txt b/CMakeLists.txt index 5599d9f..4be7f51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,12 +91,8 @@ 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." +option(${PROJECT_NAME}_WITH_SPECIAL_RESET +"Build the implementation of the special RESET." NO) option(${PROJECT_NAME}_WITH_UNOFFICIAL_RETI @@ -184,12 +180,8 @@ 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) +if(${PROJECT_NAME}_WITH_SPECIAL_RESET) + target_compile_definitions(${PROJECT_NAME} PRIVATE Z80_WITH_SPECIAL_RESET) endif() if(${PROJECT_NAME}_WITH_UNOFFICIAL_RETI) diff --git a/HISTORY b/HISTORY index f5fa674..a1738c6 100644 --- a/HISTORY +++ b/HISTORY @@ -52,50 +52,50 @@ Changes: 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 for emitting them. The - old `z80_reset` function is now called `z80_instant_reset`. -35. Added the `Z80::fetch_opcode` and `Z80::fetch` callbacks for performing +34. Renamed the `z80_reset` function to `z80_instant_reset`. +35. Added optional emulation of the special RESET, along with the new + `z80_special_reset` function. +36. Added the `Z80::fetch_opcode` and `Z80::fetch` callbacks for performing opcode fetch operations and memory read operations on instruction data respectively. -36. Added the `Z80::nop` callback for performing disregarded opcode fetch +37. Added the `Z80::nop` callback for performing disregarded opcode fetch operations during internal NOP M-cycles. -37. Added emulation of the NMI acknowledge M-cycle through the new `Z80::nmia` +38. Added emulation of the NMI acknowledge M-cycle through the new `Z80::nmia` callback. -38. Added emulation of the INT acknowledge M-cycle through the new `Z80::inta` +39. Added emulation of the INT acknowledge M-cycle through the new `Z80::inta` callback, which replaces `Z80::int_data`. -39. Added optional full emulation of the interrupt mode 0, along with the new +40. Added optional full emulation of the interrupt mode 0, along with the new `Z80::int_fetch` callback for performing 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. -40. Added four callbacks for notifying the execution of important instructions: +41. Added four callbacks for notifying the execution of important instructions: `Z80::ld_i_a`, `Z80::ld_r_a`, `Z80::reti` and `Z80::retn`. -41. Added hooking functionality through the `ld h,h` instruction and the new +42. Added hooking functionality through the `ld h,h` instruction and the new `Z80::hook` callback. -42. Added the `Z80::illegal` callback for delegating the emulation of illegal +43. Added the `Z80::illegal` callback for delegating the emulation of illegal instructions. -43. Added accurate flag behavior in the following instructions: `ldir`, `lddr`, +44. Added accurate flag behavior in the following instructions: `ldir`, `lddr`, `cpir`, `cpdr`, `inir`, `indr`, `otir` and `otdr`. -44. Added emulation of the interrupt acceptance deferral that occurs during the +45. Added emulation of the interrupt acceptance deferral that occurs during the `reti` and `retn` instructions. -45. Added MEMPTR emulation. The `bit N,(hl)` instruction now produces a correct +46. Added MEMPTR emulation. The `bit N,(hl)` instruction now produces a correct value of F. -46. Added optional emulation of the Q "register". If enabled at compile-time, +47. Added optional emulation of the Q "register". If enabled at compile-time, the `ccf` and `scf` instructions produce a correct value of F. -47. Added emulation options that can be configured at runtime. -48. Added emulation of the `out (c),255` instruction (Zilog Z80 CMOS). -49. Added optional emulation of the bug affecting the `ld a,{i|r}` instructions +48. Added emulation options that can be configured at runtime. +49. Added emulation of the `out (c),255` instruction (Zilog Z80 CMOS). +50. 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. -50. Removed `Z80::state`. Replaced with individual members for the registers, +51. Removed `Z80::state`. Replaced with individual members for the registers, the interrupt enable flip-flops and the interrupt mode. -51. Removed the superfluous EI flag. The previous opcode is checked instead, +52. Removed the superfluous EI flag. The previous opcode is checked instead, which is faster and makes the `Z80` object smaller. -52. Removed all module-related stuff. -53. Optimizations in flag computation and condition evaluation. -54. New source code comments and improvements to existing ones. -55. Improved code aesthetics. -56. Other improvements, optimizations and minor changes. +53. Removed all module-related stuff. +54. Optimizations in flag computation and condition evaluation. +55. New source code comments and improvements to existing ones. +56. Improved code aesthetics. +57. Other improvements, optimizations and minor changes. Z80 v0.1 (2018-11-10) diff --git a/README b/README index 4f5971e..4e2d692 100644 --- a/README +++ b/README @@ -176,12 +176,8 @@ library by predefining macros that enable optional implementations: 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. + -DZ80_WITH_SPECIAL_RESET=(YES|NO) + Build the implementation of the special RESET. The default is `NO`. -DZ80_WITH_UNOFFICIAL_RETI=(YES|NO) @@ -201,7 +197,6 @@ library: -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 @@ -282,8 +277,7 @@ 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_SPECIAL_RESET #define Z80_WITH_UNOFFICIAL_RETI #define Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG @@ -312,4 +306,4 @@ with this library. If not, see . -------------------------------------------------------------------------------- -Last update: 2022-07-03 README EOF +Last update: 2022-07-16 README EOF diff --git a/README.md b/README.md index 306b83d..fe7363a 100644 --- a/README.md +++ b/README.md @@ -432,12 +432,8 @@ Package-specific options are prefixed with `Z80_` and can be divided into two gr 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. +* **`-DZ80_WITH_SPECIAL_RESET=(YES|NO)`** + Build the implementation of the [special RESET](http://www.primrosebank.net/computers/z80/z80_special_reset.htm). The default is `NO`. * **`-DZ80_WITH_UNOFFICIAL_RETI=(YES|NO)`** @@ -454,7 +450,6 @@ Package maintainers should use at least the following options for the shared lib -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 ``` @@ -539,8 +534,7 @@ There are several macros that can be used to configure the source code of the li * **[`#define Z80_WITH_EXECUTE`](#option_z80_with_execute)** * **[`#define Z80_WITH_FULL_IM0`](#option_z80_with_full_im0)** * **[`#define Z80_WITH_Q`](#option_z80_with_q)** -* **[`#define Z80_WITH_RESET_SIGNAL`](#option_z80_with_reset_signal)** -* **[`#define Z80_WITH_SPECIAL_RESET_SIGNAL`](#option_z80_with_special_reset_signal)** +* **[`#define Z80_WITH_SPECIAL_RESET`](#option_z80_with_special_reset)** * **[`#define Z80_WITH_UNOFFICIAL_RETI`](#option_z80_with_unofficial_reti)** * **[`#define Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG`](#option_z80_with_zilog_nmos_ld_a_ir_bug)** diff --git a/documentation/APIReference.rst b/documentation/APIReference.rst index 37e5c0e..3cf8863 100644 --- a/documentation/APIReference.rst +++ b/documentation/APIReference.rst @@ -116,6 +116,5 @@ Functions .. doxygenfunction:: z80_power .. doxygenfunction:: z80_r .. doxygenfunction:: z80_refresh_address -.. doxygenfunction:: z80_reset .. doxygenfunction:: z80_run .. doxygenfunction:: z80_special_reset diff --git a/documentation/Installation.rst b/documentation/Installation.rst index ac3d0f1..d0f8038 100644 --- a/documentation/Installation.rst +++ b/documentation/Installation.rst @@ -132,14 +132,9 @@ The second group of package-specific options configures the source code of the l Build the implementation of the `Q "register" `_. |br| |nl| The default is ``NO``. -.. option:: -DZ80_WITH_RESET_SIGNAL=(YES|NO) +.. option:: -DZ80_WITH_SPECIAL_RESET=(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| + Build the implementation of the `special RESET `_. |br| |nl| The default is ``NO``. .. option:: -DZ80_WITH_UNOFFICIAL_RETI=(YES|NO) @@ -159,7 +154,6 @@ Package maintainers should use at least the following options for the shared lib -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: diff --git a/documentation/Integration.rst b/documentation/Integration.rst index 2001728..d3fc0c8 100644 --- a/documentation/Integration.rst +++ b/documentation/Integration.rst @@ -72,8 +72,7 @@ The second group of package-specific options, explained in the "Installation" se .. 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_SPECIAL_RESET .. c:macro:: Z80_WITH_UNOFFICIAL_RETI .. c:macro:: Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG diff --git a/documentation/VersionHistory.rst b/documentation/VersionHistory.rst index 90d8081..1202679 100644 --- a/documentation/VersionHistory.rst +++ b/documentation/VersionHistory.rst @@ -42,29 +42,30 @@ Changes: 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 for emitting them. The old ``z80_reset`` function is now called ``z80_instant_reset``. -35. Added the ``Z80::fetch_opcode`` and ``Z80::fetch`` callbacks for performing opcode fetch operations and memory read operations on instruction data respectively. -36. Added the ``Z80::nop`` callback for performing disregarded opcode fetch operations during internal NOP M-cycles. -37. Added emulation of the NMI acknowledge M-cycle through the new ``Z80::nmia`` callback. -38. Added emulation of the INT acknowledge M-cycle through the new ``Z80::inta`` callback, which replaces ``Z80::int_data``. -39. Added optional full emulation of the interrupt mode 0, along with the new ``Z80::int_fetch`` callback for performing 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. -40. Added four callbacks for notifying the execution of important instructions: ``Z80::ld_i_a``, ``Z80::ld_r_a``, ``Z80::reti`` and ``Z80::retn``. -41. Added hooking functionality through the ``ld h,h`` instruction and the new ``Z80::hook`` callback. -42. Added the ``Z80::illegal`` callback for delegating the emulation of illegal instructions. -43. Added accurate flag behavior in the following instructions: ``ldir``, ``lddr``, ``cpir``, ``cpdr``, ``inir``, ``indr``, ``otir`` and ``otdr``. -44. Added emulation of the interrupt acceptance deferral that occurs during the ``reti`` and ``retn`` instructions. -45. Added MEMPTR emulation. The ``bit N,(hl)`` instruction now produces a correct value of F. -46. Added optional emulation of the Q "register". If enabled at compile-time, the ``ccf`` and ``scf`` instructions produce a correct value of F. -47. Added emulation options that can be configured at runtime. -48. Added emulation of the ``out (c),255`` instruction (Zilog Z80 CMOS). -49. 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. -50. Removed ``Z80::state``. Replaced with individual members for the registers, the interrupt enable flip-flops and the interrupt mode. -51. Removed the superfluous EI flag. The previous opcode is checked instead, which is faster and makes the ``Z80`` object smaller. -52. Removed all module-related stuff. -53. Optimizations in flag computation and condition evaluation. -54. New source code comments and improvements to existing ones. -55. Improved code aesthetics. -56. Other improvements, optimizations and minor changes. +34. Renamed the ``z80_reset`` function to ``z80_instant_reset``. +35. Added optional emulation of the special RESET, along with the new ``z80_special_reset`` function. +36. Added the ``Z80::fetch_opcode`` and ``Z80::fetch`` callbacks for performing opcode fetch operations and memory read operations on instruction data respectively. +37. Added the ``Z80::nop`` callback for performing disregarded opcode fetch operations during internal NOP M-cycles. +38. Added emulation of the NMI acknowledge M-cycle through the new ``Z80::nmia`` callback. +39. Added emulation of the INT acknowledge M-cycle through the new ``Z80::inta`` callback, which replaces ``Z80::int_data``. +40. Added optional full emulation of the interrupt mode 0, along with the new ``Z80::int_fetch`` callback for performing 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. +41. Added four callbacks for notifying the execution of important instructions: ``Z80::ld_i_a``, ``Z80::ld_r_a``, ``Z80::reti`` and ``Z80::retn``. +42. Added hooking functionality through the ``ld h,h`` instruction and the new ``Z80::hook`` callback. +43. Added the ``Z80::illegal`` callback for delegating the emulation of illegal instructions. +44. Added accurate flag behavior in the following instructions: ``ldir``, ``lddr``, ``cpir``, ``cpdr``, ``inir``, ``indr``, ``otir`` and ``otdr``. +45. Added emulation of the interrupt acceptance deferral that occurs during the ``reti`` and ``retn`` instructions. +46. Added MEMPTR emulation. The ``bit N,(hl)`` instruction now produces a correct value of F. +47. Added optional emulation of the Q "register". If enabled at compile-time, the ``ccf`` and ``scf`` instructions produce a correct value of F. +48. Added emulation options that can be configured at runtime. +49. Added emulation of the ``out (c),255`` instruction (Zilog Z80 CMOS). +50. 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. +51. Removed ``Z80::state``. Replaced with individual members for the registers, the interrupt enable flip-flops and the interrupt mode. +52. Removed the superfluous EI flag. The previous opcode is checked instead, which is faster and makes the ``Z80`` object smaller. +53. Removed all module-related stuff. +54. Optimizations in flag computation and condition evaluation. +55. New source code comments and improvements to existing ones. +56. Improved code aesthetics. +57. Other improvements, optimizations and minor changes. v0.1 (2018-11-10) ================= diff --git a/sources/Z80.c b/sources/Z80.c index 779dc85..90b7302 100644 --- a/sources/Z80.c +++ b/sources/Z80.c @@ -957,7 +957,7 @@ static Z_INLINE zuint8 m(Z80 *self, zuint8 offset, zuint8 value) #define OTXR(hl_operator, memptr_operator) \ zuint8 out = READ(HL hl_operator); \ - zuint8 nf = (out & 128) >> 6; \ + zuint8 nf = (out >> 6) & NF; \ zuint t = (zuint)out + L; \ zuint8 hcf = (t > 255) ? HCF : 0; \ zuint8 p = (t & 7) ^ --B; \ @@ -967,9 +967,9 @@ static Z_INLINE zuint8 m(Z80 *self, zuint8 offset, zuint8 value) INXR_OTXR(out) -#define EXIT_HALT_STATE \ - HALT_LINE = FALSE; \ - if (self->halt != Z_NULL) self->halt(CONTEXT, FALSE) +#define SET_HALT_LINE(state) \ + HALT_LINE = state; \ + if (self->halt != Z_NULL) self->halt(CONTEXT, state) /* MARK: - Instructions: 8-Bit Load Group */ @@ -1223,7 +1223,7 @@ INSTRUCTION(neg) /*---------------------------------------------------------------------------. -| "ccf" and "scf" are the only instructions in which Q affects the flags. | +| `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, | @@ -1277,12 +1277,12 @@ INSTRUCTION(scf) /*-----------------------------------------------------------------------------. -| The "halt" instruction enables the HALT state after PC is incremented during | +| 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 | +| 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). | | | @@ -1302,40 +1302,35 @@ INSTRUCTION(halt) { Q_0 PC++; - if (REQUEST & Z80_REQUEST_INTERRUPT) return 4; + if (REQUEST) 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); + SET_HALT_LINE(TRUE); } if (self->nop == Z_NULL || (OPTIONS & Z80_OPTION_HALT_SKIP)) { - if (REQUEST) RESUME = FALSE; + zusize nop_cycles = self->cycle_limit - self->cycles; - 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; - } + 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) - { + else do { + R++; /* M1 */ + (void)self->nop(CONTEXT, PC); + self->cycles += 4; + if (REQUEST) { RESUME = FALSE; return 0; } - - R++; /* M1 */ - (void)self->nop(CONTEXT, PC); } + while (self->cycles < self->cycle_limit); return 0; } @@ -1543,9 +1538,9 @@ INSTRUCTION(djnz_OFFSET) {DJNZ_JR_Z_OFFSET(--B, 13, 8); } |+ reti/retn <--ED-->01***101 ........ 14:4433 | | rst N 11nnn111 ........ 11:533 | |---------------------------------------------------------------------| -| (+) The instruction has undocumented opcodes. The mnemonic "reti" | +| (+) The instruction has undocumented opcodes. The `reti` mnemonic | | is used to represent the ED4Dh opcode, which is recognized by | -| the Z80 CTC chip. All others opcodes are represented as "retn". | +| the Z80 CTC chip. All other opcodes are represented as `retn`. | '====================================================================*/ INSTRUCTION(call_WORD) {Q_0 MEMPTR = FETCH_16(PC + 1); PUSH(PC + 3); PC = MEMPTR; return 17;} @@ -1646,7 +1641,7 @@ INSTRUCTION(out_vBYTE_a) /*----------------------------------------------------------------------------. -| The "out (c),0" instruction behaves as "out (c),255" on the Zilog Z80 CMOS. | +| 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 | @@ -1885,7 +1880,7 @@ INSTRUCTION(xy_xy) /*------------------------------------------------------------------. | The CPU ignores illegal instructions with EDh prefix; in practice | -| they are all equivalent to two "nop" instructions (8 T-states). | +| they are all equivalent to two `nop` instructions (8 T-states). | '==================================================================*/ INSTRUCTION(ed_illegal) @@ -2019,21 +2014,34 @@ Z80_API void z80_power(Z80 *self, zboolean state) } +/*-------------------------------------------------------------------------. +| 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 (2016-09). "Z80 CPU User Manual" revision 11. p. 6. | +| 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" 1st edition. | +| p. 40. | +'=========================================================================*/ + Z80_API void z80_instant_reset(Z80 *self) { - if (HALT_LINE) {EXIT_HALT_STATE;} + if (HALT_LINE) {SET_HALT_LINE(FALSE);} + 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 +#ifdef Z80_WITH_SPECIAL_RESET Z80_API void z80_special_reset(Z80 *self) {REQUEST |= Z80_REQUEST_SPECIAL_RESET;} #endif @@ -2092,7 +2100,7 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) | 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. | + | function. The emulation of `ld {a,r|r,a}` takes this into account. | '=====================================================================*/ R7 = R; @@ -2104,10 +2112,11 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) /*----------------------------------------------------------------. | 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. | + | within the function that emulates the `halt` instruction. | '================================================================*/ case Z80_RESUME_HALT: - (void)halt(self); + if (REQUEST) RESUME = FALSE; + else (void)halt(self); break; /*--------------------------------------------------------------------. @@ -2133,58 +2142,12 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) 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 } while (self->cycles < cycles) /* Main execution loop. */ { if (REQUEST) { - /*-------------------------------------------------------------------------. - | 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 (2016-09). "Z80 CPU User Manual" revision 11. p. 6. | - | 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" 1st edition. | - | p. 40. | - '=========================================================================*/ -# ifdef Z80_WITH_RESET_SIGNAL - if (REQUEST & Z80_REQUEST_RESET) - { - if (HALT_LINE) {EXIT_HALT_STATE;} - - reset: - self->cycles += (self->reset != Z_NULL) - ? self->reset(CONTEXT, PC) - : Z80_CYCLES_PER_RESET; - - 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 | |--------------------------------------------------------------------------| @@ -2194,7 +2157,7 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) | 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 | + | 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 | @@ -2211,48 +2174,34 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) | * https://github.com/floooh/v6502r | '=========================================================================*/ if (REQUEST & Z80_REQUEST_REJECT_NMI) -# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL - REQUEST &= ~(zuint8)Z80_REQUEST_REJECT_NMI; +# ifdef Z80_WITH_SPECIAL_RESET + REQUEST &= Z80_REQUEST_SPECIAL_RESET; # else REQUEST = 0; # endif else if (REQUEST & Z80_REQUEST_NMI) { +# ifdef Z80_WITH_SPECIAL_RESET + zuint8 special_reset = REQUEST & Z80_REQUEST_SPECIAL_RESET; +# endif + 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 - } - } - + if (HALT_LINE) {SET_HALT_LINE(FALSE);} R++; /* M1 */ if (self->nmia != Z_NULL) (void)self->nmia(CONTEXT, PC); -# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL - PC >>= REQUEST & Z80_REQUEST_CLEAR_PC; - REQUEST = (REQUEST & (Z80_REQUEST_ANY_RESET | Z80_REQUEST_NMI)) >> 1; +# ifdef Z80_WITH_SPECIAL_RESET + PC >>= special_reset; + REQUEST = Z80_REQUEST_REJECT_NMI | (REQUEST & Z80_REQUEST_SPECIAL_RESET); # else - REQUEST = -# ifdef Z80_WITH_RESET_SIGNAL - (REQUEST & Z80_REQUEST_RESET) | -# endif - Z80_REQUEST_REJECT_NMI; + REQUEST = Z80_REQUEST_REJECT_NMI; # endif DATA[0] = 0; Q_0 PUSH(PC); - MEMPTR = PC = 0x66; + PC = MEMPTR = 0x66; self->cycles += 11; continue; } @@ -2260,21 +2209,21 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) /*-------------------------------------------------------------------------. | 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 | + | 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 | + | 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. | '=========================================================================*/ else if ( -# ifdef Z80_WITH_SPECIAL_RESET_SIGNAL +# ifdef Z80_WITH_SPECIAL_RESET (REQUEST & Z80_REQUEST_INT) && # endif - /* if the previous instruction is not "ei" */ + /* if the previous instruction is not `ei` */ DATA[0] != 0xFB && - /* if the previous instruction is not "reti/retn" or IFF1 has not changed */ + /* 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)) ) @@ -2283,27 +2232,19 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) Z80Read hook; IM0 im0; # endif + +# ifdef Z80_WITH_SPECIAL_RESET + zuint8 special_reset = REQUEST & Z80_REQUEST_SPECIAL_RESET; +# endif + zuint8 byte; 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 - } - } + if (HALT_LINE) {SET_HALT_LINE(FALSE);} /*-----------------------------------------------------. | Due to a bug, the Zilog Z80 NMOS resets PF if an INT | - | is accepted during the execution of "ld a,{i|r}". | + | is accepted during the execution of `ld a,{i|r}`. | '=====================================================*/ # ifdef Z80_WITH_ZILOG_NMOS_LD_A_IR_BUG if ( @@ -2321,22 +2262,18 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) | 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 | + | 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); +# ifdef Z80_WITH_SPECIAL_RESET + PC >>= special_reset; + REQUEST &= Z80_REQUEST_NMI | Z80_REQUEST_SPECIAL_RESET; # else - REQUEST &= -# ifdef Z80_WITH_RESET_SIGNAL - Z80_REQUEST_RESET | -# endif - Z80_REQUEST_NMI; + REQUEST &= Z80_REQUEST_NMI; # endif switch (IM) /* Response */ @@ -2526,7 +2463,7 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) self->cycles += 2 + 17; continue; - /* "rst N" is assumed for other instructions */ + /* `rst N` is assumed for other instructions */ default: Q_0 PUSH(PC); @@ -2537,9 +2474,9 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) # endif /*----------------------------------------------------------. - | Interrupt Mode 1: Execute "rst 38h" | T-states: 13:733 | + | Interrupt Mode 1: Execute `rst 38h` | T-states: 13:733 | |-----------------------------------------------------------| - | An internal "rst 38h" is executed. The interrupt response | + | An internal `rst 38h` is executed. The interrupt response | | data read from the data bus is disregarded. | '==========================================================*/ case 1: @@ -2551,7 +2488,7 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) continue; /*---------------------------------------------------------------------. - | Interrupt Mode 2: Execute "call (i:BYTE)" | T-states: 19:73333 | + | 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 | @@ -2593,22 +2530,34 @@ Z80_API zusize z80_run(Z80 *self, zusize cycles) | * 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; - } - +# ifdef Z80_WITH_SPECIAL_RESET 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); + zuint8 opcode; + + R++; /* M1 */ + opcode = DATA[0] = FETCH_OPCODE(PC); + SET_HALT_LINE(FALSE); PC--; - self->cycles += instruction_table[opcode](); + self->cycles += instruction_table[opcode](self); } + + else { + if (DATA[0] == 0x76) + { + SET_HALT_LINE(TRUE); + SET_HALT_LINE(FALSE); + } + + R++; /* M1 */ + if (self->nop != Z_NULL) (void)self->nop(CONTEXT, PC); + DATA[0] = 0; + PC = 0; + } + + continue; } # endif } diff --git a/sources/test-Z80.c b/sources/test-Z80.c index 2743263..b7434e0 100644 --- a/sources/test-Z80.c +++ b/sources/test-Z80.c @@ -817,7 +817,6 @@ int main(int argc, char **argv) cpu.nmia = cpu.inta = cpu.int_fetch = Z_NULL; - cpu.reset = Z_NULL; cpu.ld_i_a = cpu.ld_r_a = cpu.reti =