xmodem support
[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 "R 20040622"
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
18 u8 last_standalone;
19 u8 last_switch_input;
20 u8 last_misc_input;
21 u16 last_coin_value;
22 bool last_door_open;
23 char display_buf[11];
24 /* cur_motor[0] is 0 for nothing, or 1..10, or 0xff to say redraw.
25  * cur_motor[1] is 0..9 and only meaningful is cur_motor[0] != 0. */
26 u8 cur_motor[2];
27
28 bool check_standalone() {
29         if (is_standalone()) {
30                 send_string("011 In standalone mode. Function disabled." CRLF);
31                 return 1;
32         }
33         return 0;
34 }
35
36 void unknown_command() {
37         send_string("012 Unknown command. Type HELP for help." CRLF);
38 }
39
40 void motor_reply(u8 code) {
41         /* returns a message of the form MXYY - X is return code, YY is motor */
42         switch (code) {
43                 case MOTOR_SUCCESS:
44                         send_string("100 Vend successful." CRLF);
45                         break;
46                 case MOTOR_NOSLOT:
47                         send_string("151 No motor there." CRLF);
48                         break;
49                 case MOTOR_CURRENT_FAIL:
50                         send_string("152 Over current." CRLF);
51                         break;
52                 case MOTOR_HOME_FAIL:
53                         send_string("153 Home sensors failing." CRLF);
54                         break;
55                 default:
56                         send_string("159 Unknown motor error." CRLF);
57         }
58 }
59
60 void dispense_something() {
61         /* process a message VXX in sci_rx_buf where XX is motor number */
62         u8 slot;
63
64         if (check_standalone()) return;
65
66         if (my_strncmp("ALL", (char*)sci_rx_buf+1, 3)) {
67                 char motor[3];
68                 motor[2] = '\0';
69                 send_string("102 Vend all motors starting." CRLF);
70                 for (motor[0] = '0'; motor[0] <= '9'; motor[0]++) {
71                         for (motor[1] = '0'; motor[1] <= '9'; motor[1]++) {
72                                 send_string("101 Vending ");
73                                 send_string(motor);
74                                 send_string(CRLF);
75                                 motor_reply(dispense_motor(motor[0]*10+motor[1]));
76                         }
77                 }
78                 send_string("102 Vend all motors complete." CRLF);
79                 return;
80         }
81
82         if ((sci_rx_buf[1] < '0') || (sci_rx_buf[1] > '9') ||
83                 (sci_rx_buf[2] < '0') || (sci_rx_buf[2] > '9')) {
84                 sci_rx_buf[1] = sci_rx_buf[2] = '0';
85                 motor_reply(MOTOR_NOSLOT);
86                 return;
87         }
88
89         slot = (sci_rx_buf[1] - '0') * 10;
90         slot += sci_rx_buf[2] - '0';
91
92         motor_reply(dispense_motor(slot));
93 }
94
95 void write_to_display() {
96         /* process a message in the form DXXXXXXXXXX to send to display */
97         u8 i;
98         char buf[11];
99
100         if (check_standalone()) return;
101
102         for (i = 0; i < 10; i++)
103                 if (sci_rx_buf[i+1])
104                         buf[i] = sci_rx_buf[i+1];
105                 else
106                         break;
107
108         for (; i < 10; i++) /* pad the rest out with spaces */
109                 buf[i] = ' ';
110         buf[i] = '\0';
111
112         set_msg(buf);
113         send_string("300 Written." CRLF);
114 }
115
116 void send_balance() {
117         sci_tx_buf[0] = 'C';
118         sci_tx_buf[1] = have_change?'0':'1';
119         sci_tx_buf[2] = (coin_value/10000)%10;
120         sci_tx_buf[3] = (coin_value/1000)%10;
121         sci_tx_buf[4] = (coin_value/100)%10;
122         sci_tx_buf[5] = (coin_value/10)%10;
123         sci_tx_buf[6] = coin_value%10;
124         sci_tx_buf[8] = 0;
125         send_buffer(1);
126 }
127
128 void give_change() {
129         u16 cost;
130
131         if ((sci_rx_buf[1] < '0') || (sci_rx_buf[1] > '9') ||
132                 (sci_rx_buf[2] < '0') || (sci_rx_buf[2] > '9') ||
133                 (sci_rx_buf[3] < '0') || (sci_rx_buf[3] > '9') ||
134                 (sci_rx_buf[4] < '0') || (sci_rx_buf[4] > '9') ||
135                 (sci_rx_buf[5] < '0') || (sci_rx_buf[5] > '9')) {
136                 send_nack();
137         }
138         cost = sci_rx_buf[1] - '0';
139         cost *= 10; cost = sci_rx_buf[2] - '0';
140         cost *= 10; cost = sci_rx_buf[3] - '0';
141         cost *= 10; cost = sci_rx_buf[4] - '0';
142         cost *= 10; cost = sci_rx_buf[5] - '0';
143
144         coin_cost(cost);
145         send_ack();
146
147
148 void send_keypress(u8 key) {
149         /* send a packet of the form KX with X being the key, or R for reset */
150         if (is_standalone()) return;
151
152         sci_tx_buf[0] = '2';
153         if (key == KEY_RESET) {
154                 sci_tx_buf[1] = '1';
155                 sci_tx_buf[2] = '1';
156         } else {
157                 sci_tx_buf[1] = '0';
158                 sci_tx_buf[2] = (key%10)+'0';
159         }
160         sci_tx_buf[3] = '\0';
161         send_buffer(0);
162         send_string(" key." CRLF);
163 }
164
165 void send_door_msg(bool open) {
166         if (is_standalone()) return;
167         sci_tx_buf[0] = '4';
168         sci_tx_buf[1] = '0';
169         sci_tx_buf[2] = open?'1':'0';
170         send_buffer(0);
171         if (open)
172                 send_string(" door open." CRLF);
173         else
174                 send_string(" door closed." CRLF);
175 }
176
177 u8 hexchar2u8(char b) {
178         if (b >= '0' && b <= '9') return b-'0';
179         if (b >= 'a' && b <= 'f') return b-'a'+0x0a;
180         if (b >= 'A' && b <= 'F') return b-'A'+0x0a;
181         return 0;
182 }
183
184 char nibble2hexchar(u8 b) {
185         if (b <= 9) return b+'0';
186         if (b >= 10 && b <= 15) return b+'A'-10;
187         return 'X';
188 }
189
190 u8 hex2u8(char msb, char lsb) {
191         return (hexchar2u8(msb) << 4) + hexchar2u8(lsb);
192 }
193
194 static char hexconv_buf[3];
195 char* u82hex(u8 a) {
196         hexconv_buf[0] = nibble2hexchar((a&0xf0) >> 4);
197         hexconv_buf[1] = nibble2hexchar(a&0x0f);
198         hexconv_buf[2] = '\0';
199         return hexconv_buf;
200 }
201
202 void do_chime() {
203         if (check_standalone()) return;
204         if (sci_rx_buf[1] == '\0')
205                 chime_start();
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 Chimed." CRLF);
213 }
214
215 void print_switches(u8 prompted) {
216         if (prompted)
217                 send_string("600 ");
218         else
219                 send_string("610 ");
220         send_string(u82hex(misc_input));
221         send_string(" ");
222         send_string(u82hex(switch_input));
223         send_string(CRLF);
224 }
225
226 void ping_pong() {
227         /* make sure it's really a ping */
228         if (!my_strncmp("ING", (char*)sci_rx_buf+1, 3)) {
229                 unknown_command();
230                 return;
231         }
232         /* respond with ack & pong */
233         send_string("000 PONG!" CRLF);
234 }
235
236 void send_prompt() {
237         send_string(is_standalone()?"$ ":"# ");
238 }
239
240 void about() {
241         if (!my_strncmp("BOUT", (char*)sci_rx_buf+1, 4)) {
242                 unknown_command();
243                 return;
244         }
245         send_string(
246                 "-----------------------------------------------------------------" CRLF
247                 "    ROM2 (C) 2004 Bernard Blackham <[email protected]>" CRLF
248                 "-----------------------------------------------------------------" CRLF
249                 "                                        Revision " VERSION_STRING CRLF
250                 "" CRLF
251                 "   This snack machine was brought to you by " CRLF
252                 "    Bernard Blackham" CRLF
253                 "    Mark Tearle" CRLF
254                 "    Harry McNally" CRLF
255                 "    Michal Gornisiewicz" CRLF
256                 "    and others." CRLF
257                 "" CRLF
258                 " Another UCC project in action.         http://www.ucc.asn.au/" CRLF
259         );
260 }
261
262 void set_echo() {
263         if (my_strncmp("CHO ON", (char*)sci_rx_buf+1, 6))
264                 sci_echo = 1;
265         else if (my_strncmp("CHO OFF", (char*)sci_rx_buf+1, 7))
266                 sci_echo = 0;
267         else
268                 unknown_command();
269 }
270
271 void moo() {
272         if (!my_strncmp("OO", (char*)sci_rx_buf+1, 2)) {
273                 unknown_command();
274                 return;
275         }
276         send_string(
277 "       ____________" CRLF
278 "       |__________|" CRLF
279 "      /           /\\" CRLF
280 "     /  U C C    /  \\" CRLF
281 "    /___________/___/|" CRLF
282 "    |          |     |" CRLF
283 "    |  ==\\ /== |     |" CRLF
284 "    |   O   O  | \\ \\ |" CRLF
285 "    |     <    |  \\ \\|" CRLF
286 "   /|          |   \\ \\" CRLF
287 "  / |  \\_____/ |   / /" CRLF
288 " / /|          |  / /|" CRLF
289 "/||\\|          | /||\\/" CRLF
290 "    -------------|   " CRLF
291 "        | |    | | " CRLF
292 "       <__/    \\__>" CRLF
293 "" CRLF
294 "  ... Where's the cheese?" CRLF
295         );
296 }
297
298 void help() {
299         send_string(
300                 "Valid commands are:" CRLF
301                 " ABOUT         ROM information" CRLF
302                 " PING          pongs" CRLF
303                 " ECHO {ON|OFF} turn echo on or off" CRLF
304                 " Vnn           vend an item" CRLF
305                 " VALL          vend all items" CRLF
306                 " DXXXXXXXXXX   show a message on the display" CRLF
307                 " B[nn]         beep for a duration nn (optional)" CRLF
308                 " S[...]        query all internal switch states" CRLF
309                 " H[...]        this help screen" CRLF
310                 " GETROM        download the ROM source code using xmodem" CRLF
311                 "Comments start with a #" CRLF
312         );
313 }
314
315 extern const char _rom_src_data[];
316 extern const u16 _rom_src_len;
317 void getrom() {
318         if (!my_strncmp("ETROM", (char*)sci_rx_buf+1, 5)) {
319                 unknown_command();
320                 return;
321         }
322         char s[4];
323         send_string("Writing to serial port (maybe). Size is 0x");
324         send_string(u82hex(_rom_src_len >> 8));
325         send_string(u82hex(_rom_src_len & 0xff));
326         send_string(" with signature ");
327         s[0] = _rom_src_data[0];
328         s[1] = _rom_src_data[1];
329         s[2] = _rom_src_data[2];
330         s[3] = '\0';
331         send_string(s);
332         send_string(CRLF " Type YES to download via XMODEM: ");
333         msg_clr();
334         while (!sci_have_packet); /* spin */
335         if (!my_strncmp("YES", (char*)sci_rx_buf, 3)) return;
336
337         sci_init();
338         sci_doing_xmodem = 1;
339         if (!xmodem_init_xfer()) {
340                 sci_doing_xmodem = 0;
341                 send_string("XMODEM init failed. Nobody's listening :(" CRLF);
342                 return;
343         }
344         char *p = (char*)_rom_src_data;
345         char *end = (char*)_rom_src_data+_rom_src_len;
346         bool aborted = 0;
347         while (1) {
348                 if (!xmodem_send_packet((char*)p, 128)) {
349                         aborted = 1;
350                         break;
351                 }
352                 p += 128;
353                 if (p + 128 > end) {
354                         /* send partial packet */
355                         if (!xmodem_send_packet((char*)p, end-p)) aborted = 1;
356                         break;
357                 }
358         }
359
360         xmodem_finish_xfer();
361         sci_doing_xmodem = 0;
362         if (aborted)
363                 send_string(CRLF "Transfer aborted." CRLF);
364         else
365                 send_string(CRLF "Transfer complete." CRLF);
366 }
367
368 void quit() {
369         if (my_strncmp("UIT", (char*)sci_rx_buf+1, 3))
370                 send_string("013 You can't quit you doofus." CRLF);
371         else
372                 unknown_command();
373 }
374
375 int main() {
376         u8 i;
377         for (i = 0; i < 11; i++)
378                 display_buf[i] = ' ';
379         display_buf[10] = '\0';
380
381         changer_output = 0x7f;
382         _io_ports[M6811_PORTA] = 0xc0; /* display on. talking to serial port */
383         _io_ports[M6811_DDRA] = 0xfc;
384         _io_ports[M6811_DDRD] = 0x3e;
385         _io_ports[M6811_SPCR] = M6811_MSTR | M6811_SPR1;
386         set_misc_output(0x00);
387
388         display_init();
389         set_msg(" HELLO    ");
390         delay(1000);
391
392         unlock(); /* enable interrupts */
393
394         set_msg("  CRUEL   ");
395
396         //comm_init();
397         //coinmech_init();
398         sci_init();
399         keypad_init();
400         last_coin_value = 0;
401         last_door_open = 0;
402
403         delay(1000);
404
405         set_msg("   WORLD  ");
406         delay(1000);
407
408         chime_start();
409
410         send_string("5N4X0RZ R US" CRLF);
411         
412         last_standalone = is_standalone();
413         if (last_standalone)
414                 cur_motor[0] = 0xff;
415         else
416                 cur_motor[0] = 0;
417         send_prompt();
418
419         last_switch_input = switch_input;
420         last_misc_input = misc_input;
421
422         while(1) {
423                 if (cur_motor[0] == 0xff) { /* signal to say redraw screen */
424                         set_msg("*5N4X0RZ* ");
425                         cur_motor[0] = 0;
426                 }
427
428                 if (door_open() != last_door_open) {
429                         last_door_open = door_open();
430                         send_door_msg(last_door_open);
431                         chime_start();
432                         set_msg(last_door_open?"DOOR OPEN ":"DOOR CLOSE");
433                 }
434
435                 if (last_standalone != is_standalone()) {
436                         /* somebody flicked the standalone switch */
437                         msg_clr();
438                         send_string(CRLF);
439                         send_prompt();
440                         last_standalone = is_standalone();
441                 }
442
443                 if (last_misc_input != misc_input) {
444                         print_switches(0);
445                         last_misc_input = misc_input;
446                 }
447
448                 if (last_switch_input != switch_input) {
449                         print_switches(0);
450                         last_switch_input = switch_input;
451                 }
452
453                 if (sci_have_packet) {
454                         switch (sci_rx_buf[0]) {
455                                 case '\0':
456                                 case '#':
457                                         send_string(CRLF);
458                                         break;
459                                 case 'E':
460                                         set_echo();
461                                         break;
462                                 case 'H':
463                                         help();
464                                         break;
465                                 case 'V':
466                                         dispense_something();
467                                         break;
468                                 case 'D':
469                                         write_to_display();
470                                         break;
471                                 case 'B': 
472                                         do_chime();
473                                         break;
474                                 case 'P':
475                                         ping_pong();
476                                         break;
477                                 case 'A':
478                                         about();
479                                         break;
480                                 case 'S':
481                                         print_switches(1);
482                                         break;
483                                 case 'M':
484                                         moo();
485                                         break;
486                                 case 'Q':
487                                         quit();
488                                         break;
489                                 case 'G':
490                                         getrom();
491                                         break;
492                                 default:
493                                         // shurg
494                                         unknown_command();
495                                         break;
496                         }
497                         msg_clr();
498                         send_prompt();
499                 }
500
501                 keypad_read();
502                 if (keypad_pressed()) {
503                         if (is_standalone()) {
504                                 if (last_key == KEY_RESET) {
505                                         cur_motor[0] = 0xff;
506                                 } else {
507                                         if (cur_motor[0]) {
508                                                 u8 motor_num;
509                                                 cur_motor[1] = last_key%10;
510                                                 display_buf[1] = cur_motor[1]+'0';
511                                                 set_msg(display_buf);
512
513                                                 motor_num = cur_motor[0]%10;
514                                                 motor_num *= 10;
515                                                 motor_num += cur_motor[1];
516                                                 switch (dispense_motor(motor_num)) {
517                                                         case MOTOR_HOME_FAIL:
518                                                                 set_msg(" HOME FAIL ");
519                                                                 break;
520                                                         case MOTOR_CURRENT_FAIL:
521                                                                 set_msg(" OVER CRNT ");
522                                                                 break;
523                                                         case MOTOR_SUCCESS:
524                                                                 set_msg("THANK  YOU");
525                                                                 break;
526                                                         case MOTOR_NOSLOT:
527                                                                 set_msg(" NO MOTOR ");
528                                                                 break;
529                                                         default:
530                                                                 set_msg("ERRRRRRRR?");
531                                                                 break;
532                                                 }
533
534                                                 display_buf[0] = ' ';
535                                                 display_buf[1] = ' ';
536                                                 cur_motor[0] = 0xff;
537                                                 delay(500);
538                                         } else {
539                                                 cur_motor[0] = last_key;
540                                                 display_buf[0] = (last_key%10)+'0';
541                                                 set_msg(display_buf);
542                                         }
543                                 }
544                         } else
545                                 send_keypress(last_key);
546                 }
547
548                 /*
549                 if (coin_value != last_coin_value) {
550                         send_balance();
551                         last_coin_value = coin_value;
552                 }
553                 */
554         }
555 }

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