From 5bdfebdf13bef31723d2dd8682ebda7f81d330ae Mon Sep 17 00:00:00 2001 From: Candygoblen123 Date: Fri, 17 May 2024 14:15:10 -0400 Subject: [PATCH] test snake game --- Sources/CPU.swift | 352 +++++++++++++++++---------------- Sources/CPU_Instructions.swift | 4 +- Sources/main.swift | 98 ++++++++- 3 files changed, 270 insertions(+), 184 deletions(-) diff --git a/Sources/CPU.swift b/Sources/CPU.swift index 741432b..1bfc643 100644 --- a/Sources/CPU.swift +++ b/Sources/CPU.swift @@ -1,3 +1,6 @@ +import Foundation +import SDL + enum AddressingMode { case Immediate case ZeroPage @@ -61,7 +64,6 @@ class CPU { return base &+ UInt16(register_y) case .Indirect_X: let base = memRead(programCounter) - let ptr = UInt8(base) &+ register_x let lo = memRead(UInt16(ptr)) let hi = memRead(UInt16(ptr &+ 1)) @@ -101,184 +103,189 @@ class CPU { } func run() { - run { + run {} + } + func run(callback: @escaping () -> ()) { + let opcodes = OPCODES_MAP + Timer.scheduledTimer(withTimeInterval: 0.00007, repeats: true) { [self] timer in + processOpcodes(callback: callback, opcodes: opcodes, timer: timer) } } - func run(callback: () -> ()) { - let opcodes = OPCODES_MAP + func processOpcodes(callback: () -> (), opcodes: [UInt8: OpCode], timer: Timer) { + callback() + let code = memRead(programCounter) + programCounter += 1 - while true { - callback() - let code = memRead(programCounter) - programCounter += 1 + let programCounterState = programCounter + guard let opcode = opcodes[code] else {fatalError("OpCode \(code) not recgonized!")} + // print(programCounter, opcode.mnemonic) - let programCounterState = programCounter - guard let opcode = opcodes[code] else {fatalError("OpCode \(code) not recgonized!")} - - switch code { - /// LDA - case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1: - lda(opcode.mode) - /// STA - case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: - sta(opcode.mode) - case 0xd8: - status.remove(.decimalMode) - case 0x58: - status.remove(.interruptDisable) - case 0xb8: - status.remove(.overflow) - case 0x18: - clearCarryFlag() - case 0x38: - setCarryFlag() - case 0x78: - status.insert(.interruptDisable) - case 0xf8: - status.insert(.decimalMode) - case 0x48: - stackPush(register_a) - case 0x68: - pla() - case 0x08: - php() - case 0x28: - plp() - case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71: - adc(opcode.mode) - case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1: - sbc(opcode.mode) - case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31: - and(opcode.mode) - case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51: - eor(opcode.mode) - case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11: - ora(opcode.mode) - case 0x4a: - lsrAccumulator() - case 0x46, 0x56, 0x4e, 0x5e: - _ = lsr(opcode.mode) - case 0x0a: - aslAccumulator() - case 0x06, 0x16, 0x0e, 0x1e: - _ = asl(opcode.mode) - case 0x2a: - rolAccumulator() - case 0x26, 0x36, 0x2e, 0x3e: - _ = rol(opcode.mode) - case 0x6a: - rorAccumulator() - case 0x66, 0x76, 0x6e, 0x7e: - _ = ror(opcode.mode) - case 0xe6, 0xf6, 0xee, 0xfe: - _ = inc(opcode.mode) - case 0xc8: - iny() - case 0xc6, 0xd6, 0xce, 0xde: - _ = dec(opcode.mode) - case 0xca: - dex() - case 0x88: - dey() - case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1: - compare(mode: opcode.mode, compare_with: register_a) - case 0xc0, 0xc4, 0xcc: - compare(mode: opcode.mode, compare_with: register_y) - case 0xe0, 0xe4, 0xec: - compare(mode: opcode.mode, compare_with: register_x) - case 0x4c: - let memAddr = memReadU16(programCounter) - programCounter = memAddr - case 0x6c: - let memAddr = memReadU16(programCounter) - //6502 bug mode with with page boundary: - // if address $3000 contains $40, $30FF contains $80, and $3100 contains $50, - // the result of JMP ($30FF) will be a transfer of control to $4080 rather than $5080 as you intended - // i.e. the 6502 took the low byte of the address from $30FF and the high byte from $3000 - let indirectRef: UInt16 - if memAddr & 0x00ff == 0x00ff { - let lo = memRead(memAddr) - let hi = memRead(memAddr & 0x00ff) - indirectRef = UInt16(hi) << 8 | UInt16(lo) - } else { - indirectRef = memReadU16(memAddr) - } - - programCounter = indirectRef - - case 0x20: - stackPushU16(programCounter + 2 - 1) - let targetAddr = memReadU16(programCounter) - programCounter = targetAddr - case 0x60: - programCounter = stackPopU16() + 1 - case 0x40: - status.rawValue = stackPop() - status.remove(.break1) - status.remove(.break2) - - programCounter = stackPopU16() - case 0xd0: - branch(!status.contains(.zero)) - case 0x70: - branch(status.contains(.overflow)) - case 0x50: - branch(!status.contains(.overflow)) - case 0x10: - branch(!status.contains(.negative)) - case 0x30: - branch(status.contains(.negative)) - case 0xf0: - branch(status.contains(.zero)) - case 0xb0: - branch(status.contains(.carry)) - case 0x90: - branch(!status.contains(.carry)) - case 0x24, 0x2c: - bit(opcode.mode) - case 0x86, 0x96, 0x8e: - let addr = getOpperandAddress(opcode.mode) - memWrite(addr, data: register_x) - case 0x84, 0x94, 0x8c: - let addr = getOpperandAddress(opcode.mode) - memWrite(addr, data: register_y) - case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: - ldx(opcode.mode) - case 0xa0, 0xa4, 0xb4, 0xac, 0xbc: - ldy(opcode.mode) - case 0xea: - continue - case 0xa8: - register_y = register_x - updateZeroAndNegativeFlags(register_y) - case 0xba: - register_x = stackPointer - updateZeroAndNegativeFlags(register_x) - case 0x8a: - register_a = register_x - updateZeroAndNegativeFlags(register_a) - case 0x9a: - stackPointer = register_x - case 0x98: - register_a = register_y - updateZeroAndNegativeFlags(register_a) - /// TAX - case 0xaa: - tax() - /// INX - case 0xe8: - inx() - /// BRK - case 0x00: - return - default: fatalError("TODO!") + switch code { + /// LDA + case 0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1: + lda(opcode.mode) + /// STA + case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: + sta(opcode.mode) + case 0xd8: + status.remove(.decimalMode) + case 0x58: + status.remove(.interruptDisable) + case 0xb8: + status.remove(.overflow) + case 0x18: + clearCarryFlag() + case 0x38: + setCarryFlag() + case 0x78: + status.insert(.interruptDisable) + case 0xf8: + status.insert(.decimalMode) + case 0x48: + stackPush(register_a) + case 0x68: + pla() + case 0x08: + php() + case 0x28: + plp() + case 0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71: + adc(opcode.mode) + case 0xe9, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1: + sbc(opcode.mode) + case 0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31: + and(opcode.mode) + case 0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51: + eor(opcode.mode) + case 0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11: + ora(opcode.mode) + case 0x4a: + lsrAccumulator() + case 0x46, 0x56, 0x4e, 0x5e: + _ = lsr(opcode.mode) + case 0x0a: + aslAccumulator() + case 0x06, 0x16, 0x0e, 0x1e: + _ = asl(opcode.mode) + case 0x2a: + rolAccumulator() + case 0x26, 0x36, 0x2e, 0x3e: + _ = rol(opcode.mode) + case 0x6a: + rorAccumulator() + case 0x66, 0x76, 0x6e, 0x7e: + _ = ror(opcode.mode) + case 0xe6, 0xf6, 0xee, 0xfe: + _ = inc(opcode.mode) + case 0xc8: + iny() + case 0xc6, 0xd6, 0xce, 0xde: + _ = dec(opcode.mode) + case 0xca: + dex() + case 0x88: + dey() + case 0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1: + compare(mode: opcode.mode, compare_with: register_a) + case 0xc0, 0xc4, 0xcc: + compare(mode: opcode.mode, compare_with: register_y) + case 0xe0, 0xe4, 0xec: + compare(mode: opcode.mode, compare_with: register_x) + case 0x4c: + let memAddr = memReadU16(programCounter) + programCounter = memAddr + case 0x6c: + let memAddr = memReadU16(programCounter) + //6502 bug mode with with page boundary: + // if address $3000 contains $40, $30FF contains $80, and $3100 contains $50, + // the result of JMP ($30FF) will be a transfer of control to $4080 rather than $5080 as you intended + // i.e. the 6502 took the low byte of the address from $30FF and the high byte from $3000 + let indirectRef: UInt16 + if memAddr & 0x00ff == 0x00ff { + let lo = memRead(memAddr) + let hi = memRead(memAddr & 0x00ff) + indirectRef = UInt16(hi) << 8 | UInt16(lo) + } else { + indirectRef = memReadU16(memAddr) } - if programCounterState == programCounter { - programCounter += UInt16(opcode.len - 1) - } + programCounter = indirectRef + + case 0x20: + stackPushU16(programCounter + 2 - 1) + let targetAddr = memReadU16(programCounter) + programCounter = targetAddr + case 0x60: + programCounter = stackPopU16() + 1 + case 0x40: + status.rawValue = stackPop() + status.remove(.break1) + status.remove(.break2) + + programCounter = stackPopU16() + case 0xd0: + branch(!status.contains(.zero)) + case 0x70: + branch(status.contains(.overflow)) + case 0x50: + branch(!status.contains(.overflow)) + case 0x10: + branch(!status.contains(.negative)) + case 0x30: + branch(status.contains(.negative)) + case 0xf0: + branch(status.contains(.zero)) + case 0xb0: + branch(status.contains(.carry)) + case 0x90: + branch(!status.contains(.carry)) + case 0x24, 0x2c: + bit(opcode.mode) + case 0x86, 0x96, 0x8e: + let addr = getOpperandAddress(opcode.mode) + memWrite(addr, data: register_x) + case 0x84, 0x94, 0x8c: + let addr = getOpperandAddress(opcode.mode) + memWrite(addr, data: register_y) + case 0xa2, 0xa6, 0xb6, 0xae, 0xbe: + ldx(opcode.mode) + case 0xa0, 0xa4, 0xb4, 0xac, 0xbc: + ldy(opcode.mode) + case 0xea: + return + case 0xa8: + register_y = register_x + updateZeroAndNegativeFlags(register_y) + case 0xba: + register_x = stackPointer + updateZeroAndNegativeFlags(register_x) + case 0x8a: + register_a = register_x + updateZeroAndNegativeFlags(register_a) + case 0x9a: + stackPointer = register_x + case 0x98: + register_a = register_y + updateZeroAndNegativeFlags(register_a) + /// TAX + case 0xaa: + tax() + /// INX + case 0xe8: + inx() + /// BRK + case 0x00: + timer.invalidate() + SDL_DestroyWindow(window) + SDL_Quit() + exit(0) + default: fatalError("TODO!") + } + + if programCounterState == programCounter { + programCounter += UInt16(opcode.len - 1) } } @@ -323,7 +330,7 @@ class CPU { status.remove(.carry) } - let result = UInt8(sum) + let result = UInt8(truncatingIfNeeded: sum) if (data ^ result) & (result ^ register_a) & 0x80 != 0 { status.insert(.overflow) @@ -373,7 +380,6 @@ class CPU { func branch(_ condition: Bool) { if condition { let addr = memRead(programCounter) - // print(addr) let jump: Int8 = Int8(bitPattern: addr) let jump_addr = programCounter &+ 1 &+ UInt16(bitPattern: Int16(jump)) diff --git a/Sources/CPU_Instructions.swift b/Sources/CPU_Instructions.swift index f9a5187..71358aa 100644 --- a/Sources/CPU_Instructions.swift +++ b/Sources/CPU_Instructions.swift @@ -61,8 +61,8 @@ extension CPU { func sbc(_ mode: AddressingMode) { let addr = getOpperandAddress(mode) let data = memRead(addr) - let res = Int8(data) &* -1 - addToRegisterA(UInt8(res &- 1)) + let res = Int8(bitPattern: data) &* -1 + addToRegisterA(UInt8(bitPattern: res &- 1)) } func adc(_ mode: AddressingMode) { diff --git a/Sources/main.swift b/Sources/main.swift index dbfb28f..63be3b7 100644 --- a/Sources/main.swift +++ b/Sources/main.swift @@ -2,6 +2,7 @@ // 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()))") @@ -17,6 +18,81 @@ 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, @@ -41,19 +117,23 @@ let gameCode: [UInt8] = [ 0xea, 0xca, 0xd0, 0xfb, 0x60 ] -print(gameCode[32]) - - var cpu = CPU() cpu.load(gameCode) cpu.reset() -cpu.run { - // TODO: - // read user input and write it to mem[0xFF] - // update mem[0xFE] with new Random Number - // read mem mapped screen state - // render screen state +var screenState = [UInt8](repeating: 0, count: 32 * 3 * 32) +var rng = SystemRandomNumberGenerator() +cpu.run() { + 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) + } } + +// Infinite loop otherwise the program will exit prematurely +RunLoop.main.run()