blob: da1a8250acb9fa7dddeafde8f7449e1bf39af798 [file] [log] [blame] [raw]
"use strict";
/**
* @constructor
*/
function PS2(dev, keyboard, mouse)
{
var
io = dev.io,
pic = dev.pic,
me = this,
/** @type {boolean} */
enable_mouse_stream = false,
/** @type {boolean} */
enable_mouse = false,
/** @type {boolean} */
have_mouse = false,
/** @type {number} */
mouse_delta_x = 0,
/** @type {number} */
mouse_delta_y = 0,
/** @type {number} */
mouse_clicks = 0,
/** @type {boolean} */
have_keyboard = false,
/** @type {boolean} */
next_is_mouse_command = false,
/** @type {boolean} */
next_read_sample = false,
/** @type {boolean} */
next_read_led = false,
/**
* @type {Array.<number>}
*/
kbd_buffer = [],
/** @type {number} */
sample_rate = 100,
/** @type {number} */
last_mouse_packet = -1,
/**
* @type {Array.<number>}
*/
mouse_buffer = [];
if(keyboard)
{
have_keyboard = true;
keyboard.init(kbd_send_code);
}
if(mouse)
{
have_mouse = true;
mouse.init(mouse_send_click, mouse_send_delta);
// TODO: Mouse Wheel
// http://www.computer-engineering.org/ps2mouse/
}
function mouse_irq()
{
pic.push_irq(12);
}
function kbd_irq()
{
pic.push_irq(1);
}
function kbd_send_code(code)
{
//console.log(h(code));
kbd_buffer.push(code);
kbd_irq();
}
this.kbd_send_code = kbd_send_code;
function mouse_send_delta(delta_x, delta_y)
{
if(have_mouse && enable_mouse)
{
mouse_delta_x += delta_x;
mouse_delta_y += delta_y;
if(enable_mouse_stream)
{
var now = Date.now();
if(now - last_mouse_packet < 1000 / sample_rate)
{
// TODO: set timeout
return;
}
last_mouse_packet = now;
send_mouse_packet();
}
}
}
function mouse_send_click(left, middle, right)
{
if(have_mouse && enable_mouse)
{
mouse_clicks = left | right << 1 | middle << 2;
if(enable_mouse_stream)
{
send_mouse_packet();
}
}
}
function send_mouse_packet()
{
var info_byte =
(mouse_delta_y < 0) << 5 |
(mouse_delta_x < 0) << 4 |
1 << 3 |
mouse_clicks;
mouse_buffer.push(
info_byte,
mouse_delta_x & 0xFF,
mouse_delta_y & 0xFF
);
if(mouse_buffer.length > 15)
{
var off = mouse_buffer.length % 3;
mouse_buffer = mouse_buffer.slice(0, off).concat(mouse_buffer.slice(off + 3));
}
mouse_delta_x = 0;
mouse_delta_y = 0;
mouse_irq();
}
this.destroy = function()
{
if(have_keyboard)
{
keyboard.destroy();
}
if(have_mouse)
{
mouse.destroy();
}
};
var command_register = 0,
read_output_register = false,
read_command_register = false;
io.register_read(0x60, port60_read);
io.register_read(0x64, port64_read);
io.register_write(0x60, port60_write);
io.register_write(0x64, port64_write);
function port60_read()
{
//log("port 60 read: " + (buffer[0] || "(none)"));
if(!kbd_buffer.length && !mouse_buffer.length)
{
// should not happen
dbg_log("Port 60 read: Empty", LOG_PS2);
return 0xFF;
}
var do_mouse_buffer;
if(kbd_buffer.length && mouse_buffer.length)
{
// tough decision, let's ask the PIC
do_mouse_buffer = (pic.get_isr() & 2) === 0;
}
else if(kbd_buffer.length)
{
do_mouse_buffer = false;
}
else
{
do_mouse_buffer = true;
}
if(do_mouse_buffer)
{
dbg_log("Port 60 read (mouse): " + h(mouse_buffer[0]), LOG_PS2);
if(mouse_buffer.length > 1)
{
mouse_irq();
}
return mouse_buffer.shift();
}
else
{
dbg_log("Port 60 read (kbd) : " + h(kbd_buffer[0]), LOG_PS2);
if(kbd_buffer.length > 1)
{
kbd_irq();
}
return kbd_buffer.shift();
}
};
function port64_read()
{
// status port
//dbg_log("port 64 read", LOG_PS2);
var status_byte = 0x10;
if(mouse_buffer.length || kbd_buffer.length)
{
status_byte |= 1;
}
if(mouse_buffer.length)
{
status_byte |= 0x20;
}
return status_byte;
};
function port60_write(write_byte)
{
if(read_command_register)
{
command_register = write_byte;
read_command_register = false;
dbg_log("Keyboard command register = " + h(command_register), LOG_PS2);
}
else if(read_output_register)
{
read_output_register = false;
mouse_buffer = [write_byte];
mouse_irq();
}
else if(next_read_sample)
{
next_read_sample = false;
mouse_buffer = [0xFA];
sample_rate = write_byte;
mouse_irq();
}
else if(next_read_led)
{
// nope
next_read_led = false;
}
else if(next_is_mouse_command)
{
dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);
if(!have_mouse)
{
return;
}
// send ack
mouse_buffer = [0xFA];
if(write_byte === 0xFF)
{
// reset, send completion code
mouse_buffer.push(0xAA, 0x00);
enable_mouse = true;
mouse.enabled = true;
}
else if(write_byte === 0xF2)
{
// MouseID Byte
mouse_buffer.push(0, 0);
}
else if(write_byte === 0xF3)
{
// sample rate
next_read_sample = true;
}
else if(write_byte === 0xF4)
{
// enable streaming
enable_mouse_stream = true;
enable_mouse = true;
mouse.enabled = true;
}
else if(write_byte === 0xF5)
{
// disable streaming
enable_mouse_stream = true;
}
else if(write_byte === 0xF6)
{
// reset defaults
enable_mouse_stream = false;
sample_rate = 100;
// ... resolution, scaling
}
else if(write_byte === 0xEB)
{
// request single packet
dbg_log("unimplemented request single packet");
}
else
{
dbg_log("new mouse command: " + h(write_byte), LOG_PS2);
}
mouse_irq();
}
else
{
dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);
// send ack
kbd_buffer.push(0xFA);
if(write_byte === 0xFF)
{
kbd_buffer.push(0xAA, 0x00);
}
else if(write_byte === 0xF2)
{
// identify
kbd_buffer.push(0xAB, 83);
}
else if(write_byte === 0xF4)
{
// enable scanning
}
else if(write_byte === 0xF5)
{
// disable scanning
}
else if(write_byte === 0xED)
{
next_read_led = true;
}
kbd_irq();
}
};
function port64_write(write_byte)
{
dbg_log("port 64 write: " + h(write_byte), LOG_PS2);
if(write_byte === 0xFE)
{
dbg_log("CPU reboot via PS2");
dev.reboot();
}
else if(write_byte === 0x20)
{
kbd_buffer.push(command_register);
kbd_irq();
}
else if(write_byte === 0x60)
{
read_command_register = true;
}
else if(write_byte === 0xD3)
{
read_output_register = true;
}
else if(write_byte === 0xD4)
{
next_is_mouse_command = true;
}
else if(write_byte === 0xA9)
{
// test second ps/2 port
kbd_buffer = [0];
kbd_irq();
}
else if(write_byte === 0xAA)
{
kbd_buffer = [0x55];
kbd_irq();
}
else if(write_byte === 0xAB)
{
kbd_buffer = [0];
kbd_irq();
}
/*else if(write_byte === 0xAE)
{
// not sure if right ...
kbd_buffer =[];
}*/
else
{
dbg_log("port 64: New command byte: " + h(write_byte), LOG_PS2);
}
};
}