标签搜索

0b0010-抽象bit操作

limit
2021-03-22 / 0 评论 / 36 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年10月02日,已超过362天没有更新,若内容或图片失效,请留言反馈。

提出问题

这趟没屁话。

接上文,我们发觉一桩事,修改状态寄存器的时候,我们不够抽象,譬如我想处理 Zero Flag,我需要通过位运算符去操作一个数,每次写二进制,很难受,现在来改得抽象一些

解决问题

我们发觉状态寄存器有 C、Z、I、D、B、U、V、N 这些状态,那就定义一个类型来描述它们。

状态描述

状态含义
Carry Flag进位标志,操作后导致结果的第 7 位溢出或者 0 位下溢,就设置
Zero Flag零标志,如果操作结果为零,就设置。
Interrupt Disable中断禁用标志,设置后 CPU 将不处理设备的中断,除非执行清除中断禁用。
Decimal Mode十进制模式,CPU 在进行加减时遵守二进制编码的十进制(BCD)算术规则。
BBRK 相关
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

评论 (0)

取消