diff --git a/kernel/kernelcore.S b/kernel/kernelcore.S
new file mode 100644
index 0000000..b13ca46
--- /dev/null
+++ b/kernel/kernelcore.S
@@ -0,0 +1,529 @@
+# Copyright (C) 2015 The University of Notre Dame
+# This software is distributed under the GNU General Public License.
+# See the file LICENSE for details.
+
+#include "memorylayout.h"
+	
+# _start is the initial entry point for the kernel
+# Note that we start here merely because it comes
+# first in the link order.  The name _start is there
+# merely to keep the linker happy.
+
+.code16
+.text
+.global _start
+_start:
+
+# First, jump to the real code start,
+# skipping over the immediate data that follows
+	jmp	realstart
+
+# At a fixed offset, place an integer that contains
+# the length of the kernel in bytes.  This is used
+# by the bootblock code to figure out how many sectors to load.
+	
+.org KERNEL_SIZE_OFFSET
+.global kernel_size
+kernel_size:
+	.long	_end-_start
+		
+realstart:
+	
+# While we are briefly still in 16-bit real mode,
+# it is safe to call the BIOS to set things up.
+
+# Reset the disk system in order to quiet motors
+# and turn off any pending interrupts.
+		
+	mov	$0,%ah
+	int	$0x13
+
+# Turn off the screen cursor, because the
+# console will have its own.
+
+	mov	$1,%ah
+	mov	$0,%cl
+	mov	$0x20,%ch
+	int	$0x10
+
+# Get the amount of memory above 1MB and save it for later.
+# There are two ways to do this:
+
+# BIOS call 0xe801 measures memory as follows:	
+# %ax returns memory above 1MB in 1KB increments, maximum of 16MB.
+# %bx returns memory above 64MB in 64KB increments, maximum of 4GB.
+# However, this call is relatively new, so if it
+# fails, we fall back on memtest2 below.
+
+memtest1:	
+	clc
+	mov	$0, %bx
+	mov	$0xe801,%ax
+	int	$0x15
+	jc	memtest2
+	
+	shr	$10, %ax
+	shr	$4, %bx
+	add	%ax, %bx
+	mov	%bx, total_memory-_start
+	jmp	memdone
+
+# BIOS call 0x0088 measures memory as follows:
+# %ax returns memory above 1MB in 1KB increments, maxiumum of 64MB.
+
+memtest2:
+	clc
+	mov	$0, %ax
+	mov	$0x88, %ah
+	int	$0x15
+	shr	$10, %ax
+	inc	%ax
+	mov	%ax, total_memory-_start
+memdone:	
+
+# Now, set the video mode using VBE interrupts.
+# Keep trying until we find one that works.
+
+# These are documented on page 30 of the VESA-BIOS manual:
+# interrupt 0x10
+# ax = 0x4f02 "Set VBE Mode"
+# bx = mode
+#    D0-8   = Mode Number
+#    D9-10  = Reserved (must be 0)
+#    D11    = 0 Use current default refresh rate.
+#    D12-13 = 0 Reserved
+#    D14    = 0 Use windowed frame buffer model.
+#           = 1 Use linear frame buffer model.
+#    D15    = 0 Clear display memory.
+#    ES:DI  = Pointer to CRCTCInfoBlock structure.
+
+jmp video640
+	
+video1280:
+ 	mov	$0x4f02, %ax
+ 	mov	$0x411b, %bx
+	int	$0x10
+	cmp	$0x004f, %ax
+	je	videodone
+video1024:
+ 	mov	$0x4f02, %ax
+ 	mov	$0x4118, %bx
+	int	$0x10
+	cmp	$0x004f, %ax
+	je	videodone
+video800:
+	mov	$0x4f02, %ax
+ 	mov	$0x4115, %bx
+	int	$0x10
+	cmp	$0x004f, %ax
+	je	videodone
+video640:
+	mov	$0x4f02, %ax
+ 	mov	$0x4112, %bx
+	int	$0x10
+	cmp	$0x004f, %ax
+	je	videodone
+video640_lowcolor:
+	mov	$0x4f02, %ax
+	mov	$0x4111, %bx
+	int	$0x10
+	cmp	$0x004f, %ax
+	je	videodone
+
+videofailed:
+	mov	$videomsg, %esi
+	call	bios_putstring
+	jmp	halt
+
+videodone:	
+
+# After all that, query the video mode and
+# figure out the dimensions and the frame
+# buffer address.  The set mode is still in bx.
+
+	mov	%ds, %ax		# Set up the extra segment
+	mov	%ax, %es		# with the data segment
+
+	mov	$(video_info-_start),%di
+	mov	$0x4f01, %ax
+	mov	%bx, %cx
+	int	$0x10
+
+# In order to use video resolutions higher than 640x480,
+# we must enable the A20 address line. The following
+# code works on motherboards with "FAST A20", which should
+# be everything since the IBM PS/2
+inb $0x92, %al
+orb $2, %al
+outb %al, $0x92
+	
+# Finally, we are ready to enter protected mode.
+# To do this, we disable interrupts so that
+# handlers will not see an inconsistent state.
+# We then load the new interrupt and descriptor
+# tables, which are given below.  Then, we
+# enable the protection bit, and load the
+# segment selectors into the appropriate registers.
+# Finally, we make a long jump to main,
+# atomically loading the new code segment and
+# starting the kernel.
+
+	cli				# clear interrupts
+	lidt	(idt_init-_start)	# load the interrupt table
+	lgdt	(gdt_init-_start)	# load the descriptor table
+	mov	%cr0, %eax		# get the status word
+	or	$0x01, %eax		# turn on the P bit
+	mov	%eax, %cr0		# store the status word
+					# (we are now in protected mode)
+	mov	$2*8, %ax		# selector two is flat 4GB data data
+	mov	%ax, %ds		# set data, extra, and stack segments to selector two
+	mov	%ax, %es
+	mov	%ax, %ss
+	mov	$5*8, %ax		# set TSS to selector five
+	ltr	%ax
+	mov	$0, %ax			# unused segments are nulled out
+	mov	%ax, %fs
+	mov	%ax, %gs
+	mov	$INTERRUPT_STACK_TOP, %sp    # set up initial C stack
+	mov	$INTERRUPT_STACK_TOP, %bp    # set up initial C stack
+	ljmpl	$(1*8), $(kernel_main)	     # jump to the C main!
+
+# bios_putstring displays an ASCII string pointed to by %si,
+# useful for basic startup messages or fatal errors.
+
+bios_putstring:
+	mov	(%si), %al
+	cmp	$0, %al
+	jz	bios_putstring_done
+        call	bios_putchar
+	inc	%si
+	jmp	bios_putstring
+bios_putstring_done:
+        ret
+
+# bios_putchar invokes the bios to display
+# one character on the screen.
+	
+bios_putchar:
+	push	%ax
+	push	%bx
+        mov	$14,%ah
+        mov	$1,%bl
+        int	$0x10
+	pop	%bx
+	pop	%ax
+	ret
+
+# The video_info structure is filled in by a BIOS
+# call above, and is used to record the basic video
+# layout for the graphics subsystem.  See page 30
+# of the VBE specification for an explanation of this structure.
+
+.align 4
+video_info:
+	.word	0
+	.byte	0,0
+	.word	0,0,0,0
+	.long	0
+.global video_xbytes
+video_xbytes:
+	.word	0
+.global video_xres
+video_xres:
+	.word	0 
+.global video_yres
+video_yres:
+	.word	0
+	.byte	0,0,0,0,0,0,0,0,0
+	.byte	0,0,0,0,0,0,0,0,0
+.global video_buffer
+video_buffer:
+	.long	0
+	.long	0
+	.word	0
+	.word	0
+	.byte	0,0,0,0,0,0,0,0,0,0
+	.long	0
+.rept 190
+	.byte	0
+.endr
+
+.align 4
+videomsg:
+	.asciz	"fatal error: couldn't find suitable video mode!\r\n"
+
+###########################
+# 32 BIT CODE BEGINS HERE #
+###########################
+
+# All code below this point is 32-bit code and data
+# that is invoked by higher levels of the kernel from C code.
+	
+.code32
+
+# Rebooting the machine is easy.
+# Set up an invalid interrupt table, and the force an interrupt.
+# The machine will triple-fault and reboot itself.
+
+.global reboot
+reboot:
+	cli
+	lidt	idt_invalid	
+	int	$1
+
+
+.global halt
+halt:
+	cli
+	hlt
+	jmp	halt
+
+# This is the global descriptor table to be used by the kernel.
+# Because we don't really want to use segmentation, we define
+# very simple descriptors for global code and data and the TSS
+
+.align 16
+.global gdt
+gdt:
+	.word	0,0,0,0				# seg 0 - null
+	.word	0xffff, 0x0000, 0x9a00, 0x00cf	# seg 1 - kernel flat 4GB code
+	.word	0xffff, 0x0000, 0x9200, 0x00cf	# seg 2 - kernel flat 4GB data
+	.word	0xffff, 0x0000, 0xfa00, 0x00cf	# seg 3 - user flat 4GB code
+	.word	0xffff, 0x0000, 0xf200, 0x00cf	# seg 4 - user flat 4GB data
+	.word	0x0068, (tss-_start),0x8901, 0x00cf  # seg 5 - TSS
+	
+# This is the initializer for the global descriptor table.
+# It simply tells us the size and location of the table.
+
+gdt_init:
+	.word	gdt_init-gdt
+	.long	gdt
+
+# The TSS is a big task management structure used by the 386.
+# We do not use the TSS, but simply rely on pushing variables
+# around in stacks.  However, we need to use the TSS in order
+# to initialize the stack pointer and segment for priv level 0
+
+.align 16
+.global tss	
+tss:
+	.long	0
+.global interrupt_stack_pointer
+interrupt_stack_pointer:	
+	.long	INTERRUPT_STACK_TOP # initial interrupt stack ptr at 64 KB
+	.long	2*8		    # use segment 2 for the interrupt stack
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+	.long	0
+
+	
+.global total_memory
+total_memory:
+	.word	32
+	
+# First, the internal interrupts.
+# Note that some already push their own detail
+# code onto the stack.  For the others, we push
+# a zero, just to get a common layout.
+
+intr00: pushl $0 ; pushl $0  ; jmp intr_handler
+intr01: pushl $0 ; pushl $1  ; jmp intr_handler
+intr02: pushl $0 ; pushl $2  ; jmp intr_handler
+intr03: pushl $0 ; pushl $3  ; jmp intr_handler
+intr04: pushl $0 ; pushl $4  ; jmp intr_handler
+intr05: pushl $0 ; pushl $5  ; jmp intr_handler
+intr06: pushl $0 ; pushl $6  ; jmp intr_handler
+intr07: pushl $0 ; pushl $7  ; jmp intr_handler
+intr08:            pushl $8  ; jmp intr_handler
+intr09: pushl $0 ; pushl $9  ; jmp intr_handler
+intr10:            pushl $10 ; jmp intr_handler
+intr11:            pushl $11 ; jmp intr_handler
+intr12:            pushl $12 ; jmp intr_handler
+intr13:            pushl $13 ; jmp intr_handler
+intr14:            pushl $14 ; jmp intr_handler
+intr15: pushl $0 ; pushl $15 ; jmp intr_handler
+intr16: pushl $0 ; pushl $16 ; jmp intr_handler
+intr17:            pushl $17 ; jmp intr_handler
+intr18: pushl $0 ; pushl $18 ; jmp intr_handler
+intr19: pushl $0 ; pushl $19 ; jmp intr_handler
+
+# These interrupts are reserved, but could
+# conceivably occur on the next processor model
+
+intr20: pushl $0 ; pushl $20 ; jmp intr_handler
+intr21: pushl $0 ; pushl $21 ; jmp intr_handler
+intr22: pushl $0 ; pushl $22 ; jmp intr_handler
+intr23: pushl $0 ; pushl $23 ; jmp intr_handler
+intr24: pushl $0 ; pushl $24 ; jmp intr_handler
+intr25: pushl $0 ; pushl $25 ; jmp intr_handler
+intr26: pushl $0 ; pushl $26 ; jmp intr_handler
+intr27: pushl $0 ; pushl $27 ; jmp intr_handler
+intr28: pushl $0 ; pushl $28 ; jmp intr_handler
+intr29: pushl $0 ; pushl $29 ; jmp intr_handler
+intr30: pushl $0 ; pushl $30 ; jmp intr_handler
+intr31: pushl $0 ; pushl $31 ; jmp intr_handler
+
+# Now, the external hardware interrupts.
+
+intr32: pushl $0 ; pushl $32 ; jmp intr_handler
+intr33: pushl $0 ; pushl $33 ; jmp intr_handler
+intr34: pushl $0 ; pushl $34 ; jmp intr_handler
+intr35: pushl $0 ; pushl $35 ; jmp intr_handler
+intr36: pushl $0 ; pushl $36 ; jmp intr_handler
+intr37: pushl $0 ; pushl $37 ; jmp intr_handler
+intr38: pushl $0 ; pushl $38 ; jmp intr_handler
+intr39: pushl $0 ; pushl $39 ; jmp intr_handler
+intr40: pushl $0 ; pushl $40 ; jmp intr_handler
+intr41: pushl $0 ; pushl $41 ; jmp intr_handler
+intr42: pushl $0 ; pushl $42 ; jmp intr_handler
+intr43: pushl $0 ; pushl $43 ; jmp intr_handler
+intr44: pushl $0 ; pushl $44 ; jmp intr_handler
+intr45: pushl $0 ; pushl $45 ; jmp intr_handler
+intr46: pushl $0 ; pushl $46 ; jmp intr_handler
+intr47: pushl $0 ; pushl $47 ; jmp intr_handler
+intr48: pushl $0 ; pushl $48 ; jmp intr_syscall
+
+intr_handler:
+	pushl	%ds		# push segment registers
+	pushl	%es
+	pushl	%fs
+	pushl	%gs
+	pushl	%ebp		# push general regs
+	pushl	%edi
+	pushl	%esi
+	pushl	%edx
+	pushl	%ecx
+	pushl	%ebx
+	pushl	%eax
+	pushl	48(%esp)	# push interrupt code from above
+	pushl	48(%esp)	# push interrupt number from above
+	movl	$2*8, %eax	# switch to kernel data seg and extra seg
+	movl	%eax, %ds
+	movl	%eax, %es
+	call	interrupt_handler
+	addl	$4, %esp	# remove interrupt number
+	addl	$4, %esp	# remove interrupt code
+	jmp	intr_return
+	
+intr_syscall:
+	pushl	%ds		# push segment registers
+	pushl	%es
+	pushl	%fs
+	pushl	%gs
+	pushl	%ebp		# push general regs
+	pushl	%edi
+	pushl	%esi
+	pushl	%edx
+	pushl	%ecx
+	pushl	%ebx
+	pushl	%eax		# note these *are* the syscall args
+	movl	$2*8, %eax	# switch to kernel data seg and extra seg
+	movl	%eax, %ds
+	movl	%eax, %es
+	call	syscall_handler
+	addl	$4, %esp	# remove the old eax
+	jmp	syscall_return	
+
+.global intr_return
+intr_return:
+	popl	%eax
+syscall_return:	
+	popl	%ebx
+	popl	%ecx
+	popl	%edx
+	popl	%esi
+	popl	%edi
+	popl	%ebp
+	popl	%gs
+	popl	%fs
+	popl	%es
+	popl	%ds
+	addl	$4, %esp	# remove interrupt num
+	addl	$4, %esp	# remove detail code
+	iret			# iret gets the intr context
+			
+.align 2
+idt:
+	.word	intr00-_start,1*8,0x8e00,0x0001
+	.word	intr01-_start,1*8,0x8e00,0x0001
+	.word	intr02-_start,1*8,0x8e00,0x0001
+	.word	intr03-_start,1*8,0x8e00,0x0001
+	.word	intr04-_start,1*8,0x8e00,0x0001
+	.word	intr05-_start,1*8,0x8e00,0x0001
+	.word	intr06-_start,1*8,0x8e00,0x0001
+	.word	intr07-_start,1*8,0x8e00,0x0001
+	.word	intr08-_start,1*8,0x8e00,0x0001
+	.word	intr09-_start,1*8,0x8e00,0x0001
+	.word	intr10-_start,1*8,0x8e00,0x0001
+	.word	intr11-_start,1*8,0x8e00,0x0001
+	.word	intr12-_start,1*8,0x8e00,0x0001
+	.word	intr13-_start,1*8,0x8e00,0x0001
+	.word	intr14-_start,1*8,0x8e00,0x0001
+	.word	intr15-_start,1*8,0x8e00,0x0001
+	.word	intr16-_start,1*8,0x8e00,0x0001
+	.word	intr17-_start,1*8,0x8e00,0x0001
+	.word	intr18-_start,1*8,0x8e00,0x0001
+	.word	intr19-_start,1*8,0x8e00,0x0001
+	.word	intr20-_start,1*8,0x8e00,0x0001
+	.word	intr21-_start,1*8,0x8e00,0x0001
+	.word	intr22-_start,1*8,0x8e00,0x0001
+	.word	intr23-_start,1*8,0x8e00,0x0001
+	.word	intr24-_start,1*8,0x8e00,0x0001
+	.word	intr25-_start,1*8,0x8e00,0x0001
+	.word	intr26-_start,1*8,0x8e00,0x0001
+	.word	intr27-_start,1*8,0x8e00,0x0001
+	.word	intr28-_start,1*8,0x8e00,0x0001
+	.word	intr29-_start,1*8,0x8e00,0x0001
+	.word	intr30-_start,1*8,0x8e00,0x0001
+	.word	intr31-_start,1*8,0x8e00,0x0001
+	.word	intr32-_start,1*8,0x8e00,0x0001
+	.word	intr33-_start,1*8,0x8e00,0x0001
+	.word	intr34-_start,1*8,0x8e00,0x0001
+	.word	intr35-_start,1*8,0x8e00,0x0001
+	.word	intr36-_start,1*8,0x8e00,0x0001
+	.word	intr37-_start,1*8,0x8e00,0x0001
+	.word	intr38-_start,1*8,0x8e00,0x0001
+	.word	intr39-_start,1*8,0x8e00,0x0001
+	.word	intr40-_start,1*8,0x8e00,0x0001
+	.word	intr41-_start,1*8,0x8e00,0x0001
+	.word	intr42-_start,1*8,0x8e00,0x0001
+	.word	intr43-_start,1*8,0x8e00,0x0001
+	.word	intr44-_start,1*8,0x8e00,0x0001
+	.word	intr45-_start,1*8,0x8e00,0x0001
+	.word	intr46-_start,1*8,0x8e00,0x0001
+	.word	intr47-_start,1*8,0x8e00,0x0001
+	.word	intr48-_start,1*8,0xee00,0x0001
+	
+# This is the initializer for the global interrupt table.
+# It simply gives the size and location of the interrupt table
+	
+idt_init:
+	.word	idt_init-idt
+	.long	idt
+
+# This is the initializer for an invalid interrupt table.
+# Its only purpose is to assist with the reboot routine.
+	
+idt_invalid:
+	.word	0
+	.long	0