Page 1 of 1

EX (SP),HL

Posted: Mon Aug 12, 2019 2:19 am
by Cosmium
Over the past year I've occasionally perused the Z80 opcodes and timings ( :geek: ) to get reacquainted with - and programming inspiration for - little neat features of the Z80 I've forgotten or seldom used.

One such instruction is EX (SP),HL which appears to be quite powerful at 19T states considering it's a 16 bit register swap with what's on the stack.

I've never actually used it and was wondering when would be a good case to. Maybe tight loops when all the registers have already been used? I feel like it holds quite a bit of potential!

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 10:51 am
by Ralf
I believe we had in the past a discussion about this particular instruction. Maybe on WOS yet, not here ;)

I don't remember details but people said it's useful when you write things like Basic interpreter or another high level
language compiler/interpreter. Something with fetching instructions and variables.

Personally I have a different experience. For me it's useless, I've never used it.
I would gladly trade it for EX SP,HL :) Exchanging register values, not register and value in memory,

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 12:31 pm
by Joefish
When used in the combination POP HL // EX (SP),HL it lets you get the 'top-but-one' value off the stack, and copies the top value of the stack down in its place. You can actually undermine the stack by one slot with these two instructions.

It's for programming C-type function calls, where parameters are pushed onto the stack before calling the subroutine. You don't really want to leave them there, but you've got the problem that (from your subroutine's point of view) the RETurn address is at the top of the stack, not the parameters.

So you first do POP HL, which gets the function return address in HL. Now the stack pointer points to the last parameter PUSHed before the CALL. Then you do EX (SP),HL which fetches that parameter into HL, and puts the return address back in that slot on the stack, so you can still do a RET at the end of the function.

If there's another parameter on the stack, then you copy H and L into other registers for later, and do the same trick again:
POP HL
EX (SP),HL
That gets another parameter off the stack into HL, but puts the return address down in its place ready for a RET.

If that wasn't possible then you'd either (a) waste a register preserving the RETurn address while you tidied up the stack or (b) in your main routine, have to tidy up the stack after every CALL to erase the parameters you put there.

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 1:49 pm
by Ast A. Moore
Yes, it’s not a very useful instruction for game programming. I think I remember Joffa using it in Cobra for some trivial printing routine. While it’s common to use the stack for fetching the address of the beginning of a text string, in many cases you can do it much simpler just using PUSH/POP.

Cf.:

Code: Select all

	ex (sp),hl
	call printer
	ex (sp),hl
	ret


printer	ld a,(hl)	;load char. into A
	inc hl		;next address
	or a
	ret z		;return if 0 (i.e. EOS)
vs.

Code: Select all

	call printer
string	defm "Text string",0
	ret

printer
	pop hl		;grab stack address (after CALL; beginning of string)
	ld a,(hl)	;load char. into A
	inc hl		;next address
	push hl		;back onto the stack
	or a
	ret z		;return to the address right after EOS

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 2:11 pm
by Einar Saukas
Joefish wrote: Mon Aug 12, 2019 12:31 pm When used in the combination POP HL // EX (SP),HL it lets you get the 'top-but-one' value off the stack, and copies the top value of the stack down in its place. You can actually undermine the stack by one slot with these two instructions.

It's for programming C-type function calls, where parameters are pushed onto the stack before calling the subroutine. You don't really want to leave them there, but you've got the problem that (from your subroutine's point of view) the RETurn address is at the top of the stack, not the parameters.

So you first do POP HL, which gets the function return address in HL. Now the stack pointer points to the last parameter PUSHed before the CALL. Then you do EX (SP),HL which fetches that parameter into HL, and puts the return address back in that slot on the stack, so you can still do a RET at the end of the function.

If there's another parameter on the stack, then you copy H and L into other registers for later, and do the same trick again:
POP HL
EX (SP),HL
That gets another parameter off the stack into HL, but puts the return address down in its place ready for a RET.

If that wasn't possible then you'd either (a) waste a register preserving the RETurn address while you tidied up the stack or (b) in your main routine, have to tidy up the stack after every CALL to erase the parameters you put there.
It's used extensively in z88dk exactly this way, for instance:

BIFROST*2
NIRVANA+
ULAplus
ZX7

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 6:19 pm
by arkannoyed
I used it to very good effect in 3D Chess. It’s used in the main routine to swap the screen address in HL with the address in IX that just got PUSHed into the stack.

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 6:31 pm
by Joefish
Yes, you could also think of it as a way of swapping between two address values (one on the stack, one in HL), pulling one up from before and saving the current value for later. Much as you might use EX DE,HL or EXX, though obviously it's not as quick. So it's more about code space-efficiency rather than time-efficiency.

Re: EX (SP),HL

Posted: Mon Aug 12, 2019 7:06 pm
by Cosmium
Useful explanations, thanks :)

So if say you've got some time critical loop code. You've have run out of 16 bit registers and have a situation where you'll be swapping between the use of two addresses.

You could PUSH the address, put the other one in HL, then in the main loop use EX (SP),HL to alternate between them. In effect, you're using the stack's contents as a 'second shadow' HL, that just so happens to be held in memory, so naturally it will be slower than EXX and EX DE,HL, but faster than the traditional memory or stack-based alternatives that would normally be used to achieve the same thing?

Re: EX (SP),HL

Posted: Fri Sep 20, 2019 10:47 pm
by utz
Perhaps not so useful for most coders, but I often use 2 of them in sequence for timing purposes. Just 2 bytes for wasting 38 t-states - can't get any more bang for the buck than this on Z80.

Re: EX (SP),HL

Posted: Sat Sep 21, 2019 2:11 am
by djnzx48
You can use it as an easy way to swap HL and IX.

PUSH HL
EX (SP), IX
POP HL

Re: EX (SP),HL

Posted: Sat Sep 21, 2019 5:06 am
by Cosmium
djnzx48 wrote: Sat Sep 21, 2019 2:11 am You can use it as an easy way to swap HL and IX.

PUSH HL
EX (SP), IX
POP HL
That's a neat use of EX (SP), IX!

The alternative:

PUSH HL
PUSH IX
POP HL
POP IX

is 6Ts and 2 bytes longer by my calculations, so a worthwhile trick to add to the toolbox, thanks :)