got an interrupt-based uart interface library working
diff --git a/bbb_cape/src/cape/Makefile b/bbb_cape/src/cape/Makefile
index 2656cb6..6391b21 100644
--- a/bbb_cape/src/cape/Makefile
+++ b/bbb_cape/src/cape/Makefile
@@ -29,12 +29,16 @@
 	-mthumb \
 
 OBJECTS_main := main \
+	uart_common \
 
 OBJECTS_bootloader := bootloader \
+	uart_common \
+	uart \
 
 OUTPUTS := main bootloader
 
-OBJECTS := $(foreach output,$(OUTPUTS),$(OBJECTS_$(output)))
+# The sort is to remove duplicates because Make warns about those.
+OBJECTS := $(sort $(foreach output,$(OUTPUTS),$(OBJECTS_$(output))))
 
 OUTPUTS_elf := $(OUTPUTS:%=$(OBJDIR)%.elf)
 OUTPUTS_hex := $(OUTPUTS:%=$(OBJDIR)%.hex)
diff --git a/bbb_cape/src/cape/bootloader.c b/bbb_cape/src/cape/bootloader.c
index 4a161be..ef816f3 100644
--- a/bbb_cape/src/cape/bootloader.c
+++ b/bbb_cape/src/cape/bootloader.c
@@ -6,7 +6,7 @@
 static void jump_to_main(void) __attribute__((noreturn));
 static void jump_to_main(void) {
   // 0x20008000
-  __asm__ volatile(
+  __asm__ __volatile__(
       "mov sp, %[stack]\n\t"
       "bx %[reset]" : :
       [stack]"r"(RAM_START + RAM_SIZE), [reset]"r"(MAIN_FLASH_START | 1)
diff --git a/bbb_cape/src/cape/uart.c b/bbb_cape/src/cape/uart.c
new file mode 100644
index 0000000..45fa744
--- /dev/null
+++ b/bbb_cape/src/cape/uart.c
@@ -0,0 +1,67 @@
+#include "cape/uart.h"
+#include "cape/uart_common_private.h"
+
+#include "cape/util.h"
+#include "cape/uart_common.h"
+
+// TODO(brians): Add error checking.
+
+static void default_callback(int bytes) {}
+
+void uart_transmit_callback(int bytes_transmitted) ALIAS_WEAK(default_callback);
+void uart_receive_callback(int bytes_received) ALIAS_WEAK(default_callback);
+
+static int transmit_bytes, receive_bytes;
+// These actually contain 1 less than the indicated number to make the common
+// path through the ISR faster.
+static int transmitted_bytes, received_bytes;
+static uint8_t *transmit_data, *receive_data;
+
+// Enable the transmitter and interrupt when we can write.
+static const uint32_t kTransmitBits = USART_CR1_TE | USART_CR1_TXEIE;
+// Enable the receive and interrupt when there's data to read.
+static const uint32_t kReceiveBits = USART_CR1_RE | USART_CR1_RXNEIE;
+
+void USART1_IRQHandler(void) {
+  uint32_t status = UART->SR;
+  if (status & USART_SR_TXE) {
+    if ((transmitted_bytes + 1) < transmit_bytes) {
+      UART->DR = transmit_data[++transmitted_bytes];
+    } else {
+      // Get another interrupt when it's done writing that last byte.
+      UART->CR1 = (UART->CR1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE;
+    }
+  } else if (status & USART_SR_RXNE) {
+    receive_data[++received_bytes] = UART->DR;
+    if ((received_bytes + 1) >= receive_bytes) {
+      UART->CR1 &= ~kReceiveBits;
+      uart_receive_callback(receive_bytes);
+    }
+  } else if (status & USART_SR_TC) {
+      UART->CR1 &= ~(USART_CR1_TCIE | USART_CR1_TE);
+      uart_transmit_callback(transmit_bytes);
+  }
+}
+
+void uart_configure(int baud) {
+  uart_common_configure(baud);
+  NVIC_SetPriority(USART1_IRQn, 3);
+  NVIC_EnableIRQ(USART1_IRQn);
+}
+
+void uart_transmit(int bytes, uint8_t *data) {
+  transmit_bytes = bytes;
+  transmitted_bytes = 0;
+  transmit_data = data;
+  compiler_memory_barrier();
+  UART->CR1 |= kTransmitBits;
+  UART->DR = data[0];
+}
+
+void uart_receive(int bytes, uint8_t *data) {
+  receive_bytes = bytes;
+  received_bytes = -1;
+  receive_data = data;
+  compiler_memory_barrier();
+  UART->CR1 |= kReceiveBits;
+}
diff --git a/bbb_cape/src/cape/uart.h b/bbb_cape/src/cape/uart.h
new file mode 100644
index 0000000..8216bf0
--- /dev/null
+++ b/bbb_cape/src/cape/uart.h
@@ -0,0 +1,20 @@
+#ifndef CAPE_UART_H_
+#define CAPE_UART_H_
+
+#include <stdint.h>
+
+// This file deals with USART1. It sends bytes from a buffer or receives bytes
+// into a buffer and then calls a callback function.
+
+// See uart_common_configure in uart_common.h for details.
+void uart_configure(int baud);
+
+// Callbacks to be implemented by the user.
+// Implemented as weak symbols that do nothing by default.
+void uart_transmit_callback(int bytes_transmitted);
+void uart_receive_callback(int bytes_received);
+
+void uart_transmit(int bytes, uint8_t *data);
+void uart_receive(int bytes, uint8_t *data);
+
+#endif  // CAPE_UART_H_
diff --git a/bbb_cape/src/cape/uart_common.c b/bbb_cape/src/cape/uart_common.c
new file mode 100644
index 0000000..3b70764
--- /dev/null
+++ b/bbb_cape/src/cape/uart_common.c
@@ -0,0 +1,14 @@
+#include "cape/uart_common.h"
+#include "cape/uart_common_private.h"
+
+#define FPCLK 60000000
+
+void uart_common_configure(int baud) {
+  // baud = 60MHz / (8 * (2 - OVER8) * (mantissa / fraction))
+  int fraction = 8;  // the biggest it can be with OVER8=0
+  int mantissa = FPCLK * (16 /* 8 * (2 - OVER8) */ / fraction) / baud;
+  UART->BRR = (mantissa << 4) | fraction;
+  UART->CR1 = USART_CR1_UE /* enable it */ |
+      USART_CR1_M /* 9th bit for the parity */ |
+      USART_CR1_PCE /* enable parity (even by default) */;
+}
diff --git a/bbb_cape/src/cape/uart_common.h b/bbb_cape/src/cape/uart_common.h
new file mode 100644
index 0000000..bb98a07
--- /dev/null
+++ b/bbb_cape/src/cape/uart_common.h
@@ -0,0 +1,11 @@
+#ifndef CAPE_UART_COMMON_H_
+#define CAPE_UART_COMMON_H_
+
+// Configures it for baud, 1 start bit, 1 stop bit, and 1 even parity bit.
+// baud must be between 57KHz and 3.75MHz. It must be something that the USART can
+// handle exactly (see
+// <http://www.st.com/web/en/resource/technical/document/reference_manual/CD00225773.pdf#page=633>
+// or so for choices (fPCLK = 60MHz, OVER8 = 0 for now)).
+void uart_common_configure(int baud);
+
+#endif  // CAPE_UART_COMMON_H_
diff --git a/bbb_cape/src/cape/uart_common_private.h b/bbb_cape/src/cape/uart_common_private.h
new file mode 100644
index 0000000..3836b29
--- /dev/null
+++ b/bbb_cape/src/cape/uart_common_private.h
@@ -0,0 +1,8 @@
+#ifndef CAPE_UART_COMMON_PRIVATE_H_
+#define CAPE_UART_COMMON_PRIVATE_H_
+
+#include <STM32F2XX.h>
+
+#define UART USART1
+
+#endif  // CAPE_UART_COMMON_PRIVATE_H_
diff --git a/bbb_cape/src/cape/util.h b/bbb_cape/src/cape/util.h
new file mode 100644
index 0000000..b374ac8
--- /dev/null
+++ b/bbb_cape/src/cape/util.h
@@ -0,0 +1,11 @@
+#ifndef CAPE_UTIL_H_
+#define CAPE_UTIL_H_
+
+#define ALIAS_WEAK(f) __attribute__ ((weak, alias (#f)))
+
+// Prevents the compiler from reordering memory operations around this.
+static inline void compiler_memory_barrier(void) {
+  __asm__ __volatile__("" ::: "memory");
+}
+
+#endif  // CAPE_UTIL_H_