首頁 > 軟體

JS模擬實現序列加法器

2022-03-14 10:00:20

在重溫《編碼:隱匿在計算機軟硬體背後的語言》第12章——二進位制加法器時,心血來潮用JS寫了一個模擬序列加法器。

測試斷言工具TestUtils.js

function assertTrue(actual){
    if(!actual)
        throw "Error actual: " + actual + " is not true."
}

function assertFalse(actual){
    if(actual)
        throw "Error actual: " + actual + " is not false."
}

function assertIntEquals(expected, actual){
    if(typeof expected != "number")
        throw "Error expected: " + expected +" is not a number."

    if(typeof actual != "number")
        throw "Error actual: " + actual +" is not a number."

    if(expected != actual)
        throw "Error expected: " + expected + " and actual: " + actual +" is different."
}

十進位制數與二進位制數之間轉換工具utils.js

function int2LowEightBitArray(num){
    var result = []
    for(var i = 0; i < 8; i++)
        result.push(((num >> i) & 1) == 1)
    return result
}

function lowEightBitArray2Int(array){
    var result = 0
    for(var i = 0; i < 8; i++){
        if(array[i])
            result += (1 << i)
    }
    return result
}

function lowNineBitArray2Int(array){
    var result = 0
    for(var i = 0; i < 9; i++){
        if(array[i])
            result += (1 << i)
    }
    return result
}

//用二補數表示負數
function int2EightBitArray(num){
    if(num < -128 || num > 127){
        throw "Out of boundary(-128, 127)."
    }
    var result = []
    for(var i = 0; i < 8; i++)
        result.push(((num >> i) & 1) == 1)
    return result
}

function eightBitArray2Int(array){
    var result = 0
    for(var i = 0; i < 7; i++){
        if(array[i])
            result += (1 << i)
    }
    if(array[i])
        result += -128
    return result
}

function int2SixteenBitArray(num){
    if(num < -(1 << 15) || num > (1 << 15) - 1){
        throw "Out of boundary({left}, {right})."
            .replace("{left}", -(1 << 15))
            .replace("{right}", (1 << 15) - 1)
    }
    var result = []
    for(var i = 0; i < 16; i++)
        result.push(((num >> i) & 1) == 1)
    return result
}

function sixteentBitArray2Int(array){
    var result = 0
    for(var i = 0; i < 15; i++){
        if(array[i])
            result += (1 << i)
    }
    if(array[i])
        result += -(1 << 15)
    return result
}

加法器模型model.js

class DoubleInGate{
    #id
    #in1
    #in2
    #gateTypeName
    #func
    #out
    #lastOut
    #outReceiver
    #outReceiverInName

    constructor(id, gateTypeName, in1 = false, in2 = false, func){
        this.#id = id
        this.#gateTypeName = gateTypeName
        this.#in1 = in1
        this.#in2 = in2
        this.#func = func
        this.#out = this.#func(this.in1, this.in2)
        // this.#lastOut = this.#out
    }
    
    updateIn1(flag){
        this.#in1 = flag
        this.#updateOut()
    }
    
    updateIn2(flag){
        this.#in2 = flag
        this.#updateOut()
    }
    
    getOut(){
        return this.#out;
    }

    updateBothIn(in1 = false, in2 = false){
        this.#in1 = in1
        this.#in2 = in2
        this.#updateOut()
    }
    
    setOutReceiver(outReceiver, inName){
        this.#outReceiver = outReceiver
        this.#outReceiverInName = inName
    }

    #updateOut(){
        this.#lastOut = this.#out
        this.#out = this.#func(this.#in1, this.#in2)
        if(this.#out != this.#lastOut){
            this.#send(this.#out)
        }
    }

    #send(flag){
        if(this.#outReceiver){
            this.#outReceiver.receive(this.#outReceiverInName, flag)
        }
    }

    receive(inName, flag){
        if(inName == "in1"){
            this.updateIn1(flag)
        }else if(inName == "in2"){
            this.updateIn2(flag)
        }
    }

    toString(){
        return "{gateTypeName} id: {id}, in1: {in1}, in2: {in2}, out:{out}"
            .replace("{gateTypeName}", this.#gateTypeName)
            .replace("{id}", this.#id)
            .replace("{in1}", this.#in1)
            .replace("{in2}", this.#in2)
            .replace("{out}", this.#out)
    }
}

//與門
class AndGate extends DoubleInGate{
    constructor(id, in1 = false, in2 = false){
        super(id, "AndGate", in1, in2, (i1, i2) => i1 && i2)
    }
}

//或門
class OrGate extends DoubleInGate{
    constructor(id, in1 = false, in2 = false){
        super(id, "OrGate", in1, in2, (i1, i2) => i1 || i2)
    }
}

//互斥或門
class XorGate extends DoubleInGate{
    constructor(id, in1 = false, in2 = false){
        //^在js按位元互斥或, 而在java中不是
        super(id, "XorGate", in1, in2, (i1, i2) => (i1 || i2) && !(i1 && i2)) 
    }
}

class SingleInGate{
    #id
    #in
    #out
    #lastOut
    #func
    #outReceiver
    #outReceiverInName

    constructor(id, in_, func){
        this.#id = id
        this.#in = in_
        this.#func = func
        this.#out = this.#func(this.#in)
        // this.#lastOut = this.#out
    }

    updateIn(flag){
        this.#in = flag
        this.#updateOut()
    }

    getOut(){
        return this.#out
    }

    //註冊輸出埠接收者,(類觀察者模式)
    setOutReceiver(outReceiver, inName){
        this.#outReceiver = outReceiver
        this.#outReceiverInName = inName
    }

    #updateOut(){
        this.#lastOut = this.#out
        this.#out = this.#func(this.#in)
        if(this.#out != this.#lastOut){
            this.#send(this.#out)
        }
    }

    #send(flag){
        if(this.#outReceiver){
            this.#outReceiver.receive(this.#outReceiverInName, flag)
        }    
    }

    receive(inName, flag){
        if(inName == "in"){ //TODO 或許有更靈活的寫法
            this.updateIn(flag)
        }
    }
}

class NullGate extends SingleInGate{
    constructor(id, in_= false){
        super(id, in_, a=>a)
    }
}

class NotGate extends SingleInGate{
    constructor(id, in_= false){
        super(id, in_, a=>!a)
    }
}

class Adder{
    #id
    #inA
    #inB
    
    #outS
    #outC
    #lastOutS
    #lastOutC

    #outSReceiver
    #outSReceiverInName
    #outCReceiver
    #outCReceiverInName

    constructor(id, inA=false, inB=false, outS, outC){
        this.#id = id
        this.#inA = inA
        this.#inB = inB
        this.#outS = outS
        this.#outC = outC
    }

    updateInA(flag, func){
        this.#inA = flag
        this.updateOutSAndOutC(func)
    }

    updateInB(flag, func){
        this.#inB = flag
        this.updateOutSAndOutC(func)
    }

    updateBothIn(inA, inB, func){
        this.#inA = inA
        this.#inB = inB
        this.updateOutSAndOutC(func)
    }

    getOutS(){
        return this.#outS
    }

    getOutC(){
        return this.#outC
    }

    #updateOutS(flag){
        this.#lastOutS = this.#outS
        this.#outS = flag
        if(this.#outS != this.#lastOutS){
            this.#sendOutS(this.#outS)
        }
    }
    
    #updateOutC(flag){
        this.#lastOutC = this.#outC
        this.#outC = flag
        if(this.#outC != this.#lastOutC){
            this.#sendOutC(this.#outC)
        }
    }
    
    updateOutSAndOutC(func){
        if(func){
            var results = func()
            this.#updateOutS(results[0])
            this.#updateOutC(results[1])
        }
    }

    setOutSReceiver(outSReceiver, inName){
        this.#outSReceiver = outSReceiver
        this.#outSReceiverInName = inName
    }

    setOutCReceiver(outCReceiver, inName){
        this.#outCReceiver = outCReceiver
        this.#outCReceiverInName = inName
    }

    receive(inName, flag){
        if(inName == "inA"){
            this.updateInA(flag)
        }else if(inName == "inB"){
            this.updateInB(flag)
        }
    }

    #sendOutS(flag){
        if(this.#outSReceiver){
            this.#outSReceiver.receive(this.#outSReceiverInName, flag)
        }
    }

    #sendOutC(flag){
        if(this.#outCReceiver){
            this.#outCReceiver.receive(this.#outCReceiverInName, flag)
        }
    }
}

class HalfAdder extends Adder{
    #xorGate
    #andGate

    constructor(id, inA=false, inB=false){
        var xorGate = new XorGate(undefined, inA, inB);
        var andGate = new AndGate(undefined, inA, inB);
        super(id, inA, inB, xorGate.getOut(), andGate.getOut())
        this.#xorGate = xorGate
        this.#andGate = andGate 
    }

    #returnOutArray(){
        return [this.#xorGate.getOut(), this.#andGate.getOut()]
    }

    updateInA(flag){
        super.updateInA(flag, ()=>{
            this.#xorGate.updateIn1(flag)
            this.#andGate.updateIn1(flag)
            return this.#returnOutArray()
        })
    }

    updateInB(flag){
        super.updateInB(flag, ()=>{
            this.#xorGate.updateIn2(flag)
            this.#andGate.updateIn2(flag)
            return this.#returnOutArray()
        })
    }

    updateBothIn(inA, inB){
        super.updateBothIn(inA, inB, ()=>{
            this.#xorGate.updateBothIn(inA, inB)
            this.#andGate.updateBothIn(inA, inB)
            return this.#returnOutArray()
        })
    }

}

class FullAdder extends Adder{
    #inC
    #halfAdder1
    #halfAdder2
    #orGate

    constructor(id, inA = false, inB = false, inC = false){
        var halfAdder1 = new HalfAdder(undefined, inA, inB)
        var halfAdder2 = new HalfAdder(undefined, inC, halfAdder1.getOutS())
        var orGate = new OrGate(undefined, halfAdder1.getOutC(), halfAdder2.getOutC())
        super(id, inA, inB, halfAdder2.getOutS(), orGate.getOut())
        this.#inC = inC
        this.#halfAdder1 = halfAdder1
        this.#halfAdder2 = halfAdder2
        this.#orGate = orGate

        this.#halfAdder1.setOutSReceiver(halfAdder2, "inB")
        this.#halfAdder1.setOutCReceiver(orGate, "in1")
        this.#halfAdder2.setOutCReceiver(orGate, "in2")
    }

    #returnOutArray(){
        return [this.#halfAdder2.getOutS(), this.#orGate.getOut()]
    }

    updateInA(flag){
        super.updateInA(flag, ()=>{
            this.#halfAdder1.updateInA(flag)
            return this.#returnOutArray()
        })
    }

    updateInB(flag){
        super.updateInB(flag, ()=>{
            this.#halfAdder1.updateInB(flag)
            return this.#returnOutArray()
        })
    }

    updateInC(flag){
        this.#inC = flag
        this.#halfAdder2.updateInA(flag)
        this.updateOutSAndOutC(()=>{
            return this.#returnOutArray()
        })
    }

    updateBothIn(inA, inB){
        super.updateBothIn(inA, inB, ()=>{
            this.#halfAdder1.updateBothIn(inA, inB)
            return this.#returnOutArray()
        })
    }

    updateThreeIn(inA, inB, inC){
        super.updateBothIn(inA, inB)
        this.#inC = inC

        this.#halfAdder1.updateBothIn(inA, inB)
        this.#halfAdder2.updateBothIn(inC, this.#halfAdder1.getOutS())
        this.#orGate.updateBothIn(this.#halfAdder1.getOutC(), this.#halfAdder2.getOutC())

        this.updateOutSAndOutC(()=>{
            return this.#returnOutArray()
        })
    }

    receive(inName, flag){
        super.receive(inName, flag)
        if(inName == "inC"){
            this.updateInC(flag)
        }
    }
}

class EightBitBinaryAdder{
    #inC
    #outC
    #lastOutC

    #inA
    #inB
    #outS

    #fullAdders
    #fullAdderNum

    #outCReceiver
    #outCReceiverInName

    #outCInOutSFlag

    constructor(outCInOutSFlag = false){
        this.#outCInOutSFlag = outCInOutSFlag
        this.#inC = false
        this.#fullAdderNum = 8
        this.#fullAdders = []

        this.#inA = []
        this.#inB = []

        for(var i = 0; i < this.#fullAdderNum; i++){
            this.#inA.push(false)
            this.#inB.push(false)
        }

        //新鍵8個全加器
        for(var i = 0; i < this.#fullAdderNum; i++){
            this.#fullAdders.push(new FullAdder("f" + i))
            if(i != 0){
                this.#fullAdders[i - 1].setOutCReceiver(this.#fullAdders[i], "inC")
            }
        }

        this.updateOut()
    }

    updateBothIn(arrayA, arrayB){
        // this.#inC = inC
        this.#inA = arrayA
        this.#inB = arrayB

        for(var i = 0; i < this.#fullAdderNum; i++){
            if(i == 0){
                this.#fullAdders[i].updateInC(this.#inC)
            }
            this.#fullAdders[i].updateBothIn(arrayA[i], arrayB[i])
        }
        this.updateOut()
    }

    //輸出預設值
    updateOut(){
        this.#outS = []

        for(var i = 0; i < this.#fullAdderNum; i++){
            this.#outS.push(this.#fullAdders[i].getOutS())
            if(i == this.#fullAdderNum - 1){
                this.#lastOutC = this.#outC
                this.#outC = this.#fullAdders[i].getOutC()

                if(this.#lastOutC != this.#outC)
                    this.#sendOutC(this.#outC)

                if(this.#outCInOutSFlag)
                    this.#outS.push(this.#outC)

            }
        }
    }

    updateInC(flag){
        this.#inC = flag
        this.updateBothIn(this.#inA, this.#inB, flag)
    }

    receive(inName, flag){
        if(inName == "inC"){
            this.updateInC(flag)
        }
    }

    #sendOutC(flag){
        if(this.#outCReceiver){
            this.#outCReceiver.receive(this.#outCReceiverInName, flag)
        }
    }

    setOutCReceiver(outCReceiver, inName){
        this.#outCReceiver = outCReceiver
        this.#outCReceiverInName = inName
    }

    getOut(){
        return this.#outS
    }
}

class SixteenBitBinaryAdder{
    #inC
    #outC
    #lastOutC

    #inA
    #inB
    #outS

    #eightBitBinaryAdder1
    #eightBitBinaryAdder2

    #bitNum = 16

    #outCReceiver
    #outCReceiverInName

    #outCInOutSFlag

    constructor(outCInOutSFlag){
        this.#outCInOutSFlag = outCInOutSFlag
        this.#inC = false
        this.#eightBitBinaryAdder1 = new EightBitBinaryAdder()
        this.#eightBitBinaryAdder2 = new EightBitBinaryAdder()

        this.#inA = []
        this.#inB = []

        for(var i = 0; i < this.#bitNum; i++){
            this.#inA.push(false)
            this.#inB.push(false)
        }

        this.#eightBitBinaryAdder1.setOutCReceiver(this.#eightBitBinaryAdder2, "inC")

        this.updateOut()
    }

    updateBothIn(arrayA, arrayB){
        this.#inA = arrayA
        this.#inB = arrayB
        this.#eightBitBinaryAdder1.updateBothIn(arrayA.slice(0, 8), arrayB.slice(0, 8))
        this.#eightBitBinaryAdder2.updateBothIn(arrayA.slice(8), arrayB.slice(8))
        this.updateOut()
    }

    updateOut(){
        this.#outS = this.#eightBitBinaryAdder1.getOut().concat(this.#eightBitBinaryAdder2.getOut())
        //傳送send(OutC)
    }
    getOut(){
        return this.#outS
    }

    //TODO
    // updateInC(flag){
    //     this.#inC = flag

    //     this.updateBothIn(this.#inA, this.#inB, flag)
    // }

    // receive(inName, flag){
    //     if(inName == "inC"){
    //         this.updateInC(flag)
    //     }
    // }

    #sendOutC(flag){
        if(this.#outCReceiver){
            this.#outCReceiver.receive(this.#outCReceiverInName, flag)
        }
    }

    setOutCReceiver(outCReceiver, inName){
        this.#outCReceiver = outCReceiver
        this.#outCReceiverInName = inName
    }

}

執行驗證程式碼testModel.js
驗證通過判據:後臺沒有列印輸出異常。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test</title>
    <script src="../js/test/TestUtils.js"></script>
    <script src="../js/model.js"></script>
    <script src="../js/utils.js"></script>
</head>
<body>

    <script>
        var g1 = new NullGate("g1")
        var g2 = new NullGate("g2")
        var g3 = new NullGate("g3")
        var g4 = new NullGate("g4")
        var g5 = new NullGate("g5")
        var g6 = new NullGate("g6")

        g1.setOutReceiver(g2, "in")
        g2.setOutReceiver(g3, "in")
        g3.setOutReceiver(g4, "in")
        g4.setOutReceiver(g5, "in")
        g5.setOutReceiver(g6, "in")

        g1.updateIn(true)
        assertTrue(g6.getOut())
    </script>

    <script>
        //與門
        var andGate = new AndGate("a01")
        assertFalse(andGate.getOut())

        andGate.updateIn1(true)
        assertFalse(andGate.getOut())

        andGate.updateIn2(true)
        assertTrue(andGate.getOut())

        //或門
        var orGate = new OrGate("o01")
        assertFalse(orGate.getOut())

        orGate.updateIn1(true)
        assertTrue(orGate.getOut())

        orGate.updateIn2(true)
        assertTrue(orGate.getOut())

        //互斥或門
        var xorGate = new XorGate("x01")
        assertFalse(xorGate.getOut())

        xorGate.updateIn1(true)
        assertTrue(xorGate.getOut())

        xorGate.updateIn2(true)
        assertFalse(xorGate.getOut())

        xorGate.updateBothIn(false, true)
        assertTrue(xorGate.getOut())

    </script>
    <script>
        //半加器
        var ha = new HalfAdder("h01")
        assertFalse(ha.getOutS())
        assertFalse(ha.getOutC())
        
        ha.updateInB(true)
        assertTrue(ha.getOutS())
        assertFalse(ha.getOutC())

        ha.updateInA(true)
        assertFalse(ha.getOutS())
        assertTrue(ha.getOutC())

        ha.updateBothIn(true, false)
        assertTrue(ha.getOutS())
        assertFalse(ha.getOutC())
    </script>

    <script>
        //全加器
        var fa = new FullAdder("fa01")
        assertFalse(fa.getOutC())
        assertFalse(fa.getOutS())

        function test(inA, inB, inC, expectedS, expectedC){
            fa.updateThreeIn(inA, inB, inC)
            if(expectedS)
                assertTrue(fa.getOutS())
            else
                assertFalse(fa.getOutS())

            if(expectedC)
                assertTrue(fa.getOutC())
            else
                assertFalse(fa.getOutC())
        }

        test(false, false, false, false, false)
        test(false, true, false, true, false)
        test(true, false, false, true, false)
        test(true, true, false, false, true)
        test(false, false, true, true, false)
        test(false, true, true, false, true)
        test(true, false, true, false, true)
        test(true, true, true, true, true)
        
    </script>

    <script>
        for(var i = 0 ; i < 256; i++){
            assertIntEquals(i, lowEightBitArray2Int(int2LowEightBitArray(i)))
        }
    </script>
    <script>
        var ebba = new EightBitBinaryAdder(true)

        for(var i = 0; i < 256; i++){
            for(var j = 0; j < 256; j++){i
                ebba.updateBothIn(int2LowEightBitArray(i), int2LowEightBitArray(j))
                assertIntEquals(i + j, lowNineBitArray2Int(ebba.getOut()))
            }
        }

    </script>
    <script>
        for(var i = -128; i <= 127; i++){
            assertIntEquals(i, eightBitArray2Int(int2EightBitArray(i)))
        }
    </script>
    <script>
        var ebba = new EightBitBinaryAdder()

        for(var i = -64; i < 64; i++){
            for(var j = -64; j < 64; j++){
                ebba.updateBothIn(int2EightBitArray(i), int2EightBitArray(j))
                assertIntEquals(i + j, eightBitArray2Int(ebba.getOut()))
            }
        }
    </script>
    <script>
        for(var i = -(1<<15); i <= (1<<15) - 1; i++){
            assertIntEquals(i, sixteentBitArray2Int(int2SixteenBitArray(i)))
        }
    </script>

    <script>
        var sbba = new SixteenBitBinaryAdder()

        sbba.updateBothIn(int2SixteenBitArray(1156), int2SixteenBitArray(9999))
        assertIntEquals(9999 + 1156, sixteentBitArray2Int(sbba.getOut()))

        //‭16384‬ * ‭16384‬ = 268435456 如果這樣算會十分耗時
        // for(var i = -(1<<14); i < (1<<14); i++){
        //     for(var j = -(1<<14); j < (1<<14); j++){
        //         sbba.updateBothIn(int2SixteenBitArray(i), int2SixteenBitArray(j))
        //         assertIntEquals(i + j, sixteentBitArray2Int(sbba.getOut()))
        //     }
        // }
        for(var i = -100; i < 100; i++){
            for(var j = -100; j < 100; j++){
                sbba.updateBothIn(int2SixteenBitArray(i), int2SixteenBitArray(j))
                assertIntEquals(i + j, sixteentBitArray2Int(sbba.getOut()))
            }
        }
    </script>
</body>
</html>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com