Get rid of ack/nack code.
[uccvend-snackrom.git] / ROM2 / main_basic.c
1 /*
2  * main_basic.c - a simplified main procedure that relies upon a ersver to do
3  * anything smart. Just be a dumb interface to a display, keypad, coin mech
4  * and snacks.
5  */
6
7 #define VERSION_STRING "V 20040624"
8
9 #include "display_basic.h"
10 #include "keypad.h"
11 #include "chime.h"
12 #include "coinmech.h"
13 #include "motors.h"
14 #include "sci.h"
15 #include "vend.h"
16 #include "xmodem.h"
17 #include "mic.h"
18
19 u8 last_standalone;
20 u8 last_switch_input;
21 u8 last_misc_input;
22 u16 last_coin_value;
23 bool last_door_open;
24 char display_buf[11];
25 /* cur_motor[0] is 0 for nothing, or 1..10, or 0xff to say redraw.
26  * cur_motor[1] is 0..9 and only meaningful is cur_motor[0] != 0. */
27 u8 cur_motor[2];
28
29 bool check_standalone() {
30         if (is_standalone()) {
31                 send_string("011 In standalone mode. Function disabled." CRLF);
32                 return 1;
33         }
34         return 0;
35 }
36
37 bool check_badpoke() {
38         if (cant_poke()) {
39                 send_string("099 Can't poke without flipping DIP SW 3." CRLF);
40                 return 1;
41         }
42         return 0;
43 }
44
45 void unknown_command() {
46         send_string("012 Unknown command. Type HELP for help." CRLF);
47 }
48
49 void motor_reply(u8 code) {
50         /* returns a message of the form MXYY - X is return code, YY is motor */
51         switch (code) {
52                 case MOTOR_SUCCESS:
53                         send_string("100 Vend successful." CRLF);
54                         break;
55                 case MOTOR_NOSLOT:
56                         send_string("151 No motor there." CRLF);
57                         break;
58                 case MOTOR_CURRENT_FAIL:
59                         send_string("152 Over current." CRLF);
60                         break;
61                 case MOTOR_HOME_FAIL:
62                         send_string("153 Home sensors failing." CRLF);
63                         break;
64                 default:
65                         send_string("159 Unknown motor error." CRLF);
66         }
67 }
68
69 void dispense_something() {
70         /* process a message VXX in sci_rx_buf where XX is motor number */
71         u8 slot;
72
73         if (check_standalone()) return;
74         if (must_verify() && !mic_verify((void*)sci_rx_buf)) {
75                 send_string("019 Message verification failed." CRLF);
76                 return;
77         }
78
79         if (my_strncmp("ALL", (char*)sci_rx_buf+1, 3)) {
80                 char motor[3];
81                 motor[2] = '\0';
82                 send_string("102 Vend all motors starting." CRLF);
83                 for (motor[0] = '0'; motor[0] <= '9'; motor[0]++) {
84                         for (motor[1] = '0'; motor[1] <= '9'; motor[1]++) {
85                                 send_string("101 Vending ");
86                                 send_string(motor);
87                                 send_string(CRLF);
88                                 motor_reply(dispense_motor(motor[0]*10+motor[1]));
89                         }
90                 }
91                 send_string("102 Vend all motors complete." CRLF);
92                 return;
93         }
94
95         if ((sci_rx_buf[1] < '0') || (sci_rx_buf[1] > '9') ||
96                 (sci_rx_buf[2] < '0') || (sci_rx_buf[2] > '9')) {
97                 sci_rx_buf[1] = sci_rx_buf[2] = '0';
98                 motor_reply(MOTOR_NOSLOT);
99                 return;
100         }
101
102         slot = (sci_rx_buf[1] - '0') * 10;
103         slot += sci_rx_buf[2] - '0';
104
105         motor_reply(dispense_motor(slot));
106 }
107
108 void write_to_display() {
109         /* process a message in the form DXXXXXXXXXX to send to display */
110         u8 i;
111         char buf[11];
112
113         if (check_standalone()) return;
114
115         for (i = 0; i < 10; i++)
116                 if (sci_rx_buf[i+1])
117                         buf[i] = sci_rx_buf[i+1];
118                 else
119                         break;
120
121         for (; i < 10; i++) /* pad the rest out with spaces */
122                 buf[i] = ' ';
123         buf[i] = '\0';
124
125         set_msg(buf);
126         send_string("300 Written." CRLF);
127 }
128
129 void send_balance() {
130         sci_tx_buf[0] = 'C';
131         sci_tx_buf[1] = have_change?'0':'1';
132         sci_tx_buf[2] = (coin_value/10000)%10;
133         sci_tx_buf[3] = (coin_value/1000)%10;
134         sci_tx_buf[4] = (coin_value/100)%10;
135         sci_tx_buf[5] = (coin_value/10)%10;
136         sci_tx_buf[6] = coin_value%10;
137         sci_tx_buf[8] = 0;
138         send_buffer(1);
139 }
140
141 void give_change() {
142         u16 cost;
143
144         if ((sci_rx_buf[1] < '0') || (sci_rx_buf[1] > '9') ||
145                 (sci_rx_buf[2] < '0') || (sci_rx_buf[2] > '9') ||
146                 (sci_rx_buf[3] < '0') || (sci_rx_buf[3] > '9') ||
147                 (sci_rx_buf[4] < '0') || (sci_rx_buf[4] > '9') ||
148                 (sci_rx_buf[5] < '0') || (sci_rx_buf[5] > '9')) {
149                 //send_nack();
150         }
151         cost = sci_rx_buf[1] - '0';
152         cost *= 10; cost = sci_rx_buf[2] - '0';
153         cost *= 10; cost = sci_rx_buf[3] - '0';
154         cost *= 10; cost = sci_rx_buf[4] - '0';
155         cost *= 10; cost = sci_rx_buf[5] - '0';
156
157         coin_cost(cost);
158         //send_ack();
159
160
161 void send_keypress(u8 key) {
162         /* send a packet of the form KX with X being the key, or R for reset */
163         if (is_standalone()) return;
164
165         sci_tx_buf[0] = '2';
166         if (key == KEY_RESET) {
167                 sci_tx_buf[1] = '1';
168                 sci_tx_buf[2] = '1';
169         } else {
170                 sci_tx_buf[1] = '0';
171                 sci_tx_buf[2] = (key%10)+'0';
172         }
173         sci_tx_buf[3] = '\0';
174         send_buffer(0);
175         send_string(" key." CRLF);
176 }
177
178 void send_door_msg(bool open) {
179         if (is_standalone()) return;
180         sci_tx_buf[0] = '4';
181         sci_tx_buf[1] = '0';
182         sci_tx_buf[2] = open?'1':'0';
183         send_buffer(0);
184         if (open)
185                 send_string(" door open." CRLF);
186         else
187                 send_string(" door closed." CRLF);
188 }
189
190 void do_chime() {
191         if (check_standalone()) return;
192         if (sci_rx_buf[1] == '\0')
193                 chime_start();
194         else if (sci_rx_buf[1] == 'S') { /* synchronous beep */
195                 if (sci_rx_buf[2] == '\0')
196                         chime_start();
197                 else if (sci_rx_buf[3] != '\0' && sci_rx_buf[4] == '\0')
198                         chime_for(hex2u8(sci_rx_buf[2], sci_rx_buf[3]));
199                 else {
200                         send_string("510 Unknown chime duration." CRLF);
201                         return;
202                 }
203                 while (chime_count); /* spin */
204                 send_string("500 Chimed." CRLF);
205                 return;
206         } else if (sci_rx_buf[2] != '\0' && sci_rx_buf[3] == '\0')
207                 chime_for(hex2u8(sci_rx_buf[1], sci_rx_buf[2]));
208         else {
209                 send_string("510 Unknown chime duration." CRLF);
210                 return;
211         }
212         send_string("500 Chime started." CRLF);
213         return;
214 }
215
216 void do_silence() {
217         if (check_standalone()) return;
218         if (sci_rx_buf[1] == '\0')
219                 unchime_start();
220         else if (sci_rx_buf[1] == 'S') { /* synchronous beep */
221                 if (sci_rx_buf[2] == '\0')
222                         unchime_start();
223                 else if (sci_rx_buf[3] != '\0' && sci_rx_buf[4] == '\0')
224                         unchime_for(hex2u8(sci_rx_buf[2], sci_rx_buf[3]));
225                 else {
226                         send_string("511 Unknown silence duration." CRLF);
227                         return;
228                 }
229                 while (unchime_count); /* spin */
230                 send_string("501 Silenced." CRLF);
231                 return;
232         } else if (sci_rx_buf[2] != '\0' && sci_rx_buf[3] == '\0')
233                 unchime_for(hex2u8(sci_rx_buf[1], sci_rx_buf[2]));
234         else {
235                 send_string("511 Unknown silence duration." CRLF);
236                 return;
237         }
238         send_string("500 Silence started." CRLF);
239         return;
240 }
241
242 void print_switches(u8 prompted) {
243         if (prompted)
244                 send_string("600 ");
245         else
246                 send_string("610 ");
247         send_string(u82hex(misc_input));
248         send_string(" ");
249         send_string(u82hex(switch_input));
250         send_string(CRLF);
251 }
252
253 void ping_pong() {
254         /* make sure it's really a ping */
255         if (!my_strncmp("ING", (char*)sci_rx_buf+1, 3)) {
256                 unknown_command();
257                 return;
258         }
259         /* respond with ack & pong */
260         send_string("000 PONG!" CRLF);
261 }
262
263 u16 hex2addr(char* addrptr) {
264         u16 v;
265         v = hex2u8(addrptr[0], addrptr[1]) << 8;
266         v |= hex2u8(addrptr[2], addrptr[3]);
267         return v;
268 }
269
270 void peek() {
271         if (!my_strncmp("EEK", (char*)sci_rx_buf+1, 3)) {
272                 unknown_command();
273                 return;
274         }
275         if (check_badpoke()) return;
276         if (ishex(sci_rx_buf[4]) && ishex(sci_rx_buf[5]) && ishex(sci_rx_buf[6]) &&
277                         ishex(sci_rx_buf[7]) && sci_rx_buf[8] == '\0') {
278                 u16 v = hex2addr((char*)(sci_rx_buf+4));
279                 v = *((u8*)v);
280                 send_string("090 ");
281                 send_string(u82hex(v));
282                 send_string(CRLF);
283                 return;
284         }
285         send_string("091 Invalid location given." CRLF);
286 }
287
288 void poke() {
289         if (!my_strncmp("OKE", (char*)sci_rx_buf+1, 3)) {
290                 unknown_command();
291                 return;
292         }
293         if (check_badpoke()) return;
294         if (ishex(sci_rx_buf[4]) && ishex(sci_rx_buf[5]) && ishex(sci_rx_buf[6]) &&
295                         ishex(sci_rx_buf[7]) && ishex(sci_rx_buf[8]) && ishex(sci_rx_buf[9])
296                         && sci_rx_buf[10] == '\0') {
297                 u16 v;
298                 v = hex2addr((char*)(sci_rx_buf+4));
299                 *(u8*)v = hex2u8(sci_rx_buf[8], sci_rx_buf[9]);
300                 send_string("080 Written." CRLF);
301                 return;
302         }
303         send_string("081 Invalid location or byte given." CRLF);
304 }
305
306 void jump() {
307         if (!my_strncmp("UMP", (char*)sci_rx_buf+1, 3)) {
308                 unknown_command();
309                 return;
310         }
311         if (check_badpoke()) return;
312         if (ishex(sci_rx_buf[4]) && ishex(sci_rx_buf[5]) && ishex(sci_rx_buf[6]) &&
313                         ishex(sci_rx_buf[7]) && sci_rx_buf[8] == '\0') {
314                 u16 v = hex2addr((char*)(sci_rx_buf+4));
315                 send_string("070 Jumping now." CRLF);
316                 asm volatile ("jsr %0" : : "m"(*(u16*)v) : "d");
317                 send_string("071 And back." CRLF);
318                 return;
319         }
320         send_string("079 Invalid location given." CRLF);
321 }
322
323 void do_set_password() {
324         u8 i;
325         bool good = 1;
326         if (check_badpoke()) return;
327         for (i=1; i < 17; i++) {
328                 if (sci_rx_buf[i] == '\0') {
329                         good = 0;
330                         break;
331                 }
332         }
333         if (good && sci_rx_buf[17] != '\0') good = 0;
334         if (!good) {
335                 send_string("061 Password must be exactly 16 characters." CRLF);
336                 return;
337         }
338
339         mic_set_secret((char*)(sci_rx_buf+1));
340         send_string("060 Password has been set." CRLF);
341 }
342
343 void send_prompt() {
344         if (must_verify()) {
345                 send_string(u82hex(mic_challenge >> 8));
346                 send_string(u82hex(mic_challenge & 0xff));
347         }
348         send_string(is_standalone()?"% ":"# ");
349 }
350
351 void about() {
352         if (!my_strncmp("BOUT", (char*)sci_rx_buf+1, 4)) {
353                 unknown_command();
354                 return;
355         }
356         send_string(
357                 "-----------------------------------------------------------------" CRLF
358                 "    ROM2 (C) 2004 Bernard Blackham <[email protected]>" CRLF
359                 "-----------------------------------------------------------------" CRLF
360                 "                                        Revision " VERSION_STRING CRLF
361                 "" CRLF
362                 "   This snack machine was brought to you by " CRLF
363                 "    Bernard Blackham" CRLF
364                 "    Mark Tearle" CRLF
365                 "    Harry McNally" CRLF
366                 "    Michal Gornisiewicz" CRLF
367                 "    and others." CRLF
368                 "" CRLF
369                 " Another UCC project in action.         http://www.ucc.asn.au/" CRLF
370         );
371 }
372
373 void set_echo() {
374         if (my_strncmp("CHO ON", (char*)sci_rx_buf+1, 6))
375                 sci_echo = 1;
376         else if (my_strncmp("CHO OFF", (char*)sci_rx_buf+1, 7))
377                 sci_echo = 0;
378         else
379                 unknown_command();
380 }
381
382 void moo() {
383         if (!my_strncmp("OO", (char*)sci_rx_buf+1, 2)) {
384                 unknown_command();
385                 return;
386         }
387         send_string(
388 "       ____________" CRLF
389 "       |__________|" CRLF
390 "      /           /\\" CRLF
391 "     /  U C C    /  \\" CRLF
392 "    /___________/___/|" CRLF
393 "    |          |     |" CRLF
394 "    |  ==\\ /== |     |" CRLF
395 "    |   O   O  | \\ \\ |" CRLF
396 "    |     <    |  \\ \\|" CRLF
397 "   /|          |   \\ \\" CRLF
398 "  / |  \\_____/ |   / /" CRLF
399 " / /|          |  / /|" CRLF
400 "/||\\|          | /||\\/" CRLF
401 "    -------------|   " CRLF
402 "        | |    | | " CRLF
403 "       <__/    \\__>" CRLF
404 "" CRLF
405 "  ... Where's the cheese?" CRLF
406         );
407 }
408
409 void help() {
410         send_string(
411                 "Valid commands are:" CRLF
412                 " ABOUT         ROM information" CRLF
413                 " B[S][nn]      beep [synchronously] for a duration nn (optional)" CRLF
414                 " C[S][nn]      silence [synchronously] for a duration nn (optional)" CRLF
415                 " Dxxxxxxxxxx   show a message on the display" CRLF
416                 " ECHO {ON|OFF} turn echo on or off" CRLF
417                 " GETROM        download the ROM source code using xmodem" CRLF
418                 " H[...]        this help screen" CRLF
419                 "*JUMPxxxx      jumps to a subroute at location xxxx" CRLF
420                 "*PEEKxxxx      returns the value of the byte at location xxxx" CRLF
421                 "*POKExxxxyy    sets the value of location xxxx to yy" CRLF
422                 " PING          pongs" CRLF
423                 " S[...]        query all internal switch states" CRLF
424                 "+Vnn           vend an item" CRLF
425                 "+VALL          vend all items" CRLF
426                 "*Wxxxxxxxxxxxx set a new password for authenticated vends. xxx=16 chars" CRLF
427                 "" CRLF
428                 "Very few functions are available when the machine is in standalone " CRLF
429                 "mode (DIP SW 1 is set)" CRLF
430                 "+ denotes that this item requires authentication if DIP SW 2 is set" CRLF
431                 "* denotes that DIP SW 3 must be set to use these" CRLF
432                 "Commands starting with # are ignored (comments)" CRLF
433         );
434 }
435
436 extern const char _rom_src_data[];
437 extern const u16 _rom_src_len;
438 void getrom() {
439         if (!my_strncmp("ETROM", (char*)sci_rx_buf+1, 5)) {
440                 unknown_command();
441                 return;
442         }
443         char s[4];
444         send_string("Writing to serial port (maybe). Size is 0x");
445         send_string(u82hex(_rom_src_len >> 8));
446         send_string(u82hex(_rom_src_len & 0xff));
447         send_string("@0x");
448         send_string(u82hex((u16)(&_rom_src_data) >> 8));
449         send_string(u82hex((u16)(&_rom_src_data) & 0xff));
450         send_string(" with signature ");
451         s[0] = _rom_src_data[0];
452         s[1] = _rom_src_data[1];
453         s[2] = _rom_src_data[2];
454         s[3] = '\0';
455         send_string(s);
456         send_string(CRLF " Type YES to download rom.tar.bz2 via XMODEM: ");
457         msg_clr();
458         while (!sci_have_packet); /* spin */
459         if (!my_strncmp("YES", (char*)sci_rx_buf, 3)) {
460                 send_string(CRLF "Transfer cancelled." CRLF);
461                 return;
462         }
463
464         sci_init();
465         sci_doing_xmodem = 1;
466         if (!xmodem_init_xfer()) {
467                 sci_doing_xmodem = 0;
468                 send_string("XMODEM init failed. Nobody's listening :(" CRLF);
469                 return;
470         }
471         char *p = (char*)_rom_src_data;
472         char *end = (char*)_rom_src_data+_rom_src_len;
473         bool aborted = 0;
474         while (1) {
475                 if (p + 128 > end) {
476                         /* send partial packet */
477                         if (!xmodem_send_packet((char*)p, end-p)) aborted = 1;
478                         break;
479                 } if ((u16)p == 0xb600) {
480                         /* we have an eeprom here. skip it. */
481                         p += 0x0200;
482                         continue;
483                 } else if (!xmodem_send_packet((char*)p, 128)) {
484                         aborted = 1;
485                         break;
486                 }
487                 p += 128;
488         }
489
490         xmodem_finish_xfer();
491         sci_doing_xmodem = 0;
492         if (aborted)
493                 send_string(CRLF "Transfer aborted." CRLF);
494         else
495                 send_string(CRLF "Transfer complete." CRLF);
496 }
497
498 void quit() {
499         if (my_strncmp("UIT", (char*)sci_rx_buf+1, 3))
500                 send_string("013 You can't quit you doofus." CRLF);
501         else
502                 unknown_command();
503 }
504
505 int main() {
506         u8 i;
507         for (i = 0; i < 11; i++)
508                 display_buf[i] = ' ';
509         display_buf[10] = '\0';
510
511         changer_output = 0x7f;
512         _io_ports[M6811_PORTA] = 0xc0; /* display on. talking to serial port */
513         _io_ports[M6811_DDRA] = 0xfc;
514         _io_ports[M6811_DDRD] = 0x3e;
515         _io_ports[M6811_SPCR] = M6811_MSTR | M6811_SPR1;
516         set_misc_output(0x00);
517
518         display_init();
519         set_msg(" HELLO    ");
520         delay(1000);
521
522         unlock(); /* enable interrupts */
523
524         set_msg("  CRUEL   ");
525
526         //coinmech_init();
527         sci_init();
528         keypad_init();
529         last_coin_value = 0;
530         last_door_open = 0;
531
532         delay(1000);
533
534         set_msg("   WORLD  ");
535         delay(1000);
536
537         chime_start();
538
539         send_string("5N4X0RZ R US" CRLF);
540
541         mic_challenge = 0;
542         
543         last_standalone = is_standalone();
544         if (last_standalone)
545                 cur_motor[0] = 0xff;
546         else
547                 cur_motor[0] = 0;
548         send_prompt();
549
550         last_switch_input = switch_input;
551         last_misc_input = misc_input;
552
553         while(1) {
554                 if (cur_motor[0] == 0xff) { /* signal to say redraw screen */
555                         set_msg("*5N4X0RZ* ");
556                         cur_motor[0] = 0;
557                 }
558
559                 if (door_open() != last_door_open) {
560                         last_door_open = door_open();
561                         send_door_msg(last_door_open);
562                         chime_start();
563                         if (is_standalone())
564                                 set_msg(last_door_open?"DOOR OPEN ":"DOOR CLOSE");
565                 }
566
567                 if (last_standalone != is_standalone()) {
568                         /* somebody flicked the standalone switch */
569                         msg_clr();
570                         send_string(CRLF);
571                         send_prompt();
572                         last_standalone = is_standalone();
573                 }
574
575                 if (last_misc_input != misc_input) {
576                         print_switches(0);
577                         last_misc_input = misc_input;
578                 }
579
580                 if (last_switch_input != switch_input) {
581                         print_switches(0);
582                         last_switch_input = switch_input;
583                 }
584
585                 if (sci_have_packet) {
586                         switch (sci_rx_buf[0]) {
587                                 case '\0':
588                                 case '#':
589                                         send_string(CRLF);
590                                         break;
591                                 case 'A':
592                                         about();
593                                         break;
594                                 case 'B': 
595                                         do_chime();
596                                         break;
597                                 case 'C': 
598                                         do_silence();
599                                         break;
600                                 case 'D':
601                                         write_to_display();
602                                         break;
603                                 case 'E':
604                                         set_echo();
605                                         break;
606                                 case 'G':
607                                         getrom();
608                                         break;
609                                 case 'H':
610                                         help();
611                                         break;
612                                 case 'M':
613                                         moo();
614                                         break;
615                                 case 'P':
616                                         if (sci_rx_buf[1] == 'I')
617                                                 ping_pong();
618                                         else if (sci_rx_buf[1] == 'O')
619                                                 poke();
620                                         else if (sci_rx_buf[1] == 'E')
621                                                 peek();
622                                         break;
623                                 case 'J':
624                                         jump();
625                                         break;
626                                 case 'Q':
627                                         quit();
628                                         break;
629                                 case 'S':
630                                         print_switches(1);
631                                         break;
632                                 case 'V':
633                                         dispense_something();
634                                         break;
635                                 case 'W':
636                                         do_set_password();
637                                         break;
638                                 default:
639                                         // shurg
640                                         unknown_command();
641                                         break;
642                         }
643                         msg_clr();
644                         send_prompt();
645                 }
646
647                 keypad_read();
648                 if (keypad_pressed()) {
649                         if (is_standalone()) {
650                                 if (last_key == KEY_RESET) {
651                                         cur_motor[0] = 0xff;
652                                 } else {
653                                         if (cur_motor[0]) {
654                                                 u8 motor_num;
655                                                 cur_motor[1] = last_key%10;
656                                                 display_buf[1] = cur_motor[1]+'0';
657                                                 set_msg(display_buf);
658
659                                                 motor_num = cur_motor[0]%10;
660                                                 motor_num *= 10;
661                                                 motor_num += cur_motor[1];
662                                                 switch (dispense_motor(motor_num)) {
663                                                         case MOTOR_HOME_FAIL:
664                                                                 set_msg(" HOME FAIL ");
665                                                                 break;
666                                                         case MOTOR_CURRENT_FAIL:
667                                                                 set_msg(" OVER CRNT ");
668                                                                 break;
669                                                         case MOTOR_SUCCESS:
670                                                                 set_msg("THANK  YOU");
671                                                                 break;
672                                                         case MOTOR_NOSLOT:
673                                                                 set_msg(" NO MOTOR ");
674                                                                 break;
675                                                         default:
676                                                                 set_msg("ERRRRRRRR?");
677                                                                 break;
678                                                 }
679
680                                                 display_buf[0] = ' ';
681                                                 display_buf[1] = ' ';
682                                                 cur_motor[0] = 0xff;
683                                                 delay(500);
684                                         } else {
685                                                 cur_motor[0] = last_key;
686                                                 display_buf[0] = (last_key%10)+'0';
687                                                 set_msg(display_buf);
688                                         }
689                                 }
690                         } else
691                                 send_keypress(last_key);
692                 }
693
694                 /*
695                 if (coin_value != last_coin_value) {
696                         send_balance();
697                         last_coin_value = coin_value;
698                 }
699                 */
700         }
701 }

UCC git Repository :: git.ucc.asn.au