mirror of
https://github.com/Candygoblen123/SwiftNES.git
synced 2024-11-24 18:06:47 -06:00
Render: implement basic chr rom renderer
This commit is contained in:
parent
830bc5f65a
commit
bd12a61330
34
Sources/Render/Frame.swift
Normal file
34
Sources/Render/Frame.swift
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
class Frame {
|
||||||
|
var data: [UInt8] = [UInt8](repeating: 0, count: WIDTH * HEIGHT * 3)
|
||||||
|
|
||||||
|
static let WIDTH = 256
|
||||||
|
static let HEIGHT = 240
|
||||||
|
|
||||||
|
func setPixel(_ loc: (x: Int, y: Int), _ color: (r: UInt8, g: UInt8, b: UInt8)) {
|
||||||
|
let base = loc.y * 3 * Frame.WIDTH + loc.x * 3
|
||||||
|
if base + 2 < data.count {
|
||||||
|
data[base] = color.r
|
||||||
|
data[base + 1] = color.g
|
||||||
|
data[base + 2] = color.b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NESColor {
|
||||||
|
static let SYSTEM_PALLETE: [(UInt8, UInt8, UInt8)] = [
|
||||||
|
(0x80, 0x80, 0x80), (0x00, 0x3D, 0xA6), (0x00, 0x12, 0xB0), (0x44, 0x00, 0x96), (0xA1, 0x00, 0x5E),
|
||||||
|
(0xC7, 0x00, 0x28), (0xBA, 0x06, 0x00), (0x8C, 0x17, 0x00), (0x5C, 0x2F, 0x00), (0x10, 0x45, 0x00),
|
||||||
|
(0x05, 0x4A, 0x00), (0x00, 0x47, 0x2E), (0x00, 0x41, 0x66), (0x00, 0x00, 0x00), (0x05, 0x05, 0x05),
|
||||||
|
(0x05, 0x05, 0x05), (0xC7, 0xC7, 0xC7), (0x00, 0x77, 0xFF), (0x21, 0x55, 0xFF), (0x82, 0x37, 0xFA),
|
||||||
|
(0xEB, 0x2F, 0xB5), (0xFF, 0x29, 0x50), (0xFF, 0x22, 0x00), (0xD6, 0x32, 0x00), (0xC4, 0x62, 0x00),
|
||||||
|
(0x35, 0x80, 0x00), (0x05, 0x8F, 0x00), (0x00, 0x8A, 0x55), (0x00, 0x99, 0xCC), (0x21, 0x21, 0x21),
|
||||||
|
(0x09, 0x09, 0x09), (0x09, 0x09, 0x09), (0xFF, 0xFF, 0xFF), (0x0F, 0xD7, 0xFF), (0x69, 0xA2, 0xFF),
|
||||||
|
(0xD4, 0x80, 0xFF), (0xFF, 0x45, 0xF3), (0xFF, 0x61, 0x8B), (0xFF, 0x88, 0x33), (0xFF, 0x9C, 0x12),
|
||||||
|
(0xFA, 0xBC, 0x20), (0x9F, 0xE3, 0x0E), (0x2B, 0xF0, 0x35), (0x0C, 0xF0, 0xA4), (0x05, 0xFB, 0xFF),
|
||||||
|
(0x5E, 0x5E, 0x5E), (0x0D, 0x0D, 0x0D), (0x0D, 0x0D, 0x0D), (0xFF, 0xFF, 0xFF), (0xA6, 0xFC, 0xFF),
|
||||||
|
(0xB3, 0xEC, 0xFF), (0xDA, 0xAB, 0xEB), (0xFF, 0xA8, 0xF9), (0xFF, 0xAB, 0xB3), (0xFF, 0xD2, 0xB0),
|
||||||
|
(0xFF, 0xEF, 0xA6), (0xFF, 0xF7, 0x9C), (0xD7, 0xE8, 0x95), (0xA6, 0xED, 0xAF), (0xA2, 0xF2, 0xDA),
|
||||||
|
(0x99, 0xFF, 0xFC), (0xDD, 0xDD, 0xDD), (0x11, 0x11, 0x11), (0x11, 0x11, 0x11)
|
||||||
|
]
|
||||||
|
}
|
80
Sources/Render/TileViewer.swift
Normal file
80
Sources/Render/TileViewer.swift
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
struct TileViewer {
|
||||||
|
static func showTile(chrRom: [UInt8], bank: Int, tileNum: Int) -> Frame {
|
||||||
|
guard bank <= 1 else { fatalError("CHR Rom bank must be >1") }
|
||||||
|
|
||||||
|
let frame = Frame()
|
||||||
|
let bank = bank * 0x1000
|
||||||
|
|
||||||
|
let tile = chrRom[(bank + tileNum * 16)...(bank + tileNum * 16 + 15)]
|
||||||
|
|
||||||
|
for y in 0...7 {
|
||||||
|
var upper = tile[tile.startIndex + y]
|
||||||
|
var lower = tile[tile.startIndex + y + 8]
|
||||||
|
|
||||||
|
for x in (0...7).reversed() {
|
||||||
|
let value = (1 & upper) << 1 | (1 & lower)
|
||||||
|
upper = upper >> 1
|
||||||
|
lower = lower >> 1
|
||||||
|
let rgb = switch value {
|
||||||
|
case 0:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x01]
|
||||||
|
case 1:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x23]
|
||||||
|
case 2:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x28]
|
||||||
|
case 3:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x31]
|
||||||
|
default:
|
||||||
|
fatalError("Invalid Pallete Color type")
|
||||||
|
}
|
||||||
|
frame.setPixel((x, y), rgb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
static func showTileBank(chrRom: [UInt8], bank: Int) -> Frame {
|
||||||
|
let frame = Frame()
|
||||||
|
var tileY = 0
|
||||||
|
var tileX = 0
|
||||||
|
let bank = (bank * 0x1000)
|
||||||
|
|
||||||
|
for tileNum in 0..<255 {
|
||||||
|
if tileNum != 0 && tileNum % 20 == 0 {
|
||||||
|
tileY += 10;
|
||||||
|
tileX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tile = chrRom[(bank + tileNum * 16)...(bank + tileNum * 16 + 15)]
|
||||||
|
|
||||||
|
for y in 0...7 {
|
||||||
|
var upper = tile[tile.startIndex + y]
|
||||||
|
var lower = tile[tile.startIndex + y + 8]
|
||||||
|
|
||||||
|
for x in (0...7).reversed() {
|
||||||
|
let value = (1 & upper) << 1 | (1 & lower)
|
||||||
|
upper = upper >> 1
|
||||||
|
lower = lower >> 1
|
||||||
|
let rgb = switch value {
|
||||||
|
case 0:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x01]
|
||||||
|
case 1:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x23]
|
||||||
|
case 2:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x28]
|
||||||
|
case 3:
|
||||||
|
NESColor.SYSTEM_PALLETE[0x31]
|
||||||
|
default:
|
||||||
|
fatalError("Invalid Pallete Color type")
|
||||||
|
}
|
||||||
|
frame.setPixel((tileX + x, tileY + y), rgb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tileX += 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
}
|
@ -9,41 +9,117 @@ guard SDL_Init(SDL_INIT_VIDEO) == 0 else {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let window = SDL_CreateWindow(
|
let window = SDL_CreateWindow(
|
||||||
"Snake Game",
|
"SwiftNES",
|
||||||
Int32(SDL_WINDOWPOS_CENTERED_MASK), Int32(SDL_WINDOWPOS_CENTERED_MASK),
|
Int32(SDL_WINDOWPOS_CENTERED_MASK), Int32(SDL_WINDOWPOS_CENTERED_MASK),
|
||||||
Int32(32.0 * 10.0), Int32(32.0 * 10.0),
|
Int32(256.0 * 3.0), Int32(240.0 * 3.0),
|
||||||
SDL_WINDOW_SHOWN.rawValue)
|
SDL_WINDOW_SHOWN.rawValue)
|
||||||
|
|
||||||
let canvas = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC.rawValue)
|
let canvas = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC.rawValue)
|
||||||
|
|
||||||
SDL_RenderSetScale(canvas, 10.0, 10.0)
|
SDL_RenderSetScale(canvas, 3.0, 3.0)
|
||||||
|
|
||||||
var texture = SDL_CreateTexture(canvas, SDL_PIXELFORMAT_RGB24.rawValue, Int32(SDL_TEXTUREACCESS_TARGET.rawValue), 32, 32)
|
var texture = SDL_CreateTexture(canvas, SDL_PIXELFORMAT_RGB24.rawValue, Int32(SDL_TEXTUREACCESS_TARGET.rawValue), 256, 240)
|
||||||
|
|
||||||
var event = SDL_Event()
|
var event = SDL_Event()
|
||||||
var quit = false
|
var quit = false
|
||||||
|
|
||||||
func handleUserInput(_ cpu: CPU, event: inout SDL_Event) {
|
// func handleUserInput(_ cpu: CPU, event: inout SDL_Event) {
|
||||||
|
// while SDL_PollEvent(&event) > 0 {
|
||||||
|
// if event.type == SDL_QUIT.rawValue {
|
||||||
|
// SDL_DestroyWindow(window)
|
||||||
|
// SDL_Quit()
|
||||||
|
// exit(0)
|
||||||
|
// }
|
||||||
|
// if event.type == SDL_KEYDOWN.rawValue {
|
||||||
|
// switch SDL_KeyCode(UInt32(event.key.keysym.sym)) {
|
||||||
|
// case SDLK_ESCAPE:
|
||||||
|
// SDL_DestroyWindow(window)
|
||||||
|
// SDL_Quit()
|
||||||
|
// exit(0)
|
||||||
|
// case SDLK_w:
|
||||||
|
// cpu.memWrite(0xff, data: 0x77)
|
||||||
|
// case SDLK_a:
|
||||||
|
// cpu.memWrite(0xff, data: 0x61)
|
||||||
|
// case SDLK_s:
|
||||||
|
// cpu.memWrite(0xff, data: 0x73)
|
||||||
|
// case SDLK_d:
|
||||||
|
// cpu.memWrite(0xff, data: 0x64)
|
||||||
|
// default:
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func color(_ byte: UInt8) -> SDL_Color {
|
||||||
|
// switch byte{
|
||||||
|
// case 0:
|
||||||
|
// return SDL_Color(r: 0, g: 0, b: 0, a: 255)
|
||||||
|
// case 1:
|
||||||
|
// return SDL_Color(r: 255, g: 255, b: 255, a: 255)
|
||||||
|
// case 2, 9:
|
||||||
|
// return SDL_Color(r: 128, g: 128, b: 128, a: 255)
|
||||||
|
// case 3, 10:
|
||||||
|
// return SDL_Color(r: 255, g: 0, b: 0, a: 255)
|
||||||
|
// case 4, 11:
|
||||||
|
// return SDL_Color(r: 0, g: 255, b: 0, a: 255)
|
||||||
|
// case 5, 12:
|
||||||
|
// return SDL_Color(r: 0, g: 0, b: 255, a: 255)
|
||||||
|
// case 6, 13:
|
||||||
|
// return SDL_Color(r: 255, g: 0, b: 255, a: 255)
|
||||||
|
// case 7, 14:
|
||||||
|
// return SDL_Color(r: 255, g: 255, b: 0, a: 255)
|
||||||
|
// default:
|
||||||
|
// return SDL_Color(r: 0, g: 255, b: 255, a: 255)
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func readScreenState(_ cpu: CPU, frame: inout [UInt8]) -> Bool {
|
||||||
|
|
||||||
|
// var frame_idx = 0
|
||||||
|
// var update = false
|
||||||
|
// for i in 0x0200..<0x600 {
|
||||||
|
// let color_idx = cpu.memRead(UInt16(i))
|
||||||
|
// let color = color(color_idx)
|
||||||
|
// let (b1, b2, b3) = (color.r, color.b, color.g)
|
||||||
|
// if frame[frame_idx] != b1 || frame[frame_idx + 1] != b2 || frame[frame_idx + 2] != b3 {
|
||||||
|
// frame[frame_idx] = b1;
|
||||||
|
// frame[frame_idx + 1] = b2;
|
||||||
|
// frame[frame_idx + 2] = b3;
|
||||||
|
// update = true;
|
||||||
|
// }
|
||||||
|
// frame_idx += 3
|
||||||
|
// }
|
||||||
|
// return update
|
||||||
|
// }
|
||||||
|
|
||||||
|
guard let bytes = NSData(contentsOfFile: "pacman.nes") else { fatalError("Rom not found") }
|
||||||
|
var gameCode = [UInt8](repeating: 0, count: bytes.length)
|
||||||
|
bytes.getBytes(&gameCode, length: bytes.length)
|
||||||
|
|
||||||
|
let rom = try Rom(gameCode)
|
||||||
|
|
||||||
|
//let tileFrame = TileViewer.showTile(chrRom: rom.character, bank: 1, tileNum: 0)
|
||||||
|
let tileFrame = TileViewer.showTileBank(chrRom: rom.character, bank: 1)
|
||||||
|
|
||||||
|
SDL_UpdateTexture(texture, nil, tileFrame.data, 256 * 3)
|
||||||
|
SDL_RenderCopy(canvas, texture, nil, nil)
|
||||||
|
SDL_RenderPresent(canvas)
|
||||||
|
|
||||||
|
while true {
|
||||||
while SDL_PollEvent(&event) > 0 {
|
while SDL_PollEvent(&event) > 0 {
|
||||||
if event.type == SDL_QUIT.rawValue {
|
if event.type == SDL_QUIT.rawValue {
|
||||||
SDL_DestroyWindow(window)
|
SDL_DestroyWindow(window)
|
||||||
SDL_Quit()
|
SDL_Quit()
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
if event.type == SDL_KEYDOWN.rawValue {
|
if event.type == SDL_KEYDOWN.rawValue {
|
||||||
switch SDL_KeyCode(UInt32(event.key.keysym.sym)) {
|
switch SDL_KeyCode(UInt32(event.key.keysym.sym)) {
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
SDL_DestroyWindow(window)
|
SDL_DestroyWindow(window)
|
||||||
SDL_Quit()
|
SDL_Quit()
|
||||||
exit(0)
|
exit(0)
|
||||||
case SDLK_w:
|
|
||||||
cpu.memWrite(0xff, data: 0x77)
|
|
||||||
case SDLK_a:
|
|
||||||
cpu.memWrite(0xff, data: 0x61)
|
|
||||||
case SDLK_s:
|
|
||||||
cpu.memWrite(0xff, data: 0x73)
|
|
||||||
case SDLK_d:
|
|
||||||
cpu.memWrite(0xff, data: 0x64)
|
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -51,79 +127,32 @@ func handleUserInput(_ cpu: CPU, event: inout SDL_Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func color(_ byte: UInt8) -> SDL_Color {
|
// let bus = Bus(try! Rom(gameCode))
|
||||||
switch byte{
|
|
||||||
case 0:
|
|
||||||
return SDL_Color(r: 0, g: 0, b: 0, a: 255)
|
|
||||||
case 1:
|
|
||||||
return SDL_Color(r: 255, g: 255, b: 255, a: 255)
|
|
||||||
case 2, 9:
|
|
||||||
return SDL_Color(r: 128, g: 128, b: 128, a: 255)
|
|
||||||
case 3, 10:
|
|
||||||
return SDL_Color(r: 255, g: 0, b: 0, a: 255)
|
|
||||||
case 4, 11:
|
|
||||||
return SDL_Color(r: 0, g: 255, b: 0, a: 255)
|
|
||||||
case 5, 12:
|
|
||||||
return SDL_Color(r: 0, g: 0, b: 255, a: 255)
|
|
||||||
case 6, 13:
|
|
||||||
return SDL_Color(r: 255, g: 0, b: 255, a: 255)
|
|
||||||
case 7, 14:
|
|
||||||
return SDL_Color(r: 255, g: 255, b: 0, a: 255)
|
|
||||||
default:
|
|
||||||
return SDL_Color(r: 0, g: 255, b: 255, a: 255)
|
|
||||||
|
|
||||||
}
|
//var cpu = CPU(bus: bus)
|
||||||
}
|
|
||||||
|
|
||||||
func readScreenState(_ cpu: CPU, frame: inout [UInt8]) -> Bool {
|
|
||||||
|
|
||||||
var frame_idx = 0
|
|
||||||
var update = false
|
|
||||||
for i in 0x0200..<0x600 {
|
|
||||||
let color_idx = cpu.memRead(UInt16(i))
|
|
||||||
let color = color(color_idx)
|
|
||||||
let (b1, b2, b3) = (color.r, color.b, color.g)
|
|
||||||
if frame[frame_idx] != b1 || frame[frame_idx + 1] != b2 || frame[frame_idx + 2] != b3 {
|
|
||||||
frame[frame_idx] = b1;
|
|
||||||
frame[frame_idx + 1] = b2;
|
|
||||||
frame[frame_idx + 2] = b3;
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
frame_idx += 3
|
|
||||||
}
|
|
||||||
return update
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let rom = NSData(contentsOfFile: "snake.nes") else { fatalError("Rom not found") }
|
|
||||||
var gameCode = [UInt8](repeating: 0, count: rom.length)
|
|
||||||
rom.getBytes(&gameCode, length: rom.length)
|
|
||||||
|
|
||||||
let bus = Bus(try! Rom(gameCode))
|
|
||||||
|
|
||||||
var cpu = CPU(bus: bus)
|
|
||||||
//cpu.load(gameCode)
|
//cpu.load(gameCode)
|
||||||
cpu.reset()
|
//cpu.reset()
|
||||||
//cpu.programCounter = 0xC000
|
//cpu.programCounter = 0xC000
|
||||||
|
|
||||||
var screenState = [UInt8](repeating: 0, count: 32 * 3 * 32)
|
// var screenState = [UInt8](repeating: 0, count: 32 * 3 * 32)
|
||||||
var rng = SystemRandomNumberGenerator()
|
// var rng = SystemRandomNumberGenerator()
|
||||||
cpu.run(onCycle: {
|
// cpu.run(onCycle: {
|
||||||
//print(dumpCpuState(cpu))
|
// //print(dumpCpuState(cpu))
|
||||||
handleUserInput(cpu, event: &event)
|
// handleUserInput(cpu, event: &event)
|
||||||
cpu.memWrite(0xfe, data: UInt8.random(in: 1...16, using: &rng))
|
// cpu.memWrite(0xfe, data: UInt8.random(in: 1...16, using: &rng))
|
||||||
|
|
||||||
if readScreenState(cpu, frame: &screenState) {
|
// if readScreenState(cpu, frame: &screenState) {
|
||||||
SDL_UpdateTexture(texture, nil, screenState, 32 * 3)
|
// SDL_UpdateTexture(texture, nil, screenState, 32 * 3)
|
||||||
SDL_RenderCopy(canvas, texture, nil, nil)
|
// SDL_RenderCopy(canvas, texture, nil, nil)
|
||||||
SDL_RenderPresent(canvas)
|
// SDL_RenderPresent(canvas)
|
||||||
}
|
// }
|
||||||
|
|
||||||
usleep(70)
|
// usleep(70)
|
||||||
}, onComplete: {
|
// }, onComplete: {
|
||||||
SDL_DestroyWindow(window)
|
// SDL_DestroyWindow(window)
|
||||||
SDL_Quit()
|
// SDL_Quit()
|
||||||
exit(0)
|
// exit(0)
|
||||||
})
|
// })
|
||||||
|
|
||||||
// Infinite loop otherwise the program will exit prematurely
|
// Infinite loop otherwise the program will exit prematurely
|
||||||
RunLoop.main.run()
|
// RunLoop.main.run()
|
||||||
|
Loading…
Reference in New Issue
Block a user