mirror of
				https://github.com/Candygoblen123/SwiftNES.git
				synced 2025-10-30 20:42:59 -05:00 
			
		
		
		
	impl the rest of the instructions
This commit is contained in:
		
							
								
								
									
										14
									
								
								Package.resolved
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Package.resolved
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | { | ||||||
|  |   "pins" : [ | ||||||
|  |     { | ||||||
|  |       "identity" : "swiftsdl2", | ||||||
|  |       "kind" : "remoteSourceControl", | ||||||
|  |       "location" : "https://github.com/ctreffs/SwiftSDL2.git", | ||||||
|  |       "state" : { | ||||||
|  |         "revision" : "30a2886bd68e43fc19ba29b63ffe230ac0e4db7a", | ||||||
|  |         "version" : "1.4.1" | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "version" : 2 | ||||||
|  | } | ||||||
| @@ -5,11 +5,18 @@ import PackageDescription | |||||||
|  |  | ||||||
| let package = Package( | let package = Package( | ||||||
|     name: "SwiftNES", |     name: "SwiftNES", | ||||||
|  |     platforms: [.macOS("11.0")], | ||||||
|  |     dependencies: [ | ||||||
|  |       .package(url: "https://github.com/ctreffs/SwiftSDL2.git", from: "1.4.0") | ||||||
|  |     ], | ||||||
|     targets: [ |     targets: [ | ||||||
|         // Targets are the basic building blocks of a package, defining a module or a test suite. |         // Targets are the basic building blocks of a package, defining a module or a test suite. | ||||||
|         // Targets can depend on other targets in this package and products from dependencies. |         // Targets can depend on other targets in this package and products from dependencies. | ||||||
|         .executableTarget( |         .executableTarget( | ||||||
|             name: "SwiftNES"), |           name: "SwiftNES", | ||||||
|  |         dependencies: [ | ||||||
|  |           .product(name: "SDL", package: "SwiftSDL2") | ||||||
|  |         ]), | ||||||
|         .testTarget(name: "SwiftNESTests", dependencies: ["SwiftNES"]) |         .testTarget(name: "SwiftNESTests", dependencies: ["SwiftNES"]) | ||||||
|     ] |     ] | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| enum AddressingMode { | enum AddressingMode { | ||||||
|    case Immediate |    case Immediate | ||||||
|    case ZeroPage |    case ZeroPage | ||||||
| @@ -13,12 +11,28 @@ enum AddressingMode { | |||||||
|    case NoneAddressing |    case NoneAddressing | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct CPUFlags: OptionSet { | ||||||
|  |     var rawValue: UInt8 | ||||||
|  |  | ||||||
|  |     static let carry = CPUFlags(rawValue: 0b00000001) | ||||||
|  |     static let zero = CPUFlags(rawValue: 0b00000010) | ||||||
|  |     static let interruptDisable = CPUFlags(rawValue: 0b00000100) | ||||||
|  |     static let decimalMode = CPUFlags(rawValue: 0b00001000) | ||||||
|  |     static let break1 = CPUFlags(rawValue: 0b00010000) | ||||||
|  |     static let break2 = CPUFlags(rawValue: 0b00100000) | ||||||
|  |     static let overflow = CPUFlags(rawValue: 0b01000000) | ||||||
|  |     static let negative = CPUFlags(rawValue: 0b10000000) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | let STACK: UInt16 = 0x0100 | ||||||
|  | let STACK_RESET: UInt8 = 0xfd | ||||||
|  |  | ||||||
| class CPU { | class CPU { | ||||||
|     var register_a: UInt8 = 0 |     var register_a: UInt8 = 0 | ||||||
|     var register_x: UInt8 = 0 |     var register_x: UInt8 = 0 | ||||||
|     var register_y: UInt8 = 0 |     var register_y: UInt8 = 0 | ||||||
|  |     var stackPointer: UInt8 = STACK_RESET | ||||||
|     var status: UInt8 = 0 |     var status: CPUFlags = [.interruptDisable, .break2] | ||||||
|     var programCounter: UInt16 = 0 |     var programCounter: UInt16 = 0 | ||||||
|     private var memory = [UInt8](repeating: 0, count: 0xFFFF) |     private var memory = [UInt8](repeating: 0, count: 0xFFFF) | ||||||
|  |  | ||||||
| @@ -65,32 +79,12 @@ class CPU { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func memRead(_ addr: UInt16) -> UInt8 { |  | ||||||
|         memory[Int(addr)] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     func memReadU16(_ addr: UInt16) -> UInt16 { |  | ||||||
|         let lo = UInt16(memRead(addr)) |  | ||||||
|         let hi = UInt16(memRead(addr + 1)) |  | ||||||
|         return (hi << 8) | lo |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     func memWriteU16(addr: UInt16, data: UInt16) { |  | ||||||
|         let hi = UInt8(data >> 8) |  | ||||||
|         let lo = UInt8(data & 0xff) |  | ||||||
|         self.memWrite(addr: addr, data: lo) |  | ||||||
|         self.memWrite(addr: addr + 1, data: hi) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     func memWrite(addr: UInt16, data: UInt8) { |  | ||||||
|         memory[Int(addr)] = data |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     func reset() { |     func reset() { | ||||||
|         register_a = 0 |         register_a = 0 | ||||||
|         register_x = 0 |         register_x = 0 | ||||||
|         register_y = 0 |         register_y = 0 | ||||||
|         status = 0 |         stackPointer = STACK_RESET | ||||||
|  |         status = [.interruptDisable, .break2] | ||||||
|  |  | ||||||
|         programCounter = self.memReadU16(0xFFFC) |         programCounter = self.memReadU16(0xFFFC) | ||||||
|     } |     } | ||||||
| @@ -102,14 +96,21 @@ class CPU { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     func load(_ program: [UInt8]) { |     func load(_ program: [UInt8]) { | ||||||
|         memory[0x8000 ..< (0x8000 + program.count)] = program[0..<program.count] |         memory[0x0600 ..< (0x0600 + program.count)] = program[0..<program.count] | ||||||
|         memWriteU16(addr: 0xFFFC, data: 0x8000) |         memWriteU16(0xFFFC, data: 0x0600) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func run() { |     func run() { | ||||||
|  |         run { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func run(callback: () -> ()) { | ||||||
|         let opcodes = OPCODES_MAP |         let opcodes = OPCODES_MAP | ||||||
|  |  | ||||||
|         while true { |         while true { | ||||||
|  |             callback() | ||||||
|             let code = memRead(programCounter) |             let code = memRead(programCounter) | ||||||
|             programCounter += 1 |             programCounter += 1 | ||||||
|  |  | ||||||
| @@ -123,11 +124,151 @@ class CPU { | |||||||
|             /// STA |             /// STA | ||||||
|             case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: |             case 0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91: | ||||||
|                 sta(opcode.mode) |                 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 |             /// TAX | ||||||
|             case 0xAA: |             case 0xaa: | ||||||
|                 tax() |                 tax() | ||||||
|             /// INX |             /// INX | ||||||
|             case 0xE8: |             case 0xe8: | ||||||
|                 inx() |                 inx() | ||||||
|             /// BRK |             /// BRK | ||||||
|             case 0x00: |             case 0x00: | ||||||
| @@ -143,40 +284,136 @@ class CPU { | |||||||
|  |  | ||||||
|     func updateZeroAndNegativeFlags(_ result: UInt8) { |     func updateZeroAndNegativeFlags(_ result: UInt8) { | ||||||
|         if result == 0 { |         if result == 0 { | ||||||
|             status = status | 0b0000_0010 |             status.insert(.zero) | ||||||
|         } else { |         } else { | ||||||
|             status = status & 0b1111_1101 |             status.remove(.zero) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if result & 0b1000_0000 != 0 { |         if result & 0b1000_0000 != 0 { | ||||||
|             status = status | 0b1000_0000 |             status.insert(.negative) | ||||||
|         } else { |         } else { | ||||||
|             status = status & 0b0111_1111 |             status.remove(.negative) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func lda(_ mode: AddressingMode) { |     func setRegisterA(_ value: UInt8) { | ||||||
|         let addr = getOpperandAddress(mode) |  | ||||||
|         let value = memRead(addr) |  | ||||||
|  |  | ||||||
|         register_a = value |         register_a = value | ||||||
|         updateZeroAndNegativeFlags(register_a) |         updateZeroAndNegativeFlags(register_a) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func tax() { |     func setCarryFlag() { | ||||||
|         register_x = register_a |         status.insert(.carry) | ||||||
|         updateZeroAndNegativeFlags(register_x) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func inx() { |     func clearCarryFlag() { | ||||||
|         register_x = register_x &+ 1 |         status.remove(.carry) | ||||||
|         updateZeroAndNegativeFlags(register_x) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func sta(_ mode: AddressingMode) { |     /// note: ignoring decimal mode | ||||||
|  |     /// http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html | ||||||
|  |     func addToRegisterA(_ data: UInt8) { | ||||||
|  |         let shouldCarry = status.contains(.carry) ? 1 : 0 | ||||||
|  |         let sum = UInt16(register_a) + UInt16(data) + UInt16(shouldCarry) | ||||||
|  |  | ||||||
|  |         let carry = sum > 0xff | ||||||
|  |  | ||||||
|  |         if carry { | ||||||
|  |             status.insert(.carry) | ||||||
|  |         } else { | ||||||
|  |             status.remove(.carry) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let result = UInt8(sum) | ||||||
|  |  | ||||||
|  |         if (data ^ result) & (result ^ register_a) & 0x80 != 0 { | ||||||
|  |             status.insert(.overflow) | ||||||
|  |         } else { | ||||||
|  |             status.remove(.overflow) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         setRegisterA(result) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func stackPop() -> UInt8 { | ||||||
|  |         stackPointer = stackPointer &+ 1 | ||||||
|  |         return memRead(STACK + UInt16(stackPointer)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func stackPush(_ data: UInt8) { | ||||||
|  |         memWrite(STACK + UInt16(stackPointer), data: data) | ||||||
|  |         stackPointer = stackPointer &- 1 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func stackPopU16() -> UInt16 { | ||||||
|  |         let lo = UInt16(stackPop()) | ||||||
|  |         let hi = UInt16(stackPop()) | ||||||
|  |  | ||||||
|  |         return hi << 8 | lo | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func stackPushU16(_ data: UInt16) { | ||||||
|  |         let hi = UInt8(data >> 8) | ||||||
|  |         let lo = UInt8(data & 0xff) | ||||||
|  |         stackPush(hi) | ||||||
|  |         stackPush(lo) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func compare(mode: AddressingMode, compare_with: UInt8) { | ||||||
|         let addr = getOpperandAddress(mode) |         let addr = getOpperandAddress(mode) | ||||||
|         memWrite(addr: addr, data: register_a) |         let data = memRead(addr) | ||||||
|  |         if data <= compare_with { | ||||||
|  |             status.insert(.carry) | ||||||
|  |         } else { | ||||||
|  |             status.remove(.carry) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         updateZeroAndNegativeFlags(compare_with &- data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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)) | ||||||
|  |  | ||||||
|  |             programCounter = jump_addr | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | extension CPU: Memory { | ||||||
|  |     func memRead(_ addr: UInt16) -> UInt8 { | ||||||
|  |         memory[Int(addr)] | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func memWrite(_ addr: UInt16, data: UInt8) { | ||||||
|  |         memory[Int(addr)] = data | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | protocol Memory { | ||||||
|  |     func memRead(_ addr: UInt16) -> UInt8 | ||||||
|  |  | ||||||
|  |     func memWrite(_ addr: UInt16, data: UInt8) | ||||||
|  |  | ||||||
|  |     func memReadU16(_ addr: UInt16) -> UInt16 | ||||||
|  |  | ||||||
|  |     func memWriteU16(_ addr: UInt16, data: UInt16) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | extension Memory { | ||||||
|  |     func memReadU16(_ addr: UInt16) -> UInt16 { | ||||||
|  |         let lo = UInt16(memRead(addr)) | ||||||
|  |         let hi = UInt16(memRead(addr + 1)) | ||||||
|  |         return (hi << 8) | lo | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func memWriteU16(_ addr: UInt16, data: UInt16) { | ||||||
|  |         let hi = UInt8(data >> 8) | ||||||
|  |         let lo = UInt8(data & 0xff) | ||||||
|  |         self.memWrite(addr, data: lo) | ||||||
|  |         self.memWrite(addr + 1, data: hi) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										262
									
								
								Sources/CPU_Instructions.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								Sources/CPU_Instructions.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | |||||||
|  | extension CPU { | ||||||
|  |     func lda(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let value = memRead(addr) | ||||||
|  |  | ||||||
|  |         setRegisterA(value) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func ldy(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         register_y = data | ||||||
|  |         updateZeroAndNegativeFlags(register_y) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func ldx(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         register_x = data | ||||||
|  |         updateZeroAndNegativeFlags(register_x) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func sta(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         memWrite(addr, data: register_a) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func and(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         self.setRegisterA(data & register_a) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func eor(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         setRegisterA(data ^ register_a) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func ora(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         setRegisterA(data | register_a) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func tax() { | ||||||
|  |         register_x = register_a | ||||||
|  |         updateZeroAndNegativeFlags(register_x) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func inx() { | ||||||
|  |         register_x = register_x &+ 1 | ||||||
|  |         updateZeroAndNegativeFlags(register_x) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func iny() { | ||||||
|  |         register_y = register_y &+ 1 | ||||||
|  |         updateZeroAndNegativeFlags(register_y) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func sbc(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         let res = Int8(data) &* -1 | ||||||
|  |         addToRegisterA(UInt8(res &- 1)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func adc(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         addToRegisterA(data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func aslAccumulator() { | ||||||
|  |         var data = register_a | ||||||
|  |         if data >> 7 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data << 1 | ||||||
|  |         setRegisterA(data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func asl(_ mode: AddressingMode) -> UInt8 { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         var data = memRead(addr) | ||||||
|  |         if data >> 7 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data << 1 | ||||||
|  |         memWrite(addr, data: data) | ||||||
|  |         updateZeroAndNegativeFlags(data) | ||||||
|  |         return data | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func lsrAccumulator() { | ||||||
|  |         var data = register_a | ||||||
|  |         if data & 1 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data >> 1 | ||||||
|  |         setRegisterA(data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func lsr(_ mode: AddressingMode) -> UInt8 { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         var data = memRead(addr) | ||||||
|  |         if data & 1 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data >> 1 | ||||||
|  |         memWrite(addr, data: data) | ||||||
|  |         updateZeroAndNegativeFlags(data) | ||||||
|  |         return data | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func rolAccumulator() { | ||||||
|  |         var data = register_a | ||||||
|  |         let oldCarry = status.contains(.carry) | ||||||
|  |  | ||||||
|  |         if data >> 7 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data << 1 | ||||||
|  |         if oldCarry { | ||||||
|  |             data = data | 1 | ||||||
|  |         } | ||||||
|  |         setRegisterA(data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func rol(_ mode: AddressingMode) -> UInt8 { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         var data = memRead(addr) | ||||||
|  |         let oldCarry = status.contains(.carry) | ||||||
|  |  | ||||||
|  |         if data >> 7 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         data = data << 1 | ||||||
|  |         if oldCarry { | ||||||
|  |             data = data | 1 | ||||||
|  |         } | ||||||
|  |         memWrite(addr, data: data) | ||||||
|  |         updateZeroAndNegativeFlags(data) | ||||||
|  |         return data | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func rorAccumulator() { | ||||||
|  |         var data = register_a | ||||||
|  |         let oldCarry = status.contains(.carry) | ||||||
|  |  | ||||||
|  |         if data & 1 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data >> 1 | ||||||
|  |         if oldCarry { | ||||||
|  |             data = data | 0b10000000 | ||||||
|  |         } | ||||||
|  |         setRegisterA(data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func ror(_ mode: AddressingMode) -> UInt8 { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         var data = memRead(addr) | ||||||
|  |         let oldCarry = status.contains(.carry) | ||||||
|  |         if data & 1 == 1 { | ||||||
|  |             setCarryFlag() | ||||||
|  |         } else { | ||||||
|  |             clearCarryFlag() | ||||||
|  |         } | ||||||
|  |         data = data >> 1 | ||||||
|  |         if oldCarry { | ||||||
|  |             data = data | 0b10000000 | ||||||
|  |         } | ||||||
|  |         memWrite(addr, data: data) | ||||||
|  |         updateZeroAndNegativeFlags(data) | ||||||
|  |         return data | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func inc(_ mode: AddressingMode) -> UInt8 { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         var data = memRead(addr) | ||||||
|  |         data = data &+ 1 | ||||||
|  |         memWrite(addr, data: data) | ||||||
|  |         updateZeroAndNegativeFlags(data) | ||||||
|  |         return data | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func dey() { | ||||||
|  |         register_y = register_y &- 1 | ||||||
|  |         updateZeroAndNegativeFlags(register_y) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func dex() { | ||||||
|  |         register_x = register_x &- 1 | ||||||
|  |         updateZeroAndNegativeFlags(register_x) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func dec(_ mode: AddressingMode) -> UInt8 { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         var data = memRead(addr) | ||||||
|  |         data = data &- 1 | ||||||
|  |         memWrite(addr, data: data) | ||||||
|  |         updateZeroAndNegativeFlags(data) | ||||||
|  |         return data | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func pla() { | ||||||
|  |         let data = stackPop() | ||||||
|  |         setRegisterA(data) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func plp() { | ||||||
|  |         status.rawValue = stackPop() | ||||||
|  |         status.remove(.break1) | ||||||
|  |         status.remove(.break2) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func php() { | ||||||
|  |         var flags = status | ||||||
|  |         flags.insert(.break1) | ||||||
|  |         flags.insert(.break2) | ||||||
|  |         stackPush(flags.rawValue) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     func bit(_ mode: AddressingMode) { | ||||||
|  |         let addr = getOpperandAddress(mode) | ||||||
|  |         let data = memRead(addr) | ||||||
|  |         let and = register_a & data | ||||||
|  |         if and == 0 { | ||||||
|  |             status.insert(.zero) | ||||||
|  |         } else { | ||||||
|  |             status.remove(.zero) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if data & 0b10000000 > 0 { | ||||||
|  |             status.insert(.negative) | ||||||
|  |         } else { | ||||||
|  |             status.remove(.negative) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if data & 0b01000000 > 0 { | ||||||
|  |             status.insert(.overflow) | ||||||
|  |         } else { | ||||||
|  |             status.remove(.overflow) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										0
									
								
								Sources/Util.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								Sources/Util.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -1,7 +1,59 @@ | |||||||
| // The Swift Programming Language | // The Swift Programming Language | ||||||
| // https://docs.swift.org/swift-book | // https://docs.swift.org/swift-book | ||||||
|  |  | ||||||
| let cpu = CPU() | import SDL | ||||||
| let program: [UInt8] = [0xa9, 0xc0, 0xaa, 0xe8, 0x00] |  | ||||||
| cpu.loadAndRun(program) | guard SDL_Init(SDL_INIT_VIDEO) == 0 else { | ||||||
| print(cpu.status) |     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) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -8,9 +8,133 @@ struct OpCode { | |||||||
|  |  | ||||||
| let CPU_OP_CODES: [OpCode] = [ | let CPU_OP_CODES: [OpCode] = [ | ||||||
|     OpCode(code: 0x00, mnemonic: "BRK", len: 1, cycles: 7, mode: .NoneAddressing), |     OpCode(code: 0x00, mnemonic: "BRK", len: 1, cycles: 7, mode: .NoneAddressing), | ||||||
|     OpCode(code: 0xaa, mnemonic: "TAX", len: 1, cycles: 2, mode: .NoneAddressing), |     OpCode(code: 0xea, mnemonic: "NOP", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|     OpCode(code: 0xe8, mnemonic: "INX", len: 1, cycles: 2, mode: .NoneAddressing), |  | ||||||
|  |  | ||||||
|  |     /// Arithmetic | ||||||
|  |     OpCode(code: 0x69, mnemonic: "ADC", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0x65, mnemonic: "ADC", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x75, mnemonic: "ADC", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x6d, mnemonic: "ADC", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x7d, mnemonic: "ADC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0x79, mnemonic: "ADC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0x61, mnemonic: "ADC", len: 2, cycles: 5, mode: .Indirect_X), | ||||||
|  |     OpCode(code: 0x71, mnemonic: "ADC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xe9, mnemonic: "SBC", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0xe5, mnemonic: "SBC", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xf5, mnemonic: "SBC", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0xed, mnemonic: "SBC", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0xfd, mnemonic: "SBC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0xf9, mnemonic: "SBC", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0xe1, mnemonic: "SBC", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|  |     OpCode(code: 0xf1, mnemonic: "SBC", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x29, mnemonic: "AND", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0x25, mnemonic: "AND", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x35, mnemonic: "AND", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x2d, mnemonic: "AND", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x3d, mnemonic: "AND", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0x39, mnemonic: "AND", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0x21, mnemonic: "AND", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|  |     OpCode(code: 0x31, mnemonic: "AND", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x49, mnemonic: "EOR", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0x45, mnemonic: "EOR", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x55, mnemonic: "EOR", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x4d, mnemonic: "EOR", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x5d, mnemonic: "EOR", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0x59, mnemonic: "EOR", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0x41, mnemonic: "EOR", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|  |     OpCode(code: 0x51, mnemonic: "EOR", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x09, mnemonic: "ORA", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0x05, mnemonic: "ORA", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x15, mnemonic: "ORA", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x0d, mnemonic: "ORA", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x1d, mnemonic: "ORA", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0x19, mnemonic: "ORA", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0x01, mnemonic: "ORA", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|  |     OpCode(code: 0x11, mnemonic: "ORA", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     /// Shifts | ||||||
|  |     OpCode(code: 0x0a, mnemonic: "ASL", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x06, mnemonic: "ASL", len: 2, cycles: 5, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x16, mnemonic: "ASL", len: 2, cycles: 6, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x0e, mnemonic: "ASL", len: 3, cycles: 6, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x1e, mnemonic: "ASL", len: 3, cycles: 7, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x4a, mnemonic: "LSR", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x46, mnemonic: "LSR", len: 2, cycles: 5, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x56, mnemonic: "LSR", len: 2, cycles: 6, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x4e, mnemonic: "LSR", len: 3, cycles: 6, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x5e, mnemonic: "LSR", len: 3, cycles: 7, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x2a, mnemonic: "ROL", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x26, mnemonic: "ROL", len: 2, cycles: 5, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x36, mnemonic: "ROL", len: 2, cycles: 6, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x2e, mnemonic: "ROL", len: 3, cycles: 6, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x3e, mnemonic: "ROL", len: 3, cycles: 7, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x6a, mnemonic: "ROR", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x66, mnemonic: "ROR", len: 2, cycles: 5, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x76, mnemonic: "ROR", len: 2, cycles: 6, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0x6e, mnemonic: "ROR", len: 3, cycles: 6, mode: .Absolute), | ||||||
|  |     OpCode(code: 0x7e, mnemonic: "ROR", len: 3, cycles: 7, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xe6, mnemonic: "INC", len: 2, cycles: 5, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xf6, mnemonic: "INC", len: 2, cycles: 6, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0xee, mnemonic: "INC", len: 3, cycles: 6, mode: .Absolute), | ||||||
|  |     OpCode(code: 0xfe, mnemonic: "INC", len: 3, cycles: 7, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xe8, mnemonic: "INX", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xc8, mnemonic: "INY", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xc6, mnemonic: "DEC", len: 2, cycles: 5, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xd6, mnemonic: "DEC", len: 2, cycles: 6, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0xce, mnemonic: "DEC", len: 3, cycles: 6, mode: .Absolute), | ||||||
|  |     OpCode(code: 0xde, mnemonic: "DEC", len: 3, cycles: 7, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xca, mnemonic: "DEX", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x88, mnemonic: "DEY", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xc9, mnemonic: "CMP", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0xc5, mnemonic: "CMP", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xd5, mnemonic: "CMP", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0xcd, mnemonic: "CMP", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0xdd, mnemonic: "CMP", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0xd9, mnemonic: "CMP", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0xc1, mnemonic: "CMP", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|  |     OpCode(code: 0xd1, mnemonic: "CMP", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xc0, mnemonic: "CPY", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0xc4, mnemonic: "CPY", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xcc, mnemonic: "CPY", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xe0, mnemonic: "CPX", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0xe4, mnemonic: "CPX", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xec, mnemonic: "CPX", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |  | ||||||
|  |     /// Branching | ||||||
|  |     OpCode(code: 0x4c, mnemonic: "JMP", len: 3, cycles: 3, mode: .NoneAddressing), // AddressingMode that acts as Immidiate | ||||||
|  |     OpCode(code: 0x6c, mnemonic: "JMP", len: 3, cycles: 5, mode: .NoneAddressing), // AddressingMode.Indirect with 6502 bug | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x20, mnemonic: "JSR", len: 3, cycles: 6, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x60, mnemonic: "RTS", len: 1, cycles: 6, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x40, mnemonic: "RTI", len: 1, cycles: 6, mode: .NoneAddressing), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xd0, mnemonic: "BNE", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x70, mnemonic: "BVS", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x50, mnemonic: "BVC", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x30, mnemonic: "BMI", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xf0, mnemonic: "BEQ", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xb0, mnemonic: "BCS", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x90, mnemonic: "BCC", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x10, mnemonic: "BPL", len: 2, cycles: 2 /* +1 if branch succeeds +2 of to a new page */ , mode: .NoneAddressing), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x24, mnemonic: "BIT", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x2c, mnemonic: "BIT", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |  | ||||||
|  |     /// Stores, Loads | ||||||
|     OpCode(code: 0xa9, mnemonic: "LDA", len: 2, cycles: 2, mode: .Immediate), |     OpCode(code: 0xa9, mnemonic: "LDA", len: 2, cycles: 2, mode: .Immediate), | ||||||
|     OpCode(code: 0xa5, mnemonic: "LDA", len: 2, cycles: 3, mode: .ZeroPage), |     OpCode(code: 0xa5, mnemonic: "LDA", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|     OpCode(code: 0xb5, mnemonic: "LDA", len: 2, cycles: 4, mode: .ZeroPage_X), |     OpCode(code: 0xb5, mnemonic: "LDA", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
| @@ -20,18 +144,61 @@ let CPU_OP_CODES: [OpCode] = [ | |||||||
|     OpCode(code: 0xa1, mnemonic: "LDA", len: 2, cycles: 6, mode: .Indirect_X), |     OpCode(code: 0xa1, mnemonic: "LDA", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|     OpCode(code: 0xb1, mnemonic: "LDA", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), |     OpCode(code: 0xb1, mnemonic: "LDA", len: 2, cycles: 5 /* +1 if page crossed */, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|     OpCode(code: 0xa5, mnemonic: "STA", len: 2, cycles: 3, mode: .ZeroPage), |     OpCode(code: 0xa2, mnemonic: "LDX", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0xa6, mnemonic: "LDX", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xb6, mnemonic: "LDX", len: 2, cycles: 4, mode: .ZeroPage_Y), | ||||||
|  |     OpCode(code: 0xae, mnemonic: "LDX", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0xbe, mnemonic: "LDX", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xa0, mnemonic: "LDY", len: 2, cycles: 2, mode: .Immediate), | ||||||
|  |     OpCode(code: 0xa4, mnemonic: "LDY", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0xb4, mnemonic: "LDY", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|  |     OpCode(code: 0xac, mnemonic: "LDY", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |     OpCode(code: 0xbc, mnemonic: "LDY", len: 3, cycles: 4 /* +1 if page crossed */, mode: .Absolute_X), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x85, mnemonic: "STA", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|     OpCode(code: 0x95, mnemonic: "STA", len: 2, cycles: 4, mode: .ZeroPage_X), |     OpCode(code: 0x95, mnemonic: "STA", len: 2, cycles: 4, mode: .ZeroPage_X), | ||||||
|     OpCode(code: 0x8d, mnemonic: "STA", len: 3, cycles: 4, mode: .Absolute), |     OpCode(code: 0x8d, mnemonic: "STA", len: 3, cycles: 4, mode: .Absolute), | ||||||
|     OpCode(code: 0x9d, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_X), |     OpCode(code: 0x9d, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_X), | ||||||
|     OpCode(code: 0x99, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_Y), |     OpCode(code: 0x99, mnemonic: "STA", len: 3, cycles: 5, mode: .Absolute_Y), | ||||||
|     OpCode(code: 0x81, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_X), |     OpCode(code: 0x81, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_X), | ||||||
|     OpCode(code: 0x91, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_Y), |     OpCode(code: 0x91, mnemonic: "STA", len: 2, cycles: 6, mode: .Indirect_Y), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x86, mnemonic: "STX", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x96, mnemonic: "STX", len: 2, cycles: 4, mode: .Absolute_Y), | ||||||
|  |     OpCode(code: 0x8e, mnemonic: "STX", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0x84, mnemonic: "STY", len: 2, cycles: 3, mode: .ZeroPage), | ||||||
|  |     OpCode(code: 0x94, mnemonic: "STY", len: 2, cycles: 4, mode: .Absolute_X), | ||||||
|  |     OpCode(code: 0x8c, mnemonic: "STY", len: 3, cycles: 4, mode: .Absolute), | ||||||
|  |  | ||||||
|  |     /// Flag Clears | ||||||
|  |     OpCode(code: 0xd8, mnemonic: "CLD", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x58, mnemonic: "CLI", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xb8, mnemonic: "CLV", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x18, mnemonic: "CLC", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x38, mnemonic: "SEC", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x78, mnemonic: "SEI", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xf8, mnemonic: "SED", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |  | ||||||
|  |     OpCode(code: 0xaa, mnemonic: "TAX", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xa8, mnemonic: "TAY", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0xba, mnemonic: "TSX", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x8a, mnemonic: "TXA", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x9a, mnemonic: "TXS", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x98, mnemonic: "TYA", len: 1, cycles: 2, mode: .NoneAddressing), | ||||||
|  |  | ||||||
|  |     /// Stack | ||||||
|  |     OpCode(code: 0x48, mnemonic: "PHA", len: 1, cycles: 3, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x68, mnemonic: "PLA", len: 1, cycles: 4, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x08, mnemonic: "PHP", len: 1, cycles: 3, mode: .NoneAddressing), | ||||||
|  |     OpCode(code: 0x28, mnemonic: "PLP", len: 1, cycles: 4, mode: .NoneAddressing), | ||||||
| ] | ] | ||||||
|  |  | ||||||
| let OPCODES_MAP: [UInt8: OpCode] = { | let OPCODES_MAP: [UInt8: OpCode] = { | ||||||
|     var map: [UInt8:OpCode] = [:] |     var map: [UInt8:OpCode] = [:] | ||||||
|     for cpuop in CPU_OP_CODES { |     for cpuop in CPU_OP_CODES { | ||||||
|  |         guard map[cpuop.code] == nil else {fatalError("Duplicate opcode \(cpuop) and \(map[cpuop.code]!)")} | ||||||
|         map[cpuop.code] = cpuop |         map[cpuop.code] = cpuop | ||||||
|     } |     } | ||||||
|     return map |     return map | ||||||
|   | |||||||
| @@ -7,13 +7,13 @@ class CPUTests: XCTestCase { | |||||||
|         cpu.loadAndRun([0xa9, 0x05, 0x00]) |         cpu.loadAndRun([0xa9, 0x05, 0x00]) | ||||||
|  |  | ||||||
|         XCTAssertEqual(cpu.register_a, 0x05) |         XCTAssertEqual(cpu.register_a, 0x05) | ||||||
|         XCTAssert(cpu.status & 0b0000_0010 == 0b00) |         XCTAssert(cpu.status.rawValue & 0b0000_0010 == 0b00) | ||||||
|         XCTAssert(cpu.status & 0b1000_0000 == 0) |         XCTAssert(cpu.status.rawValue & 0b1000_0000 == 0) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func test_lda_from_memory() { |     func test_lda_from_memory() { | ||||||
|         let cpu = CPU() |         let cpu = CPU() | ||||||
|         cpu.memWrite(addr: 0x10, data: 0x55) |         cpu.memWrite(0x10, data: 0x55) | ||||||
|  |  | ||||||
|         cpu.loadAndRun([0xa5, 0x10, 0x00]) |         cpu.loadAndRun([0xa5, 0x10, 0x00]) | ||||||
|  |  | ||||||
| @@ -23,13 +23,13 @@ class CPUTests: XCTestCase { | |||||||
|     func test_0xa9_lda_zero_flag() { |     func test_0xa9_lda_zero_flag() { | ||||||
|         let cpu = CPU() |         let cpu = CPU() | ||||||
|         cpu.loadAndRun([0xa9, 0x00, 0x00]) |         cpu.loadAndRun([0xa9, 0x00, 0x00]) | ||||||
|         XCTAssert(cpu.status & 0b0000_0010 == 0b10) |         XCTAssert(cpu.status.rawValue & 0b0000_0010 == 0b10) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func test_0xa9_lda_neg_flag() { |     func test_0xa9_lda_neg_flag() { | ||||||
|         let cpu = CPU() |         let cpu = CPU() | ||||||
|         cpu.loadAndRun([0xa9, 0xFF, 0x00]) |         cpu.loadAndRun([0xa9, 0xFF, 0x00]) | ||||||
|         XCTAssert(cpu.status & 0b1000_0000 == 0b1000_0000) |         XCTAssert(cpu.status.rawValue & 0b1000_0000 == 0b1000_0000) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     func test_0xaa_tax_move_a_to_x() { |     func test_0xaa_tax_move_a_to_x() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user