This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

ca65 related question... possibly pertaining to a macro

ca65 related question... possibly pertaining to a macro
by on (#127651)
I remember when I was learning some other programming languages that I've since forgotten, there would be something along the lines of the "length" of a variable or some such. Like, string.length or thereabouts. Well, I was wondering if there was something I can do similar to that with a macro in ca65. An example of what I need is this...

I have a table of bytes. The table of bytes gets read through and tested every frame. At the end of the routine that tests these bytes, I have a simple INX/CPX #$xx to see if all the bytes have been read or not. The slight inconvenience I have is that everytime I add some bytes to the end of the table, I have to manually update what the 'X' register is compared to. I was wondering if there was a way to make a macro or something like that which I could plug in, in place of #$xx, that had a parameter of the name of the table. So when the assembler builds the file, it takes the "table.length" and places the proper number where the CPX #$xx is. Is something like this possible?
Re: ca65 related question... possibly pertaining to a macro
by on (#127654)
A standard thing I've done is put a label at the end. This relies on assemblers always putting things in-order.
This won't work with tools that fuzz the order of arrays to uncover bugs, but most assemblers don't do that.

For example, in ca65:
Code:
mystruct: .byte 2 4 5 7 8
endofmystruct:

mystructlength = endofmystruct - mystruct
Re: ca65 related question... possibly pertaining to a macro
by on (#127655)
The first thing I suggest is scanning the table backwards (if that doesn't break any of your logic), since you can get rid of the CPX and just use the N flag (0-based index, maximum of 128 elements) or the Z flag (1-based index, maximum of 256 elements) to detect the end of the loop, making it faster.

You'd still need to initialize X to the length of the table to begin the loop though, so the question remains. I don't think the assembler has a way to know where the table ends unless you put another label there, in which case you can do (EndLabel - StartLabel) to get the length of the table:

Code:
   LDX #(EndLabel - StartLabel)
Loop:
   ;ACCESS TABLE HERE
   DEX
   BNE Loop

I'm not very familiar with CA65 though, so someone else might have a solution more suited for it.
Re: ca65 related question... possibly pertaining to a macro
by on (#127656)
As a slight simplification of lidnariq's suggestion, ca65 has the * symbol which is the program counter position known at link time, letting you do this:
Code:
mystruct: .byte 2 4 5 7 8
mystructlength = * - mystruct
Re: ca65 related question... possibly pertaining to a macro
by on (#127657)
Very cool!

I happen to have another table that I always have right below it, as it is something that gets accessed along with that routine, so I only have to use that second table with it:

Code:
   cpx #(tele_y - tele_floor)


tokumaru wrote:
The first thing I suggest is scanning the table backwards (if that doesn't break any of your logic), since you can get rid of the CPX and just use the N flag (0-based index, maximum of 128 elements) or the Z flag (1-based index, maximum of 256 elements) to detect the end of the loop, making it faster.


I use that technique when I'm working on some of my 1k games to save a byte here and there, but I find it to be more confusing overall. When I'm doing a regular full blown project, I just stick with the CPX method. Good suggestion of course, though : )

Thanks guys, this was really helpful!
Re: ca65 related question... possibly pertaining to a macro
by on (#127660)
Syntax should be as:

Code:
mystruct: .byte 2, 4, 5, 7, 8


And with ca65, you can use .sizeof() instead of label math, example: (loop from start to #0 as suggested):
Code:

  LDX #(.sizeof(mystruct)-1)
  :
  ; do stuff
  DEX
  BPL :-



edit:Fix bad code :)
Re: ca65 related question... possibly pertaining to a macro
by on (#127661)
Movax12 wrote:
And with ca65, you can use .sizeof() instead of label math

How does it know where the table ends, though? If I put a second .byte statement on the next line, does that also count as part of the table? What if I .incbin a file?
Re: ca65 related question... possibly pertaining to a macro
by on (#127662)
For .incbin I am not sure, but for a label, it seems to be based on EOL.
I know you can also get the size of a .scope (or a .proc):

Code:
.scope DATA
    mylabel: .byte 12, 23, 45, 45, 67, ABh
             .byte 12, 23, 45, 45, 67, ABh
.endscope

.out .sprintf("The size of DATA is: %d" .sizeof(DATA))
Re: ca65 related question... possibly pertaining to a macro
by on (#127666)
If you have a bunch of different items each with different lengths, you can make a table of their lengths just as you make a table of pointers to the start. Or you can do it the C way: reserve one byte value for "end of data" and end the loop once you hit that byte.
Code:
page_ptrs:
  .addr page1_txt, page2_txt, page3_txt
num_pages = (* - page_ptrs) / 2

page1_txt:
  .incbin "instructions1.txt"
  .byte $00
page2_txt:
  .incbin "instructions2.txt"
  .byte $00