It is currently Tue Nov 13, 2018 9:39 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 15 posts ] 
Author Message
PostPosted: Mon Oct 29, 2018 6:08 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1726
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:
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:
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.)

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Mon Oct 29, 2018 6:13 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
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


Top
 Profile  
 
PostPosted: Mon Oct 29, 2018 6:22 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1726
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:
array = arrayOfArray[index];

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Mon Oct 29, 2018 6:28 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
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


Top
 Profile  
 
PostPosted: Mon Oct 29, 2018 6:29 pm 
Offline
Formerly 65024U

Joined: Sat Mar 27, 2010 12:57 pm
Posts: 2264
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.

Top
 Profile  
 
PostPosted: Mon Oct 29, 2018 6:34 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1726
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.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Mon Oct 29, 2018 6:38 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1726
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:
LDA index
ASL
TAY
LDA (arrayOfArray), Y
STA array + 0
INY
LDA (arrayOfArray), Y
STA array + 1

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Wed Oct 31, 2018 6:03 am 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 474
Location: Rive nord de Montréal
Your code works as long as index < 128; otherwise, you need something like this:
Code:
    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)))


Top
 Profile  
 
PostPosted: Wed Oct 31, 2018 9:28 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
Quote:
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


Top
 Profile  
 
PostPosted: Wed Oct 31, 2018 10:27 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
Actual code generated:
Code:
; 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.


Top
 Profile  
 
PostPosted: Wed Oct 31, 2018 1:38 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 489
Location: Central Illinois, USA
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)

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Wed Oct 31, 2018 10:19 pm 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 474
Location: Rive nord de Montréal
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:
    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)))


Top
 Profile  
 
PostPosted: Wed Oct 31, 2018 10:59 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
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:
; 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.)


Top
 Profile  
 
PostPosted: Thu Nov 01, 2018 4:40 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1726
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:
; 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.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Thu Nov 01, 2018 9:03 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google [Bot], VEG and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group