Indirect calls with the Z80

Page 2/4
1 | | 3 | 4

By Grauw

Ascended (8516)

Grauw's picture

06-04-2018, 21:19

My Glass assembler follows standard Zilog notation, so no shenanigans like add a Wink, however I’ve made an exception for jp (hl) which is just plain misleading. So nowadays I can always type jp hl.

Smile

By DarkSchneider

Paladin (882)

DarkSchneider's picture

06-04-2018, 21:37

@theNestruo @NYYRIKKI I know it really executes the HL content not the content at address pointed by HL. But I write ASM in M80 code that is the assembler I use, and in M80 is written like that.
Look that I put on HL the address to execute.
Read the M80 JP (HL) like JP HL of your assemblers.

By hit9918

Prophet (2868)

hit9918's picture

06-04-2018, 21:53

there is another level of confusion Big smile when the code was made to really jump into the table because the table itself contains jump opcodes.
the example code with sla a, it may do for a rack of JR opcodes a 2 byte.

By ARTRAG

Enlighted (6287)

ARTRAG's picture

06-04-2018, 21:57

DarkSchneider you jump in the middle of the table executing it as code.
Your functions simply cannot work. Not with a table of addresses at least.
Look at theNestruo's code or santiontanon's case if you want a correct reference.

@Santi, a variation on your case/switch is this

ld a,(function_index)
ld b,a
djnz not1
FUNCTION1:
[...]
not1: djnz not2
FUNCTION2:
[...]
not2: djnz not3
FUNCTION3:
[...]

It works best when each case fits in 127 byte

By Grauw

Ascended (8516)

Grauw's picture

06-04-2018, 23:01

However actually jumping into a table is a good idea, if you make the table consist of jumps rather than addresses, it’s quite a bit faster Smile.

@DarkSchneider No, jp (hl) is official syntax, not some M80 specific notation or anything like that. I think not many assemblers allow you to write it as jp hl. I just did it in mine because it bugs me.

By MOA

Champion (293)

MOA's picture

07-04-2018, 04:09

So we're talking about virtual tables like in C++? I quickly put this together:

; in: a = func (0..127), ix = this
; virtual function cannot use hl, af for arguments
call_virtual:
	; fetch vtable ptr
	ld l,(ix+0)
	ld h,(ix+1)
	; index into it
	add a,a    ; can get rid of this instruction when you make it a pre-condition for the input
	add a,l
	ld l,a
	; can get rid of the next 3 instructions when virtual table doesn't cross 256 byte boundary
	ld a,h
	adc a,0
	ld h,a
	; jump to function in vtable
	jp (hl)

; so most optimal version would look like:
;  pre: a = function idx * 2, vtable doesn't cross 256 byte boundary
call_virtual_optimal:
	; fetch vtable ptr & index into it
	add a,(ix+0)
	ld h,(ix+1)
	ld l,a
	; jump to function in vtable
	jp (hl)

; or... even better (probably fastest) if you use hl for this ptr
;   (but it doesn't preserve the 'this' ptr, so might require extra instructions if you need it)
;  pre: a = func idx * 2, vtable doesn't cross 256 byte boundary
call_virtual_optimal:
	; fetch vtable ptr & index into it
	add a,(hl)
	inc l
	ld h,(hl)
	ld l,a
	; jump to function in vtable
	jp (hl)

By Grauw

Ascended (8516)

Grauw's picture

07-04-2018, 04:21

Nice overview. I think a multiple of two index is fair, and the vtable alignment too tbh, but here’s a slight optimisation for an unaligned vtable (13 cycles more than call_virtual_optimal(1)):

call_virtual:
    add a,(ix+0)
    ld h,(ix+1)
    ld l,a
    jr nc,$+3
    inc h
    jp (hl)

By MOA

Champion (293)

MOA's picture

07-04-2018, 04:40

Indeed, very nice optimization.

my guts were saying "ld + adc + ld (no branches)" is faster, but you're right: on z80 it isn't... modern hardware doesn't like those jumps at all.

By Grauw

Ascended (8516)

Grauw's picture

07-04-2018, 04:42

I haven’t used separate vtables so far though. When I need polymorphism I usually either:

  1. Embed the vtable in the object as method pointers like I do for Driver_PrintInfo here.
  2. Embed a jump entry table in the object. The first one only needs a jp ix, but you can also have additional ones while preserving the “this” pointer by starting the method with adding the negated jump offset to ix.

But worse for size since each object increases with the size of the table, but both are pretty fast and usually you won’t have so many virtual methods anyway.

By MOA

Champion (293)

MOA's picture

07-04-2018, 04:42

Grauw wrote:

I haven’t used separate vtables so far though. When I need polymorphism I usually either:

  1. Embed the vtable in the object as method pointers like I do for Driver_PrintInfo here.
  2. Embed a jump entry table in the object. The first one only needs a jp ix, but you can also have additional ones while preserving the “this” pointer by starting the method with adding the negated jump offset to ix.

Thanks :) Nice reading material for me tomorrow, as I should hit the sack. Are you up - like me - so late, or up very early? hehe ;)

Page 2/4
1 | | 3 | 4