diff --git a/Makefile.build b/Makefile.build
index 2d43ff2d270d8e2caff7a87971321c7e590617ff..d136a6f1c1e8fbc60df45bc898ef0d96fd7f53ed 100644
--- a/Makefile.build
+++ b/Makefile.build
@@ -71,6 +71,7 @@ userprog_SRC += userprog/syscall_create.c
 userprog_SRC += userprog/syscall_remove.c
 userprog_SRC += userprog/syscall_write.c
 userprog_SRC += userprog/file_descriptors_map.c
+userprog_SRC += userprog/syscall_read.c
 
 # No virtual memory code yet.
 #vm_SRC = vm/file.c			# Some file.
diff --git a/userprog/syscall.c b/userprog/syscall.c
index e6abc56803da28b385bb69fa7b493d30496ddcc9..fb62a79e4a8e648b19c08c774ef33dfcef5ca417 100644
--- a/userprog/syscall.c
+++ b/userprog/syscall.c
@@ -79,6 +79,9 @@ syscall_handler (struct intr_frame *f UNUSED)
   case SYSCALL_OPEN:
     syscall_open(f);
 	break;
+  case SYSCALL_READ:
+    syscall_read(f);
+    break;
   case SYSCALL_FILESIZE:
 	syscall_filesize(f);
 	break;
diff --git a/userprog/syscall_read.c b/userprog/syscall_read.c
new file mode 100644
index 0000000000000000000000000000000000000000..67f7d9858ea87ecc23b717a9b8dee1353d323abe
--- /dev/null
+++ b/userprog/syscall_read.c
@@ -0,0 +1,42 @@
+/*
+ * The Read System Call
+ *
+ * Authored by Joshua Saxby
+ */
+#include <stddef.h>
+#include "devices/input.h"
+#include "filesys/file.h"
+#include "system_calls.h"
+#include "threads/interrupt.h"
+
+void syscall_read(struct intr_frame *f) {
+    // first argument is syscall code (already handled)
+    int fd = *((int*)f->esp + 1); // file descriptor is second argument
+    char* buffer = (void*)(*((int*)f->esp + 2)); // buffer is third argument
+    unsigned size = *((unsigned*)((int*)f->esp + 3)); // size to read is fourth
+    // reading from stdin (keyboard) is a special case
+    switch (fd) {
+    case 0: {
+            // read from keyboard for as many bytes as requested
+            for (size_t i = 0; i < size; i++) {
+                *(buffer + i) = input_getc();
+            }
+            f->eax = size;
+            break;
+        }
+    case 1: // stdout
+    case 2: // stderr
+        f->eax = -1; // it is a mistake to attempt to read from stdout or stderr
+        break;
+    default: {
+            // otherwise, we need to read from a file denoted by fd
+            struct file* file_to_read = get_associated_file_pointer(fd);
+            if (file_to_read == NULL) {
+                f->eax = -1; // invalid file descriptor
+                break;
+            }
+            f->eax = file_read(file_to_read, buffer, size);
+            break;
+        }
+    }
+}
diff --git a/userprog/system_calls.h b/userprog/system_calls.h
index ce7ae18823f34dd39916c550db655291e0f7c6d3..b22867040cc55488aa61fdffd7dca93e74638288 100644
--- a/userprog/system_calls.h
+++ b/userprog/system_calls.h
@@ -36,6 +36,13 @@ void syscall_wait(struct intr_frame *f);
  */
 void syscall_create(struct intr_frame *f);
 
+/*
+ * Reads size bytes from the file open as fd into buffer.
+ * Returns the number of bytes actually read (0 at end of file), or -1 if the
+ * file could not be read (due to a condition other than end of file).
+ */
+void syscall_read(struct intr_frame *f);
+
 /*
  * Writes size bytes from buffer to the open file fd.
  * Returns the number of bytes actually written, which may be less than size if