diff --git a/Makefile.build b/Makefile.build
index 30d709bed0682faf702cab87e8fe75b2e85cffd3..2fb7073287800f5c80383e3a10287304011d6a18 100644
--- a/Makefile.build
+++ b/Makefile.build
@@ -67,6 +67,7 @@ userprog_SRC += userprog/syscall_exit.c
 userprog_SRC += userprog/syscall_halt.c
 userprog_SRC += userprog/syscall_wait.c
 userprog_SRC += userprog/syscall_create.c
+userprog_SRC += userprog/syscall_write.c
 
 # No virtual memory code yet.
 #vm_SRC = vm/file.c			# Some file.
diff --git a/lib/kernel/stdio.h b/lib/kernel/stdio.h
index 3e5bae9b65b27d6008ee758df692da91e2b00b58..c7f9b392acc1aaac194b340560f8c57bbc75cb3f 100644
--- a/lib/kernel/stdio.h
+++ b/lib/kernel/stdio.h
@@ -1,5 +1,6 @@
 #ifndef __LIB_KERNEL_STDIO_H
 #define __LIB_KERNEL_STDIO_H
+#include <stddef.h>
 
 void putbuf (const char *, size_t);
 
diff --git a/userprog/syscall.c b/userprog/syscall.c
index c78175f256fa5a9e0b31bda1d2ae6fe2fb2bdd07..36285fa804bdc8218b41a750b0a253f6f2d96eab 100644
--- a/userprog/syscall.c
+++ b/userprog/syscall.c
@@ -60,6 +60,7 @@ syscall_handler (struct intr_frame *f UNUSED)
     break;
   case SYSCALL_EXIT:
     syscall_exit(f);
+    thread_exit();
     break;
   case SYSCALL_EXEC:
     syscall_exec(f);
@@ -70,9 +71,11 @@ syscall_handler (struct intr_frame *f UNUSED)
   case SYSCALL_CREATE:
     syscall_create(f);
     break;
+  case SYSCALL_WRITE:
+    syscall_write(f);
+    break;
   default:
     printf ("WARNING: Invalid Syscall (%d)\n", syscall_number);
     thread_exit ();
   }
-  // TODO: remove this call to exit as we don't want all syscalls to make the thread exit
 }
diff --git a/userprog/syscall_write.c b/userprog/syscall_write.c
new file mode 100644
index 0000000000000000000000000000000000000000..98aeb142ddd9b52187ac7dbba13d30a3043bb960
--- /dev/null
+++ b/userprog/syscall_write.c
@@ -0,0 +1,55 @@
+/*
+ * The Write System Call
+ *
+ * Authored by Joshua Saxby
+ */
+#include <stdio.h>
+#include "filesys/file.h"
+#include "lib/kernel/stdio.h"
+#include "system_calls.h"
+#include "threads/interrupt.h"
+
+// the maximum number of characters to write to the console in one go
+static const size_t CONSOLE_WRITE_MAX_SIZE = 255;
+
+static void write_to_console(const char* buffer, size_t size);
+
+void syscall_write(struct intr_frame *f) {
+    // first argument is syscall code (already handled)
+    int fd = *((int*)f->esp + 1); // file descriptor is second argument
+    void* buffer = (void*)(*((int*)f->esp + 2)); // buffer is third argument
+    unsigned size = *((unsigned*)((int*)f->esp + 3)); // size to write is fourth
+    // writing to stdout or stderr (fd in {1, 2}) is a special case
+    switch (fd) {
+    case 1: // stdin
+    case 2: // stderr
+        write_to_console((const char*)buffer, size);
+        f->eax = size;
+        break;
+    default: {
+            // otherwise, we need to write to a file denoted by fd
+            struct file* file_to_write = (struct file*)fd;
+            f->eax = file_write(file_to_write, buffer, size);
+            break;
+        }
+    }
+}
+
+static void write_to_console(const char* buffer, size_t size) {
+    // refuse to write more than a certain limit of chars at a time to console
+    if (size <= CONSOLE_WRITE_MAX_SIZE) {
+        putbuf(buffer, size);
+    } else {
+        const char* cursor = buffer;
+        size_t chars_left = size;
+        do {
+            size_t write_amount = (
+                chars_left < CONSOLE_WRITE_MAX_SIZE
+                ? chars_left : CONSOLE_WRITE_MAX_SIZE
+            );
+            putbuf(cursor, write_amount);
+            cursor += write_amount;
+            chars_left -= write_amount;
+        } while (chars_left > 0);
+    }
+}
diff --git a/userprog/system_calls.h b/userprog/system_calls.h
index 6fadcd1f7e3d556e4884521bb746da97de11b6e0..be83270c704309e07150583226503aafc2ce1c82 100644
--- a/userprog/system_calls.h
+++ b/userprog/system_calls.h
@@ -35,6 +35,13 @@ void syscall_wait(struct intr_frame *f);
  */
 void syscall_create(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
+ * some bytes could not be written.
+ */
+void syscall_write(struct intr_frame *f);
+
 /*
  * NOTE: There are more system calls implemented by Pintos but we are not
  * implementing them because the assignment brief does not ask of it.