Page 1 of 1
Copying data from a file larger than 256 bytes
Posted: Thu Jan 27, 2011 10:18 pm
by devmas
So I started doing some NES coding.
I have a question. It's probably fairly newbie-ish...
I have incbin'd a file from YY-CHR. Basically, it's nametable and attribute table data I made for a title screen. I used a hex editor to look at the binary data of the YY-CHR file, and the format is like this:
0000 - 03BF: Name table
03C0 - 03FF: Attribute table
I want to copy all of it to the PPU with an incrementing register. However, I'm not sure how to...
Normally, I would do a simple loop containing:
LDA titlescreengfx, x
STA $2007
INX
However, since the data's more than 256 bytes big, x won't increment that big.
Of course, instead of .incbinning the data I have the following options:
- split the 1 kb file up into 4 parts
- .db the data and create labels every 256 bytes
...but for ease of use, I'd rather just .incbin the file. What method should I use to copy the file to the PPU?
Note: I'm using NESASM, but wouldn't be opposed to knowing about it in ASM6.
Another note: I know it's horrible inefficient to have a 1 kb file like that. If I need to, I'll compress the data with a simple compression, either RLE or LZ.
Posted: Thu Jan 27, 2011 10:28 pm
by WJYkK
Assuming that before that you wrote to $20 and $00 to $2006 and then you wrote the loop, after that you could write $21 and $00 to $2006 and repeat the loop, e.g.:
Code: Select all
lda #$20 ;Here we're writing to $2000
sta $2006
lda #$00
sta $2006
ldx #$00
loop1:
lda titlescreengfx,x
sta $2007
inx
cpx #$00
bne loop1
lda #$21 ;And here - to $2100 - $100 bytes later
sta $2006
lda #$00
sta $2006
loop2:
lda titlescreengfx,x
sta $2007
inx
cpx #$00
bne loop2
Etc.
Posted: Thu Jan 27, 2011 10:39 pm
by devmas
I see... Thanks for your help.
I see that loop1 copies the first 256 bytes from the file to the PPU.
But won't loop2 just go back to reading from titlescreengfx + 0, the beginning of the .incbin file? So it would just copy the first 256 bytes of the file to the PPU again?
Or is there something I'm missing?
Posted: Thu Jan 27, 2011 10:47 pm
by tokumaru
Use indirect indexed addressing (LDA ($XX), Y). Instead of using a hardcoded address (i.e. the label) when reading the data, you use a 16-bit zero page variable to point to the data. Since this is a variable, you can manipulate the address you are reading from.
This addressing mode only works with the Y register, so the copying code will look something like this (this will copy 256 * X bytes):
Code: Select all
;setup the pointer
lda #<OneKByteOfData
sta ZPVariable+0
lda #>OneKByteOfData
sta ZPVariable+1
;set the number of times to copy 256 bytes
ldx #$04
;clear the index
ldy #$00
CopyByte:
;copy a byte
lda (ZPVariable), y
sta $2007
;move on to the next byte
iny
bne CopyByte
;skip to the end if there's no more data
dex
beq Done
;move the pointer 256 bytes ahead
inc ZPVariable+1
jmp CopyByte
Done:
If the amount of data to copy is not a multiple of 256, you have to find other ways to detect the end of the data, like counting bytes or using a flag (a value that marks the end of the data, like a tile index never used in the image), but this is the basic idea.
Also, this is standard 6502 syntax, which NESASM doesn't seem to follow. To load the individual bytes of an address you might have to use LOW() and HIGH() instead of < and >, and [] for indirection, rather than ().
EDIT: You can make a lot of loops like WJYkK suggested (I'm assuming he forgot to put +256 after the label in the second loop), but that solution is far from optimal, since for each name table in your program you will need 4 loops. Soon that can add up to a lot of code, not only taking a lot of space but also making maintenance harder. If you use pointers, you can have a single routine do all the copying you need, you only have to provide it with the source and destination addresses (source goes into the pointer and destination goes to $2006), along with the number of bytes to copy. This could also be stored before the data itself, like this:
Code: Select all
SomeData:
.dw SomeDataEnd - SomeDataStart
SomeDataStart:
.incbin "somedata.bin"
SomeDataEnd:
Then when you ask your copying routine to copy SomeData, it will first copy that 16-bit value to RAM and decrement it as the data is transferred, in order to know when to stop.
Posted: Thu Jan 27, 2011 11:29 pm
by devmas
Oh, that's very helpful, thanks.
Because it's only one name table, I will probably go with the method like WJYkK described, but the information you've provided will be of great use to me for other things.
I didn't know about adding to labels like +256, nor did I know about indexed addressing...
Posted: Thu Jan 27, 2011 11:45 pm
by tokumaru
devmas wrote:Because it's only one name table, I will probably go with the method like WJYkK described
So be sure to use titlescreengfx+256, titlescreengfx+512, etc., that he forgot to. Also, you don't have to write the address to $2006 for each loop, the PPU will continue from where it stopped.
but the information you've provided will be of great use to me for other things.
Yeah, that addressing mode is used A LOT. You probably won't find a single serious 6502 program that doesn't use it, as it makes accessing data much easier.
I didn't know about adding to labels like +256
A label is just an easy way for us silly humans to refer to a memory location, because we're better with words than with numbers, but in the end, your assembled program will access all memory locations by number only. Since the label is just a "nickname" for a number, you can do math with it. So if you do LDA Label+256 you will be reading from the location 256 bytes ahead of wherever Label points to.
nor did I know about indexed addressing...
Yes you did, because you were using X (an index register) to read the data!

You didn't know about INDIRECT addressing, which is when instead of using an address directly, we point to the memory location where the address is.
Posted: Fri Jan 28, 2011 3:52 am
by Bregalad
Because it's only one name table, I will probably go with the method like WJYkK described, but the information you've provided will be of great use to me for other things.
Trust me, there is no reason NOT to use indirect adressing if you're copying more than 256 bytes. Using multiple loops that copies 256 or less bytes will just waste program size for nothing.
Unless you want to do something that executes very fast with unrolled loops or something but you probably don't want to worry about that before doing something pretty advanced.