提出问题
这趟没屁话。
接上文,我们发觉一桩事,修改状态寄存器的时候,我们不够抽象,譬如我想处理 Zero Flag,我需要通过位运算符去操作一个数,每次写二进制,很难受,现在来改得抽象一些
解决问题
我们发觉状态寄存器有 C、Z、I、D、B、U、V、N 这些状态,那就定义一个类型来描述它们。
状态描述
状态 | 含义 |
---|---|
Carry Flag | 进位标志,操作后导致结果的第 7 位溢出或者 0 位下溢,就设置 |
Zero Flag | 零标志,如果操作结果为零,就设置。 |
Interrupt Disable | 中断禁用标志,设置后 CPU 将不处理设备的中断,除非执行清除中断禁用。 |
Decimal Mode | 十进制模式,CPU 在进行加减时遵守二进制编码的十进制(BCD)算术规则。 |
B | BRK 相关 |
U | 中断相关,这个要配合 B 标志使用 |
Overflow Flag | 溢出标志,结果出现溢出时设置。 |
Negative Flag | 负标志,操作结果的第 7 位设置为 1,就要设置。 |
关于中断这里先不讲,直接讲概念没用,后面有实际场景来补充更好理解。
struct Flag: OptionSet {
static let C = Flag(rawValue: 0b0000_0001) // 1 << 0
static let Z = Flag(rawValue: 0b0000_0010) // 1 << 1
static let I = Flag(rawValue: 0b0000_0100) // 1 << 2
static let D = Flag(rawValue: 0b0000_1000) // 1 << 3
static let B = Flag(rawValue: 0b0001_0000) // 1 << 4
static let U = Flag(rawValue: 0b0010_0000) // 1 << 5
static let V = Flag(rawValue: 0b0100_0000) // 1 << 6
static let N = Flag(rawValue: 0b1000_0000) // 1 << 7
internal var rawValue: UInt8 = 0
init(rawValue flag: UInt8) {
rawValue = flag
}
func bits() -> UInt8 {
rawValue
}
}
这样我就把这些状态抽象出来了,如果不想用 rawValue 这个命名,可以把 OptionSet 去掉,反正对应着现在的内容写就可以,使用是一样的效果,OptionSet 是一个协议,用来约束我们的结构体。
然后我们要做得就是把行为抽象,根据我们目前已有的代码,我们需要两个函数,当然我也自作主张地添加了一个 set 方法用来处理分支的情况
extension Flag {
mutating func insert(other: Flag) {
rawValue |= other.bits()
}
mutating func remove(other: Flag) {
rawValue &= (~other.bits())
}
mutating func set(other: Flag, condition: Bool) {
if condition {
insert(other)
} else {
remove(other)
}
}
}
修改
我们得把 CPU 的 status 改成我们定义的 Flag 结构体
class CPU {
// ...
var status: Flag = Flag(rawValue: 0b0010_0100)
// ...
}
extension CPU {
func reset() {
// ...
status = Flag(rawValue: 0b0010_0100)
// ...
}
}
我们的仿真代码也要改一下
extension CPU {
func interpret(program: [UInt8]) {
pc = 0
while true {
let code = program[Int(pc)]
pc += 1
switch code {
// ...
case 0xa9:
let param = program[Int(pc)]
pc += 1
a = param
// 处理 Zero Flag
status.set(other: .Z, condition: a == 0)
// 处理 Negative Flag
status.set(other: .N, condition: a >> 7 == 1)
default:
break
}
}
}
}
测试
由于我们把实现修改嘞,所以我们还要把测试代码改一下,看看效果
func testSth() {
let cpu = CPU()
cpu.interpret(program: [0xa9, 0x00, 0x00])
assert(cpu.status.bits() & 0b0000_0010 == 0b10)
cpu.reset()
cpu.interpret(program: [0xa9, 0x05, 0x00])
assert(cpu.a == 0x05)
assert(cpu.status.bits() & 0b0000_0010 == 0)
assert(cpu.status.bits() & 0b1000_0000 == 0)
}
Good Job,现在只要一两行代码就把之前的操作涵盖了,后面仿真其他指令的时候就更容易嘞,心智负担不会太重。
评论 (0)