Z80 Special Reset - redcode/Z80 GitHub Wiki

Although not mentioned in Zilog databooks the Z80 CPU supports two types of reset: normal and special. A normal reset disables the maskable interrupt, selects interrupt mode 0, zeroes registers I & R and zeroes the program counter (PC). A special reset zeroes PC only. Zilog literature states that /RESET must be active for at least three clock cycles (3T) before being accepted. Tests with short reset pulses 1T or 2T long have shown that this is not true and the reason for Zilog saying otherwise is explained later.

What is the special reset?

U.S. Patent No. 4,486,827 describes the Z80 special reset and the abstract is succinct:

A special reset function is provided in the CPU, using the same control input to the CPU as a normal reset, to reset only the program counter to facilitate the use of a single CPU in a microprocessor development system.

The patent document should be downloaded to learn more about the intended application of the special reset. It enables breakpoints whilst debugging Z80 code that preserve the complete state of the system except for PC which is easily saved. Nothing is put on the stack unlike when taking a "snapshot" using the non-maskable interrupt (possibly destroying data) and in any case the /NMI pin might not be available. Sharing the same 64K address space means the memory containing the monitoring program is not available to the user during development but changes to assembly code to cater for this are normally trivial.

How the special reset is generated

z80_special_reset_1

Very precise timing is required to generate a special reset signal. The figure above shows a Z80 instruction opcode fetch (M1 cycle). To be special /RESET must be low only at the rising edge of clock state M1T2 as shown in red (the low pulse width is illustrative). A reset will be normal if /RESET is low at any rising edge of CLK other than M1T2. The results of tests with 1T and 2T reset pulses during M1 cycles are shown below. Other M cycles were not considered as a special reset during these is impossible.

RESET low at rising edge of   Type of
 M1T1 M1T2 M1T3 M1T4 ....      reset

   X                           Normal
        X                     Special
             X                 Normal
                  X            Normal

   X    X                      Normal
        X    X                 Normal
             X    X            Normal
                  X    X       Normal

Note that a reset pulse that is very short (much less than 1T long) will still generate a reset provided the minimum /RESET to rising edge of CLK setup and hold times are met. For the Z80A these are 60 ns and 0 ns respectively for the NMOS version or 60 ns and 10 ns respectively for the CMOS version.

How the CPU detects the special reset

Below is a modified version of Fig. 11 from the patent with changes made for clarity and consistency. The external logic with two D-type flip-flops is just one way to create the special reset pulse (note that M1 here is /M1 inverted). During testing the special reset signal was a registered output from a programmable logic device (PLD) clocked on the falling edge of CLK, enabled when /M1 is low and /MREQ is high.

z80_special_reset_2

As there is only one reset pin the Z80 designers included extra circuitry to distinguish between the two reset functions. Inside the chip are the equivalent of three D-type flip-flops, two with enable and one with enable and clear. The RESI f-f samples the /RESET input on every rising edge of CLK, the normal reset f-f samples RESI on every falling edge of CLK except in M1T2 and the special reset f-f samples RESI on the falling edge in M1T2 only.

/NRES acts as an asynchronous clear for the special reset f-f. After /RESET goes low during M1T1 RESI goes high on the rising edge of M1T2 and CLRPC goes high on the falling edge. If /RESET is still low at the next rising edge (at the start of M1T3) /NRES will go low on the falling edge and clear CLRPC. If /RESET is low at the rising edges of both M1T1 and M1T2 /NRES will prevent CLRPC going high.

Therefore a 2T reset pulse is guaranteed to produce a normal reset at any time provided the minimum setup and hold times are met and the tests shown earlier prove this to be the case. However an asynchronous reset signal cannot be relied upon to always meet the setup time. It might not be detected until almost a whole clock cycle after becoming active which is why databooks say /RESET should be low for at least 3T.

After the special reset is detected

CLRPC going high is no guarantee that the reset will be special but if not cleared by /NRES CLRPC will stay high until the falling edge of T2 in the next M1 cycle. The patent shows CLRPC sampled on the rising edge of M1T2 at which time the type of reset is not in doubt and PC will be zeroed if CLRPC is set. However an opcode fetch from address zero must wait until the following M1 cycle as the incremented PC has already been placed on the address bus and /M1 has gone low. Thus following the instruction when /RESET is active there is an M1 cycle in which the opcode of the next instruction is fetched (but ignored). This extra M1 cycle is needed to complete the previous instruction due to fetch-execute overlap and is also when PC is zeroed.

The patent describes external circuitry with a multiplexer that zeroes the data bus to ensure the opcode is read as a NOP (although test results indicate the opcode is ignored anyway) and a 16-bit register that holds the last value of PC before it changes to zero. This is the address to which the CPU should jump after the special reset routines have finished and so the instruction here must not be executed before then. Of course if the special reset was generated at a pre-determined breakpoint this continuation address would be known beforehand.

To summarize the instruction in which the special reset pulse occurs is completed and due to the fetch-execute overlap the following opcode is fetched and ignored then the opcode at address zero is fetched and executed. No return address is pushed automatically onto the stack as the special reset is not an interrupt and if PC is not saved during the reset or the breakpoint specified in advance the CPU will not know the correct address to resume normal program execution.

Prefixed instructions and the special reset

Tests have shown that the special reset is accepted in the first or second M1 cycle of an instruction prefixed by CB the only difference being that the reset takes effect 4T sooner when /RESET is low during the second. Identical behaviour is assumed for the other prefixes DD, ED and FD. Thus the next M1 cycle after an accepted special reset pulse might not always be an opcode fetch of a new instruction. Prefixed instructions are followed by a disregarded opcode fetch in the same way as non-prefixed ones and two examples of program execution are shown below.

PC    Instruction  Comments

0012  RLC B        Special reset during fetch from 0012
0014  PUSH BC      Fetch from 0014, opcode ignored, 0000 -> PC
0000  xxx          Fetch from 0000, starts 12T after start of fetch from 0012

0012  RLC B        Special reset during fetch from 0013
0014  PUSH BC      Fetch from 0014, opcode ignored, 0000 -> PC
0000  xxx          Fetch from 0000, starts 8T after start of fetch from 0013

      xxx = don't care
      All addresses are in hexadecimal 

Halt and the special reset

An interesting situation arises when there is a special reset pulse after a HALT instruction. The HALT state can be exited by a maskable interrupt (if enabled) or a non-maskable interrupt or either type of reset. Until one of these happens databooks say that "the CPU executes NOPs to maintain memory refresh" without giving any more detail. HALT needs to be executed only once to place the CPU in the HALT state and if the HALT opcode is continually read thereafter this would have the same effect presumably as executing NOPs. If HALT were replaced by NOP after /HALT has gone low would this be another way to exit the HALT state?

The answer is no. When /HALT is low PC has already been incremented and the opcode fetched is for the instruction after HALT. The HALT state stops this instruction from being executed and PC from incrementing so this opcode is read again and again until an exit condition occurs. An interrupt when halted pushes PC onto the stack unchanged as it is already the correct return address. This is no different from an interrupt when not halted as /INT and /NMI are sampled and accepted at the end of an instruction by which time PC has incremented. (N.B. What "The Undocumented Z80 Documented" says about HALT and PC is wrong.)

If a special reset pulse occurs when /HALT is low the HALT state is exited and the opcode of the instruction after HALT is fetched and executed with no delay. This is possible because special reset is sampled on the rising edge of M1T2 and the opcode is sampled on the rising edge of M1T3. Tests show that /HALT goes high after the falling edge of M1T2. Once the instruction after HALT is completed there is an opcode fetch of the next instruction then a fetch from address zero as already described.

Therefore apart from which registers are zeroed it can be seen that there is another difference between a normal and a special reset during the HALT state: the latter executes an instruction between the HALT and the actual reset. This could be used to save PC without the need for an external register. CALL pushes a return address but the two memory reads to get the address of the subroutine are a waste of time as it is never actually called. RST is a better choice as it is single-byte and faster. RST 18 was used during tests but any of the eight restarts would do as the address is effectively redundant and RST 00 is perhaps the most appropriate.

Tests show there is a significant difference between special resets when unhalted and halted. For the latter the HALT state prevents PC from incrementing during M1T1 at the start of the opcode fetch after HALT so PC is one less than it should be. This Z80 CPU hardware bug corrupts multi-byte instructions except those starting with DD or FD. Three examples from testing are shown below.

0011  xxx          Not in halt state
0012  HALT
0013  RST 18       Special reset during fetch from 0013 *
0018  xxx          Fetch from 0018, opcode ignored, 0000 -> PC
0000  xxx          Fetch from 0000, starts 15T after start of fetch from 0013

0011  xxx          Not in halt state
0012  HALT
0013  PUSH AF      Special reset during fetch from 0013
0013  PUSH AF      Fetch from 0013 again, opcode ignored, 0000 -> PC
0000  xxx          Fetch from 0000, starts 15T after start of first fetch from 0013

0011  xxx          Not in halt state
0012  HALT
0013  LD (8000),A  Special reset during fetch from 0013 **
0015  DB 80        Fetch from 0015, opcode ignored, 0000 -> PC ***
0000  xxx          Fetch from 0000, starts 17T after start of fetch from 0013

   *  Address pushed onto stack = 0013
  **  A written to (0032) not (8000) !
 ***  High byte of address read as opcode but ignored

If the special reset pulse occurs when /HALT is high during a HALT opcode fetch then PC is incremented at the end of the fetch and /HALT goes low very briefly. If the special reset pulse occurs when /HALT is low during a HALT fetch (two consecutive HALT instructions) then /HALT goes high before the rising edge of M1T3, PC is not incremented at the end of the fetch and /HALT goes low again very briefly. Examples of program execution in both cases are shown below.

0011  xxx          Not in halt state
0012  HALT         Special reset during fetch from 0012
0013  xxx          Fetch from 0013, opcode ignored, 0000 -> PC
0000  xxx          Fetch from 0000, starts 8T after start of fetch from 0012

0011  xxx          Not in halt state
0012  HALT
0013  HALT         Special reset during first fetch from 0013
0013  HALT         Fetch from 0013 again, opcode ignored, 0000 -> PC
0000  xxx          Fetch from 0000, starts 8T after start of first fetch from 0013

How the tests were done

Dave Stevenson's MTX Plus+ CPU board was used for testing the special reset. Some modifications were needed first, in particular creating a separate reset signal for the Z80 only. The onboard Altera MAX 7000 series CPLD contained all the necessary test logic and 64 bytes of Z80 machine code so that only one device needed to be programmed. The initial test program is shown below.

0000  JR NC,0002   interrupt vector table DW 0030
0002  LD A,I       or LD A,R
0004  NOP
0005  RLCA
0006  JR C,0028    jump if bit 7 of I or R set
0008  LD A,80      opcodes at 0008-000F zeroed after 2nd fetch from 0000
000A  LD I,A       or LD R,A
000C  NOP
000D  EI
000E  IM 2         or IM 1
0010  NOP          special reset generated at 001x (varied) after 1st fetch from 0000
0020  HALT         here if normal reset, generate interrupt, stop, INT low
0028  HALT         here if special reset, generate interrupt
0030  HALT         here if IM 2 interrupt accepted, stop, INT high
0038  HALT         here if IM 1 interrupt accepted, stop, INT high

There is a NOP at any address not listed up to 003F. The first two bytes of the program form the complete interrupt mode 2 vector table as zero is placed on the data bus during a maskable interrupt acknowledge cycle. 30 was chosen so that IM 1 and IM 2 tests end at different addresses and 30 00 is a harmless instruction as whatever the state of the carry flag the second instruction after a reset will be read from address 0002. Note that only A5-A0 were used for address decoding, the I register can have any value during IM 2 tests and any combination of I or R and IM 1 or IM 2 is permissible.

Each test began with a button press generating a 200 ms normal reset. Code at 0000-000F was then executed, followed by a short reset pulse (normal or special) shortly after address 0010. A special reset preserved I and R and the CPU executed the HALT at 0028. A normal reset zeroed I and R and the CPU executed the HALT at 0020 (opcodes from 0008-000F were zeroed after this second reset). A maskable interrupt was generated when the address reached 002x accepted only if the reset was special. Thus the state of /INT at the end of the test was a simple indication of the type of reset.

Opcodes were placed carefully to minimise the amount of logic required even though there was ample available. The CPLD has 128 macrocells with five combinatorial product terms per cell. (Unused terms can be borrowed from adjacent cells if required.) As it turned out only eight macrocells were needed to create 17 non-zero bytes with three to five product terms per data bit and 33 in total for D7-D0. Later tests utilised more logic as instructions were added in the range 0010-001F and the results of these were listed earlier.

In order to see more information Dave Stevenson's diagnostic card was connected. This has 7-segment LEDs that can latch and display the contents of the address and data buses. The CPLD output a signal that latched the final PC and various data values during opcode fetches when halted at the end of each test. The data bus is irrelevant when in the HALT state and therefore can be used by other devices except during an opcode fetch with special reset. Initially one data value was displayed, then two and ultimately four. A counter was implemented with the refresh address as the low bits to display each data byte in turn for about one second.

The values displayed on the data LEDs included the last PC after the special reset before the fetch from address zero, the number of fetches from address zero (a reset count), the number of halt cycles completed, the number of T-states from the reset pulse to the fetch from address zero and the last 16 successive samples at the falling edge of CLK before the fetch from address zero of /HALT and /MREQ. The latter showed clearly the different types of M-cycle. (It was necessary to also sample /HALT on the rising edge of CLK to detect the very short pulses discussed earlier.)

As the tests were not about speed a 1 MHz CLK signal was output by the CPLD. Any signals sampled by the CPU on the rising edge of CLK were clocked using the falling edge in the CPLD and any signal from the CPU clocked on the falling edge was sampled by the CPLD on the rising edge. The state of /MREQ, /M1 and /RFSH at the falling edge of CLK was used to distinguish each of the four T-states during M1 cycles as shown below. (Using the rising edge of CLK gives ambiguous results.)

At falling edge of CLK   T state
   MREQ  M1  RFSH

    H    L    H            M1T1
    L    L    H            M1T2
    H    H    L            M1T3
    L    H    L            M1T4

Tests used a variety of different CPUs from Zilog, Mostek and SGS in NMOS and CMOS versions. A genuine Zilog CPU was used mainly and other brands only as a check. The special reset behaviour was the same no matter which CPU was tested. The only difference detected was that register B does not have the same value on power-up in the CMOS version compared to NMOS.

Conclusions

The Z80 special reset has been tested and works as described in the patent. A hardware bug was discovered when the special reset occurs after a HALT instruction. Normal reset pulses less than three clock cycles long were tested successfully.

Thanks

A very special thank you to Dave Stevenson for not only hosting the original article and allowing his MTX Plus+ project to be delayed but also for performing all the tests and reporting the results often without being told what they were all about. The whole process took far longer than anticipated and throughout Dave was remarkably patient and good-humoured.

Tony Brewer
December 2014 (revised November 2022 and November 2024)

⚠️ **GitHub.com Fallback** ⚠️