// The Swift Programming Language // https://docs.swift.org/swift-book import SDL import Foundation guard SDL_Init(SDL_INIT_VIDEO) == 0 else { fatalError("SDL could not initialize! SDL_Error: \(String(cString: SDL_GetError()))") } let window = SDL_CreateWindow( "Snake Game", Int32(SDL_WINDOWPOS_CENTERED_MASK), Int32(SDL_WINDOWPOS_CENTERED_MASK), Int32(32.0 * 10.0), Int32(32.0 * 10.0), SDL_WINDOW_SHOWN.rawValue) let canvas = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC.rawValue) SDL_RenderSetScale(canvas, 10.0, 10.0) var texture = SDL_CreateTexture(canvas, SDL_PIXELFORMAT_RGB24.rawValue, Int32(SDL_TEXTUREACCESS_TARGET.rawValue), 32, 32) var event = SDL_Event() var quit = false 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 } let gameCode: [UInt8] = [ 0x20, 0x06, 0x06, 0x20, 0x38, 0x06, 0x20, 0x0d, 0x06, 0x20, 0x2a, 0x06, 0x60, 0xa9, 0x02, 0x85, 0x02, 0xa9, 0x04, 0x85, 0x03, 0xa9, 0x11, 0x85, 0x10, 0xa9, 0x10, 0x85, 0x12, 0xa9, 0x0f, 0x85, 0x14, 0xa9, 0x04, 0x85, 0x11, 0x85, 0x13, 0x85, 0x15, 0x60, 0xa5, 0xfe, 0x85, 0x00, 0xa5, 0xfe, 0x29, 0x03, 0x18, 0x69, 0x02, 0x85, 0x01, 0x60, 0x20, 0x4d, 0x06, 0x20, 0x8d, 0x06, 0x20, 0xc3, 0x06, 0x20, 0x19, 0x07, 0x20, 0x20, 0x07, 0x20, 0x2d, 0x07, 0x4c, 0x38, 0x06, 0xa5, 0xff, 0xc9, 0x77, 0xf0, 0x0d, 0xc9, 0x64, 0xf0, 0x14, 0xc9, 0x73, 0xf0, 0x1b, 0xc9, 0x61, 0xf0, 0x22, 0x60, 0xa9, 0x04, 0x24, 0x02, 0xd0, 0x26, 0xa9, 0x01, 0x85, 0x02, 0x60, 0xa9, 0x08, 0x24, 0x02, 0xd0, 0x1b, 0xa9, 0x02, 0x85, 0x02, 0x60, 0xa9, 0x01, 0x24, 0x02, 0xd0, 0x10, 0xa9, 0x04, 0x85, 0x02, 0x60, 0xa9, 0x02, 0x24, 0x02, 0xd0, 0x05, 0xa9, 0x08, 0x85, 0x02, 0x60, 0x60, 0x20, 0x94, 0x06, 0x20, 0xa8, 0x06, 0x60, 0xa5, 0x00, 0xc5, 0x10, 0xd0, 0x0d, 0xa5, 0x01, 0xc5, 0x11, 0xd0, 0x07, 0xe6, 0x03, 0xe6, 0x03, 0x20, 0x2a, 0x06, 0x60, 0xa2, 0x02, 0xb5, 0x10, 0xc5, 0x10, 0xd0, 0x06, 0xb5, 0x11, 0xc5, 0x11, 0xf0, 0x09, 0xe8, 0xe8, 0xe4, 0x03, 0xf0, 0x06, 0x4c, 0xaa, 0x06, 0x4c, 0x35, 0x07, 0x60, 0xa6, 0x03, 0xca, 0x8a, 0xb5, 0x10, 0x95, 0x12, 0xca, 0x10, 0xf9, 0xa5, 0x02, 0x4a, 0xb0, 0x09, 0x4a, 0xb0, 0x19, 0x4a, 0xb0, 0x1f, 0x4a, 0xb0, 0x2f, 0xa5, 0x10, 0x38, 0xe9, 0x20, 0x85, 0x10, 0x90, 0x01, 0x60, 0xc6, 0x11, 0xa9, 0x01, 0xc5, 0x11, 0xf0, 0x28, 0x60, 0xe6, 0x10, 0xa9, 0x1f, 0x24, 0x10, 0xf0, 0x1f, 0x60, 0xa5, 0x10, 0x18, 0x69, 0x20, 0x85, 0x10, 0xb0, 0x01, 0x60, 0xe6, 0x11, 0xa9, 0x06, 0xc5, 0x11, 0xf0, 0x0c, 0x60, 0xc6, 0x10, 0xa5, 0x10, 0x29, 0x1f, 0xc9, 0x1f, 0xf0, 0x01, 0x60, 0x4c, 0x35, 0x07, 0xa0, 0x00, 0xa5, 0xfe, 0x91, 0x00, 0x60, 0xa6, 0x03, 0xa9, 0x00, 0x81, 0x10, 0xa2, 0x00, 0xa9, 0x01, 0x81, 0x10, 0x60, 0xa2, 0x00, 0xea, 0xea, 0xca, 0xd0, 0xfb, 0x60 ] var cpu = CPU() cpu.load(gameCode) cpu.reset() var screenState = [UInt8](repeating: 0, count: 32 * 3 * 32) var rng = SystemRandomNumberGenerator() cpu.run(onCycle: { handleUserInput(cpu, event: &event) cpu.memWrite(0xfe, data: UInt8.random(in: 1...16, using: &rng)) if readScreenState(cpu, frame: &screenState) { SDL_UpdateTexture(texture, nil, screenState, 32 * 3) SDL_RenderCopy(canvas, texture, nil, nil) SDL_RenderPresent(canvas) } }, onComplete: { SDL_DestroyWindow(window) SDL_Quit() exit(0) }) // Infinite loop otherwise the program will exit prematurely RunLoop.main.run()