tibasic.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * Copyright (c) 2011 Matthew Iselin
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <iostream>
  17. #include <fstream>
  18. #include <vector>
  19. #include <string>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include "tibasic.h"
  23. using namespace std;
  24. /// \todo More error handling.
  25. unsigned short Compiler::doChecksum(size_t sum)
  26. {
  27. // Bottom 16 bits of the sum.
  28. return (unsigned short) (sum & 0xFFFF);
  29. }
  30. size_t Compiler::sumBytes(const char *data, size_t len)
  31. {
  32. size_t ret = 0;
  33. for(size_t i = 0; i < len; i++)
  34. ret += data[i];
  35. return ret;
  36. }
  37. string trim(const string& str)
  38. {
  39. size_t first = str.find_first_not_of(' ');
  40. if (string::npos == first)
  41. {
  42. return str;
  43. }
  44. size_t last = str.find_last_not_of(' ');
  45. return str.substr(first, (last - first + 1));
  46. }
  47. extern bool verbose;
  48. bool Compiler::compile(string inFile, string outFile)
  49. {
  50. ifstream f(inFile.c_str(), ifstream::in);
  51. string tmpLine;
  52. // Output information ready for writing the compiled code to a file.
  53. vector<token_t> output;
  54. unsigned short outputSize = 0;
  55. while(!f.eof())
  56. {
  57. getline(f, tmpLine, '\n');
  58. // ignore empty lines
  59. if(!tmpLine.length())
  60. {
  61. if(verbose)
  62. log(Debug, "Empty line detected!");
  63. continue;
  64. }
  65. // remove comments
  66. tmpLine = tmpLine.substr(0, tmpLine.find("#", 0));
  67. // strip spaces at the beginning and end of lines
  68. tmpLine = trim(tmpLine);
  69. // ignore lines with now only whitespaces
  70. bool containsSpaces = tmpLine.find_first_not_of(' ') != std::string::npos;
  71. if(!containsSpaces)
  72. {
  73. if(verbose)
  74. log(Debug, "Line with only whitespaces / comments detected!");
  75. continue;
  76. }
  77. // Parse.
  78. token_t token;
  79. while(tmpLine.length())
  80. {
  81. // Grab the longest possible token we can from the input.
  82. string s = tmpLine.substr(0, getLongestToken());
  83. bool validToken = false;
  84. while(!validToken && s.length())
  85. {
  86. validToken = lookupToken(s, token);
  87. if(!validToken)
  88. s = s.substr(0, s.length() - 1);
  89. }
  90. // Special case for alphabet characters
  91. if(!s.length() && isalpha(tmpLine[0]))
  92. {
  93. token.token = toupper(tmpLine[0]);
  94. token.sz = 1;
  95. s = tmpLine.substr(0, 1);
  96. }
  97. if(!s.length())
  98. {
  99. // Error, asplode!
  100. log(Error, "Invalid token.");
  101. f.close();
  102. return false;
  103. }
  104. else
  105. {
  106. outputSize += token.sz;
  107. output.push_back(token);
  108. if(verbose)
  109. std::cout << "Debug: '" << s << "'\n";
  110. tmpLine = tmpLine.substr(s.length(), tmpLine.length());
  111. }
  112. }
  113. // Output a newline.
  114. bool gotNewline = lookupToken("\n", token);
  115. if(gotNewline)
  116. {
  117. outputSize += token.sz;
  118. output.push_back(token);
  119. }
  120. }
  121. // Have the file read and parsed now. Time to write the output.
  122. struct ProgramHeader phdr; struct VariableEntry ventry;
  123. memset(&phdr, 0, sizeof(ProgramHeader));
  124. memset(&ventry, 0, sizeof(VariableEntry));
  125. phdr.datalen = sizeof(VariableEntry) + outputSize + sizeof(outputSize);
  126. strcpy(phdr.sig, "**TI83F*");
  127. phdr.extsig[0] = 0x1A; phdr.extsig[1] = 0x0A; phdr.extsig[2] = 0;
  128. strcpy(phdr.comment, "Generated by the TI-BASIC Compiler.");
  129. /// \todo Magic numbers!
  130. ventry.start = 0x0D;
  131. ventry.length1 = ventry.length2 = outputSize + sizeof(outputSize);
  132. ventry.type = 0x05;
  133. // Convoluted magic to get the filename. Minus the extension. :)
  134. size_t i = 0;
  135. size_t n = outFile.find_last_of('/');
  136. if(n == inFile.npos) n = outFile.find_last_of('\\');
  137. if(n == inFile.npos) n = 0; else n++;
  138. for(; (i < 8) && (n < inFile.length() - 4); n++)
  139. {
  140. if(outFile[n] == '.')
  141. break;
  142. ventry.name[i++] = toupper(outFile[n]);
  143. }
  144. // Begin writing to file.
  145. FILE *out = fopen(outFile.c_str(), "wb");
  146. fwrite(&phdr, sizeof(phdr), 1, out);
  147. fwrite(&ventry, sizeof(ventry), 1, out);
  148. fwrite(&outputSize, 2, 1, out);
  149. // Sum of all bytes for checksum purposes.
  150. size_t sum = 0;
  151. for(vector<token_t>::iterator it = output.begin();
  152. it != output.end();
  153. ++it)
  154. {
  155. fwrite(&(it->token), it->sz, 1, out);
  156. sum += it->token;
  157. }
  158. // Perform a checksum and write to file.
  159. sum += outputSize;
  160. sum += sumBytes(reinterpret_cast<const char*>(&ventry), sizeof(ventry));
  161. unsigned short checksum = doChecksum(sum);
  162. fwrite(&checksum, 2, 1, out);
  163. fclose(out);
  164. return true;
  165. }
  166. bool Compiler::decompile(string inFile, string outFile)
  167. {
  168. // Parse the file.
  169. FILE *fp = fopen(inFile.c_str(), "rb");
  170. if(!fp)
  171. {
  172. log(Error, "Couldn't open input file.");
  173. return false;
  174. }
  175. /// \todo Checksum verification.
  176. // File header
  177. struct ProgramHeader phdr;
  178. fread(&phdr, sizeof(phdr), 1, fp);
  179. // Variable entry
  180. struct VariableEntry ventry;
  181. fread(&ventry, sizeof(ventry), 1, fp);
  182. size_t tokenLength = 0;
  183. fread(&tokenLength, 2, 1, fp);
  184. size_t nBytesRead = 0;
  185. unsigned short temp;
  186. string sOutput = "";
  187. bool bAsmProgram = false;
  188. while((!feof(fp)) && (nBytesRead < tokenLength))
  189. {
  190. fread(&temp, 1, 2, fp);
  191. // If we're in assembly mode, just copy the bytes straight in a numbers.
  192. if(bAsmProgram)
  193. {
  194. if(((temp & 0xFF) == 0x3F))
  195. sOutput += "\n";
  196. sOutput += temp & 0xFF;
  197. fseek(fp, -1, SEEK_CUR);
  198. nBytesRead++;
  199. continue;
  200. }
  201. // Convoluted.
  202. string conv;
  203. bool bIsFound = lookupToken(temp, conv);
  204. if(!bIsFound)
  205. bIsFound = lookupToken(temp & 0xFF, conv);
  206. if(!bIsFound)
  207. {
  208. sOutput += static_cast<char>(temp);
  209. fseek(fp, -1, SEEK_CUR);
  210. nBytesRead++;
  211. }
  212. else
  213. {
  214. sOutput += conv;
  215. token_t tokenInfo;
  216. lookupToken(conv, tokenInfo);
  217. if(tokenInfo.sz < sizeof(unsigned short))
  218. {
  219. fseek(fp, -1, SEEK_CUR);
  220. nBytesRead++;
  221. }
  222. else
  223. nBytesRead += 2;
  224. if(conv == "AsmPrgm")
  225. bAsmProgram = !bAsmProgram;
  226. }
  227. }
  228. fclose(fp);
  229. // Write the output now.
  230. ofstream f(outFile.c_str());
  231. f << sOutput;
  232. f.close();
  233. /// \todo error handling for output file.
  234. return true;
  235. }