     i += 14;
   }
   else {
-    six_in(global_clock.s, msg + i);
+    six_write(global_clock.s, msg + i);
     i += 6;
     msg[i++] = 's';
-    six_in(global_clock.u, msg + i);
+    six_write(global_clock.u, msg + i);
     i += 6;
     msg[i++] = 'u';
   }
-  Serial.write(msg, i);
+  write_now(msg, i);
+}
+
+void abort_and_reset() {
+  shiftbuf(2);
+  if (running > 0) halt_channels();
+  running = 0;
+}
+
+void reset_parameters() {
+  shiftbuf(2);  // TODO--actually do something
+}
+
+void report_identity() {
+  shiftbuf(2);
+  Serial.println(whoamiN, DEC);
+  Serial.write(whoami, whoamiN);
   Serial.send_now();
 }
+
+void report_pins() {
+  shiftbuf(2);
+  byte pinout[27];
+  int i = 0;
+  pinout[i++] = '~';
+  for (; i <= digiN; i++) pinout[i] = HEXTRI(digi[i-1]);
+  if (waveN > 0) pinout[i++] = '@';
+  pinout[i++] = '$';
+  Serial.write(pinout, i);
+  Serial.send_now();
+}
+
+
+/*****************************************************
+** Interpretation of global commands with arguments **
+*****************************************************/
 
 void shift_timing(int direction) {
   if (bufi >= 9) {
     Dura my_shift = parse_time(buf + 2);
     shiftbuf(9);
-    if (direction < 0) diminish_by(&pending_shift, my_shift);
-    else advance_from(&pending_shift, my_shift);
-  }
-}
-
-void raise_unknown_command_error(byte cmd, byte chan) {
-  shiftbuf(2);
-  halt_channels();
-  running = -1;
-  memcpy(errormsg, "unknown", 7);
-  memset(errormsg + 7, ' ', errormsgN - 7);
-  errormsg[8] = cmd;
-  errormsg[10] = chan;
-}
+    if (my_shift.s >= 0) {
+      if (direction < 0) diminish_by(&pending_shift, my_shift);
+      else advance_from(&pending_shift, my_shift);
+    }
+  }
+}
+
+
+/******************************************************************
+** Interpretation of channel-specific commands with no arguments **
+******************************************************************/
 
 void abort_channel(Channel *ch) {
   shiftbuf(3);

}

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;

void shiftbuf(int n) {
  if (n > 0) {
    for (int i = n, j = 0; i < bufi; i++, j++) buf[j] = buf[i];
    bufi = (n >= bufi) ? 0 : bufi - n;
  }
}

void start_running() {
  shiftbuf(2);  // TODO--actually do something

  byte unused1;
};

int protoi = 0;            // Index of next empty protocol slot
int proton = 0;            // Total number of protocols
Protocol protocols[PROT];  // Slots for protocols (not necessarily contiguous)

Channel channels[CHAN];

int errormsgN = 12;
byte errormsg[12];

int eepsigN = 8;                    // Stimulator signature size
byte* eepsig = (byte*)"stim1.0 ";   // Expected string
int idN = 12;                       // Size of identifying string that follows
int eN = eepsigN + idN;             // Whole identifier (stim + name)
int whoamiN = 0;                    // Size of string returned to client
byte whoami[24];                    // ~ + eepsig + id + $ + zero terminator

int bufi = 0;    // Input buffer index
int bufN = 63;   // Input buffer size
byte buf[63];    // Input buffer
void shiftbuf(int n) {
  if (n > 0) {
    for (int i = n, j = 0; i < bufi; i++, j++) buf[j] = buf[i];
    bufi = (n >= bufi) ? 0 : bufi - n;
  }
}


int tick;               // Last clock tick (microseconds), used to update time
Dura global_clock;      // Counts up from the start of protocols
