• he/him

Chronicling writing a simple NES emulator to learn Swift, macOS APIs, and retro architecture.


The NES has three major parts, the CPU, the Picture Processing Unit for generating graphics, and the Audio Processing Unit for sound. Let's start modelling the processor first, which is a slightly modified 6502 CPU.

So, let's start modelling the 6502 CPU in Swift. Its cost reduced design makes it extremely simple.

First, it has some registers. Only 5, plus flags:

struct RegisterSet {
    var a : UInt8 = 0 // Accumulator
    var x : UInt8 = 0 // Index X
    var y : UInt8 = 0 // Index Y
    
    var pc : UInt16 = 0 // Program Counter
    var sp : UInt8 = 0 // Stack Pointer
    var p : FlagSet = FlagSet() // Processor status / flags
}
syntax highlighting by codehost

And it has some six flags, plus one non physical-flag that only appears when the flags are pushed on the stack:

struct FlagSet {
    // Represent the flags of the processor
    var c = false // carry
    var z = false // zero
    var i = false // interrupt disable
    var d = false // decimal mode
    var b = true  // break, virtual flag, normally on, except during IRQ processing
    var v = false // signed overflow
    var n = false // negative
}

I've decided to model the flags as individual booleans for clarity of code. However, the processor occasionally needs to pack them as a byte when it stacks them for interrupt processing, or when the program uses the PHP or PLP opcodes to push or pull the flag register to/from the stack. Since the Swift standard library includes a type OptionSet for bitmasks, I'll try using that rather than writing the C style bitmasking myself. Let's make it a private nested type:

    // Values used for converting to and from a byte (ie, when putting on the stack)
    private struct FlagValue : OptionSet {
        // NV1B_DIZC
        let rawValue : UInt8
        
        static let c = FlagValue(rawValue: 0x01)
        static let z = FlagValue(rawValue: 0x02)
        static let i = FlagValue(rawValue: 0x04)
        static let d = FlagValue(rawValue: 0x08)
        static let b = FlagValue(rawValue: 0x10)
        static let always = FlagValue(rawValue: 0x20)
        static let v = FlagValue(rawValue: 0x40)
        static let n = FlagValue(rawValue: 0x80)
    }

The processor has a quirk that the unused bit of the bitmask is always pushed as a 1.
A quick function to serialize it to a byte:

    func toByte() -> UInt8 {
        var values = FlagValue.always
        if c { values.insert(.c) }
        if z { values.insert(.z) }
        if i { values.insert(.i) }
        if d { values.insert(.d) }
        if b { values.insert(.b) }
        if v { values.insert(.v) }
        if n { values.insert(.n) }
        return values.rawValue
    }

And the reverse, creating a new FlagSet from a byte:

    init(fromByte : UInt8) {
        let values = FlagValue(rawValue:fromByte)
        c = values.contains(.c)
        z = values.contains(.z)
        i = values.contains(.i)
        d = values.contains(.d)
        b = values.contains(.b)
        v = values.contains(.v)
        n = values.contains(.n)
    }
syntax highlighting by codehost

I'm trying not to worry too much about efficiency right now, but I was curious and did check that these contains and insert function calls of the OptionSet bitset can be inlined, though the assembly seemed overly complex. (Did you know Godbolt has Swift now?)

In future installments: modelling the memory space and opcodes.


You must log in to comment.