Copying a pointer of a pointer to a pointer

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
DRW
Posts: 1976
Joined: Sat Sep 07, 2013 2:59 pm

Copying a pointer of a pointer to a pointer

Post by DRW » Mon Oct 29, 2018 6:08 pm

In the moment, I'm a bit confused. cc65 only creates blown code out of it and I haven't managed to figure it out myself yet.

What is the 6502 Assembly version of the following code?

Code: Select all

unsigned char index;
const unsigned char *const *arrayOfArray;
const unsigned char *array;

array = arrayOfArray[index];
I would have thought that this is the correct code:

Code: Select all

LDA index
ASL
TAY
LDA (arrayOfArray + 0), Y
STA array + 0
LDA (arrayOfArray + 1), Y
STA array + 1
But with this, my game freezes, even though it works fine with the C version.

What am I doing wrong?

(By the way, all of these variables are declared in the zeropage.)
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
dougeff
Posts: 2712
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by dougeff » Mon Oct 29, 2018 6:13 pm

LDA (arrayOfArray + 0), Y

Are you sure you want Indirect Indexed mode here? maybe indexed mode would be right.

LDA arrayOfArray, Y
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
DRW
Posts: 1976
Joined: Sat Sep 07, 2013 2:59 pm

Re: Copying a pointer of a pointer to a pointer

Post by DRW » Mon Oct 29, 2018 6:22 pm

Well, the answer to that question is pretty straightforward: I want the Assembly code to do exactly what the C code is doing. :mrgreen:

This is what I want to accomplish in Assembly:

Code: Select all

array = arrayOfArray[index];
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
dougeff
Posts: 2712
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by dougeff » Mon Oct 29, 2018 6:28 pm

Ok. I was trying to make a point about how ca65 is confused by your parentheses into thinking you want indirect mode, but I believe you wanted indexed mode.

Remove the parentheses.

You could do INY to get the high byte.

LDA arrayOfArray, Y
STA array + 0
INY
LDA arrayOfArray, Y
STA array + 1

then
LDY #0
LDA (array), Y

to access your array.
nesdoug.com -- blog/tutorial on programming for the NES

3gengames
Formerly 65024U
Posts: 2276
Joined: Sat Mar 27, 2010 12:57 pm

Re: Copying a pointer of a pointer to a pointer

Post by 3gengames » Mon Oct 29, 2018 6:29 pm

Increment Y instead. You're pulling a bad zeropage location by doing Pointer+1 because it then grabs the incorrect address because the location shouldn't change.

With (ZP),Y, the location at $00ZP and $00ZP+1 is queried for the address, the value of Y is then added to that address pointer to by the zeropage location, and that result of that is the address you actually get the data from.

If ZP is $00, and address $00 contains $00, and address $01 contains $80, you will get the data from $8000+Y. You always use ($00),Y, you never use ($01),Y.
Last edited by 3gengames on Tue Oct 30, 2018 12:40 am, edited 1 time in total.

User avatar
DRW
Posts: 1976
Joined: Sat Sep 07, 2013 2:59 pm

Re: Copying a pointer of a pointer to a pointer

Post by DRW » Mon Oct 29, 2018 6:34 pm

Sorry. Still doesn't work.

With the line in C, everything is fine. But with the Assembly code in its place, the game doesn't work correctly, so the above code is probably not the equivalent of the C code.
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
DRW
Posts: 1976
Joined: Sat Sep 07, 2013 2:59 pm

Re: Copying a pointer of a pointer to a pointer

Post by DRW » Mon Oct 29, 2018 6:38 pm

O.k., got it: Indirect access was indeed correct. But one cannot do LDA (xyz + 1), Y.
Your INY in between was the correct solution.

Thanks.

Here's the complete correct code:

Code: Select all

LDA index
ASL
TAY
LDA (arrayOfArray), Y
STA array + 0
INY
LDA (arrayOfArray), Y
STA array + 1
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 501
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: Copying a pointer of a pointer to a pointer

Post by Jarhmander » Wed Oct 31, 2018 6:03 am

Your code works as long as index < 128; otherwise, you need something like this:

Code: Select all

    lda arrayOfArray
    clc
    adc index
    sta zptemp
    lda arrayOfArray+1
    adc #0
    sta zptemp+1
    ldy #0
    lda (zptemp), y
    sta array
    iny
    lda (zptemp), y
    sta array+1
Maybe you know that "index" is small, but cc65 doesn't, so it probably does the above. Is this what the assembly code generated by cc65 looks like, or is much worse?
((λ (x) (x x)) (λ (x) (x x)))

User avatar
dougeff
Posts: 2712
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by dougeff » Wed Oct 31, 2018 9:28 am

Is this what the assembly code generated by cc65 looks like, or is much worse?
HAHAHAHAHA

(breath)

A... HAHAHAHA

it's worse
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
rainwarrior
Posts: 7824
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by rainwarrior » Wed Oct 31, 2018 10:27 am

Actual code generated:

Code: Select all

; array = arrayOfArray[index];

	ldx     #$00
	lda     _index
	asl     a
	bcc     L0085
	inx
	clc
L0085:	adc     _arrayOfArray
	pha
	txa
	adc     _arrayOfArray+1
	tax
	pla
	;jsr     ldaxi ; i've placed the subroutine inline here
		ldy     #1
		sta     ptr1
		stx     ptr1+1
		lda     (ptr1),y
		tax
		dey
		lda     (ptr1),y
		rts
	sta     _array
	stx     _array+1
The ldaxi function for reference.

That looks fairly efficient for what it does, but it's not able to do the final resolution of the pointer inline, it has to put the pointer in A:X and branch to that subroutine. (Jarhmander was missing the ASL by the way. CC65 does in fact know that index is 8-bit, but the implied *2 on the index promotes it to integer anyway.)

So in terms of efficiency, we've got maybe an extra JSR/RTS, PHA,PLA, TXA, TAX, TAX? About 25 extra cycles? The subroutine call itself accounts for half of it.

I kinda wonder how much efficiency CC65 would be able to gain with a special "7-bit index" type for accessing arrays of 16-bit data, that it could double without promoting to an int.

User avatar
gauauu
Posts: 700
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by gauauu » Wed Oct 31, 2018 1:38 pm

rainwarrior wrote: I kinda wonder how much efficiency CC65 would be able to gain with a special "7-bit index" type for accessing arrays of 16-bit data, that it could double without promoting to an int.
Oh man, probably a ton. One thing I've noticed in reading the assembly that it generates is that it often adds a lot of cycles for "correctness" in things like this, when I don't need or care about correctness. Some of the big speedups I get when re-writing from C to assembly are places where I can cut corners (because for example, I know that my index is never going to need 16 bits)

User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 501
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: Copying a pointer of a pointer to a pointer

Post by Jarhmander » Wed Oct 31, 2018 10:19 pm

Wow, this shows that I should not post minutes before leaving for work...

This is correct, and about the best I can think of at the moment:

Code: Select all

    ldy #0
    sty zptemp+1
    lda index
    asl a
    rol zptemp+1
    adc arrayOfArray
    sta zptemp
    lda arrayOfArray+1
    adc zptemp+1
    sta zptemp+1
    lda (zptemp), y
    sta array
    iny
    lda (zptemp), y
    sta array+1
It looks more compact than cc65's code, but the generated code uses lot of 1 byte instructions. The code above is 33 bytes vs 38 (inlined pointer dereference) or 31 (non inlined code, without accounting for ldaxi) for the C version. As for speed, it's a constant 53 cycles vs up to 63 (inlined) / 75 (non-inlined). Hence, as I expect ldaxi to be used fairly often, cc65's code looks optimal space-wise.
((λ (x) (x x)) (λ (x) (x x)))

User avatar
rainwarrior
Posts: 7824
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by rainwarrior » Wed Oct 31, 2018 10:59 pm

If you do enough casting to turn everything into explicitly 8-bit operations it gets a little closer to optimal, except for remembering what's in the Y register:

Code: Select all

; extern unsigned char index;
; extern const unsigned char* arrayOfArray[];
; extern const unsigned char* array;
;
; index <<= 1;
; *(((unsigned char*)&array)+0) = (((unsigned char *)&arrayOfArray)+0)[index];
; *(((unsigned char*)&array)+1) = (((unsigned char *)&arrayOfArray)+1)[index];

	lda     _index
	asl     a
	sta     _index
	ldy     _index
	lda     _arrayOfArray,y
	sta     _array
	ldy     _index
	lda     _arrayOfArray+1,y
	sta     _array+1
...but at that point you should just be writing inline assembly instead. :P It'll make more sense than that syntax. (Maybe it wouldn't be so bad in macro form.)

User avatar
DRW
Posts: 1976
Joined: Sat Sep 07, 2013 2:59 pm

Re: Copying a pointer of a pointer to a pointer

Post by DRW » Thu Nov 01, 2018 4:40 am

rainwarrior wrote:...but at that point you should just be writing inline assembly instead. :P It'll make more sense than that syntax. (Maybe it wouldn't be so bad in macro form.)
And if you write a macro and capsulate it away anyway, you can again simply use inline Assembly. :mrgreen:

By the way:
rainwarrior wrote:

Code: Select all

; index <<= 1;
I would be careful with that. You might still need the original index value, so you need to remember to put it back afterwards. Again a reason to use inline assembly (with or without macros) right away.
My game "City Trouble": www.denny-r-walter.de/city.htm

User avatar
rainwarrior
Posts: 7824
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Copying a pointer of a pointer to a pointer

Post by rainwarrior » Thu Nov 01, 2018 9:03 am

The important part of using "index" in that example is that it can only hold an 8-bit result, i.e. we need to cast back to unsigned char after the <<.

I tried to find a way to turn it into some sort of temporary value, but any way I did that CC65 ended up putting it on the stack, and then you get library calls etc.

In assembly of course you can just put it in a register, but CC65 isn't really smart enough for that, as we can see. (The register keyword is unhelpful here.) I couldn't find any useful way to express it in C that didn't produce worse code, unfortunately.


The other suggestion is to just use an index that is already pre-multiplied by 2, so you never end up having to double it, but that's not something I could express with code in this example.

Post Reply