Accessing tables with Z80 ASM

Page 1/3
| 2 | 3

By tokumaru

Expert (83)

tokumaru's picture

10-07-2006, 04:56

Hi again guys.

I'm still learning Z80 ASM, so I've been looking at some sample code around here. The thing is I usually use a lot of look-up tables in my programs, and it seems that the Z80 lacks an efficient way to load information from them.

All codes I've seen that use tables have loaded the index of the element into hl and then added the base address of the table. That seems very slow. in 6502 ASM, I'm used to loading values from tables using 8-bit index registers, and the 16-bit base address goes along with the load intruction (or can also be in RAM, wich is usefull for big tables). It's simple and fast, 'cause there is no add, just load the index and load the value from the table.

Now, is that really true that you must perform an addition (worse, a 16-bit addition) to find the location of an element in a table? I can only imagine how bad it gets when you are working with multiple-byte values, where adding the base address to the element index is not enough, you'd also have to multiply the index of the element by the number of bytes of each element. Seems dead slow.

Doesn't the Z80 have a couple of index registers? I didn't find much info on how they work, and no one seems to use them. I'm assuming they aren't of much use, as the gameboy CPU is a modified Z80 that doesn't have them.

Any thoughts on this?

Login or register to post comments

By DamageX

Master (217)

DamageX's picture

10-07-2006, 06:51

I had the same feeling when I was translating my music player routine from 6502 asm to Z80 asm. The best idea I could come up with was to put the base address in IX or IY, add the index, and then access other tables immediately following the first one by using the 8-bit offset.

So this:
ldx #1
lda status,x
sta oldstatus,x

could become:
ld ix,status
ld bc,$0001
add ix,bc
ld a,(ix+0)
ld (ix+30),a

It looks like the ix and iy registers can be more useful if you use undocumented Z80 opcodes but I'm not sure which ones are safe to use or which ones could cause a problem (like on R800 maybe). I'm thinking that for speed critical code you might even have tables aligned at 256-byte boundaries and then load ixh with the base address and ixl with the index.

By ARTRAG

Enlighted (6845)

ARTRAG's picture

10-07-2006, 08:55

if you use 256byte alligned tables you can use HL and the access costs only one load instruction (in L)

ld h, table_address/256

ld l,element_number
ld a,(hl)
etc.
ld l,new_element_number
ld a,(hl)
etc.

if you have two tables and objects in fix postitions (as tipically happens in records)
you can use IX and IY like this:

ld ix, record_1
ld iy, record_2

ld a,(IX+field1)
ld (IY+filed1),a

if you have to access many fields in a record and your code is in ram you can use this SMC

ld ix, record

ld a,offset
ld (incode-1),a ; it is supposed to point to the offset in the ld a,(ix+0) instruction

ld a,(ix+0)
incode: etc.

By jltursan

Prophet (2619)

jltursan's picture

10-07-2006, 09:45

And keep in mind that the 6502 CPU is memory access oriented while the Z80 CPU is a register oriented CPU; so lookup tables are specifically a lot faster under the first one. The Z80 index registers IX and IY are painfully slow.
You can find a lot of discussion that involves lookup tables (and other tricks) in this thread:
speeding up assembly routine

By tokumaru

Expert (83)

tokumaru's picture

10-07-2006, 14:35

Thanks for the replies guys. I liked your ideas, and I think that trying to keep the tables aligned to 256 byte pages and using only l as an index is the best choice in many cases.

For cases when you only have to load one or two values, I wouldn't mind having to add, but there are other complicated things (like level maps that are not linear - I use a lot of those) that could become very slow.

About IX and IY, does anyone actually use those in games? In the past few week I've looked at a few source codes and I've never seen those beeing used.

By ARTRAG

Enlighted (6845)

ARTRAG's picture

10-07-2006, 15:08

If you use arrays of complex structs for representing enemis or objects, they can be quite handy.

LD B, NUMBER_OF_OBJECTS
LD IX,FIRST_RECORD

LOOP:
LD A,(IX+FIELD1)
ETC
LD A,(IX+FIELD2)
ETC
; DO ALL THE ACTIONS YOU NEED USING HL,DE,C,A

LD DE,RECORDSIZE
ADD IX,DE
DJNZ LOOP

When you need to work on two list of records
you can follow the same template using IY too as
pointer to the second list

By tokumaru

Expert (83)

tokumaru's picture

10-07-2006, 15:33

You are right. I can see it beeing usefull for handling enemies in a list. The "field" part could be used to access each property of the enemy/object, such as it's coordinates, speed, health, etc. You could do all the work in an enemy/object using the same index register (only changing it for the next object), while you'd have to keep messing with hl to access each property if you were to do it that way.

On the other hand, if IX and IY are slow as jltursan said, you might be better off using hl and incrementing l to access each field. I wouldn't know about the speed yet, gotta check it up.

By Sonic_aka_T

Enlighted (4130)

Sonic_aka_T's picture

10-07-2006, 15:35

About IX and IY, does anyone actually use those in games? In the past few week I've looked at a few source codes and I've never seen those beeing used.Hehe, try looking at Microcabin games! It's really quite amazing these games are as fast as they are! Heck, these ppl (or their compiler, probably) would've used IX/IY as an accumulator if they could! Having said that, there are occasions in which IX/IY can be handy, and even faster than using HL/DE.

By Sonic_aka_T

Enlighted (4130)

Sonic_aka_T's picture

10-07-2006, 15:37

It looks like the ix and iy registers can be more useful if you use undocumented Z80 opcodes but I'm not sure which ones are safe to use or which ones could cause a problem (like on R800 maybe).These are all official instructions on the R800. Apart from the undocumented flags the R800 'emulates' the Z80 perfectly. Only instruction I'm not sure of is IN F,(C) or whatever it was...

By jltursan

Prophet (2619)

jltursan's picture

10-07-2006, 15:47

Since now I've been using IX and IY very commonly; but just because speed doesn't matters and I'm a bit lazy. They're usually more handy to use than plan a better table structure. Most of the time, design a good field order in registers of your table could save you to use index registers, if you're able to process the data in every register sequentially you'll save a lot of cycles by using only HL/DE and BC common registers.

By pitpan

Prophet (3152)

pitpan's picture

10-07-2006, 17:12

IIRC, the undocumented Z80 instruction SLL x is not supported by the R800 CPU, that executes it as a kind of bit-test instruction. Therefore: be careful. In all my MSX(1) games, first of all I detect if it is being executed in a Turbo-R computer. If so, then it switches to Z80 CPU [SETCPU].

Page 1/3
| 2 | 3