The way this multiply code uses 'bit' is called
bit masking. In addition to bit testing, bit masking can set or clear bits in a variable.
let x = x | bit; // Set bit
let y = y & ~bit; // Clear bit
You can also set or clear multiple bits at once. For example, in the screen I/O code you might have a pointer into the middle of a line and want to do something at a different offset in the same line. You need to get back to the beginning of the line and add the new offset. One way to do this would be
let linePtr = (linePtr/32)*32) + newOffset;
Since all screen rows begin at a multiple of 32, their addresses always have bits 0-4 = 0. Bit masking can clear those bits much faster than the division and multiplication used above!
let linePtr = (linePtr & ~31) + newOffset;
The bit based multiply code posted above always loops 16 times. It would be nice if it was faster when multiplying by small numbers; there are lots of x*2, x*32, etc. in the screen code.
You can use bit masking to clear the 'y' bits as you process then and break the loop when 'y' becomes 0.
A nice refinement would be to swap 'x' and 'y' in cases like 3*x, but the overhead to do that might slow down the general case enough to overwhelm the average performance, especially if it needs to call Math.abs().
Here are some other examples -- right shifting and bit reversal:
/// UNSIGNED right shift 'x' one bit.
function int shr(int x) {
var int y; // = 0
var int i; // = 0
while (i < 15) {
let y = y+y; // y << 1
if (x < 0) { // Test x & 0x8000.
let y = y | 1;
}
let x = x+x; // x << 1
let i = i+1;
}
return y;
}
/// Reverse the bits in 'x'.
function int rev(int x) {
var int y; // = 0
var int bit;
let bit = 1;
while ( ~ (bit = 0)) {
if (x < 0) { // Test x & 0x8000.
let y = y | bit;
}
let bit = bit+bit; // bit << 1
let x = x+x; // x << 1
}
return y;
}
--Mark