Swift 中的 16 位 logic/computer 模拟

16 bit logic/computer simulation in Swift

我正在尝试使用 Swift 对 16 位计算机进行基本模拟。计算机将具有

就是这样。我有足够的知识来直观地创建这些部件并了解它们的工作原理,但是在使用我当前的方法时,使用更多输入来制作更大的组件变得越来越困难。

我目前的方法是将每个组件包装在 struct 中。这在早期就奏效了,但在坚持计算机科学原理的同时管理多个输入变得越来越困难。

主要问题是组件未随时钟信号更新。当在输出变量 c 上调用 get 时,我有更新组件的输出。然而,这忽略了时钟信号的概念,并且可能会在以后引起更多问题。

也很难为每个变量制作 getter 和 setter 而不会出现有关可变性的错误。尽管我已经解决了这些错误,但它们很烦人并且会减慢开发过程。

最后一个大问题是更新输出。当输入改变时,输出不会更新;它会在被告知这样做时更新。这对真实计算机的质量不准确,是一个根本错误。

这是一个例子。就是我前面提到的ALU。它需要两个 16 位输入并输出 16 位。它有两个一元 ALU,可以使 16 位数字为零、取反或两者兼而有之。最后,它根据 f 标志添加或进行一些明智的比较,如果选择 no 标志则反转输出。

struct ALU {
    //Operations are done in the order listed. For example, if zx and nx are 1, it first makes input 1 zero and then inverts it.
    var x : [Int] //Input 1
    var y : [Int] //Input 2
    var zx : Int //Make input 1 zero
    var zy : Int //Make input 2 zero
    var nx : Int //Invert input 1
    var ny : Int //Invert input 2
    var f : Int //If 0, do a bitwise AND operation. If 1, add the inputs
    var no : Int //Invert the output
    public var c : [Int] { //Output
        get {
            //Numbers first go through unary ALUs. These can negate the input (and output the value), return 0, or return the inverse of 0. They then undergo the operation specified by f, either addition or a bitwise and operation, and are negated if n is 1.
            var ux = UnaryALU(z: zx, n: nx, x: x).c //Unary ALU. See comments for more
            var uy = UnaryALU(z: zy, n: ny, x: y).c 
            var fd = select16(s: f, d1: Add16(a: ux, b: uy).c, d0: and16(a: ux, b: uy).c).c //Adds a 16 bit number or does a bitwise and operation. For more on select16, see the line below.
            var out = select16(s: no, d1: not16(a: fd).c, d0: fd).c //Selects a number. If s is 1, it returns d1. If s is 0, it returns d0. d0 is the value returned by fd, while d1 is the inverse.
            return out
        }
    }
    public init(x:[Int],y:[Int],zx:Int,zy:Int,nx:Int,ny:Int,f:Int,no:Int) {
        self.x = x
        self.y = y
        self.zx = zx
        self.zy = zy
        self.nx = nx
        self.ny = ny
        self.f = f
        self.no = no
    }
}

我使用 c 作为输出变量,在 Int 数组中存储多位值,并在 Int 值中存储单个位。

我在 Swift Playgrounds 3.0 和 Swift 5.0 第 6 代 iPad 上执行此操作。我将每个组件或组件集存储在模块中的单独文件中,这就是为什么一些变量和所有 structs 都标记为 public 的原因。我将不胜感激任何帮助。提前致谢。

所以,我完全重做了我的方法,并找到了绕过我面临的问题的方法。我所做的是为每个输入制作我称之为“跟踪器变量”的东西。当为每个变量调用 get 时,它 returns 分配给它的跟踪器值。当调用 set 时,它会调用更新电路输出的 update() 函数。它还更新跟踪器的值。这实质上创建了每个变量的“副本”。我这样做是为了防止任何无限循环。

不幸的是,这里需要追踪器。我会证明为什么

var variable : Type {
    get {
        return variable //Calls the getter again, resulting in an infinite loop
    }
    set {
        //Do something
    }
}

为了制作 setter,Swift 也需要制作 getter。在此示例中,调用 variable 只是再次调用 get,导致对 get 的永无止境的级联调用。跟踪器变量是一种使用最少额外代码的变通方法。

使用更新方法确保输出响应任何输入的变化。由于组件本身的架构,这也适用于时钟信号。虽然它看起来像时钟,但实际上并没有。

例如,在数据触发器中,时钟信号被传递到门中。时钟信号所做的只是在信号关闭时停用组件。因此,我可以在 update() 内实现它,同时保持对现实的忠实。

这是一个半加器的例子。请注意,我提到的跟踪器变量在其名称前用下划线标记。它有两个输入,xy,每个都是 1 位。它还有两个输出,highlow,也称为进位和求和。输出也是一位。

struct halfAdder {
    private var _x : Bool //Tracker for x
    public var x: Bool { //Input 1
        get {
            return _x //Return the tracker’s value
        }
        set {
            _x = x //Set the tracker to x
            update() //Update the output
        }
    }
    private var _y : Bool //Tracker for y
    public var y: Bool { //Input 2
        get {
            return _y
        }
        set {
            _y = y
            update()
        }
    }
    public var high : Bool //High output, or ‘carry’
    public var low : Bool //Low output, or ‘sum’
    internal mutating func update(){ //Updates the output
        high = x && y //AND gate, sets the high output
        low = (x || y) && !(x && y) //XOR gate, sets the low output
    }
    public init(x:Bool, y:Bool){ //Initializer
        self.high = false //This will change when the variables are set, ensuring a correct output. 
        self.low = false //See above
        self._x = x //Setting trackers and variables
        self._y = y
        self.x = x
        self.y = y
    }
}

这是一个非常干净的方法,除了跟踪器,完成这个任务。它可以通过使用 Bool 的数组而不是单个值来简单地扩展以适应任意数量的位。它尊重时钟信号,在输入变化时更新输出,与真实计算机非常相似。