diff --git a/kernel/mouse.c b/kernel/mouse.c
new file mode 100644
index 0000000..6928673
--- /dev/null
+++ b/kernel/mouse.c
@@ -0,0 +1,249 @@
+/*
+Copyright (C) 2016-2019 The University of Notre Dame
+This software is distributed under the GNU General Public License.
+See the file LICENSE for details.
+*/
+
+#include "mouse.h"
+#include "console.h"
+#include "ioports.h"
+#include "interrupt.h"
+#include "kernel/ascii.h"
+#include "process.h"
+#include "kernelcore.h"
+#include "event_queue.h"
+
+/*
+The PS2 interface uses a data port and a command port.
+Reading the command port yields a status byte,
+while writing to the command port executes commands.
+*/
+
+#define PS2_DATA_PORT    0x60
+#define PS2_COMMAND_PORT 0x64
+
+/* Some commands that may be sent to the command port. */
+
+#define PS2_COMMAND_READ_CONFIG 0x20
+#define PS2_COMMAND_WRITE_CONFIG 0x60
+#define PS2_COMMAND_DISABLE_MOUSE 0xA7
+#define PS2_COMMAND_ENABLE_MOUSE 0xA8
+#define PS2_COMMAND_DISABLE_KEYBOARD 0xAD
+#define PS2_COMMAND_ENABLE_KEYBOARD 0xAE
+#define PS2_COMMAND_MOUSE_PREFIX 0xD4
+
+/* The status byte read from the command port has these fields. */
+
+#define PS2_STATUS_OBF 0x01	// true: may not write to data port
+#define PS2_STATUS_IBF 0x02	// true: may read from data port
+#define PS2_STATUS_SYS 0x04	// true when port is initialized
+#define PS2_STATUS_A2  0x08	// true if command port was last written to
+#define PS2_STATUS_INH 0x10	// true if keyboard inhibited
+#define PS2_STATUS_MOBF 0x20	// true if mouse output available
+#define PS2_STATUS_TOUT 0x40	// true if timeout during I/O
+#define PS2_STATUS_PERR 0x80	// true indicates parity error
+
+/*
+In addition, a configuration byte may be read/written
+via PS2_COMMAND_READ/WRITECONFIG.  The configuration byte
+has these bitfields.
+*/
+
+#define PS2_CONFIG_PORTA_IRQ 0x01
+#define PS2_CONFIG_PORTB_IRQ 0x02
+#define PS2_CONFIG_SYSTEM    0x04
+#define PS2_CONFIG_RESERVED1 0x08
+#define PS2_CONFIG_PORTA_CLOCK 0x10
+#define PS2_CONFIG_PORTB_CLOCK 0x20
+#define PS2_CONFIG_PORTA_TRANSLATE 0x40
+#define PS2_CONFIG_RESERVED2   0x80
+
+/*
+The mouse has several specialized commands that may
+be sent by first sending PS2_COMMAND_MOUSE_PREFIX,
+then one of the following:
+*/
+
+#define PS2_MOUSE_COMMAND_ENABLE_STREAMING 0xea
+#define PS2_MOUSE_COMMAND_ENABLE_DEVICE    0xf4
+#define PS2_MOUSE_COMMAND_RESET 0xff
+
+/*
+To read/write data to/from the PS2 port, we must first check
+for the input/output buffer full (IBF/OBF) bits are set appropriately
+in the status register.
+*/
+
+uint8_t ps2_read_data()
+{
+	uint8_t status;
+	do {
+		status = inb(PS2_COMMAND_PORT);
+	} while(!(status & PS2_STATUS_OBF));
+	return inb(PS2_DATA_PORT);
+}
+
+void ps2_write_data(uint8_t data)
+{
+	uint8_t status;
+	do {
+		status = inb(PS2_COMMAND_PORT);
+	} while(status & PS2_STATUS_IBF);
+	return outb(data, PS2_DATA_PORT);
+}
+
+/*
+In a similar way, to write a command to the status port,
+we must also check that the IBF field is cleared.
+*/
+
+void ps2_write_command(uint8_t data)
+{
+	uint8_t status;
+	do {
+		status = inb(PS2_COMMAND_PORT);
+	} while(status & PS2_STATUS_IBF);
+	return outb(data, PS2_COMMAND_PORT);
+}
+
+/*
+Clear the buffer of all data by reading until OBF and IBF
+are both clear.  Useful when resetting the device to achieve
+a known state.
+*/
+
+void ps2_clear_buffer()
+{
+	uint8_t status;
+	do {
+		status = inb(PS2_COMMAND_PORT);
+		if(status & PS2_STATUS_OBF) {
+			inb(PS2_DATA_PORT);
+			continue;
+		}
+	} while(status & (PS2_STATUS_OBF | PS2_STATUS_IBF));
+}
+
+/*
+Send a mouse-specific command by sending the mouse prefix,
+then the mouse command as data, then reading back an
+acknowledgement.
+*/
+
+void ps2_mouse_command(uint8_t command)
+{
+	ps2_write_command(PS2_COMMAND_MOUSE_PREFIX);
+	ps2_write_data(command);
+	ps2_read_data();
+}
+
+/*
+Read and write the PS2 configuration byte, which
+does not involve an acknowledgement.
+*/
+
+uint8_t ps2_config_get()
+{
+	ps2_write_command(PS2_COMMAND_READ_CONFIG);
+	return ps2_read_data();
+}
+
+void ps2_config_set(uint8_t config)
+{
+	ps2_write_command(PS2_COMMAND_WRITE_CONFIG);
+	ps2_write_data(config);
+}
+
+static struct mouse_state state = {0,0,0};
+static struct mouse_state last_state = {0,0,0};
+
+/*
+On each interrupt, read three bytes from the PS 2 port, which
+gives buttons and status, X and Y position.  The ninth (sign) bit
+of the X and Y position is given as a single bit in the status
+word, so we must assemble a twos-complement integer if needed.
+Finally, take those values and update the current mouse state.
+*/
+
+static void mouse_interrupt(int i, int code)
+{
+	uint8_t m1 = inb(PS2_DATA_PORT);
+	uint8_t m2 = inb(PS2_DATA_PORT);
+	uint8_t m3 = inb(PS2_DATA_PORT);
+
+	last_state = state;
+
+	state.buttons = m1 & 0x03;
+	state.x += m1 & 0x10 ? 0xffffff00 | m2 : m2;
+	state.y -= m1 & 0x20 ? 0xffffff00 | m3 : m3;
+
+	if(state.x < 0)
+		state.x = 0;
+	if(state.y < 0)
+		state.y = 0;
+	if(state.x >= video_xres)
+		state.x = video_xres - 1;
+	if(state.y >= video_yres)
+		state.y = video_yres - 1;
+
+	// XXX skip mouse events for now!
+	return;
+
+	if(state.buttons!=last_state.buttons) {
+		int i;
+		for(i=0;i<8;i++) {
+			uint8_t mask = (1<<i);
+			if( (state.buttons&mask) && !(last_state.buttons&mask) ) {
+				event_queue_post_root(EVENT_BUTTON_DOWN,i,state.x,state.y);
+			} else if( !(state.buttons&mask) && (last_state.buttons&mask) ) {
+				event_queue_post_root(EVENT_BUTTON_UP,i,state.x,state.y);
+			}
+		}
+	}
+
+	if(state.x!=last_state.x || state.y!=last_state.y) {	
+		event_queue_post_root(EVENT_MOUSE_MOVE,0,state.x,state.y);
+	}
+}
+
+/*
+Do a non-blocking read of the current mouse state.
+Block interrupts while reading, to avoid inconsistent state.
+*/
+
+void mouse_read(struct mouse_state *e)
+{
+	interrupt_disable(44);
+	*e = state;
+	interrupt_enable(44);
+}
+
+/*
+Unlike the keyboard, the mouse is not automatically enabled
+at bootup.  We must first obtain tbe ps2 configuration register,
+enable both port A (keyboard) and port B (mouse), then send
+a series of commands to reset the mouse and enable "streaming",
+which causes an interrupt for every move of the mouse.
+*/
+
+void mouse_init()
+{
+	ps2_clear_buffer();
+
+	uint8_t config = ps2_config_get();
+	config |= PS2_CONFIG_PORTA_IRQ;
+	config |= PS2_CONFIG_PORTB_IRQ;
+	config &= ~PS2_CONFIG_PORTA_CLOCK;
+	config &= ~PS2_CONFIG_PORTB_CLOCK;
+	config |= PS2_CONFIG_PORTA_TRANSLATE;
+	ps2_config_set(config);
+
+	ps2_mouse_command(PS2_MOUSE_COMMAND_RESET);
+	ps2_mouse_command(PS2_MOUSE_COMMAND_ENABLE_DEVICE);
+	ps2_mouse_command(PS2_MOUSE_COMMAND_ENABLE_STREAMING);
+
+	interrupt_register(44, mouse_interrupt);
+	interrupt_enable(44);
+
+	printf("mouse: ready\n");
+}