So I'm having some difficulty understanding what exactly I want to do with the fine X and fine Y scroll in a per-scanline renderer. I know the fine scroll is an offset. So am I correct in my understanding that the offset is within the palette entries? In other words, if I have a fine X scroll of 4, I'm picking the palette entry for 4 pixels ahead instead of the currently rendered pixel? Likewise, if I'm on the 7th pixel of the row, and the fine X scroll is 4, am I selecting the 3rd pixel of the next tile as the palette color for that pixel?
Currently aside from setting the registers (which I've verified against Loopy's doc and a zillion other posts here) I'm not doing anything with the fine X scroll. I've tried a few high level ideas but I'm really just confused as to how and when I need to be using it.
Here's what I'm currently using to select tiles:
Currently aside from setting the registers (which I've verified against Loopy's doc and a zillion other posts here) I'm not doing anything with the fine X scroll. I've tried a few high level ideas but I'm really just confused as to how and when I need to be using it.
Here's what I'm currently using to select tiles:
Code:
func (p *Ppu) renderTileRow() {
// Generates each tile, one scanline at a time
// and applies the palette
// 32 total for 32 tiles, 8 pixels of each
for x := 0; x < 32; x++ {
attrAddr := 0x23C0 | (p.VramAddress & 0xC00) | int(p.AttributeLocation[p.VramAddress&0x3FF])
shift := p.AttributeShift[p.VramAddress&0x3FF]
attr := ((p.Vram[attrAddr] >> shift) & 0x03) << 2
ntAddress := 0x2000 | (p.VramAddress & 0xC00)
// bgPatternTableAddress() returns the address of that tile index so 0x0/0x1000 + (address * 0x10)
t := p.bgPatternTableAddress(p.Vram[(p.VramAddress&0x3FF)+ntAddress])
tile := p.Vram[t : t+16]
xcoord := p.VramAddress & 0x1F
p.decodePatternTile([]Word{tile[p.TilerowCounter], tile[p.TilerowCounter+8]},
xcoord*8,
p.Scanline,
p.bgPaletteEntry(attr),
nil, false)
// Flip bit 10 on wraparound
if p.VramAddress&0x1F == 0x1F {
// If rendering is enabled, at the end of a scanline
// copy bits 10 and 4-0 from VRAM latch into VRAMADDR
p.VramAddress = p.VramAddress ^ (p.VramLatch & 0x41F)
} else {
p.VramAddress++
}
}
p.TilerowCounter++
if p.TilerowCounter == 8 {
p.TilerowCounter = 0
}
}
// Generates each tile, one scanline at a time
// and applies the palette
// 32 total for 32 tiles, 8 pixels of each
for x := 0; x < 32; x++ {
attrAddr := 0x23C0 | (p.VramAddress & 0xC00) | int(p.AttributeLocation[p.VramAddress&0x3FF])
shift := p.AttributeShift[p.VramAddress&0x3FF]
attr := ((p.Vram[attrAddr] >> shift) & 0x03) << 2
ntAddress := 0x2000 | (p.VramAddress & 0xC00)
// bgPatternTableAddress() returns the address of that tile index so 0x0/0x1000 + (address * 0x10)
t := p.bgPatternTableAddress(p.Vram[(p.VramAddress&0x3FF)+ntAddress])
tile := p.Vram[t : t+16]
xcoord := p.VramAddress & 0x1F
p.decodePatternTile([]Word{tile[p.TilerowCounter], tile[p.TilerowCounter+8]},
xcoord*8,
p.Scanline,
p.bgPaletteEntry(attr),
nil, false)
// Flip bit 10 on wraparound
if p.VramAddress&0x1F == 0x1F {
// If rendering is enabled, at the end of a scanline
// copy bits 10 and 4-0 from VRAM latch into VRAMADDR
p.VramAddress = p.VramAddress ^ (p.VramLatch & 0x41F)
} else {
p.VramAddress++
}
}
p.TilerowCounter++
if p.TilerowCounter == 8 {
p.TilerowCounter = 0
}
}