<NME> ps2.js
<BEF> "use strict";

/** @const */
let PS2_LOG_VERBOSE = false;

/**
 * @constructor
 * @param {CPU} cpu
 * @param {BusConnector} bus
 */
function PS2(cpu, bus)
{
    /** @const @type {CPU} */
    this.cpu = cpu;

    /** @const @type {BusConnector} */
    this.bus = bus;

    /** @type {boolean} */
    this.enable_mouse_stream = false;

    /** @type {boolean} */
    this.use_mouse = false;

    /** @type {boolean} */
    this.have_mouse = true;

    /** @type {number} */
    this.mouse_delta_x = 0;
    /** @type {number} */
    this.mouse_delta_y = 0;
    /** @type {number} */
    this.mouse_clicks = 0;

    /** @type {boolean} */
    this.have_keyboard = true;

    /** @type {boolean} */
    this.enable_keyboard_stream = false;

    /** @type {boolean} */
    this.next_is_mouse_command = false;

    /** @type {boolean} */
    this.next_read_sample = false;

    /** @type {boolean} */
    this.next_read_led = false;

    /** @type {boolean} */
    this.next_handle_scan_code_set = false;

        sample_rate = 100,

        /** @type {number} */
        resolution = 1,

        /** @type {number} */
        last_mouse_packet = -1,

    this.last_port60_byte = 0;

    /** @type {number} */
    this.sample_rate = 100;

    /** @type {number} */
    this.mouse_detect_state = 0;

    /** @type {number} */
    this.mouse_id = 0x00;

    /** @type {boolean} */
    this.mouse_reset_workaround = false;

    /** @type {number} */
    this.wheel_movement = 0;

    /** @type {number} */
    this.resolution = 4;

    /** @type {boolean} */
    this.scaling2 = false;

    /** @type {number} */
    this.last_mouse_packet = -1;

    /**
     * @type {ByteQueue}
     */
    this.mouse_buffer = new ByteQueue(1024);

    /**
     * @type {boolean}
     * Also known as DBBOUT OBF - Output Buffer Full flag
     */
    this.next_byte_is_ready = false;

    /** @type {boolean} */
    this.next_byte_is_aux = false;

    this.bus.register("keyboard-code", function(code)
    {
        this.kbd_send_code(code);
    }, this);

    {
        if(have_mouse && enable_mouse)
        {
            mouse_delta_x += delta_x;
            mouse_delta_y += delta_y;

            if(enable_mouse_stream)
            {
    }, this);

    this.bus.register("mouse-wheel", function(data)
    {
        this.wheel_movement -= data[0];
        this.wheel_movement -= data[1] * 2; // X Wheel Movement
        this.wheel_movement = Math.min(7, Math.max(-8, this.wheel_movement));
        this.send_mouse_packet(0, 0);
    }, this);

    this.command_register = 1 | 4;
    // TODO: What should be the initial value?
    this.controller_output_port = 0;
    this.read_output_register = false;
    this.read_command_register = false;
    this.read_controller_output_port = false;

    cpu.io.register_read(0x60, this, this.port60_read);
    cpu.io.register_read(0x64, this, this.port64_read);

    cpu.io.register_write(0x60, this, this.port60_write);
    cpu.io.register_write(0x64, this, this.port64_write);
}

PS2.prototype.get_state = function()
{
    var state = [];

    state[0] = this.enable_mouse_stream;
    state[1] = this.use_mouse;
    state[2] = this.have_mouse;
    state[3] = this.mouse_delta_x;
                (mouse_delta_y < 0) << 5 |
                (mouse_delta_x < 0) << 4 |
                1 << 3 | 
                mouse_clicks;

        last_mouse_packet = Date.now();

        mouse_buffer.push(info_byte);
        mouse_buffer.push(mouse_delta_x);
        mouse_buffer.push(mouse_delta_y);

        dbg_log("adding mouse packets:" + [info_byte, mouse_delta_x, mouse_delta_y], LOG_PS2);

    state[26] = this.mouse_detect_state;
    state[27] = this.mouse_reset_workaround;

        mouse_irq();
    }

    this.destroy = function()
    {
        if(have_keyboard)
    this.read_controller_output_port = state[24];
    this.mouse_id = state[25] || 0;
    this.mouse_detect_state = state[26] || 0;
    this.mouse_reset_workaround = state[27] || false;

    this.next_byte_is_ready = false;
    this.next_byte_is_aux = false;
    this.kbd_buffer.clear();
    this.mouse_buffer.clear();

    this.bus.send("mouse-enable", this.use_mouse);
};

PS2.prototype.raise_irq = function()
{
    if(this.next_byte_is_ready)
    {
        // Wait until previous byte is read
        // http://halicery.com/Hardware/8042/8042_1503033_TXT.htm
        return;
    }

    // Kbd has priority over aux
    if(this.kbd_buffer.length)
    {
        this.kbd_irq();
    }
    else if(this.mouse_buffer.length)
    {
        this.mouse_irq();
    }
};

PS2.prototype.mouse_irq = function()
{
    this.next_byte_is_ready = true;
    this.next_byte_is_aux = true;

    if(this.command_register & 2)
    {
        dbg_log("Mouse irq", LOG_PS2);

        // Pulse the irq line
        // Note: can't lower immediately after rising, so lower before rising
        // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/
        this.cpu.device_lower_irq(12);
        this.cpu.device_raise_irq(12);
    }
};

PS2.prototype.kbd_irq = function()
{
    this.next_byte_is_ready = true;
    this.next_byte_is_aux = false;

    if(this.command_register & 1)
    {
        dbg_log("Keyboard irq", LOG_PS2);

        // Pulse the irq line
        // Note: can't lower immediately after rising, so lower before rising
        // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/
        this.cpu.device_lower_irq(1);
        this.cpu.device_raise_irq(1);
    }
};

PS2.prototype.kbd_send_code = function(code)
{
    if(this.enable_keyboard_stream)
    {
        dbg_log("adding kbd code: " + h(code), LOG_PS2);
        this.kbd_buffer.push(code);
        this.raise_irq();
    }
};

PS2.prototype.mouse_send_delta = function(delta_x, delta_y)
{
    if(!this.have_mouse || !this.use_mouse)
    {
        return;
    }

    // note: delta_x or delta_y can be floating point numbers

    var factor = this.resolution * this.sample_rate / 80;

    this.mouse_delta_x += delta_x * factor;
    this.mouse_delta_y += delta_y * factor;

    if(this.enable_mouse_stream)
    {
        var change_x = this.mouse_delta_x | 0,
            change_y = this.mouse_delta_y | 0;

        if(change_x || change_y)
        {
            var now = Date.now();

            //if(now - this.last_mouse_packet < 1000 / this.sample_rate)
            //{
            //    // TODO: set timeout
            //    return;
            //}

            this.mouse_delta_x -= change_x;
            this.mouse_delta_y -= change_y;

            this.send_mouse_packet(change_x, change_y);
        }
    }
};

PS2.prototype.mouse_send_click = function(left, middle, right)
{
    if(!this.have_mouse || !this.use_mouse)
    {
        return;
    }

    this.mouse_clicks = left | right << 1 | middle << 2;

    if(this.enable_mouse_stream)
    {
        this.send_mouse_packet(0, 0);
    }
};

PS2.prototype.send_mouse_packet = function(dx, dy)

            if(write_byte > 3)
            {
                dbg_log("invalid resolution, resetting to 1", LOG_PS2);
            }
            else
            {

    this.last_mouse_packet = Date.now();

    //if(this.scaling2)
    //{
    //    // only in automatic packets, not 0xEB requests
    //    delta_x = this.apply_scaling2(delta_x);
    //    delta_y = this.apply_scaling2(delta_y);
    //}

    this.mouse_buffer.push(info_byte);
    this.mouse_buffer.push(delta_x);
    this.mouse_buffer.push(delta_y);

    if(this.mouse_id === 0x04)
    {
        this.mouse_buffer.push(
            0 << 5 | // TODO: 5th button
            0 << 4 | // TODO: 4th button
            this.wheel_movement & 0x0F
        );
        this.wheel_movement = 0;
    }
    else if(this.mouse_id === 0x03)
    {
        this.mouse_buffer.push(this.wheel_movement & 0xFF); // Byte 4 - Z Movement
        this.wheel_movement = 0;
    }

    if(PS2_LOG_VERBOSE)
    {
        dbg_log("adding mouse packets: " + [info_byte, dx, dy], LOG_PS2);
            switch(write_byte)
            {
            case 0xE6:
                  // set scaling to 1:1
                break;
            case 0xE7:
                  // set scaling to 2:1
                break;
            case 0xE8:
                // set mouse resolution
                next_read_resolution = true;
                break;
            case 0xE9:
                  // status request - send one packet
                send_mouse_packet();
                break;
            case 0xEB:
        case 5:
                dbg_log("unimplemented request single packet", LOG_PS2);
                break;
            case 0xF2:
                  //  MouseID Byte
                mouse_buffer.push(0);
                mouse_buffer.push(0);
                break;
{
    //dbg_log("port 60 read: " + (buffer[0] || "(none)"));

    this.next_byte_is_ready = false;

    if(!this.kbd_buffer.length && !this.mouse_buffer.length)
    {
        // should not happen
        dbg_log("Port 60 read: Empty", LOG_PS2);
        return this.last_port60_byte;
    }

    if(this.next_byte_is_aux)
    {
        this.cpu.device_lower_irq(12);
                // reset defaults 
                enable_mouse_stream = false;
                sample_rate = 100;
                // ... resolution, scaling
                break;
            case 0xFF:
                // reset, send completion code
    }

    if(this.kbd_buffer.length || this.mouse_buffer.length)
    {
        this.raise_irq();
    }

    return this.last_port60_byte;
};

PS2.prototype.port64_read = function()
{
    // status port

    var status_byte = 0x10;

    if(this.next_byte_is_ready)
    {
        status_byte |= 0x1;
    }
    if(this.next_byte_is_aux)
    {
        status_byte |= 0x20;
    }

    dbg_log("port 64 read: " + h(status_byte), LOG_PS2);

    return status_byte;
};

PS2.prototype.port60_write = function(write_byte)
{
    dbg_log("port 60 write: " + h(write_byte), LOG_PS2);

    if(this.read_command_register)
    {
        this.command_register = write_byte;
        this.read_command_register = false;

        // not sure, causes "spurious ack" in Linux
        //this.kbd_buffer.push(0xFA);
        //this.kbd_irq();

        dbg_log("Keyboard command register = " + h(this.command_register), LOG_PS2);
    }
    else if(this.read_output_register)
    {
        this.read_output_register = false;

        this.mouse_buffer.clear();
        this.mouse_buffer.push(write_byte);
        this.mouse_irq();
    }
    else if(this.next_read_sample)
    {
        this.next_read_sample = false;
        this.mouse_buffer.clear();
        this.mouse_buffer.push(0xFA);

        this.sample_rate = write_byte;

        switch(this.mouse_detect_state)
        {
            case -1:
                if(write_byte === 60)
                {
                    // Detect Windows NT and turn on workaround the bug
                    // 200->100->80->60
                    this.mouse_reset_workaround = true;
                    this.mouse_detect_state = 0;
                }
                else
                {
                    this.mouse_reset_workaround = false;
                    this.mouse_detect_state = (write_byte === 200) ? 1 : 0;
                }
                break;
            case 0:
                if(write_byte === 200) this.mouse_detect_state = 1;
                break;
            case 1:
                if(write_byte === 100) this.mouse_detect_state = 2;
                else if(write_byte === 200) this.mouse_detect_state = 3;
                else this.mouse_detect_state = 0;
                break;
            case 2:
                // Host sends sample rate 200->100->80 to activate Intellimouse wheel
                if(write_byte === 80) this.mouse_id = 0x03;
                this.mouse_detect_state = -1;
                break;
            case 3:
                // Host sends sample rate 200->200->80 to activate Intellimouse 4th, 5th buttons
                if(write_byte === 80) this.mouse_id = 0x04;
                this.mouse_detect_state = -1;
                break;
        }

        dbg_log("mouse sample rate: " + h(write_byte) + ", mouse id: " + h(this.mouse_id), LOG_PS2);

        if(!this.sample_rate)
        {
            dbg_log("invalid sample rate, reset to 100", LOG_PS2);
            this.sample_rate = 100;
        }

        this.mouse_irq();
    }
    else if(this.next_read_resolution)
    {
        this.next_read_resolution = false;
        this.mouse_buffer.clear();
        this.mouse_buffer.push(0xFA);

        if(write_byte > 3)
        {
            this.resolution = 4;
            dbg_log("invalid resolution, resetting to 4", LOG_PS2);
        }
        else
        {
            this.resolution = 1 << write_byte;
            dbg_log("resolution: " + this.resolution, LOG_PS2);
        }
        this.mouse_irq();
    }
    else if(this.next_read_led)
    {
        // nope
        this.next_read_led = false;
        this.kbd_buffer.push(0xFA);
        this.kbd_irq();
    }
    else if(this.next_handle_scan_code_set)
    {
        this.next_handle_scan_code_set = false;

        this.kbd_buffer.push(0xFA);
        this.kbd_irq();

        if(write_byte)
        {
            // set scan code set
        }
        else
        {
            this.kbd_buffer.push(2);
        }
    }
    else if(this.next_read_rate)
    {
        // nope
        this.next_read_rate = false;
        this.kbd_buffer.push(0xFA);
        this.kbd_irq();
    }
    else if(this.next_is_mouse_command)
    {
        this.next_is_mouse_command = false;
        dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);

        if(!this.have_mouse)
        {
            return;
        }

        // send ack
        this.kbd_buffer.clear();
        this.mouse_buffer.clear();
        this.mouse_buffer.push(0xFA);

        switch(write_byte)
        {
        case 0xE6:
            // set scaling to 1:1
            dbg_log("Scaling 1:1", LOG_PS2);
            this.scaling2 = false;
            break;
        case 0xE7:
            // set scaling to 2:1
            dbg_log("Scaling 2:1", LOG_PS2);
            this.scaling2 = true;
            break;
        case 0xE8:
            // set mouse resolution
            this.next_read_resolution = true;
            break;
        case 0xE9:
            // status request - send one packet
            this.send_mouse_packet(0, 0);
            break;
        case 0xEB:
            // request single packet
            dbg_log("unimplemented request single packet", LOG_PS2);
            this.send_mouse_packet(0, 0);
            break;
        case 0xF2:
            //  MouseID Byte
            dbg_log("required id: " + h(this.mouse_id), LOG_PS2);
            this.mouse_buffer.push(this.mouse_id);

            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
            // this.send_mouse_packet(0, 0);
            this.raise_irq();
            break;
        case 0xF3:
            // sample rate
            this.next_read_sample = true;
            break;
        case 0xF4:
            // enable streaming
            this.enable_mouse_stream = true;
            this.use_mouse = true;
            this.bus.send("mouse-enable", true);

            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
            break;
        case 0xF5:
            // disable streaming
            this.enable_mouse_stream = false;
            break;
        case 0xF6:
            // set defaults
            this.enable_mouse_stream = false;
            this.sample_rate = 100;
            this.scaling2 = false;
            this.resolution = 4;
            break;
        case 0xFF:
            // reset, send completion code
            dbg_log("Mouse reset", LOG_PS2);
            this.mouse_buffer.push(0xAA);
            this.mouse_buffer.push(0);

            this.use_mouse = true;
            this.bus.send("mouse-enable", true);

            this.enable_mouse_stream = false;
            this.sample_rate = 100;
            this.scaling2 = false;
            this.resolution = 4;

            if(!this.mouse_reset_workaround)
            {
                this.mouse_id = 0x00;
            }

            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
            break;

        default:
            dbg_log("Unimplemented mouse command: " + h(write_byte), LOG_PS2);
        }

        this.mouse_irq();
    }
    else if(this.read_controller_output_port)
    {
        this.read_controller_output_port = false;
        this.controller_output_port = write_byte;
        // If we ever want to implement A20 masking, here is where
        // we should turn the masking off if the second bit is on
    }
    else
    {
        dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);

        // send ack
        this.mouse_buffer.clear();
        this.kbd_buffer.clear();
        this.kbd_buffer.push(0xFA);

        switch(write_byte)
        {
        case 0xED:
            this.next_read_led = true;
            break;
        case 0xF0:
            // get/set scan code set
            this.next_handle_scan_code_set = true;
            break;
        case 0xF2:
            // identify
            this.kbd_buffer.push(0xAB);
            this.kbd_buffer.push(83);
            break;
        case 0xF3:
            //  Set typematic rate and delay
            this.next_read_rate = true;
            break;
        case 0xF4:
            // enable scanning
            dbg_log("kbd enable scanning", LOG_PS2);
            this.enable_keyboard_stream = true;
            break;
        case 0xF5:
            // disable scanning
            dbg_log("kbd disable scanning", LOG_PS2);
            this.enable_keyboard_stream = false;
            break;
        case 0xF6:
            // reset defaults
            //this.enable_keyboard_stream = false;
            break;
        case 0xFF:
            this.kbd_buffer.clear();
            this.kbd_buffer.push(0xFA);
            this.kbd_buffer.push(0xAA);
            this.kbd_buffer.push(0);
            break;
        default:
            dbg_log("Unimplemented keyboard command: " + h(write_byte), LOG_PS2);
        }

        this.kbd_irq();
    }
};

PS2.prototype.port64_write = function(write_byte)
{
    dbg_log("port 64 write: " + h(write_byte), LOG_PS2);

    switch(write_byte)
    {
    case 0x20:
        this.kbd_buffer.clear();
        this.mouse_buffer.clear();
        this.kbd_buffer.push(this.command_register);
        this.kbd_irq();
        break;
    case 0x60:
        this.read_command_register = true;
        break;
    case 0xD1:
        this.read_controller_output_port = true;
        break;
    case 0xD3:
        this.read_output_register = true;
        break;
    case 0xD4:
        this.next_is_mouse_command = true;
        break;
    case 0xA7:
        // Disable second port
        dbg_log("Disable second port", LOG_PS2);
        this.command_register |= 0x20;
        break;
    case 0xA8:
        // Enable second port
        dbg_log("Enable second port", LOG_PS2);
        this.command_register &= ~0x20;
        break;
    case 0xA9:
        // test second ps/2 port
        this.kbd_buffer.clear();
        this.mouse_buffer.clear();
        this.kbd_buffer.push(0);
        this.kbd_irq();
        break;
    case 0xAA:
        this.kbd_buffer.clear();
        this.mouse_buffer.clear();
        this.kbd_buffer.push(0x55);
        this.kbd_irq();
        break;
    case 0xAB:
        // Test first PS/2 port
        this.kbd_buffer.clear();
        this.mouse_buffer.clear();
        this.kbd_buffer.push(0);
        this.kbd_irq();
        break;
    case 0xAD:
        // Disable Keyboard
        dbg_log("Disable Keyboard", LOG_PS2);
        this.command_register |= 0x10;
        break;
    case 0xAE:
        // Enable Keyboard
        dbg_log("Enable Keyboard", LOG_PS2);
        this.command_register &= ~0x10;
        break;
    case 0xFE:
        dbg_log("CPU reboot via PS2");
        this.cpu.reboot_internal();
        break;
    default:
        dbg_log("port 64: Unimplemented command byte: " + h(write_byte), LOG_PS2);
    }
};

<MSG> resolution and scaling

<DFF> @@ -53,7 +53,10 @@ function PS2(dev, keyboard, mouse)
         sample_rate = 100,
 
         /** @type {number} */
-        resolution = 1,
+        resolution = 4,
+
+        /** @type {boolean} */
+        scaling2 = false,
 
         /** @type {number} */
         last_mouse_packet = -1,
@@ -106,8 +109,8 @@ function PS2(dev, keyboard, mouse)
     {
         if(have_mouse && enable_mouse)
         {
-            mouse_delta_x += delta_x;
-            mouse_delta_y += delta_y;
+            mouse_delta_x += delta_x * resolution / 4 | 0;
+            mouse_delta_y += delta_y * resolution / 4 | 0;
 
             if(enable_mouse_stream)
             {
@@ -146,13 +149,22 @@ function PS2(dev, keyboard, mouse)
                 (mouse_delta_y < 0) << 5 |
                 (mouse_delta_x < 0) << 4 |
                 1 << 3 | 
-                mouse_clicks;
+                mouse_clicks,
+            delta_x = mouse_delta_x,
+            delta_y = mouse_delta_y;
 
         last_mouse_packet = Date.now();
 
+        if(scaling2)
+        {
+            // only in automatic packets, not 0xEB requests
+            delta_x = apply_scaling2(delta_x);
+            delta_y = apply_scaling2(delta_y);
+        }
+
         mouse_buffer.push(info_byte);
-        mouse_buffer.push(mouse_delta_x);
-        mouse_buffer.push(mouse_delta_y);
+        mouse_buffer.push(delta_x);
+        mouse_buffer.push(delta_y);
 
         dbg_log("adding mouse packets:" + [info_byte, mouse_delta_x, mouse_delta_y], LOG_PS2);
 
@@ -162,6 +174,29 @@ function PS2(dev, keyboard, mouse)
         mouse_irq();
     }
 
+    function apply_scaling2(n)
+    {
+        // http://www.computer-engineering.org/ps2mouse/#Inputs.2C_Resolution.2C_and_Scaling
+        var abs = Math.abs(n),
+            sign = n >> 31;
+
+        switch(abs)
+        {
+            case 0:
+            case 1:
+            case 3:
+                return n;
+            case 2:
+                return sign;
+            case 4: 
+                return 6 * sign;
+            case 5:
+                return 9 * sign;
+            default:
+                return n << 1;
+        }
+    }
+
     this.destroy = function()
     {
         if(have_keyboard)
@@ -298,7 +333,8 @@ function PS2(dev, keyboard, mouse)
 
             if(write_byte > 3)
             {
-                dbg_log("invalid resolution, resetting to 1", LOG_PS2);
+                resolution = 4;
+                dbg_log("invalid resolution, resetting to 4", LOG_PS2);
             }
             else
             {
@@ -337,17 +373,21 @@ function PS2(dev, keyboard, mouse)
             switch(write_byte)
             {
             case 0xE6:
-                  // set scaling to 1:1
+                // set scaling to 1:1
+                dbg_log("Scaling 1:1", LOG_PS2);
+                scaling2 = false;
                 break;
             case 0xE7:
-                  // set scaling to 2:1
+                // set scaling to 2:1
+                dbg_log("Scaling 2:1", LOG_PS2);
+                scaling2 = true;
                 break;
             case 0xE8:
                 // set mouse resolution
                 next_read_resolution = true;
                 break;
             case 0xE9:
-                  // status request - send one packet
+                // status request - send one packet
                 send_mouse_packet();
                 break;
             case 0xEB:
@@ -355,7 +395,7 @@ function PS2(dev, keyboard, mouse)
                 dbg_log("unimplemented request single packet", LOG_PS2);
                 break;
             case 0xF2:
-                  //  MouseID Byte
+                //  MouseID Byte
                 mouse_buffer.push(0);
                 mouse_buffer.push(0);
                 break;
@@ -377,7 +417,8 @@ function PS2(dev, keyboard, mouse)
                 // reset defaults 
                 enable_mouse_stream = false;
                 sample_rate = 100;
-                // ... resolution, scaling
+                scaling2 = false;
+                resolution = 4;
                 break;
             case 0xFF:
                 // reset, send completion code

{'commit': 'diff --git a/src/ps2.js b/src/ps2.js\nindex e0e32441..d7cf6dd1 100644\n--- a/src/ps2.js\n+++ b/src/ps2.js\n@@ -53,7 +53,10 @@ function PS2(dev, keyboard, mouse)\n         sample_rate = 100,\n \n         /** @type {number} */\n-        resolution = 1,\n+        resolution = 4,\n+\n+        /** @type {boolean} */\n+        scaling2 = false,\n \n         /** @type {number} */\n         last_mouse_packet = -1,\n@@ -106,8 +109,8 @@ function PS2(dev, keyboard, mouse)\n     {\n         if(have_mouse && enable_mouse)\n         {\n-            mouse_delta_x += delta_x;\n-            mouse_delta_y += delta_y;\n+            mouse_delta_x += delta_x * resolution / 4 | 0;\n+            mouse_delta_y += delta_y * resolution / 4 | 0;\n \n             if(enable_mouse_stream)\n             {\n@@ -146,13 +149,22 @@ function PS2(dev, keyboard, mouse)\n                 (mouse_delta_y < 0) << 5 |\n                 (mouse_delta_x < 0) << 4 |\n                 1 << 3 | \n-                mouse_clicks;\n+                mouse_clicks,\n+            delta_x = mouse_delta_x,\n+            delta_y = mouse_delta_y;\n \n         last_mouse_packet = Date.now();\n \n+        if(scaling2)\n+        {\n+            // only in automatic packets, not 0xEB requests\n+            delta_x = apply_scaling2(delta_x);\n+            delta_y = apply_scaling2(delta_y);\n+        }\n+\n         mouse_buffer.push(info_byte);\n-        mouse_buffer.push(mouse_delta_x);\n-        mouse_buffer.push(mouse_delta_y);\n+        mouse_buffer.push(delta_x);\n+        mouse_buffer.push(delta_y);\n \n         dbg_log("adding mouse packets:" + [info_byte, mouse_delta_x, mouse_delta_y], LOG_PS2);\n \n@@ -162,6 +174,29 @@ function PS2(dev, keyboard, mouse)\n         mouse_irq();\n     }\n \n+    function apply_scaling2(n)\n+    {\n+        // http://www.computer-engineering.org/ps2mouse/#Inputs.2C_Resolution.2C_and_Scaling\n+        var abs = Math.abs(n),\n+            sign = n >> 31;\n+\n+        switch(abs)\n+        {\n+            case 0:\n+            case 1:\n+            case 3:\n+                return n;\n+            case 2:\n+                return sign;\n+            case 4: \n+                return 6 * sign;\n+            case 5:\n+                return 9 * sign;\n+            default:\n+                return n << 1;\n+        }\n+    }\n+\n     this.destroy = function()\n     {\n         if(have_keyboard)\n@@ -298,7 +333,8 @@ function PS2(dev, keyboard, mouse)\n \n             if(write_byte > 3)\n             {\n-                dbg_log("invalid resolution, resetting to 1", LOG_PS2);\n+                resolution = 4;\n+                dbg_log("invalid resolution, resetting to 4", LOG_PS2);\n             }\n             else\n             {\n@@ -337,17 +373,21 @@ function PS2(dev, keyboard, mouse)\n             switch(write_byte)\n             {\n             case 0xE6:\n-                  // set scaling to 1:1\n+                // set scaling to 1:1\n+                dbg_log("Scaling 1:1", LOG_PS2);\n+                scaling2 = false;\n                 break;\n             case 0xE7:\n-                  // set scaling to 2:1\n+                // set scaling to 2:1\n+                dbg_log("Scaling 2:1", LOG_PS2);\n+                scaling2 = true;\n                 break;\n             case 0xE8:\n                 // set mouse resolution\n                 next_read_resolution = true;\n                 break;\n             case 0xE9:\n-                  // status request - send one packet\n+                // status request - send one packet\n                 send_mouse_packet();\n                 break;\n             case 0xEB:\n@@ -355,7 +395,7 @@ function PS2(dev, keyboard, mouse)\n                 dbg_log("unimplemented request single packet", LOG_PS2);\n                 break;\n             case 0xF2:\n-                  //  MouseID Byte\n+                //  MouseID Byte\n                 mouse_buffer.push(0);\n                 mouse_buffer.push(0);\n                 break;\n@@ -377,7 +417,8 @@ function PS2(dev, keyboard, mouse)\n                 // reset defaults \n                 enable_mouse_stream = false;\n                 sample_rate = 100;\n-                // ... resolution, scaling\n+                scaling2 = false;\n+                resolution = 4;\n                 break;\n             case 0xFF:\n                 // reset, send completion code\n', 'commit_subject': 'resolution and scaling', 'repo_name': 'copy/v86', 'committer': {'name': 'copy', 'email': 'f84e2e2dadd87384fb55f25886926b777e8378f1@copy.sh', 'time_sec': '1392770962', 'tz_offset': '60', 'date': {'seconds': '1392770962'}}, 'commit_tree': 'd84e8076a0745c5d87d342188b85a43ad8660bae', 'commit_message': 'resolution and scaling\n', 'language_name': 'JavaScript', 'license': 'bsd-2-clause', 'hunks': '@@ -53,7 +53,10 @@ function PS2(dev, keyboard, mouse)\n         sample_rate = 100,\n \n         /** @type {number} */\n-        resolution = 1,\n+        resolution = 4,\n+\n+        /** @type {boolean} */\n+        scaling2 = false,\n \n         /** @type {number} */\n         last_mouse_packet = -1,\n@@ -106,8 +109,8 @@ function PS2(dev, keyboard, mouse)\n     {\n         if(have_mouse && enable_mouse)\n         {\n-            mouse_delta_x += delta_x;\n-            mouse_delta_y += delta_y;\n+            mouse_delta_x += delta_x * resolution / 4 | 0;\n+            mouse_delta_y += delta_y * resolution / 4 | 0;\n \n             if(enable_mouse_stream)\n             {\n@@ -146,13 +149,22 @@ function PS2(dev, keyboard, mouse)\n                 (mouse_delta_y < 0) << 5 |\n                 (mouse_delta_x < 0) << 4 |\n                 1 << 3 | \n-                mouse_clicks;\n+                mouse_clicks,\n+            delta_x = mouse_delta_x,\n+            delta_y = mouse_delta_y;\n \n         last_mouse_packet = Date.now();\n \n+        if(scaling2)\n+        {\n+            // only in automatic packets, not 0xEB requests\n+            delta_x = apply_scaling2(delta_x);\n+            delta_y = apply_scaling2(delta_y);\n+        }\n+\n         mouse_buffer.push(info_byte);\n-        mouse_buffer.push(mouse_delta_x);\n-        mouse_buffer.push(mouse_delta_y);\n+        mouse_buffer.push(delta_x);\n+        mouse_buffer.push(delta_y);\n \n         dbg_log("adding mouse packets:" + [info_byte, mouse_delta_x, mouse_delta_y], LOG_PS2);\n \n@@ -162,6 +174,29 @@ function PS2(dev, keyboard, mouse)\n         mouse_irq();\n     }\n \n+    function apply_scaling2(n)\n+    {\n+        // http://www.computer-engineering.org/ps2mouse/#Inputs.2C_Resolution.2C_and_Scaling\n+        var abs = Math.abs(n),\n+            sign = n >> 31;\n+\n+        switch(abs)\n+        {\n+            case 0:\n+            case 1:\n+            case 3:\n+                return n;\n+            case 2:\n+                return sign;\n+            case 4: \n+                return 6 * sign;\n+            case 5:\n+                return 9 * sign;\n+            default:\n+                return n << 1;\n+        }\n+    }\n+\n     this.destroy = function()\n     {\n         if(have_keyboard)\n@@ -298,7 +333,8 @@ function PS2(dev, keyboard, mouse)\n \n             if(write_byte > 3)\n             {\n-                dbg_log("invalid resolution, resetting to 1", LOG_PS2);\n+                resolution = 4;\n+                dbg_log("invalid resolution, resetting to 4", LOG_PS2);\n             }\n             else\n             {\n@@ -337,17 +373,21 @@ function PS2(dev, keyboard, mouse)\n             switch(write_byte)\n             {\n             case 0xE6:\n-                  // set scaling to 1:1\n+                // set scaling to 1:1\n+                dbg_log("Scaling 1:1", LOG_PS2);\n+                scaling2 = false;\n                 break;\n             case 0xE7:\n-                  // set scaling to 2:1\n+                // set scaling to 2:1\n+                dbg_log("Scaling 2:1", LOG_PS2);\n+                scaling2 = true;\n                 break;\n             case 0xE8:\n                 // set mouse resolution\n                 next_read_resolution = true;\n                 break;\n             case 0xE9:\n-                  // status request - send one packet\n+                // status request - send one packet\n                 send_mouse_packet();\n                 break;\n             case 0xEB:\n@@ -355,7 +395,7 @@ function PS2(dev, keyboard, mouse)\n                 dbg_log("unimplemented request single packet", LOG_PS2);\n                 break;\n             case 0xF2:\n-                  //  MouseID Byte\n+                //  MouseID Byte\n                 mouse_buffer.push(0);\n                 mouse_buffer.push(0);\n                 break;\n@@ -377,7 +417,8 @@ function PS2(dev, keyboard, mouse)\n                 // reset defaults \n                 enable_mouse_stream = false;\n                 sample_rate = 100;\n-                // ... resolution, scaling\n+                scaling2 = false;\n+                resolution = 4;\n                 break;\n             case 0xFF:\n                 // reset, send completion code\n', 'addition_count': 53, 'deletion_count': 12, 'src_file': 'a/src/ps2.js', 'tgt_file': 'b/src/ps2.js', 'file_extension': '.js', 'before_file': '"use strict";\n\n/** @const */\nlet PS2_LOG_VERBOSE = false;\n\n/**\n * @constructor\n * @param {CPU} cpu\n * @param {BusConnector} bus\n */\nfunction PS2(cpu, bus)\n{\n    /** @const @type {CPU} */\n    this.cpu = cpu;\n\n    /** @const @type {BusConnector} */\n    this.bus = bus;\n\n    /** @type {boolean} */\n    this.enable_mouse_stream = false;\n\n    /** @type {boolean} */\n    this.use_mouse = false;\n\n    /** @type {boolean} */\n    this.have_mouse = true;\n\n    /** @type {number} */\n    this.mouse_delta_x = 0;\n    /** @type {number} */\n    this.mouse_delta_y = 0;\n    /** @type {number} */\n    this.mouse_clicks = 0;\n\n    /** @type {boolean} */\n    this.have_keyboard = true;\n\n    /** @type {boolean} */\n    this.enable_keyboard_stream = false;\n\n    /** @type {boolean} */\n    this.next_is_mouse_command = false;\n\n    /** @type {boolean} */\n    this.next_read_sample = false;\n\n    /** @type {boolean} */\n    this.next_read_led = false;\n\n    /** @type {boolean} */\n    this.next_handle_scan_code_set = false;\n\n        sample_rate = 100,\n\n        /** @type {number} */\n        resolution = 1,\n\n        /** @type {number} */\n        last_mouse_packet = -1,\n\n    this.last_port60_byte = 0;\n\n    /** @type {number} */\n    this.sample_rate = 100;\n\n    /** @type {number} */\n    this.mouse_detect_state = 0;\n\n    /** @type {number} */\n    this.mouse_id = 0x00;\n\n    /** @type {boolean} */\n    this.mouse_reset_workaround = false;\n\n    /** @type {number} */\n    this.wheel_movement = 0;\n\n    /** @type {number} */\n    this.resolution = 4;\n\n    /** @type {boolean} */\n    this.scaling2 = false;\n\n    /** @type {number} */\n    this.last_mouse_packet = -1;\n\n    /**\n     * @type {ByteQueue}\n     */\n    this.mouse_buffer = new ByteQueue(1024);\n\n    /**\n     * @type {boolean}\n     * Also known as DBBOUT OBF - Output Buffer Full flag\n     */\n    this.next_byte_is_ready = false;\n\n    /** @type {boolean} */\n    this.next_byte_is_aux = false;\n\n    this.bus.register("keyboard-code", function(code)\n    {\n        this.kbd_send_code(code);\n    }, this);\n\n    {\n        if(have_mouse && enable_mouse)\n        {\n            mouse_delta_x += delta_x;\n            mouse_delta_y += delta_y;\n\n            if(enable_mouse_stream)\n            {\n    }, this);\n\n    this.bus.register("mouse-wheel", function(data)\n    {\n        this.wheel_movement -= data[0];\n        this.wheel_movement -= data[1] * 2; // X Wheel Movement\n        this.wheel_movement = Math.min(7, Math.max(-8, this.wheel_movement));\n        this.send_mouse_packet(0, 0);\n    }, this);\n\n    this.command_register = 1 | 4;\n    // TODO: What should be the initial value?\n    this.controller_output_port = 0;\n    this.read_output_register = false;\n    this.read_command_register = false;\n    this.read_controller_output_port = false;\n\n    cpu.io.register_read(0x60, this, this.port60_read);\n    cpu.io.register_read(0x64, this, this.port64_read);\n\n    cpu.io.register_write(0x60, this, this.port60_write);\n    cpu.io.register_write(0x64, this, this.port64_write);\n}\n\nPS2.prototype.get_state = function()\n{\n    var state = [];\n\n    state[0] = this.enable_mouse_stream;\n    state[1] = this.use_mouse;\n    state[2] = this.have_mouse;\n    state[3] = this.mouse_delta_x;\n                (mouse_delta_y < 0) << 5 |\n                (mouse_delta_x < 0) << 4 |\n                1 << 3 | \n                mouse_clicks;\n\n        last_mouse_packet = Date.now();\n\n        mouse_buffer.push(info_byte);\n        mouse_buffer.push(mouse_delta_x);\n        mouse_buffer.push(mouse_delta_y);\n\n        dbg_log("adding mouse packets:" + [info_byte, mouse_delta_x, mouse_delta_y], LOG_PS2);\n\n    state[26] = this.mouse_detect_state;\n    state[27] = this.mouse_reset_workaround;\n\n        mouse_irq();\n    }\n\n    this.destroy = function()\n    {\n        if(have_keyboard)\n    this.read_controller_output_port = state[24];\n    this.mouse_id = state[25] || 0;\n    this.mouse_detect_state = state[26] || 0;\n    this.mouse_reset_workaround = state[27] || false;\n\n    this.next_byte_is_ready = false;\n    this.next_byte_is_aux = false;\n    this.kbd_buffer.clear();\n    this.mouse_buffer.clear();\n\n    this.bus.send("mouse-enable", this.use_mouse);\n};\n\nPS2.prototype.raise_irq = function()\n{\n    if(this.next_byte_is_ready)\n    {\n        // Wait until previous byte is read\n        // http://halicery.com/Hardware/8042/8042_1503033_TXT.htm\n        return;\n    }\n\n    // Kbd has priority over aux\n    if(this.kbd_buffer.length)\n    {\n        this.kbd_irq();\n    }\n    else if(this.mouse_buffer.length)\n    {\n        this.mouse_irq();\n    }\n};\n\nPS2.prototype.mouse_irq = function()\n{\n    this.next_byte_is_ready = true;\n    this.next_byte_is_aux = true;\n\n    if(this.command_register & 2)\n    {\n        dbg_log("Mouse irq", LOG_PS2);\n\n        // Pulse the irq line\n        // Note: can\'t lower immediately after rising, so lower before rising\n        // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/\n        this.cpu.device_lower_irq(12);\n        this.cpu.device_raise_irq(12);\n    }\n};\n\nPS2.prototype.kbd_irq = function()\n{\n    this.next_byte_is_ready = true;\n    this.next_byte_is_aux = false;\n\n    if(this.command_register & 1)\n    {\n        dbg_log("Keyboard irq", LOG_PS2);\n\n        // Pulse the irq line\n        // Note: can\'t lower immediately after rising, so lower before rising\n        // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/\n        this.cpu.device_lower_irq(1);\n        this.cpu.device_raise_irq(1);\n    }\n};\n\nPS2.prototype.kbd_send_code = function(code)\n{\n    if(this.enable_keyboard_stream)\n    {\n        dbg_log("adding kbd code: " + h(code), LOG_PS2);\n        this.kbd_buffer.push(code);\n        this.raise_irq();\n    }\n};\n\nPS2.prototype.mouse_send_delta = function(delta_x, delta_y)\n{\n    if(!this.have_mouse || !this.use_mouse)\n    {\n        return;\n    }\n\n    // note: delta_x or delta_y can be floating point numbers\n\n    var factor = this.resolution * this.sample_rate / 80;\n\n    this.mouse_delta_x += delta_x * factor;\n    this.mouse_delta_y += delta_y * factor;\n\n    if(this.enable_mouse_stream)\n    {\n        var change_x = this.mouse_delta_x | 0,\n            change_y = this.mouse_delta_y | 0;\n\n        if(change_x || change_y)\n        {\n            var now = Date.now();\n\n            //if(now - this.last_mouse_packet < 1000 / this.sample_rate)\n            //{\n            //    // TODO: set timeout\n            //    return;\n            //}\n\n            this.mouse_delta_x -= change_x;\n            this.mouse_delta_y -= change_y;\n\n            this.send_mouse_packet(change_x, change_y);\n        }\n    }\n};\n\nPS2.prototype.mouse_send_click = function(left, middle, right)\n{\n    if(!this.have_mouse || !this.use_mouse)\n    {\n        return;\n    }\n\n    this.mouse_clicks = left | right << 1 | middle << 2;\n\n    if(this.enable_mouse_stream)\n    {\n        this.send_mouse_packet(0, 0);\n    }\n};\n\nPS2.prototype.send_mouse_packet = function(dx, dy)\n\n            if(write_byte > 3)\n            {\n                dbg_log("invalid resolution, resetting to 1", LOG_PS2);\n            }\n            else\n            {\n\n    this.last_mouse_packet = Date.now();\n\n    //if(this.scaling2)\n    //{\n    //    // only in automatic packets, not 0xEB requests\n    //    delta_x = this.apply_scaling2(delta_x);\n    //    delta_y = this.apply_scaling2(delta_y);\n    //}\n\n    this.mouse_buffer.push(info_byte);\n    this.mouse_buffer.push(delta_x);\n    this.mouse_buffer.push(delta_y);\n\n    if(this.mouse_id === 0x04)\n    {\n        this.mouse_buffer.push(\n            0 << 5 | // TODO: 5th button\n            0 << 4 | // TODO: 4th button\n            this.wheel_movement & 0x0F\n        );\n        this.wheel_movement = 0;\n    }\n    else if(this.mouse_id === 0x03)\n    {\n        this.mouse_buffer.push(this.wheel_movement & 0xFF); // Byte 4 - Z Movement\n        this.wheel_movement = 0;\n    }\n\n    if(PS2_LOG_VERBOSE)\n    {\n        dbg_log("adding mouse packets: " + [info_byte, dx, dy], LOG_PS2);\n            switch(write_byte)\n            {\n            case 0xE6:\n                  // set scaling to 1:1\n                break;\n            case 0xE7:\n                  // set scaling to 2:1\n                break;\n            case 0xE8:\n                // set mouse resolution\n                next_read_resolution = true;\n                break;\n            case 0xE9:\n                  // status request - send one packet\n                send_mouse_packet();\n                break;\n            case 0xEB:\n        case 5:\n                dbg_log("unimplemented request single packet", LOG_PS2);\n                break;\n            case 0xF2:\n                  //  MouseID Byte\n                mouse_buffer.push(0);\n                mouse_buffer.push(0);\n                break;\n{\n    //dbg_log("port 60 read: " + (buffer[0] || "(none)"));\n\n    this.next_byte_is_ready = false;\n\n    if(!this.kbd_buffer.length && !this.mouse_buffer.length)\n    {\n        // should not happen\n        dbg_log("Port 60 read: Empty", LOG_PS2);\n        return this.last_port60_byte;\n    }\n\n    if(this.next_byte_is_aux)\n    {\n        this.cpu.device_lower_irq(12);\n                // reset defaults \n                enable_mouse_stream = false;\n                sample_rate = 100;\n                // ... resolution, scaling\n                break;\n            case 0xFF:\n                // reset, send completion code\n    }\n\n    if(this.kbd_buffer.length || this.mouse_buffer.length)\n    {\n        this.raise_irq();\n    }\n\n    return this.last_port60_byte;\n};\n\nPS2.prototype.port64_read = function()\n{\n    // status port\n\n    var status_byte = 0x10;\n\n    if(this.next_byte_is_ready)\n    {\n        status_byte |= 0x1;\n    }\n    if(this.next_byte_is_aux)\n    {\n        status_byte |= 0x20;\n    }\n\n    dbg_log("port 64 read: " + h(status_byte), LOG_PS2);\n\n    return status_byte;\n};\n\nPS2.prototype.port60_write = function(write_byte)\n{\n    dbg_log("port 60 write: " + h(write_byte), LOG_PS2);\n\n    if(this.read_command_register)\n    {\n        this.command_register = write_byte;\n        this.read_command_register = false;\n\n        // not sure, causes "spurious ack" in Linux\n        //this.kbd_buffer.push(0xFA);\n        //this.kbd_irq();\n\n        dbg_log("Keyboard command register = " + h(this.command_register), LOG_PS2);\n    }\n    else if(this.read_output_register)\n    {\n        this.read_output_register = false;\n\n        this.mouse_buffer.clear();\n        this.mouse_buffer.push(write_byte);\n        this.mouse_irq();\n    }\n    else if(this.next_read_sample)\n    {\n        this.next_read_sample = false;\n        this.mouse_buffer.clear();\n        this.mouse_buffer.push(0xFA);\n\n        this.sample_rate = write_byte;\n\n        switch(this.mouse_detect_state)\n        {\n            case -1:\n                if(write_byte === 60)\n                {\n                    // Detect Windows NT and turn on workaround the bug\n                    // 200->100->80->60\n                    this.mouse_reset_workaround = true;\n                    this.mouse_detect_state = 0;\n                }\n                else\n                {\n                    this.mouse_reset_workaround = false;\n                    this.mouse_detect_state = (write_byte === 200) ? 1 : 0;\n                }\n                break;\n            case 0:\n                if(write_byte === 200) this.mouse_detect_state = 1;\n                break;\n            case 1:\n                if(write_byte === 100) this.mouse_detect_state = 2;\n                else if(write_byte === 200) this.mouse_detect_state = 3;\n                else this.mouse_detect_state = 0;\n                break;\n            case 2:\n                // Host sends sample rate 200->100->80 to activate Intellimouse wheel\n                if(write_byte === 80) this.mouse_id = 0x03;\n                this.mouse_detect_state = -1;\n                break;\n            case 3:\n                // Host sends sample rate 200->200->80 to activate Intellimouse 4th, 5th buttons\n                if(write_byte === 80) this.mouse_id = 0x04;\n                this.mouse_detect_state = -1;\n                break;\n        }\n\n        dbg_log("mouse sample rate: " + h(write_byte) + ", mouse id: " + h(this.mouse_id), LOG_PS2);\n\n        if(!this.sample_rate)\n        {\n            dbg_log("invalid sample rate, reset to 100", LOG_PS2);\n            this.sample_rate = 100;\n        }\n\n        this.mouse_irq();\n    }\n    else if(this.next_read_resolution)\n    {\n        this.next_read_resolution = false;\n        this.mouse_buffer.clear();\n        this.mouse_buffer.push(0xFA);\n\n        if(write_byte > 3)\n        {\n            this.resolution = 4;\n            dbg_log("invalid resolution, resetting to 4", LOG_PS2);\n        }\n        else\n        {\n            this.resolution = 1 << write_byte;\n            dbg_log("resolution: " + this.resolution, LOG_PS2);\n        }\n        this.mouse_irq();\n    }\n    else if(this.next_read_led)\n    {\n        // nope\n        this.next_read_led = false;\n        this.kbd_buffer.push(0xFA);\n        this.kbd_irq();\n    }\n    else if(this.next_handle_scan_code_set)\n    {\n        this.next_handle_scan_code_set = false;\n\n        this.kbd_buffer.push(0xFA);\n        this.kbd_irq();\n\n        if(write_byte)\n        {\n            // set scan code set\n        }\n        else\n        {\n            this.kbd_buffer.push(2);\n        }\n    }\n    else if(this.next_read_rate)\n    {\n        // nope\n        this.next_read_rate = false;\n        this.kbd_buffer.push(0xFA);\n        this.kbd_irq();\n    }\n    else if(this.next_is_mouse_command)\n    {\n        this.next_is_mouse_command = false;\n        dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);\n\n        if(!this.have_mouse)\n        {\n            return;\n        }\n\n        // send ack\n        this.kbd_buffer.clear();\n        this.mouse_buffer.clear();\n        this.mouse_buffer.push(0xFA);\n\n        switch(write_byte)\n        {\n        case 0xE6:\n            // set scaling to 1:1\n            dbg_log("Scaling 1:1", LOG_PS2);\n            this.scaling2 = false;\n            break;\n        case 0xE7:\n            // set scaling to 2:1\n            dbg_log("Scaling 2:1", LOG_PS2);\n            this.scaling2 = true;\n            break;\n        case 0xE8:\n            // set mouse resolution\n            this.next_read_resolution = true;\n            break;\n        case 0xE9:\n            // status request - send one packet\n            this.send_mouse_packet(0, 0);\n            break;\n        case 0xEB:\n            // request single packet\n            dbg_log("unimplemented request single packet", LOG_PS2);\n            this.send_mouse_packet(0, 0);\n            break;\n        case 0xF2:\n            //  MouseID Byte\n            dbg_log("required id: " + h(this.mouse_id), LOG_PS2);\n            this.mouse_buffer.push(this.mouse_id);\n\n            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;\n            // this.send_mouse_packet(0, 0);\n            this.raise_irq();\n            break;\n        case 0xF3:\n            // sample rate\n            this.next_read_sample = true;\n            break;\n        case 0xF4:\n            // enable streaming\n            this.enable_mouse_stream = true;\n            this.use_mouse = true;\n            this.bus.send("mouse-enable", true);\n\n            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;\n            break;\n        case 0xF5:\n            // disable streaming\n            this.enable_mouse_stream = false;\n            break;\n        case 0xF6:\n            // set defaults\n            this.enable_mouse_stream = false;\n            this.sample_rate = 100;\n            this.scaling2 = false;\n            this.resolution = 4;\n            break;\n        case 0xFF:\n            // reset, send completion code\n            dbg_log("Mouse reset", LOG_PS2);\n            this.mouse_buffer.push(0xAA);\n            this.mouse_buffer.push(0);\n\n            this.use_mouse = true;\n            this.bus.send("mouse-enable", true);\n\n            this.enable_mouse_stream = false;\n            this.sample_rate = 100;\n            this.scaling2 = false;\n            this.resolution = 4;\n\n            if(!this.mouse_reset_workaround)\n            {\n                this.mouse_id = 0x00;\n            }\n\n            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;\n            break;\n\n        default:\n            dbg_log("Unimplemented mouse command: " + h(write_byte), LOG_PS2);\n        }\n\n        this.mouse_irq();\n    }\n    else if(this.read_controller_output_port)\n    {\n        this.read_controller_output_port = false;\n        this.controller_output_port = write_byte;\n        // If we ever want to implement A20 masking, here is where\n        // we should turn the masking off if the second bit is on\n    }\n    else\n    {\n        dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);\n\n        // send ack\n        this.mouse_buffer.clear();\n        this.kbd_buffer.clear();\n        this.kbd_buffer.push(0xFA);\n\n        switch(write_byte)\n        {\n        case 0xED:\n            this.next_read_led = true;\n            break;\n        case 0xF0:\n            // get/set scan code set\n            this.next_handle_scan_code_set = true;\n            break;\n        case 0xF2:\n            // identify\n            this.kbd_buffer.push(0xAB);\n            this.kbd_buffer.push(83);\n            break;\n        case 0xF3:\n            //  Set typematic rate and delay\n            this.next_read_rate = true;\n            break;\n        case 0xF4:\n            // enable scanning\n            dbg_log("kbd enable scanning", LOG_PS2);\n            this.enable_keyboard_stream = true;\n            break;\n        case 0xF5:\n            // disable scanning\n            dbg_log("kbd disable scanning", LOG_PS2);\n            this.enable_keyboard_stream = false;\n            break;\n        case 0xF6:\n            // reset defaults\n            //this.enable_keyboard_stream = false;\n            break;\n        case 0xFF:\n            this.kbd_buffer.clear();\n            this.kbd_buffer.push(0xFA);\n            this.kbd_buffer.push(0xAA);\n            this.kbd_buffer.push(0);\n            break;\n        default:\n            dbg_log("Unimplemented keyboard command: " + h(write_byte), LOG_PS2);\n        }\n\n        this.kbd_irq();\n    }\n};\n\nPS2.prototype.port64_write = function(write_byte)\n{\n    dbg_log("port 64 write: " + h(write_byte), LOG_PS2);\n\n    switch(write_byte)\n    {\n    case 0x20:\n        this.kbd_buffer.clear();\n        this.mouse_buffer.clear();\n        this.kbd_buffer.push(this.command_register);\n        this.kbd_irq();\n        break;\n    case 0x60:\n        this.read_command_register = true;\n        break;\n    case 0xD1:\n        this.read_controller_output_port = true;\n        break;\n    case 0xD3:\n        this.read_output_register = true;\n        break;\n    case 0xD4:\n        this.next_is_mouse_command = true;\n        break;\n    case 0xA7:\n        // Disable second port\n        dbg_log("Disable second port", LOG_PS2);\n        this.command_register |= 0x20;\n        break;\n    case 0xA8:\n        // Enable second port\n        dbg_log("Enable second port", LOG_PS2);\n        this.command_register &= ~0x20;\n        break;\n    case 0xA9:\n        // test second ps/2 port\n        this.kbd_buffer.clear();\n        this.mouse_buffer.clear();\n        this.kbd_buffer.push(0);\n        this.kbd_irq();\n        break;\n    case 0xAA:\n        this.kbd_buffer.clear();\n        this.mouse_buffer.clear();\n        this.kbd_buffer.push(0x55);\n        this.kbd_irq();\n        break;\n    case 0xAB:\n        // Test first PS/2 port\n        this.kbd_buffer.clear();\n        this.mouse_buffer.clear();\n        this.kbd_buffer.push(0);\n        this.kbd_irq();\n        break;\n    case 0xAD:\n        // Disable Keyboard\n        dbg_log("Disable Keyboard", LOG_PS2);\n        this.command_register |= 0x10;\n        break;\n    case 0xAE:\n        // Enable Keyboard\n        dbg_log("Enable Keyboard", LOG_PS2);\n        this.command_register &= ~0x10;\n        break;\n    case 0xFE:\n        dbg_log("CPU reboot via PS2");\n        this.cpu.reboot_internal();\n        break;\n    default:\n        dbg_log("port 64: Unimplemented command byte: " + h(write_byte), LOG_PS2);\n    }\n};\n', 'message': 'resolution and scaling\n', 'author_name': 'copy', 'lang': 'js'}