Page 1 of 1

Basic nametable reading question (.incbin addressing)

Posted: Tue Aug 01, 2017 4:55 pm
by nesrocks
Sorry for the newbie question, I've been searching for practical examples on this but so far not much luck...

So, when developing a nes game with the cc65 compiler and using Nes Screen Tool, if I include a full screen nametable (the whole screen tiles) binary like so:

Code: Select all

nametable:
.incbin "level_01_bg.nam"
Is it possible to read all of its bytes to send data to the PPU if the included file is longer than $FF bytes? I can only access its first 256 bytes by using "LDA nametable,x", but the rest I have no idea how to access.

I got it to work (the full nametable loaded and displayed) by copy/paste the nametable as asm code from Nes Screen Tool and then setting labels dividing it in 4 areas, but workflow would be streamlined if I could export a file from Nes Screen Tool and compile the game as is, especially because of the nametable splitting and having to rename ".db" to ".byte" (is there a way to make NSS more cc65 compatible?).

minor edit: more descriptive title

Re: Basic nametable reading question

Posted: Tue Aug 01, 2017 5:09 pm
by tepples
Do you know what a pointer is?

You'll need to make a pointer in two bytes of zero page and use the (dd),Y addressing mode to step through all four 256-byte pages of nametable data.

EDIT: spelling

Re: Basic nametable reading question

Posted: Tue Aug 01, 2017 5:15 pm
by mikejmoffitt
You can use the Indirect Indexed mode, with Y, to load from a large table.
Assuming a ZP two-byte variable "addr_ptr":

Code: Select all


; Put nametable source address in addr_ptr
lda #<nametable
sta addr_ptr
lda #>nametable
sta addr_ptr+1

; Copy one page of data ($100 bytes)
@copypage:
ldy #0
@copyloop:
lda (addr_ptr), y
sta PPUDATA
iny
bne @copyloop

; Move on to the next page, or leave if finished
lda nametable+1
clc
adc #1
sta nametable+1
cmp #(>nametable + 4)
bne @copypage
This code is really rough, so please forgive minor errors. The jist of it is a copy loop using Y from $00-$FF, and then incrementing the high byte of your pointer after that and doing it again until you are done. In this case, it is four iterations to cover a whole nametable.

This code assumes you have already loaded PPUDATA.

Re: Basic nametable reading question

Posted: Tue Aug 01, 2017 5:21 pm
by nesrocks
tepples wrote:Do you know what a pointer is?
So you mean I can use that to retrieve the label's address at runtime and find the rest of the data relative to its position? I'll look further into zero page pointers and see if that's the case, thanks.

Pre-posting edit: It seems that's the case (sans my probably imprecise way of describing it), thanks mikejmoffitt!

edit: it works! The only typo in your code is that instead of reading and writing to/from "addr_ptr+1" you wrote "nametable+1" but that was an easy fix. Thanks! I'll study those operators (<, >, +1 etc), I kind of see what they do but I had never seen them.

Updated code for posterity:

Code: Select all

; Put nametable source address in addr_ptr
lda #<nametable
sta addr_ptr
lda #>nametable
sta addr_ptr+1

; Copy one page of data ($100 bytes)
@copypage:
ldy #0
@copyloop:
lda (addr_ptr), y
sta PPUDATA
iny
bne @copyloop

; Move on to the next page, or leave if finished
lda addr_ptr+1
clc
adc #1
sta addr_ptr+1
cmp #(>nametable + 4)
bne @copypage

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 9:17 am
by thefox
nesrocks wrote:

Code: Select all

nametable:
.incbin "level_01_bg.nam"
Indirect addressing with (dd),y is the way to go (smaller code size, not that much slower), but I'll just note that you could've also done this to avoid splitting the data manually to 4 parts:

Code: Select all

nametable_part0:
nametable_part1 := nametable_part0+$100
nametable_part2 := nametable_part1+$100
nametable_part3 := nametable_part2+$100
.incbin "level_01_bg.nam"

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 10:03 am
by tokumaru
thefox wrote:

Code: Select all

nametable_part0:
nametable_part1 := nametable_part0+$100
nametable_part2 := nametable_part1+$100
nametable_part3 := nametable_part2+$100
.incbin "level_01_bg.nam"
I sometimes need to do this (usually to label the individual arrays/fields of structures of arrays), so I created a ca65 macro for this (IIRC, you need to supply the names of all the labels and the macro calculates how far apart they are based on the length of the data and the number of labels - so it won't work if the arrays aren't all the same length). I can post it if anyone is interested, I just don't have it with me right now.

For something constant-sized like a name table I probably wouldn't bother creating separate labels though, I'd just adjust the base address directly in the 4 loops that copy the data:

Code: Select all

lda nametable+$000, x
;(...)
lda nametable+$100, x
;(...)
lda nametable+$200, x
;(...)
lda nametable+$300, x

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 11:57 am
by thefox
For completeness, here's one more option (ca65 syntax):

Code: Select all

nametable_part0:
.incbin "level_01_bg.nam", 0*256, 256
nametable_part1:
.incbin "level_01_bg.nam", 1*256, 256
nametable_part2:
.incbin "level_01_bg.nam", 2*256, 256
nametable_part3:
.incbin "level_01_bg.nam", 3*256, 256

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 12:00 pm
by rainwarrior
!!! How did I not know that ca65 has way to trim an .incbin like that???
Thanks for making me aware of this.

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 12:09 pm
by mikejmoffitt
nesrocks wrote:
tepples wrote:Do you know what a pointer is?
So you mean I can use that to retrieve the label's address at runtime and find the rest of the data relative to its position? I'll look further into zero page pointers and see if that's the case, thanks.

Pre-posting edit: It seems that's the case (sans my probably imprecise way of describing it), thanks mikejmoffitt!

edit: it works! The only typo in your code is that instead of reading and writing to/from "addr_ptr+1" you wrote "nametable+1" but that was an easy fix. Thanks! I'll study those operators (<, >, +1 etc), I kind of see what they do but I had never seen them.
Glad it mostly worked! The < and > operators retrieve the low and high addresses of the provided symbol, respectively. <nametable gets the lower byte, and >nametable gets the higher byte of nametable. Putting those into a pointer in order gets you a pointer to the nametable.

addr_ptr+1 simply means "the high byte of addr_ptr", which is one byte after the symbol. That's because addr_ptr uses two bytes to represent a memory address. We're checking the high byte (in this little-endian system) because it effectively indexes the page, which we've already iterated through.
rainwarrior wrote:!!! How did I not know that ca65 has way to trim an .incbin like that???
Thanks for making me aware of this.
Agreed, that's fantastic. This makes a yy-chr palette file directly usable as well.

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 12:12 pm
by tokumaru
rainwarrior wrote:!!! How did I not know that ca65 has way to trim an .incbin like that???
I didn't know either! That may come in handy sometime!

Re: Basic nametable reading question

Posted: Wed Aug 02, 2017 12:38 pm
by FrankenGraphics
Incbin management like this is super useful for a beginner like me, too. Tip of the month at the very least!

Re: Basic nametable reading question (.incbin addressing)

Posted: Wed Aug 02, 2017 3:19 pm
by nesrocks
Amazing stuff that this information could be spread! And here I was wondering if writing "+1" was correct... :lol: Now I know a decimal number doesn't need a preceding character. Coming from a hacking background where I mostly worked with hex numbers typing byte code directly into the hex it looked weird to see in asm code.

And yeah, being able to .incbin only a section of a file seems very powerful for the workflow, one could include parts of other ROMs without splitting it into different files or clogging your own ROM with useless bytes. The uses are endless.