Contention pattern for HALT

The place for codemasters or beginners to talk about programming any language for the Spectrum.
User avatar
SkoolKid
Manic Miner
Posts: 407
Joined: Wed Nov 15, 2017 3:07 pm

Contention pattern for HALT

Post by SkoolKid »

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?
SkoolKit - disassemble a game today
Pyskool - a remake of Skool Daze and Back to Skool
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Contention pattern for HALT

Post by ketmar »

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 */
User avatar
SkoolKid
Manic Miner
Posts: 407
Joined: Wed Nov 15, 2017 3:07 pm

Re: Contention pattern for HALT

Post by SkoolKid »

ketmar wrote: Tue Jan 16, 2024 12:56 pm it is basically NOP, but without advancing the PC. so it's the usual...
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.
SkoolKit - disassemble a game today
Pyskool - a remake of Skool Daze and Back to Skool
Patrik Rak
Microbot
Posts: 117
Joined: Mon Apr 13, 2020 3:07 pm

Re: Contention pattern for HALT

Post by Patrik Rak »

SkoolKid wrote: Tue Jan 16, 2024 12:36 pm Based on what I've already learnt, I'm guessing that HALT's contention pattern is simply pc:4. Can anyone confirm?
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
User avatar
SkoolKid
Manic Miner
Posts: 407
Joined: Wed Nov 15, 2017 3:07 pm

Re: Contention pattern for HALT

Post by SkoolKid »

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.)
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?

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.
SkoolKit - disassemble a game today
Pyskool - a remake of Skool Daze and Back to Skool
User avatar
SkoolKid
Manic Miner
Posts: 407
Joined: Wed Nov 15, 2017 3:07 pm

Re: Contention pattern for HALT

Post by SkoolKid »

SkoolKid wrote: Fri Jan 19, 2024 7:35 pm From what you're saying, this looks like a bug in Fuse to me. Or I'm misunderstanding.
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.
SkoolKit - disassemble a game today
Pyskool - a remake of Skool Daze and Back to Skool
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Contention pattern for HALT

Post by ketmar »

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.)
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…
User avatar
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

Post by 1024MAK »

Well, there will still be a memory access because the normal DRAM refresh still NEEDS to occur.

Mark
:!: Standby alert :!:
“There are four lights!”
Step up to red alert. Sir, are you absolutely sure? It does mean changing the bulb :dance
Looking forward to summer later in the year.
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Contention pattern for HALT

Post by ketmar »

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.
AndyC
Dynamite Dan
Posts: 1409
Joined: Mon Nov 13, 2017 5:12 am

Re: Contention pattern for HALT

Post by AndyC »

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.
Last edited by AndyC on Sun Jan 21, 2024 9:39 am, edited 1 time in total.
edjones
Drutt
Posts: 33
Joined: Fri Feb 28, 2020 1:42 pm

Re: Contention pattern for HALT

Post by edjones »

ketmar wrote: Sun Jan 21, 2024 6:24 am sure. but i meant that Z80 keep reading and decoding HALT opcode again and again.
It keeps reading the opcode following the HALT after its first execution.
Patrik Rak
Microbot
Posts: 117
Joined: Mon Apr 13, 2020 3:07 pm

Re: Contention pattern for HALT

Post by Patrik Rak »

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…
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).

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.
Patrik Rak
Microbot
Posts: 117
Joined: Mon Apr 13, 2020 3:07 pm

Re: Contention pattern for HALT

Post by Patrik Rak »

SkoolKid wrote: Fri Jan 19, 2024 7:35 pm Are the contents of PC+1 continually fetched (with contention pattern pc:4 there too) while the processor is HALTed?

From what you're saying, this looks like a bug in Fuse to me. Or I'm misunderstanding.
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 :)).
User avatar
Zoran
Drutt
Posts: 18
Joined: Fri Aug 16, 2019 12:56 pm

Re: Contention pattern for HALT

Post by Zoran »

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:
Image

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.
User avatar
Guesser
Manic Miner
Posts: 641
Joined: Wed Nov 15, 2017 2:35 pm
Contact:

Re: Contention pattern for HALT

Post by Guesser »

Zoran wrote: Sun Jan 21, 2024 3:59 pm But yes, Fuse might have been fixed after 1.6 got released. This is the version I have.
It's still the same, but lets just say no-one's been desperate to fix things that only affect tests.
User avatar
Zoran
Drutt
Posts: 18
Joined: Fri Aug 16, 2019 12:56 pm

Re: Contention pattern for HALT

Post by Zoran »

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.
User avatar
ZjoyKiLer
Dizzy
Posts: 67
Joined: Thu Sep 09, 2021 3:20 pm

Re: Contention pattern for HALT

Post by ZjoyKiLer »

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…
You can try it on Visual Z80 Remix: https://floooh.github.io/visualz80remix/
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Contention pattern for HALT

Post by ketmar »

thank you all for the info. fixed ZXEmuT so it will perform fetching at "haltpc+1". ;-)
Patrik Rak
Microbot
Posts: 117
Joined: Mon Apr 13, 2020 3:07 pm

Re: Contention pattern for HALT

Post by Patrik Rak »

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.
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.
User avatar
ZjoyKiLer
Dizzy
Posts: 67
Joined: Thu Sep 09, 2021 3:20 pm

Re: Contention pattern for HALT

Post by ZjoyKiLer »

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

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
    }
}
My implementation: https://github.com/redcode/Z80/blob/mas ... 80.c#L1315
User avatar
Zoran
Drutt
Posts: 18
Joined: Fri Aug 16, 2019 12:56 pm

Re: Contention pattern for HALT

Post by Zoran »

ZjoyKiLer wrote: Wed Jan 24, 2024 5:01 pm I handle it differently. I have a special loop for the HALT state.

...
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.
Patrik Rak
Microbot
Posts: 117
Joined: Mon Apr 13, 2020 3:07 pm

Re: Contention pattern for HALT

Post by Patrik Rak »

ZjoyKiLer wrote: Wed Jan 24, 2024 5:01 pm I handle it differently. I have a special loop for the HALT state.
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.
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Contention pattern for HALT

Post by ketmar »

Patrik Rak wrote: Sun Feb 04, 2024 2:28 pm I also consume all the remaining T-cycles in the HALT itself.
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
Microbot
Posts: 117
Joined: Mon Apr 13, 2020 3:07 pm

Re: Contention pattern for HALT

Post by Patrik Rak »

ketmar wrote: Sun Feb 04, 2024 10:48 pm sadly, this doesn't quite work if you need a sophisticated breakpoint system.
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.
User avatar
ketmar
Manic Miner
Posts: 713
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Contention pattern for HALT

Post by ketmar »

Patrik Rak wrote: Sun Feb 18, 2024 10:55 am Why not check if it can be done or not?
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.).
Post Reply