/[projet1]/public/pc/tools/osdk/main/bas2tap/sources/bas2tap.cpp
Defence Force logotype

Contents of /public/pc/tools/osdk/main/bas2tap/sources/bas2tap.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1497 - (show annotations)
Sun Oct 14 14:00:30 2018 UTC (12 months, 4 weeks ago) by dbug
File size: 32094 byte(s)
Bas2Tap 2.1:
- The DATA instruction should not be usable with defines
- Correctly filtered out comments at the end of a #define...
1
2 #include "infos.h"
3
4 #include "common.h"
5 #include <map>
6 #include <set>
7 #include <string>
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #ifdef _WIN32
12 #include <conio.h>
13 #else
14 #define memicmp strncasecmp
15 #endif
16 #include <string.h>
17
18 //
19 // Things to do:
20 // - Handle auto-numeration and labels
21 // - Option to optimize the programs (truncate variables to two characters, remove comments)
22 //
23
24 const char *keywords[]=
25 {
26 // 128-246: BASIC keywords
27 "END","EDIT","STORE","RECALL","TRON","TROFF","POP","PLOT",
28 "PULL","LORES","DOKE","REPEAT","UNTIL","FOR","LLIST","LPRINT","NEXT","DATA",
29 "INPUT","DIM","CLS","READ","LET","GOTO","RUN","IF","RESTORE","GOSUB","RETURN",
30 "REM","HIMEM","GRAB","RELEASE","TEXT","HIRES","SHOOT","EXPLODE","ZAP","PING",
31 "SOUND","MUSIC","PLAY","CURSET","CURMOV","DRAW","CIRCLE","PATTERN","FILL",
32 "CHAR","PAPER","INK","STOP","ON","WAIT","CLOAD","CSAVE","DEF","POKE","PRINT",
33 "CONT","LIST","CLEAR","GET","CALL","!","NEW","TAB(","TO","FN","SPC(","@",
34 "AUTO","ELSE","THEN","NOT","STEP","+","-","*","/","^","AND","OR",">","=","<",
35 "SGN","INT","ABS","USR","FRE","POS","HEX$","&","SQR","RND","LN","EXP","COS",
36 "SIN","TAN","ATN","PEEK","DEEK","LOG","LEN","STR$","VAL","ASC","CHR$","PI",
37 "TRUE","FALSE","KEY$","SCRN","POINT","LEFT$","RIGHT$","MID$"
38 // 247- : Error messages
39 };
40
41 enum TokenCodes
42 {
43 Token_END=0,
44 Token_EDIT,
45 Token_STORE,
46 Token_RECALL,
47 Token_TRON,
48 Token_TROFF,
49 Token_POP,
50 Token_PLOT,
51 Token_PULL,
52 Token_LORES,
53 Token_DOKE,
54 Token_REPEAT,
55 Token_UNTIL,
56 Token_FOR,
57 Token_LLIST,
58 Token_LPRINT,
59 Token_NEXT,
60 Token_DATA,
61 Token_INPUT,
62 Token_DIM,
63 Token_CLS,
64 Token_READ,
65 Token_LET,
66 Token_GOTO,
67 Token_RUN,
68 Token_IF,
69 Token_RESTORE,
70 Token_GOSUB,
71 Token_RETURN,
72 Token_REM,
73 Token_HIMEM,
74 Token_GRAB,
75 Token_RELEASE,
76 Token_TEXT,
77 Token_HIRES,
78 Token_SHOOT,
79 Token_EXPLODE,
80 Token_ZAP,
81 Token_PING,
82 Token_SOUND,
83 Token_MUSIC,
84 Token_PLAY,
85 Token_CURSET,
86 Token_CURMOV,
87 Token_DRAW,
88 Token_CIRCLE,
89 Token_PATTERN,
90 Token_FILL,
91 Token_CHAR,
92 Token_PAPER,
93 Token_INK,
94 Token_STOP,
95 Token_ON,
96 Token_WAIT,
97 Token_CLOAD,
98 Token_CSAVE,
99 Token_DEF,
100 Token_POKE,
101 Token_PRINT,
102 Token_CONT,
103 Token_LIST,
104 Token_CLEAR,
105 Token_GET,
106 Token_CALL,
107 Token_SymbolExclamation,
108 Token_NEW,
109 Token_TAB,
110 Token_TO,
111 Token_FN,
112 Token_SPC,
113 Token_SymbolArobase,
114 Token_AUTO,
115 Token_ELSE,
116 Token_THEN,
117 Token_NOT,
118 Token_STEP,
119 Token_SymbolPlus,
120 Token_SymbolMinus,
121 Token_SymbolMultiply,
122 Token_SymbolDivide,
123 Token_SymbolExponent,
124 Token_AND,
125 Token_OR,
126 Token_SymbolMore,
127 Token_SymbolEqual,
128 Token_SymbolLess,
129 Token_SGN,
130 Token_INT,
131 Token_ABS,
132 Token_USR,
133 Token_FRE,
134 Token_POS,
135 Token_HEX,
136 Token_SymbolAmpersand,
137 Token_SQR,
138 Token_RND,
139 Token_LN,
140 Token_EXP,
141 Token_COS,
142 Token_SIN,
143 Token_TAN,
144 Token_ATN,
145 Token_PEEK,
146 Token_DEEK,
147 Token_LOG,
148 Token_LEN,
149 Token_STR,
150 Token_VAL,
151 Token_ASC,
152 Token_CHR,
153 Token_PI,
154 Token_TRUE,
155 Token_FALSE,
156 Token_KEY,
157 Token_SCRN,
158 Token_POINT,
159 Token_LEFT,
160 Token_RIGHT,
161 Token_MID
162 };
163
164
165
166 unsigned char head[14]={ 0x16,0x16,0x16,0x24,0,0,0,0,0,0,5,1,0,0 };
167
168
169
170
171 void Tap2Bas(unsigned char *ptr_buffer,size_t file_size,const char *destFile)
172 {
173 unsigned int i, car;
174
175 FILE *out = stdout;
176 if (destFile && (strlen(destFile) != 0))
177 {
178 out = fopen(destFile, "wb");
179 }
180 if (out == NULL)
181 {
182 printf("Can't open file for writing\n");
183 exit(1);
184 }
185
186
187 if (ptr_buffer[0]!=0x16 || ptr_buffer[3]!=0x24)
188 {
189 ShowError("Not an Oric file");
190 }
191 if (ptr_buffer[6])
192 {
193 ShowError("Not a BASIC file");
194 }
195 i=13;
196 while (ptr_buffer[i++]);
197 while (ptr_buffer[i] || ptr_buffer[i+1])
198 {
199 i+=2;
200 fprintf(out," %u ",ptr_buffer[i]+(ptr_buffer[i+1]<<8));
201 i+=2;
202 while ((car=ptr_buffer[i++]))
203 {
204 if (car<128)
205 fputc(car,out);
206 else
207 if (car < 247)
208 {
209 fprintf(out,"%s", keywords[car - 128]);
210 }
211 else
212 {
213 // Probably corrupted listing
214 // 247 : NEXT WITHOUT FOR
215 fprintf(out,"CORRUPTED_ERROR_CODE_%u", car);
216 }
217 }
218 fputc('\r',out);
219 fputc('\n', out);
220 }
221
222 fclose(out);
223 }
224
225
226
227 // tap2bas
228 int search_keyword(const char *str)
229 {
230 for (unsigned int i=0;i<sizeof(keywords)/sizeof(char *);i++)
231 {
232 if (strncmp(keywords[i],str,strlen(keywords[i]))==0)
233 {
234 return i;
235 }
236 }
237 return -1;
238 }
239
240
241
242 // New routine
243 struct LineData
244 {
245 LineData()
246 {
247 trimmedLine = "";
248 sourceNumber = -1;
249 basicNumber = -1;
250 }
251
252 std::string trimmedLine;
253 int sourceNumber;
254 int basicNumber;
255 };
256
257
258
259
260 char ProcessOptionalWhiteSpace(unsigned char*& bufPtr, const char*& ligne,bool optimize)
261 {
262 char car;
263 while (car = *ligne)
264 {
265 if (car == ' ') // Space
266 {
267 if (!optimize)
268 {
269 *bufPtr++ = car;
270 }
271 ligne++;
272 }
273 else
274 if (car == '\t') // Tab
275 {
276 if (!optimize)
277 {
278 *bufPtr++ = car;
279 }
280 ligne++;
281 }
282 else
283 {
284 //ligne++;
285 return car;
286 }
287 }
288
289 return car;
290 }
291
292
293 bool IsValidLineNumber(char car)
294 {
295 if ((car >= '0') && (car <= '9')) return true;
296 return false;
297 }
298
299 bool IsValidLabelName(char car)
300 {
301 if (((car >= 'a') && (car <= 'z')) ||
302 ((car >= 'A') && (car <= 'Z')) ||
303 (car == '_')) return true;
304 return false;
305 }
306
307
308
309 class BasicTokenizer
310 {
311 public:
312
313
314
315 std::string GetPotentialSymbolName(const char*& ligne)
316 {
317 std::string potentialLabelName;
318 char car;
319 while ((IsValidLabelName(car = *ligne) || IsValidLineNumber(*ligne)) && (search_keyword(ligne)<0))
320 {
321 potentialLabelName += car;
322 ligne++;
323 }
324 return potentialLabelName;
325 }
326
327
328 void ProcessPossibleLineNumber(unsigned char*& bufPtr, const char*& ligne,bool shouldValidateLineNumber, bool optimize)
329 {
330 // Should have one or more (comma separated) numbers, variables, or labels.
331 char car = ProcessOptionalWhiteSpace(bufPtr, ligne,optimize);
332 if (IsValidLineNumber(car))
333 {
334 // Line Number
335 int lineNumber = 0;
336 while (IsValidLineNumber(car = *ligne))
337 {
338 lineNumber = (lineNumber * 10) + (car - '0');
339 *bufPtr++ = car;
340 ligne++;
341 }
342 if (shouldValidateLineNumber && (m_ValidLineNumbers.find(lineNumber) == m_ValidLineNumbers.end()))
343 {
344 ShowError("Can't find line number %d referred by jump instruction in file %s line number line %d", lineNumber, m_CurrentFileName.c_str(), m_CurrentLineNumber);
345 }
346 }
347 else
348 if (IsValidLabelName(car))
349 {
350 // Label Name (or variable)
351 std::string potentialLabelName = GetPotentialSymbolName(ligne);
352 /*
353 std::string potentialLabelName;
354 while ((IsValidLabelName(car = *ligne) || IsValidLineNumber(*ligne)) && (search_keyword(ligne)<0))
355 {
356 potentialLabelName += car;
357 ligne++;
358 }
359 */
360
361 if (!potentialLabelName.empty())
362 {
363 std::string valueToWrite;
364
365 auto findIt = m_Labels.find(potentialLabelName);
366 if (findIt != m_Labels.end())
367 {
368 // Found the label \o/
369 // We replace the value by the stringified line number
370 int lineNumber = findIt->second;
371 bufPtr += sprintf((char*)bufPtr, "%d", lineNumber);
372 }
373 else
374 {
375 // Did not find the label...
376 // ...maybe it's a define?
377 auto findIt = m_Defines.find(potentialLabelName);
378 if (findIt != m_Defines.end())
379 {
380 // Found a matching define \o/
381 // We replace the value by the actual value
382 std::string defineValue = findIt->second;
383 bufPtr += sprintf((char*)bufPtr, "%s", defineValue.c_str());
384 }
385 else
386 {
387 // Not a define either... probably a variable then...?
388 // Just write it "as is"
389 bufPtr += sprintf((char*)bufPtr, "%s", potentialLabelName.c_str());
390 }
391 }
392 }
393 }
394 }
395
396
397
398 void Bas2Tap(const char *sourceFile, const char *destFile, bool autoRun, bool useColor, bool optimize)
399 {
400 unsigned char buf[48192];
401 unsigned int end, adr;
402
403 bool useExtendedBasic = false;
404
405
406 // Mike: Need to improve the parsing of this with a global function to split
407 // a text file in separate lines.
408 std::vector<std::string> textData;
409 if (!LoadText(sourceFile, textData))
410 {
411 ShowError("Unable to load source file");
412 }
413
414 {
415 //
416 // First pass: Get the labels and line numbers
417 //
418 int lastLineNumber = 0;
419 int incrementStep = 5;
420
421
422 m_CurrentFileName = sourceFile;
423 std::string labelName = "";
424 LineData lineData;
425 lineData.sourceNumber = 0;
426 std::vector<std::string>::const_iterator lineIt = textData.begin();
427 while (lineIt != textData.end())
428 {
429 const std::string& currentLine(*lineIt);
430 ++lineIt;
431 lineData.sourceNumber++;
432 if (!currentLine.empty())
433 {
434 char firstCar = currentLine[0];
435 bool startsByWhiteSpace = (firstCar == ' ') || (firstCar == '\t');
436
437 bool shouldSkip = false;
438
439 lineData.trimmedLine = StringTrim(currentLine, " \t\f\v\n\r\xEF\xBB\xBF\xFF\xFE");
440
441 if (!lineData.trimmedLine.empty())
442 {
443 const char* ligne = lineData.trimmedLine.c_str();
444 if (ligne[0] == '#')
445 {
446 // Preprocessor directive
447 if (memicmp(ligne, "#file", 5) == 0)
448 {
449 //"#file font.BAS""
450 // Very approximative "get the name of the file and reset the line counter" code.
451 // Will clean up that when I will have some more time.
452 ligne += 5;
453 m_CurrentFileName = ligne;
454 lineData.sourceNumber = 0;
455 }
456 else
457 if (memicmp(ligne, "#labels", 7) == 0)
458 {
459 //"#labels"
460 useExtendedBasic = true;
461 shouldSkip = true;
462 }
463 else
464 if (memicmp(ligne, "#optimize", 7) == 0)
465 {
466 //"#optimize"
467 optimize = true;
468 shouldSkip = true;
469 }
470 else
471 if (memicmp(ligne, "#define", 7) == 0)
472 {
473 //"#define DEFINE_NAME REPLACEMENT_VALUE"
474 ligne += 7;
475 std::string line(StringTrim(ligne));
476 std::string defineName = StringTrim(StringSplit(line, " \t"));
477 std::string defineValue = StringTrim(StringSplit(line, "' \t"));
478
479 const char* ptrDefineName = defineName.c_str();
480 std::string potentialUsableName = GetPotentialSymbolName(ptrDefineName);
481 if (potentialUsableName != defineName)
482 {
483 int keyw = search_keyword(ptrDefineName);
484 if (keyw >= 0)
485 {
486 ShowError("Define named '%s' in file %s line number line %d contains the name of a BASIC instruction '%s'", defineName.c_str(), m_CurrentFileName.c_str(), lineData.sourceNumber, keywords[keyw]);
487 }
488 }
489
490
491 m_Defines[defineName] = defineValue;
492
493 shouldSkip = true;
494 }
495 else
496 {
497 ShowError("Unknown preprocessor directive in file %s line number line %d", m_CurrentFileName.c_str(), lineData.sourceNumber);
498 }
499 }
500 else
501 {
502 // Standard line
503 int number = get_value(ligne, -1);
504 if (number<0)
505 {
506 char car = ligne[0];
507 if (car != 0)
508 {
509 char car2 = ligne[1];
510 if ((car == '\'') || (car == ';') || ((car == '/') && (car2 == '/')))
511 {
512 // We accept the usual C, Assembler and BASIC comments are actual comments that do not count as normal lines
513 // Technically we could have used a decent pre-processor, or even a full file filter, but I'm aiming at "more bangs for the bucks" approach.
514 // If necessary we can refactor later
515 continue;
516 }
517 }
518
519 // Mike: Need to add better diagnostic here
520 if (useExtendedBasic)
521 {
522 if (startsByWhiteSpace)
523 {
524 // Normal line, missing a line number, we generate one
525 number = lastLineNumber + incrementStep;
526 }
527 else
528 {
529 // Possibly a label
530 std::string line(ligne);
531 labelName = StringTrim(StringSplit(line, ": \t"));
532 if (labelName.empty())
533 {
534 // Not a label, so maybe a line of basic without line number
535 ShowError("Missing label information in file %s line %d", m_CurrentFileName.c_str(), lineData.sourceNumber);
536 break;
537 }
538 else
539 {
540 // Definitely a label, or something unfortunately detected as a label because of the ":" at the end :p
541 if (m_Labels.find(labelName) != m_Labels.end())
542 {
543 ShowError("Label '%s' found in file %s line %d is already defined", labelName.c_str(), m_CurrentFileName.c_str(), lineData.sourceNumber);
544 break;
545 }
546
547 bool hasSetIncrement = false;
548 bool hasSetNumber = false;
549
550 while (!line.empty())
551 {
552 std::string lineOrIncrement = StringTrim(StringSplit(line, ": \t"));
553 if (!lineOrIncrement.empty())
554 {
555 char car = lineOrIncrement[0];
556 char car2 = (lineOrIncrement.size()>=2)?lineOrIncrement[1]:0;
557 if ( (car == '\'') || (car == ';') || ((car == '/') && (car2 == '/')) )
558 {
559 // Comment
560 break;
561 }
562 else
563 if (car == '+')
564 {
565 // Increment
566 if (hasSetIncrement)
567 {
568 ShowError("Line increment value for label '%s' found in file %s line %d was already set to %d", labelName.c_str(), m_CurrentFileName.c_str(), lineData.sourceNumber, incrementStep);
569 }
570 lineOrIncrement = lineOrIncrement.substr(1);
571 incrementStep = std::stoi(lineOrIncrement);
572 hasSetIncrement = true;
573 }
574 else
575 {
576 // Line number
577 if (hasSetNumber)
578 {
579 ShowError("Line number value for label '%s' found in file %s line %d was already set to %d", labelName.c_str(), m_CurrentFileName.c_str(), lineData.sourceNumber, lastLineNumber);
580 }
581 char* endPtr = nullptr;
582 const char* startPtr = lineOrIncrement.c_str();
583 lastLineNumber = std::strtol(startPtr,&endPtr,10);
584 if (startPtr == endPtr)
585 {
586 ShowError("Invalid line number value '%s' for label '%s' found in file %s line %d", startPtr, labelName.c_str(), m_CurrentFileName.c_str(), lineData.sourceNumber);
587 }
588
589 m_Labels[labelName] = lastLineNumber;
590 hasSetNumber = true;
591 }
592 }
593 }
594
595 if (!hasSetNumber)
596 {
597 m_Labels[labelName] = lastLineNumber + incrementStep;
598 }
599 shouldSkip = true;
600 }
601 }
602 }
603 else
604 {
605 ShowError("Missing line number in file %s line %d", m_CurrentFileName.c_str(), lineData.sourceNumber);
606 break;
607 }
608 }
609 else
610 {
611 // We have a valid line number, if we have a pending label, record it
612 if (!labelName.empty())
613 {
614 m_Labels[labelName] = number;
615 labelName.clear();
616 }
617 lineData.trimmedLine = StringTrim(ligne);
618 }
619 if (number >= 0)
620 {
621 lineData.basicNumber = number;
622 lastLineNumber = number;
623 m_ValidLineNumbers.insert(number);
624 }
625 }
626 if (!shouldSkip)
627 {
628 // No need to add labels as actual lines to parse
629 m_ActualLines.push_back(lineData);
630 }
631 }
632 }
633 }
634 }
635
636 unsigned char* bufPtr = buf;
637 m_CurrentFileName = sourceFile;
638
639 {
640 //
641 // Second pass: Solve the labels
642 //
643 int previousLineNumber = -1;
644
645 std::vector<LineData>::const_iterator lineIt = m_ActualLines.begin();
646 while (lineIt != m_ActualLines.end())
647 {
648 const LineData& lineData(*lineIt);
649 std::string currentLine = lineData.trimmedLine;
650 m_CurrentLineNumber = lineData.sourceNumber;
651
652 if (lineData.basicNumber < previousLineNumber)
653 {
654 ShowError("BASIC line number %d in file %s line number line %d is smaller than the previous line %d", lineData.basicNumber, m_CurrentFileName.c_str(), m_CurrentLineNumber, previousLineNumber);
655 }
656 previousLineNumber = lineData.basicNumber;
657
658 if (!currentLine.empty())
659 {
660 const char* ligne = currentLine.c_str();
661 if (ligne[0] == '#')
662 {
663 // Preprocessor directive
664 if (memicmp(ligne, "#file", 5) == 0)
665 {
666 //"#file font.BAS""
667 // Very approximative "get the name of the file and reset the line counter" code.
668 // Will clean up that when I will have some more time.
669 ligne += 5;
670 m_CurrentFileName = ligne;
671 m_CurrentLineNumber = 0;
672 }
673 else
674 {
675 ShowError("Unknown preprocessor directive in file %s line number line %d", m_CurrentFileName.c_str(), m_CurrentLineNumber);
676 }
677 }
678 else
679 {
680 // Standard line
681 unsigned char* lineStart = bufPtr;
682
683 *bufPtr++ = 0;
684 *bufPtr++ = 0;
685
686 *bufPtr++ = lineData.basicNumber & 0xFF;
687 *bufPtr++ = lineData.basicNumber >> 8;
688
689 bool color = useColor;
690 bool isComment = false;
691 bool isQuotedString = false;
692 bool isData = false;
693
694
695 while (*ligne == ' ') ligne++;
696
697
698 while (*ligne)
699 {
700 unsigned char car = *ligne;
701 unsigned char car2 = *(ligne + 1);
702
703 if (isComment)
704 {
705 char value = *ligne++;
706 if (!optimize)
707 {
708 if (color)
709 {
710 color = false;
711 *bufPtr++ = 27; // ESCAPE
712 *bufPtr++ = 'B'; // GREEN labels
713 }
714 *bufPtr++ = value;
715 }
716 }
717 else
718 if (isQuotedString)
719 {
720 if (car == '"')
721 {
722 isQuotedString = false;
723 }
724 if (car == '~')
725 {
726 // Special control code
727 if ( (car2>=96) && (car2 <= 'z') ) // 96=arobase ('a'-1)
728 {
729 *bufPtr++ = car2-96;
730 ligne+=2;
731 }
732 else
733 if ((car2 >= '@') && (car2 <= 'Z'))
734 {
735 *bufPtr++ = 27; // ESCAPE
736 *bufPtr++ = car2; // Actual control code
737 ligne+=2;
738 }
739 else
740 {
741 ShowError("The sequence '~%c' in file %s line number line %d is not a valid escape sequence ", car2, m_CurrentFileName.c_str(), m_CurrentLineNumber);
742 }
743
744 }
745 else
746 {
747 *bufPtr++ = *ligne++;
748 }
749 }
750 else
751 if (isData)
752 {
753 // Data is a very special system where nothing is tokenized, so you can have FOR or THEN, they will be interpreted as normal strings
754 if (car == ':')
755 {
756 isData = false;
757 }
758 else
759 if (car == '"')
760 {
761 isQuotedString = true;
762 }
763 else
764 {
765 auto previousPtr = bufPtr;
766 ProcessPossibleLineNumber(bufPtr, ligne, false, optimize);
767 ProcessOptionalWhiteSpace(bufPtr, ligne, optimize);
768 if (previousPtr != bufPtr)
769 {
770 continue;
771 }
772 }
773 *bufPtr++ = *ligne++;
774 }
775 else
776 {
777 ProcessOptionalWhiteSpace(bufPtr, ligne, optimize);
778
779 int keyw = search_keyword(ligne);
780 if (keyw == Token_REM || (*ligne == '\''))
781 {
782 // REM
783 isComment = true;
784 if (optimize)
785 {
786 continue;
787 }
788 }
789 else
790 if (keyw == Token_DATA)
791 {
792 // DATA
793 isData = true;
794 }
795
796 car = *ligne;
797 car2 = *(ligne+1);
798
799 if (car == '"')
800 {
801 isQuotedString = true;
802 }
803 else
804 if ( (car == 0xA7) || ((car == 0xC2) && (car2 == 0xA7)) )
805 {
806 //
807 // Special 'ยง' symbol that get replaced by the current line number.
808 // Appears in encodings as either "C2 A7" or "A7"
809 //
810 bufPtr += sprintf((char*)bufPtr, "%d", lineData.basicNumber);
811 ++ligne;
812 if (car == 0xC2)
813 {
814 // Need to skip two characters...
815 ++ligne;
816 }
817 continue;
818 }
819 else
820 if (car == '(')
821 {
822 *bufPtr++ = *ligne++; // Open parenthesis
823 ProcessPossibleLineNumber(bufPtr, ligne, false, optimize);
824 continue;
825 }
826 else
827 if (car == ',')
828 {
829 *bufPtr++ = *ligne++; // comma
830 ProcessPossibleLineNumber(bufPtr, ligne, false, optimize);
831 continue;
832 }
833
834 if (keyw >= 0)
835 {
836 *bufPtr++ = keyw | 128;
837 ligne += strlen(keywords[keyw]);
838 ProcessOptionalWhiteSpace(bufPtr, ligne, optimize);
839
840 //
841 // Note: This bunch of tests should be replaced by actual flags associated to keywords to define their behavior:
842 // - Can be followed by a line number
843 // - Can be the complementary part of an expression (and thus should not be part of a symbol)
844 // - ...
845 //
846 if (useExtendedBasic &&
847 ((keyw == Token_GOTO)
848 || (keyw == Token_GOSUB)
849 || (keyw == Token_RESTORE)
850 || (keyw == Token_SymbolEqual)
851 || (keyw == Token_SymbolMinus)
852 || (keyw == Token_SymbolPlus)
853 || (keyw == Token_SymbolDivide)
854 || (keyw == Token_SymbolPlus)
855 || (keyw == Token_TO)
856 || (keyw == Token_THEN)
857 || (keyw == Token_ELSE)))
858 {
859 if ((keyw == Token_THEN) || (keyw == Token_ELSE))
860 {
861 if (search_keyword(ligne) >= 0)
862 {
863 // THEN and ELSE instructions can be followed directly by a line number... but they can also have an instruction like PRINT
864 ProcessOptionalWhiteSpace(bufPtr, ligne, optimize);
865 continue;
866 }
867 }
868 // Should have one or more (comma separated) numbers, variables, or labels.
869 bool shouldValidateLineNumber = ! ( (keyw == Token_SymbolEqual) || (keyw == Token_SymbolMinus) || (keyw == Token_SymbolPlus) || (keyw == Token_SymbolMultiply) || (keyw == Token_SymbolDivide));
870 ProcessPossibleLineNumber(bufPtr, ligne, shouldValidateLineNumber,optimize);
871 ProcessOptionalWhiteSpace(bufPtr, ligne, optimize);
872 }
873 }
874 else
875 {
876 *bufPtr++ = *ligne++;
877 }
878 }
879 }
880
881 if (optimize)
882 {
883 // Remove any white space at the end of the line
884 while (((bufPtr-1) > (lineStart+4)) && (bufPtr[-1] == ' '))
885 {
886 --bufPtr;
887 }
888 }
889 if (bufPtr == lineStart + 4)
890 {
891 // If the line is empty, we add a REM token...
892 *bufPtr++ = Token_REM | 128;
893 }
894
895 *bufPtr++ = 0;
896
897 adr = 0x501 + bufPtr-buf;
898
899 *lineStart++ = adr & 0xFF;
900 *lineStart++ = adr >> 8;
901
902 }
903 }
904 ++lineIt;
905 }
906 *bufPtr++ = 0;
907 *bufPtr++ = 0;
908 }
909
910 //following line modified by Wilfrid AVRILLON (Waskol) 06/20/2009
911 //It should follow this rule of computation : End_Address=Start_Address+File_Size-1
912 //Let's assume a 1 byte program, it starts at address #501 and ends at address #501 (Address=Address+1-1) !
913 //It was a blocking issue for various utilities (tap2wav for instance)
914 //end=0x501+i-1; //end=0x501+i;
915 int i = bufPtr - buf;
916 end = 0x501 + i;
917
918 if (autoRun) head[7] = 0x80; // Autorun for basic :)
919 else head[7] = 0;
920
921 head[8] = end >> 8;
922 head[9] = end & 0xFF;
923
924 //
925 // Save file
926 //
927 FILE *out = fopen(destFile, "wb");
928 if (out == NULL)
929 {
930 printf("Can't open file for writing\n");
931 exit(1);
932 }
933 fwrite(head, 1, 13, out);
934 // write the name
935 if (m_CurrentFileName.length() > 0)
936 {
937 char *currentFileDup = strdup(m_CurrentFileName.c_str());
938 char *fileName = currentFileDup;
939 // only take the file name from the path
940 // try to find '\\'
941 char *lastsep = strrchr(fileName, '\\');
942 if (lastsep != NULL)
943 {
944 // if there is something after the separator
945 if (lastsep + 1 != 0)
946 fileName = lastsep + 1;
947 }
948 else
949 {
950 // try to find /
951 lastsep = strrchr(fileName, '/');
952 if (lastsep != NULL)
953 {
954 // if there is something after the separator
955 if (lastsep + 1 != 0)
956 fileName = lastsep + 1;
957 }
958 }
959 // remove the extension if there is one
960 char *lastdot = strrchr(fileName, '.');
961 if (lastdot != NULL)
962 *lastdot = 0;
963 fwrite(fileName, 1, strlen(fileName), out);
964 free(currentFileDup);
965 }
966 fwrite("\x00", 1, 1, out);
967 fwrite(buf, 1, i + 1, out);
968 // oricutron bug work around
969 //fwrite("\x00", 1, 1, out);
970 fclose(out);
971 }
972
973
974
975 public:
976 std::map<std::string, int> m_Labels; // Name to resolved line number
977 std::set<int> m_ValidLineNumbers; // Useful to know if a GOTO or GOSUB refers to an invalid line number
978
979 std::map<std::string,std::string> m_Defines; // Very primitive macro expansion system (only support direct replacement at the moment)
980
981 std::vector<LineData> m_ActualLines;
982
983 std::string m_CurrentFileName;
984 int m_CurrentLineNumber;
985 };
986
987
988
989 #define NB_ARG 2
990
991 int main(int argc, char **argv)
992 {
993 //
994 // Some initialization for the common library
995 //
996 SetApplicationParameters(
997 "Bas2Tap",
998 TOOL_VERSION_MAJOR,
999 TOOL_VERSION_MINOR,
1000 "{ApplicationName} - Version {ApplicationVersion} - This program is a part of the OSDK\r\n"
1001 "\r\n"
1002 "Authors:\r\n"
1003 " Fabrice Frances (original version) \r\n"
1004 " Mickael Pointier (extended BASIC features) \r\n"
1005 "\r\n"
1006 "Purpose:\r\n"
1007 " Converting a text file containing a BASIC source code to a binary\r\n"
1008 " encoded TAPE file that can be loaded using the CLOAD command.\r\n"
1009 " (and the opposite operation as well).\r\n"
1010 "\r\n"
1011 "Parameters:\r\n"
1012 " <options> <sourcefile> <destinationfile>\r\n"
1013 "\r\n"
1014 "Options:\r\n"
1015 " -b2t[0|1] for converting to tape format with autorun (1) or not (0)\r\n"
1016 " -t2b for converting from tape format text\r\n"
1017 " -color[0|1] for enabling colored comments\r\n"
1018 " -optimize[0|1] for allowing for optimizations (disabling comments, etc...)\r\n"
1019 "\r\n"
1020 "Source Commands:\r\n"
1021 " #labels to enable labels and auto numbering\r\n"
1022 " #optimize to enable optimization\r\n"
1023 " #defines to define symbols\r\n"
1024 " 'tilde' to escape sequences and 'paragraph' to insert current line number\r\n"
1025 "\r\n"
1026 "Example:\r\n"
1027 " {ApplicationName} -b2t1 final.txt osdk.tap\r\n"
1028 " {ApplicationName} -t2b osdk.tap program.txt\r\n"
1029 );
1030
1031 bool basicToTape=true;
1032 bool autoRun=true;
1033 bool useColor=false;
1034 bool optimize = false;
1035
1036 ArgumentParser argumentParser(argc,argv);
1037
1038 while (argumentParser.ProcessNextArgument())
1039 {
1040 if (argumentParser.IsSwitch("-t2b"))
1041 {
1042 // Tape to BASIC source code
1043 basicToTape=false;
1044 }
1045 else
1046 if (argumentParser.IsSwitch("-b2t"))
1047 {
1048 // BASIC source code to tape
1049 basicToTape=true;
1050 autoRun=argumentParser.GetBooleanValue(true);
1051 }
1052 else
1053 if (argumentParser.IsSwitch("-color"))
1054 {
1055 // Handling of color codes
1056 useColor=argumentParser.GetBooleanValue(false);
1057 }
1058 else
1059 if (argumentParser.IsSwitch("-optimize"))
1060 {
1061 // Handling of optimization (disables color if enabled)
1062 optimize =argumentParser.GetBooleanValue(false);
1063 }
1064 }
1065
1066 if (argumentParser.GetParameterCount()!=NB_ARG)
1067 {
1068 ShowError(0);
1069 }
1070
1071
1072 std::string nameSrc(argumentParser.GetParameter(0));
1073 std::string nameDst(argumentParser.GetParameter(1));
1074
1075 if (basicToTape)
1076 {
1077 BasicTokenizer tokenizer;
1078 tokenizer.Bas2Tap(nameSrc.c_str(), nameDst.c_str(), autoRun, useColor, optimize);
1079 }
1080 else
1081 {
1082 // Load the source file
1083 void* ptr_buffer_void;
1084 size_t file_size;
1085 if (!LoadFile(nameSrc.c_str(),ptr_buffer_void,file_size))
1086 {
1087 ShowError("Unable to load the source file");
1088 }
1089 unsigned char *ptr_buffer=(unsigned char*)ptr_buffer_void;
1090
1091 Tap2Bas(ptr_buffer,file_size,nameDst.c_str());
1092 }
1093
1094 exit(0);
1095 }

  ViewVC Help
Powered by ViewVC 1.1.26