Implemented 36-bit multiplication with support for (up to) 72-bit results
This commit is contained in:
parent
92a919c927
commit
f03a5686e5
3 changed files with 106 additions and 17 deletions
|
|
@ -1773,6 +1773,9 @@ X86.fnMULb = function(dst, src)
|
||||||
*
|
*
|
||||||
* This sets regMDHi:regMDLo to the 64-bit result of dst * src, both of which are treated as unsigned.
|
* This sets regMDHi:regMDLo to the 64-bit result of dst * src, both of which are treated as unsigned.
|
||||||
*
|
*
|
||||||
|
* The algorithm is based on the traditional "by hand" multiplication method, by treating the two inputs
|
||||||
|
* (dst and src) as two 2-digit numbers, where each digit is a base-65536 digit.
|
||||||
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
* @param {number} dst (any 32-bit number, treated as unsigned)
|
* @param {number} dst (any 32-bit number, treated as unsigned)
|
||||||
* @param {number} src (any 32-bit number, treated as unsigned)
|
* @param {number} src (any 32-bit number, treated as unsigned)
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ var i36Reg = new Int36();
|
||||||
*/
|
*/
|
||||||
function dumpInt36(i36)
|
function dumpInt36(i36)
|
||||||
{
|
{
|
||||||
return i36.toString() + " (" + i36.toString(8, true) + ")";
|
return i36.toString() + " [" + i36.toString(8, true) + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -93,7 +93,8 @@ function test(sCmd, fREPL)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(sOp + (sNum1? (" " + dumpInt36(i36Op)) : "") + ": " + dumpInt36(i36Reg));
|
console.log(sOp + " " + dumpInt36(i36Op));
|
||||||
|
if (sOp != "set") console.log(" = " + dumpInt36(i36Reg));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -115,12 +116,20 @@ var onCommand = function (cmd, context, filename, callback)
|
||||||
};
|
};
|
||||||
|
|
||||||
test("set -34,359,738,368");
|
test("set -34,359,738,368");
|
||||||
test("add 1");
|
|
||||||
test("sub 1");
|
|
||||||
test("sub 1");
|
test("sub 1");
|
||||||
test("add 1");
|
test("add 1");
|
||||||
test("add 0");
|
test("add 0");
|
||||||
|
|
||||||
|
for (let i = 0; i <= 12; i++) {
|
||||||
|
test("set 34,000,000,000");
|
||||||
|
test("mul " + Math.pow(8, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i <= 12; i++) {
|
||||||
|
test("set -34,000,000,000");
|
||||||
|
test("mul " + Math.pow(8, i));
|
||||||
|
}
|
||||||
|
|
||||||
repl.start({
|
repl.start({
|
||||||
prompt: "int36> ",
|
prompt: "int36> ",
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,12 @@ class Int36 {
|
||||||
this.value -= Int36.BIT36;
|
this.value -= Int36.BIT36;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* The 'extended' property stores an additional 36 bits of data after a multiplication, and any
|
||||||
|
* remainder after a division. It's strictly an output-only property for those operations, and
|
||||||
|
* its value does not affect the result of ANY operation.
|
||||||
|
*/
|
||||||
|
this.extended = 0;
|
||||||
this.error = Int36.ERROR.NONE;
|
this.error = Int36.ERROR.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,26 +125,49 @@ class Int36 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* octal(value)
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
static octal(value)
|
||||||
|
{
|
||||||
|
if (value < 0) value += Int36.BIT36;
|
||||||
|
return ("00000000000" + value.toString(8)).slice(-12);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toString(radix, fUnsigned)
|
* toString(radix, fUnsigned)
|
||||||
*
|
*
|
||||||
* @param {number} [radix] (default is 10)
|
* @param {number} [radix] (default is 10)
|
||||||
* @param {boolean} [fUnsigned] (default is signed)
|
* @param {boolean} [fUnsigned] (default is signed for radix 10, unsigned for any other radix)
|
||||||
*/
|
*/
|
||||||
toString(radix = 10, fUnsigned)
|
toString(radix = 10, fUnsigned)
|
||||||
{
|
{
|
||||||
var s;
|
var s;
|
||||||
var value = this.value;
|
var value = this.value;
|
||||||
if (fUnsigned) {
|
var extended = this.extended;
|
||||||
if (value < 0) {
|
if (radix == 8) {
|
||||||
value += Int36.BIT36;
|
s = Int36.octal(value);
|
||||||
|
if (extended) {
|
||||||
|
s = Int36.octal(extended) + ',' + s;
|
||||||
}
|
}
|
||||||
if (radix == 8) {
|
if (DEBUG && this.error) s += " error 0x" + this.error.toString(16);
|
||||||
s = "0o" + ("00000000000" + value.toString(8)).slice(-12);
|
return s;
|
||||||
if (DEBUG && this.error) s += " error 0x" + this.error.toString(16);
|
}
|
||||||
|
if (radix != 10) fUnsigned = true;
|
||||||
|
if (fUnsigned || extended) {
|
||||||
|
if (value < 0) value += Int36.BIT36;
|
||||||
|
if (extended) {
|
||||||
|
if (fUnsigned) extended += Int36.BIT36;
|
||||||
|
/*
|
||||||
|
* TODO: Come up with a solution that won't overflow JavaScript's more limited precision.
|
||||||
|
*/
|
||||||
|
value = extended * Int36.BIT36 + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!s) s = value.toString(radix);
|
s = value.toString(radix);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,13 +236,10 @@ class Int36 {
|
||||||
/**
|
/**
|
||||||
* mul(i36)
|
* mul(i36)
|
||||||
*
|
*
|
||||||
* TODO: Support multiplication results > 36 bits (ie, up to 72 bits) if an additional Int36
|
|
||||||
* parameter is provided.
|
|
||||||
*
|
|
||||||
* @param {Int36} i36
|
* @param {Int36} i36
|
||||||
*/
|
*/
|
||||||
mul(i36) {
|
mul(i36) {
|
||||||
this.value = this.truncate(this.value * i36.value);
|
this.mulExtended(i36.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -222,7 +248,58 @@ class Int36 {
|
||||||
* @param {number} num
|
* @param {number} num
|
||||||
*/
|
*/
|
||||||
mulNum(num) {
|
mulNum(num) {
|
||||||
this.value = this.truncate(this.value * Int36.validate(num));
|
this.mulExtended(Int36.validate(num));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mulExtended(value)
|
||||||
|
*
|
||||||
|
* To support 72-bit results, we perform the multiplication process as you would "by hand",
|
||||||
|
* treating each of the operands to be multiplied as two 2-digit numbers, where each digit is
|
||||||
|
* an 18-bit number (base 2^18). Each individual multiplication of these 18-bit "digits"
|
||||||
|
* will produce a result within 2^36, well within JavaScript integer accuracy.
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
mulExtended(value) {
|
||||||
|
var fNeg = false, extended;
|
||||||
|
var n1 = this.value, n2 = value;
|
||||||
|
|
||||||
|
if (n1 < 0) {
|
||||||
|
n1 = -n1;
|
||||||
|
fNeg = !fNeg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n2 < 0) {
|
||||||
|
n2 = -n2;
|
||||||
|
fNeg = !fNeg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n1 < Int36.BIT18 && n2 < Int36.BIT18) {
|
||||||
|
value = n1 * n2;
|
||||||
|
extended = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var n1d1 = (n1 % Int36.BIT18);
|
||||||
|
var n1d2 = Math.trunc(n1 / Int36.BIT18);
|
||||||
|
var n2d1 = (n2 % Int36.BIT18);
|
||||||
|
var n2d2 = Math.trunc(n2 / Int36.BIT18);
|
||||||
|
|
||||||
|
var m1d1 = n1d1 * n2d1;
|
||||||
|
var m1d2 = (n1d2 * n2d1) + Math.trunc(m1d1 / Int36.BIT18);
|
||||||
|
extended = Math.trunc(m1d2 / Int36.BIT18);
|
||||||
|
m1d2 = (m1d2 % Int36.BIT18) + (n1d1 * n2d2);
|
||||||
|
value = (m1d2 * Int36.BIT18) + (m1d1 % Int36.BIT18);
|
||||||
|
extended += Math.trunc(m1d2 / Int36.BIT18) + (n1d2 * n2d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fNeg) {
|
||||||
|
value = -value;
|
||||||
|
extended = -extended - (value? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = this.truncate(value);
|
||||||
|
this.extended = this.truncate(extended);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue