about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--012transform.cc7
-rw-r--r--023jump.cc3
-rw-r--r--026assert.cc4
-rw-r--r--027debug.cc16
-rw-r--r--039wait.cc2
-rw-r--r--040brace.cc1
-rw-r--r--047jump_label.cc93
-rw-r--r--050scenario.cc1
-rw-r--r--061channel.mu31
-rw-r--r--070display.cc54
-rw-r--r--071print.mu90
-rw-r--r--chessboard.mu6
-rw-r--r--termbox/libtermbox.abin0 -> 51148 bytes
-rw-r--r--termbox/tags173
-rw-r--r--termbox/termbox.c5
-rw-r--r--termbox/termbox.obin0 -> 48352 bytes
-rw-r--r--termbox/utf8.obin0 -> 2320 bytes
17 files changed, 443 insertions, 43 deletions
diff --git a/012transform.cc b/012transform.cc
index 209eb5d9..b95b036c 100644
--- a/012transform.cc
+++ b/012transform.cc
@@ -47,9 +47,6 @@ void parse_int_reagents() {
 
 void populate_value(reagent& r) {
   if (r.initialized) return;
-  char* end = NULL;
-  int result = strtol(r.name.c_str(), &end, /*any base*/0);
-  if (*end != '\0') return;
-//?   cout << "setting value\n"; //? 1
-  r.set_value(result);
+  if (!is_number(r.name)) return;
+  r.set_value(to_int(r.name));
 }
diff --git a/023jump.cc b/023jump.cc
index e9bd5011..807113f3 100644
--- a/023jump.cc
+++ b/023jump.cc
@@ -6,6 +6,7 @@ JUMP,
 Recipe_number["jump"] = JUMP;
 :(before "End Primitive Recipe Implementations")
 case JUMP: {
+  assert(current_instruction().ingredients.at(0).initialized);
   assert(ingredients.size() == 1);
   assert(ingredients.at(0).size() == 1);  // scalar
   instruction_counter += ingredients.at(0).at(0);
@@ -40,6 +41,7 @@ JUMP_IF,
 Recipe_number["jump-if"] = JUMP_IF;
 :(before "End Primitive Recipe Implementations")
 case JUMP_IF: {
+  assert(current_instruction().ingredients.at(1).initialized);
   assert(ingredients.size() == 2);
   assert(ingredients.at(0).size() == 1);  // scalar
   if (!ingredients.at(0).at(0)) {
@@ -79,6 +81,7 @@ JUMP_UNLESS,
 Recipe_number["jump-unless"] = JUMP_UNLESS;
 :(before "End Primitive Recipe Implementations")
 case JUMP_UNLESS: {
+  assert(current_instruction().ingredients.at(1).initialized);
   assert(ingredients.size() == 2);
   assert(ingredients.at(0).size() == 1);  // scalar
   if (ingredients.at(0).at(0)) {
diff --git a/026assert.cc b/026assert.cc
index 277ade39..7217d5c2 100644
--- a/026assert.cc
+++ b/026assert.cc
@@ -15,7 +15,9 @@ case ASSERT: {
   assert(ingredients.at(0).size() == 1);  // scalar
   if (!ingredients.at(0).at(0)) {
     assert(isa_literal(current_instruction().ingredients.at(1)));
-    raise << current_instruction().ingredients.at(1).name << '\n';
+//?     tb_shutdown(); //? 1
+    raise << current_instruction().ingredients.at(1).name << '\n' << die();
+//?     exit(0); //? 1
   }
   break;
 }
diff --git a/027debug.cc b/027debug.cc
index 60b38319..1e313d98 100644
--- a/027debug.cc
+++ b/027debug.cc
@@ -54,11 +54,21 @@ case _EXIT: {
 }
 
 :(before "End Primitive Recipe Declarations")
-_DUMP_LAYER,
+_START_DUMPING_LAYER,
 :(before "End Primitive Recipe Numbers")
-Recipe_number["$dump-layer"] = _DUMP_LAYER;
+Recipe_number["$start-dumping-layer"] = _START_DUMPING_LAYER;
 :(before "End Primitive Recipe Implementations")
-case _DUMP_LAYER: {
+case _START_DUMPING_LAYER: {
   Trace_stream->dump_layer = current_instruction().ingredients.at(0).name;
   break;
 }
+
+:(before "End Primitive Recipe Declarations")
+_DUMP_TRACE,
+:(before "End Primitive Recipe Numbers")
+Recipe_number["$dump-trace"] = _DUMP_TRACE;
+:(before "End Primitive Recipe Implementations")
+case _DUMP_TRACE: {
+  DUMP("");
+  break;
+}
diff --git a/039wait.cc b/039wait.cc
index 5d4acebd..82354a53 100644
--- a/039wait.cc
+++ b/039wait.cc
@@ -130,7 +130,7 @@ case SWITCH: {
   index_t id = some_other_running_routine();
   if (id) {
     assert(id != Current_routine->id);
-    cerr << "waiting on " << id << " from " << Current_routine->id << '\n';
+//?     cerr << "waiting on " << id << " from " << Current_routine->id << '\n'; //? 1
     Current_routine->state = WAITING;
     Current_routine->waiting_on_routine = id;
   }
diff --git a/040brace.cc b/040brace.cc
index fa8255ef..224dbf96 100644
--- a/040brace.cc
+++ b/040brace.cc
@@ -308,6 +308,7 @@ recipe main [
 +after-brace: jump 2:offset
 
 :(scenario break_label)
+% Hide_warnings = true;
 recipe main [
   1:integer <- copy 0:literal
   {
diff --git a/047jump_label.cc b/047jump_label.cc
new file mode 100644
index 00000000..9bc0c442
--- /dev/null
+++ b/047jump_label.cc
@@ -0,0 +1,93 @@
+//: Support jumps to labels.
+//: We'll also treat 'break' and 'continue' as jumps. The choice of name is
+//: just documentation about intent.
+
+:(scenario jump_to_label)
+recipe main [
+  jump +target:offset
+  1:integer <- copy 0:literal
+  +target
+]
+-mem: storing 0 in location 1
+
+:(after "int main")
+  Transform.push_back(transform_labels);
+
+:(code)
+void transform_labels(const recipe_number r) {
+  map<string, index_t> offset;
+  for (index_t i = 0; i < Recipe[r].steps.size(); ++i) {
+    const instruction& inst = Recipe[r].steps.at(i);
+    if (!inst.label.empty()) offset[inst.label] = i;
+  }
+  for (index_t i = 0; i < Recipe[r].steps.size(); ++i) {
+    instruction& inst = Recipe[r].steps.at(i);
+    if (inst.operation == Recipe_number["jump"]) {
+//?       cerr << inst.to_string() << '\n'; //? 1
+      replace_offset(inst.ingredients.at(0), offset, r);
+    }
+    if (inst.operation == Recipe_number["jump-if"] || inst.operation == Recipe_number["jump-unless"]) {
+      replace_offset(inst.ingredients.at(1), offset, r);
+    }
+    if ((inst.operation == Recipe_number["loop"] || inst.operation == Recipe_number["break"])
+        && inst.ingredients.size() == 1) {
+      replace_offset(inst.ingredients.at(0), offset, r);
+    }
+    if ((inst.operation == Recipe_number["loop-if"] || inst.operation == Recipe_number["loop-unless"]
+            || inst.operation == Recipe_number["break-if"] || inst.operation == Recipe_number["break-unless"])
+        && inst.ingredients.size() == 2) {
+      replace_offset(inst.ingredients.at(1), offset, r);
+    }
+  }
+}
+
+:(code)
+void replace_offset(reagent& x, /*const*/ map<string, index_t>& offset, const recipe_number r) {
+//?   cerr << "AAA " << x.to_string() << '\n'; //? 1
+  assert(isa_literal(x));
+//?   cerr << "BBB " << x.to_string() << '\n'; //? 1
+  assert(!x.initialized);
+//?   cerr << "CCC " << x.to_string() << '\n'; //? 1
+  if (is_number(x.name)) return;  // non-labels will be handled like other integer operands
+//?   cerr << "DDD " << x.to_string() << '\n'; //? 1
+  if (offset.find(x.name) == offset.end())
+    raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n';
+  x.set_value(offset[x.name]);
+}
+
+:(scenario break_to_label)
+recipe main [
+#?   $print [aaa]
+  {
+    {
+      break +target:offset
+      1:integer <- copy 0:literal
+    }
+  }
+  +target
+]
+-mem: storing 0 in location 1
+
+:(scenario jump_if_to_label)
+recipe main [
+  {
+    {
+      jump-if 1:literal, +target:offset
+      1:integer <- copy 0:literal
+    }
+  }
+  +target
+]
+-mem: storing 0 in location 1
+
+:(scenario loop_unless_to_label)
+recipe main [
+  {
+    {
+      loop-unless 0:literal, +target:offset  # loop/break with a label don't care about braces
+      1:integer <- copy 0:literal
+    }
+  }
+  +target
+]
+-mem: storing 0 in location 1
diff --git a/050scenario.cc b/050scenario.cc
index 9a88d278..d950b7ae 100644
--- a/050scenario.cc
+++ b/050scenario.cc
@@ -88,6 +88,7 @@ time_t mu_time; time(&mu_time);
 cerr << "\nMu tests: " << ctime(&mu_time);
 for (index_t i = 0; i < Scenarios.size(); ++i) {
 //?   cerr << Passed << '\n'; //? 1
+//?   cerr << i << ": " << Scenarios.at(i).name << '\n'; //? 1
   run_mu_scenario(Scenarios.at(i));
   if (Passed) cerr << ".";
 }
diff --git a/061channel.mu b/061channel.mu
index 33b2566f..79f4dd02 100644
--- a/061channel.mu
+++ b/061channel.mu
@@ -276,13 +276,38 @@ recipe buffer-lines [
     line:address:buffer <- init-buffer, 30:literal
     # read characters from 'in' until newline, copy into line
     {
+      +next-character
       c:character, in:address:channel <- read in:address:channel
-      # todo: handle backspace
+      # drop a character on backspace
+      {
+        # special-case: if it's a backspace
+        backspace?:boolean <- equal c:character, 8:literal
+        break-unless backspace?:boolean
+        # drop previous character
+#?         return-to-console #? 2
+#?         $print [backspace! #? 1
+#? ] #? 1
+        buffer-length:address:integer <- get-address line:address:buffer/deref, length:offset
+        {
+          buffer-empty?:boolean <- equal buffer-length:address:integer/deref, 0:literal
+          break-if buffer-empty?:boolean
+#?           $print [before: ], buffer-length:address:integer/deref, [ #? 1
+#? ] #? 1
+          buffer-length:address:integer/deref <- subtract buffer-length:address:integer/deref, 1:literal
+#?           $print [after: ], buffer-length:address:integer/deref, [ #? 1
+#? ] #? 1
+        }
+#?         $exit #? 2
+        # and don't append this one
+        loop +next-character:label
+      }
+      # append anything else
       line:address:buffer <- buffer-append line:address:buffer, c:character
       line-done?:boolean <- equal c:character, 13:literal/newline
       break-if line-done?:boolean
       loop
     }
+#?     return-to-console #? 1
     # copy line into 'out'
     i:integer <- copy 0:literal
     line-contents:address:array:character <- get line:address:buffer/deref, data:offset
@@ -292,9 +317,13 @@ recipe buffer-lines [
       break-if done?:boolean
       c:character <- index line-contents:address:array:character/deref, i:integer
       out:address:channel <- write out:address:channel, c:character
+#?       $print [writing ], i:integer, [: ], c:character, [ #? 1
+#? ] #? 1
       i:integer <- add i:integer, 1:literal
       loop
     }
+#?     $dump-trace #? 1
+#?     $exit #? 1
     loop
   }
   reply out:address:channel/same-as-ingredient:1
diff --git a/070display.cc b/070display.cc
index d420d2f8..938d3c8f 100644
--- a/070display.cc
+++ b/070display.cc
@@ -68,8 +68,8 @@ case PRINT_CHARACTER_TO_DISPLAY: {
   size_t width = (w >= 0) ? w : 0;
   assert(ingredients.at(0).size() == 1);  // scalar
   long long int c = ingredients.at(0).at(0);
-  if (c == '\n') {
-    if (Display_row < height) {
+  if (c == '\n' || c == '\r') {
+    if (Display_row < height-1) {
       Display_column = 0;
       ++Display_row;
       tb_set_cursor(Display_column, Display_row);
@@ -77,9 +77,18 @@ case PRINT_CHARACTER_TO_DISPLAY: {
     }
     break;
   }
+  if (c == '\b') {
+    if (Display_column > 0) {
+      tb_change_cell(Display_column-1, Display_row, ' ', TB_WHITE, TB_DEFAULT);
+      --Display_column;
+      tb_set_cursor(Display_column, Display_row);
+      tb_present();
+    }
+    break;
+  }
   tb_change_cell(Display_column, Display_row, c, TB_WHITE, TB_DEFAULT);
-  if (Display_column < width) {
-    Display_column++;
+  if (Display_column < width-1) {
+    ++Display_column;
     tb_set_cursor(Display_column, Display_row);
   }
   tb_present();
@@ -119,9 +128,13 @@ MOVE_CURSOR_DOWN_ON_DISPLAY,
 Recipe_number["move-cursor-down-on-display"] = MOVE_CURSOR_DOWN_ON_DISPLAY;
 :(before "End Primitive Recipe Implementations")
 case MOVE_CURSOR_DOWN_ON_DISPLAY: {
-  Display_row++;
-  tb_set_cursor(Display_column, Display_row);
-  tb_present();
+  int h=tb_height();
+  size_t height = (h >= 0) ? h : 0;
+  if (Display_row < height-1) {
+    Display_row++;
+    tb_set_cursor(Display_column, Display_row);
+    tb_present();
+  }
   break;
 }
 
@@ -131,9 +144,11 @@ MOVE_CURSOR_UP_ON_DISPLAY,
 Recipe_number["move-cursor-up-on-display"] = MOVE_CURSOR_UP_ON_DISPLAY;
 :(before "End Primitive Recipe Implementations")
 case MOVE_CURSOR_UP_ON_DISPLAY: {
-  Display_row--;
-  tb_set_cursor(Display_column, Display_row);
-  tb_present();
+  if (Display_row > 0) {
+    Display_row--;
+    tb_set_cursor(Display_column, Display_row);
+    tb_present();
+  }
   break;
 }
 
@@ -143,9 +158,13 @@ MOVE_CURSOR_RIGHT_ON_DISPLAY,
 Recipe_number["move-cursor-right-on-display"] = MOVE_CURSOR_RIGHT_ON_DISPLAY;
 :(before "End Primitive Recipe Implementations")
 case MOVE_CURSOR_RIGHT_ON_DISPLAY: {
-  Display_column++;
-  tb_set_cursor(Display_column, Display_row);
-  tb_present();
+  int w=tb_width();
+  size_t width = (w >= 0) ? w : 0;
+  if (Display_column < width-1) {
+    Display_column++;
+    tb_set_cursor(Display_column, Display_row);
+    tb_present();
+  }
   break;
 }
 
@@ -155,9 +174,11 @@ MOVE_CURSOR_LEFT_ON_DISPLAY,
 Recipe_number["move-cursor-left-on-display"] = MOVE_CURSOR_LEFT_ON_DISPLAY;
 :(before "End Primitive Recipe Implementations")
 case MOVE_CURSOR_LEFT_ON_DISPLAY: {
-  Display_column--;
-  tb_set_cursor(Display_column, Display_row);
-  tb_present();
+  if (Display_column > 0) {
+    Display_column--;
+    tb_set_cursor(Display_column, Display_row);
+    tb_present();
+  }
   break;
 }
 
@@ -191,6 +212,7 @@ case READ_KEY_FROM_KEYBOARD: {
 //?   cerr << event_type << '\n'; //? 1
   if (event_type == TB_EVENT_KEY) {
     if (event.key == TB_KEY_CTRL_C) tb_shutdown(), exit(1);
+    if (event.key == TB_KEY_BACKSPACE2) event.key = TB_KEY_BACKSPACE;
     result = event.key ? event.key : event.ch;
     found = true;
   }
diff --git a/071print.mu b/071print.mu
index c78bc069..d6c72b32 100644
--- a/071print.mu
+++ b/071print.mu
@@ -56,27 +56,52 @@ recipe print-character [
   default-space:address:array:location <- new location:type, 30:literal
   x:address:screen <- next-ingredient
   c:character <- next-ingredient
-#?   $print x:address:character, [ print-character #? 1
-#? ] #? 1
   {
     # if x exists
+    # (handle special cases exactly like in the real screen)
     break-unless x:address:screen
-#?   $print [print-character2 #? 1
-#? ] #? 1
-    # save character in fake screen
     row:address:integer <- get-address x:address:screen/deref, cursor-row:offset
-#?     $print [CCC: ], row:address:integer, [ -> ], row:address:integer/deref, [ #? 1
-#? ] #? 1
-#?     $stop-tracing #? 1
     column:address:integer <- get-address x:address:screen/deref, cursor-column:offset
     width:integer <- get x:address:screen/deref, num-columns:offset
+    height:integer <- get x:address:screen/deref, num-rows:offset
+    max-row:integer <- subtract height:integer, 1:literal
+    # special-case: newline
+    {
+      newline?:boolean <- equal c:character, 13:literal
+#?       $print c:character, [ ], newline?:boolean, [ #? 1
+#? ] #? 1
+      break-unless newline?:boolean
+      {
+        # unless cursor is already at bottom
+        at-bottom?:boolean <- greater-or-equal row:address:integer/deref, max-row:integer
+        break-if at-bottom?:boolean
+        # move it to the next row
+        column:address:integer/deref <- copy 0:literal
+        row:address:integer/deref <- add row:address:integer/deref, 1:literal
+      }
+      reply x:address:screen/same-as-ingredient:0
+    }
+    # save character in fake screen
     index:integer <- multiply row:address:integer/deref, width:integer
     index:integer <- add index:integer, column:address:integer/deref
     buf:address:array:character <- get x:address:screen/deref, data:offset
     cursor:address:character <- index-address buf:address:array:character/deref, index:integer
-#?     $print cursor:address:character, [ #? 1
-#? ] #? 1
-    cursor:address:character/deref <- copy c:character  # todo: newline, etc.
+    # special-case: backspace
+    {
+      backspace?:boolean <- equal c:character, 8:literal
+      break-unless backspace?:boolean
+      {
+        # unless cursor is already at left margin
+        at-left?:boolean <- lesser-or-equal column:address:integer/deref, 0:literal
+        break-if at-left?:boolean
+        # clear previous location
+        column:address:integer/deref <- subtract column:address:integer/deref, 1:literal
+        cursor:address:character <- subtract cursor:address:character, 1:literal
+        cursor:address:character/deref <- copy 32:literal/space
+      }
+      reply x:address:screen/same-as-ingredient:0
+    }
+    cursor:address:character/deref <- copy c:character
     # increment column unless it's already all the way to the right
     {
       at-right?:boolean <- equal column:address:integer/deref, width:integer
@@ -105,6 +130,44 @@ scenario print-character-at-top-left [
   ]
 ]
 
+scenario print-backspace-character [
+  run [
+#?     $start-tracing #? 3
+    1:address:screen <- init-fake-screen 3:literal/width, 2:literal/height
+    1:address:screen <- print-character 1:address:screen, 97:literal  # 'a'
+    1:address:screen <- print-character 1:address:screen, 8:literal  # backspace
+    2:integer <- get 1:address:screen/deref, cursor-column:offset
+    3:address:array:character <- get 1:address:screen/deref, data:offset
+    4:array:character <- copy 3:address:array:character/deref
+  ]
+  memory-should-contain [
+    2 <- 0  # cursor column
+    4 <- 6  # width*height
+    5 <- 32  # space, not 'a'
+    6 <- 0
+  ]
+]
+
+scenario print-newline-character [
+  run [
+#?     $start-tracing #? 3
+    1:address:screen <- init-fake-screen 3:literal/width, 2:literal/height
+    1:address:screen <- print-character 1:address:screen, 97:literal  # 'a'
+    1:address:screen <- print-character 1:address:screen, 13:literal/newline
+    2:integer <- get 1:address:screen/deref, cursor-row:offset
+    3:integer <- get 1:address:screen/deref, cursor-column:offset
+    4:address:array:character <- get 1:address:screen/deref, data:offset
+    5:array:character <- copy 4:address:array:character/deref
+  ]
+  memory-should-contain [
+    2 <- 1  # cursor row
+    3 <- 0  # cursor column
+    5 <- 6  # width*height
+    6 <- 97  # 'a'
+    7 <- 0
+  ]
+]
+
 recipe clear-line [
   default-space:address:array:location <- new location:type, 30:literal
   x:address:screen <- next-ingredient
@@ -115,7 +178,10 @@ recipe clear-line [
     column:address:integer <- get-address x:address:screen/deref, cursor-column:offset
     original-column:integer <- copy column:address:integer/deref
     # space over the entire line
+#?     $start-tracing #? 1
     {
+#?       $print column:address:integer/deref, [ #? 1
+#? ] #? 1
       done?:boolean <- greater-or-equal column:address:integer/deref, n:integer
       break-if done?:boolean
       print-character x:address:screen, [ ]  # implicitly updates 'column'
@@ -165,7 +231,7 @@ recipe move-cursor [
 
 scenario clear-line-erases-printed-characters [
   run [
-#?     $start-tracing #? 3
+#?     $start-tracing #? 4
     1:address:screen <- init-fake-screen 3:literal/width, 2:literal/height
     # print a character
     1:address:screen <- print-character 1:address:screen, 97:literal  # 'a'
diff --git a/chessboard.mu b/chessboard.mu
index 3481c19b..003f3116 100644
--- a/chessboard.mu
+++ b/chessboard.mu
@@ -157,6 +157,7 @@ recipe read-move [
   default-space:address:array:location <- new location:type, 30:literal
   stdin:address:channel <- next-ingredient
   from-file:integer <- read-file stdin:address:channel
+#?   return-to-console #? 1
   {
     q-pressed?:boolean <- lesser-than from-file:integer, 0:literal
     break-unless q-pressed?:boolean
@@ -173,6 +174,7 @@ recipe read-move [
   x:address:integer/deref <- read-file stdin:address:channel
   x:address:integer <- get-address result:address:move/deref, to-rank:offset
   x:address:integer/deref <- read-rank stdin:address:channel
+#?   $exit #? 1
   expect-from-channel stdin:address:channel, 13:literal  # newline
   reply result:address:move
 ]
@@ -192,6 +194,8 @@ recipe read-file [
     reply -1:literal
   }
   file:integer <- subtract c:character, 97:literal  # 'a'
+#?   $print file:integer, [ #? 1
+#? ] #? 1
   # 'a' <= file <= 'h'
   above-min:boolean <- greater-or-equal file:integer, 0:literal
   assert above-min:boolean [file too low]
@@ -215,6 +219,8 @@ recipe read-rank [
     reply -1:literal
   }
   rank:integer <- subtract c:character, 49:literal  # '1'
+#?   $print rank:integer, [ #? 1
+#? ] #? 1
   # assert'1' <= rank <= '8'
   above-min:boolean <- greater-or-equal rank:integer 0:literal
   assert above-min:boolean [rank too low]
diff --git a/termbox/libtermbox.a b/termbox/libtermbox.a
new file mode 100644
index 00000000..4b6742c1
--- /dev/null
+++ b/termbox/libtermbox.a
Binary files differdiff --git a/termbox/tags b/termbox/tags
new file mode 100644
index 00000000..0a798f4e
--- /dev/null
+++ b/termbox/tags
@@ -0,0 +1,173 @@
+!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR	Darren Hiebert	/dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME	Exuberant Ctags	//
+!_TAG_PROGRAM_URL	http://ctags.sourceforge.net	/official site/
+!_TAG_PROGRAM_VERSION	5.9~svn20110310	//
+CELL	termbox.c	28;"	d	file:
+ENOUGH_DATA_FOR_PARSING	termbox.c	515;"	d	file:
+IS_CURSOR_HIDDEN	termbox.c	29;"	d	file:
+LAST_ATTR_INIT	termbox.c	400;"	d	file:
+LAST_COORD_INIT	termbox.c	30;"	d	file:
+TB_BLACK	termbox.h	20;"	d
+TB_BLUE	termbox.h	24;"	d
+TB_BOLD	termbox.h	31;"	d
+TB_CYAN	termbox.h	26;"	d
+TB_DEFAULT	termbox.h	19;"	d
+TB_EFAILED_TO_OPEN_TTY	termbox.h	39;"	d
+TB_EOF	termbox.h	203;"	d
+TB_EPIPE_TRAP_ERROR	termbox.h	42;"	d
+TB_EUNSUPPORTED_TERMINAL	termbox.h	38;"	d
+TB_EVENT_KEY	termbox.h	97;"	d
+TB_EVENT_MOUSE	termbox.h	99;"	d
+TB_EVENT_RESIZE	termbox.h	98;"	d
+TB_GREEN	termbox.h	22;"	d
+TB_HIDE_CURSOR	termbox.h	73;"	d
+TB_KEY_ARROW_DOWN	termbox.h	125;"	d
+TB_KEY_ARROW_LEFT	termbox.h	126;"	d
+TB_KEY_ARROW_RIGHT	termbox.h	127;"	d
+TB_KEY_ARROW_UP	termbox.h	124;"	d
+TB_KEY_BACKSPACE	termbox.h	144;"	d
+TB_KEY_BACKSPACE2	termbox.h	178;"	d
+TB_KEY_CTRL_2	termbox.h	136;"	d
+TB_KEY_CTRL_3	termbox.h	168;"	d
+TB_KEY_CTRL_4	termbox.h	169;"	d
+TB_KEY_CTRL_5	termbox.h	171;"	d
+TB_KEY_CTRL_6	termbox.h	173;"	d
+TB_KEY_CTRL_7	termbox.h	174;"	d
+TB_KEY_CTRL_8	termbox.h	179;"	d
+TB_KEY_CTRL_A	termbox.h	137;"	d
+TB_KEY_CTRL_B	termbox.h	138;"	d
+TB_KEY_CTRL_BACKSLASH	termbox.h	170;"	d
+TB_KEY_CTRL_C	termbox.h	139;"	d
+TB_KEY_CTRL_D	termbox.h	140;"	d
+TB_KEY_CTRL_E	termbox.h	141;"	d
+TB_KEY_CTRL_F	termbox.h	142;"	d
+TB_KEY_CTRL_G	termbox.h	143;"	d
+TB_KEY_CTRL_H	termbox.h	145;"	d
+TB_KEY_CTRL_I	termbox.h	147;"	d
+TB_KEY_CTRL_J	termbox.h	148;"	d
+TB_KEY_CTRL_K	termbox.h	149;"	d
+TB_KEY_CTRL_L	termbox.h	150;"	d
+TB_KEY_CTRL_LSQ_BRACKET	termbox.h	167;"	d
+TB_KEY_CTRL_M	termbox.h	152;"	d
+TB_KEY_CTRL_N	termbox.h	153;"	d
+TB_KEY_CTRL_O	termbox.h	154;"	d
+TB_KEY_CTRL_P	termbox.h	155;"	d
+TB_KEY_CTRL_Q	termbox.h	156;"	d
+TB_KEY_CTRL_R	termbox.h	157;"	d
+TB_KEY_CTRL_RSQ_BRACKET	termbox.h	172;"	d
+TB_KEY_CTRL_S	termbox.h	158;"	d
+TB_KEY_CTRL_SLASH	termbox.h	175;"	d
+TB_KEY_CTRL_T	termbox.h	159;"	d
+TB_KEY_CTRL_TILDE	termbox.h	135;"	d
+TB_KEY_CTRL_U	termbox.h	160;"	d
+TB_KEY_CTRL_UNDERSCORE	termbox.h	176;"	d
+TB_KEY_CTRL_V	termbox.h	161;"	d
+TB_KEY_CTRL_W	termbox.h	162;"	d
+TB_KEY_CTRL_X	termbox.h	163;"	d
+TB_KEY_CTRL_Y	termbox.h	164;"	d
+TB_KEY_CTRL_Z	termbox.h	165;"	d
+TB_KEY_DELETE	termbox.h	119;"	d
+TB_KEY_END	termbox.h	121;"	d
+TB_KEY_ENTER	termbox.h	151;"	d
+TB_KEY_ESC	termbox.h	166;"	d
+TB_KEY_F1	termbox.h	106;"	d
+TB_KEY_F10	termbox.h	115;"	d
+TB_KEY_F11	termbox.h	116;"	d
+TB_KEY_F12	termbox.h	117;"	d
+TB_KEY_F2	termbox.h	107;"	d
+TB_KEY_F3	termbox.h	108;"	d
+TB_KEY_F4	termbox.h	109;"	d
+TB_KEY_F5	termbox.h	110;"	d
+TB_KEY_F6	termbox.h	111;"	d
+TB_KEY_F7	termbox.h	112;"	d
+TB_KEY_F8	termbox.h	113;"	d
+TB_KEY_F9	termbox.h	114;"	d
+TB_KEY_HOME	termbox.h	120;"	d
+TB_KEY_INSERT	termbox.h	118;"	d
+TB_KEY_MOUSE_LEFT	termbox.h	128;"	d
+TB_KEY_MOUSE_MIDDLE	termbox.h	130;"	d
+TB_KEY_MOUSE_RELEASE	termbox.h	131;"	d
+TB_KEY_MOUSE_RIGHT	termbox.h	129;"	d
+TB_KEY_MOUSE_WHEEL_DOWN	termbox.h	133;"	d
+TB_KEY_MOUSE_WHEEL_UP	termbox.h	132;"	d
+TB_KEY_PGDN	termbox.h	123;"	d
+TB_KEY_PGUP	termbox.h	122;"	d
+TB_KEY_SPACE	termbox.h	177;"	d
+TB_KEY_TAB	termbox.h	146;"	d
+TB_MAGENTA	termbox.h	25;"	d
+TB_RED	termbox.h	21;"	d
+TB_REVERSE	termbox.h	33;"	d
+TB_UNDERLINE	termbox.h	32;"	d
+TB_WHITE	termbox.h	27;"	d
+TB_YELLOW	termbox.h	23;"	d
+WRITE_INT	termbox.c	292;"	d	file:
+WRITE_LITERAL	termbox.c	291;"	d	file:
+back_buffer	termbox.c	/^static struct cellbuf back_buffer;$/;"	v	typeref:struct:cellbuf	file:
+background	termbox.c	/^static uint16_t background = TB_DEFAULT;$/;"	v	file:
+bg	termbox.h	/^  uint16_t bg;  \/* background color and attributes *\/$/;"	m	struct:tb_cell
+buffer_size_change_request	termbox.c	/^static volatile int buffer_size_change_request;$/;"	v	file:
+cellbuf	termbox.c	/^struct cellbuf {$/;"	s	file:
+cellbuf_clear	termbox.c	/^static void cellbuf_clear(struct cellbuf *buf)$/;"	f	file:
+cellbuf_free	termbox.c	/^static void cellbuf_free(struct cellbuf *buf)$/;"	f	file:
+cellbuf_init	termbox.c	/^static void cellbuf_init(struct cellbuf *buf, int width, int height)$/;"	f	file:
+cellbuf_resize	termbox.c	/^static void cellbuf_resize(struct cellbuf *buf, int width, int height)$/;"	f	file:
+cells	termbox.c	/^  struct tb_cell *cells;$/;"	m	struct:cellbuf	typeref:struct:cellbuf::tb_cell	file:
+ch	termbox.h	/^  uint32_t ch;  \/* unicode character *\/$/;"	m	struct:tb_cell
+ch	termbox.h	/^  uint32_t ch;$/;"	m	struct:tb_event
+convertnum	termbox.c	/^static int convertnum(uint32_t num, char* buf) {$/;"	f	file:
+cursor_x	termbox.c	/^static int cursor_x = -1;$/;"	v	file:
+cursor_y	termbox.c	/^static int cursor_y = -1;$/;"	v	file:
+fg	termbox.h	/^  uint16_t fg;  \/* foreground color and attributes *\/$/;"	m	struct:tb_cell
+foreground	termbox.c	/^static uint16_t foreground = TB_DEFAULT;$/;"	v	file:
+front_buffer	termbox.c	/^static struct cellbuf front_buffer;$/;"	v	typeref:struct:cellbuf	file:
+get_term_size	termbox.c	/^static void get_term_size(int *w, int *h)$/;"	f	file:
+h	termbox.h	/^  int32_t h;$/;"	m	struct:tb_event
+height	termbox.c	/^  int height;$/;"	m	struct:cellbuf	file:
+inout	termbox.c	/^static int inout;$/;"	v	file:
+input_buffer	termbox.c	/^static struct bytebuffer input_buffer;$/;"	v	typeref:struct:bytebuffer	file:
+key	termbox.h	/^  uint16_t key;$/;"	m	struct:tb_event
+lastx	termbox.c	/^static int lastx = LAST_COORD_INIT;$/;"	v	file:
+lasty	termbox.c	/^static int lasty = LAST_COORD_INIT;$/;"	v	file:
+orig_tios	termbox.c	/^static struct termios orig_tios;$/;"	v	typeref:struct:termios	file:
+output_buffer	termbox.c	/^static struct bytebuffer output_buffer;$/;"	v	typeref:struct:bytebuffer	file:
+read_up_to	termbox.c	/^static int read_up_to(int n) {$/;"	f	file:
+send_attr	termbox.c	/^static void send_attr(uint16_t fg, uint16_t bg)$/;"	f	file:
+send_char	termbox.c	/^static void send_char(int x, int y, uint32_t c)$/;"	f	file:
+send_clear	termbox.c	/^static void send_clear(void)$/;"	f	file:
+sigwinch_handler	termbox.c	/^static void sigwinch_handler(int xxx)$/;"	f	file:
+tb_cell	termbox.h	/^struct tb_cell {$/;"	s
+tb_cell_buffer	termbox.c	/^struct tb_cell *tb_cell_buffer()$/;"	f
+tb_change_cell	termbox.c	/^void tb_change_cell(int x, int y, uint32_t ch, uint16_t fg, uint16_t bg)$/;"	f
+tb_clear	termbox.c	/^void tb_clear(void)$/;"	f
+tb_event	termbox.h	/^struct tb_event {$/;"	s
+tb_height	termbox.c	/^int tb_height(void)$/;"	f
+tb_init	termbox.c	/^int tb_init(void)$/;"	f
+tb_peek_event	termbox.c	/^int tb_peek_event(struct tb_event *event, int timeout)$/;"	f
+tb_poll_event	termbox.c	/^int tb_poll_event(struct tb_event *event)$/;"	f
+tb_present	termbox.c	/^void tb_present(void)$/;"	f
+tb_set_clear_attributes	termbox.c	/^void tb_set_clear_attributes(uint16_t fg, uint16_t bg)$/;"	f
+tb_set_cursor	termbox.c	/^void tb_set_cursor(int cx, int cy)$/;"	f
+tb_shutdown	termbox.c	/^void tb_shutdown(void)$/;"	f
+tb_utf8_char_length	utf8.c	/^int tb_utf8_char_length(char c)$/;"	f
+tb_utf8_char_to_unicode	utf8.c	/^int tb_utf8_char_to_unicode(uint32_t *out, const char *c)$/;"	f
+tb_utf8_unicode_to_char	utf8.c	/^int tb_utf8_unicode_to_char(char *out, uint32_t c)$/;"	f
+tb_width	termbox.c	/^int tb_width(void)$/;"	f
+termh	termbox.c	/^static int termh = -1;$/;"	v	file:
+termw	termbox.c	/^static int termw = -1;$/;"	v	file:
+type	termbox.h	/^  uint8_t type;$/;"	m	struct:tb_event
+update_size	termbox.c	/^static void update_size(void)$/;"	f	file:
+update_term_size	termbox.c	/^static void update_term_size(void)$/;"	f	file:
+utf8_length	utf8.c	/^static const unsigned char utf8_length[256] = {$/;"	v	file:
+utf8_mask	utf8.c	/^static const unsigned char utf8_mask[6] = {$/;"	v	file:
+w	termbox.h	/^  int32_t w;$/;"	m	struct:tb_event
+wait_fill_event	termbox.c	/^static int wait_fill_event(struct tb_event *event, struct timeval *timeout)$/;"	f	file:
+width	termbox.c	/^  int width;$/;"	m	struct:cellbuf	file:
+winch_fds	termbox.c	/^static int winch_fds[2];$/;"	v	file:
+write_cursor	termbox.c	/^static void write_cursor(int x, int y) {$/;"	f	file:
+write_sgr	termbox.c	/^static void write_sgr(uint16_t fg, uint16_t bg) {$/;"	f	file:
+write_sgr_bg	termbox.c	/^static void write_sgr_bg(uint16_t bg) {$/;"	f	file:
+write_sgr_fg	termbox.c	/^static void write_sgr_fg(uint16_t fg) {$/;"	f	file:
+x	termbox.h	/^  int32_t x;$/;"	m	struct:tb_event
+y	termbox.h	/^  int32_t y;$/;"	m	struct:tb_event
diff --git a/termbox/termbox.c b/termbox/termbox.c
index 13ef1359..2fbbf5b4 100644
--- a/termbox/termbox.c
+++ b/termbox/termbox.c
@@ -130,10 +130,7 @@ int tb_init(void)
 
 void tb_shutdown(void)
 {
-  if (termw == -1) {
-    fputs("tb_shutdown() should not be called twice.", stderr);
-    abort();
-  }
+  if (termw == -1) return;
 
   bytebuffer_puts(&output_buffer, funcs[T_SHOW_CURSOR]);
   bytebuffer_puts(&output_buffer, funcs[T_SGR0]);
diff --git a/termbox/termbox.o b/termbox/termbox.o
new file mode 100644
index 00000000..074741b5
--- /dev/null
+++ b/termbox/termbox.o
Binary files differdiff --git a/termbox/utf8.o b/termbox/utf8.o
new file mode 100644
index 00000000..2506bab2
--- /dev/null
+++ b/termbox/utf8.o
Binary files differ