|
| 1 | +# Windfish CPU |
| 2 | + |
| 3 | +At its core, Windfish defines a protocol-based representation of an abstract CPU's instruction set that supports bi-directional coding between binary and semantic representations of the instructions. |
| 4 | + |
| 5 | +### Defining the shape of an instruction |
| 6 | + |
| 7 | +Implementing a CPU starts by implementing the `InstructionSpec` protocol, typically as an enum. The `InstructionSpec` type defines both the potential shape of an instruction and the assembly language names of the instructions. In the example below, we define an instruction specification that is able to represent four different types of operations, most accepting one or more operands: |
| 8 | + |
| 9 | +```swift |
| 10 | +enum Spec: CPU.InstructionSpec { |
| 11 | + typealias WidthType = UInt16 |
| 12 | + |
| 13 | + case nop |
| 14 | + case cp(Numeric) |
| 15 | + case ld(Numeric, Numeric) |
| 16 | + case call(Condition? = nil, Numeric) |
| 17 | + |
| 18 | + enum Numeric: Hashable { |
| 19 | + case imm8 |
| 20 | + case imm16 |
| 21 | + case a |
| 22 | + case arg(Int) |
| 23 | + } |
| 24 | + |
| 25 | + enum Condition: Hashable { |
| 26 | + case nz |
| 27 | + case z |
| 28 | + } |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +This specification allows us to represent any of the following instructions: |
| 33 | + |
| 34 | +```swift |
| 35 | +.nop |
| 36 | +.cp(.a) |
| 37 | +.cp(.imm8) |
| 38 | +.ld(.a, .imm8) |
| 39 | +.call(nil, .imm16) |
| 40 | +.call(.z, .imm16) |
| 41 | +``` |
| 42 | + |
| 43 | +### Defining an instruction set |
| 44 | + |
| 45 | +Instruction specifications can be used to create an instruction table where each index maps to the binary value of a corresponding instruction specification: |
| 46 | + |
| 47 | +```swift |
| 48 | +static let table: [Instruction.Spec] = [ |
| 49 | + /* 0x00 */ .nop, |
| 50 | + /* 0x01 */ .ld(.a, .imm8), |
| 51 | + /* 0x02 */ .ld(.a, .imm16), |
| 52 | + /* 0x03 */ .call(.nz, .imm16), |
| 53 | + /* 0x04 */ .call(nil, .imm16), |
| 54 | +] |
| 55 | +``` |
| 56 | + |
| 57 | +This table is then stored within an `InstructionSet`: |
| 58 | + |
| 59 | +```swift |
| 60 | +struct InstructionSet: CPU.InstructionSet { |
| 61 | + typealias InstructionType = Instruction |
| 62 | + |
| 63 | + static let table: [Instruction.Spec] = [ |
| 64 | + /* 0x00 */ .nop, |
| 65 | + /* 0x01 */ .ld(.a, .imm8), |
| 66 | + /* 0x02 */ .ld(.a, .imm16), |
| 67 | + /* 0x03 */ .call(.nz, .imm16), |
| 68 | + /* 0x04 */ .call(nil, .imm16), |
| 69 | + ] |
| 70 | + static var prefixTables: [Instruction.Spec: [Instruction.Spec]] = [] |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +The `InstructionSet` type defines common methods for working with an instruction set including the ability to compute the width of an instruction and converting the instruction's opcode to byte and assembly representations. |
| 75 | + |
| 76 | +```swift |
| 77 | +struct InstructionSet: CPU.InstructionSet { |
| 78 | + // ... |
| 79 | + |
| 80 | + static var widths: [Instruction.Spec : InstructionWidth<UInt16>] = { |
| 81 | + return computeAllWidths() |
| 82 | + }() |
| 83 | + |
| 84 | + static var opcodeBytes: [Instruction.Spec : [UInt8]] = { |
| 85 | + return computeAllOpcodeBytes() |
| 86 | + }() |
| 87 | + |
| 88 | + static var opcodeStrings: [Instruction.Spec : String] = { |
| 89 | + return computeAllOpcodeStrings() |
| 90 | + }() |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +### Defining an instruction |
| 95 | + |
| 96 | +Instruction specifications form the basis by which real instructions are represented. A real instruction consists of both an instruction specification and any optional immediate values associated with the instruction. |
| 97 | + |
| 98 | +```swift |
| 99 | +struct Instruction: CPU.Instruction { |
| 100 | + let spec: Spec |
| 101 | + let immediate: ImmediateValue? |
| 102 | + |
| 103 | + enum ImmediateValue: CPU.InstructionImmediate { |
| 104 | + case imm8(UInt8) |
| 105 | + case imm16(UInt16) |
| 106 | + } |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +For example, `.ld(.a, .imm8)` would be initialized with a 1-byte `.imm8` value. The initialization of an instruction like this would look like so: |
| 111 | + |
| 112 | +```swift |
| 113 | +let instruction = Instruction(spec: .ld(.a, .imm8), immediate: .imm8(127)) |
| 114 | +``` |
0 commit comments