EX (SP),HL
EX (SP),HL
Over the past year I've occasionally perused the Z80 opcodes and timings ( ) 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!
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!
Cosmium
https://cosmium.itch.io/
https://cosmium.itch.io/
Re: EX (SP),HL
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,
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
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 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.
- Ast A. Moore
- Rick Dangerous
- Posts: 2641
- Joined: Mon Nov 13, 2017 3:16 pm
Re: EX (SP),HL
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.:
vs.
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)
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.
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.
- Einar Saukas
- Bugaboo
- Posts: 3139
- Joined: Wed Nov 15, 2017 2:48 pm
Re: EX (SP),HL
It's used extensively in z88dk exactly this way, for instance: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.
BIFROST*2
NIRVANA+
ULAplus
ZX7
- arkannoyed
- Manic Miner
- Posts: 438
- Joined: Mon Feb 05, 2018 9:56 am
- Location: Northamptonshire
Re: EX (SP),HL
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
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
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?
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?
Cosmium
https://cosmium.itch.io/
https://cosmium.itch.io/
Re: EX (SP),HL
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
You can use it as an easy way to swap HL and IX.
PUSH HL
EX (SP), IX
POP HL
PUSH HL
EX (SP), IX
POP HL
Re: EX (SP),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
Cosmium
https://cosmium.itch.io/
https://cosmium.itch.io/