 
 void six_in(int value, byte *target) {
   for (int i = 5; i >= 0; i--, value /= 10) target[i] = '0'+(value%10);
+}
+
+int six_out(byte *target) {
+  int s = 0;
+  for (int i = 0; i < 6; i++) {
+    byte c = target[i];
+    if (c < '0' || c > '9') return -1;
+    s = 10*s + (c-'0');
+  }
+  return s;
+}
+
+void parse_time_error() {
+  halt_channels();
+  memcpy(errormsg, "bad time    ", 12);
+  running = -1;
+}
+
+Dura parse_time(byte *target) {
+  Dura result;
+  result.s = 0;
+  result.u = 0;
+  int s = six_out(target);
+  if (s < 0) parse_time_error();
+  else {
+    byte c = target[6];
+    if (c == 's') result.s = s;
+    else if (c == 'u') result.u = s;
+    else if (c == 'm') { result.s = s/1000; result.u = s%1000; }
+    else parse_time_error();
+  }
+  return result;
 }
 
 volatile int count_me;

    else if (cmd == '>') shift_timing(1);
    else if (cmd == '<') shift_timing(-1);
    else if ((cmd >= '0' && cmd <= '9') || (cmd >= 'A' && cmd <= 'N'))
                         run_command_on(channels + DEHEXT(cmd));
    else if (cmd == '@') run_command_on(channels + DIG);
    else if (cmd == '.') abort_and_reset();
    else if (cmd == '"') reset_parameters();
    else if (cmd == ':') report_identity();

  ARM_DEMCR |= ARM_DEMCR_TRCENA;
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
  for (int i = 0; i < PROT; i++) protocols[i].next = 255;
  for (int j = 0; j < CHAN; j++) {
    channels[j].protocol = 255;
    if (j < DIG) channels[j].disabled = (j >= digiN) ? 1 : 0;
    else channels[j].disabled = (waveN < 1) ? 1 : 0;
  }
  running = 0;
  blinking.s = 0;
  blinking.u = 1000;   // 1 ms delay before doing anything
  pending_shift.s = 0;
  pending_shift.u = 0;
  tick = micros();
  bufi = 0;
}
