diff --git a/Makefile b/Makefile
index d53b7ae..6c1eb51 100644
--- a/Makefile
+++ b/Makefile
@@ -17,11 +17,14 @@ hello.elf: hello.o
 calc.elf: hex.o calc.o
 	$(LD) -m elf32lriscv -T link.ld $^ -o $@
 
-tests: tests/btohex.elf
+tests: tests/btohex.elf tests/tohex.elf
 
 tests/btohex.elf: hex.o tests/btohex.o
 	$(LD) -m elf32lriscv -T link.ld $^ -o $@
 
+tests/tohex.elf: hex.o tests/tohex.o
+	$(LD) -m elf32lriscv -T link.ld $^ -o $@
+
 %.o : %.s
 	$(AS) $(ASFLAGS) $< -o $@
 
diff --git a/calc.s b/calc.s
index 0504e50..d1be333 100644
--- a/calc.s
+++ b/calc.s
@@ -160,14 +160,3 @@ memcpy_loop:
   j memcpy_loop
 memcpy_done:
   ret
-
-# Write hex representation into buffer
-#    arguments:
-#       a0: value to format
-#       a1: address of buffer to write to
-#   temporaries used:
-#       t0
-#    return:
-#       none
-tohex:
-  
diff --git a/hex.s b/hex.s
index d5f0c2a..034f72e 100644
--- a/hex.s
+++ b/hex.s
@@ -1,4 +1,5 @@
 .global btohex
+.global tohex
 
 .section .rodata
 hexchars:
@@ -6,6 +7,35 @@ hexchars:
 
 .text
 
+# Write hex representation of word into buffer
+#    arguments:
+#       a0: value to format
+#       a1: address of buffer to write to
+#   temporaries used:
+#       t0
+#    return:
+#       none
+tohex:
+    li s0, 0          # set up loop variables
+    li s1, 4
+    addi a1, a1, 6    # offset output address to end chars
+    addi sp, sp, -12  # allocate stack space
+tohex_loop:
+    sw ra, 8(sp)      # save contents of ra
+    sw a0, 4(sp)      # save contents of ra
+    sw a1, 0(sp)      # save contents of ra
+    jal btohex
+    lw a1, 0(sp)      # restore contents of a1
+    sh a0, 0(a1)      # write the two bytes to the output buffer
+    lw a0, 4(sp)      # restore contents of a0
+    lw ra, 8(sp)      # restore contents of ra
+    srli a0, a0, 8    # shift the next byte to format down
+    addi a1, a1, -2   # decrement output buffer address
+    addi s0, s0, 1    # increment loop counter
+    blt s0, s1, tohex_loop # TODO: we don't need the loop counter and output addr
+    addi sp, sp, 12   # deallocate stack space
+    ret
+
 # Convert byte to ASCII hex
 #    arguments:
 #       a0: value to format
@@ -14,16 +44,15 @@ hexchars:
 #    return:
 #       a0: value in ASCII hex
 btohex:
-  andi t0, a0, 0xF      # Mask off lower nybble
-  la   a1, hexchars     # load address of hexchars
-  add  a2, a1, t0       # offset to nybble
-  lbu  t0, 0(a2)        # load hex char at offset
-  srli t1, a0, 4        # shift upper nybble down
-  andi t1, t1, 0xF      # mask off upper nybble
-  add  a2, a1, t1       # offset to nybble
-  lbu  t1, 0(a2)        # load hex char at offset
-  mv a0, t1             # copy upper char to a0
-  slli a0, a0, 8        # shuft upper char up
-  or   a0, a0, t0       # OR the lower char into a0
-  ret
+    la   a1, hexchars     # load address of hexchars
+    andi t0, a0, 0xF      # Mask off lower nybble
+    add  a2, a1, t0       # a2 = offset to nybble hex char
+    lbu  t0, 0(a2)        # load hex char for lower nybble into t0
+    srli t1, a0, 4        # shift upper nybble down
+    andi t1, t1, 0xF      # mask off upper nybble
+    add  a2, a1, t1       # offset to nybble
+    lbu  a0, 0(a2)        # load hex char for upper nybble into a0
+    slli t0, t0, 8        # shift lower char up (little endian)
+    or   a0, a0, t0       # OR the lower char into a0
+    ret
 
diff --git a/tests/test_btohex.sh b/tests/test_btohex.sh
deleted file mode 100644
index c39f321..0000000
--- a/tests/test_btohex.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-test_btohex() {
-  # FIXME: Remove grep when this bug is fixed:
-  # https://github.com/sysprog21/rv32emu/issues/561
-  result=$("${RV32EMU}" -d - -q tests/btohex | grep -v '^\d' | "${JQ}" .x11)
-
-  test $? -eq 0 && test "${result}" -eq 16693 # 16693 is A5 in ASCII
-}
-
diff --git a/tests/test_hex.sh b/tests/test_hex.sh
new file mode 100644
index 0000000..0ad1be0
--- /dev/null
+++ b/tests/test_hex.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+test_btohex() {
+  # FIXME: Remove grep when this bug is fixed:
+  # https://github.com/sysprog21/rv32emu/issues/561
+  result=$("${RV32EMU}" -d - -q tests/btohex.elf | grep -v '^\d' | "${JQ}" .x11)
+
+   # 16693 is 5A in ASCII (reversed from input due to little endian)
+  test $? -eq 0 && test "${result}" -eq 13633
+}
+
+test_tohex() {
+  # FIXME: Remove grep when this bug is fixed:
+  # https://github.com/sysprog21/rv32emu/issues/561
+  result=$("${RV32EMU}" -q tests/tohex.elf | grep -v '^\d')
+
+  test $? -eq 0 && test "${result}" = "CAFEFEED"
+}
diff --git a/tests/tohex.s b/tests/tohex.s
new file mode 100644
index 0000000..ee2a7b7
--- /dev/null
+++ b/tests/tohex.s
@@ -0,0 +1,32 @@
+# Test for btohex
+
+.org 0
+# Provide program starting address to linker
+.global _start
+
+.extern tohex
+
+/* newlib system calls */
+.set SYSEXIT,  93
+.set SYSWRITE, 64
+
+.section .bss
+buf: .skip 9
+
+.text
+_start:
+    li a0, '\n
+    la a1, buf
+    sb a0, 8(a1)        # append newline to buf
+    li a0, 0xCAFEFEED
+    jal tohex
+
+    li t0, SYSWRITE     # "write" syscall
+    li a0, 1            # 1 = standard output (stdout)
+    la a1, buf          # load address of output string
+    li a2, 9            # length of output string
+    ecall               # invoke syscall to print the string
+
+    li t0, SYSEXIT      # "exit" syscall
+    la a0, 0            # Use 0 return code
+    ecall               # invoke syscall to terminate the program