EX (SP),HL

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
Cosmium
Microbot
Posts: 154
Joined: Tue Dec 04, 2018 10:20 pm
Location: USA

EX (SP),HL

Post 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!
Ralf
Rick Dangerous
Posts: 2279
Joined: Mon Nov 13, 2017 11:59 am
Location: Poland

Re: EX (SP),HL

Post 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,
User avatar
Joefish
Rick Dangerous
Posts: 2041
Joined: Tue Nov 14, 2017 10:26 am

Re: EX (SP),HL

Post 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.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2640
Joined: Mon Nov 13, 2017 3:16 pm

Re: EX (SP),HL

Post 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
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Einar Saukas
Bugaboo
Posts: 3070
Joined: Wed Nov 15, 2017 2:48 pm

Re: EX (SP),HL

Post 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
User avatar
arkannoyed
Manic Miner
Posts: 435
Joined: Mon Feb 05, 2018 9:56 am
Location: Northamptonshire

Re: EX (SP),HL

Post 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.
User avatar
Joefish
Rick Dangerous
Posts: 2041
Joined: Tue Nov 14, 2017 10:26 am

Re: EX (SP),HL

Post 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.
User avatar
Cosmium
Microbot
Posts: 154
Joined: Tue Dec 04, 2018 10:20 pm
Location: USA

Re: EX (SP),HL

Post 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?
User avatar
utz
Microbot
Posts: 113
Joined: Wed Nov 15, 2017 9:04 am
Contact:

Re: EX (SP),HL

Post 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.
User avatar
djnzx48
Manic Miner
Posts: 729
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: EX (SP),HL

Post by djnzx48 »

You can use it as an easy way to swap HL and IX.

PUSH HL
EX (SP), IX
POP HL
User avatar
Cosmium
Microbot
Posts: 154
Joined: Tue Dec 04, 2018 10:20 pm
Location: USA

Re: EX (SP),HL

Post 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 :)
Post Reply