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
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.
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.
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
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
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.
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
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.
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.
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
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 ...
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
Hi guys,
I've had a look at this routines, and played around with @Executioner (http://www.cpcwiki.eu/forum/index.php?action=profile;u=17) 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. :'(
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.
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
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)
Quote from: AMSDOS on 09:28, 11 May 14
I've had a look at this routines, and played around with @Executioner (http://www.cpcwiki.eu/forum/profile/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
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.
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.