mirror of
https://github.com/Asraelite/littlebigcomputer.git
synced 2025-07-18 00:26:50 +00:00
Init commit
This commit is contained in:
commit
c45ad79440
48 changed files with 6786 additions and 0 deletions
298
assembler/bit_utils.ts
Normal file
298
assembler/bit_utils.ts
Normal file
|
@ -0,0 +1,298 @@
|
|||
const EPSILON = 2 ** (-24);
|
||||
|
||||
class Analog {
|
||||
value: Float32Array;
|
||||
private constructor(value: number) {
|
||||
this.value = new Float32Array(1);
|
||||
this.value[0] = Math.max(Math.min(value, 1), -1);
|
||||
}
|
||||
|
||||
static fromRaw(value: number): Analog {
|
||||
return new Analog(value);
|
||||
}
|
||||
|
||||
static fromEpsilon(value: number): Analog {
|
||||
return new Analog(EPSILON * value);
|
||||
}
|
||||
|
||||
asInternal(): string {
|
||||
const view = new DataView(this.value.buffer);
|
||||
const int = view.getUint32(0, true);
|
||||
const bits = int.toString(2).padStart(32, '0');
|
||||
const sign = bits[0];
|
||||
const exponent = bits.slice(1, 9);
|
||||
const mantissa = bits.slice(9);
|
||||
return `${sign} ${exponent} ${mantissa}`;
|
||||
}
|
||||
|
||||
rawValue(): number {
|
||||
return this.value[0];
|
||||
}
|
||||
|
||||
asBinary(sectionSize: number = 24, showFraction = true): string {
|
||||
if (this.value[0] >= 1) {
|
||||
return '>=1';
|
||||
}
|
||||
const sign = this.value[0] < 0 ? '-' : ' ';
|
||||
const number = Math.abs(this.value[0]) * (2 ** 24);
|
||||
const bits = (number | 0).toString(2).padStart(24, '0');
|
||||
const fraction = (number - (number | 0));
|
||||
const fractionBits = (fraction * (2 ** 24) | 0).toString(2).padStart(24, '0');
|
||||
const parts = bits.match(new RegExp(`.{1,${sectionSize}}`, 'g'))!;
|
||||
if (showFraction) {
|
||||
parts.push('.', ...fractionBits.match(/.{1,6}/g)!);
|
||||
}
|
||||
return `${sign}${parts.join(' ')}`;
|
||||
}
|
||||
|
||||
asHex(): string {
|
||||
const number = Math.abs(this.value[0]) * (2 ** 24);
|
||||
const hex = (number | 0).toString(16).padStart(6, '0');
|
||||
return `0x${hex}`;
|
||||
}
|
||||
|
||||
asNote(): string {
|
||||
const max = (2 ** 19);
|
||||
const result = this.value[0] * max;
|
||||
return `${result.toFixed(5)} x32`;
|
||||
}
|
||||
|
||||
asEpsilon(): number {
|
||||
return this.value[0] / EPSILON;
|
||||
}
|
||||
|
||||
get v() {
|
||||
return this.rawValue();
|
||||
}
|
||||
|
||||
get i() {
|
||||
return this.asInternal();
|
||||
}
|
||||
|
||||
get b() {
|
||||
return this.asBinary();
|
||||
}
|
||||
|
||||
get bs() {
|
||||
return this.asBinary(6);
|
||||
}
|
||||
|
||||
get b8() {
|
||||
return this.asBinary(8);
|
||||
}
|
||||
|
||||
get bh() {
|
||||
return this.asBinary(6, false);
|
||||
}
|
||||
|
||||
get h() {
|
||||
return this.asHex();
|
||||
}
|
||||
|
||||
get n() {
|
||||
return this.asNote();
|
||||
}
|
||||
|
||||
get e() {
|
||||
return this.asEpsilon();
|
||||
}
|
||||
}
|
||||
|
||||
function int24(value: number): Analog {
|
||||
return Analog.fromEpsilon(value);
|
||||
}
|
||||
|
||||
function raw(value: number): Analog {
|
||||
return Analog.fromRaw(value);
|
||||
}
|
||||
|
||||
function rand(): Analog {
|
||||
return Analog.fromEpsilon((Math.random() * (2 ** 24)) | 0);
|
||||
}
|
||||
|
||||
function battery(percentage: number): Analog {
|
||||
return Analog.fromRaw(percentage / 100);
|
||||
}
|
||||
|
||||
function add(...values: Array<Analog>): Analog {
|
||||
if (values.length === 1) return values[0];
|
||||
const result = values[0].value[0] + values[1].value[0];
|
||||
return add(Analog.fromRaw(result), ...values.slice(2));
|
||||
}
|
||||
|
||||
function addOverflowing(a: Analog, b: Analog): [Analog, Analog] {
|
||||
let result = a.v + b.v;
|
||||
let carry = 0;
|
||||
if (result > 1) {
|
||||
carry = 1;
|
||||
result -= 1;
|
||||
}
|
||||
return [Analog.fromRaw(result), Analog.fromRaw(carry)];
|
||||
}
|
||||
|
||||
function sub(first: Analog, ...values: Array<Analog>): Analog {
|
||||
if (values.length === 0) return first;
|
||||
const result = first.value[0] - values[0].value[0];
|
||||
return sub(Analog.fromRaw(result), ...values.slice(1));
|
||||
}
|
||||
|
||||
function compare(first: Analog, second: Analog): Analog {
|
||||
if (Math.abs(first.value[0]) >= Math.abs(second.value[0])) {
|
||||
return first;
|
||||
} else {
|
||||
return second;
|
||||
}
|
||||
}
|
||||
|
||||
function mul(first: Analog, ...values: Array<Analog>): Analog {
|
||||
if (values.length === 0) return first;
|
||||
const result = first.value[0] * values[0].value[0];
|
||||
return mul(Analog.fromRaw(result), ...values.slice(1));
|
||||
}
|
||||
|
||||
function mulRaw(first: Analog, ...values: Array<Analog>): Analog {
|
||||
if (values.length === 0) return first;
|
||||
const result = first.value[0] * values[0].value[0];
|
||||
return mul(Analog.fromRaw(result), ...values.slice(1));
|
||||
}
|
||||
|
||||
function recip(x: number): Analog {
|
||||
const value = ((2 ** 25) / x + 1) & 0xffffff;
|
||||
return int24(value);
|
||||
}
|
||||
|
||||
function not(first: Analog): Analog {
|
||||
const result = 1.0 - first.value[0];
|
||||
return Analog.fromRaw(result);
|
||||
}
|
||||
|
||||
function raise(value: Analog, inputs: number, repetitions: number): Analog {
|
||||
let result = value;
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
result = add(...Array(inputs).fill(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function lower(value: Analog, repetitions: number): Analog {
|
||||
let result = value;
|
||||
for (let i = 0; i < repetitions; i++) {
|
||||
result = mul(result, battery(50));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function exactlyEquals(first: Analog, second: Analog): boolean {
|
||||
return first.value[0] === second.value[0];
|
||||
}
|
||||
|
||||
function topBit(value: Analog): boolean {
|
||||
return value.value[0] >= 0.5;
|
||||
}
|
||||
|
||||
function truncate(value: Analog, keepBits: number): Analog {
|
||||
const truncated = value.asEpsilon() & (((2 ** keepBits) - 1) << (24 - keepBits));
|
||||
return Analog.fromEpsilon(truncated);
|
||||
}
|
||||
|
||||
function negate(value: Analog): Analog {
|
||||
return mul(value, battery(-100));
|
||||
}
|
||||
|
||||
function split(value: Analog, raise = true): [Analog, Analog] {
|
||||
let bottom = value.asEpsilon() & ((2 ** 12) - 1);
|
||||
if (raise) bottom *= (2 ** 12)
|
||||
return [truncate(value, 12), Analog.fromEpsilon(bottom)];
|
||||
}
|
||||
|
||||
function rshift(value: Analog, shift: number): Analog {
|
||||
return lower(value, shift);
|
||||
}
|
||||
|
||||
function lshift(value: Analog, shift: number): Analog {
|
||||
return raise(value, 2, shift);
|
||||
}
|
||||
|
||||
export function expose() {
|
||||
window['int24'] = int24;
|
||||
window['i'] = int24;
|
||||
window['raw'] = raw;
|
||||
window['rand'] = rand;
|
||||
window['battery'] = battery;
|
||||
window['b'] = battery;
|
||||
window['add'] = add;
|
||||
window['addOverflowing'] = addOverflowing;
|
||||
window['sub'] = sub;
|
||||
window['compare'] = compare;
|
||||
window['mul'] = mul;
|
||||
window['mulRaw'] = mulRaw;
|
||||
window['recip'] = recip;
|
||||
window['not'] = not;
|
||||
window['raise'] = raise;
|
||||
window['lower'] = lower;
|
||||
window['exactlyEquals'] = exactlyEquals;
|
||||
window['topBit'] = topBit;
|
||||
window['truncate'] = truncate;
|
||||
window['negate'] = negate;
|
||||
window['split'] = split;
|
||||
window['rshift'] = rshift;
|
||||
window['lshift'] = lshift;
|
||||
}
|
||||
|
||||
export function run() {
|
||||
// setTimeout(() => location.reload(), 2000);
|
||||
|
||||
for (let i = 0; i < 1; i++) {
|
||||
const a = int24(0xffffff);
|
||||
const b = int24(0xffffff);
|
||||
// const a = rand();
|
||||
// const b = rand();
|
||||
if (!calc(a, b)) {
|
||||
console.log('failed', a.e, b.e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function calc(a: Analog, b: Analog): boolean {
|
||||
const aVal = a.e;
|
||||
const bVal = b.e;
|
||||
const productBigInt = BigInt(aVal) * BigInt(bVal);
|
||||
const expectedHigh = int24(Number((productBigInt >> 24n) & 0xffffffn));
|
||||
const expectedLow = int24(Number(productBigInt & 0xffffffn));
|
||||
|
||||
print('multiply', a.bs, b.bs, expectedHigh.asBinary(6, false) + ' /' + expectedLow.asBinary(6, false), `${a.e} * ${b.e} = ${a.e * b.e}`);
|
||||
|
||||
const [ah, al] = split(a);
|
||||
const [bh, bl] = split(b);
|
||||
|
||||
print('split', ah.bh, al.bh, bh.bh, bl.bh);
|
||||
|
||||
const high = mul(ah, bh);
|
||||
const middle1 = mul(ah, bl);
|
||||
const middle2 = mul(al, bh);
|
||||
const low = mul(al, bl);
|
||||
let [middle, middleCarry] = addOverflowing(middle1, middle2);
|
||||
let [middleHigh, middleLow] = split(middle);
|
||||
middleHigh = rshift(middleHigh, 12);
|
||||
middleCarry = rshift(middleCarry, 12);
|
||||
|
||||
print('middle1', middle1.bh);
|
||||
print('middle2', middle2.bh);
|
||||
|
||||
print('high', high.bh);
|
||||
print('middle', middleHigh.bh, middleLow.bh, middleCarry.bh);
|
||||
print('low', high.bh);
|
||||
|
||||
const [lowSum, lowCarry] = addOverflowing(low, middleLow);
|
||||
print('lowCarry', lowCarry.bh);
|
||||
const highSum = add(high, middleHigh, middleCarry, rshift(lowCarry, 24));
|
||||
|
||||
const resultValue = (BigInt(highSum.e) * (2n ** 24n)) + BigInt(lowSum.e);
|
||||
|
||||
print('result', highSum.bs, lowSum.bs, highSum.bh + ' /' + lowSum.bh, resultValue, productBigInt);
|
||||
return resultValue === productBigInt;
|
||||
}
|
||||
|
||||
function print(...values: Array<any>) {
|
||||
console.log(values.join('\n') + '\n');
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue