mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2024-12-03 03:51:16 -06:00
PPU: impl scrolling
This commit is contained in:
parent
af3be8d93d
commit
9f12bb2ba0
@ -23,8 +23,11 @@ class Bus {
|
||||
|
||||
func tick(_ cycles: UInt8) {
|
||||
self.cycles += Int(cycles)
|
||||
let newFrame = self.ppu.tick(cycles * 3)
|
||||
if newFrame {
|
||||
|
||||
let nmiBefore = ppu.nmiInterrupt != nil
|
||||
_ = self.ppu.tick(cycles * 3)
|
||||
let nmiAfter = ppu.nmiInterrupt != nil
|
||||
if !nmiBefore && nmiAfter {
|
||||
gameloopCallback(ppu)
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,9 @@ class NesPPU {
|
||||
func tick(_ cycles: UInt8) -> Bool {
|
||||
self.cycles += Int(cycles)
|
||||
if self.cycles >= 341 {
|
||||
if checkSprite0Hit(self.cycles) {
|
||||
status.setSpriteZeroHit(true)
|
||||
}
|
||||
self.cycles = self.cycles - 341
|
||||
self.scanline += 1
|
||||
|
||||
@ -50,6 +53,12 @@ class NesPPU {
|
||||
return false
|
||||
}
|
||||
|
||||
func checkSprite0Hit(_ cycle: Int) -> Bool {
|
||||
let y = Int(oamData[0])
|
||||
let x = Int(oamData[0])
|
||||
return (y == scanline) && x <= cycle && mask.showSprites()
|
||||
}
|
||||
|
||||
func pollNMI() -> UInt8? {
|
||||
let tmp = self.nmiInterrupt
|
||||
self.nmiInterrupt = nil
|
||||
|
@ -70,4 +70,19 @@ struct ControlRegister: OptionSet {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
func nametableAddr() -> UInt16 {
|
||||
switch rawValue & 0b11 {
|
||||
case 0:
|
||||
0x2000
|
||||
case 1:
|
||||
0x2400
|
||||
case 2:
|
||||
0x2800
|
||||
case 3:
|
||||
0x2c00
|
||||
default:
|
||||
fatalError("naemtableAddr: Not possible!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,40 @@
|
||||
class Render {
|
||||
static func render(_ ppu: NesPPU, frame: Frame) {
|
||||
let bank = ppu.ctrl.backgroundPatternAddr()
|
||||
let scroll = (x: Int(ppu.scroll.x), y: Int(ppu.scroll.y))
|
||||
|
||||
for i in 0..<0x03c0 { // FIXME: For now, just use first nametable
|
||||
let tileAddr = UInt16(ppu.vram[i])
|
||||
let tileLoc = (col: i % 32, row: i / 32)
|
||||
let tile = ppu.chrRom[(bank + Int(tileAddr) * 16)...(bank + Int(tileAddr) * 16 + 15)]
|
||||
let bgPalette = getBgPalette(ppu, tileLoc: tileLoc)
|
||||
let (mainNametable, secondNametable) = switch (ppu.mirroring, ppu.ctrl.nametableAddr()) {
|
||||
case (.vertical, 0x2000), (.vertical, 0x2800), (.horizontal, 0x2000), (.horizontal, 0x2400):
|
||||
(ppu.vram[0..<0x400], ppu.vram[0x400..<0x800])
|
||||
case (.vertical, 0x2400), (.vertical, 0x2c00), (.horizontal, 0x2800), (.horizontal, 0x2c00):
|
||||
(ppu.vram[0x400..<0x800], ppu.vram[0..<0x400])
|
||||
default:
|
||||
fatalError("cringe looking nametable arrangment: \(ppu.mirroring)")
|
||||
}
|
||||
|
||||
// MARK: Draw Background
|
||||
for y in 0...7 {
|
||||
var upper = tile[tile.startIndex + y]
|
||||
var lower = tile[tile.startIndex + y + 8]
|
||||
renderNameTable(
|
||||
ppu,
|
||||
frame: frame,
|
||||
nameTable: Array(mainNametable),
|
||||
viewPort: Rect(x1: scroll.x, y1: scroll.y, x2: 256, y2: 240),
|
||||
shift: (-scroll.x, -scroll.y)
|
||||
)
|
||||
|
||||
for x in [7,6,5,4,3,2,1,0] {
|
||||
let value = (1 & lower) << 1 | (1 & upper)
|
||||
upper = upper >> 1
|
||||
lower = lower >> 1
|
||||
let rgb = switch value {
|
||||
case 0:
|
||||
NESColor.SYSTEM_PALLETE[Int(ppu.paletteTable[0])]
|
||||
case 1:
|
||||
NESColor.SYSTEM_PALLETE[Int(bgPalette[1])]
|
||||
case 2:
|
||||
NESColor.SYSTEM_PALLETE[Int(bgPalette[2])]
|
||||
case 3:
|
||||
NESColor.SYSTEM_PALLETE[Int(bgPalette[3])]
|
||||
default:
|
||||
fatalError("Invalid Pallete Color type")
|
||||
}
|
||||
frame.setPixel((tileLoc.col * 8 + x, tileLoc.row * 8 + y), rgb)
|
||||
}
|
||||
}
|
||||
if scroll.x > 0 {
|
||||
renderNameTable(
|
||||
ppu,
|
||||
frame: frame,
|
||||
nameTable: Array(secondNametable),
|
||||
viewPort: Rect(x1: 0, y1: 0, x2: scroll.x, y2: 240),
|
||||
shift: (256 - scroll.x, 0)
|
||||
)
|
||||
} else if scroll.y > 0 {
|
||||
renderNameTable(
|
||||
ppu,
|
||||
frame: frame,
|
||||
nameTable: Array(secondNametable),
|
||||
viewPort: Rect(x1: 0, y1: 0, x2: 256, y2: scroll.y),
|
||||
shift: (0, 240 - scroll.y)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: Draw Sprites
|
||||
@ -85,9 +88,50 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
static func getBgPalette(_ ppu: NesPPU, tileLoc: (col: Int, row: Int)) -> [UInt8] {
|
||||
static func renderNameTable(_ ppu: NesPPU, frame: Frame, nameTable: [UInt8], viewPort: Rect, shift: (x: Int, y: Int)) {
|
||||
let bank = ppu.ctrl.backgroundPatternAddr()
|
||||
let attributeTable = nameTable[0x3c0..<0x400]
|
||||
|
||||
for i in 0..<0x3c0 {
|
||||
let tileAddr = UInt16(nameTable[i])
|
||||
let tileLoc = (col: i % 32, row: i / 32)
|
||||
let tile = ppu.chrRom[(bank + Int(tileAddr) * 16)...(bank + Int(tileAddr) * 16 + 15)]
|
||||
let bgPalette = getBgPalette(ppu, tileLoc: tileLoc, attributeTable: Array(attributeTable))
|
||||
|
||||
// MARK: Draw Background
|
||||
for y in 0...7 {
|
||||
var upper = tile[tile.startIndex + y]
|
||||
var lower = tile[tile.startIndex + y + 8]
|
||||
|
||||
for x in [7,6,5,4,3,2,1,0] {
|
||||
let value = (1 & lower) << 1 | (1 & upper)
|
||||
upper = upper >> 1
|
||||
lower = lower >> 1
|
||||
let rgb = switch value {
|
||||
case 0:
|
||||
NESColor.SYSTEM_PALLETE[Int(ppu.paletteTable[0])]
|
||||
case 1:
|
||||
NESColor.SYSTEM_PALLETE[Int(bgPalette[1])]
|
||||
case 2:
|
||||
NESColor.SYSTEM_PALLETE[Int(bgPalette[2])]
|
||||
case 3:
|
||||
NESColor.SYSTEM_PALLETE[Int(bgPalette[3])]
|
||||
default:
|
||||
fatalError("Invalid Pallete Color type")
|
||||
}
|
||||
|
||||
let pixelLoc = (x: tileLoc.col * 8 + x, y: tileLoc.row * 8 + y)
|
||||
if pixelLoc.x >= viewPort.x1 && pixelLoc.x < viewPort.x2 && pixelLoc.y >= viewPort.y1 && pixelLoc.y < viewPort.y2 {
|
||||
frame.setPixel((shift.x + pixelLoc.x, shift.y + pixelLoc.y), rgb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func getBgPalette(_ ppu: NesPPU, tileLoc: (col: Int, row: Int), attributeTable: [UInt8]) -> [UInt8] {
|
||||
let attrTableIndex = tileLoc.row / 4 * 8 + tileLoc.col / 4
|
||||
let attrByte = ppu.vram[0x3c0 + attrTableIndex] // FIXME: still using hardcoded first nametable
|
||||
let attrByte = attributeTable[attrTableIndex]
|
||||
|
||||
let palleteIndex = switch (tileLoc.col % 4 / 2, tileLoc.row % 4 / 2) {
|
||||
case (0,0):
|
||||
@ -103,7 +147,12 @@ class Render {
|
||||
}
|
||||
|
||||
let palleteStartIndex = 1 + Int(palleteIndex) * 4
|
||||
return [ppu.paletteTable[0], ppu.paletteTable[palleteStartIndex], ppu.paletteTable[palleteStartIndex + 1], ppu.paletteTable[palleteStartIndex + 2]]
|
||||
return [
|
||||
ppu.paletteTable[0],
|
||||
ppu.paletteTable[palleteStartIndex],
|
||||
ppu.paletteTable[palleteStartIndex + 1],
|
||||
ppu.paletteTable[palleteStartIndex + 2],
|
||||
]
|
||||
}
|
||||
|
||||
static func getSpritePalette(_ ppu: NesPPU, paletteIndex: UInt8) -> [UInt8] {
|
||||
@ -116,3 +165,10 @@ class Render {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
struct Rect {
|
||||
let x1: Int
|
||||
let y1: Int
|
||||
let x2: Int
|
||||
let y2: Int
|
||||
}
|
||||
|
@ -22,7 +22,8 @@ var texture = SDL_CreateTexture(canvas, SDL_PIXELFORMAT_RGB24.rawValue, Int32(SD
|
||||
|
||||
var event = SDL_Event()
|
||||
|
||||
guard let bytes = NSData(contentsOfFile: "pacman.nes") else { fatalError("Rom not found") }
|
||||
guard let bytes = NSData(contentsOfFile: "smb1.nes") else { fatalError("Rom not found") }
|
||||
|
||||
var gameCode = [UInt8](repeating: 0, count: bytes.length)
|
||||
bytes.getBytes(&gameCode, length: bytes.length)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user