Basic nametable reading question (.incbin addressing)

A place for your artistic side. Discuss techniques and tools for pixel art on the NES, GBC, or similar platforms.

Moderator: Moderators

Post Reply
User avatar
nesrocks
Posts: 459
Joined: Thu Aug 13, 2015 4:40 pm
Location: Rio de Janeiro - Brazil
Contact:

Basic nametable reading question (.incbin addressing)

Post by nesrocks » Tue Aug 01, 2017 4:55 pm

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
Last edited by nesrocks on Wed Aug 02, 2017 12:49 pm, edited 4 times in total.
https://twitter.com/bitinkstudios <- Follow me on twitter! Thanks!

tepples
Posts: 21983
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Basic nametable reading question

Post by tepples » Tue Aug 01, 2017 5:09 pm

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

User avatar
mikejmoffitt
Posts: 1349
Joined: Sun May 27, 2012 8:43 pm

Re: Basic nametable reading question

Post by mikejmoffitt » Tue Aug 01, 2017 5:15 pm

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.

User avatar
nesrocks
Posts: 459
Joined: Thu Aug 13, 2015 4:40 pm
Location: Rio de Janeiro - Brazil
Contact:

Re: Basic nametable reading question

Post by nesrocks » Tue Aug 01, 2017 5:21 pm

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
https://twitter.com/bitinkstudios <- Follow me on twitter! Thanks!

User avatar
thefox
Posts: 3141
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Basic nametable reading question

Post by thefox » Wed Aug 02, 2017 9:17 am

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"
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi

User avatar
tokumaru
Posts: 11708
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Basic nametable reading question

Post by tokumaru » Wed Aug 02, 2017 10:03 am

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

User avatar
thefox
Posts: 3141
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Basic nametable reading question

Post by thefox » Wed Aug 02, 2017 11:57 am

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
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi

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

Re: Basic nametable reading question

Post by rainwarrior » Wed Aug 02, 2017 12:00 pm

!!! How did I not know that ca65 has way to trim an .incbin like that???
Thanks for making me aware of this.

User avatar
mikejmoffitt
Posts: 1349
Joined: Sun May 27, 2012 8:43 pm

Re: Basic nametable reading question

Post by mikejmoffitt » Wed Aug 02, 2017 12:09 pm

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.

User avatar
tokumaru
Posts: 11708
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Basic nametable reading question

Post by tokumaru » Wed Aug 02, 2017 12:12 pm

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!

User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2032
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Basic nametable reading question

Post by FrankenGraphics » Wed Aug 02, 2017 12:38 pm

Incbin management like this is super useful for a beginner like me, too. Tip of the month at the very least!
http://www.frankengraphics.com - personal NES blog

User avatar
nesrocks
Posts: 459
Joined: Thu Aug 13, 2015 4:40 pm
Location: Rio de Janeiro - Brazil
Contact:

Re: Basic nametable reading question (.incbin addressing)

Post by nesrocks » Wed Aug 02, 2017 3:19 pm

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.
https://twitter.com/bitinkstudios <- Follow me on twitter! Thanks!

Post Reply