Author Topic: Manipulating 16-bit data in a table  (Read 4434 times)

0 Members and 1 Guest are viewing this topic.

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Manipulating 16-bit data in a table
« on: 01:22, 27 September 10 »
I want to manipulate 16-bit data held in a table using a pointer, so it would be setup as follows:

Code: [Select]
pointer:   defw table
table:     defw &0000,&0111,&0222,&0333,&0444,&0555,&0666,&0777,&0888,&0999,&0AAA,&0BBB,&0CCC,&0DDD,&0EEE,&0FFF

All I want to do is read the 16-bit number from the table, increase the value by 1, store it back and repeat until all numbers in the table have been increased.

Can anyone give me any pointers for the quickest method of doing this (all registers are available)?  I've written routines that read 16-bit values (such as screen address tables etc) but can't remember how to change the data held in the tables...!  :-[

Offline Executioner

  • Supporter
  • 6128 Plus
  • *
  • Posts: 783
  • Country: au
  • WinAPE Developer
    • WinAPE
  • Liked: 392
  • Likes Given: 60
Re: Manipulating 16-bit data in a table
« Reply #1 on: 03:39, 27 September 10 »
Here's a possible solution:

(Increment entry A in the table)
Code: [Select]
inc_value:
  add a      ; Multiply by 2
  ld c,a
  ld b,0
  ld hl,(pointer)
  add hl,bc
  inc (hl)
  ret nz
  inc hl
  inc (hl)
  ret

Offline Executioner

  • Supporter
  • 6128 Plus
  • *
  • Posts: 783
  • Country: au
  • WinAPE Developer
    • WinAPE
  • Liked: 392
  • Likes Given: 60
Re: Manipulating 16-bit data in a table
« Reply #2 on: 03:45, 27 September 10 »
It may be worth doing this with a WinAPE macro if you're using WinAPE (inlined takes 8 bytes):

Code: [Select]
macro inc_table entry
   ld hl,entry * 2 + table
   inc (hl)
   jr nz,@exit
   inc hl
   inc (hl)
@exit:
endm
« Last Edit: 03:48, 27 September 10 by Executioner »

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #3 on: 17:18, 27 September 10 »
Both great suggestions, many thanks.

I'd forgotten about inc (hl) in the z80 instruction set  ::)

Offline eliot

  • CPC664
  • ***
  • Posts: 87
  • Country: fr
    • Benediction - demogroup since 1995
  • Liked: 57
  • Likes Given: 27
Re: Manipulating 16-bit data in a table
« Reply #4 on: 20:31, 27 September 10 »
You could use the Stack Pointer. Just to be sure to locate the table on a correct address : &9000, &9002 not &9001... (I don't know the words to explain it in english).
Code: [Select]
  org &8000
 
 ld (SAVESP+1),sp
 ld sp,table
;
 pop hl
 inc hl
 push hl
;
 pop hl
;
 pop hl
 inc hl
 push hl 
;
 pop hl
;
 pop hl
 inc hl
 push hl 
;
SAVESP ld sp,0
 ret

 org &9000

table defw &1111,&2222,&3333



Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #5 on: 22:33, 27 September 10 »
You could use the Stack Pointer.

That's an interesting solution - I will have a play around with it and see, thank you.

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #6 on: 00:55, 28 September 10 »
On the topic of 16-bit numbers...

Anyone know the best way to CP a 16-bit number?

I have data in the range of -4 to 512 loaded into HL.  I just want to CP HL to see if it's -4 and do a CALL with Z flag set if the CP is true.  Works fine on 8-bit numbers, but best solution I've come up with for 16-bit numbers is to CP the low byte and if true then use another routine to CP the high byte before executing the code I want if both are true.

Is there a better way...?!


Offline Executioner

  • Supporter
  • 6128 Plus
  • *
  • Posts: 783
  • Country: au
  • WinAPE Developer
    • WinAPE
  • Liked: 392
  • Likes Given: 60
Re: Manipulating 16-bit data in a table
« Reply #7 on: 03:08, 30 September 10 »
If you're just comparing for equality, you can just compare the high and low bytes. But I generally use either a cphlde routine such as the following:

Code: [Select]
.cphlde
ld a,h
cp d
ret nz
ld a,l
cp e
ret

The advantage being that you can compare for greater, less etc using the carry and zero flags.

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #8 on: 10:29, 30 September 10 »
The advantage being that you can compare for greater, less etc using the carry and zero flags.

A much more eloquent solution than mine, thanks!

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2275
  • Likes Given: 3478
Re: Manipulating 16-bit data in a table
« Reply #9 on: 11:17, 30 September 10 »
On the topic of 16-bit numbers...

Anyone know the best way to CP a 16-bit number?

I have data in the range of -4 to 512 loaded into HL.  I just want to CP HL to see if it's -4 and do a CALL with Z flag set if the CP is true.  Works fine on 8-bit numbers, but best solution I've come up with for 16-bit numbers is to CP the low byte and if true then use another routine to CP the high byte before executing the code I want if both are true.

Is there a better way...?!
Code: [Select]
ld hl,number
ld bc,other_number
or a
sbc hl,bc

carry will be set if HL<BC,
no carry if HL>=BC
zero will be set if HL=BC.
you can also test sign flag.

So minus if HL<BC
and positive if HL>=BC

I normally use this, but if course you are changing the value in HL. sometimes this is useful (e.g. sprite clipping), othertimes it is not.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #10 on: 12:32, 30 September 10 »
Code: [Select]
ld hl,number
ld bc,other_number
or a
sbc hl,bc

This is awesome  :)

Btw, what is sprite clipping?  Heard the term before but can't find a definition anywhere...?

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2275
  • Likes Given: 3478
Re: Manipulating 16-bit data in a table
« Reply #11 on: 12:43, 30 September 10 »
This is awesome  :)

Btw, what is sprite clipping?  Heard the term before but can't find a definition anywhere...?

Think of a rectangular area on the screen, positioned (top left at 10,10, bottom right at 100,100). Now if a sprite is located at 5,5 and has height and width 16,16. You want to draw this sprite so that only those parts of the sprite inside the rectangular area are shown.

To do this you "clip" the sprite to the area.

If the sprite is stored simply, top-to-bottom and left-to-right and not compressed, then you can use the coordinates of the sprite and the area, to calculate the data needed to adjust how you draw the sprite.

Example area.y - sprite.y will give you the number of lines to skip before starting to draw the sprite.
area.x - sprite.x will give you the number of pixels to skip for each line of the sprite when drawing the sprite.

You can then use a modified drawing routine to draw it as a much smaller sprite.

The result is the sprite will appear to be cut by the rectangular area.

Working out the part of the sprite to draw is called "clipping the sprite", then you finally draw the "clipped sprite".


Some computers have this kind of clipping built into the hardware.

Both the c64 and cpc will effectively clip the sprite to the side of the screen (to the border).

If you didn't clip the sprite, then on the cpc, when you draw it and you move over the right side of the screen you would see the other side of the sprite appear on the left side too because the bytes that make the screen wrap around in this way.
So to solve that the sprite is clipped.


My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #12 on: 13:00, 30 September 10 »
If you didn't clip the sprite, then on the cpc, when you draw it and you move over the right side of the screen you would see the other side of the sprite appear on the left side too because the bytes that make the screen wrap around in this way.
So to solve that the sprite is clipped.

I had exactly this problem to solve in my game programming and you've just given me the solution!

Thanks for the explanation  :)

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2275
  • Likes Given: 3478
Re: Manipulating 16-bit data in a table
« Reply #13 on: 13:08, 30 September 10 »
I had exactly this problem to solve in my game programming and you've just given me the solution!

Thanks for the explanation  :)
I normally clip to byte coordinates, it makes things much easier.
I then either use preshifted sprites to plot at a particular pixel offset, or use a pre-shift table and do it at runtime.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #14 on: 13:39, 30 September 10 »
I normally clip to byte coordinates, it makes things much easier.
I then either use preshifted sprites to plot at a particular pixel offset, or use a pre-shift table and do it at runtime.

I understand pre-shifting sprites (I think - 1 byte = 2 pixels in MODE 0 so quickest way to display a sprite and move it pixel by pixel is to have two copies, 1 normal and 1 pre-shifted by 1 pixel...?) but haven't heard about the pre-shift table.

Actually, how do you move a sprite pixel by pixel without pre-shifting it*...?

*you can tell I'm a Plus user, hardware sprites ftw :)

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2275
  • Likes Given: 3478
Re: Manipulating 16-bit data in a table
« Reply #15 on: 14:51, 30 September 10 »
I understand pre-shifting sprites (I think - 1 byte = 2 pixels in MODE 0 so quickest way to display a sprite and move it pixel by pixel is to have two copies, 1 normal and 1 pre-shifted by 1 pixel...?) but haven't heard about the pre-shift table.
Yes correct

Actually, how do you move a sprite pixel by pixel without pre-shifting it*...?

*you can tell I'm a Plus user, hardware sprites ftw :)

With this method you have 1 copy of the sprite.

The choice is then either to use a lookup table or do the processing yourself (e.g. perform the shift, combine and write to screen).
A lookup table ("preshift table") makes the code quicker, but at the expense of 512 bytes in mode 0 (approx 1.5k in mode 1 I think, because you need 3 tables). You need 1 table for each pixel position. (1 table for mode 0, 3 tables for mode 1, 7 tables for mode 2 ).

The idea is you read a byte of pixel data from sprite (unshifted), use the lookup table to find the version "preshifted" by x pixels. Each entry in the table is 2 bytes, because when you start to shift it moves out of a byte's range. Unused pixels are set to 0.
You then combine this with the previous shifted byte (which you need to remember), and combine with screen (potentially includes shifted masks!).

I shifted the sprites at runtime in my conversion of balloonacy, I can't remember if I used a table or not.

I did plan to make a sprite example which did shifting like this, but I have not found the time yet.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #16 on: 20:41, 30 September 10 »
The idea is you read a byte of pixel data from sprite (unshifted), use the lookup table to find the version "preshifted" by x pixels. Each entry in the table is 2 bytes, because when you start to shift it moves out of a byte's range. Unused pixels are set to 0.
You then combine this with the previous shifted byte (which you need to remember), and combine with screen (potentially includes shifted masks!).

That looks like quite some overhead there, can see why people use pre-shifted sprites.

However, the pre-shifts eat up RAM... bring on the 512kb cartridges eh  ;)

Offline AMSDOS

  • Supporter
  • 6128 Plus
  • *
  • Posts: 3.919
  • Country: au
    • index.php?action=treasury
    • Programs for Turbo Pascal 3
  • Liked: 1132
  • Likes Given: 1902
Re: Manipulating 16-bit data in a table
« Reply #17 on: 02:04, 01 October 10 »
On the discussion of Tables and Lookup tables I wasn't sure if my example was relevant - it should work on all CPCs though (only tested it on the Old school machines though). Because the Sprite Routine itself is quick and dirty the Sprite itself requires the invisible border around the sprite to prevent it from writing up the screen! I've modified this so it works in AMSDOS!  :)
 
Code: [Select]
org &4000
     ld a,0
     call &bc0e
     ld bc,&0101
     call &bc38
     call setcolors
     call initialise
     ld hl,(xpos)
     call findcolumn
     ld (scradr),hl
     ld hl,sprdata
     ld de,(scradr)
     ld b,8
     ld c,15
     call disspr
 .mainloop
     call control
     jp mainloop
 .conkey
     call &bb1e
     ret
 .exit
     call &bb03
     ld a,2
     call &bc0e
     jp 0
 .setcolors
     ld hl,sprcolor
     ld a,0
 .colloop
     ld c,(hl)
     ld b,c
     push af
     push hl
     call &bc32
     pop hl
     pop af
     inc hl
     inc a
     cp 10
     jr c,colloop
  .control
     ld a,8
     call conkey
     jr nz,moveleft
     ld a,1
     call conkey
     jr nz,moveright
     ld a,0
     call conkey
     jr nz,moveup
     ld a,2
     call conkey
     jp nz,movedown
     ld a,66
     call conkey
     jr nz,exit
     ret
  .moveleft
     ld a,&0
     ld hl,(xpos)
     cp h
     jp nz,doleft
  .showleft
     ld hl,(xpos)
     call findcolumn
     ld (scradr),hl
     ld de,(scradr)
     ld hl,sprdata
     ld b,8
     ld c,15
     call disspr
     ret
  .doleft
     ld hl,(xpos)
     dec h
     ld (xpos),hl
     jp showleft
  .moveright
     ld a,&46
     ld hl,(xpos)
     cp h
     jp nz,doright
  .showright
     ld hl,(xpos)
     call findcolumn
     ld (scradr),hl
     ld de,(scradr)
     ld hl,sprdata
     ld b,8
     ld c,15
     call disspr
     ret
  .doright
     ld hl,(xpos)
     inc h
     ld (xpos),hl
     jp showright
  .moveup
     ld a,&0
     ld hl,(xpos)
     cp l
     jp nz,doup
  .showup
     ld hl,(xpos)
     call findcolumn
     ld (scradr),hl
     ld hl,sprdata
     ld de,(scradr)
     ld b,8
     ld c,15
     call disspr
     ret
  .doup
     ld hl,(xpos)
     dec l
     ld (xpos),hl
     jp showup
  .movedown
     ld a,&b6
     ld hl,(xpos)
     cp l
     jp nz,dodown
  .showdown
     ld hl,(xpos)
     call findcolumn
     ld (scradr),hl
     ld hl,sprdata
     ld de,(scradr)
     ld b,8
     ld c,15
     call disspr
     ret
  .dodown
     ld hl,(xpos)
     inc l
     ld (xpos),hl
     jp showdown
  .findcolumn
                            ;; H = x coordinate (0-79)
                            ;; L = y coordinate (0-199)
                            ;; HL = screen address (top-left of sprite)
     push bc                    ;; store BC because we are modifying it in this
                                ;; routine
     push de                    ;; store DE because we are modifying it in this
                                ;; routine
     ld c,h                     ;; store x coordinate in C register
     ld h,0                     ;; L = y coordinate, H = 0
     add hl,hl                  ;; double L. Now L is a byte offset from the
                                ;; start of the table pointing to the
                                ;; entry corresponding to screen Y
                                ;; coordinate. We double because each entry
                                ;; is two bytes.
     ld de,table                ;; base of table
     add hl,de                  ;; add offset to base to get actual address in
                                ;; memory of the table entry
     ld a,(hl)
     inc hl
     ld h,(hl)
     ld l,a                     ;; HL = value from table. it is the screen
                                ;; address for the start of this line
                                ;; corresponding to the Y coordinate
     ld b,0                     ;; BC = x coordinate as a 16-bit value. C = x
                                ;; coordinate
     add hl,bc                  ;; add on X coordinate
                                ;; HL = final screen coordinate for x,y position
     pop de                     ;; restore registers we used
     pop bc
     ret
.initialise
     ld de,table
     ld hl,&c000
     ld b,&19
.next
     push bc
     push hl
     ld b,&8
.loop
     push bc
     ld a,l
     ld (de),a
     inc de
     ld a,h
     ld (de),a
     inc de
     ld bc,&800
     add hl,bc
     pop bc
     djnz loop
     pop hl
     ld bc,&50
     add hl,bc
     pop bc
     djnz next
     ret
.disspr
     ld a,b
     ld (pl+1),a
 .loop1
     push de
 .pl
     ld b,&0
 .loop2
     ldi
     inc c
     djnz loop2
     pop de
     ld a,d
     add &8
     ld d,a
     jr nc,end
     ld a,e
     add &50
     ld e,a
     ld a,d
     adc &c0
     ld d,a
  .end
     dec c
     jr nz,loop1
     ret
  .table
     defs 400
  .scradr
     defw &c000
  .xpos
     defb &00
  .ypos
     defb &00
  .scrrow
     defb 0
  .sprcolor
     defb 0,26,0,6,3,15,18,1,2,20,0
  .sprdata
     defb 12,12,12,12,12,12,12,12
     defb 8,0,0,0,0,0,0,4
     defb 8,0,&54,&fc,0,0,0,4
     defb 8,0,&a9,3,&a8,0,0,4
     defb 8,0,&f0,&f0,&a0,0,0,4
     defb 8,&44,&f0,&a0,&a0,0,0,4
     defb 8,&44,&e4,&70,&a0,0,0,4
     defb 8,&44,&b0,&10,&20,0,0,4
     defb 8,&44,&b0,0,0,0,0,4
     defb 8,0,&e4,&30,&20,&68,0,4
     defb 8,0,&83,&e9,&83,&16,&80,4
     defb 8,&41,&d6,&fc,&a8,&68,0,4
     defb 8,0,&56,&56,2,0,0,4
     defb 8,0,&c3,&41,&82,0,0,4
     defb 8,0,0,0,0,0,0,4
     defb 12,12,12,12,12,12,12,12
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #18 on: 10:16, 01 October 10 »
On the discussion of Tables and Lookup tables I wasn't sure if my example was relevant

That's a nicely comprehensive routine - and I'm sure you could optimise it a little...  ;)

The problem I always have with Z80 tables is remembering the difference between the memory location of a table (e.g. LD HL,table) and the contents of the table (LD IX,table : LD L,(IX) : INC IX : LD H,(IX) ) etc.  When you need to add using a pointer to the mix it becomes even more confusing... and then if you are using the ASIC where the data just has to be copied directly into the paged memory it becomes even harder to remember what technique to use...!

But I enjoy battling with these routines and always try to write my own utilities (I'm currently writing one to grab and display compressed hardware sprites) because by doing this you fully understand how the whole thing works - if I remember to comment my source code that is!
« Last Edit: 10:18, 01 October 10 by redbox »

Offline AMSDOS

  • Supporter
  • 6128 Plus
  • *
  • Posts: 3.919
  • Country: au
    • index.php?action=treasury
    • Programs for Turbo Pascal 3
  • Liked: 1132
  • Likes Given: 1902
Re: Manipulating 16-bit data in a table
« Reply #19 on: 12:32, 01 October 10 »
redbox wrote:

That's a nicely comprehensive routine - and I'm sure you could optimise it a little...  ;)

Yes I'm kind of stuck with using what I have until some faster routines are made!  ???  Perhaps instead of calling firmware routines, plug the relevant RSTs from those well known addresses into my routines. Would save wasting 17 Clock Cycles from a Call.  :-\
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

Offline Axelay

  • 6128 Plus
  • ******
  • Posts: 586
  • Country: au
  • Liked: 383
  • Likes Given: 87
Re: Manipulating 16-bit data in a table
« Reply #20 on: 12:38, 01 October 10 »
That looks like quite some overhead there, can see why people use pre-shifted sprites.

However, the pre-shifts eat up RAM... bring on the 512kb cartridges eh  ;)

No more so than those hardware sprites you're so keen on!  ;)

Depending on the game design though, there's no reason why preshifted sprites needs to consume that much RAM, if a level or screen only uses a few sprites at a time, you only need some code to generate the required preshifted frames at the start of the level.

Not sure shifting the sprites at runtime sounds like much fun though, having some frames get bogged down by shifting sprites while others dont could make a real mess of your frame rate.

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Manipulating 16-bit data in a table
« Reply #21 on: 14:54, 01 October 10 »
Depending on the game design though, there's no reason why preshifted sprites needs to consume that much RAM, if a level or screen only uses a few sprites at a time, you only need some code to generate the required preshifted frames at the start of the level.

That's an interesting way of looking at it and I often forget that things can be done before the main loop occurs.

But if there is lots of sprites then 128k and multi-load (disc or cartridge) is the way to go - sorry tape users  :)   It's hard enough as it is cramming everything into 64k, let alone when you factor in &4000 to &8000 can't be used as sprite storage on the Plus due to paging in the ASIC.