aboutsummaryrefslogtreecommitdiff
path: root/ppu/sprites.go
diff options
context:
space:
mode:
authorcrecher <crecher@crecher.net>2026-10-13 01:13:13 +0200
committercrecher <crecher@crecher.net>2026-10-13 01:13:13 +0200
commit99cae5a5a37f41f878a84857361eda47910303ba (patch)
tree31c8665ca991f95e09e93bb2e6ec91d81d2427ff /ppu/sprites.go
parent96ff6d8eda2ba434682909f4ff4ce350d78b2273 (diff)
downloadgones-99cae5a5a37f41f878a84857361eda47910303ba.tar.gz
gones-99cae5a5a37f41f878a84857361eda47910303ba.zip
sprites!!!!!!
Diffstat (limited to 'ppu/sprites.go')
-rw-r--r--ppu/sprites.go97
1 files changed, 97 insertions, 0 deletions
diff --git a/ppu/sprites.go b/ppu/sprites.go
new file mode 100644
index 0000000..83d1703
--- /dev/null
+++ b/ppu/sprites.go
@@ -0,0 +1,97 @@
+package ppu
+
+import "math/bits"
+
+const (
+ spriteAttrPalette = 0x03
+ spriteAttrBehind = 0x20 // draw behind opaque background pixels
+ spriteAttrFlipH = 0x40
+ spriteAttrFlipV = 0x80
+)
+
+type sprite struct {
+ patternLo, patternHi uint8
+ attr uint8
+ x uint8
+ isSprite0 bool
+}
+
+// ill add comments later im tired
+// https://www.nesdev.org/wiki/PPU_sprite_evaluation
+func (p *PPU) evaluateSprites() {
+ p.spriteCount = 0
+ // nothing is drawn above line 1, so the pre-render line and the last line leave it empty
+ if !p.renderingEnabled() || p.Scanline >= ScreenHeight-1 {
+ return
+ }
+
+ height := 8
+ if p.Ctrl&ctrlSpriteSize16 != 0 {
+ height = 16
+ }
+
+ for i := 0; i < 64; i++ {
+ row := p.Scanline - int(p.OAM[i*4])
+ if row < 0 || row >= height {
+ continue
+ }
+ if p.spriteCount == 8 {
+ p.spriteOverflow = true
+ break
+ }
+ p.spriteLine[p.spriteCount] = p.fetchSprite(i, row, height)
+ p.spriteCount++
+ }
+}
+
+func (p *PPU) fetchSprite(i, row, height int) sprite {
+ tile := p.OAM[i*4+1]
+ attr := p.OAM[i*4+2]
+ if attr&spriteAttrFlipV != 0 {
+ row = height - 1 - row
+ }
+
+ var addr uint16
+ if height == 16 {
+ table := uint16(tile&1) << 12
+ tile &^= 1
+ if row >= 8 {
+ tile++
+ row -= 8
+ }
+ addr = table | uint16(tile)<<4 | uint16(row)
+ } else {
+ var base uint16
+ if p.Ctrl&ctrlSpritePatternHigh != 0 {
+ base = 0x1000
+ }
+ addr = base | uint16(tile)<<4 | uint16(row)
+ }
+
+ lo := p.readVRAM(addr)
+ hi := p.readVRAM(addr + 8)
+ if attr&spriteAttrFlipH != 0 {
+ lo, hi = bits.Reverse8(lo), bits.Reverse8(hi)
+ }
+ return sprite{patternLo: lo, patternHi: hi, attr: attr, x: p.OAM[i*4+3], isSprite0: i == 0}
+}
+
+func (p *PPU) spritePixel(x int) (pixel, palette uint8, behind, isSprite0 bool) {
+ if p.Mask&maskShowSprites == 0 || (x < 8 && p.Mask&maskShowSpritesLeft == 0) {
+ return 0, 0, false, false
+ }
+ for i := 0; i < p.spriteCount; i++ {
+ s := &p.spriteLine[i]
+ offset := x - int(s.x)
+ if offset < 0 || offset > 7 {
+ continue
+ }
+ shift := 7 - offset
+ pixel = (s.patternHi>>shift&1)<<1 | (s.patternLo >> shift & 1)
+ if pixel == 0 {
+ continue
+ }
+ return pixel, s.attr & spriteAttrPalette, s.attr&spriteAttrBehind != 0, s.isSprite0
+ }
+ return 0, 0, false, false
+}