1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
}
|