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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1531 - (hide annotations)
Thu Feb 20 17:39:55 2020 UTC (5 days ago) by dbug
File size: 32116 byte(s)


1 dbug 70
2     #include "infos.h"
3    
4     #include "common.h"
5 dbug 1489 #include <map>
6     #include <set>
7     #include <string>
8 dbug 70
9     #include <stdlib.h>
10     #include <stdio.h>
11 Jylam 1125 #ifdef _WIN32
12 dbug 70 #include <conio.h>
13 Jylam 1125 #else
14     #define memicmp strncasecmp
15     #endif
16 dbug 70 #include <string.h>
17    
18 dbug 1119 //
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 dbug 70
24 Jylam 1175 const char *keywords[]=
25 Jylam 1125 {
26 dbug 1447 // 128-246: BASIC keywords
27 dbug 1119 "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 dbug 1447 // 247- : Error messages
39 dbug 70 };
40    
41 dbug 1489 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 dbug 70 unsigned char head[14]={ 0x16,0x16,0x16,0x24,0,0,0,0,0,0,5,1,0,0 };
167    
168    
169    
170    
171 dbug 1447 void Tap2Bas(unsigned char *ptr_buffer,size_t file_size,const char *destFile)
172 dbug 70 {
173 dbug 1119 unsigned int i, car;
174 dbug 70
175 dbug 1447 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 Jylam 1125 if (ptr_buffer[0]!=0x16 || ptr_buffer[3]!=0x24)
188     {
189 dbug 1119 ShowError("Not an Oric file");
190     }
191 Jylam 1125 if (ptr_buffer[6])
192     {
193     ShowError("Not a BASIC file");
194 dbug 1119 }
195 Jylam 1125 i=13;
196 dbug 1119 while (ptr_buffer[i++]);
197 Jylam 1125 while (ptr_buffer[i] || ptr_buffer[i+1])
198 dbug 1119 {
199     i+=2;
200 dbug 1447 fprintf(out," %u ",ptr_buffer[i]+(ptr_buffer[i+1]<<8));
201 dbug 1119 i+=2;
202 Jylam 1175 while ((car=ptr_buffer[i++]))
203 dbug 1119 {
204 Jylam 1125 if (car<128)
205 dbug 1447 fputc(car,out);
206 Jylam 1125 else
207 dbug 1447 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 dbug 1119 }
218 dbug 1447 fputc('\r',out);
219     fputc('\n', out);
220 dbug 1119 }
221 dbug 1447
222     fclose(out);
223 dbug 70 }
224    
225    
226    
227     // tap2bas
228     int search_keyword(const char *str)
229     {
230 Jylam 1175 for (unsigned int i=0;i<sizeof(keywords)/sizeof(char *);i++)
231 dbug 1119 {
232     if (strncmp(keywords[i],str,strlen(keywords[i]))==0)
233     {
234     return i;
235     }
236     }
237     return -1;
238 dbug 70 }
239    
240    
241    
242 dbug 1489 // New routine
243     struct LineData
244 dbug 70 {
245 dbug 1489 LineData()
246     {
247     trimmedLine = "";
248     sourceNumber = -1;
249     basicNumber = -1;
250     }
251 dbug 70
252 dbug 1489 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 dbug 1119 {
265 dbug 1489 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 dbug 1119 }
288 dbug 70
289 dbug 1489 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 dbug 1119 {
321 dbug 1489 potentialLabelName += car;
322     ligne++;
323     }
324     return potentialLabelName;
325     }
326 dbug 70
327 dbug 1489
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 dbug 1119 {
338 dbug 1489 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 dbug 1119 {
368 dbug 1489 // 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 dbug 1119 {
380 dbug 1489 // 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 dbug 1119 }
385     else
386     {
387 dbug 1489 // Not a define either... probably a variable then...?
388     // Just write it "as is"
389     bufPtr += sprintf((char*)bufPtr, "%s", potentialLabelName.c_str());
390 dbug 1119 }
391     }
392 dbug 1489 }
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 dbug 1119 {
434 dbug 1489 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 dbug 1119 {
443 dbug 1489 const char* ligne = lineData.trimmedLine.c_str();
444     if (ligne[0] == '#')
445 dbug 1480 {
446 dbug 1489 // Preprocessor directive
447     if (memicmp(ligne, "#file", 5) == 0)
448 dbug 1480 {
449 dbug 1489 //"#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 dbug 1480 }
456 dbug 1489 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 dbug 1497 std::string defineName = StringTrim(StringSplit(line, " \t"));
477     std::string defineValue = StringTrim(StringSplit(line, "' \t"));
478 dbug 1480
479 dbug 1489 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 dbug 1480
490    
491 dbug 1489 m_Defines[defineName] = defineValue;
492 dbug 70
493 dbug 1489 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 dbug 1119 {
502 dbug 1489 // Standard line
503     int number = get_value(ligne, -1);
504     if (number<0)
505 dbug 1119 {
506 dbug 1489 char car = ligne[0];
507     if (car != 0)
508 dbug 1481 {
509 dbug 1489 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 dbug 1481 }
518 dbug 1489
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 dbug 1119 }
609 dbug 1489 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 Jylam 1125 }
626 dbug 1489 if (!shouldSkip)
627 dbug 1119 {
628 dbug 1489 // No need to add labels as actual lines to parse
629     m_ActualLines.push_back(lineData);
630 Jylam 1125 }
631 dbug 1489 }
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 dbug 1119 {
666 dbug 1489 //"#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 Jylam 1125 }
673     else
674 dbug 1119 {
675 dbug 1489 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 dbug 1119 {
705 dbug 1489 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 Jylam 1125 }
717     else
718 dbug 1489 if (isQuotedString)
719 dbug 1119 {
720 dbug 1489 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 dbug 1119 }
750 dbug 1489 else
751     if (isData)
752     {
753 dbug 1497 // 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 dbug 1489 if (car == ':')
755     {
756     isData = false;
757     }
758 dbug 1497 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 dbug 1489 *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 dbug 1497 || (keyw == Token_TO)
856 dbug 1489 || (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 dbug 1119 }
880 dbug 1489
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 dbug 1119 }
903     }
904 dbug 1489 ++lineIt;
905 dbug 1119 }
906 dbug 1489 *bufPtr++ = 0;
907     *bufPtr++ = 0;
908 dbug 1119 }
909 dbug 70
910 dbug 1119 //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 Jylam 1125 //Let's assume a 1 byte program, it starts at address #501 and ends at address #501 (Address=Address+1-1) !
913 dbug 1119 //It was a blocking issue for various utilities (tap2wav for instance)
914     //end=0x501+i-1; //end=0x501+i;
915 dbug 1481 int i = bufPtr - buf;
916 dbug 1489 end = 0x501 + i;
917 dbug 208
918 dbug 1489 if (autoRun) head[7] = 0x80; // Autorun for basic :)
919     else head[7] = 0;
920 dbug 70
921 dbug 1489 head[8] = end >> 8;
922     head[9] = end & 0xFF;
923 dbug 70
924 dbug 1119 //
925     // Save file
926     //
927 dbug 1489 FILE *out = fopen(destFile, "wb");
928     if (out == NULL)
929 Jylam 1125 {
930     printf("Can't open file for writing\n");
931     exit(1);
932 dbug 1119 }
933 dbug 1489 fwrite(head, 1, 13, out);
934 Hialmar 1127 // write the name
935 dbug 1489 if (m_CurrentFileName.length() > 0)
936 dbug 1307 {
937 dbug 1489 char *currentFileDup = strdup(m_CurrentFileName.c_str());
938 dbug 1307 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 dbug 1489 else
949 dbug 1307 {
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 Hialmar 1127 }
966     fwrite("\x00", 1, 1, out);
967 dbug 1489 fwrite(buf, 1, i + 1, out);
968 Hialmar 1127 // oricutron bug work around
969     //fwrite("\x00", 1, 1, out);
970 dbug 1119 fclose(out);
971 dbug 70 }
972    
973    
974    
975 dbug 1489 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 dbug 70
979 dbug 1489 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 dbug 70 #define NB_ARG 2
990    
991 Jylam 1125 int main(int argc, char **argv)
992 dbug 70 {
993 dbug 1119 //
994     // Some initialization for the common library
995     //
996     SetApplicationParameters(
997     "Bas2Tap",
998     TOOL_VERSION_MAJOR,
999     TOOL_VERSION_MINOR,
1000 dbug 1531 "{ApplicationName} - Version {ApplicationVersion} - This program is a part of the OSDK (http://www.osdk.org)\r\n"
1001 dbug 1119 "\r\n"
1002 dbug 1489 "Authors:\r\n"
1003     " Fabrice Frances (original version) \r\n"
1004     " Mickael Pointier (extended BASIC features) \r\n"
1005 dbug 1119 "\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 Jylam 1125 "Parameters:\r\n"
1012 dbug 1119 " <options> <sourcefile> <destinationfile>\r\n"
1013     "\r\n"
1014 Jylam 1125 "Options:\r\n"
1015 dbug 1119 " -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 dbug 1489 " -color[0|1] for enabling colored comments\r\n"
1018     " -optimize[0|1] for allowing for optimizations (disabling comments, etc...)\r\n"
1019 dbug 1481 "\r\n"
1020 dbug 1489 "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 Jylam 1128 "Example:\r\n"
1027 dbug 1119 " {ApplicationName} -b2t1 final.txt osdk.tap\r\n"
1028     " {ApplicationName} -t2b osdk.tap program.txt\r\n"
1029     );
1030 dbug 70
1031 dbug 1119 bool basicToTape=true;
1032 Jylam 1125 bool autoRun=true;
1033 dbug 1119 bool useColor=false;
1034 dbug 1481 bool optimize = false;
1035 dbug 70
1036 dbug 1119 ArgumentParser argumentParser(argc,argv);
1037 dbug 70
1038 dbug 1119 while (argumentParser.ProcessNextArgument())
1039     {
1040     if (argumentParser.IsSwitch("-t2b"))
1041 dbug 70 {
1042 dbug 1119 // Tape to BASIC source code
1043     basicToTape=false;
1044 dbug 70 }
1045 Jylam 1125 else
1046 dbug 1119 if (argumentParser.IsSwitch("-b2t"))
1047     {
1048     // BASIC source code to tape
1049     basicToTape=true;
1050     autoRun=argumentParser.GetBooleanValue(true);
1051     }
1052 Jylam 1125 else
1053 dbug 1119 if (argumentParser.IsSwitch("-color"))
1054     {
1055     // Handling of color codes
1056     useColor=argumentParser.GetBooleanValue(false);
1057     }
1058 dbug 1481 else
1059     if (argumentParser.IsSwitch("-optimize"))
1060     {
1061     // Handling of optimization (disables color if enabled)
1062     optimize =argumentParser.GetBooleanValue(false);
1063     }
1064 dbug 1119 }
1065 dbug 70
1066 dbug 1119 if (argumentParser.GetParameterCount()!=NB_ARG)
1067     {
1068     ShowError(0);
1069     }
1070 dbug 70
1071    
1072 dbug 1119 std::string nameSrc(argumentParser.GetParameter(0));
1073     std::string nameDst(argumentParser.GetParameter(1));
1074 dbug 70
1075 dbug 1119 if (basicToTape)
1076     {
1077 dbug 1489 BasicTokenizer tokenizer;
1078     tokenizer.Bas2Tap(nameSrc.c_str(), nameDst.c_str(), autoRun, useColor, optimize);
1079 dbug 1119 }
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 dbug 1447 Tap2Bas(ptr_buffer,file_size,nameDst.c_str());
1092 dbug 1119 }
1093    
1094     exit(0);
1095 dbug 70 }

  ViewVC Help
Powered by ViewVC 1.1.26