Special RESET WIP; Removed the normal RESET signal; Documentation/comments; Other improvements.

This commit is contained in:
redcode
2022-07-24 21:24:18 +02:00
parent 9638a7174d
commit 314edff52d
12 changed files with 198 additions and 293 deletions

View File

@@ -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}}

View File

@@ -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 <tt>ld i,a</tt> 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 <tt>ld i,a</tt> 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 <tt>ld r,a</tt> 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 <tt>ld r,a</tt> 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

View File

@@ -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)

50
HISTORY
View File

@@ -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)

14
README
View File

@@ -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 <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------
Last update: 2022-07-03 README EOF
Last update: 2022-07-16 README EOF

View File

@@ -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`.
* <span id="option_z80_with_reset_signal">**`-DZ80_WITH_RESET_SIGNAL=(YES|NO)`**</span>
Build the implementation of the normal RESET signal.
The default is `NO`.
* <span id="option_z80_with_special_reset_signal">**`-DZ80_WITH_SPECIAL_RESET_SIGNAL=(YES|NO)`**</span>
Build the implementation of the [special RESET](http://www.primrosebank.net/computers/z80/z80_special_reset.htm) signal.
* <span id="option_z80_with_special_reset">**`-DZ80_WITH_SPECIAL_RESET=(YES|NO)`**</span>
Build the implementation of the [special RESET](http://www.primrosebank.net/computers/z80/z80_special_reset.htm).
The default is `NO`.
* <span id="option_z80_with_unofficial_reti">**`-DZ80_WITH_UNOFFICIAL_RETI=(YES|NO)`**</span>
@@ -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)**

View File

@@ -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

View File

@@ -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" <https://worldofspectrum.org/forums/discussion/41704>`_. |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 <http://www.primrosebank.net/computers/z80/z80_special_reset.htm>`_ signal. |br| |nl|
Build the implementation of the `special RESET <http://www.primrosebank.net/computers/z80/z80_special_reset.htm>`_. |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:

View File

@@ -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

View File

@@ -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)
=================

View File

@@ -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
}

View File

@@ -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 =