Is there a way to get Program Counter (PC) on Z80?

Por DarkSchneider

Paladin (1011)

imagem de DarkSchneider

22-02-2023, 16:31

Looking at instruction set but cannot find. I would be interested in supported instructions/methods.

Entrar ou registrar-se para comentar

Por aoineko

Paladin (1002)

imagem de aoineko

22-02-2023, 16:47

PC is set by instructions like JP, JR, CALL or RET.

The PC value at the point you want to check it is always the address of the instruction at the point you want to check it. Smile

What do you want to achieve exactly?

Por sjoerd

Hero (609)

imagem de sjoerd

22-02-2023, 16:52

  call pc_on_stack
pc_on_stack:
  pop hl                              ; pc in hl

Por bore

Master (165)

imagem de bore

22-02-2023, 17:30

The assembler/linker will typically know where the PC is at all times so you can just load it directly to hl:

addr:	ld	hl, addr

or

	ld	hl, $

The fun thing with sjoerds version is that it can be put in a function that you can call:

	; returns callers program counter in hl
get_pc:
	pop	hl
	jp	(hl)

Por DarkSchneider

Paladin (1011)

imagem de DarkSchneider

22-02-2023, 18:53

@bore yes I though about using $, but that is at compile time.

Maybe I had to expose the case: it is for a chunk of code that will be moved, it only uses relative jumps and has some part like this:
if content of some address != 0 then call it, if not continue.
As the code is going to be moved, it was necessary something to return from an indirect call.

The method posted by @sjoerd looks interesting.

I think it can be solved in a fast way putting in somewhere in a fixed memory location:

somewhere_hook:
jp 0x0000

Somewhere_hook is a fixed location and followed by the address to check.
Then in the code that is going to be moved:

“check” (the 2 bytes at somewhere_hook + 1)
call nz, somewhere_hook

Then after the execution of that indirect call it will return to the right location.

Por Ped7g

Expert (67)

imagem de Ped7g

22-02-2023, 20:40

on ZX Spectrum you can sometimes write those two instructions `pop hl : jp (hl)` to VRAM at fixed address like 0x4000, and `call 0x4000` will load HL with following address -> this way you can figure out your running PC even if you don't know where your code was loaded and who started it from where. If you have similar two-byte buffer on MSX which is usually free and can be overwritten and called, you can use the sjoerd's code for runtime detection. (And then for example relocate rest of your code accordingly, for example something like in this ZX example (it expect starting address in BC as the ZX Basic provides it, but you can patch it to use the sjoerd's trick and use value from HL): https://github.com/z00m128/sjasmplus/blob/master/examples/re...

It's not possible to write completely relative [non trivial] code with Z80, eventually you need some `call` or `jp`, only 8bit -126..+129 range is covered by `jr/djnz` relative jumps, so usually everyone does place code at fixed position and assembles it like that, but you can use the relocation trick with table data to patch existing code.

Por theNestruo

Champion (421)

imagem de theNestruo

22-02-2023, 23:01

DarkSchneider wrote:

if content of some address != 0 then call it, if not continue.
As the code is going to be moved, it was necessary something to return from an indirect call.

A different approach:

JP_HL:
    jp (hl)

(...)
    ld hl, somewhere_hook
    ld a, h
    or l
    call nz, JP_HL
(...)

Or this, slighly slower but smaller if you use it in more than a couple of places:

JP_HL_IF_NOT_ZERO:
    ld a, h
    or l
    ret z
JP_HL:
    jp (hl)

(...)
    ld hl, somewhere_hook
    call JP_HL_IF_NOT_ZERO
(...)

Por Daemos

Prophet (2061)

imagem de Daemos

23-02-2023, 09:03


Call getpc ;returns pc in hl

Getpc:
Pop hl
Push hl
Ret


Por DarkSchneider

Paladin (1011)

imagem de DarkSchneider

23-02-2023, 12:26

Yep the good is that code like that is rellocatable on any place so only requires a very small ammount of space in a fixed area.

About rellocatable code, relative jumps of more than 120 bytes are not bad, and if required larger can use trampolines (that are just another JR) that you skip (with another JR) in a normal execution (not reached by a condition that required that trampoline).
So we can make good pieces of code to be allocated in stack when required to be visible from any slot/page/segment combination (like ISR) or for meta-programming.