Contention pattern for HALT
Contention pattern for HALT
I've recently been looking into how memory contention affects Z80 instructions, using the Contended memory page of the Sinclair Wiki as my main source. However, one instruction that's missing from that page is HALT.
Based on what I've already learnt, I'm guessing that HALT's contention pattern is simply pc:4. Can anyone confirm?
Based on what I've already learnt, I'm guessing that HALT's contention pattern is simply pc:4. Can anyone confirm?
Re: Contention pattern for HALT
it is basically NOP, but without advancing the PC. so it's the usual
/* read opcode -- OCR(4) */
/* t1: setting /MREQ & /RD */
/* t2: memory read */
/* t3, t4: decode command, increment R */
/* read opcode -- OCR(4) */
/* t1: setting /MREQ & /RD */
/* t2: memory read */
/* t3, t4: decode command, increment R */
Re: Contention pattern for HALT
I'll take that as a yes. Fuse seems to go with pc:4, so I will too.
On an unrelated note, I didn't realise until now just how much an instruction can be delayed when it's located in and/or accesses contended memory. Take CPIR, for example. Normally 21 T-states (when BC != 1 and A != (HL)), but if it's located in contended memory, and HL also points at contended memory, and the instruction starts at T-state 14335 on a 48K Spectrum, then it takes 64 T-states (3.05x as long) to complete. That's nuts.
-
- Microbot
- Posts: 117
- Joined: Mon Apr 13, 2020 3:07 pm
Re: Contention pattern for HALT
Only the first fetch of the HALT itself. Then it executes NOPs internally, with PC already being PC+1.
(So it's not like it keeps executing itself similar to LDIR, which is what people originally believed.)
Patrik
Re: Contention pattern for HALT
Are the contents of PC+1 continually fetched (with contention pattern pc:4 there too) while the processor is HALTed? Or are there no further fetches until an interrupt is accepted?Patrik Rak wrote: ↑Fri Jan 19, 2024 5:50 pm Only the first fetch of the HALT itself. Then it executes NOPs internally, with PC already being PC+1.
(So it's not like it keeps executing itself similar to LDIR, which is what people originally believed.)
Experimenting with Fuse executing HALT at 0x6000 starting at T-state 14335 gives the following T-state values after each iteration:
14335, 14345 (pc:4 for the HALT, as expected), 14353 (implying pc:4), 14361 (pc:4 again), 14369, 14377...
From what you're saying, this looks like a bug in Fuse to me. Or I'm misunderstanding.
Re: Contention pattern for HALT
Fuse exhibits the same behaviour when the HALT is at 0x7FFF, i.e. PC+1 is 0x8000, in uncontended memory:
14335, 14345 (pc:4 for the HALT, as expected), 14353 (implying pc:4), 14361 (pc:4 again), 14369, 14377...
That seems wrong. I'm using Fuse version 1.6.0, btw. Maybe it's too old to have incorporated the new-found behaviour of HALT.
Re: Contention pattern for HALT
is it really like that? i was sure that Z80 keeps reading the memory. that explains why HALT is not ts-precise (i.e. CPU checks for /INTR each 4ts instead of each 1ts). it would be logical to either just loop and check for /INTR (and /NMI) on each tick if there is no command fetching — or make HALT simply decrement the PC, and then there is no need for any additional loop circuitry and flags at all.Patrik Rak wrote: ↑Fri Jan 19, 2024 5:50 pm Only the first fetch of the HALT itself. Then it executes NOPs internally, with PC already being PC+1.
(So it's not like it keeps executing itself similar to LDIR, which is what people originally believed.)
it would be interesting to test real Z80 (both CMOS and NMOS) with a device which can overwrite RAM without raising an interrupt, and check if Z80 will really get out of HALT loop in this case. sadly, i don't have neither real Z80, nor skills to implement this test…
- 1024MAK
- Bugaboo
- Posts: 3123
- Joined: Wed Nov 15, 2017 2:52 pm
- Location: Sunny Somerset in the U.K. in Europe
Re: Contention pattern for HALT
Well, there will still be a memory access because the normal DRAM refresh still NEEDS to occur.
Mark
Mark
Standby alert
“There are four lights!”
Step up to red alert. Sir, are you absolutely sure? It does mean changing the bulb
Looking forward to summer later in the year.
“There are four lights!”
Step up to red alert. Sir, are you absolutely sure? It does mean changing the bulb
Looking forward to summer later in the year.
Re: Contention pattern for HALT
sure. but i meant that Z80 keep reading and decoding HALT opcode again and again. i don't know how CPUs are developed, but using "sheer conventional logic", the easiest way to implement HALT is to insert /INTR check before the final PC decrement. so when /INTR is not raised, PC will be decremented, and HALT opcode read again; but interrupt will push the non-decremented PC, getting out of the loop.
of course, i may be simply pipedreaming, because i know nothing about developing the hardware.
of course, i may be simply pipedreaming, because i know nothing about developing the hardware.
Re: Contention pattern for HALT
From the Zilog docs;
Each cycle in the HALT state is a normal M1 (fetch) cycle except that the data received from the memory is ignored and an NOP instruction is forced internally to the CPU. The HALT acknowledge signal is active during this time indicating that the proces-sor is in the HALT state.
Each cycle in the HALT state is a normal M1 (fetch) cycle except that the data received from the memory is ignored and an NOP instruction is forced internally to the CPU. The HALT acknowledge signal is active during this time indicating that the proces-sor is in the HALT state.
Last edited by AndyC on Sun Jan 21, 2024 9:39 am, edited 1 time in total.
-
- Microbot
- Posts: 117
- Joined: Mon Apr 13, 2020 3:07 pm
Re: Contention pattern for HALT
Yes, it's exactly as I said. It keeps reading the memory from PC+1, but uses it for nothing, just executes NOP internaly (that is, standard 4 T cycles).ketmar wrote: ↑Sat Jan 20, 2024 6:35 am is it really like that? i was sure that Z80 keeps reading the memory. that explains why HALT is not ts-precise (i.e. CPU checks for /INTR each 4ts instead of each 1ts). it would be logical to either just loop and check for /INTR (and /NMI) on each tick if there is no command fetching — or make HALT simply decrement the PC, and then there is no need for any additional loop circuitry and flags at all.
it would be interesting to test real Z80 (both CMOS and NMOS) with a device which can overwrite RAM without raising an interrupt, and check if Z80 will really get out of HALT loop in this case. sadly, i don't have neither real Z80, nor skills to implement this test…
What you describe (decrementing PC to execute HALT again) is exactly how we originally believed that it worked. But during research of the soft reset feature it was discovered that it indeed reads from PC+1, not PC.
-
- Microbot
- Posts: 117
- Joined: Mon Apr 13, 2020 3:07 pm
Re: Contention pattern for HALT
Yes, it's continually fetched and contended.
As for Fuse, it might have been fixed in newer version, I don't know. For me, the most reliable reference is SpecEmu, it stays up to date with the recent discoveries (at least most of the time ).
Re: Contention pattern for HALT
There is a test HALT2INT, written by Woody. It is available in ZJoyKiler's collection: https://github.com/redcode/Z80/wiki/HALT2INT. On that page you can see the expected test result.
Let's see HALT2INT v. 3, executed in Fuse 1.6:
So, Fuse 1.6 does not pass this test.
But yes, Fuse might have been fixed after 1.6 got released. This is the version I have.
Let's see HALT2INT v. 3, executed in Fuse 1.6:
So, Fuse 1.6 does not pass this test.
But yes, Fuse might have been fixed after 1.6 got released. This is the version I have.
Re: Contention pattern for HALT
It's still the same, but lets just say no-one's been desperate to fix things that only affect tests.
Re: Contention pattern for HALT
There is a thing regarding this -- if an emulator change its behaviour and while cpu is halted, keeps PC increased (on position "halt + 1"), the question is that there are new incompatibilities with saving to / loading from snapshots. Incompatibilities between emulators, as well as incompatibilities with older snapshots.
If I'm not mistaken, only szx format is actually affected by this. Neither sna nor z80 formats store halt state.
When this behaviour was revealed (halt instruction increases PC and then the cpu, while halted, keeps reading the following instruction, but discarding it), I remember there was a discussion (probably on discord, I'm not sure, cannot find it).
if I remember well, it was concluded that emulators should continue storing the PC which points to halt instruction, regardless of how the behaviour is internally implemented in particular emulator.
And when loading from snapshots, if halt flag is set in snapshot, an emulator should assume that stored PC value points to halt instruction and handle this appropriately (by increasing it if needed).
I am no authority on this, but the emulator authors ought to agree on this, so that there is one standard behaviour.
If I'm not mistaken, only szx format is actually affected by this. Neither sna nor z80 formats store halt state.
When this behaviour was revealed (halt instruction increases PC and then the cpu, while halted, keeps reading the following instruction, but discarding it), I remember there was a discussion (probably on discord, I'm not sure, cannot find it).
if I remember well, it was concluded that emulators should continue storing the PC which points to halt instruction, regardless of how the behaviour is internally implemented in particular emulator.
And when loading from snapshots, if halt flag is set in snapshot, an emulator should assume that stored PC value points to halt instruction and handle this appropriately (by increasing it if needed).
I am no authority on this, but the emulator authors ought to agree on this, so that there is one standard behaviour.
Re: Contention pattern for HALT
You can try it on Visual Z80 Remix: https://floooh.github.io/visualz80remix/ketmar wrote: ↑Sat Jan 20, 2024 6:35 am is it really like that? i was sure that Z80 keeps reading the memory. that explains why HALT is not ts-precise (i.e. CPU checks for /INTR each 4ts instead of each 1ts). it would be logical to either just loop and check for /INTR (and /NMI) on each tick if there is no command fetching — or make HALT simply decrement the PC, and then there is no need for any additional loop circuitry and flags at all.
it would be interesting to test real Z80 (both CMOS and NMOS) with a device which can overwrite RAM without raising an interrupt, and check if Z80 will really get out of HALT loop in this case. sadly, i don't have neither real Z80, nor skills to implement this test…
Re: Contention pattern for HALT
thank you all for the info. fixed ZXEmuT so it will perform fetching at "haltpc+1". ;-)
-
- Microbot
- Posts: 117
- Joined: Mon Apr 13, 2020 3:07 pm
Re: Contention pattern for HALT
Yes, your post is spot on. The emulators keep the PC pointed to HALT, only contend by PC+1 (those which got it right - others still contend by PC). The PC is then stepped in the interrupt handling code when it knows HALT is being executed. If HALT would actually increase the PC, it would be more difficult to handle end-of-frame emulation, as well as things as DI+HALT. This is why SZX also implements it the way it does, i.e., HALT internally using the same logic as LDIR would. Thanks to the fact there is no contention at that point, it is possible to re-execute HALT and no one finds a difference.Zoran wrote: ↑Sun Jan 21, 2024 6:37 pm if I remember well, it was concluded that emulators should continue storing the PC which points to halt instruction, regardless of how the behaviour is internally implemented in particular emulator.
And when loading from snapshots, if halt flag is set in snapshot, an emulator should assume that stored PC value points to halt instruction and handle this appropriately (by increasing it if needed).
I am no authority on this, but the emulator authors ought to agree on this, so that there is one standard behaviour.
Re: Contention pattern for HALT
I handle it differently. I have a special loop for the HALT state. When the emulator executes the HALT instruction, it enters such a loop, in which the opcode after HALT is fetched all the time and PC does point to that opcode always. If the emulator consumes all the requested cycles, it exits the loop, but when the emulation is executed again, it enters the loop again until an exit condition (interrupt or reset) occurs. Having a specific loop for the HALT state prevents having to check if the CPU is halted before executing the next instruction, thus making the emulation faster.Patrik Rak wrote: ↑Tue Jan 23, 2024 8:25 pm Yes, your post is spot on. The emulators keep the PC pointed to HALT, only contend by PC+1 (those which got it right - others still contend by PC). The PC is then stepped in the interrupt handling code when it knows HALT is being executed. If HALT would actually increase the PC, it would be more difficult to handle end-of-frame emulation, as well as things as DI+HALT. This is why SZX also implements it the way it does, i.e., HALT internally using the same logic as LDIR would. Thanks to the fact there is no contention at that point, it is possible to re-execute HALT and no one finds a difference.
Very simplified pseudocode:
Code: Select all
halt {
self.pc++
self.cycles += 4
self.halt_state = true
while (self.cycles < self.cycle_limit && self.halt_state) {
fetch(self.pc)
self.cycles += 4
}
}
Re: Contention pattern for HALT
Anyway, each particular emulator can choose its own way, as long as it behaves correctly (that is, as long as you get points in S.H.I.T.).
But, how the state should be saved to/loaded from szx has to be uniquely universally accepted.
-
- Microbot
- Posts: 117
- Joined: Mon Apr 13, 2020 3:07 pm
Re: Contention pattern for HALT
I also consume all the remaining T-cycles in the HALT itself. The re-execution happens only afterwards if needed (i.e., unless the HALT state is finished by accepting IRQ). This allows me to emulate parts of the frame efficiently, without having to deal with HALT everywhere.
Re: Contention pattern for HALT
sadly, this doesn't quite work if you need a sophisticated breakpoint system. Zymosis uses callbacks for each type of memory i/o, and one general breakpoint callback before each instruction. this way i can stop execution on arbitrarily complex condition, but HALT cannot be special anymore: memory reading cycles should still be performed. and adding more flags to core loop will make it even slower in the general case…Patrik Rak wrote: ↑Sun Feb 04, 2024 2:28 pm I also consume all the remaining T-cycles in the HALT itself.
-
- Microbot
- Posts: 117
- Joined: Mon Apr 13, 2020 3:07 pm
Re: Contention pattern for HALT
Why not check if it can be done or not? For example, my HALT checks if the reads need to be contended and if not it uses simple modulo 4 to compute the remaining T cycles. Otherwise it does the contended fetches in a loop. Your code could similarly check for presence of relevant breakpoints and take the fast path if possible.
Re: Contention pattern for HALT
because my Z80 emulator is a standalone library. it cannot know what callback might want to do, and adding more API to check if any breakpoint is possible is both complex, and even the main code sometimes doesn't know (complex expression, scripting language, etc.).