JavaScript模拟CPU ALU进行加减乘除

以下代码用于理解计算原理,并不具有通用性!

//================== utils =======================
let MQ, ACC, X;
function strSplice(str, index, num, insert = '') {
    return str.slice(0, index) + insert + str.slice(index + num);
}

function accLeftShift() {
    let moveOut = ACC[0];
    ACC = strSplice(ACC, 0, 1);
    ACC = strSplice(ACC, ACC.length, 0, "0");
    return moveOut;
}

function accRightShift() {
    let moveOut = ACC[ACC.length-1];
    ACC = strSplice(ACC, ACC.length-1, 1);
    ACC = strSplice(ACC, 0, 0, "0");
    return moveOut;
}

function accRightShiftWithComplementCode(isDoubleFlags) {
    let flag = ACC[0];// double flag first bit is positive or negtive
    let firstNumBit = isDoubleFlags?2:1;
    let moveOut = ACC[ACC.length-1];
    ACC = strSplice(ACC, ACC.length-1, 1);
    ACC = strSplice(ACC, firstNumBit, 0, flag);
    return moveOut;
}

// test spec
// ACC = '00100000';
// console.log(accRightShiftWithComplementCode(true));
// console.log(accRightShiftWithComplementCode(true));
// console.log(accRightShiftWithComplementCode(true));
// console.log(accRightShiftWithComplementCode(true));
// console.log(accRightShiftWithComplementCode(true));

function bitAdd(ACC, num) {
    let carry = 0;// 1 or 0
    for (let i = num.length-1; i >= 0; i--) {
        let sum = parseInt(ACC[i]) + parseInt(num[i]) + carry;
        carry = sum > 1 ? 1 : 0;
        ACC = strSplice(ACC, i, 1, sum % 2);
    };
    return ACC;
}

function mQRightShift(moveOut) {
    MQ = strSplice(MQ, 0, 0, moveOut);
    MQ = strSplice(MQ, MQ.length-1, 1);// remove last bit
}

/*
    decime convert to binary
*/
function dec2Bin(num, bits = 8) {
    let b = new Array(bits).fill(0);
    if(num<0) b[0] = 1;
    num = Math.abs(num);
    let i = bits - 1;
    while(parseInt(num / 2)) {
        b[i--] = (num % 2);
        num = parseInt(num / 2);
    }
    b[i--] = num % 2;
    return b.join('');
}

function originCode2ComplementCode(bStr, isUnsigned = false) {
    // default first bit is flag bit
    let b = bStr.split('');
    if(b[0] == '0' && !isUnsigned) return bStr; // positive num originCode equ with complementCode
    for(let i = isUnsigned?0:1; i < b.length; i++) {// unisigned code uppest bit is not flag bit can be reversed
        b[i] = (!parseInt(b[i])?1:0); 
    }
    //
    bStr = b.join('');
    
    return bitAdd(bStr, '0'.repeat(bStr.length-1) + '1');// +1
}

// add double flag bit
function addDoubleFlags(a) {
    return (a[0] === '1'? '1' : '0' ) + a;
}

// ============ calc ================

// ++++++++++++ originCodeMultiple +++++++++++++++
function originCodeMultiple(a, b) {
    let aAbs = Math.abs(a).toString().replace(".", "");
    let bAbs = Math.abs(b).toString().replace(".", "");//  remove minus point default 0.xxx
    let aFlag = a < 0 ? 0 : 1;
    let bFlag = b < 0 ? 0 : 1;
    let cFlag = (aFlag ^ bFlag).toString();
    ACC = "0".repeat( aAbs.length);// 0(.)0000 without minus point
    MQ = bAbs.slice(1);// 01111 => 1111
    X = aAbs;// add flag bit "0"
    for (let i = MQ.length-1; i >= 0; i--) {
        if(MQ[MQ.length-1] === "1") {
            ACC = bitAdd(ACC, X);
        }
        mQRightShift(accRightShift());
    }
    return cFlag + strSplice(ACC,1,0,'.') + MQ;
}

// ++++++++++++ complementCodeMultiple +++++++++++++++
function complementCodeMultiple(a, b) {
    ACC = '0'.repeat(a.length-1).toString();// -1 remove minus point bit
    ACC = '0' + ACC;// double flags
    let aAbs = a.toString().replace(".", "");
    let bAbs = b.toString().replace(".", "");//  remove minus point default 00.xxx
    MQ = originCode2ComplementCode(bAbs) + '0';// add aux bit
    let aComplementCode = originCode2ComplementCode(aAbs);
    let subAComplementCode = originCode2ComplementCode((aAbs[0]==='1'?'0':'1') + aAbs.slice(1));
    aComplementCode = aComplementCode[0] + aComplementCode;
    subAComplementCode = subAComplementCode[0] + subAComplementCode;
   
    for(let i=MQ.length-1; i>=1 ; i--) {
        // do n + 1 loop add, n loop rightShift
        let prev = MQ[MQ.length-1];
        let next = MQ[MQ.length-2];
        if(prev === '0' && next === '1') {
            ACC = bitAdd(ACC, subAComplementCode);
        }else if(prev === '1' && next === '0') {
            ACC = bitAdd(ACC, aComplementCode);
        }else{
            // ACC + 0
        }
        // n+1 add n rightShift
        i>1 && mQRightShift(accRightShiftWithComplementCode(true));
    }
    ACC = [...ACC]
    ACC.splice(2, 0, '.');// double flag add minus point
    return ACC.join('') + MQ.slice(0, -2);
}

// console.log(complementCodeMultiple('1.1101000', '0.1011000'));
// console.log(complementCode('1.1101', '0.1011'));

// ++++++++++++ originCodeDivide +++++++++++++++
function originCodeDivideWithRecover(a, b) {
    // use one flag bit
    a = a.toString().replace('.', '');
    b = b.toString().replace('.', '');
    let aFlag = a[0];
    let bFlag = b[0];
    let cFlag = parseInt(aFlag) ^ parseInt(bFlag);
    ACC = a;
    let bComplementCode = originCode2ComplementCode(b);
    let bSubComplementCode = originCode2ComplementCode(b[0]==='1'?'0':'1' + b.slice(1));

    MQ = [];
    for(let i=a.length-1; i>=0; i--) {
        let curQuotient = '1';
        ACC = bitAdd(ACC, bSubComplementCode);
        if(ACC[0] === '1') {
            // recover curQuotient
            ACC = bitAdd(ACC, bComplementCode);
            MQ.push('0');
           
        }else{
            MQ.push(curQuotient);  
        }
        i>0 && accLeftShift();
    }
    ACC[0] = cFlag;
    ACC = [...ACC]
    ACC.splice(1, 0, '.');// double flag add minus point
    let Remainder = ACC.join('');

    MQ[0] = cFlag;
    MQ = [...MQ]
    MQ.splice(1, 0, '.');// double flag add minus point
    let Quotient = MQ.join('');

    let nBitNum = a.length-1;
    console.log(`Remainder => ${Remainder}X${2}^${-nBitNum}`, `Quotient => ${Quotient}`)
}

function originCodeDivideWithoutRecover(a, b) {
    // use one flag bit
    a = a.toString().replace('.', '');
    b = b.toString().replace('.', '');
    let aFlag = a[0];
    let bFlag = b[0];
    let cFlag = parseInt(aFlag) ^ parseInt(bFlag);
    ACC = a;
    let bComplementCode = originCode2ComplementCode(b);
    let bSubComplementCode = originCode2ComplementCode(b[0]==='1'?'0':'1' + b.slice(1));

    MQ = [];
    let nextQuotient = '1';
    for(let i=a.length-1; i>=0; i--) {
        let curQuotient = nextQuotient;
       if(curQuotient === '1') {
        ACC = bitAdd(ACC, bSubComplementCode);
       }else{
        ACC = bitAdd(ACC, bComplementCode);
       }
        if(ACC[0] === '1') {
            MQ.push('0');
            nextQuotient = '0';
        }else{
            MQ.push('1');
            nextQuotient = '1';
        }
        i>0 && accLeftShift();
    }
    if(ACC[0] === '1') {
        // remainder is negtive add positive complement code
        ACC = bitAdd(ACC, bComplementCode);
    }
    ACC[0] = cFlag;
    ACC = [...ACC]
    ACC.splice(1, 0, '.');// double flag add minus point
    let Remainder = ACC.join('');

    MQ[0] = cFlag;
    MQ = [...MQ]
    MQ.splice(1, 0, '.');// double flag add minus point
    let Quotient = MQ.join('');

    let nBitNum = a.length-1;
    console.log(`Remainder => ${Remainder}X${2}^${-nBitNum}`, `Quotient => ${Quotient}`)
}

// ====== test spec ========
// console.log(originCodeDivideWithRecover('0.1011', '0.1101'));
// console.log(originCodeDivideWithoutRecover('0.1011', '0.1101'));

// ++++++++ complementCodeDivideWithoutRecover ++++++++
function complementCodeDivideWithoutRecover(a, b) {
    // use one flag bit
    a = a.toString().replace('.', '');
    b = b.toString().replace('.', '');
    a = originCode2ComplementCode(a)
    ACC = a[0] + a;// double flags
    MQ = [];
    let n = a.length;
    let bComplementCode = originCode2ComplementCode(b);
    let subBComplementCode = originCode2ComplementCode((b[0]==='1'?'0':'1') + b.slice(1));
    bComplementCode = bComplementCode[0] + bComplementCode;
    subBComplementCode = subBComplementCode[0] + subBComplementCode;
    
    for(let i=n-1; i>=0; i--) {
        if(ACC[0] === bComplementCode[0]) {
            ACC = bitAdd(ACC, subBComplementCode);
            MQ.unshift('1');
        }else{
            ACC = bitAdd(ACC, bComplementCode);
            MQ.unshift('0');
        }   
        i>0 && accLeftShift();
    }
    
    ACC = [...ACC].slice(1);
    ACC.splice(1, 0, '.');// double flag add minus point
    let Remainder = ACC.join('');

    MQ = [...MQ]
    MQ.splice(1, 0, '.');// double flag add minus point
    let Quotient = MQ.join('');

    let nBitNum = n-1;
    console.log(`Remainder => ${Remainder}X${2}^${-nBitNum}`, `Quotient => ${Quotient}`)
}

// ====== test spec ========
// console.log(complementCodeDivideWithoutRecover('0.1011', '0.1101'));
// console.log(originCodeDivideWithoutRecover('0.1011', '0.1101'));


// ++++++++++++ complementCodeAddMinus +++++++++++++++
/*
    bits max bits with a flag bit
*/
function complementCodeAddMinus(a = 15, b = 124, bits = 8) {
    a = originCode2ComplementCode(dec2Bin(a, bits));
    b = originCode2ComplementCode(dec2Bin(b, bits));

    // use double flags judge overflow
    a = addDoubleFlags(a);
    b = addDoubleFlags(b);
    let acc = bitAdd(a, b);
    console.log(overflowjudgeByDouFlags(acc));
    return acc
}

function overflowjudgeByDouFlags(num) {
    let isOverFlow = num[0] ^ num[1]// if double flags equ with each other is not overflow
    if(isOverFlow) {
        if(num[0] === '1') return 'lower overflow'
        else return 'upper overflow'
    }else{
        return 'in the range';
    }
}

// ++++++++++++ unsignedCodeAddMinus +++++++++++++++
/*
    action 0 => add, 1 => minus
*/
function unsignedCodeAddMinus(a, b, action = 0,bits = 8) {
    a = dec2Bin(a);
    b = dec2Bin(b);
    // unsigned code without any flag bit
    if(action === 1) {
        b = originCode2ComplementCode(b, true);
    }
    let acc = bitAdd('0'+a, '0'+b);// add zero bit is to judge overflow without modify bitAdd func
    console.log(judgeUnsignedCodeAddMinusOverFlow(acc, action));
    return acc.slice(1);// remove temp judge overflow bit
}

function judgeUnsignedCodeAddMinusOverFlow(a, action = 0) {
    if(action === 1 && a[0] === '0') {
        return 'lower overflow';
    }else if(action === 0 && a[0] === '1') {
        return 'upper overflow';
    }
    return 'in the range';
}

// ++++++++++++ originCodeAddMinus +++++++++++++++
function bitMinus(MCC, num) {
    MCC = MCC.split('');
    num = num.split('');
    for(let i=num.length-1; i>=0; i--) {
        let a = MCC[i];
        let b = num[i];
        if((a=='1' && b=='1') || (a=='0' && b=='0')) {
            MCC[i]= '0';
        } else if(a=='1' && b=='0') {
            MCC[i]= '1';
        }else{
            // need borrow bit
            let borrowSuc = false;
            for(let j = i-1; j>=0; j--) {
                if(MCC[j] === '1') {
                    MCC[j] = '0';
                    borrowSuc = true;
                    for(let z=j+1; z<=i; z++) {
                        MCC[z] = '1';
                    }
                    break;
                }
            }
            if(!borrowSuc) {
                console.log(`can't borrow bit!`);
                return NaN;
            }
        }
    }
    return MCC.join('');
}

function originCodeAddMinus(a, b, action = 0) {
    a = dec2Bin(a);
    b = dec2Bin(b);
    // warning: when minus num , use ACC calc can be mistake, so use MCC calc
    if(action === 1) {
        let mcc = bitMinus(a, b);
        return mcc;
    }else{
        let acc = bitAdd(a, b);
        return acc
    }
}


//================== test spec ====================

console.log(bitMinus('10000000', '00000001'));// 01111111
console.log(originCodeAddMinus(10, 6));// 00010000
console.log(originCodeAddMinus(10, 6, 1));// 00000100
console.log(complementCodeAddMinus(15, 124));//  01,0001011
console.log(complementCodeAddMinus(-24, -124));//  10,1101100
console.log(unsignedCodeAddMinus(99, 100));// 11000111
console.log(unsignedCodeAddMinus(99, 100, 1));// 11111111


console.log(originCodeMultiple(-0.1101111111111, -0.1100110110111));// 0.65625
console.log(complementCodeMultiple('1.1101000', '0.1011000'));

(originCodeDivideWithRecover('0.1011', '0.1101'));
(originCodeDivideWithoutRecover('0.1011', '0.1101'));
(complementCodeDivideWithoutRecover('0.1011', '0.1101'));
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值