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

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