1 /*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.0 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9 * License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Initial Developer of this code is David Baum.
13 * Portions created by David Baum are Copyright (C) 1998 David Baum.
14 * All Rights Reserved.
15 *
16 * Portions created by John Hansen are Copyright (C) 2005 John Hansen.
17 * All Rights Reserved.
18 *
19 */
20
21 #ifdef WIN32
22 #include <windows.h>
23 #endif
24
25 #include <cstdio>
26 #include <cstring>
27 #include <cctype>
28 #include <cstdlib>
29 #include <ctime>
30
31 #include "Program.h"
32 #include "RCX_Image.h"
33 #include "RCX_Link.h"
34 #include "Symbol.h"
35 #include "PreProc.h"
36 #include "parser.h"
37 #include "Macro.h"
38 #include "RCX_Cmd.h"
39 #include "RCX_Log.h"
40 #include "SRecord.h"
41 #include "AutoFree.h"
42 #include "DirList.h"
43 #include "Buffer.h"
44 #include "Error.h"
45 #include "Compiler.h"
46 #include "CmdLine.h"
47 #include "version.h"
48
49 using std::fopen;
50 using std::printf;
51 using std::time_t;
52 using std::localtime;
53 using std::tm;
54
55
56 // use this to check for memory leaks in the compiler
57 //#define CHECK_LEAKS
58
59 // use these to debug the LEXER
60 //#define TEST_LEXER 1 // test lexer only
61 //#define TEST_LEXER 2 // test lexer and pre-processor
62
63
64
65 #ifdef CHECK_LEAKS
66 #include <DebugNew.h>
67 #endif
68
69 // some win32 consoles are lame...use stdout instead of stderr for usage, etc.
70 #ifdef WIN32
71 #define STDERR stdout
72 #else
73 #define STDERR stderr
74 #endif
75
76 FILE *gErrorStream = stderr;
77
78 #define kMaxPrintedErrors 10
79
80
81
82 class AutoLink : public RCX_Link
83 {
84 public:
AutoLink()85 AutoLink() : fSerialPort(0), fOpen(false) {}
~AutoLink()86 ~AutoLink() { Close(); }
87
88 RCX_Result Open();
89 void Close();
90 RCX_Result Send(const RCX_Cmd *cmd, bool retry=true);
91
SetSerialPort(const char * sp)92 void SetSerialPort(const char *sp) { fSerialPort = sp; }
93
94 bool DownloadProgress(int soFar, int total);
95
96 private:
97 const char* fSerialPort;
98 bool fOpen;
99 };
100
101
102
103 class MyCompiler : public Compiler, public ErrorHandler
104 {
105 public:
MyCompiler()106 MyCompiler() {}
107
108 Buffer *CreateBuffer(const char *name);
109
110 void AddError(const Error &e, const LexLocation *loc);
AddDir(const char * dirspec)111 void AddDir(const char *dirspec) { fDirs.Add(dirspec); }
112
113 private:
114 DirList fDirs;
115 } gMyCompiler;
116
117
118
119 #define kMaxFirmware 65536
120 #define kOldIncludePathEnv "NQCC_INCLUDE"
121 #define kNewIncludePathEnv "NQC_INCLUDE"
122 #define kOptionsEnv "NQC_OPTIONS"
123 #define kLowBattery 6600
124 #define kLow45Battery 3300
125
126 #define kRCXFileExtension ".rcx"
127 #define kNQCFileExtension ".nqc"
128
129
130
131 // error codes in addition to RCX_Result codes
132 #define kUsageError (kRCX_LastError - 1)
133 #define kQuietError (kRCX_LastError - 2)
134
135 // codes for the actions
136 enum
137 {
138 kFirstActionCode = 256,
139 kDatalogCode = kFirstActionCode,
140 kDatalogFullCode,
141 kClearMemoryCode,
142 kFirmwareCode,
143 kFirmware4xCode,
144 kNearCode,
145 kFarCode,
146 kWatchCode,
147 kSleepCode,
148 kRunCode,
149 kProgramCode,
150 kMessageCode,
151 kRawCode,
152 kRaw1Code,
153 kRemoteCode,
154 kApiCode,
155 kHelpCode,
156 kCompileStdinCode
157 };
158
159 // these must be in the same order as the codes for the long options
160 static const char *sActionNames[] = {
161 "datalog", "datalog_full", "clear", "firmware", "firmfast", "near", "far", "watch",
162 "sleep", "run", "pgm", "msg", "raw", "raw1", "remote", "api", "help", ""
163 };
164
165 // these MUST be in the same order as the RCX_TargetType values
166 static const char *sTargetNames[] =
167 {
168 "rcx",
169 "cm",
170 "scout",
171 "rcx2",
172 "spy",
173 "swan"
174 };
175
176
177 struct Request
178 {
179 const char *fSourceFile;
180 const char *fOutputFile;
181 const char *fListFile;
182 bool fListing;
183 bool fSourceListing;
184 bool fDownload;
185 bool fBinary;
186 bool fGenLASM;
187 int fFlags;
188 };
189
190 static int GetActionCode(const char *arg);
191 static RCX_Result ProcessCommandLine(int argc, char **argv);
192 static void PrintError(RCX_Result error, const char *filename = 0);
193 static void PrintUsage();
194 static void DefineMacro(const char *text);
195 static RCX_Result ProcessFile(const char *sourceFile, const Request &req);
196 static char *CreateFilename(const char *source, const char *oldExt, const char *newExt);
197 static const char *LeafName(const char *filename);
198 static int CheckExtension(const char *s1, const char *ext);
199 static RCX_Image *Compile(const char *sourceFile, int flags);
200 static bool GenerateListing(RCX_Image *image, const char *filename, bool includeSource, bool generateLASM);
201 static RCX_Result Download(RCX_Image *image);
202 static RCX_Result UploadDatalog(bool verbose);
203 static RCX_Result DownloadFirmware(const char *filename, bool fast);
204 static RCX_Result SetWatch(const char *timeSpec);
205 static RCX_Result SetErrorFile(const char *filename);
206 static RCX_Result RedirectOutput(const char *filename);
207 static RCX_Result SendRawCommand(const char *text, bool retry);
208 static RCX_Result ClearMemory();
209 static RCX_Result SetTarget(const char *name);
210 static RCX_Result SendRemote(const char *event, int repeat);
211 static void PrintToken(int t, TokenVal v);
212 static void PrintApi(bool compatMode);
213 static bool SameString(const char *s1, const char *s2);
214 static void PrintVersion();
215
216 AutoLink gLink;
217 RCX_TargetType gTargetType = kRCX_RCXTarget;
218 bool gVerbose = false;
219 int gTimeout = 0;
220 bool gQuiet = false;
221
222
main(int argc,char ** argv)223 int main(int argc, char **argv)
224 {
225 RCX_Result result;
226
227 // add any default include paths
228 gMyCompiler.AddDir(getenv(kOldIncludePathEnv));
229 gMyCompiler.AddDir(getenv(kNewIncludePathEnv));
230
231 result = ProcessCommandLine(argc, argv);
232 PrintError(result);
233
234 gLink.Close();
235
236 if (gErrorStream != stderr &&
237 gErrorStream != stdout)
238 fclose(gErrorStream);
239
240 #ifdef DEBUG_NEW
241 printf("%d allocations, %d total bytes, %d max\n", gDebugNewAllocCount, gDebugNewAllocCurr, gDebugNewAllocMax);
242 #endif
243
244
245 return RCX_ERROR(result) ? result : 0;
246 }
247
248
ProcessCommandLine(int argc,char ** argv)249 RCX_Result ProcessCommandLine(int argc, char ** argv)
250 {
251 bool optionsOK = true;
252 bool fileProcessed = false;
253 Request req = { 0 }; // rest will be zero'ed
254 CmdLine args;
255 RCX_Result result = kRCX_OK;
256 RCX_Cmd cmd;
257
258 // first add environment options
259 args.Parse(getenv(kOptionsEnv));
260
261 // add all command line args after the first one
262 #ifdef WIN32
263 // For Win32, bypass the argc/argv array and get the raw command line
264 // This is for backwards compatability with BricxCC which doesn't like its
265 // args to be escaped by the runtime
266 #pragma unused(argc, argv)
267 args.Parse(GetCommandLineA(), 1);
268 #else
269 if (argc > 1)
270 args.Add(argc-1, argv+1);
271 #endif
272
273 // process the args
274 while(args.Remain() && !RCX_ERROR(result))
275 {
276 const char* a=args.Next();
277 bool isOption = (a[0] == '-');
278
279 #ifdef WIN32
280 if (a[0]=='/') isOption = true;
281 #endif
282
283 if (isOption)
284 {
285 int code = GetActionCode(a+1);
286
287 if (code)
288 {
289 optionsOK = false;
290 }
291 else
292 {
293 if (!optionsOK) return kUsageError;
294 code = a[1];
295 }
296
297 switch(code)
298 {
299 // options
300 case '1':
301 req.fFlags |= Compiler::kCompat_Flag;
302 break;
303 case 'D':
304 if (*(a+2)=='\0') return kUsageError;
305 DefineMacro(a+2);
306 break;
307 case 'E':
308 result = SetErrorFile(a+2);
309 break;
310 case 'I':
311 if (*(a+2)=='\0') return kUsageError;
312 gMyCompiler.AddDir(a+2);
313 break;
314 case 'L':
315 req.fListing = true;
316 req.fListFile = a[2] ? a+2 : 0;
317 break;
318 case 'R':
319 result = RedirectOutput(a+2);
320 break;
321 case 's':
322 req.fSourceListing = true;
323 break;
324 case 'c':
325 req.fGenLASM = true;
326 break;
327 case 'O':
328 if (*(a+2)=='\0') return kUsageError;
329 req.fOutputFile = a+2;
330 break;
331 case 'S':
332 if (*(a+2)=='\0') return kUsageError;
333 gLink.SetSerialPort(a+2);
334 break;
335 case 'T':
336 if (*(a+2)=='\0') return kUsageError;
337 result = SetTarget(a+2);
338 break;
339 case 'x':
340 gLink.SetOmitHeader(true);
341 break;
342 // case 'p':
343 // if (*(a+2)=='\0') return kUsageError;
344 // gLink.SetRCXProgramChunkSize(atoi(a+2));
345 // break;
346 case 'f':
347 if (*(a+2)=='\0') return kUsageError;
348 gLink.SetRCXFirmwareChunkSize(atoi(a+2));
349 break;
350 case 'w':
351 if (*(a+2)=='\0') return kUsageError;
352 gLink.SetDownloadWaitTime(atoi(a+2));
353 break;
354 case 'U':
355 if (*(a+2)=='\0') return kUsageError;
356 Compiler::Get()->Undefine(a+2);
357 break;
358 case 'd':
359 req.fDownload = true;
360 break;
361 case 'l':
362 req.fListing = true;
363 break;
364 case 'n':
365 req.fFlags |= Compiler::kNoSysFile_Flag;
366 break;
367 case 'b':
368 req.fBinary = true;
369 break;
370 case 't':
371 if (!args.Remain()) return kUsageError;
372 gTimeout = args.NextInt();
373 break;
374 case 'v':
375 gVerbose = true;
376 break;
377 case 'q':
378 gQuiet = true;
379 break;
380
381 // actions
382 case kCompileStdinCode:
383 result = ProcessFile(nil, req);
384 fileProcessed = true;
385 break;
386 case kDatalogCode:
387 result = UploadDatalog(false);
388 break;
389 case kDatalogFullCode:
390 result = UploadDatalog(true);
391 break;
392 case kClearMemoryCode:
393 result = ClearMemory();
394 break;
395 case kFirmwareCode:
396 if (!args.Remain()) return kUsageError;
397 result = DownloadFirmware(args.Next(), false);
398 break;
399 case kFirmware4xCode:
400 if (!args.Remain()) return kUsageError;
401 result = DownloadFirmware(args.Next(), true);
402 break;
403 case kNearCode:
404 result = gLink.Send(cmd.Set(kRCX_IRModeOp, 0));
405 break;
406 case kFarCode:
407 result = gLink.Send(cmd.Set(kRCX_IRModeOp, 1));
408 break;
409 case kWatchCode:
410 if (!args.Remain()) return kUsageError;
411 result = SetWatch(args.Next());
412 break;
413 case kSleepCode:
414 if (!args.Remain()) return kUsageError;
415 result = gLink.Send(cmd.Set(kRCX_AutoOffOp, (UByte)args.NextInt()));
416 break;
417 case kRunCode:
418 result = gLink.Send(cmd.Set(kRCX_StartTaskOp, getTarget(gTargetType)->fRanges[kRCX_TaskChunk].fBase));
419 break;
420 case kProgramCode:
421 if (!args.Remain()) return kUsageError;
422 result = gLink.Send(cmd.Set(kRCX_SelectProgramOp, (UByte)(args.NextInt()-1)));
423 break;
424 case kMessageCode:
425 if (!args.Remain()) return kUsageError;
426 result = gLink.Send(cmd.Set(kRCX_Message, (UByte)(args.NextInt())), false);
427 break;
428 case kRawCode:
429 case kRaw1Code: // one-time send, no retry
430 if (!args.Remain()) return kUsageError;
431 result = SendRawCommand(args.Next(), code == kRawCode);
432 break;
433 case kRemoteCode:
434 if (args.Remain() < 2) return kUsageError;
435 {
436 const char *event = args.Next();
437 int repeat = args.NextInt();
438 result = SendRemote(event, repeat);
439 }
440 break;
441
442 case kApiCode:
443 PrintApi(req.fFlags & Compiler::kCompat_Flag);
444 break;
445
446 case kHelpCode:
447 PrintUsage();
448 break;
449 default:
450 return kUsageError;
451 }
452 }
453 else if (!fileProcessed)
454 {
455 result = ProcessFile(a, req);
456
457 #ifdef CHECK_LEAKS
458 int firstAllocCount = gDebugNewAllocCount;
459 Compiler::Get()->Reset();
460 result = ProcessFile(a, req);
461 printf("%d leaked allocations\n", gDebugNewAllocCount - firstAllocCount);
462 #endif
463 optionsOK = false;
464 fileProcessed = true;
465 }
466 else
467 return kUsageError;
468 }
469
470 // check if we did anything (compile and/or actions)
471 if (optionsOK) return kUsageError;
472
473 return result;
474 }
475
476
PrintApi(bool compatMode)477 void PrintApi(bool compatMode)
478 {
479 Buffer *b = Compiler::CreateApiBuffer(compatMode);
480 fwrite(b->GetData(), b->GetLength(), 1, stdout);
481 delete b;
482 }
483
484
SetTarget(const char * name)485 RCX_Result SetTarget(const char *name)
486 {
487 for(unsigned int i=0; i<sizeof(sTargetNames) / sizeof(const char *); ++i)
488 {
489 if (SameString(name, sTargetNames[i]))
490 {
491 gTargetType = (RCX_TargetType)i;
492 return kRCX_OK;
493 }
494 }
495
496 fprintf(STDERR, "Warning: use of abbreviated target names has been deprecated\n");
497
498 switch(name[0])
499 {
500 case 'r':
501 case 'R':
502 gTargetType = kRCX_RCXTarget;
503 break;
504 case 'c':
505 case 'C':
506 gTargetType = kRCX_CMTarget;
507 break;
508 case 's':
509 case 'S':
510 gTargetType = kRCX_ScoutTarget;
511 break;
512 default:
513 return kUsageError;
514 }
515
516 return kRCX_OK;
517 }
518
519
SetWatch(const char * timeSpec)520 RCX_Result SetWatch(const char *timeSpec)
521 {
522 int hour;
523 int minute;
524 RCX_Cmd cmd;
525
526 if (strcmp(timeSpec, "now")==0)
527 {
528 time_t t;
529 struct tm *tmp;
530
531 time(&t);
532 tmp = localtime(&t);
533 hour = tmp->tm_hour;
534 minute = tmp->tm_min;
535 }
536 else
537 {
538 int t = atoi(timeSpec);
539 hour = t / 100;
540 minute = t % 100;
541 }
542
543 return gLink.Send(cmd.Set(kRCX_SetWatchOp, (UByte)hour, (UByte)minute));
544 }
545
546
ProcessFile(const char * sourceFile,const Request & req)547 RCX_Result ProcessFile(const char *sourceFile, const Request &req)
548 {
549 RCX_Image *image;
550 RCX_Result result = kRCX_OK;
551 bool ok = true;
552 bool compiled = false;
553
554 if (sourceFile && (req.fBinary || CheckExtension(sourceFile, kRCXFileExtension)))
555 {
556 // load RCX image file
557 image = new RCX_Image();
558 result = image->Read(sourceFile);
559 if (RCX_ERROR(result))
560 {
561 PrintError(result, sourceFile);
562 delete image;
563 return kQuietError;
564 }
565 }
566 else
567 {
568 // compile file
569 compiled = true;
570 image = Compile(sourceFile, req.fFlags);
571
572 if (!image)
573 {
574 int errors = ErrorHandler::Get()->GetErrorCount();
575
576 if (errors)
577 fprintf(gErrorStream, "# %d error%s during compilation\n", errors, errors==1 ? "" : "s");
578 return kQuietError;
579 }
580
581 const char *outputFile = req.fOutputFile;
582 char *newFilename = 0;
583
584 if (!req.fDownload && !req.fListing && !req.fOutputFile && sourceFile)
585 outputFile = newFilename = CreateFilename(LeafName(sourceFile), kNQCFileExtension, kRCXFileExtension);
586
587 if (outputFile)
588 {
589 if (!image->Write(outputFile))
590 {
591 fprintf(gErrorStream, "Error: could not create output file \"%s\"\n", outputFile);
592 ok = false;
593 }
594 }
595
596 if (newFilename)
597 delete [] newFilename;
598 }
599
600 // generate the listing
601 if (req.fListing)
602 {
603 if (!GenerateListing(image, req.fListFile, compiled && req.fSourceListing, req.fGenLASM))
604 ok = false;
605 }
606
607 // reset the compiler after generating the listing so that the Compiler's
608 // buffers will be available for inserting source code into the listing
609 if (compiled)
610 Compiler::Get()->Reset();
611
612 if (req.fDownload)
613 {
614 result = Download(image);
615 }
616
617 // Check for the case of a listing or output file failure but
618 // download ok. In this case an error code must still be returned
619 // so that command line processing stops and main() indicates
620 // the failure
621 if (result==kRCX_OK && !ok)
622 result = kQuietError;
623
624 delete image;
625 return result;
626 }
627
628
GenerateListing(RCX_Image * image,const char * filename,bool includeSource,bool generateLASM)629 bool GenerateListing(RCX_Image *image, const char *filename, bool includeSource, bool generateLASM)
630 {
631 FILE *fp;
632
633 if (filename)
634 {
635 fp = fopen(filename, "w");
636 if (!fp)
637 {
638 fprintf(STDERR, "Error: could not generate listing to file %s\n", filename);
639 return false;
640 }
641 }
642 else
643 fp = stdout;
644
645 RCX_StdioPrinter dst(fp);
646 image->Print(&dst, includeSource ? Compiler::Get() : 0, generateLASM);
647
648 if (fp != stdout)
649 fclose(fp);
650
651 return true;
652 }
653
654
Download(RCX_Image * image)655 RCX_Result Download(RCX_Image *image)
656 {
657 RCX_Result result;
658 RCX_Cmd cmd;
659
660 fprintf(STDERR, "Downloading Program:");
661 result = gLink.Open();
662 if (result != kRCX_OK) goto ErrorReturn;
663
664 result = image->Download(&gLink);
665 if (result != kRCX_OK) goto ErrorReturn;
666
667 fprintf(STDERR, "complete\n");
668
669 result = gLink.GetBatteryLevel();
670 if (!RCX_ERROR(result))
671 {
672 fprintf(STDERR, "Battery Level = %3.1f V\n", (double)result / 1000);
673 int lowBatt = (gTargetType==kRCX_SpyboticsTarget) ? kLow45Battery : kLowBattery;
674 if (result < lowBatt)
675 fprintf(STDERR, "*** Warning: batteries are low ***\n");
676 }
677
678 return kRCX_OK;
679
680 ErrorReturn:
681 fprintf(STDERR, "error\n");
682 return result;
683 }
684
685
Compile(const char * sourceFile,int flags)686 RCX_Image *Compile(const char *sourceFile, int flags)
687 {
688 Buffer *mainBuf;
689
690 if (sourceFile)
691 {
692 FILE *fp = fopen(sourceFile, "rb");
693 if (!fp)
694 {
695 fprintf(gErrorStream, "Error: could not open file \"%s\"\n", sourceFile);
696 return nil;
697 }
698
699 mainBuf = new Buffer();
700 mainBuf->Create(sourceFile, fp);
701 fclose(fp);
702 }
703 else
704 {
705 mainBuf = new Buffer();
706 mainBuf->Create("<stdin>", stdin);
707 }
708
709 #ifdef TEST_LEXER
710 LexPush(mainBuf);
711
712 int t;
713 TokenVal v;
714 LexLocation loc;
715
716 #if TEST_LEXER > 1
717 while((t=gPreProc->Get(v)) != 0)
718 #else
719 while((t=yylex(v)) != 0)
720 #endif
721 {
722 LexCurrentLocation(loc);
723 printf("%3d (%2d) : ", loc.fOffset, loc.fLength);
724 PrintToken(t, v);
725 }
726
727 LexCurrentLocation(loc);
728 printf("%3d (%2d) : EOF\n", loc.fOffset, loc.fLength);
729
730 return 0;
731 #else
732 Buffer *b = new Buffer();
733 b->Create(mainBuf->GetName(), mainBuf->GetData(), mainBuf->GetLength());
734
735 return Compiler::Get()->Compile(mainBuf, getTarget(gTargetType), flags);
736 #endif
737 }
738
739
UploadDatalog(bool verbose)740 RCX_Result UploadDatalog(bool verbose)
741 {
742 RCX_Log log;
743 RCX_Result result;
744 int i;
745
746 fprintf(STDERR, "Uploading Datalog");
747
748 result = gLink.Open();
749 if (RCX_ERROR(result)) return result;
750
751 result = log.Upload(&gLink);
752 if (RCX_ERROR(result)) return result;
753
754 fprintf(STDERR, "\n");
755
756 for(i=0; i<log.GetLength(); i++)
757 {
758 char line[256];
759 log.SPrintEntry(line, i, verbose);
760 printf("%s\n", line);
761 }
762
763 return kRCX_OK;
764 }
765
766
ClearMemory()767 RCX_Result ClearMemory()
768 {
769 RCX_Result result;
770 RCX_Cmd cmd;
771
772 // halt all tasks
773 result = gLink.Send(cmd.Set(kRCX_StopAllOp));
774 if (RCX_ERROR(result)) return result;
775
776 for(UByte p=0; p<5; p++)
777 {
778 // select and delete program
779 result = gLink.Send(cmd.Set(kRCX_SelectProgramOp, p));
780 if (RCX_ERROR(result)) return result;
781
782 result = gLink.Send(cmd.MakeDeleteSubs());
783 if (RCX_ERROR(result)) return result;
784
785 result = gLink.Send(cmd.MakeDeleteTasks());
786 if (RCX_ERROR(result)) return result;
787 }
788
789 result = gLink.Send(cmd.Set(kRCX_SetDatalogOp, 0, 0));
790 return result;
791 }
792
793
DownloadFirmware(const char * filename,bool fast)794 RCX_Result DownloadFirmware(const char *filename, bool fast)
795 {
796 FILE *fp;
797 SRecord srec;
798 bool ok;
799 RCX_Result result;
800 ULong rom, ram;
801
802 fp = fopen(filename, "rb");
803 if (!fp)
804 {
805 fprintf(STDERR, "Error: could not open file \'%s\'\n", filename);
806 return kQuietError;
807 }
808
809 ok = srec.Read(fp, kMaxFirmware);
810 fclose(fp);
811
812 if (!ok)
813 {
814 fprintf(STDERR, "Error: \'%s\' is not a valid S-Record file\n", filename);
815 return kQuietError;
816 }
817
818 result = gLink.Open();
819 if (RCX_ERROR(result)) return result;
820
821 fprintf(STDERR, "Downloading firmware:");
822 fflush(STDERR);
823 result = gLink.DownloadFirmware(srec.GetData(), srec.GetLength(), srec.GetStart(), fast);
824 fputc('\n', STDERR);
825 if (RCX_ERROR(result)) return result;
826
827 result = gLink.GetVersion(rom, ram);
828 if (RCX_ERROR(result)) return result;
829
830 fprintf(STDERR, "Current Version: %08lx/%08lx\n", rom, ram);
831
832 return result;
833 }
834
835
SendRawCommand(const char * text,bool retry)836 RCX_Result SendRawCommand(const char *text, bool retry)
837 {
838 int length = (int)strlen(text);
839 RCX_Cmd cmd;
840 RCX_Result result;
841
842 // we need an even number of chars in text
843 if (length & 1) return kUsageError;
844
845 // determine actual length of command
846 length /= 2;
847 cmd.SetLength(length);
848
849 for(int i=0; i<length; i++)
850 {
851 int byte = SRecord::ReadHexByte(text);
852 if (byte == -1) return kUsageError;
853 cmd[i] = (UByte) byte;
854 text+=2;
855 }
856
857 result = gLink.Send(&cmd, retry);
858
859 if (result > 0)
860 {
861 for(int i=0; i<result; i++)
862 printf("%02x ", gLink.GetReplyByte(i));
863 printf("\n");
864 }
865
866 return result;
867 }
868
869
SendRemote(const char * event,int repeat)870 RCX_Result SendRemote(const char *event, int repeat)
871 {
872 RCX_Cmd cmd;
873 int low, high;
874
875 if (repeat == 0) return kUsageError;
876
877 if (strlen(event) != 4)
878 return kUsageError;
879
880 high = SRecord::ReadHexByte(event);
881 low = SRecord::ReadHexByte(event+2);
882 if (high==-1 || low==-1) return kUsageError;
883
884
885 cmd.Set(kRCX_Remote, low, high);
886
887 while(repeat--)
888 {
889 gLink.Send(&cmd, false);
890 }
891
892 return kRCX_OK;
893 }
894
895
CreateFilename(const char * source,const char * oldExt,const char * newExt)896 char *CreateFilename(const char *source, const char *oldExt, const char *newExt)
897 {
898 char *filename;
899 size_t n = strlen(source);
900 size_t newExtLen = strlen(newExt);
901
902 if (CheckExtension(source, oldExt))
903 n -= strlen(oldExt);
904
905 filename = new char[n + newExtLen + 1];
906 memcpy(filename, source, n);
907 strcpy(filename + n, newExt);
908
909 return filename;
910 }
911
912
CheckExtension(const char * s1,const char * ext)913 int CheckExtension(const char *s1, const char *ext)
914 {
915 size_t slen = strlen(s1);
916 size_t plen = strlen(ext);
917 unsigned i;
918
919 if (slen < plen) return 0;
920
921 for(i=0; i<plen; i++)
922 if (tolower(s1[slen-plen+i]) != tolower(ext[i])) return 0;
923
924 return 1;
925 }
926
927
LeafName(const char * filename)928 const char *LeafName(const char *filename)
929 {
930 const char *ptr;
931
932 // start at end and work towards beginning
933 ptr = filename + strlen(filename) - 1;
934
935 // test each character (including first one)
936 while(ptr >= filename)
937 {
938 // check for delimiter
939 if (*ptr == DIR_DELIMITER) break;
940
941 #ifdef WIN32
942 // extra check for ':' in paths for Windows
943 if (*ptr == ':') break;
944 #endif
945
946 --ptr;
947 }
948
949 // we either stopped at the char before the first, or at
950 // the delimiter - either way return pointer to next char
951 return ptr+1;
952 }
953
954
SetErrorFile(const char * filename)955 RCX_Result SetErrorFile(const char *filename)
956 {
957 FILE *fp;
958
959 if (*filename==0)
960 {
961 gErrorStream = stdout;
962 return kRCX_OK;
963 }
964
965 fp = fopen(filename, "w");
966 if (!fp)
967 {
968 fprintf(STDERR, "Error: could not open error file \'%s\'\n", filename);
969 return kQuietError;
970 }
971
972 gErrorStream = fp;
973 return kRCX_OK;
974 }
975
976
RedirectOutput(const char * filename)977 RCX_Result RedirectOutput(const char *filename)
978 {
979 if (*filename==0)
980 {
981 fprintf(STDERR, "Error: -R requires a filename\n");
982 return kQuietError;
983 }
984
985 if (!freopen(filename, "w", stdout))
986 {
987 fprintf(STDERR, "Error: could not open output file \'%s\'\n", filename);
988 return kQuietError;
989 }
990
991 return kRCX_OK;
992 }
993
994
DefineMacro(const char * text)995 void DefineMacro(const char *text)
996 {
997 const char *body;
998 body = strchr(text, '=');
999 if (body)
1000 {
1001 // create a copy of the symbol name
1002 int length = body - text;
1003 char *name = new char[length+1];
1004 memcpy(name, text, (size_t)length);
1005 name[length] = 0;
1006
1007 Compiler::Get()->Define(name, body+1);
1008
1009 delete [] name;
1010 }
1011 else
1012 {
1013 Compiler::Get()->Define(text);
1014 }
1015 }
1016
1017
GetActionCode(const char * arg)1018 int GetActionCode(const char *arg)
1019 {
1020 for(int i=0; i< (int)(sizeof(sActionNames)/sizeof(const char *)); ++i)
1021 if (strcmp(sActionNames[i], arg)==0) return i+kFirstActionCode;
1022
1023 return 0;
1024 }
1025
1026
PrintError(RCX_Result error,const char * filename)1027 void PrintError(RCX_Result error, const char *filename)
1028 {
1029 const char *targetName = getTarget(gTargetType)->fName;
1030 if (!filename) filename = "?";
1031
1032 if (error >= 0) return;
1033
1034 switch(error)
1035 {
1036 case kRCX_RequestError:
1037 fprintf(STDERR, "Request error\n");
1038 break;
1039 case kRCX_OpenSerialError:
1040 fprintf(STDERR, "Could not open serial port or USB device\n");
1041 break;
1042 case kRCX_IREchoError:
1043 fprintf(STDERR, "Problem talking to IR device\n");
1044 break;
1045 case kRCX_ReplyError:
1046 if (gLink.WasErrorFromMissingFirmware()) {
1047 fprintf(STDERR, "No firmware installed on %s\n", targetName);
1048 }
1049 else {
1050 fprintf(STDERR, "No reply from %s\n", targetName);
1051 }
1052 break;
1053 case kRCX_MemFullError:
1054 fprintf(STDERR, "Not enough free memory in %s to download program\n", targetName);
1055 break;
1056 case kRCX_FileError:
1057 fprintf(STDERR, "Could not access file \'%s\'\n", filename);
1058 break;
1059 case kRCX_FormatError:
1060 fprintf(STDERR, "File \'%s\' is not a valid RCX image\n", filename);
1061 break;
1062 case kUsageError:
1063 PrintVersion();
1064 fprintf(STDERR,"Usage error: try \'nqc -help\' to display options\n");
1065 break;
1066 case kQuietError:
1067 break;
1068
1069 case kRCX_PipeModeError:
1070 fprintf(STDERR,"USB driver does not support -firmfast, CyberMaster, or Spybotics\n");
1071 break;
1072
1073 case kRCX_USBUnsupportedError:
1074 fprintf(STDERR,"USB Tower not supported\n");
1075 break;
1076
1077 case kRCX_GhostNotFoundError:
1078 fprintf(STDERR,"Ghost libraries are not installed properly\n");
1079 break;
1080 default:
1081 fprintf(STDERR, "Error #%d\n", -error);
1082 break;
1083 }
1084 }
1085
PrintVersion()1086 void PrintVersion()
1087 {
1088 fprintf(STDERR,"nqc version %s\n", VERSION_STRING);
1089 fprintf(STDERR," Copyright (C) 2005 John Hansen. All Rights Reserved.\n");
1090 }
1091
PrintUsage()1092 void PrintUsage()
1093 {
1094 PrintVersion();
1095 fprintf(stdout,"Usage: nqc [options] [actions] [ - | filename ] [actions]\n");
1096 fprintf(stdout," - : read from stdin instead of a source_file\n");
1097 fprintf(stdout,"Options:\n");
1098 fprintf(stdout," -T<target>: target can be RCX, CM, Scout, RCX2, Spy, or Swan\n");
1099 fprintf(stdout," -d: download program\n");
1100 fprintf(stdout," -n: prevent the system file (rcx.nqh) from being included\n");
1101 fprintf(stdout," -b: treat input file as a binary file (don't compile it)\n");
1102 fprintf(stdout," -D<sym>[=<value>] : define macro <sym>\n");
1103 fprintf(stdout," -E[<filename>] : write compiler errors to <filename> (or stdout)\n");
1104 fprintf(stdout," -R<filename> : redirect text output (datalog, etc) to <filename>\n");
1105 fprintf(stdout," -I<path>: search <path> for include files\n");
1106 fprintf(stdout," -L[<filename>] : generate code listing to <filename> (or stdout)\n");
1107 fprintf(stdout," -s: include source code in listings if possible\n");
1108 fprintf(stdout," -c: generate LASM compatible listings\n");
1109 fprintf(stdout," -v: verbose\n");
1110 fprintf(stdout," -q: quiet\n");
1111 fprintf(stdout," -x: omit packet header (RCX only)\n");
1112 fprintf(stdout," -f<size>: set firmware chunk size\n");
1113 fprintf(stdout," -w<ms>: set the download wait timeout\n");
1114 // fprintf(stdout," -p<size>: set program chunk size\n");
1115 // fprintf(stdout," -X<factor>: set USB tower speed factor (<100=faster, >100=slower)\n");
1116 fprintf(stdout," -O<outfile>: specify output file\n");
1117 fprintf(stdout," -S<portname>: specify serial port\n");
1118 fprintf(stdout," -U<sym>: undefine macro <sym>\n");
1119 fprintf(stdout,"Actions:\n");
1120 fprintf(stdout," -run: run current program\n");
1121 fprintf(stdout," -pgm <number>: select program number\n");
1122 fprintf(stdout," -datalog | -datalog_full: upload datalog\n");
1123 fprintf(stdout," -near: set IR to near mode\n");
1124 fprintf(stdout," -far: set IR to far mode\n");
1125 fprintf(stdout," -watch <time> | now: set RCX time\n");
1126 fprintf(stdout," -firmware <filename>: download firmware\n");
1127 fprintf(stdout," -firmfast <filename>: download firmware at quad speed\n");
1128 fprintf(stdout," -sleep <timeout>: set RCX sleep timeout\n");
1129 fprintf(stdout," -msg <number>: send IR message to RCX\n");
1130 fprintf(stdout," -raw <data>: format data as a packet and send to RCX\n");
1131 fprintf(stdout," -remote <value> <repeat>: send a remote command to the RCX\n");
1132 fprintf(stdout," -clear: erase all programs and datalog in RCX\n");
1133 fprintf(stdout," -api: dump the standard api file (e.g. rcx.nqh) to stdout\n");
1134 fprintf(stdout," -help: display command line options\n");
1135 }
1136
1137
Open()1138 RCX_Result AutoLink::Open()
1139 {
1140 RCX_Result result;
1141
1142 if (!fOpen)
1143 {
1144 ULong options = gTimeout & RCX_Link::kRxTimeoutMask;
1145 if (gVerbose) options |= RCX_Link::kVerboseMode;
1146
1147 result = RCX_Link::Open(gTargetType, fSerialPort, options);
1148 if (RCX_ERROR(result)) return result;
1149
1150 fOpen = true;
1151 }
1152 return kRCX_OK;
1153 }
1154
1155
Close()1156 void AutoLink::Close()
1157 {
1158 if (fOpen)
1159 {
1160 RCX_Link::Close();
1161 fOpen = false;
1162 }
1163 }
1164
1165
Send(const RCX_Cmd * cmd,bool retry)1166 RCX_Result AutoLink::Send(const RCX_Cmd *cmd, bool retry)
1167 {
1168 RCX_Result result;
1169
1170 result = Open();
1171 if (RCX_ERROR(result)) return result;
1172
1173 if (retry)
1174 {
1175 result = Sync();
1176 if (RCX_ERROR(result)) return result;
1177 }
1178
1179 result = RCX_Link::Send(cmd, retry);
1180
1181 return retry ? result : kRCX_OK;
1182 }
1183
1184
DownloadProgress(int,int)1185 bool AutoLink::DownloadProgress(int /* soFar */, int /* total */)
1186 {
1187 fputc('.', STDERR);
1188 fflush(STDERR);
1189 return true;
1190 }
1191
1192
1193
AddError(const Error & e,const LexLocation * loc)1194 void MyCompiler::AddError(const Error &e, const LexLocation *loc)
1195 {
1196 // check the error count
1197 if (!e.IsWarning())
1198 {
1199 int errorCount = ErrorHandler::Get()->GetErrorCount();
1200 // only print the first few errors
1201 if (errorCount > kMaxPrintedErrors)
1202 {
1203 if (errorCount == kMaxPrintedErrors+1)
1204 fprintf(gErrorStream, "Too many errors - only first %d reported\n", kMaxPrintedErrors);
1205 return;
1206 }
1207 }
1208
1209 char msg[Error::kMaxErrorMsg];
1210 e.SPrint(msg);
1211
1212 const char *errorType = e.IsWarning() ? "Warning" : "Error";
1213 fprintf(gErrorStream, "# %s: %s\n", errorType, msg);
1214
1215 if (loc && loc->fIndex!=kIllegalSrcIndex)
1216 {
1217 Buffer *b = Compiler::Get()->GetBuffer(loc->fIndex);
1218 int lineStart = loc->fOffset;
1219 int line = b->FindLine(lineStart);
1220 const char *ptr;
1221 int i;
1222
1223 // print file/line info
1224 fprintf(gErrorStream, "File \"%s\" ; line %d\n", b->GetName(), line);
1225
1226 // print the code line
1227 fprintf(gErrorStream, "# ");
1228 for(ptr=b->GetData() + lineStart; *ptr != '\n'; ++ptr)
1229 {
1230 putc((*ptr == '\t') ? ' ' : *ptr, gErrorStream);
1231 }
1232
1233 // mark the token
1234 fprintf(gErrorStream,"\n# ");
1235 for(i=0; i<(loc->fOffset-lineStart); i++)
1236 putc(' ', gErrorStream);
1237 for(i=0; i<loc->fLength; i++)
1238 putc('^', gErrorStream);
1239 fprintf(gErrorStream,"\n");
1240 }
1241
1242 fprintf(gErrorStream, "#----------------------------------------------------------\n");
1243
1244 // this is a hack for Windows!
1245 fflush(gErrorStream);
1246 }
1247
1248
CreateBuffer(const char * name)1249 Buffer *MyCompiler::CreateBuffer(const char *name)
1250 {
1251 static char pathname[DirList::kMaxPathname];
1252
1253 if (!fDirs.Find(name, pathname))
1254 return nil;
1255
1256 Buffer *buf = new Buffer();
1257
1258 if (buf->Create(name, pathname))
1259 return buf;
1260 else
1261 {
1262 delete buf;
1263 return 0;
1264 }
1265 }
1266
1267
1268 #ifdef TEST_LEXER
PrintToken(int t,TokenVal v)1269 void PrintToken(int t, TokenVal v)
1270 {
1271 printf("%3d / ", t);
1272 if (t < 256)
1273 printf("%c", t);
1274 else switch(t)
1275 {
1276 case ID:
1277 printf("%s",v.fSymbol->GetKey());
1278 break;
1279 case NUMBER:
1280 printf("%d", v.fInt);
1281 break;
1282 case IF:
1283 printf("if");
1284 break;
1285 case STRING:
1286 printf("%s", v.fString);
1287 break;
1288 default:
1289 printf("0x%08x", v.fInt);
1290 break;
1291 }
1292
1293 printf("\n");
1294 }
1295 #endif
1296
1297
SameString(const char * s1,const char * s2)1298 bool SameString(const char *s1, const char *s2)
1299 {
1300 char c1, c2;
1301
1302 while(true)
1303 {
1304 c1 = *s1++;
1305 c2 = *s2++;
1306
1307 if (c1==0 && c2==0) return true;
1308
1309 if (tolower(c1) != tolower(c2)) return false;
1310 }
1311 }
1312