Skip to content

Instantly share code, notes, and snippets.

@drhelius
Last active October 13, 2024 15:09
Show Gist options
  • Save drhelius/8497817 to your computer and use it in GitHub Desktop.
Save drhelius/8497817 to your computer and use it in GitHub Desktop.
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
@skiselev
Copy link

skiselev commented May 3, 2024

The BM1 specific behavior doesn't seem to work.
I tried testing two КР1858ВМ1 that I have along with other Z80 CPUs and clones, using following sequences of commands:

LD A,28H
LD (BC),A
BIT 0,(HL)

And as the result I get bits 5 and 3 set in the flags, just like on other Z80 CPUs. According to the document above, on these CPUs, MEMPTR_hi should be set to 0 after running LD (BC),A, and so if the documentation is correct, the flags register bits 5 and 3 would be 0 after running BIT 0,(HL)

@drhelius
Copy link
Author

drhelius commented May 3, 2024

This document is not mine, just posted it here for preservation and for my own reference.
I wasn't able to test this behavior in real hardware but is really interesting you cannot replicate the BM1 differences.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment