News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_AMSDOS

Decreasing 16bit Numbers from an Array (ASM Example)

Started by AMSDOS, 12:05, 26 April 14

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

AMSDOS


Hi, I was hoping someone could help me out, though I'm guessing it's a routine issue that lots of people have had. I've been able to make an array with 8bit numbers and make a loop to go through the array and deduct successfully, though with 16bit numbers I've found the low byte is updated fine with the high byte remains unchanged. I'm using "xpoint" & "ypoint" to point to positions of the array and then using dec (hl) to decreased the contents, though as it shows for the larger numbers they are not being completely decreased. Is it possible to do it like this or does this need more checks to appropriately deduct those numbers to them smaller numbers at different intervals. I tried including "ld (xpoint),hl" and "ld (ypoint),hl" after "dec (hl)" though it didn't seem to make any difference. :(

org &4000


ld b,20


.loop
ld hl,(xpoint)
dec (hl)


ld hl,(ypoint)
dec (hl)


ld hl,(xpoint)
inc hl
inc hl
inc hl
inc hl
ld (xpoint),hl


ld hl,(ypoint)
inc hl
inc hl
inc hl
inc hl
ld (ypoint),hl


djnz loop


ld hl,array
ld (xpoint),hl


ld hl,array+2
ld (ypoint),hl


ret




.xpoint defw array
.ypoint defw array+2


.array defw 112,93
defw 268,32
defw 432,277
defw 404,336
defw 208,100
defw 308,53
defw 627,103
defw 236,195
defw 370,140
defw 156,370
defw 344,74
defw 99,48
defw 162,235
defw 521,268
defw 416,80
defw 238,18
defw 327,340
defw 323,162
defw 56,394
defw 89,0
* 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

MaV

Quote from: AMSDOS on 12:05, 26 April 14I'm using "xpoint" & "ypoint" to point to positions of the array and then using dec (hl) to decreased the contents, though as it shows for the larger numbers they are not being completely decreased.
dec (hl) only decreases the byte at the address that hl points to, unfortunately.
Black Mesa Transit Announcement System:
"Work safe, work smart. Your future depends on it."

AMSDOS

Quote from: MaV on 12:21, 26 April 14
dec (hl) only decreases the byte at the address that hl points to, unfortunately.


ok so by the looks of it, the easiest way to do 16bit subtractions is like this:



org &4000
.first equ &4100
.second equ &4102
.result equ &4104
ld hl,(first)
ld de,(second)
and a
sbc hl,de
ld (result),hl
ret





so I guess hl would have the address of the array contents, de has the number to decrease it by and the result would have to go back to where the previous value is.
* 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

redbox

Yes, you need to use HL as the pointer when using DEC (HL). For example:


ld hl,xpoint
dec (hl)

xpoint: defb 10


However, this also means that DEC (HL) only works for 8-bit numbers.

For 16-bit, most people use a SBC based routine.  However, if you want if to always subtract one you could possibly do:


ld hl,(xpoint)
ld de,&ffff
add hl,de
ld (xpoint),hl




Tai


Quote
For 16-bit, most people use a SBC based routine.  However, if you want if to always subtract one you could possibly do:



ld hl,(xpoint)
ld de,&ffff
add hl,de
ld (xpoint),hl



Or you can simply do:



ld hl,(xpoint)
dec hl
ld (xpoint),hl

redbox

Quote from: Tai on 22:11, 28 April 14
Or you can simply do:

Yep, that'll do it ;)

But I used the principle of adding &ffff because then you can adapt it to deduct a larger amount if needed.

Executioner

#6
You need to subtract from both the least significant byte and most significant byte of the 16 bit pair. To do this you can use something like the following:


ld hl,(xpoint)
ld a,(hl)
sub 1
ld (hl),a
inc hl
ld a,(hl)
sbc 0
ld (hl),a
inc hl
inc hl
inc hl
ld (xpoint),hl


This will do the 16 bit subtraction and increment your pointer to the next X value.

If you're always subtracting 1 from both x and y, there's no need to store both xpoint and ypoint, and you can do away with the extra inc hl's. You can also optimise it by using registers (ie. ld de,1... sub e ... sbc d), aligning the tables so you don't have to do 16 bit pointer increments, here's an example that would allow you to subtract 2 from all X values and 3 from all Y values:


ld de,2
ld bc,3

ld hl,array

.loop
ld a,(hl)
sub e
ld (hl),a
inc l
ld a,(hl)
sbc d
ld (hl),a
inc l
ld a,(hl)
sub c
ld (hl),a
inc l
ld a,(hl)
sbc b
ld (hl),a
inc l
ld a,l
cp endarr
jr nz,loop
ret

align 256
.array    defw 112,93
    defw 268,32
    defw 432,277
    defw 404,336
    defw 208,100
    defw 308,53
    defw 627,103
    defw 236,195
    defw 370,140
    defw 156,370
    defw 344,74
    defw 99,48
    defw 162,235
    defw 521,268
    defw 416,80
    defw 238,18
    defw 327,340
    defw 323,162
    defw 56,394
    defw 89,0
.endarr

AMSDOS

Sorry guys, what I'm trying to do here is it decrement the values stored in the array. At the moment all I'm doing was to decrease the values in the array by 1, though eventually I'm going to try and use it to simulate movement (without moving the screen). The points in the array represent the points on screen, so I'll want to decrement that by 4 to move the points across the screen.
* 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

AMSDOS


Quote from: Executioner on 00:50, 29 April 14
You need to subtract from both the least significant byte and most significant byte of the 16 bit pair. To do this you can use something like the following:


ld hl,(xpoint)
ld a,(hl)
sub 1
ld (hl),a
inc hl
ld a,(hl)
sbc 0
ld (hl),a
inc hl
inc hl
inc hl
ld (xpoint),hl


This will do the 16 bit subtraction and increment your pointer to the next X value.

If you're always subtracting 1 from both x and y, there's no need to store both xpoint and ypoint, and you can do away with the extra inc hl's. You can also optimise it by using registers (ie. ld de,1... sub e ... sbc d), aligning the tables so you don't have to do 16 bit pointer increments, here's an example that would allow you to subtract 2 from all X values and 3 from all Y values:


ld de,2
ld bc,3

ld hl,array

.loop
ld a,(hl)
sub e
ld (hl),a
inc l
ld a,(hl)
sbc d
ld (hl),a
inc l
ld a,(hl)
sub c
ld (hl),a
inc l
ld a,(hl)
sbc b
ld (hl),a
inc l
ld a,l
cp endarr
jr nz,loop
ret

align 256
.array    defw 112,93
    defw 268,32
    defw 432,277
    defw 404,336
    defw 208,100
    defw 308,53
    defw 627,103
    defw 236,195
    defw 370,140
    defw 156,370
    defw 344,74
    defw 99,48
    defw 162,235
    defw 521,268
    defw 416,80
    defw 238,18
    defw 327,340
    defw 323,162
    defw 56,394
    defw 89,0
.endarr



Thanks for that, I've been able to adjust the routine above so it deducts the right amounts of the values in the array, using this approach is it possible to check when each of the x co-ordinates reaches 0? I find it somewhat puzzling because I guess the high and low bytes would have to be check to make sure they equal 0.
* 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

Executioner

Quote from: AMSDOS on 01:51, 30 April 14
using this approach is it possible to check when each of the x co-ordinates reaches 0? I find it somewhat puzzling because I guess the high and low bytes would have to be check to make sure they equal 0.

Yes, it's possible, but you've run out of easy registers to check. It may be a better idea to have two tables (one for X co-ords and one for Y), then adjust them seperately that way you can use B for looping and C to do the check eg.


ld hl,xarray
ld de,1
ld b,20

.loop
ld a,(hl)
sub e
ld c,a
ld (hl),a
inc l
ld a,(hl)
sbc d
ld (hl),a
or e
jr z,zero
inc l
djnz loop


Executioner

Another approach to this (although I don't generally recommend using index registers because of performance, but in this case it may be just as fast) would be to use a routine similar to the following:


ld ix,array
ld de,-1     ; use -ve subtraction rather than addition since the Z80 only has 16-bit SBC, not SUB
ld bc,-1
ld a,array  ; This is just the lowest 8 bits and can be used to quickly add 4 to ix and test the end of the table

.loop
ld l,(ix + 0)    ; Get X co-ord
ld h,(ix + 1)
add hl,de      ; Add value
jr z,zerox
ld (ix + 0),l
ld (ix + 1),h

ld l,(ix + 2)   ; Get Y co-ord
ld h,(ix + 3)
add hl,bc
jr z,zeroy
ld (ix + 2),l
ld (ix + 3),h

add 4          ; Increment pointer to next point
ld lx,a
cp endarray  ; And test if at end
jr nz,loop


align 256
.array
  dw ...


ralferoo

The real question is whether you really want to check when it reaches 0, or if you're content to detect when it goes past zero...

And this kind of question drives a lot of how you design things for 8-bit (and to an extent later machines too).

For instance, you could have:

process_all:
ld a,(hl)
sub 3
ld (hl),a
inc hl

ld a,(hl)
sbc a,0
ld (hl),a
jr nc, not_past_0
inc hl

dec hl
ld (hl),#01
inc hl
ld (hl),#3f   ;#013f is 319 in hex

not_past_0:
inc hl
djnz process_all


If you really want to check for 0, you could try something like this maybe:


process_all:
ld a,-3
add a,(hl)
ld (hl),a
inc hl

jr nc,no_high_byte_change

sbc a,a    ; A=#FF if carry, #00 if no carry
add a,(hl)   ; last two lines equivalent to LD A,(HL):SBC A,0 but 1 byte shorter and 1µs quicker (and a different final carry flag)
ld (hl),a
jr nz,wasnt_zero

dec hl
ld (hl),#01
inc hl
ld (hl),#40   ;#0140 is 320 in hex

wasnt_zero:
no_high_byte_change:
inc hl
djnz process_all


AMSDOS

Hi guys,


I've had a look at this routines, and played around with @Executioner first routine (which checks when the x value is 0) and have adjusted my values so they are all divisible by 4 so will eventually get to 0. This is what I first came up with, but it's not working for me, even though I've checked the code posted and checked by Assembly book which seems to concur. It appears the program isn't jumping when the value in x-array isn't zero, this is what I came up with:




org &4000

ld de,4
ld b,20


ld hl,x_array


.loop
ld a,(hl)
sub e
ld c,a
ld (hl),a
inc l
ld a,(hl)
sbc d
ld (hl),a
or e
jr z,fetch
inc l
djnz loop
ret


.fetch ld hl,640
ld (x_array),hl
;; ret
jr loop


.x_array defw 112,268,432,404,208,308,628,236,368,156,344,96,160
defw 520,416,236,324,320,56,88



I tried a variation of the ".fetch" routine because I don't think it's supposed to work like that which looks like this:



.fetch
          ld de,640
          ld (hl),e
          inc hl
          ld (hl),d
          dec hl
          ret
;;        jr loop



Wasn't sure what the deal is with "jr" in that does it work like a "call" and return back to where it was or if I needed to jump relative back to the main loop.  :'(


I guess it's not important it this doesn't get solved because I was doing this to make a horizontal star scroller which I've been able to resolve in the other thread, though I made another routine a few of months ago which is also restricted to a 8bit size playing field, so I were to update that to a 16bit playing field , I would need to know what's happening here.


Unfortunately this stuff is doing my head in.  :'(
* 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

Axelay

Quote from: AMSDOS on 05:24, 10 May 14
I tried a variation of the ".fetch" routine because I don't think it's supposed to work like that which looks like this:



Yes, that first version of fetch is using hl to store your new x value, without preserving the current contents of hl which is used as a pointer to x_array in the main loop.  Your alternative fetch is closer to what you need, but you should move the dec hl before the ld (hl),e so that hl is pointing to the least significant byte when it writes e.


Quote from: AMSDOS on 05:24, 10 May 14Wasn't sure what the deal is with "jr" in that does it work like a "call" and return back to where it was or if I needed to jump relative back to the main loop.  :'(




"ret" will pop a 16 bit value off the stack and execute the next instruction from that memory address.  Jumps, like jr, dont push a return address on the stack where a "call" does, so the "ret" in fetch will return to BASIC if you have called it from there with the code you have presented.  You could either replace the "jr z,fetch" in the loop with "call z,fetch", or replace the "ret" with "jr fetch_ret" and place a "fetch_ret" label just after "jr z,fetch" so the loop does not have the pointer in hl corrupt, as it would with a "jr loop".  In the case of "jr loop" the loop would begin pointing to the most significant byte of an x value in your table, because it would have skipped that last "inc l" in the loop, as well as missed decrementing the counter in register b.

AMSDOS

Quote from: Axelay on 07:34, 10 May 14




Yes, that first version of fetch is using hl to store your new x value, without preserving the current contents of hl which is used as a pointer to x_array in the main loop.  Your alternative fetch is closer to what you need, but you should move the dec hl before the ld (hl),e so that hl is pointing to the least significant byte when it writes e.




"ret" will pop a 16 bit value off the stack and execute the next instruction from that memory address.  Jumps, like jr, dont push a return address on the stack where a "call" does, so the "ret" in fetch will return to BASIC if you have called it from there with the code you have presented.  You could either replace the "jr z,fetch" in the loop with "call z,fetch", or replace the "ret" with "jr fetch_ret" and place a "fetch_ret" label just after "jr z,fetch" so the loop does not have the pointer in hl corrupt, as it would with a "jr loop".  In the case of "jr loop" the loop would begin pointing to the most significant byte of an x value in your table, because it would have skipped that last "inc l" in the loop, as well as missed decrementing the counter in register b.


Thanks for your suggestions, I tried them out but unfortunately the same think still seems to be occurring, so when a value in the x_array reaches 0, it throws it back to &FFFC :(
I also tried another variation by changing "jr z,fetch" to "jr nz,not_zero" and having the fetch routine in between that, so the jump would come out to "inc l", but I had no luck with that either.




org &4000

ld de,4
ld b,20


ld hl,x_array


.loop
ld a,(hl)
sub e
ld c,a
ld (hl),a
inc l
ld a,(hl)
sbc d
ld (hl),a
or e
jr z,fetch


.fetch_return inc l
djnz loop
ret


.fetch ld de,640
dec hl
ld (hl),e
inc hl
ld (hl),d
jr fetch_return


.x_array defw 112,268,432,404,208,308,628,236,368,156,344,96,160
defw 520,416,236,324,320,56,88
* 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

Executioner

Quote from: AMSDOS on 05:24, 10 May 14
It appears the program isn't jumping when the value in x-array isn't zero, this is what I came up with:

The "or e" instruction just before the jr z should be "or c". Not sure if that was a typo in my original code or if you added that :)

You also need to use inc hl/dec hl (rather than inc l/dec l) everywhere unless you align the table otherwise you might miss the page boundary, so add:

align 256

before your x_array.

align 256 will make sure x_array is aligned to a page (#xx00)

Bruce Abbott

Quote from: AMSDOS on 09:28, 11 May 14

I've had a look at this routines, and played around with @Executioner first routine (which checks when the x value is 0) and have adjusted my values so they are all divisible by 4 so will eventually get to 0.
Adjusting the values is not necessary if you check whether the result goes to zero or below zero, then add 640 to wrap around.

        org &4000
   
        ld de,4
        ld b,20
        ld hl,x_array
.loop
        ld a,(hl)
        sub e        ; subtract low byte
        ld c,a       ; c = low byte
        ld (hl),a
        inc hl
        ld a,(HL)
        sbc d        ; subtract high byte
        ld (hl),a
        jr c,wrap    ; if < 0 then wrap
        or c
        jr nz,next   ; if = 0 then wrap 
.wrap
        dec hl
        ld a,(hl)
        add 640 MOD 256
        ld (hl),a
        inc hl       ; add 640
        ld a,(hl)
        adc 640/256
        ld (hl),a
.next       
        inc hl
        djnz loop
        ret

.x_array
        defw 5,4,3,2,1,0,640,639,257,256,255
        defw 96,97,98,99,100,101,102,103,104




AMSDOS

Quote from: Executioner on 08:55, 12 May 14
The "or e" instruction just before the jr z should be "or c". Not sure if that was a typo in my original code or if you added that :)


I was wondering if that was the case, so I changed it.

QuoteYou also need to use inc hl/dec hl (rather than inc l/dec l) everywhere unless you align the table otherwise you might miss the page boundary, so add:

align 256

before your x_array.

align 256 will make sure x_array is aligned to a page (#xx00)


I was trying this without & with this align approach, though was getting some unusual results. The problem now appears to be when the lowest number (54) reaches 0, results from the data in the x_array change, I'm not fully understanding what's happening though because the first digit - 112 seems to perform correctly, though figures like 520 are performing oddly. It seems to be related to the lowest to largest numbers and when they reach 0 because I counted how many cycles it took before the odd result, 14 was the result with 14x4 being 56.
* 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

AMSDOS

Quote from: Bruce Abbott on 09:39, 12 May 14
Adjusting the values is not necessary if you check whether the result goes to zero or below zero, then add 640 to wrap around.

        org &4000
   
        ld de,4
        ld b,20
        ld hl,x_array
.loop
        ld a,(hl)
        sub e        ; subtract low byte
        ld c,a       ; c = low byte
        ld (hl),a
        inc hl
        ld a,(HL)
        sbc d        ; subtract high byte
        ld (hl),a
        jr c,wrap    ; if < 0 then wrap
        or c
        jr nz,next   ; if = 0 then wrap 
.wrap
        dec hl
        ld a,(hl)
        add 640 MOD 256
        ld (hl),a
        inc hl       ; add 640
        ld a,(hl)
        adc 640/256
        ld (hl),a
.next       
        inc hl
        djnz loop
        ret

.x_array
        defw 5,4,3,2,1,0,640,639,257,256,255
        defw 96,97,98,99,100,101,102,103,104



Hmmm okay so this works on the concept that the data can be anything and if a negative did occur, then the program can deal with that as well. I have a look at it sometime, I wasn't sure if it can deal with simply increasing the appropriate number when it reaches zero, it's a bit of a work in progress, but I'll have a look at your example.
* 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

Powered by SMFPacks Menu Editor Mod