It's hard to tell at the moment, as I have no way of easily running my own code yet on the Spectrum +2. It's not a system I'm particularly familiar with, and I'm currently reliant on exiting programs I can load in from tape. Very primitive compared to what I'm used to!

I think this needs to be systematically investigated. First, you would need to setup a test case where a LDIR is consistently interrupted. Then it should be possible, by changing just one thing at a time, to determine what has an influence over the results.

As far as I know, nobody has done this kind of investigation yet. It would also be interesting to see what happens with CPIR/CPDR when interrupted, as well. The known "formula" for it's YF/XF is complex but it gave quite a good insight into the internal operation on that data path segment.

Could be a red herring, but it could well be something floating on the bus when the interupt happens. The repeating instructions are "odd" in that they decrement the PC to do the repeat, that PC decrement could be related.

Another thought, flags bit 5 and bit 3 are usually set to bit 5 and 3 of some data that is being processed, documentation says bit 5 is set from bit one of some internal data. Have you checked if it is actually bit five?

"The Undocumented Z80 Documented" has also been mentioned in this thread but it has not been updated for years and is not 100% correct, e.g. most of what it says about HALT is wrong, as explained on the Special Reset page.

There was a topic in one of the World of Spectrum forums by someone who investigated the Z80 registers after a reset. By an amazing piece of detective work he discovered the Special Reset without knowing about it. I'll try to find the topic again if it's still available as it's well worth reading, even if you consider the Special Reset to have little or no practical application. (In fact it was included in the Z80 to make the hardware in the Zilog in-circuit emulator simpler.)

Last edited by TonyB on Sat Aug 11, 2018 8:12 pm, edited 1 time in total.

After fixing a few bugs, I'm now able to decode this trace without any failures being flagged by my Z80 emulation code.

So I'm pretty happy now that the emulation is in good shape.

There are just two cases I know of now where I'm not able to predict the correct values for the undocumented F5/F3 flags (a.k.a. YF and XF):
- following SCF (Set carry) or CCF (Complement carry)
- following LDIR (and probably LDDR/LDI/LDD)

The first case (with SCF/CCF) I understand. It turns out the setting of the undocumented F5/F3 flags in SCF and CCF actually depends on whether the preceding instruction modified the flags or not. This is described in this post. I don't see any problem in implementing this, I just haven't done so yet,

The second case (with LDIR) has me very confused. It seems that the current understanding is that F5 and F3 flags take on bits 1 and 3 of (data + A). I'm doing this, and still seeing errors.

If you look at the flags value in the final line (marked fail), you can see the F3 flags has been "corrected" from 0 to 1 based on what was PUSHed to memory. This represents reality, i.e. what the Z80 actually set them to.

The unofficial documentation says these should be based on (data + A) = (0x38 + 0x38) = 0x70. So F5 should be bit 1 and F3 should be bit 3, both 0. That how I'm calculating them. But it seems the Z80 is actually doing something different, as the value pushed shows F3 is actually 1.

I'm sure I'm missing something here, but I can't for the life of me figure it out.

Dave

I think it would be less confusing to call bits 5 and 3 of the flags YF and XF consistently when talking about them individually. In your logs you mix letters and numbers after F= and I suggest displaying Y and X if they are 1 and spaces if they are 0, to be consistent with the other flags.

Regarding the behaviour of LDIR when interrupted, I wonder whether YF and XF come from just A or (HL), not A + (HL). Can you test this, with values that are not both 38H?

It should be possible to discover the exact behaviour of YF and XF in block instructions that are interrupted, on a Spectrum with your current test hardware. The frame time is 69888 T on the 16K/48K and 70908T on 128K/+2/+2A/+3. By using HALT, then a carefully calculated software timing loop, you should be to get an LDIR interrupt to occur when BC has any value of your choosing. (You'll need to allow for the interrupt routine time, of course.) HALT takes 4T, therefore the uncertainty of precisely when /INT goes low is 0-4T, much less than 21T length of LDIR.

I think it would be less confusing to call bits 5 and 3 of the flags YF and XF consistently when talking about them individually. In your logs you mix letters and numbers after F= and I suggest displaying Y and X if they are 1 and spaces if they are 0, to be consistent with the other flags.

Regarding the behaviour of LDIR when interrupted, I wonder whether YF and XF come from just A or (HL), not A + (HL). Can you test this, with values that are not both 38H?

It should be possible to discover the exact behaviour of YF and XF in block instructions that are interrupted, on a Spectrum with your current test hardware. The frame time is 69888 T on the 16K/48K and 70908T on 128K/+2/+2A/+3. By using HALT, then a carefully calculated software timing loop, you should be to get an LDIR interrupt to occur when BC has any value of your choosing. (You'll need to allow for the interrupt routine time, of course.) HALT takes 4T, therefore the uncertainty of precisely when /INT goes low is 0-4T, much less than 21T length of LDIR.

After some investigation with help from BigEd and his real Z80 Co Pro, we've determined they come from bits 13 and 11 of the PC (i.e. the address of the LDIR instruction). Plugging that knowledge into the decoder has eliminated all of the mis-predication errors:https://github.com/hoglet67/Z80Decoder/ ... 80.c#L2104

After some investigation with help from BigEd and his real Z80 Co Pro, we've determined they come from bits 13 and 11 of the PC (i.e. the address of the LDIR instruction). Plugging that knowledge into the decoder has eliminated all of the mis-predication errors:https://github.com/hoglet67/Z80Decoder/ ... 80.c#L2104

I still have to figure out what's happening in the ST CMOS Z80, as it doesn't match either of the NMOS cases.

Unfortunately I can't access github nowadays due to authentication errors as I use an old PC. Are the links just to C code or is there more discussion there?

What you have found is new information. I imagine that CPxR, INxR and OTxR operate in the same way.

The final iteration of LDIR is an LDI and therefore the two instructions have the same flags on exit. The extra machine cycle (M4) in LDIR clearly involves the ALU, which overwrites the LDI values of YF and XF with bits from PC. Is that simply a side-effect or does the ALU subtract two from PC? Using the 16-bit incrementer/decrementer to decrement PC twice would seem to be easier and faster.

After some investigation with help from BigEd and his real Z80 Co Pro, we've determined they come from bits 13 and 11 of the PC (i.e. the address of the LDIR instruction).

That makes sense, as the last bit of execution done before an INT is responded to is PC=PC-2 which does PClo=PClo-2; PChi=PChi-Carry, and bit 13/11 are bit 5/3 of PChi. Almost every instruction that copies bit 7 of something to bit 7 of the flags to set the P/M flag actually copies bits 7/5/3.

I vaguely remember reading that the PC=PC-2 is done using the JR silicon stuffing an offset of &FE into the process.

What you have found is new information. I imagine that CPxR, INxR and OTxR operate in the same way.

That's encouraging to hear.

I will try to check the other instructions, but I'm thinking that:
- CPiR updates the flags (including XF and YF) with the comparison at each step
- INxR/OTxR updates the flags (including XF and YF) with B - 1 at each step.

So being interrupted would not expose anything different to this.

For some reason, LDxR is different. This is possibly because PF is coming from the incrementer (BC == 1) rather than the ALU.

The final iteration of LDIR is an LDI, which is why the two instructions have the same flags on exit.
The extra machine cycle (M4) in LDIR clearly involves the ALU, which overwrites the LDI values of YF and XF with bits from PC. Is that simply a side-effect or does the ALU subtract two from PC? Using the 16-bit incrementer/decrementer to decrement PC twice would seem to be easier and faster.

The other thing I can't explain is why WZ is updated during LDIR (apparently it ends up at PC + 1). I think that's possible related as well.

I vaguely remember reading that the PC=PC-2 is done using the JR silicon stuffing an offset of &FE into the process.

Ah, faulty memory, I was remembering the JR cc,-1 trick to do a conditional RST &38.

The PC=PC-2 uses the same increment/decrementer circuitry that is used for the normal 16-bit INCs and DECs and the PC increment and - where the 2 comes in - pushing and pulling from the stack which has two INCs or DECs. As the 16-bit INC/DEC circuitry has no interaction with the ULA it doesn't change any flags, so the observed flag changing effects must be a result of the test for BC=0 and there must be some sort of "leakage" through to the flags. DEC BC within a block instruction is the only time a 16-bit INC/DEC affects the flags.

What you have found is new information. I imagine that CPxR, INxR and OTxR operate in the same way.

That's encouraging to hear.

I will try to check the other instructions, but I'm thinking that:
- CPiR updates the flags (including XF and YF) with the comparison at each step
- INxR/OTxR updates the flags (including XF and YF) with B - 1 at each step.

So being interrupted would not expose anything different to this.

For some reason, LDxR is different. This is possibly because PF is coming from the incrementer (BC == 1) rather than the ALU.

The other thing I can't explain is why WZ is updated during LDIR (apparently it ends up at PC + 1). I think that's possible related as well.

I wasn't very clear. My conjecture is that XF and YF will be the same for all the block instructions only when they are interrupted. XF = PC[11] and YF = PC[13] is consistent with the flags for this 16-bit subtraction: PC+2 - 2 = PC, so the ALU could be changing PC. The 16-bit inc/dec is used for HL, DE and BC in LDx and in theory should be available during the extra 5T in LDxR but perhaps it isn't.

What is the source of the WZ info?

Last edited by TonyB on Sun Aug 12, 2018 8:45 pm, edited 1 time in total.

MEMPTR, esoteric register of the ZiLOG Z80 CPU.
by Boo-boo (first and draft English translation by Vladimir Kladov)
As it is known, after the instruction BIT n,(HL) execution, bits 3 and 5 of the flag register become containing values that is not documented in the official documentation at all. Actually these bits are copied from the bits 11 and 13 of the internal register pair of Z80 CPU, which is used for 16-bit operations, and in most cases to handle addresses. This is usual practice for processors having 8-bits data bus working with 16-bits data.
It is not known why and how these bits of the internal buffer register are copied to the flags register though. At least Sean Young in the "Undocumented Z80 Documented" refers to that phenomenon (http://www.myquest.nl/z80undocumented/) and a bit more info can be found in the Z80 description of another "nocash" project (http://www.work.de/nocash/zxdocs.htm) where such register pair is called as MEMPTR. Unfortunately until now attemts to crack the algorithm setting the value of the MEMPTR by different processor instructions on base of knowning only two bits of those 16-bits register were not successful.
But miraculously in result of many experiments (based on the hyposesis that index addressing instructions initialize the MEMPTR always the same way) and also after the deep meditations under the results of these samples we have found that CPI instruction increments the MEMPTR by 1 whereas CPD instruction decrements it. Hence, decrementing the MEMPTR in the loop and monitoring borrow from the high bits having two known bits in the flag register, it is possible to determine unambigously 14 low bits of the MEMPTR and having these in our hands to say for sure on which rule MEMPTR is set after each instruction.
A list of instructions changing the MEMPTR is follow, together with the formula for new MEMPTR value. Here "rp" means register pair (16 bits register BC, DE, HL or SP - ?), and "INDEX" means register pair IX or IY. Instructions not listed below do not affect MEMPTR as it is found. All the CPU chips tested give the same results except KP1858BM1 and T34BM1 slices noted as "BM1" in the text.
====================================================================================
LD A,(addr)
MEMPTR = addr + 1
LD (addr),A
MEMPTR_low = (addr + 1) & #FF, MEMPTR_hi = A
Note for *BM1: MEMPTR_low = (addr + 1) & #FF, MEMPTR_hi = 0
LD A,(rp) where rp -- BC or DE
MEMPTR = rp + 1
LD (rp),A where rp -- BC or DE
MEMPTR_low = (rp + 1) & #FF, MEMPTR_hi = A
Note for *BM1: MEMPTR_low = (rp + 1) & #FF, MEMPTR_hi = 0
LD (addr), rp
LD rp,(addr)
MEMPTR = addr + 1
EX (SP),rp
MEMPTR = rp value after the operation
ADD/ADC/SBC rp1,rp2
MEMPTR = rp1_before_operation + 1
RLD/RRD
MEMPTR = HL + 1
JR/DJNZ/RET/RETI/RST (jumping to addr)
MEMPTR = addr
JP(except JP rp)/CALL addr (even in case of conditional call/jp, independantly on condition satisfied or not)
MEMPTR = addr
IN A,(port)
MEMPTR = (A_before_operation << 8) + port + 1
IN A,(C)
MEMPTR = BC + 1
OUT (port),A
MEMPTR_low = (port + 1) & #FF, MEMPTR_hi = A
Note for *BM1: MEMPTR_low = (port + 1) & #FF, MEMPTR_hi = 0
OUT (C),A
MEMPTR = BC + 1
LDIR/LDDR
when BC == 1: MEMPTR is not changed
when BC <> 1: MEMPTR = PC + 1, where PC = instruction address
CPI
MEMPTR = MEMPTR + 1
CPD
MEMPTR = MEMPTR - 1
CPIR
when BC=1 or A=(HL): exactly as CPI
In other cases MEMPTR = PC + 1 on each step, where PC = instruction address.
Note* since at the last execution BC=1 or A=(HL), resulting MEMPTR = PC + 1 + 1
(if there were not interrupts during the execution)
CPDR
when BC=1 or A=(HL): exactly as CPD
In other cases MEMPTR = PC + 1 on each step, where PC = instruction address.
Note* since at the last execution BC=1 or A=(HL), resulting MEMPTR = PC + 1 - 1
(if there were not interrupts during the execution)
INI
MEMPTR = BC_before_decrementing_B + 1
IND
MEMPTR = BC_before_decrementing_B - 1
INIR
exactly as INI on each execution.
I.e. resulting MEMPTR = ((1 << 8) + C) + 1
INDR
exactly as IND on each execution.
I.e. resulting MEMPTR = ((1 << 8) + C) - 1
OUTI
MEMPTR = BC_after_decrementing_B + 1
OUTD
MEMPTR = BC_after_decrementing_B - 1
OTIR
exactly as OUTI on each execution. I.e. resulting MEMPTR = C + 1
OTDR
exactly as OUTD on each execution. I.e. resulting MEMPTR = C - 1
Any instruction with (INDEX+d):
MEMPTR = INDEX+d
Interrupt call to addr:
As usual CALL. I.e. MEMPTR = addr
====================================================================================
What is the profit of which secret knowledge? First of all, it is possible now to program Z80 emulators supporting _all_ the undocumented pecularities of the CPU. In the second place the fact that on some Z80 clones MEMPTR register behaves a bit another adds a method of model checking. Seems very enough!
(c)2006, zx.pk.ru
Theoretical part: boo_boo, Vladimir Kladov
Testing real Z80 chips: Wlodek, CHRV, icebear, molodcov_alex, goodboy

So this implies MEMPTR is involved during cycle 17-21 when the PC is decremented, but not elsewhere.

Thanks, I have a copy of the MEMPTR info that I'd forgotten about. It does suggest that WZ/MEMPTR is written after the first decrement of PC+2 by the 16-bit decrementer and that the ALU flags are a side-effect. Apologies for the insulting question, but did you test LDIR at address xxFE or xxFF?

I got a bit lost on the thread as so many others to follow, but my rc2014 was a Zilog Z84C0010PEG z80 if anyone needs me to check anything. If already have one of those for testing then it lets me off the hook!

I got a bit lost on the thread as so many others to follow, but my rc2014 was a Zilog Z84C0010PEG z80 if anyone needs me to check anything. If already have one of those for testing then it lets me off the hook!

Mostly of the last few days has been a deep dive into some of the undocumented aspects of the Z80, that are in fact visible and can (and do) get flagged up as suspicious by the decoder.

I am interested in how the CMOS Z80s differ from the NMOS ones - so far there is the "bug" in OUT (C),0 and differences in the XF and YF flags after SCF and CCF. But that was with a ST CMOS Z80, not a Zilog one.

[Thanks for the info. Perhaps you could check MEMPTR too, on a Zilog Z80?

The tests so far on the Z80 Co Pro have used a SGS Z8400BB1 Z80B CPU (6MHz) datecode 88346.

When you say check MEMPTR, what do you mean exactly?

i.e. Attempt to validate the info in that document, or something more specific?

SGS CPU is fine as that's a genuine Zilog second-source. Re MEMPTR, I mean validating just the LDIR behaviour in the document, if possible. I think the tricky bit is making sure WZ is not used between the interrupted LDIR and BIT x,(HL), which rules out IM 2.

Last edited by TonyB on Sun Aug 12, 2018 11:23 pm, edited 2 times in total.

Re MEMPTR, I mean validating just the LDIR behaviour in the document, if possible. I think the tricky bit is making sure WZ is not used between the interrupted LDIR and BIT x,(HL), which rules out IM 2.

It sounds like you have better documentation about MEMPTR than I have.

It should be possible to temporarily use IM 1 in the Acorn Z80 Co Pro. RST &38 is used for the error handler, but could be borrowed for a while. But are you sure IM 1 doesn't also use MEMPTR (as the RST instrucion seems to)?

Re MEMPTR, I mean validating just the LDIR behaviour in the document, if possible. I think the tricky bit is making sure WZ is not used between the interrupted LDIR and BIT x,(HL), which rules out IM 2.

It sounds like you have better documentation about MEMPTR than I have.

It should be possible to temporarily use IM 1 in the Acorn Z80 Co Pro. RST &38 is used for the error handler, but could be borrowed for a while. But are you sure IM 1 doesn't also use MEMPTR (as the RST instrucion seems to)?

Dave

I mentioned IM 2 only because it involves indirect addressing and must write WZ. The Memptr doc says RST writes WZ and IM 1 does a RST 38H. It also says an interrupt call to an address changes WZ and this could be intended to refer to IM 2. Anyway, it seems that both IM 2 and IM 1 destroy the previous value of WZ.

This still leaves IM 0, which could be used with a little bit of circuitry. The best opcode to put on the data bus during the interrupt acknowledge cycle is E9H for JP (HL) as this does not modify WZ, according to the doc. Incidentally, this is a good example of why the instruction mnemonic should be JP HL. If the addressing were indirect then WZ would be overwritten. E9H = 11101001B, so D1/2/4 must be low when /IORQ and /M1 are low.

I don't know anything about the Acorn Z80 Co Pro. Are there pull-up resistors on the data bus, or does is it just float high? Checking WZ by using BIT x,(HL) is additional to testing the flags when block instructions are interrupted and I could understand if you don't feel inclined to do it.

I think there is a way to find out what the flags are at the end of every machine cycle, so that one could see how the flags change or not during instructions with multiple M cycles. The method might also expose WZ at the same time. It requires a small PLD, a GAL22V10 would do the job and maybe only a 16V8, but it does not use any interrupts.

Last edited by TonyB on Thu Aug 30, 2018 7:56 pm, edited 5 times in total.