diff --git a/userprog/syscall_read.c b/userprog/syscall_read.c
index bd6c2dfbf379f22aa79adeec3202217ecbb70072..67f7d9858ea87ecc23b717a9b8dee1353d323abe 100644
--- a/userprog/syscall_read.c
+++ b/userprog/syscall_read.c
@@ -3,10 +3,40 @@
  *
  * Authored by Joshua Saxby
  */
-#include "filesys/filesys.h"
+#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) {
-    (void*)0;
+    // 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;
+        }
+    }
 }