I have put together a small routine that copies a 16x16 metatile map to the name table. However, I have come to a slight problem. When the first two tiles of the metatile is written to the name table, the next two come right after, when they are supposed to be written right under the two first, like this:
AB
CD
But now it's like this:
ABCD
I understand that I somehow has to increase the PPU adress by 32 after B has been written, but how do I do that? Here's the code:
Code:
; PALETTE LOADING AND INITIALIZATION HERE
RenderOuterLoop:
lda LevelData, x
sta TempMul
asl a
asl a
clc
adc TempMul
tax
ldy #0
RenderInnerLoop:
lda Blocks, x
sta $2007
inx
iny
cpy #4
bne RenderInnerLoop
lda #0
sta $2000
ldx CurrentBlock
inx
cpx #240
stx CurrentBlock
bne RenderOuterLoop
Blocks:
; DUMMY TILE
.db 0 ; Tile 1
.db 0 ; tile 2
.db 0 ; tile 3
.db 0 ; tile 4
.db 0 ; attribute byte (low two bits)
; BOX TILE
.db 01
.db 02
.db 03
.db 04
.db 0
LevelData is the level data, which consists of a 240 bytes map over metatiles (this is a included file). CurrentBlock is just there to remember which metatile in the level map it had come to.
Thanks.
I you are writing only one metatile, you'll have to set the address, write 2 tiles, set the address again, write the other 2 tiles.
If you are rendering a full column of metatiles, however, you'll most likely want to draw ACACACACAC... of all metatiles until you complete the first column of tiles. When that is over, you set the address to the next column (previous address + 1) and write BDBDBDBDBD... of all metatiles.
That's what I do when rendering columns.
Would I have enough time during VBlank to write two whole columns? That would be a bit easier.
If you're quick enough about it, yeah you could fit in two columns. Although you'll still have to write one column at a time.
Final Fantasy broke up its metatiles in a way which seemed strange to me a long time ago, but now I totally understand it (and it makes a lot of sense). It has $80 different 16x16 tiles per map, so it stored that all of the A tiles bunched together in one clump, then all the B tiles, then C, then D. This made drawing really easy -- say it needed to draw a column of ACAC:
Code:
lda map_data,y ; get the metatile ID
tax ; put it in X
lda a_data,x ; load the 'A' tile
sta $2007 ; draw it
lda c_data,x ; load the 'C' tile
sta $2007 ; draw it
;loop
If you're storing all the ABCD stuff together, you'll have to do some math to calculate where the desired 'C' tile is. This setup lets you avoid that by keeping all the tiles seperate.
Don't know if that helps or not ^^
Jaffe wrote:
Would I have enough time during VBlank to write two whole columns? That would be a bit easier.
If you are willing to give up some PRG space to completely unroll the rendering loop, you can do a lot more than that. If you unroll it partially, you are able to write 2 columns without problems.
I store my metatiles just a Disch said, first all the A's, then all the B's, etc. Then you can do it as he said, using the same index (the unmodified metatile index) to read all the tiles. I'd do that outside of VBlank, though, and output everything to a buffer. Then, the code that outputs the data to $2007 can just dump the buffer, wich can be really fast.
Thanks, you guys are great!
tokumaru wrote:
If you are willing to give up some PRG space to completely unroll the rendering loop, you can do a lot more than that. If you unroll it partially, you are able to write 2 columns without problems.
Unrolling is crazy and space-consumming. You should only use it in very particular case. VBlank is short, but you still have enough time to write a complete row or columns with normal loops and doing sprite DMA in the same VBlank. That is perfectly doable.
I said "if". I'm sure regular loops allow for a decent ammount of tiles to be rendered, if that's enough for you, great. But in case you wish to write more stuff, don't think it is impossible, just unroll the loops. Sometimes it is worth the required space.
I'm myself against using crazy programming methods unless you really want do special effects or something.
You shouldn't mention that in a newbie thread I think, because it doesn't give them the image of fair programming, but of tricky programming.
But if we don't mention that kind of stuff, the first thing we see is newbies trying to put a whole lot of logic inside Vblank. VBlank should have very little logic, regardless of using long unrolled loops and large buffers or not...
I get your point, guys. I just do all the calculations, etc. to a buffer while the PPU is writing to the screen, and then I just copy it to VRAM during VBlank.
Yes, but in some case where you'd have nothing but just two nametable bytes to update in VBlank (such as Dragon Warrior games' blinking cursor), you may even have time to do all related calculation during VBlank to simplify logic, since you'd update nothing else than just one or two nametable bytes. The data to write itself is very plain, but the calulation for the adress of the cursor could be a little more complicated.
Most nesdev people are against this, but I see nothing wrong and many commercial games DOES this.
However, the most often, you'd want to do buffering. That is fair for anything that have to do with scrolling and metatiles, and is overall better, unless you're doing something very simple as above.
Ok, I made a loop that loads a whole 240 bytes map of 16x16 metatiles. Here's the code (I know it's a huge mess):
Code:
lda #$20
sta $2006
lda #$00
sta $2006
lda #%00000100
sta $2000
ldx #00
stx CurrentColumn
ldy #00
sty PPULow
LoadBlockColumns:
tya
clc
adc CurrentColumn
tax
lda LevelData, x
tax
lda PartFlag
cmp #0
beq RenderAC
RenderBD:
lda B_blocks, x
sta $2007
lda D_blocks, x
sta $2007
jmp ContinueRender
RenderAC:
lda A_blocks, x
sta $2007
lda C_blocks, x
sta $2007
ContinueRender:
tya
clc
adc #16
tay
cpy #240
bne LoadBlockColumns
ldy #0
lda PartFlag
eor #%00000001
sta PartFlag
inc CurrentColumn
lda #$20
sta $2006
lda CurrentColumn
sta $2006
ldx CurrentColumn
cpx #32
bne LoadBlockColumns
This is just some test code. The whole loop is ran with the background and sprite display off.
PartFlag is used to see if it should draw AC or BD tiles. However, this loop gives me a quite corrupt image at the end of the name table:
[/img]