+#define COINMECH_ID 0x20
+
+volatile u8 last_byte;
+volatile u8 packet_pos;
+volatile u16 value_1;
+volatile u8 value_2;
+volatile u8 dec_point;
+
+volatile u16 coin_value;
+u8 item_cost;
+volatile bool have_change;
+
+u8 parity_test(u8 c) {
+ u8 parity = 0;
+ for (parity = 0; c; c = c>>1) {
+ if (c&1) parity = !parity;
+ }
+ return parity;
+}
+
+bool parity_good(u8 c) {
+
+/*
+ * parity_good truth table:
+ *
+ * | parity_test(c)
+ * | 0 1
+ * -----+---------
+ * R8 0 | 1 0
+ * 1 | 0 1
+ *
+ * equates to even parity?
+ */
+
+ u8 R8 = (_io_ports[M6811_SCCR1] & M6811_R8)?1:0;
+ u8 p = parity_test(c)?1:0;
+ return R8 == p;
+}
+
+void send_byte(u8 c) {
+ last_byte = c;
+ while (!(_io_ports[M6811_SCSR] & M6811_TDRE)); /* wait for TD register empty */
+
+ if (parity_test(c))
+ bset((void*)&_io_ports[M6811_SCCR1], M6811_T8);
+ else
+ bclr((void*)&_io_ports[M6811_SCCR1], M6811_T8);
+}
+
+#define IS_CTRL(x) (x & 0x10) /* true if this packet is a control packet */
+void sci_interrupt_coinmech() {
+ u8 in;
+ in = _io_ports[M6811_SCDR];
+
+ /* test for framing errors & parity bit */
+ if (_io_ports[M6811_SCSR] & M6811_FE || !parity_good(in)) {
+ _io_ports[M6811_SCDR]; /* read of register req'd to clear FE */
+ send_byte(0xff); /* request a retransmit */
+ return;
+ }
+
+ /* all bytes must have the correct ID in the 3 MSBs */
+ if ((in & 0xe0) != COINMECH_ID) return;
+
+ /* we have a good packet */
+ if (in == 0x3f) {
+ /* retransmit was requested */
+ send_byte(last_byte);
+ return;
+ }
+
+ if (packet_pos != 0 || IS_CTRL(in)) {
+ in &= 0x0f;
+ switch (in) {
+ case 0x01:
+ /* just reply with ack */
+ /* original firmware does something with link master price holding */
+ send_byte(0x00);
+ case 0x02:
+ /* write back how much change to give, or 0xfe to hold it. */
+ if (item_cost) {
+ send_byte(item_cost / value_2);
+ } else
+ send_byte(0xfe);
+ break;
+ case 0x03:
+ /* hmmm, maybe we're sposed to do something here. firmware doesnt */
+ /* and just sets a random bit - something like "changer in use"? */
+ break;
+ case 0x08:
+ /* start of packet */
+ packet_pos = 1;
+ send_byte(0x00);
+ break;
+ default:
+ /* just ack it and move on */
+ send_byte(0x00);
+ }
+ } else {
+ in &= 0x0f;
+ switch (packet_pos) {
+ case 1: value_1 = in; break;
+ case 2: value_1 |= in << 4; break;
+ case 3: value_1 |= in << 8; break;
+ case 4: value_1 |= in << 12; break;
+ case 5: value_2 = in; break;
+ case 6: value_2 |= in << 4; break;
+ case 7: dec_point = in; break;
+ case 8: have_change = (in&0x01); break;
+ default:
+ if (packet_pos == 9) {
+ packet_pos = 0;
+ coin_value = value_1*value_2;
+ }
+ /* hmmm, else? */
+ }
+ packet_pos++;
+ send_byte(0x00); /* ack */
+ }
+}
+
+void coin_eat() {
+ coin_cost(coin_value); /* eat everything */
+}
+
+void coin_cost(u16 cost) {
+ item_cost = cost;
+ while(coin_value); /* wait until coin mech cleared */
+}
+
+void coinmech_init() {
+ packet_pos = 0;
+ value_1 = value_2 = 0;
+ dec_point = 0;
+ coin_value = 0;
+ item_cost = 0;
+ have_change = 0;
+ _io_ports[M6811_SCCR1] = 0x10;
+ _io_ports[M6811_SCCR2] = 0x2e;
+ _io_ports[M6811_BAUD] = 0x03;
+ send_byte(0xff);
+}