diff --git a/kernel/pic.c b/kernel/pic.c
new file mode 100644
index 0000000..e494170
--- /dev/null
+++ b/kernel/pic.c
@@ -0,0 +1,75 @@
+/*
+Copyright (C) 2015-2019 The University of Notre Dame
+This software is distributed under the GNU General Public License.
+See the file LICENSE for details.
+*/
+
+#include "ioports.h"
+#include "kernel/types.h"
+#include "console.h"
+
+#define PIC_ICW1 0x11
+#define PIC_ICW4_MASTER 0x01
+#define PIC_ICW4_SLAVE  0x05
+#define PIC_ACK_SPECIFIC 0x60
+
+static uint8_t pic_control[2] = { 0x20, 0xa0 };
+static uint8_t pic_data[2] = { 0x21, 0xa1 };
+
+void pic_init(int pic0base, int pic1base)
+{
+	outb(PIC_ICW1, pic_control[0]);
+	outb(pic0base, pic_data[0]);
+	outb(1 << 2, pic_data[0]);
+	outb(PIC_ICW4_MASTER, pic_data[0]);
+	outb(~(1 << 2), pic_data[0]);
+
+	outb(PIC_ICW1, pic_control[1]);
+	outb(pic1base, pic_data[1]);
+	outb(2, pic_data[1]);
+	outb(PIC_ICW4_SLAVE, pic_data[1]);
+	outb(~0, pic_data[1]);
+
+	printf("pic: ready\n");
+}
+
+void pic_enable(uint8_t irq)
+{
+	uint8_t mask;
+	if(irq < 8) {
+		mask = inb(pic_data[0]);
+		mask = mask & ~(1 << irq);
+		outb(mask, pic_data[0]);
+	} else {
+		irq -= 8;
+		mask = inb(pic_data[1]);
+		mask = mask & ~(1 << irq);
+		outb(mask, pic_data[1]);
+		pic_enable(2);
+	}
+}
+
+void pic_disable(uint8_t irq)
+{
+	uint8_t mask;
+	if(irq < 8) {
+		mask = inb(pic_data[0]);
+		mask = mask | (1 << irq);
+		outb(mask, pic_data[0]);
+	} else {
+		irq -= 8;
+		mask = inb(pic_data[1]);
+		mask = mask | (1 << irq);
+		outb(mask, pic_data[1]);
+	}
+}
+
+void pic_acknowledge(uint8_t irq)
+{
+	if(irq >= 8) {
+		outb(PIC_ACK_SPECIFIC + (irq - 8), pic_control[1]);
+		outb(PIC_ACK_SPECIFIC + (2), pic_control[0]);
+	} else {
+		outb(PIC_ACK_SPECIFIC + irq, pic_control[0]);
+	}
+}