summaryrefslogtreecommitdiff
path: root/lib/lego
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 20:52:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 20:52:35 +0000
commit46439007cf417cbd9ac8049bb4122c890097a0fa (patch)
tree6fdb25e5f3a2b6d5657eb23b35774b631d4d97e4 /lib/lego
parent37da2899f40661e3e9631e497da8dc59b971cbd0 (diff)
20060303-partial
Diffstat (limited to 'lib/lego')
-rw-r--r--lib/lego/llp.h4
-rw-r--r--lib/lego/styx.c1113
-rw-r--r--lib/lego/styx.srec329
-rw-r--r--lib/lego/styx_abp.srec331
4 files changed, 1777 insertions, 0 deletions
diff --git a/lib/lego/llp.h b/lib/lego/llp.h
new file mode 100644
index 00000000..db2c956f
--- /dev/null
+++ b/lib/lego/llp.h
@@ -0,0 +1,4 @@
+#define LLP_ALTERNATING 0x01
+#define LLP_POLL_IMMEDIATE 0x02
+#define LLP_POLL_PERIODIC 0x04
+#define LLP_COMPRESSION 0x08
diff --git a/lib/lego/styx.c b/lib/lego/styx.c
new file mode 100644
index 00000000..4c91953d
--- /dev/null
+++ b/lib/lego/styx.c
@@ -0,0 +1,1113 @@
+/*
+ * styx.c
+ *
+ * A Styx fileserver for a Lego RCX
+ *
+ * Nigel Roles
+ * Vita Nuova
+ *
+ * This is a heavily modified version of test5.c
+ *
+ * I couldn't have done this without Kekoa...
+ *
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Librcx sample program code, released February 9,
+ * 1999.
+ *
+ * The Initial Developer of the Original Code is Kekoa Proudfoot.
+ * Portions created by Kekoa Proudfoot are Copyright (C) 1999
+ * Kekoa Proudfoot. All Rights Reserved.
+ *
+ * Contributor(s): Kekoa Proudfoot <kekoa@graphics.stanford.edu>
+ */
+
+//#include "stdlib.h"
+#include "rom.h"
+
+#include "lib9.h"
+#include "styx.h"
+
+#include "llp.h"
+
+#define ASSERT(cond) if (!(cond)) fatal(__LINE__)
+#define FATAL fatal(__LINE__)
+#define PROGRESS progress(__LINE__)
+
+#if 0
+#define ABP
+#endif
+
+uchar *send_fid_reply_payload(void);
+void send_fid_reply(uchar type, ushort tag, ushort fid, uchar *msg, short len);
+void send_error_reply(unsigned short tag, char *msg);
+
+static unsigned short msgcount;
+static unsigned char compressed_incoming[150];
+static unsigned char incoming[1024];
+static unsigned char compressed_reply[150];
+short compressed_reply_len;
+static unsigned char reply[1024];
+unsigned short reply_len;
+unsigned short transmitted_reply_len;
+unsigned char alternating_bit;
+static uchar dir[116];
+uchar prepared;
+uchar reader_count;
+uchar dispatch[6];
+
+/* ROM pseudofunctions */
+
+static inline void
+set_data_pointer (void *ptr)
+{
+ play_sound_or_set_data_pointer(0x1771, (short)ptr, 0);
+}
+
+static inline char
+check_valid (void)
+{
+ char valid;
+ check_for_data(&valid, NULL);
+ return valid;
+}
+
+static inline int
+receive_message (void *ptr, int len)
+{
+ char bytes = 0;
+ receive_data(ptr, len, &bytes);
+ /* Bytes includes checksum, since we don't want that, return bytes-1 */
+ return bytes - 1;
+}
+
+static inline void
+send_message (void *ptr, int len)
+{
+ if (len)
+ while (send_data(0x1776, 0, ptr, len));
+}
+
+int
+poll_power(void)
+{
+ static short debounce = 0;
+ static short state = -1;
+ short status;
+ get_power_status(0x4000, &status);
+ if (state != status)
+ debounce = 0;
+ else if (debounce < 10)
+ debounce++;
+ state = status;
+ return debounce >= 10 ? state : -1;
+}
+
+static void
+progress(short line)
+{
+ set_lcd_number(LCD_UNSIGNED, line, LCD_DECIMAL_0);
+ refresh_display();
+}
+
+static void
+fatal(short line)
+{
+ set_lcd_segment(LCD_STANDING);
+ progress(line);
+ while (poll_power() != 0)
+ ;
+}
+
+typedef struct Reader {
+ ushort tag;
+ ushort fid;
+ ushort offset;
+ ushort count;
+ struct Reader *next;
+} Reader;
+
+typedef struct DirectoryEntry {
+ char *name;
+ uchar qid;
+ const struct DirectoryEntry *sub;
+ short (*read)(const struct DirectoryEntry *dp, ushort tag, ushort fid, ushort offset, ushort count);
+ short (*write)(const struct DirectoryEntry *dp, ushort offset, ushort count, uchar *buf);
+} DirectoryEntry;
+
+#define QID_ROOT 0
+#define QID_MOTOR 1
+#define QID_MOTOR_0 2
+#define QID_MOTOR_1 3
+#define QID_MOTOR_2 4
+#define QID_MOTOR_012 5
+#define QID_SENSOR 6
+#define QID_SENSOR_0 7
+#define QID_SENSOR_1 8
+#define QID_SENSOR_2 9
+
+typedef struct Sensor {
+ sensor_t sensor;
+ uchar active;
+ uchar greater;
+ ushort thresh;
+ Reader *reader;
+} Sensor;
+
+Sensor sensor[3];
+
+short
+atoin(char *s, short lim)
+{
+ short total = 0;
+ while (*s && lim) {
+ char c = *s++;
+ if (c >= '0' && c <= '9')
+ total = total * 10 + c - '0';
+ else
+ break;
+ lim--;
+ }
+ return total;
+}
+
+short
+itoa(char *buf, short value)
+{
+ char *bp = buf;
+ short divisor;
+ if (value < 0) {
+ *bp++ = '-';
+ value = -value;
+ }
+ if (value == 0)
+ *bp++ = '0';
+ else {
+ divisor = 10000;
+ while (divisor > value)
+ divisor /= 10;
+ while (divisor) {
+ *bp++ = '0' + value / divisor;
+ value %= divisor;
+ divisor /= 10;
+ }
+ }
+ return bp - buf;
+}
+
+Reader *
+readercreate(ushort tag, ushort fid, ushort offset, ushort count)
+{
+ Reader *rp = malloc(sizeof(Reader));
+ rp->tag = tag;
+ rp->fid = fid;
+ rp->offset = offset;
+ rp->count = count;
+ rp->next = 0;
+ reader_count++;
+ return rp;
+}
+
+void
+readerfree(Reader *rp)
+{
+ free(rp);
+ reader_count--;
+}
+
+int
+senderrorreset(Reader *rp, void *magic)
+{
+ send_error_reply(rp->tag, "reset");
+ return 1;
+}
+
+void
+readerlistfindanddestroy(Reader **rpp, int (*action)(Reader *rp, void *magic), void *magic)
+{
+ while (*rpp) {
+ Reader *rp = *rpp;
+ if ((*action)(rp, magic)) {
+ *rpp = rp->next;
+ readerfree(rp);
+ }
+ else
+ rpp = &(rp->next);
+ }
+}
+
+void
+allreaderlistfindanddestroy(int (*action)(Reader *rp, void *magic), void *magic)
+{
+ short i;
+ for (i = 0; i < 3; i++)
+ readerlistfindanddestroy(&sensor[i].reader, action, magic);
+}
+
+short
+sensorwrite(const DirectoryEntry *dp, ushort offset, ushort count, uchar *data)
+{
+ short i;
+ Sensor *sp;
+ uchar greater;
+ short type, mode;
+ ushort k;
+
+ if (offset != 0)
+ return -1;
+ i = dp->qid - QID_SENSOR_0;
+ sp = &sensor[i];
+ k = count;
+ if (k == 0)
+ return -1;
+ switch (data[0]) {
+ case 'b':
+ type = SENSOR_TYPE_TOUCH;
+ mode = SENSOR_MODE_PULSE;
+ break;
+ case 'l':
+ type = SENSOR_TYPE_TOUCH;
+ mode = SENSOR_MODE_RAW;
+ break;
+ default:
+ return -1;
+ }
+ data++; k--;
+ if (k == 0)
+ return -1;
+ if (*data == '>') {
+ greater = 1;
+ data++;
+ k--;
+ }
+ else if (*data == '<') {
+ greater = 0;
+ data++;
+ k--;
+ }
+ else
+ greater = 1;
+ if (k == 0)
+ return -1;
+ readerlistfindanddestroy(&sp->reader, senderrorreset, 0);
+ set_sensor_passive(SENSOR_0 + i);
+ sp->sensor.type = type;
+ sp->sensor.mode = mode;
+ sp->thresh = atoin(data, k);
+ sp->sensor.raw = 0;
+ sp->sensor.value = 0;
+ sp->sensor.boolean = 0;
+ sp->active = 1;
+ sp->greater = greater;
+ set_sensor_active(SENSOR_0 + i);
+ return count;
+}
+
+void
+send_read_reply(ushort tag, ushort fid, ushort offset, ushort len, uchar *answer, short answerlen)
+{
+ uchar *out = send_fid_reply_payload();
+ ushort actual;
+ if (offset < answerlen) {
+ actual = answerlen - offset;
+ if (actual > len)
+ actual = len;
+ memcpy(out + 3, answer + offset, actual);
+ }
+ else
+ actual = 0;
+ out[0] = actual;
+ out[1] = actual >> 8;
+ out[2] = 0;
+ send_fid_reply(Rread, tag, fid, 0, actual + 3);
+}
+
+void
+send_sensor_read_reply(ushort tag, ushort fid, ushort offset, ushort count, short value)
+{
+ short answerlen;
+ char answer[8];
+ /* reply is countlow counthigh pad data[count] */
+ answerlen = itoa(answer, value);
+ send_read_reply(tag, fid, offset, count, answer, answerlen);
+}
+
+int
+sensortriggered(Sensor *sp)
+{
+ if (sp->greater)
+ return sp->sensor.value >= sp->thresh;
+ else
+ return sp->sensor.value < sp->thresh;
+}
+
+short
+sensorread(const struct DirectoryEntry *dp, ushort tag, ushort fid, ushort offset, ushort count)
+{
+ short i;
+ Sensor *sp;
+ i = dp->qid - QID_SENSOR_0;
+ sp = sensor + i;
+ if (!sp->active)
+ return -1;
+ if (sensortriggered(sp))
+ send_sensor_read_reply(tag, fid, offset, count, sp->sensor.value);
+ else {
+ /* add to queue */
+ Reader *rp = readercreate(tag, fid, offset, count);
+ rp->next = sp->reader;
+ sp->reader = rp;
+ }
+ return 0;
+}
+
+void
+sensorpoll(void)
+{
+ short i;
+ Sensor *sp;
+
+ if ((dispatch[0] & 0x80) == 0) {
+ return;
+ }
+ dispatch[0] &= 0x7f;
+ /* do the following every 3 ms with a following wind */
+ for (i = 0; i < 3; i++) {
+ sp = sensor + i;
+ if (sp->active) {
+ /*
+ * read sensor 4 times to reduce debounce on each
+ * edge to effectively 25 counts, or 75ms
+ * allowing about 8 pulses a second
+ */
+ read_sensor(SENSOR_0 + i, &sp->sensor);
+ read_sensor(SENSOR_0 + i, &sp->sensor);
+ read_sensor(SENSOR_0 + i, &sp->sensor);
+ read_sensor(SENSOR_0 + i, &sp->sensor);
+ if (sensortriggered(sp)) {
+ /* complete any outstanding reads */
+ while (sp->reader) {
+ Reader *rp = sp->reader;
+ sp->reader = rp->next;
+ send_sensor_read_reply(rp->tag, rp->fid, rp->offset, rp->count, sp->sensor.value);
+ readerfree(rp);
+ }
+ }
+ }
+ }
+}
+
+short
+motorparse(uchar *flag, short *mode, short *power, uchar *data)
+{
+ switch (data[0]) {
+ case 'f': *mode = MOTOR_FWD; break;
+ case 'r': *mode = MOTOR_REV; break;
+ case 's': *mode = MOTOR_STOP; break;
+ case 'F': *mode = MOTOR_FLOAT; break;
+ case '-': return 1;
+ default:
+ return 0;
+ }
+ if (data[1] >= '0' && data[1] <= '7')
+ *power = data[1] - '0';
+ else
+ return 0;
+ *flag = 1;
+ return 1;
+}
+
+short
+motorwrite(const DirectoryEntry *dp, ushort offset, ushort count, uchar *data)
+{
+ short mode[3], power[3];
+ uchar flag[3];
+ short i;
+
+ if (offset != 0)
+ return -1;
+ flag[0] = flag[1] = flag[2] = 0;
+ if (dp->qid == QID_MOTOR_012) {
+ if (count != 6)
+ return -1;
+ if (!motorparse(flag, mode, power, data)
+ || !motorparse(flag + 1, mode + 1, power + 1, data + 2)
+ || !motorparse(flag + 2, mode + 2, power + 2, data + 4))
+ return -1;
+ }
+ else {
+ if (count != 2)
+ return -1;
+ i = dp->qid - QID_MOTOR_0;
+ if (!motorparse(flag + i, mode + i, power + i, data))
+ return -1;
+ }
+ for (i = 0; i < 3; i++)
+ if (flag[i])
+ control_motor(MOTOR_0 + i, mode[i], power[i]);
+ return count;
+}
+
+const uchar qid_root[8] = { QID_ROOT, 0, 0, 0x80 };
+
+const DirectoryEntry dir_root[], dir_slash[];
+
+const DirectoryEntry dir_motor[] = {
+ { "..", QID_ROOT, dir_root },
+ { "0", QID_MOTOR_0, 0, 0, motorwrite },
+ { "1", QID_MOTOR_1, 0, 0, motorwrite },
+ { "2", QID_MOTOR_2, 0, 0, motorwrite },
+ { "012", QID_MOTOR_012, 0, 0, motorwrite },
+ { 0 }
+};
+
+const DirectoryEntry dir_sensor[] = {
+ { "..", QID_ROOT, dir_root },
+ { "0", QID_SENSOR_0, 0, sensorread, sensorwrite },
+ { "1", QID_SENSOR_1, 0, sensorread, sensorwrite },
+ { "2", QID_SENSOR_2, 0, sensorread, sensorwrite },
+ { 0 }
+};
+
+const DirectoryEntry dir_root[] = {
+ { "..", QID_ROOT, dir_slash },
+ { "motor", QID_MOTOR, dir_motor },
+ { "sensor", QID_SENSOR, dir_sensor },
+ { 0 }
+};
+
+const DirectoryEntry dir_slash[] = {
+ { "/", QID_ROOT, dir_root },
+ { 0 }
+};
+
+const DirectoryEntry *qid_map[] = {
+ /* QID_ROOT */ &dir_slash[0],
+ /* QID_MOTOR */ &dir_root[1],
+ /* QID_MOTOR_0 */ &dir_motor[1],
+ /* QID_MOTOR_1 */ &dir_motor[2],
+ /* QID_MOTOR_2 */ &dir_motor[3],
+ /* QID_MOTOR_012 */ &dir_motor[4],
+ /* QID_SENSOR */ &dir_root[2],
+ /* QID_SENSOR_0 */ &dir_sensor[1],
+ /* QID_SENSOR_1 */ &dir_sensor[2],
+ /* QID_SENSOR_2 */ &dir_sensor[3],
+};
+
+#define QID_MAP_MAX (sizeof(qid_map) / sizeof(qid_map[0]))
+
+typedef struct Fid {
+ struct Fid *next;
+ ushort fid;
+ uchar open;
+ uchar qid[8];
+} Fid;
+
+Fid *fids;
+
+Fid *
+fidfind(ushort fid)
+{
+ Fid *fp;
+ for (fp = fids; fp && fp->fid != fid; fp = fp->next)
+ ;
+ return fp;
+}
+
+Fid *
+fidcreate(ushort fid, const uchar qid[8])
+{
+ Fid *fp;
+ fp = malloc(sizeof(Fid));
+ ASSERT(fp);
+ fp->open = 0;
+ fp->fid = fid;
+ fp->next = fids;
+ memcpy(fp->qid, qid, 8);
+ fids = fp;
+ return fp;
+}
+
+int
+matchfp(Reader *rp, void *magic)
+{
+ if (rp->fid == ((Fid *)magic)->fid) {
+ return 1;
+ }
+ return 0;
+}
+
+void
+fiddelete(Fid *fp)
+{
+ Fid **fpp;
+ /* clobber any outstanding reads on this fid */
+ allreaderlistfindanddestroy(matchfp, fp);
+ /* now clobber the fid */
+ for (fpp = &fids; *fpp; fpp = &(*fpp)->next)
+ if (*fpp == fp) {
+ *fpp = fp->next;
+ free(fp);
+ return;
+ }
+ FATAL;
+}
+
+const DirectoryEntry *
+nthentry(const DirectoryEntry *dp, ushort n)
+{
+ const DirectoryEntry *sdp;
+ ASSERT(dp->sub);
+ for (sdp = dp->sub; sdp->name; sdp++)
+ if (strcmp(sdp->name, "..") != 0) {
+ if (n == 0)
+ return sdp;
+ n--;
+ }
+ return 0;
+}
+
+int
+fidwalk(Fid *fp, char name[28])
+{
+ const DirectoryEntry *sdp;
+ const DirectoryEntry *dp;
+
+ if (fp->open)
+ return -1;
+ ASSERT(fp->qid[0] < QID_MAP_MAX);
+ dp = qid_map[fp->qid[0]];
+ if (dp->sub == 0)
+ return -1;
+ for (sdp = dp->sub; sdp->name; sdp++)
+ if (strcmp(sdp->name, name) == 0) {
+ fp->qid[0] = sdp->qid;
+ fp->qid[3] = sdp->sub ? 0x80 : 0;
+ return 1;
+ }
+ return 0;
+}
+
+void
+mkdirent(const DirectoryEntry *dp, uchar *dir)
+{
+ memset(dir, 0, DIRLEN);
+ strcpy(dir, dp->name);
+ strcpy(dir + 28, "lego");
+ strcpy(dir + 56, "lego");
+ dir[84] = dp->qid;
+ dir[92] = dp->sub ? 0555 : 0666;
+ dir[93] = dp->sub ? (0555 >> 8) : (0666 >> 8);
+ dir[95] = dp->sub ? 0x80 : 0;
+}
+
+int
+fidstat(Fid *fp, uchar *dir)
+{
+ const DirectoryEntry *dp;
+ if (fp->open)
+ return -1;
+ ASSERT(fp->qid[0] < QID_MAP_MAX);
+ dp = qid_map[fp->qid[0]];
+ mkdirent(dp, dir);
+ return 1;
+}
+
+int
+fidopen(Fid *fp, uchar mode)
+{
+ if (fp->open
+ || (mode & ORCLOSE)
+ /*|| (mode & OTRUNC) */)
+ return 0;
+ if (fp->qid[3] && (mode == OWRITE || mode == ORDWR))
+ /* can't write directories */
+ return 0;
+ fp->open = 1;
+ return 1;
+}
+
+short
+fidread(Fid *fp, ushort tag, ushort offset, ushort count)
+{
+ short k;
+ uchar *p;
+ const DirectoryEntry *dp;
+ uchar *buf;
+
+ ASSERT(fp->qid[0] < QID_MAP_MAX);
+ dp = qid_map[fp->qid[0]];
+
+ if (fp->qid[3] & 0x80) {
+ if (!fp->open)
+ return -1;
+ if (count % DIRLEN != 0 || offset % DIRLEN != 0)
+ return -1;
+ count /= DIRLEN;
+ offset /= DIRLEN;
+ buf = send_fid_reply_payload();
+ p = buf + 3;
+ for (k = 0; k < count; k++) {
+ const DirectoryEntry *sdp = nthentry(dp, offset + k);
+ if (sdp == 0)
+ break;
+ mkdirent(sdp, p);
+ p += DIRLEN;
+ }
+/* a read beyond just returns 0
+ if (k == 0 && count)
+ return -1;
+*/
+ k *= DIRLEN;
+ buf[0] = k;
+ buf[1] = k >> 8;
+ buf[2] = 0;
+ send_fid_reply(Rread, tag, fp->fid, 0, k + 3);
+ return 0;
+ }
+ /* right, that's that out of the way */
+ if (!dp->read)
+ return -1;
+ return (*dp->read)(dp, tag, fp->fid, offset, count);
+}
+
+short
+fidwrite(Fid *fp, ushort offset, ushort count, uchar *buf)
+{
+ const DirectoryEntry *dp;
+ if (fp->qid[3] & 0x80)
+ return -1; /* can't write directories */
+ if (!fp->open)
+ return -1;
+ ASSERT(fp->qid[0] < QID_MAP_MAX);
+ dp = qid_map[fp->qid[0]];
+ if (!dp->write)
+ return -1; /* no write method */
+ return (*dp->write)(dp, offset, count, buf);
+}
+
+int
+rlencode(unsigned char *out, int limit, unsigned char *in, int len)
+{
+ unsigned char *ip, *op;
+ int oc, zc;
+
+ if (len == 0)
+ return -1;
+ ip = in;
+ op = out;
+ zc = 0;
+
+ oc = 0;
+
+ for (;;) {
+ int last = ip >= in + len;
+ if (*ip != 0 || last)
+ {
+ switch (zc) {
+ case 1:
+ if (oc >= len - 1)
+ return -1;
+ *op++ = 0;
+ oc++;
+ break;
+ case 2:
+ if (oc >= len - 2)
+ return -1;
+ *op++ = 0;
+ *op++ = 0;
+ oc += 2;
+ break;
+ case 0:
+ break;
+ default:
+ if (oc >= len - 2)
+ return -1;
+ *op++ = 0x88;
+ *op++ = zc - 2;
+ oc += 2;
+ break;
+ }
+ zc = 0;
+ }
+ if (last)
+ break;
+ if (*ip == 0x88) {
+ if (oc >= len - 2)
+ return -1;
+ *op++ = 0x88;
+ *op++ = 0x00;
+ oc += 2;
+ }
+ else if (*ip == 0x00)
+ {
+ zc++;
+ }
+ else {
+ if (oc >= len - 1)
+ return -1;
+ *op++ = *ip;
+ oc++;
+ }
+ ip++;
+ }
+ return oc;
+}
+
+int
+rldecode(unsigned char *out, unsigned char *in, int len)
+{
+ int oc, k;
+
+ oc = 0;
+
+ while (len) {
+ if (*in != 0x88) {
+ *out++ = *in++;
+ oc++;
+ len--;
+ continue;
+ }
+ in++;
+ switch (*in) {
+ case 0:
+ *out++ = 0x88;
+ oc++;
+ break;
+ default:
+ k = *in + 2;
+ oc += k;
+ while (k-- > 0)
+ *out++ = 0;
+ }
+ in++;
+ len -= 2;
+ }
+ return oc;
+}
+
+void
+prepare_transmission(void)
+{
+ if (prepared)
+ return;
+ compressed_reply_len = rlencode(compressed_reply + 3, sizeof(compressed_reply) - 3, reply, reply_len);
+ if (compressed_reply_len < 0) {
+ memcpy(compressed_reply + 3, reply, reply_len);
+ compressed_reply_len = reply_len;
+ compressed_reply[2] = 0x0;
+ }
+ else
+ compressed_reply[2] = LLP_COMPRESSION;
+ if (reader_count)
+ compressed_reply[2] |= LLP_POLL_PERIODIC;
+ compressed_reply[2] |= !alternating_bit;
+ compressed_reply_len++;
+ compressed_reply[0] = compressed_reply_len;
+ compressed_reply[1] = compressed_reply_len >> 8;
+ compressed_reply_len += 2;
+ prepared = 1;
+}
+
+void
+transmit(void)
+{
+ prepare_transmission();
+ transmitted_reply_len = reply_len;
+ send_message(compressed_reply, compressed_reply_len);
+}
+
+void
+flush_reply_buffer(void)
+{
+ if (reply_len > transmitted_reply_len)
+ memcpy(reply, reply + transmitted_reply_len, reply_len - transmitted_reply_len);
+ reply_len -= transmitted_reply_len;
+ prepared = 0;
+}
+
+void
+send_reply(unsigned char type, unsigned short tag, unsigned char *msg, short len)
+{
+ uchar *p = reply + reply_len;
+ p[0] = type;
+ p[1] = tag & 0xff;
+ p[2] = tag >> 8;
+ if (msg)
+ memcpy(p + 3, msg, len);
+ reply_len += len + 3;
+ prepared = 0;
+}
+
+void
+send_error_reply(unsigned short tag, char *msg)
+{
+ short len;
+ uchar *p = reply + reply_len;
+ p[0] = Rerror;
+ p[1] = tag & 0xff;
+ p[2] = tag >> 8;
+ len = (short)strlen(msg);
+ if (len > 64)
+ len = 64;
+ memcpy(p + 3, msg, len);
+ reply_len += 67;
+ prepared = 0;
+}
+
+uchar *
+send_fid_reply_payload(void)
+{
+ return reply + reply_len + 5;
+}
+
+void
+send_fid_reply(uchar type, ushort tag, ushort fid, uchar *msg, short len)
+{
+ uchar *p = reply + reply_len;
+ p[0] = type;
+ p[1] = tag & 0xff;
+ p[2] = tag >> 8;
+ p[3] = fid & 0xff;
+ p[4] = fid >> 8;
+ if (msg)
+ memcpy(p + 5, msg, len);
+ reply_len += len + 5;
+ prepared = 0;
+}
+
+int
+matchtag(Reader *rp, void *oldtag)
+{
+ if (rp->tag == (ushort)oldtag) {
+ return 1;
+ }
+ return 0;
+}
+
+void
+flushtag(ushort oldtag)
+{
+ /* a little inefficient this - there can be at most one match! */
+ allreaderlistfindanddestroy(matchtag, (void *)oldtag);
+}
+
+void
+process_styx_message(unsigned char *msg, short len)
+{
+ unsigned char type;
+ ushort tag, oldtag, fid, newfid;
+ ushort offset, count;
+ short extra;
+ Fid *fp, *nfp;
+ short written;
+ uchar buf[2];
+
+ ASSERT(len >= 3);
+
+ type = *msg++; len--;
+ tag = (msg[1] << 8) | msg[0]; len -= 2; msg += 2;
+
+ switch (type) {
+ case Tnop:
+ send_reply(Rnop, tag, 0, 0);
+ goto done;
+ case Tflush:
+ ASSERT(len == 2);
+ oldtag = (msg[1] << 8) | msg[0];
+ flushtag(oldtag);
+ send_reply(Rflush, tag, 0, 0);
+ goto done;
+ }
+ /* all other messages take a fid as well */
+ ASSERT(len >= 2);
+ fid = (msg[1] << 8) | msg[0]; len -= 2; msg += 2;
+ fp = fidfind(fid);
+
+ switch (type) {
+ case Tattach:
+ ASSERT(len == 56);
+ if (fp) {
+ fid_in_use:
+ send_error_reply(tag, "fid in use");
+ }
+ else {
+ fp = fidcreate(fid, qid_root);
+ send_fid_reply(Rattach, tag, fid, fp->qid, 8);
+ }
+ break;
+ case Tclunk:
+ case Tremove:
+ ASSERT(len == 0);
+ if (!fp) {
+ no_such_fid:
+ send_error_reply(tag, "no such fid");
+ }
+ else {
+ fiddelete(fp);
+ if (type == Tremove)
+ send_error_reply(tag, "can't remove");
+ else
+ send_fid_reply(Rclunk, tag, fid, 0, 0);
+ }
+ break;
+ case Tclone:
+ ASSERT(len == 2);
+ newfid = (msg[1] << 8) | msg[0];
+ nfp = fidfind(newfid);
+ if (!fp)
+ goto no_such_fid;
+ if (fp->open) {
+ send_error_reply(tag, "can't clone");
+ break;
+ }
+ if (nfp)
+ goto fid_in_use;
+ nfp = fidcreate(newfid, fp->qid);
+ send_fid_reply(Rclone, tag, fid, 0, 0);
+ break;
+ case Twalk:
+ ASSERT(len == 28);
+ if (!fidwalk(fp, msg))
+ send_error_reply(tag, "no such name");
+ else
+ send_fid_reply(Rwalk, tag, fid, fp->qid, 8);
+ break;
+ case Tstat:
+ ASSERT(len == 0);
+ if (!fidstat(fp, dir))
+ send_error_reply(tag, "can't stat");
+ else
+ send_fid_reply(Rstat, tag, fid, dir, 116);
+ break;
+ ASSERT(len == 0);
+ case Tcreate:
+ ASSERT(len == 33);
+ send_error_reply(tag, "can't create");
+ break;
+ case Topen:
+ ASSERT(len == 1);
+ if (!fidopen(fp, msg[0]))
+ send_error_reply(tag, "can't open");
+ else
+ send_fid_reply(Ropen, tag, fid, fp->qid, 8);
+ break;
+ case Tread:
+ ASSERT(len == 10);
+ offset = (msg[1] << 8) | msg[0];
+ count = (msg[9] << 8) | msg[8];
+ if (fidread(fp, tag, offset, count) < 0)
+ send_error_reply(tag, "can't read");
+ break;
+ case Twrite:
+ ASSERT(len >= 11);
+ offset = (msg[1] << 8) | msg[0];
+ count = (msg[9] << 8) | msg[8];
+ msg += 11;
+ len -= 11;
+ ASSERT(count == len);
+ written = fidwrite(fp, offset, count, msg);
+ if (written < 0)
+ send_error_reply(tag, "can't write");
+ else {
+ buf[0] = written;
+ buf[1] = written >> 8;
+ send_fid_reply(Rwrite, tag, fid, buf, 2);
+ }
+ break;
+ default:
+ FATAL;
+ }
+done:
+ ;
+}
+
+void
+process_llp_message(unsigned char *msg, short len)
+{
+ short styxlen;
+ switch (msg[0]) {
+ case 0x45:
+ case 0x4d:
+ if (len != 5)
+ FATAL;
+ styxlen = compressed_incoming[0] | (compressed_incoming[1] << 8);
+ /* transfer the transmitted checksum to the end */
+ compressed_incoming[styxlen + 2 - 1] = msg[3];
+ /* check alternating bit */
+#ifdef ABP
+ if ((compressed_incoming[2] & 1) != alternating_bit ||
+ ((msg[0] & 8) != 0) != alternating_bit) {
+ transmit();
+ break;
+ }
+#endif
+ alternating_bit = !alternating_bit;
+ flush_reply_buffer();
+ if (styxlen > 1) {
+ if (compressed_incoming[2] & LLP_COMPRESSION) {
+ /* decompress everything but length and link header */
+ styxlen = rldecode(incoming, compressed_incoming + 3, styxlen - 1);
+ process_styx_message(incoming, styxlen);
+ }
+ else
+ process_styx_message(compressed_incoming + 3, styxlen - 1);
+ }
+ transmit();
+ break;
+ default:
+ FATAL;
+ }
+}
+
+int
+main (void)
+{
+ int count = 0;
+ char buf[16];
+ char temp[64];
+
+ mem_init();
+ memset(temp,0, sizeof(temp));
+
+ /* Initialize */
+
+ init_timer(&temp[6], &dispatch[0]);
+ init_power();
+ init_sensors();
+ init_serial(&temp[4], &temp[6], 1, 1);
+
+ set_lcd_number(LCD_UNSIGNED, 0, LCD_DECIMAL_0);
+ set_lcd_segment(LCD_WALKING);
+ refresh_display();
+
+ set_data_pointer(compressed_incoming);
+
+ alternating_bit = 0;
+ compressed_reply_len = 0;
+ reply_len = 0;
+ prepared = 0;
+
+ while (poll_power() != 0) {
+
+ /* If a message has arrived, send a response with opcode inverted */
+
+ if (check_valid()) {
+ int len = receive_message(buf, sizeof(buf));
+ msgcount++;
+ process_llp_message(buf, len);
+ }
+ sensorpoll();
+ }
+
+ return 0;
+}
diff --git a/lib/lego/styx.srec b/lib/lego/styx.srec
new file mode 100644
index 00000000..687a50aa
--- /dev/null
+++ b/lib/lego/styx.srec
@@ -0,0 +1,329 @@
+S00C0000737479782E7372656340
+S11880006DF06DF16DF26DF35E00967E6D736D726D716D7054AD
+S118801570446F20796F7520627974652C207768656E20492057
+S10B802A6B6E6F636B3F0000F5
+S11880326DF60D761B870D621B8279014000790029F25E00965F
+S1188047AC6B039A0A6F62FFFE1D23470819226B829A084012E9
+S118805C6B039A08790200091D234E060B036B839A086F61FF76
+S1188071FE6B819A0A6B039A08790200097900FFFF1D234F02CC
+S11880860D100B876D7654706DF60D760D02790330026DF3790F
+S118809B01301F79001FF25E0096BC0B87790027C85E0096D480
+S11880B06D7654706DF60D766DF40D047901300679001B625EB4
+S11880C50096DE0D405E00808E5E0080320D0046F86D746D7656
+S11880DA54706DF60D766DF40D041900684B473C0D1147380B7A
+S11880EF040CBA8AD0AA09422E0CB8F00088D090FF1B01684BC7
+S118810447200D11471C0B040CBA8AD0AA0942120D0209220901
+S118811922092009000CBAF200092040D66D746D7654706DF617
+S118812E0D761B871B876DF46DF56FE0FFFE0D146FE0FFFC0DEA
+S1188143444C16FA2D688A6F62FFFE0D230B036FE3FFFC170CE8
+S118815817040B040D444610FA306F63FFFC68BA0B036FE3FFC5
+S118816DFC404C790527101D454F407901000A0D505E0096EA0C
+S11881820D051D454EF0402E0D510D405E0096EA0C8A8A306F7C
+S118819763FFFC68BA0B036FE3FFFC0D510D405E0096FC0D0448
+S11881AC7901000A0D505E0096EA0D050D5546CE6F62FFFC6F38
+S11881C163FFFE19326FE2FFFC6F60FFFC6D756D740B870B87FD
+S11881D66D7654706DF60D761B876DF46DF50D040D156FE2FF1B
+S11881EBFE7900000A5E00972269846F8500026F62FFFE6F8241
+S118820000046F6200046F82000619226F8200086A0AA40A8AB5
+S1188215016A8AA40A6D756D740B876D7654706DF60D765E006D
+S118822A979C6A0AA40A8AFF6A8AA40A6D7654706DF60D7669C5
+S118823F00790198885E008FD6790000016D7654706DF60D76C2
+S11882541B871B876DF46DF50D056FE1FFFE6FE2FFFC40266990
+S1188269546F61FFFC0D406F62FFFE5D200D00470E6F4200082A
+S118827E69D20D405E0082244006790500080945695246D66DFD
+S1188293756D740B870B876D7654706DF60D761B871B876DF426
+S11882A86DF56FE0FFFE6FE1FFFC19557904A3DC6F62FFFC6F1F
+S11882BD61FFFE0D405E0082508C0E94000B05790200021D25D0
+S11882D24FE46D756D740B870B876D7654706DF60D7679030070
+S11882E70C19376DF46DF56FE2FFFE6F6400040D1147045A0077
+S11882FC83FE6E080002F00088F990FF6FE0FFFC6F65FFFC094E
+S11883115509550955190509557902A3D009520D256F62FFFE7D
+S118832646045A0083FE684BF300790200621D23470C79020088
+S118833B6C1D23470A5A0083FE79020060400219226FE2FFF8B1
+S11883500B046F63FFFE1B030D3346045A0083FE684AAA3E46D3
+S118836504FA014006AA3C460C18AA6EEAFFFB0B041B034006FB
+S118837AFA016EEAFFFB0D3346045A0083FE7901823A0D50881D
+S118838F0C900019226FE3FFF45E0082506F62FFFC8A00921091
+S11883A46FE2FFF66F61FFF6790019C45E0096DEFA0168DA6EE2
+S11883B96AFFF96EDA00016F63FFF40D310D405E0080DC6FD0B7
+S11883CE000A19226FD200026FD200046EDA0006FA016EDA0038
+S11883E3086E6AFFFB6EDA00096F61FFF6790019465E0096DEE7
+S11883F86F60FFFE40047900FFFF6D756D747903000C09376DED
+S118840D7654706DF60D767903000619376DF46DF56FE0FFFE55
+S11884226FE1FFFC0D256F6400085E0090340D031D45443219C6
+S1188437546F6000041D0443046F6400046F62000609526FE243
+S118844C00060D300B800B000D426F6100066FE3FFFA5E0097D9
+S1188461AE6F63FFFA4002194468BC0D420C2A18226EBA0001DE
+S118847618AA6EBA00020B840B046DF419226DF26F62FFFC6F2D
+S118848B61FFFEF80F5E0090480B870B876D756D7479030006D4
+S11884A009376D7654706DF60D767903000C19376DF46DF56FEC
+S11884B5E0FFF66FE1FFF40D257904FFF809646F6100060D4060
+S11884CA5E00812C6DF06DF46F6200046DF20D526F61FFF46F0B
+S11884DF60FFF65E0084108F0697006D756D747903000C093786
+S11884F46D7654706DF60D766E0A0009471019116F0300046FFB
+S118850902000A1D234514400E19116F0300046F02000A1D230B
+S118851E4404790100010D106D7654706DF60D761B876DF46D67
+S1188533F56FE1FFFE0D256E0A0002F2008AF992FF0D240944BD
+S118854809440944192409447902A3D009420D246E4A00084686
+S118855D067900FFFF404E0D405E0084F80D0047206F420004AA
+S11885726DF26F6200066DF26F6200040D516F60FFFE5E00847A
+S1188587A60B870B8740226F6200066DF26F6200040D516F6077
+S118859CFFFE5E0081DA6F42000C6F8200086FC0000C0B871974
+S11885B1006D756D740B876D7654706DF60D761B876DF46DF56A
+S11885C67903A400683A4D045A008686EA7F68BA19440D4509E0
+S11885DB5509550955194509557902A3D009520D256E5A00086F
+S11885F00D430B036FE3FFFE0CAA46045A0086768C0094100D32
+S1188605520D41790014C05E0096AC0D520D41790014C05E0077
+S118861A96AC0D520D41790014C05E0096AC0D520D4179001431
+S118862FC05E0096AC0D505E0084F80D00473840306F54000CD0
+S11886446F4200086FD2000C6F41000269406F5200046DF26F29
+S11886594200066DF26F4200045E0084A60D405E0082240B8741
+S118866E0B876F52000C46CA6F64FFFE790200021D244E045A4A
+S11886830085D86D756D740B876D7654706DF60D766DF46DF5DC
+S11886980D050D246F600004680BF300790200661D2347244E73
+S11886AD127902002D1D234750790200461D23472440327902CA
+S11886C200721D23470E0B021D23470E4022790200014010794F
+S11886D7020002400A7902000340047902000469926E0A000187
+S11886EC8AD0AA074304190040146E0A0001F2008AD092FF69F7
+S1188701C2FA0168DA790000016D756D746D7654706DF60D7696
+S11887167903001A19376DF46DF56FE2FFEE0D1147045A008818
+S118872B1A6EE9FFF26EE9FFF16EE9FFF06E0A0002AA054704D2
+S11887405A0087CC790200066F60FFEE1D2047045A00881A7939
+S118875504FFF409647905FFFA09657902FFF009626FE2FFECB1
+S118876A6F6300046DF30D420D516F60FFEC5E0086900B870D46
+S118877F0046045A00881A0D611B811B817900FFF109606F6252
+S118879400040B826DF21B826FE200047902FFF609625E00862B
+S11887A9900B870D00476A0D611B817900FFF209606F63000424
+S11887BE0B830B836DF37902FFF809624044790200026F63FF77
+S11887D3EE1D2346426E0B0002F3001B837900FFF409606FE0A7
+S11887E8FFEA0D3209227905FFFA09650D5109217904FFF00943
+S11887FD640D4009306F6300046DF36F63FFEA09230D325E00BF
+S118881286900B870D0046067900FFFF405019337904FFF40980
+S1188827647900FFF009606FE0FFE86F60FFE86C0A6FE0FFE86B
+S118883C0CAA471E0D318900912069426DF2695279001A4E6F7B
+S1188851E3FFE65E0096BC0B876F63FFE60B840B850B037902A5
+S118886600021D234FC66F60FFEE6D756D747903001A09376DE0
+S118887B7654706DF60D760D036B00A408400269000D00470896
+S11888906F0200021D3246F26D7654706DF60D761B876DF46DD8
+S11888A5F50D056FE1FFFE7900000E5E0097220D0446087900F0
+S11888BA02135E0080B418AA6ECA00046FC500026B02A4086948
+S11888CFC20D4088059000790200086F61FFFE5E0097AE6B8482
+S11888E4A4080D406D756D740B876D7654706DF60D766F03002E
+S11888F9026F1200021D23470419004004790000016D765470D8
+S118890E6DF60D766DF40D04790088F20D415E00829E7903A419
+S118892308401269301D40460A690269B25E00979C400E0D0326
+S1188938693246EA790002325E0080B46D746D7654706DF60D24
+S118894D766DF46DF50D040D156F4200044608790002395E0090
+S118896280B46F440004401C6940790198DC5E0097D20D004703
+S11889770A0D5546040D40400C1B058C0A9400694246E0190064
+S118898C6D756D746D7654706DF60D761B876DF46DF50D056F9C
+S11889A1E1FFFE6E5A000446226E5A0005AA0943087900024B1A
+S11889B65E0080B46E5A0005F20009226F229A0C6F220004461A
+S11889CB067900FFFF403E0D20690247360D0469406F61FFFEFC
+S11889E05E0097D20D00461E6E4A00026EDA00056F4200041872
+S11889F5BB0D224702FB806EDB000879000001400A8C0A94007C
+S1188A0A694246CC19006D756D740B876D7654706DF60D761B80
+S1188A1F876DF46DF56FE0FFFE0D157902007419110D505E00B2
+S1188A3498086F62FFFE69210D505E00981E0D50881C900079B6
+S1188A490400050D427901995E5E0097AE0D50883890000D42AC
+S1188A5E7901995E5E0097AE6F63FFFE6E3A00026EDA00546F67
+S1188A73320004FBB60D224702FB6D6EDB005CFA016EDA005DDE
+S1188A886F63FFFE6F32000418BB0D224702FB806EDB005F6D86
+S1188A9D756D740B876D7654706DF60D766DF46DF50D040D1555
+S1188AB26E4A000447067900FFFF40266E4A0005AA0943087991
+S1188AC700026B5E0080B46E4A0005F20009226F209A0C0D512A
+S1188ADC5E008A1A790000016D756D746D7654706DF60D766E47
+S1188AF10A000446107369460C6E0A0008470A89FFA901420491
+S1188B061900400AFA016E8A0004790000016D7654706DF60D6B
+S1188B1B767903000A19376DF46DF56FE0FFFE6FE1FFFC6FE24A
+S1188B30FFFA6E0A0005AA094308790002875E0080B46F60FF56
+S1188B45FE6E0A0005F20009226F229A0C6FE2FFF86E0A000880
+S1188B5AEA8046045A008C3A6E0A00044720790100746F60008E
+S1188B6F045E0098320D004610790100746F60FFFA5E00983280
+S1188B840D0047087900FFFF5A008C6C790100746F6000045E94
+S1188B990098446FE00004790100746F60FFFA5E0098446FE055
+S1188BAEFFFA5E0090346FE0FFF66F64FFF60B840B041955403B
+S1188BC30C0D415E008A1A8C7494000B056F6200041D2544122C
+S1188BD86F61FFFA09516F60FFF85E00894A0D0046DA0D5209D5
+S1188BED22092209221952092209220925095509556F63FFF685
+S1188C0268BD0D520C2A10021E226EBA000118AA6EBA00020B2D
+S1188C17850B056DF519226DF26F63FFFE6F3200026F61FFFC76
+S1188C2CF80F5E00904819000B870B8740326F60FFF86F03000B
+S1188C410647246F6200046DF26F60FFFA6DF06F60FFFE6F0213
+S1188C5600026F61FFFC6F60FFF85D300B870B8740047900FF05
+S1188C6BFF6D756D747903000A09376D7654706DF60D761B8739
+S1188C806DF46DF50D046FE1FFFE0D256E4A0008EA8046066EA4
+S1188C954A000446067900FFFF403C6E4A0005AA094308790005
+S1188CAA02B35E0080B46E4A0005F20009226F209A0C6F0200EA
+S1188CBF0847166F6200046DF26F0300080D526F61FFFE5D30D0
+S1188CD40B8740047900FFFF6D756D740B876D7654706DF60DCE
+S1188CE9761B876DF46DF56F63000446087900FFFF5A008DD243
+S1188CFE0D25194419116F6300040D5209326FE2FFFE40386806
+S1188D135BAB8846186F6200041B821D214CD4688B0B0018AACB
+S1188D28688A0B000B8140180CBB46040B0440106F6200041BF1
+S1188D3D021D214CB4688B0B000B010B0519336F62FFFE1D2567
+S1188D52450479030001685A46040D3347B2790200011D2447F9
+S1188D67124E060D4447584038790200021D24471A402E6F62C7
+S1188D7C00041B021D214D045A008CF618AA688A0B000B014047
+S1188D91346F6200041B821D214D045A008CF618AA688A0B00F9
+S1188DA640186F6200041B821D214D045A008CF6FA88688A0B00
+S1188DBB000CCA8AFE688A0B000B8119440D3346045A008D12D8
+S1188DD00D106D756D740B876D7654706DF60D761B876DF46D1B
+S1188DE5F50D040D2519226FE2FFFE0D55475E681AAA8847149E
+S1188DFA68CA0B010B046F62FFFE0B026FE2FFFE1B0540E20B9D
+S1188E0F016818F0000D00461468CA0B046F62FFFE0B026FE205
+S1188E24FFFE0B011B8540C40D030B836F62FFFE09326FE2FF91
+S1188E39FE0D321B030B011B854FAC188868C80B040D321B03E2
+S1188E4E4EF6409E6F60FFFE6D756D740B876D7654706DF60DB1
+S1188E63766DF46DF56A0CA40E47045A008F0079059F586B027F
+S1188E78A4106DF20D527901009379009EC35E008CE66B80A429
+S1188E8D060B870D004C206B02A4100D5179009EC35E0097AEBF
+S1188EA26B02A4106B82A40679029EC268AC0D23400879039E7E
+S1188EB7C2FA0868BA6A0AA40A47047D3070206838F0006A0A0E
+S1188ECCA40F4602C80168B86B00A4060D020B026B82A4066A77
+S1188EE10BA4076A8B9EC00C2A10021E226A8A9EC10B800B00FE
+S1188EF66B80A406FA016A8AA40E6D756D746D7654706DF60D53
+S1188F0B766DF46DF55E008E606B02A4106B82A40C79059EC02E
+S1188F206B04A406471A6DF46DF51922790117767900343E5E70
+S1188F350098560B870B870C8846E66D756D746D7654706DF684
+S1188F4A0D766B02A4106B03A40C1D32430E79009F580D3109F5
+S1188F5F0119325E0097AE6B02A40C6B03A41019230D326B8263
+S1188F74A41018AA6A8AA40E6D7654706DF60D766DF46DF50D6B
+S1188F89246F6500046B03A41079029F5809320D2368B86EB98D
+S1188F9E00010C1918116EB900020D44470E0B830B030D520D94
+S1188FB3410D305E0097AE6B02A4100B820B0209526B82A410CD
+S1188FC818AA6A8AA40E6D756D746D7654706DF60D766DF46D0A
+S1188FDDF50D156B04A41079029F5809420D24FA0368CA6EC8EE
+S1188FF200010C0818006EC800020D505E009874790200401D62
+S1189007204F020D200B840B040D020D510D405E0097AE6B024A
+S118901CA4108A4392006B82A41018AA6A8AA40E6D756D746DEF
+S11890317654706DF60D766B00A41079029F5D09020D206D7655
+S118904654706DF60D766DF46DF56F6400046B03A41079059F8E
+S118905B5809350D5368B86EB900010C1918116EB900026EBA1F
+S118907000030C2A18226EBA00040D4447108B0593006F6200AC
+S1189085060D410D305E0097AE6B02A4108A0592006F60000687
+S118909A09026B82A41018AA6A8AA40E6D756D746D7654706DD2
+S11890AFF60D7669021D12470419004004790000016D765470CC
+S11890C46DF60D760D01790090AE5E00829E6D7654706DF60D53
+S11890D9767903000E19376DF46DF50D050D14790000021D049C
+S11890EE4E08790003935E0080B46C5A6EEAFFFD6E5B00010C82
+S1189103B318BB685AF20014AB14236FE3FFFA1B841B040B858A
+S11891186E6BFFFDF3000D33470A790200041D23470E40446DE0
+S118912DF319226F61FFFAF801402E790200021D244708790045
+S1189142039D5E0080B46E5800010C801888685AF20014A8146B
+S1189157205E0090C419226DF26F61FFFAF8055E008F800B87CE
+S118916C5A009496790200011D244E08790003A45E0080B46E33
+S11891815800010C801888685AF20014A814206FE0FFF81B84C7
+S11891960B855E00887E6FE0FFF46E6BFFFDF3008BFA93FF7932
+S11891AB0200161D2343045A00948E09336F3299DA592079024C
+S11891C000381D244708790003AA5E0080B46F63FFF447087989
+S11891D50199635A0094567901988E6F60FFF85E00889C6FE009
+S11891EAFFF4790200086DF2880590006DF06F62FFF86F61FF86
+S11891FFFAF81D5A0094840D444708790003B65E0080B46F62A1
+S1189214FFF446087901996E5A0094566F60FFF45E00890E6E16
+S11892296BFFFDAB1446087901997A5A00945619226DF26DF2EE
+S118923E6F62FFF86F61FFFAF8135A009484790200021D244704
+S118925308790003C45E0080B46E5C00010CC418CC685AF200F5
+S118926814AC14240D405E00887E6F62FFF446045A0092186EC4
+S118927D2A00044708790199875A0094560D0047045A0091D466
+S11892926F61FFF4890591000D405E00889C19226DF26DF26FAA
+S11892A762FFF86F61FFFAF8075A0094847902001C1D244708F4
+S11892BC790003D35E0080B40D516F60FFF45E0089940D0046CA
+S11892D108790199935A009456790200086DF26F63FFF48B055B
+S11892E693006DF36F62FFF86F61FFFAF8095A0094840D4447E0
+S11892FB08790003DA5E0080B47904A3580D416F60FFF45E0084
+S11893108AA60D004608790199A05A009456790200746DF26D07
+S1189325F46F62FFF86F61FFFAF8175A009484790200211D244C
+S118933A4708790003E25E0080B4790199AB5A0094567902005E
+S118934F011D244708790003E65E0080B468596F60FFF45E009F
+S11893648AEC0D004608790199B85A009456790200086DF26FBF
+S118937960FFF4880590006DF06F62FFF86F61FFFAF80B5A0020
+S118938E94847902000A1D244708790003ED5E0080B46E5A00D6
+S11893A3010CA218AA6859F1006E5B00090CB318BB6E5800085C
+S11893B8F000148B14036DF3149A14126F61FFFA6F60FFF45ED9
+S11893CD008B180B870D004D045A009496790199C35A00945656
+S11893E27902000A1D244E08790003F45E0080B46E5A00010C7F
+S11893F7A218AA6FE2FFF6685AF2006F60FFF614A814206FE0FC
+S118940CFFF66E5B00090CB318BB6E5A0008F20014AB14238DA9
+S11894210B95008CF594FF1D434710790003F96FE3FFF25E00B1
+S118943680B46F63FFF26DF50D326F61FFF66F60FFF45E008C14
+S118944B7A0B870D004C0E790199CE6F60FFFA5E008FD64036B3
+S11894606EE8FFFE0C0810001E006EE8FFFF790200026DF20D21
+S1189475621B826DF26F62FFF86F61FFFAF8115E0090480B871E
+S118948A0B874008790004045E0080B46D756D747903000E0986
+S118949F376D7654706DF60D766DF46DF50D04684BF3007902FB
+S11894B400451D23470C7902004D1D2347045A009548790200C2
+S11894C9051D214708790004125E0080B479039A28683DF500FF
+S11894DE6A0A9A290CA218AA14AD14250D5209326E4B00036E10
+S11894F3AB000118BB6A0AA40F4602FB016A8BA40F5E008F4899
+S1189508790200011D254F326A0A9A2AEA08471E79019A2B79C4
+S118951D049AC00D521B020D405E008DDC0D050D510D405E002C
+S118953290D6400C79009A2B0D511B015E0090D65E008F0840BD
+S1189547087900042C5E0080B46D756D746D7654706DF60D7678
+S118955C7903005219376DF45E00970E7900FFB0096079020068
+S11895714019115E0098087902A4007901FFB6096179003B9A73
+S11895865E0096AC790029645E0096D4790014985E0096D479F8
+S118959B0200016DF26DF27902FFB609627901FFB4096179004B
+S11895B030D05E0098560B870B87790230026DF2192279013041
+S11895C51F79001FF25E0096BC0B877901300779001B625E009D
+S11895DA96DE790027C85E0096D419446DF479029A2879011748
+S11895EF717900327C5E0096BC0B876A8CA40F6B84A4066B8458
+S1189604A4106A8CA40E405C19227901FFAF0961790034265E57
+S11896190096AC6E6AFFAFF2000CAA473E18AA6EEAFFAE790201
+S118962EFFAE09626DF2790200107901FFF00961790033B05E94
+S11896430096BC0B876E69FFAEF1006B029A200B026B829A20DA
+S11896581B017900FFF009605E0094A45E0085BC5E0080320DBA
+S114966D00469C19006D747903005209376D76547057
+S118967E6DF60D7679029A207903A4121D32470A188868A80B2B
+S1189693021D3246F85E009558FA016A8AFFCC6B0200005D2040
+S10796A86D76547013
+S11396AC6DF66DF20D165D000D600B876D765470C2
+S11896BC6DF66F7300046DF36DF20D165D000D600B870B876D0F
+S10696D176547058
+S10D96D46DF65D000D606D765470B4
+S10F96DE6DF60D165D000D606D76547085
+S11596EA6DF56DF60D060D155F520D606D766D755470C9
+S11596FC6DF56DF60D060D155F500D606D766D755470B9
+S118970E6DF60D767903A4127902EF00193269B26D7654706D46
+S1189723F60D766DF46DF50D040B04ECFE0B841D04455C79001D
+S1189738A4120D01404C69024D4209217902EEFF1D2142144068
+S118974D0E690209326982691209211D51420469134CEE6903E9
+S11897621D4345220D428A0692001D23450A0D02094219436908
+S1189777A369846902C28069820B804010E27F09210D107905B0
+S118978CEEFF1D5043AC19006D756D746D7654706DF60D766FA3
+S11097A102FFFEE27F6F82FFFE6D765470C2
+S11897AE6DF60D766DF40D140D010D0309231D31470A6C4A6833
+S11297C38A0B001D3046F60D106D746D765470D0
+S11897D26DF60D76401C680B681A1CAB430679000001401E1C43
+S11897E7AB44067900FFFF40140B000B01680A46E0681A19005F
+S10F97FC0CAA47047900FFFF6D7654703E
+S11898086DF60D760D0309231D3047066CB91D3046FA6D7654A2
+S104981D70D6
+S117981E6DF60D760D036C1A68BA0B030CAA46F66D765470ED
+S11598326DF56DF60D060D155F4C0D606D766D75547085
+S11598446DF56DF60D060D155F4E0D606D766D75547071
+S11898566DF66F7300066DF36F7300066DF36DF20D165D000D1A
+S10C986B608F0697006D765470BD
+S11798746DF60D76193340020B036C0A46FA0D306D765470C0
+S1189888726573657400000000800000000098DC000099120005
+S118989D00000098DA020000000000871298D803000000000032
+S11898B2871298D6040000000000871298D20500000000008703
+S11898C712000000000000000000003031320032003100300050
+S11898DC2E2E000098DC000099120000000098DA0700000085FA
+S11898F12A82E098D808000000852A82E098D609000000852A23
+S118990682E00000000000000000000098DC0000994800000091
+S118991B0099410100989600000000993A060098E000000000D9
+S11899300000000000000000000073656E736F72006D6F746FC5
+S1189945720000995C00009912000000000000000000000000F7
+S118995A00002F006C65676F0066696420696E20757365006E19
+S118996F6F2073756368206669640063616E27742072656D6FAA
+S118998476650063616E277420636C6F6E65006E6F20737563A9
+S118999968206E616D650063616E277420737461740063616EB1
+S11899AE2774206372656174650063616E2774206F70656E00D2
+S11899C363616E277420726561640063616E277420777269744F
+S11899D86500924C948E92B4948E934C948E9334948E93909408
+S11899ED8E93E2948E9206948E9206948E92F8948E948E948ED8
+S1099A02948E948E91BEC7
+S1189A080000FFFF9948991C98A098AA98B498BE992698EA98BC
+S1069A1DF498FEB8
+S90380007C
diff --git a/lib/lego/styx_abp.srec b/lib/lego/styx_abp.srec
new file mode 100644
index 00000000..d7026013
--- /dev/null
+++ b/lib/lego/styx_abp.srec
@@ -0,0 +1,331 @@
+S0100000737479785F6162702E73726563AA
+S11880006DF06DF16DF26DF35E0096A66D736D726D716D705485
+S118801570446F20796F7520627974652C207768656E20492057
+S10B802A6B6E6F636B3F0000F5
+S11880326DF60D761B870D621B8279014000790029F25E00965F
+S1188047D46B039A326F62FFFE1D23470819226B829A30401271
+S118805C6B039A30790200091D234E060B036B839A306F61FF26
+S1188071FE6B819A326B039A30790200097900FFFF1D234F027C
+S11880860D100B876D7654706DF60D760D02790330026DF3790F
+S118809B01301F79001FF25E0096E40B87790027C85E0096FC30
+S11880B06D7654706DF60D766DF40D047901300679001B625EB4
+S11880C50097060D405E00808E5E0080320D0046F86D746D762D
+S11880DA54706DF60D766DF40D041900684B473C0D1147380B7A
+S11880EF040CBA8AD0AA09422E0CB8F00088D090FF1B01684BC7
+S118810447200D11471C0B040CBA8AD0AA0942120D0209220901
+S118811922092009000CBAF200092040D66D746D7654706DF617
+S118812E0D761B871B876DF46DF56FE0FFFE0D146FE0FFFC0DEA
+S1188143444C16FA2D688A6F62FFFE0D230B036FE3FFFC170CE8
+S118815817040B040D444610FA306F63FFFC68BA0B036FE3FFC5
+S118816DFC404C790527101D454F407901000A0D505E009712E3
+S11881820D051D454EF0402E0D510D405E0097120C8A8A306F53
+S118819763FFFC68BA0B036FE3FFFC0D510D405E0097240D041F
+S11881AC7901000A0D505E0097120D050D5546CE6F62FFFC6F0F
+S11881C163FFFE19326FE2FFFC6F60FFFC6D756D740B870B87FD
+S11881D66D7654706DF60D761B876DF46DF50D040D156FE2FF1B
+S11881EBFE7900000A5E00974A69846F8500026F62FFFE6F8219
+S118820000046F6200046F82000619226F8200086A0AA43A8A85
+S1188215016A8AA43A6D756D740B876D7654706DF60D765E003D
+S118822A97C46A0AA43A8AFF6A8AA43A6D7654706DF60D76693D
+S118823F00790198B05E008FD6790000016D7654706DF60D769A
+S11882541B871B876DF46DF50D056FE1FFFE6FE2FFFC40266990
+S1188269546F61FFFC0D406F62FFFE5D200D00470E6F4200082A
+S118827E69D20D405E0082244006790500080945695246D66DFD
+S1188293756D740B870B876D7654706DF60D761B871B876DF426
+S11882A86DF56FE0FFFE6FE1FFFC19557904A40C6F62FFFC6FEE
+S11882BD61FFFE0D405E0082508C0E94000B05790200021D25D0
+S11882D24FE46D756D740B870B876D7654706DF60D7679030070
+S11882E70C19376DF46DF56FE2FFFE6F6400040D1147045A0077
+S11882FC83FE6E080002F00088F990FF6FE0FFFC6F65FFFC094E
+S11883115509550955190509557902A40009520D256F62FFFE4C
+S118832646045A0083FE684BF300790200621D23470C79020088
+S118833B6C1D23470A5A0083FE79020060400219226FE2FFF8B1
+S11883500B046F63FFFE1B030D3346045A0083FE684AAA3E46D3
+S118836504FA014006AA3C460C18AA6EEAFFFB0B041B034006FB
+S118837AFA016EEAFFFB0D3346045A0083FE7901823A0D50881D
+S118838F0C900019226FE3FFF45E0082506F62FFFC8A00921091
+S11883A46FE2FFF66F61FFF6790019C45E009706FA0168DA6EB9
+S11883B96AFFF96EDA00016F63FFF40D310D405E0080DC6FD0B7
+S11883CE000A19226FD200026FD200046EDA0006FA016EDA0038
+S11883E3086E6AFFFB6EDA00096F61FFF6790019465E009706BE
+S11883F86F60FFFE40047900FFFF6D756D747903000C09376DED
+S118840D7654706DF60D767903000619376DF46DF56FE0FFFE55
+S11884226FE1FFFC0D256F6400085E0090340D031D45443219C6
+S1188437546F6000041D0443046F6400046F62000609526FE243
+S118844C00060D300B800B000D426F6100066FE3FFFA5E0097D9
+S1188461D66F63FFFA4002194468BC0D420C2A18226EBA0001B6
+S118847618AA6EBA00020B840B046DF419226DF26F62FFFC6F2D
+S118848B61FFFEF80F5E0090480B870B876D756D7479030006D4
+S11884A009376D7654706DF60D767903000C19376DF46DF56FEC
+S11884B5E0FFF66FE1FFF40D257904FFF809646F6100060D4060
+S11884CA5E00812C6DF06DF46F6200046DF20D526F61FFF46F0B
+S11884DF60FFF65E0084108F0697006D756D747903000C093786
+S11884F46D7654706DF60D766E0A0009471019116F0300046FFB
+S118850902000A1D234514400E19116F0300046F02000A1D230B
+S118851E4404790100010D106D7654706DF60D761B876DF46D67
+S1188533F56FE1FFFE0D256E0A0002F2008AF992FF0D240944BD
+S118854809440944192409447902A40009420D246E4A00084655
+S118855D067900FFFF404E0D405E0084F80D0047206F420004AA
+S11885726DF26F6200066DF26F6200040D516F60FFFE5E00847A
+S1188587A60B870B8740226F6200066DF26F6200040D516F6077
+S118859CFFFE5E0081DA6F42000C6F8200086FC0000C0B871974
+S11885B1006D756D740B876D7654706DF60D761B876DF46DF56A
+S11885C67903A430683A4D045A008686EA7F68BA19440D4509B0
+S11885DB5509550955194509557902A40009520D256E5A00083E
+S11885F00D430B036FE3FFFE0CAA46045A0086768C0094100D32
+S1188605520D41790014C05E0096D40D520D41790014C05E004F
+S118861A96D40D520D41790014C05E0096D40D520D41790014E1
+S118862FC05E0096D40D505E0084F80D00473840306F54000CA8
+S11886446F4200086FD2000C6F41000269406F5200046DF26F29
+S11886594200066DF26F4200045E0084A60D405E0082240B8741
+S118866E0B876F52000C46CA6F64FFFE790200021D244E045A4A
+S11886830085D86D756D740B876D7654706DF60D766DF46DF5DC
+S11886980D050D246F600004680BF300790200661D2347244E73
+S11886AD127902002D1D234750790200461D23472440327902CA
+S11886C200721D23470E0B021D23470E4022790200014010794F
+S11886D7020002400A7902000340047902000469926E0A000187
+S11886EC8AD0AA074304190040146E0A0001F2008AD092FF69F7
+S1188701C2FA0168DA790000016D756D746D7654706DF60D7696
+S11887167903001A19376DF46DF56FE2FFEE0D1147045A008818
+S118872B1A6EE9FFF26EE9FFF16EE9FFF06E0A0002AA054704D2
+S11887405A0087CC790200066F60FFEE1D2047045A00881A7939
+S118875504FFF409647905FFFA09657902FFF009626FE2FFECB1
+S118876A6F6300046DF30D420D516F60FFEC5E0086900B870D46
+S118877F0046045A00881A0D611B811B817900FFF109606F6252
+S118879400040B826DF21B826FE200047902FFF609625E00862B
+S11887A9900B870D00476A0D611B817900FFF209606F63000424
+S11887BE0B830B836DF37902FFF809624044790200026F63FF77
+S11887D3EE1D2346426E0B0002F3001B837900FFF409606FE0A7
+S11887E8FFEA0D3209227905FFFA09650D5109217904FFF00943
+S11887FD640D4009306F6300046DF36F63FFEA09230D325E00BF
+S118881286900B870D0046067900FFFF405019337904FFF40980
+S1188827647900FFF009606FE0FFE86F60FFE86C0A6FE0FFE86B
+S118883C0CAA471E0D318900912069426DF2695279001A4E6F7B
+S1188851E3FFE65E0096E40B876F63FFE60B840B850B0379027D
+S118886600021D234FC66F60FFEE6D756D747903001A09376DE0
+S118887B7654706DF60D760D036B00A438400269000D00470866
+S11888906F0200021D3246F26D7654706DF60D761B876DF46DD8
+S11888A5F50D056FE1FFFE7900000E5E00974A0D0446087900C8
+S11888BA02135E0080B418AA6ECA00046FC500026B02A4386918
+S11888CFC20D4088059000790200086F61FFFE5E0097D66B845A
+S11888E4A4380D406D756D740B876D7654706DF60D766F0300FE
+S11888F9026F1200021D23470419004004790000016D765470D8
+S118890E6DF60D766DF40D04790088F20D415E00829E7903A419
+S118892338401269301D40460A690269B25E0097C4400E0D03CE
+S1188938693246EA790002325E0080B46D746D7654706DF60D24
+S118894D766DF46DF50D040D156F4200044608790002395E0090
+S118896280B46F440004401C6940790199045E0097FA0D0047B2
+S11889770A0D5546040D40400C1B058C0A9400694246E0190064
+S118898C6D756D746D7654706DF60D761B876DF46DF50D056F9C
+S11889A1E1FFFE6E5A000446226E5A0005AA0943087900024B1A
+S11889B65E0080B46E5A0005F20009226F229A346F22000446F2
+S11889CB067900FFFF403E0D20690247360D0469406F61FFFEFC
+S11889E05E0097FA0D00461E6E4A00026EDA00056F420004184A
+S11889F5BB0D224702FB806EDB000879000001400A8C0A94007C
+S1188A0A694246CC19006D756D740B876D7654706DF60D761B80
+S1188A1F876DF46DF56FE0FFFE0D157902007419110D505E00B2
+S1188A3498306F62FFFE69210D505E0098460D50881C90007966
+S1188A490400050D42790199865E0097D60D50883890000D425C
+S1188A5E790199865E0097D66F63FFFE6E3A00026EDA00546F17
+S1188A73320004FBB60D224702FB6D6EDB005CFA016EDA005DDE
+S1188A886F63FFFE6F32000418BB0D224702FB806EDB005F6D86
+S1188A9D756D740B876D7654706DF60D766DF46DF50D040D1555
+S1188AB26E4A000447067900FFFF40266E4A0005AA0943087991
+S1188AC700026B5E0080B46E4A0005F20009226F209A340D5102
+S1188ADC5E008A1A790000016D756D746D7654706DF60D766E47
+S1188AF10A000446107369460C6E0A0008470A89FFA901420491
+S1188B061900400AFA016E8A0004790000016D7654706DF60D6B
+S1188B1B767903000A19376DF46DF56FE0FFFE6FE1FFFC6FE24A
+S1188B30FFFA6E0A0005AA094308790002875E0080B46F60FF56
+S1188B45FE6E0A0005F20009226F229A346FE2FFF86E0A000858
+S1188B5AEA8046045A008C3A6E0A00044720790100746F60008E
+S1188B6F045E00985A0D004610790100746F60FFFA5E00985A30
+S1188B840D0047087900FFFF5A008C6C790100746F6000045E94
+S1188B9900986C6FE00004790100746F60FFFA5E00986C6FE005
+S1188BAEFFFA5E0090346FE0FFF66F64FFF60B840B041955403B
+S1188BC30C0D415E008A1A8C7494000B056F6200041D2544122C
+S1188BD86F61FFFA09516F60FFF85E00894A0D0046DA0D5209D5
+S1188BED22092209221952092209220925095509556F63FFF685
+S1188C0268BD0D520C2A10021E226EBA000118AA6EBA00020B2D
+S1188C17850B056DF519226DF26F63FFFE6F3200026F61FFFC76
+S1188C2CF80F5E00904819000B870B8740326F60FFF86F03000B
+S1188C410647246F6200046DF26F60FFFA6DF06F60FFFE6F0213
+S1188C5600026F61FFFC6F60FFF85D300B870B8740047900FF05
+S1188C6BFF6D756D747903000A09376D7654706DF60D761B8739
+S1188C806DF46DF50D046FE1FFFE0D256E4A0008EA8046066EA4
+S1188C954A000446067900FFFF403C6E4A0005AA094308790005
+S1188CAA02B35E0080B46E4A0005F20009226F209A346F0200C2
+S1188CBF0847166F6200046DF26F0300080D526F61FFFE5D30D0
+S1188CD40B8740047900FFFF6D756D740B876D7654706DF60DCE
+S1188CE9761B876DF46DF56F63000446087900FFFF5A008DD243
+S1188CFE0D25194419116F6300040D5209326FE2FFFE40386806
+S1188D135BAB8846186F6200041B821D214CD4688B0B0018AACB
+S1188D28688A0B000B8140180CBB46040B0440106F6200041BF1
+S1188D3D021D214CB4688B0B000B010B0519336F62FFFE1D2567
+S1188D52450479030001685A46040D3347B2790200011D2447F9
+S1188D67124E060D4447584038790200021D24471A402E6F62C7
+S1188D7C00041B021D214D045A008CF618AA688A0B000B014047
+S1188D91346F6200041B821D214D045A008CF618AA688A0B00F9
+S1188DA640186F6200041B821D214D045A008CF6FA88688A0B00
+S1188DBB000CCA8AFE688A0B000B8119440D3346045A008D12D8
+S1188DD00D106D756D740B876D7654706DF60D761B876DF46D1B
+S1188DE5F50D040D2519226FE2FFFE0D55475E681AAA8847149E
+S1188DFA68CA0B010B046F62FFFE0B026FE2FFFE1B0540E20B9D
+S1188E0F016818F0000D00461468CA0B046F62FFFE0B026FE205
+S1188E24FFFE0B011B8540C40D030B836F62FFFE09326FE2FF91
+S1188E39FE0D321B030B011B854FAC188868C80B040D321B03E2
+S1188E4E4EF6409E6F60FFFE6D756D740B876D7654706DF60DB1
+S1188E63766DF46DF56A0CA43E47045A008F0079059F886B021F
+S1188E78A4406DF20D527901009379009EF35E008CE66B80A4C9
+S1188E8D360B870D004C206B02A4400D5179009EF35E0097D607
+S1188EA26B02A4406B82A43679029EF268AC0D23400879039EEE
+S1188EB7F2FA0868BA6A0AA43A47047D3070206838F0006A0AAE
+S1188ECCA43F4602C80168B86B00A4360D020B026B82A4366AE7
+S1188EE10BA4376A8B9EF00C2A10021E226A8A9EF10B800B006E
+S1188EF66B80A436FA016A8AA43E6D756D746D7654706DF60DF3
+S1188F0B766DF46DF55E008E606B02A4406B82A43C79059EF09E
+S1188F206B04A436471A6DF46DF51922790117767900343E5E40
+S1188F3500987E0B870B870C8846E66D756D746D7654706DF65C
+S1188F4A0D766B02A4406B03A43C1D32430E79009F880D310965
+S1188F5F0119325E0097D66B02A43C6B03A44019230D326B82DB
+S1188F74A44018AA6A8AA43E6D7654706DF60D766DF46DF50D0B
+S1188F89246F6500046B03A44079029F8809320D2368B86EB92D
+S1188F9E00010C1918116EB900020D44470E0B830B030D520D94
+S1188FB3410D305E0097D66B02A4400B820B0209526B82A44045
+S1188FC818AA6A8AA43E6D756D746D7654706DF60D766DF46DDA
+S1188FDDF50D156B04A44079029F8809420D24FA0368CA6EC88E
+S1188FF200010C0818006EC800020D505E00989C790200401D3A
+S1189007204F020D200B840B040D020D510D405E0097D66B0222
+S118901CA4408A4392006B82A44018AA6A8AA43E6D756D746D5F
+S11890317654706DF60D766B00A44079029F8D09020D206D76F5
+S118904654706DF60D766DF46DF56F6400046B03A44079059F5E
+S118905B8809350D5368B86EB900010C1918116EB900026EBAEF
+S118907000030C2A18226EBA00040D4447108B0593006F6200AC
+S1189085060D410D305E0097D66B02A4408A0592006F6000062F
+S118909A09026B82A44018AA6A8AA43E6D756D746D7654706D72
+S11890AFF60D7669021D12470419004004790000016D765470CC
+S11890C46DF60D760D01790090AE5E00829E6D7654706DF60D53
+S11890D9767903000E19376DF46DF50D050D14790000021D049C
+S11890EE4E08790003935E0080B46C5A6EEAFFFD6E5B00010C82
+S1189103B318BB685AF20014AB14236FE3FFFA1B841B040B858A
+S11891186E6BFFFDF3000D33470A790200041D23470E40446DE0
+S118912DF319226F61FFFAF801402E790200021D244708790045
+S1189142039D5E0080B46E5800010C801888685AF20014A8146B
+S1189157205E0090C419226DF26F61FFFAF8055E008F800B87CE
+S118916C5A009496790200011D244E08790003A45E0080B46E33
+S11891815800010C801888685AF20014A814206FE0FFF81B84C7
+S11891960B855E00887E6FE0FFF46E6BFFFDF3008BFA93FF7932
+S11891AB0200161D2343045A00948E09336F329A025920790223
+S11891C000381D244708790003AA5E0080B46F63FFF447087989
+S11891D501998B5A009456790198B66F60FFF85E00889C6FE0B9
+S11891EAFFF4790200086DF2880590006DF06F62FFF86F61FF86
+S11891FFFAF81D5A0094840D444708790003B65E0080B46F62A1
+S1189214FFF44608790199965A0094566F60FFF45E00890E6EEE
+S11892296BFFFDAB144608790199A25A00945619226DF26DF2C6
+S118923E6F62FFF86F61FFFAF8135A009484790200021D244704
+S118925308790003C45E0080B46E5C00010CC418CC685AF200F5
+S118926814AC14240D405E00887E6F62FFF446045A0092186EC4
+S118927D2A00044708790199AF5A0094560D0047045A0091D43E
+S11892926F61FFF4890591000D405E00889C19226DF26DF26FAA
+S11892A762FFF86F61FFFAF8075A0094847902001C1D244708F4
+S11892BC790003D35E0080B40D516F60FFF45E0089940D0046CA
+S11892D108790199BB5A009456790200086DF26F63FFF48B0533
+S11892E693006DF36F62FFF86F61FFFAF8095A0094840D4447E0
+S11892FB08790003DA5E0080B47904A3880D416F60FFF45E0054
+S11893108AA60D004608790199C85A009456790200746DF26DDF
+S1189325F46F62FFF86F61FFFAF8175A009484790200211D244C
+S118933A4708790003E25E0080B4790199D35A00945679020036
+S118934F011D244708790003E65E0080B468596F60FFF45E009F
+S11893648AEC0D004608790199E05A009456790200086DF26F97
+S118937960FFF4880590006DF06F62FFF86F61FFFAF80B5A0020
+S118938E94847902000A1D244708790003ED5E0080B46E5A00D6
+S11893A3010CA218AA6859F1006E5B00090CB318BB6E5800085C
+S11893B8F000148B14036DF3149A14126F61FFFA6F60FFF45ED9
+S11893CD008B180B870D004D045A009496790199EB5A0094562E
+S11893E27902000A1D244E08790003F45E0080B46E5A00010C7F
+S11893F7A218AA6FE2FFF6685AF2006F60FFF614A814206FE0FC
+S118940CFFF66E5B00090CB318BB6E5A0008F20014AB14238DA9
+S11894210B95008CF594FF1D434710790003F96FE3FFF25E00B1
+S118943680B46F63FFF26DF50D326F61FFF66F60FFF45E008C14
+S118944B7A0B870D004C0E790199F66F60FFFA5E008FD640368B
+S11894606EE8FFFE0C0810001E006EE8FFFF790200026DF20D21
+S1189475621B826DF26F62FFF86F61FFFAF8115E0090480B871E
+S118948A0B874008790004045E0080B46D756D747903000E0986
+S118949F376D7654706DF60D766DF46DF50D04684BF3007902FB
+S11894B400451D23470C7902004D1D2347045A0095707902009A
+S11894C9051D214708790004125E0080B479039A58683DF500CF
+S11894DE6A0A9A590CA218AA14AD14250D5209326E4B00036EE0
+S11894F3AB00016A089A5AE8016A0AA43F1CA84666684A1102D9
+S1189508130A1102130A1102130AEA01E2006A0BA43FF3001D98
+S118951D32464A18AA0C884602FA016A8AA43F5E008F48790253
+S118953200011D254F326A0A9A5A733A471E79019A5B79049A5C
+S1189547F00D521B020D405E008DDC0D050D510D405E0090D60A
+S118955C400C79009A5B0D511B015E0090D65E008F0840087948
+S118957100042C5E0080B46D756D746D7654706DF60D76790353
+S1189586005219376DF45E0097367900FFB00960790200401939
+S118959B115E0098307902A4307901FFB6096179003B9A5E00EC
+S11895B096D4790029645E0096FC790014985E0096FC790200B2
+S11895C5016DF26DF27902FFB609627901FFB40961790030D023
+S11895DA5E00987E0B870B87790230026DF219227901301F7957
+S11895EF001FF25E0096E40B877901300779001B625E00970646
+S1189604790027C85E0096FC19446DF479029A5879011771794F
+S118961900327C5E0096E40B876A8CA43F6B84A4366B84A440AB
+S118962E6A8CA43E405C19227901FFAF0961790034265E00961B
+S1189643D46E6AFFAFF2000CAA473E18AA6EEAFFAE7902FFAE98
+S118965809626DF2790200107901FFF00961790033B05E009681
+S118966DE40B876E69FFAEF1006B029A500B026B829A501B01A2
+S11896827900FFF009605E0094A45E0085BC5E0080320D004666
+S11296979C19006D747903005209376D76547075
+S11896A66DF60D7679029A507903A4421D32470A188868A80BA3
+S11896BB021D3246F85E009580FA016A8AFFCC6B0200005D20F0
+S10796D06D765470EB
+S11396D46DF66DF20D165D000D600B876D7654709A
+S11896E46DF66F7300046DF36DF20D165D000D600B870B876DE7
+S10696F976547030
+S10D96FC6DF65D000D606D7654708C
+S10F97066DF60D165D000D606D7654705C
+S11597126DF56DF60D060D155F520D606D766D755470A0
+S11597246DF56DF60D060D155F500D606D766D75547090
+S11897366DF60D767903A4427902EF00193269B26D7654706DEE
+S118974BF60D766DF46DF50D040B04ECFE0B841D04455C7900F5
+S1189760A4420D01404C69024D4209217902EEFF1D2142144010
+S11897750E690209326982691209211D51420469134CEE6903C1
+S118978A1D4345220D428A0692001D23450A0D020942194369E0
+S118979FA369846902C28069820B804010E27F09210D10790588
+S11897B4EEFF1D5043AC19006D756D746D7654706DF60D766F7B
+S11097C902FFFEE27F6F82FFFE6D7654709A
+S11897D66DF60D766DF40D140D010D0309231D31470A6C4A680B
+S11297EB8A0B001D3046F60D106D746D765470A8
+S11897FA6DF60D76401C680B681A1CAB430679000001401E1C1B
+S118980FAB44067900FFFF40140B000B01680A46E0681A190036
+S10F98240CAA47047900FFFF6D76547015
+S11898306DF60D760D0309231D3047066CB91D3046FA6D76547A
+S104984570AE
+S11798466DF60D760D036C1A68BA0B030CAA46F66D765470C5
+S115985A6DF56DF60D060D155F4C0D606D766D7554705D
+S115986C6DF56DF60D060D155F4E0D606D766D75547049
+S118987E6DF66F7300066DF36F7300066DF36DF20D165D000DF2
+S10C9893608F0697006D76547095
+S117989C6DF60D76193340020B036C0A46FA0D306D76547098
+S11898B0726573657400000000800000000099040000993A008C
+S11898C5000000990202000000000087129900030000000000B8
+S11898DA871298FE040000000000871298FA050000000000878B
+S11898EF12000000000000000000003031320032003100300028
+S11899042E2E000099040000993A000000009902070000008557
+S11899192A82E0990008000000852A82E098FE09000000852AA9
+S118992E82E00000000000000000000099040000997000000018
+S1189943009969010098BE000000009962060099080000000010
+S11899580000000000000000000073656E736F72006D6F746F9D
+S118996D72000099840000993A0000000000000000000000007F
+S118998200002F006C65676F0066696420696E20757365006EF1
+S11899976F2073756368206669640063616E27742072656D6F82
+S11899AC76650063616E277420636C6F6E65006E6F2073756381
+S11899C168206E616D650063616E277420737461740063616E89
+S11899D62774206372656174650063616E2774206F70656E00AA
+S11899EB63616E277420726561640063616E2774207772697427
+S1189A006500924C948E92B4948E934C948E9334948E939094DF
+S1189A158E93E2948E9206948E9206948E92F8948E948E948EAF
+S1099A2A948E948E91BE9F
+S1189A300000FFFF9970994498C898D298DC98E6994E99129952
+S1069A451C99263F
+S90380007C