/exult/tags/Release1_00/usecode/ucxt/src/ucfunc.cc

# · C++ · 1085 lines · 801 code · 168 blank · 116 comment · 135 complexity · deff48692c53f3f7042b4334cd1dc6ab MD5 · raw file

  1. /*
  2. * Copyright (C) 2001-2002 The Exult Team
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. # include <config.h>
  20. #endif
  21. #include "ucdata.h"
  22. #include "ucfunc.h"
  23. #include <set>
  24. #ifdef HAVE_SSTREAM
  25. #include <sstream>
  26. #else
  27. #include <strstream>
  28. #endif
  29. #include <algorithm>
  30. #include <iomanip>
  31. #include "files/utils.h"
  32. #include "ops.h"
  33. #if 0
  34. #define DEBUG_INDENT
  35. #define DEBUG_PARSE
  36. #define DEBUG_PARSE2
  37. #define DEBUG_PARSE2a
  38. #define DEBUG_READ
  39. #define DEBUG_PRINT
  40. #define DEBUG_READ_PAIR(X, Y) cout << '\t' << X << '\t' << Y << endl;
  41. #else
  42. #undef DEBUG_INDENT
  43. #undef DEBUG_PARSE
  44. #undef DEBUG_PARSE2
  45. #undef DEBUG_READ
  46. #undef DEBUG_PRINT
  47. #define DEBUG_READ_PAIR(X, Y)
  48. #endif
  49. //#define DEBUG_PARSE2
  50. //#define DEBUG_PARSE2a
  51. //#define DEBUG_PRINT
  52. using std::ostream;
  53. using std::ifstream;
  54. using std::string;
  55. using std::vector;
  56. using std::map;
  57. using std::endl;
  58. using std::pair;
  59. using std::ios;
  60. using std::streampos;
  61. using std::cout;
  62. using std::setw;
  63. using std::less;
  64. const string VARNAME = "var";
  65. const string VARPREFIX = "var";
  66. string demunge_ocstring(UCFunc &ucf, const FuncMap &funcmap, const string &asmstr, const vector<unsigned int> &params, const map<unsigned int, string> &intrinsics, const UCc &op, bool ucs_output);
  67. /* Assumption the 'var's are in their 'zeroed' state on initialization,
  68. unless something else is assigned to them. */
  69. inline ostream &tab_indent(const unsigned int indent, ostream &o)
  70. {
  71. #ifdef DEBUG_INDENT
  72. o << indent;
  73. #endif
  74. switch(indent)
  75. {
  76. case 0: break;
  77. case 1: o << '\t'; break;
  78. case 2: o << "\t\t"; break;
  79. case 3: o << "\t\t\t"; break;
  80. case 4: o << "\t\t\t\t"; break;
  81. case 5: o << "\t\t\t\t\t"; break;
  82. default:
  83. for(unsigned int i=0; i<indent; ++i) o << '\t';
  84. break;
  85. }
  86. return o;
  87. }
  88. /* Outputs the short function data 'list' format, returns true upon success */
  89. bool UCFunc::output_list(ostream &o, unsigned int funcno, const UCOptions &options)
  90. {
  91. o << "#" << std::setbase(10) << std::setw(4) << funcno << std::setbase(16) << ": "
  92. << (return_var ? '&' : ' ')
  93. << std::setw(4) << _funcid << "H "
  94. << std::setw(8) << _offset << " "
  95. << std::setw(4) << _funcsize << " "
  96. << std::setw(4) << _datasize << " "
  97. << std::setw(4) << codesize() << " ";
  98. if(options.ucdebug)
  99. o << _data.find(0)->second;
  100. o << endl;
  101. return true;
  102. }
  103. /* Outputs the usecode-script formatted usecode, returns true upon success */
  104. bool UCFunc::output_ucs(ostream &o, const FuncMap &funcmap, const map<unsigned int, string> &intrinsics, const UCOptions &options)
  105. {
  106. unsigned int indent=0;
  107. if(_externs.size()) tab_indent(indent, o) << "// externs" << endl;
  108. // output the 'externs'
  109. for(vector<unsigned int>::iterator e=_externs.begin(); e!=_externs.end(); e++)
  110. {
  111. FuncMap::const_iterator fmp = funcmap.find(*e);
  112. output_ucs_funcname(tab_indent(indent, o) << "extern ", funcmap, *e, fmp->second.num_args, fmp->second.return_var) << ';' << endl;
  113. }
  114. if(_externs.size()) o << endl;
  115. // output the function name
  116. output_ucs_funcname(tab_indent(indent, o), funcmap, _funcid, _num_args, return_var) << endl;
  117. // start of func
  118. tab_indent(indent++, o) << '{' << endl;
  119. for(unsigned int i=_num_args; i<_num_args+_num_locals; i++)
  120. tab_indent(indent, o) << VARNAME << ' ' << VARPREFIX << std::setw(4) << i << ';' << endl;
  121. if(return_var) tab_indent(indent, o) << VARNAME << ' ' << "rr" << ';' << endl;
  122. if(_num_locals>0) o << endl;
  123. output_ucs_data(o, funcmap, intrinsics, options, indent);
  124. tab_indent(--indent, o) << '}' << endl;
  125. return true;
  126. }
  127. /* outputs the general 'function name' in long format. For function
  128. declarations and externs */
  129. ostream &UCFunc::output_ucs_funcname(ostream &o, const FuncMap &funcmap,
  130. unsigned int funcid,
  131. unsigned int numargs, bool return_var)
  132. {
  133. // do we return a variable
  134. if(return_var) o << VARNAME << ' ';
  135. // output the "function name"
  136. // TODO: Probably want to grab this from a file in the future...
  137. //o << demunge_ocstring(*this, funcmap, "%f1", ucc._params_parsed, intrinsics, ucc, true)
  138. FuncMap::const_iterator fmp = funcmap.find(funcid);
  139. if(fmp->second.funcname.size())
  140. {
  141. if(fmp->second.funcname[0]=='&')
  142. o << fmp->second.funcname.substr(1, fmp->second.funcname.size()-1);
  143. else
  144. o << fmp->second.funcname;
  145. }
  146. else
  147. o << "Func" << std::setw(4) << funcid;
  148. // output the "function number"
  149. o << " 0x" << funcid
  150. // output ObCurly braces
  151. << " (";
  152. for(unsigned int i=0; i<numargs; i++)
  153. o << VARNAME << ' ' << VARPREFIX << std::setw(4) << i << ((i==numargs-1) ? "" : ", ");
  154. o << ")";
  155. return o;
  156. }
  157. ostream &UCFunc::output_ucs_funcname(ostream &o, const FuncMap &funcmap)
  158. {
  159. return output_ucs_funcname(o, funcmap, _funcid, _num_args, return_var);
  160. }
  161. void UCFunc::output_ucs_data(ostream &o, const FuncMap &funcmap, const map<unsigned int, string> &intrinsics, const UCOptions &options, unsigned int indent)
  162. {
  163. vector<unsigned int> labeltmp(1);
  164. for(vector<GotoSet>::iterator i=gotoset.begin(); i!=gotoset.end(); ++i)
  165. {
  166. // we don't want to output the first "jump" (the start of the function)
  167. if(i!=gotoset.begin())
  168. {
  169. labeltmp[0]=i->offset();
  170. tab_indent(indent++, o) << demunge_ocstring(*this, funcmap, "label%f*_%1:", labeltmp, intrinsics, UCc(), true) << endl;
  171. }
  172. for(GotoSet::iterator j=(*i)().begin(); j!=(*i)().end(); j++)
  173. {
  174. const UCc &ucc = *(j->first);
  175. if(options.uselesscomment)
  176. tab_indent(indent, o) << "// Offset: " << std::setw(4) << ucc._offset << endl;
  177. output_ucs_opcode(o, funcmap, opcode_table_data, ucc, intrinsics, indent);
  178. }
  179. if(i!=gotoset.begin()) --indent; //decrement it again to skip the label statement.
  180. }
  181. }
  182. void UCFunc::output_ucs_opcode(ostream &o, const FuncMap &funcmap, const vector<UCOpcodeData> &optab, const UCc &op, const map<unsigned int, string> &intrinsics, unsigned int indent)
  183. {
  184. tab_indent(indent, o) << demunge_ocstring(*this, funcmap, optab[op._id].ucs_nmo, op._params_parsed, intrinsics, op, true) << ';' << endl;
  185. #ifdef DEBUG_PRINT
  186. for(vector<UCc *>::const_iterator i=op._popped.begin(); i!=op._popped.end(); i++)
  187. {
  188. if((*i)->_popped.size())
  189. output_ucs_opcode(o, funcmap, opcode_table_data, **i, intrinsics, indent+1);
  190. else
  191. // tab_indent(indent+1, o) << demunge_ocstring(*this, funcmap, optab[(*i)->_id].ucs_nmo, op._params_parsed, **i) << endl;
  192. tab_indent(indent+1, o) << optab[(*i)->_id].ucs_nmo << endl;
  193. }
  194. #endif
  195. }
  196. void UCFunc::output_ucs_node(ostream &o, const FuncMap &funcmap, UCNode* ucn, const map<unsigned int, string> &intrinsics, unsigned int indent, const UCOptions &options)
  197. {
  198. if(!ucn->nodelist.empty()) tab_indent(indent, o) << '{' << endl;
  199. if(ucn->ucc!=0)
  200. output_asm_opcode(tab_indent(indent, o), funcmap, opcode_table_data, intrinsics, *(ucn->ucc), options);
  201. if(ucn->nodelist.size())
  202. for(vector<UCNode *>::iterator i=ucn->nodelist.begin(); i!=ucn->nodelist.end(); i++)
  203. {
  204. //tab_indent(indent, o);
  205. output_ucs_node(o, funcmap, *i, intrinsics, indent+1, options);
  206. }
  207. // end of func
  208. if(!ucn->nodelist.empty()) tab_indent(indent, o) << '}' << endl;
  209. }
  210. /* Just a quick function to remove all the ucc structured flagged as removable */
  211. inline void gc_gotoset(vector<GotoSet> &gotoset)
  212. {
  213. for(vector<GotoSet>::iterator i=gotoset.begin(); i!=gotoset.end(); i++)
  214. {
  215. i->gc();
  216. #ifdef DEBUG_GOTOSET
  217. cout << "----" << endl;
  218. #endif
  219. }
  220. }
  221. void UCFunc::parse_ucs(const FuncMap &funcmap, const map<unsigned int, string> &intrinsics, const UCOptions &options)
  222. {
  223. for(vector<UCc>::iterator i=_opcodes.begin(); i!=_opcodes.end(); i++)
  224. node.nodelist.push_back(new UCNode(&(*i)));
  225. parse_ucs_pass1(node.nodelist);
  226. parse_ucs_pass2(gotoset, funcmap, intrinsics);
  227. gc_gotoset(gotoset);
  228. if(!options.basic)
  229. {
  230. parse_ucs_pass3(gotoset, intrinsics);
  231. }
  232. #ifdef DEBUG_PARSE2
  233. for(vector<GotoSet>::iterator i=gotoset.begin(); i!=gotoset.end(); i++)
  234. {
  235. cout << std::setw(4) << i->offset() << endl;
  236. for(GotoSet::iterator j=(*i)().begin(); j!=(*i)().end(); j++)
  237. {
  238. cout << '\t' << std::setw(4) << j->first->_offset << '\t' << j->first->_id << endl;
  239. }
  240. }
  241. #endif
  242. }
  243. /* Pass 1 turns the 1-dimentional vector of opcodes, into a 2-dimentional array
  244. consisting of all the opcodes within two 'goto target offsets'. */
  245. void UCFunc::parse_ucs_pass1(vector<UCNode *> &nodes)
  246. {
  247. vector<unsigned int> jumps;
  248. // collect jump references
  249. for(unsigned int i=0; i<nodes.size(); i++)
  250. {
  251. if(nodes[i]->ucc!=0)
  252. {
  253. unsigned int isjump=0;
  254. for(vector<pair<unsigned int, unsigned int> >::iterator op=opcode_jumps.begin(); op!=opcode_jumps.end(); op++)
  255. if(op->first==nodes[i]->ucc->_id)
  256. {
  257. isjump=op->second;
  258. break;
  259. }
  260. if(isjump!=0)
  261. {
  262. assert(nodes[i]->ucc->_params_parsed.size()>=isjump);
  263. jumps.push_back(nodes[i]->ucc->_params_parsed[isjump-1]);
  264. }
  265. }
  266. }
  267. gotoset.push_back(GotoSet());
  268. for(unsigned int i=0; i<nodes.size(); i++)
  269. {
  270. if(nodes[i]->ucc!=0)
  271. {
  272. if(count(jumps.begin(), jumps.end(), nodes[i]->ucc->_offset))
  273. {
  274. gotoset.push_back(nodes[i]->ucc);
  275. }
  276. else
  277. gotoset.back().add(nodes[i]->ucc);
  278. }
  279. }
  280. }
  281. /* In Pass 2 we convert our 2-dimensional 'GotoSet' array into an array with
  282. each UCc, having it's parameters sitting in it's UCc::_popped vector. Elements
  283. that are parameters are flagged for removal (Gotoset::()[i]->second=true) from
  284. the original GotoSet. */
  285. void UCFunc::parse_ucs_pass2(vector<GotoSet> &gotoset, const FuncMap &funcmap, const map<unsigned int, string> &intrinsics)
  286. {
  287. for(vector<GotoSet>::iterator i=gotoset.begin(); i!=gotoset.end(); ++i)
  288. {
  289. parse_ucs_pass2a((*i)().rbegin(), (*i)(), 0, funcmap, intrinsics);
  290. }
  291. }
  292. vector<UCc *> UCFunc::parse_ucs_pass2a(vector<pair<UCc *, bool> >::reverse_iterator current, vector<pair<UCc *, bool> > &vec, unsigned int opsneeded, const FuncMap &funcmap, const map<unsigned int, string> &intrinsics)
  293. {
  294. vector<UCc *> vucc;
  295. unsigned int opsfound=0;
  296. #ifdef DEBUG_PARSE2
  297. output_asm_opcode(tab_indent(4, cout), *this, funcmap, opcode_table_data, intrinsics, *(current->first));
  298. #endif
  299. for(;vec.rend()!=current; current++)
  300. {
  301. #ifdef DEBUG_PARSE2
  302. output_asm_opcode(tab_indent(3, cout), *this, funcmap, opcode_table_data, intrinsics, *(current->first));
  303. #endif
  304. if(current->second==false)
  305. {
  306. if((opcode_table_data[current->first->_id].num_pop!=0) || (opcode_table_data[current->first->_id].call_effect!=0))
  307. {
  308. //if(opcode_table_data[current->first->_id].num_pop<0x7F)
  309. {
  310. #ifdef DEBUG_PARSE2
  311. output_asm_opcode(tab_indent(3, cout << "0x" << std::setw(2) << current->first->_id << "-"), *this, funcmap, opcode_table_data, intrinsics, *(current->first));
  312. tab_indent(3, cout << "0x" << std::setw(2) << current->first->_id << "-") << opcode_table_data[current->first->_id].num_pop << endl;
  313. #endif
  314. unsigned int num_args=0;
  315. if(opcode_table_data[current->first->_id].num_pop>0x7F)
  316. {
  317. #ifdef DEBUG_PARSE2a
  318. cout << "CALL EFFECT: " << opcode_table_data[current->first->_id].num_pop << '\t';
  319. #endif
  320. unsigned int offset = 0x100 - opcode_table_data[current->first->_id].num_pop - 1;
  321. num_args = current->first->_params_parsed[offset];
  322. #ifdef DEBUG_PARSE2a
  323. cout << num_args << endl;
  324. #endif
  325. }
  326. else if(opcode_table_data[current->first->_id].call_effect!=0)
  327. {
  328. assert(current->first->_params_parsed.size()>=1);
  329. assert(_externs.size()>=current->first->_params_parsed[0]);
  330. FuncMap::const_iterator fmp = funcmap.find(_externs[current->first->_params_parsed[0]]);
  331. assert(fmp!=funcmap.end());
  332. #ifdef DEBUG_PARSE2
  333. cout << "CALL: " << fmp->second.funcid << '\t' << fmp->second.num_args << endl;
  334. #endif
  335. num_args = fmp->second.num_args;
  336. }
  337. else
  338. {
  339. #ifdef DEBUG_PARSE2
  340. cout << "Non-CALL: \t" << opcode_table_data[current->first->_id].num_pop << endl;
  341. #endif
  342. num_args = opcode_table_data[current->first->_id].num_pop;
  343. }
  344. if(num_args>0)
  345. {
  346. /* save the 'current' value as the return value and increment it so it's
  347. pointing at the 'next' current value */
  348. vector<pair<UCc *, bool> >::reverse_iterator ret(current);
  349. ret->first->_popped = parse_ucs_pass2a(++current, vec, num_args, funcmap, intrinsics);
  350. assert(current!=ret);
  351. --current;
  352. assert(current==ret);
  353. #ifdef DEBUG_PARSE2a
  354. output_asm_opcode(tab_indent(1, cout), *this, funcmap, opcode_table_data, intrinsics, *(ret->first));
  355. for(vector<UCc *>::iterator i=ret->first->_popped.begin(); i!=ret->first->_popped.end(); i++)
  356. output_asm_opcode(tab_indent(2, cout), *this, funcmap, opcode_table_data, intrinsics, **i);
  357. #endif
  358. }
  359. }
  360. }
  361. if((opsneeded!=0) && (current->second==false))
  362. {
  363. // if it's a 'push' opcode and we need items to return that we've popped off the stack...
  364. if(opcode_table_data[current->first->_id].num_push!=0)
  365. {
  366. #ifdef DEBUG_PARSE2
  367. output_asm_opcode(tab_indent(4, cout << "P-"), *this, funcmap, opcode_table_data, intrinsics, *(current->first));
  368. #endif
  369. opsfound+=opcode_table_data[current->first->_id].num_push;
  370. vucc.push_back(current->first);
  371. current->second=true;
  372. }
  373. // if it's a call to a function that returns a variable...
  374. else if(opcode_table_data[current->first->_id].call_effect!=0)
  375. {
  376. FuncMap::const_iterator fmp = funcmap.find(_externs[current->first->_params_parsed[0]]);
  377. assert(fmp!=funcmap.end());
  378. if(fmp->second.return_var)
  379. {
  380. #ifdef DEBUG_PARSE2
  381. output_asm_opcode(tab_indent(4, cout << "C-"), *this, funcmap, opcode_table_data, intrinsics, *(current->first));
  382. #endif
  383. opsfound+=1;
  384. vucc.push_back(current->first);
  385. current->second=true;
  386. }
  387. }
  388. else
  389. current->second=true;
  390. // if we've found all the ops we were searching for, return them
  391. if(opsfound>=opsneeded)
  392. {
  393. return vucc;
  394. }
  395. }
  396. }
  397. }
  398. if(vucc.size()>0) cout << "DID NOT FIND ALL OPCODE PARAMETERS." << endl;
  399. return vucc;
  400. }
  401. /* The 'optimisation' phase. Attempting to remove as many goto...labels as possible. */
  402. void UCFunc::parse_ucs_pass3(vector<GotoSet> &gotoset, const map<unsigned int, string> &intrinsics)
  403. {
  404. }
  405. bool UCFunc::output_tt(std::ostream &o)
  406. {
  407. o << "\t<0x" << setw(4) << _funcid << ">" << endl;
  408. for(map<unsigned int, string, less<unsigned int> >::iterator i=_data.begin(); i!=_data.end(); i++)
  409. {
  410. o << "\t\t<0x" << setw(4) << i->first << ">" << endl
  411. << "\t\t`" << i->second << "`" << endl
  412. << "\t\t</>" << endl;
  413. }
  414. o << "\t</>" << endl;
  415. return true;
  416. }
  417. /* calculates the relative offset jump location, used in opcodes jmp && jne */
  418. inline int calc16reloffset(const UCc &op, unsigned int param)
  419. {
  420. /* forumla:
  421. real offset = offset of start of current opcode
  422. + int of parameter (since you can jump backwards)
  423. + 1 (size of "opcode")
  424. + size of "opcode" parameter data
  425. NOTE: since param is unsigned, a twos-complimant is required:
  426. formula: 0xFFFF - (unsigned short)param + 1
  427. ^^^^^^ max of unsighed short
  428. */
  429. return op._offset + ((param>>15) ? (-1 * (0xFFFF - static_cast<unsigned short>(param) + 1)) : static_cast<int>(param)) + 1 + op._params.size();
  430. }
  431. /* calculates the relative offset jump location, used in opcodes jmp && jne */
  432. inline int calc32reloffset(const UCc &op, unsigned int param) //FIXME: Test this!
  433. {
  434. /* forumla:
  435. real offset = offset of start of current opcode
  436. + int of parameter (since you can jump backwards)
  437. + 1 (size of "opcode")
  438. + size of "opcode" parameter data
  439. NOTE: since param is unsigned, a twos-complimant is required:
  440. formula: 0xFFFFFFFF - (unsigned int)param + 1
  441. ^^^^^^ max of unsighed int
  442. */
  443. return op._offset + ((param>>31) ? (-1 * (0xFFFFFFFF - static_cast<unsigned int>(param) + 1)) : static_cast<int>(param)) + 1 + op._params.size();
  444. }
  445. void ucc_parse_parambytes(UCc &ucop, const UCOpcodeData &otd)
  446. {
  447. unsigned int first=0;
  448. for(vector<pair<unsigned int, bool> >::const_iterator s=otd.param_sizes.begin(); s!=otd.param_sizes.end(); ++s)
  449. {
  450. //cout << ucop._id << '\t' << ucop._params.size() << endl;
  451. assert(first<ucop._params.size());
  452. unsigned int ssize=s->first;
  453. bool offset_munge=s->second;
  454. assert(ssize!=0);
  455. if(ssize==1)
  456. ucop._params_parsed.push_back(static_cast<unsigned int>(ucop._params[first++]));
  457. else if(ssize==2)
  458. if(offset_munge)
  459. {
  460. unsigned int calcvar = static_cast<unsigned int>(ucop._params[first++]);
  461. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 8);
  462. unsigned int reloffset = calc16reloffset(ucop, calcvar);
  463. ucop._params_parsed.push_back(reloffset);
  464. ucop._jump_offsets.push_back(reloffset);
  465. }
  466. else
  467. {
  468. unsigned int calcvar = static_cast<unsigned int>(ucop._params[first++]);
  469. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 8);
  470. ucop._params_parsed.push_back(calcvar);
  471. }
  472. else if(ssize==4)
  473. if(offset_munge)
  474. {
  475. unsigned int calcvar = static_cast<unsigned int>(ucop._params[first++]);
  476. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 8);
  477. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 16);
  478. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 24);
  479. unsigned int reloffset = calc32reloffset(ucop, calcvar);
  480. ucop._params_parsed.push_back(reloffset);
  481. ucop._jump_offsets.push_back(reloffset);
  482. }
  483. else
  484. {
  485. unsigned int calcvar = static_cast<unsigned int>(ucop._params[first++]);
  486. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 8);
  487. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 16);
  488. calcvar += ((static_cast<unsigned int>(ucop._params[first++])) << 24);
  489. ucop._params_parsed.push_back(calcvar);
  490. }
  491. else
  492. assert(false); // just paranoia.
  493. }
  494. }
  495. /* prints the "assembler" output of the usecode, currently trying to duplicate
  496. the output of the original ucdump... returns true if successful*/
  497. bool UCFunc::output_asm(ostream &o, const FuncMap &funcmap, const map<unsigned int, string> &intrinsics, const UCOptions &options)
  498. {
  499. if(options.verbose) cout << "Printing function..." << endl;
  500. o << "Function at file offset " << std::setw(8) << _offset << "H" << endl;
  501. o << "\t.funcnumber " << std::setw(4) << _funcid << "H" << endl;
  502. if(ext32) o << "\t.ext32" << endl;
  503. o << "\t.msize " << ((ext32) ? setw(8) : setw(4)) << _funcsize << "H" << endl;
  504. o << "\t.dsize " << ((ext32) ? setw(8) : setw(4)) << _datasize << "H" << endl;
  505. if(debugging_info)
  506. o << "\t .dbgoffset " << std::setw(4) << debugging_offset << "H" << endl;
  507. if(_data.size())
  508. output_asm_data(o);
  509. o << "Code segment at file offset " << std::setw(8) << _codeoffset << "H" << endl;
  510. o << "\t.argc " << std::setw(4) << _num_args << "H" << endl;
  511. o << "\t.localc " << std::setw(4) << _num_locals << "H" << endl;
  512. o << "\t.externsize " << std::setw(4) << _externs.size() << "H" << endl;
  513. for(typeof(_externs.begin()) i=_externs.begin(); i!=_externs.end(); i++)
  514. o << '\t' << " .extern " << std::setw(4) << *i << "H" << endl;
  515. /* for(unsigned int i=0; i<_externs.size(); i++) //FIXME: ::iterators
  516. o << '\t' << " .extern " << std::setw(4) << _externs[i] << "H" << endl;*/
  517. for(vector<UCc>::iterator op=_opcodes.begin(); op!=_opcodes.end(); op++)
  518. output_asm_opcode(o, funcmap, opcode_table_data, intrinsics, *op, options);
  519. return true;
  520. }
  521. void UCFunc::output_asm_data(ostream &o)
  522. {
  523. static const unsigned int nochars=60;
  524. // limit of about 60 chars to a line, wrap to the next line if longer then this...
  525. for(map<unsigned int, string, less<unsigned int> >::iterator i=_data.begin(); i!=_data.end(); i++)
  526. {
  527. for(unsigned int j=0; j<i->second.size(); j++)
  528. {
  529. if(j==0)
  530. o << setw(4) << i->first;
  531. if((j!=0) && !(j%nochars))
  532. o << "'" << endl;
  533. if(!(j%nochars))
  534. o << "\tdb\t'";
  535. o << i->second[j];
  536. }
  537. o << "'" << endl;
  538. o << "\tdb\t00" << endl;
  539. }
  540. }
  541. void UCFunc::output_raw_opcodes(ostream &o, const UCc &op)
  542. {
  543. // chars in opcode
  544. o << ' ' << std::setw(2) << static_cast<unsigned int>(op._id);
  545. if(op._params.size()) cout << ' ';
  546. for(unsigned int i=0; i<op._params.size(); i++)
  547. {
  548. o << std::setw(2) << static_cast<unsigned int>(op._params[i]);
  549. if(i!=op._params.size())
  550. o << ' ';
  551. }
  552. // seperator
  553. unsigned int numsep = op._params.size();
  554. //cout << endl << numsep << endl;
  555. if(numsep>6)
  556. o << endl << "\t\t\t";
  557. else if (numsep>5)
  558. o << " ";
  559. else if (numsep>2)
  560. o << "\t";
  561. else
  562. o << "\t\t";
  563. }
  564. void UCFunc::output_asm_opcode(ostream &o, const FuncMap &funcmap, const vector<UCOpcodeData> &optab, const map<unsigned int, string> &intrinsics, const UCc &op, const UCOptions &options)
  565. {
  566. // offset
  567. o << std::setw(4) << op._offset << ':';
  568. if(options.rawops) output_raw_opcodes(o, op);
  569. else o << '\t';
  570. o << demunge_ocstring(*this, funcmap, optab[op._id].asm_nmo, op._params_parsed, intrinsics, op, false);
  571. if(options.autocomment)
  572. o << demunge_ocstring(*this, funcmap, optab[op._id].asm_comment, op._params_parsed, intrinsics, op, false);
  573. o << endl;
  574. }
  575. inline unsigned int charnum2uint(const char c)
  576. {
  577. switch(c)
  578. {
  579. case '1': return 1;
  580. case '2': return 2;
  581. case '3': return 3;
  582. case '4': return 4;
  583. case '5': return 5;
  584. case '6': return 6;
  585. case '7': return 7;
  586. case '8': return 8;
  587. case '9': return 9;
  588. default: return 0;
  589. }
  590. return 0; // can't happen
  591. }
  592. // FIXME: Remove the passed &params value. Get it from op._params_parsed
  593. string demunge_ocstring(UCFunc &ucf, const FuncMap &funcmap, const string &asmstr, const vector<unsigned int> &params, const map<unsigned int, string> &intrinsics, const UCc &op, bool ucs_output)
  594. {
  595. #ifdef HAVE_SSTREAM
  596. std::stringstream str;
  597. #else
  598. std::strstream str;
  599. #endif
  600. str << std::setfill('0') << std::setbase(16);
  601. str.setf(ios::uppercase);
  602. size_t len=asmstr.length();
  603. if(len==0) return string(); // for the degenerate case
  604. bool finished=false; // terminating details are at end-of-while
  605. unsigned int i=0; // istr index
  606. unsigned int width=0; // width value for setw()
  607. if(ucs_output && opcode_table_data[op._id].flag_paren) str << '(';
  608. while(!finished&&i<len)
  609. {
  610. bool special_call(false); // FIXME: <sigh> temporary exception handling for call (0x24)
  611. char c = asmstr[i];
  612. width = 4; // with defaults to 4
  613. switch(c)
  614. {
  615. case '\\':
  616. i++;
  617. c = asmstr[i];
  618. switch(c)
  619. {
  620. case '\\': str << '\\'; break;
  621. case 'n': str << '\n'; break;
  622. case 't': str << '\t'; break;
  623. case '\'': str << '\''; break;
  624. case '"': str << '\"'; break;
  625. case 'b': // bell is invalid
  626. default: // we'll silently drop errors... it's the only "clean" way
  627. str << '\\' << c;
  628. }
  629. break;
  630. case '%':
  631. {
  632. i++;
  633. c = asmstr[i];
  634. // if it's a "byte" set width to 2, and get the next char
  635. if(c=='b') { i++; c = asmstr[i]; width=2; }
  636. // if it's a "short" set width to 4, and get the next char
  637. else if(c=='s') { i++; c = asmstr[i]; width=4; }
  638. // if it's a "int" set width to 8, and get the next char
  639. else if(c=='n') { i++; c = asmstr[i]; width=8; }
  640. // if it's a "long" set width to 16, and get the next char
  641. else if(c=='l') { i++; c = asmstr[i]; width=16; }
  642. // if we want to output the 'decimal' value rather then the default hex
  643. else if(c=='d')
  644. {
  645. i++; c = asmstr[i];
  646. unsigned int t = charnum2uint(c);
  647. if(t!=0)
  648. {
  649. assert(params.size()>=t);
  650. str << std::setbase(10) << params[t-1] << std::setbase(16);
  651. }
  652. else if(c=='%')
  653. str << '%';
  654. break;
  655. }
  656. // if it's the character representation of a text data string we want
  657. else if(c=='t')
  658. {
  659. bool commentformat=false;
  660. i++; c = asmstr[i];
  661. // if we only want to output the 'short' format of the text (comment format)
  662. if(c=='c')
  663. {
  664. commentformat=true;
  665. i++; c = asmstr[i];
  666. }
  667. unsigned int t = charnum2uint(c);
  668. assert(params.size()>=t);
  669. assert(t!=0);
  670. string s = ucf._data.find(params[t-1])->second;
  671. if(commentformat)
  672. if(s.size()>17) s = s.substr(0, 17) + string("...");
  673. // escape the appropriate characters...
  674. // we'll only do it in the 'full' text output for the moment.
  675. if(!commentformat)
  676. for(string::size_type i=0; i<s.size(); i++)
  677. if((s[i]=='\"') || (s[i]=='\\'))
  678. {
  679. s.insert(i, "\\");
  680. ++i;
  681. }
  682. str << s;
  683. break;
  684. }
  685. // if it's the intrinsic name we want
  686. else if(c=='i')
  687. {
  688. i++; c = asmstr[i];
  689. unsigned int t = charnum2uint(c);
  690. assert(params.size()>=t);
  691. assert(t!=0);
  692. string s = intrinsics.find(params[t-1])->second;
  693. str << s;
  694. break;
  695. }
  696. // if it's external function name we want
  697. else if(c=='f')
  698. {
  699. i++; c = asmstr[i];
  700. if(c=='*')
  701. {
  702. if(ucf.funcname.size())
  703. {
  704. if(ucf.funcname[0]=='&')
  705. str << ucf.funcname.substr(1, ucf.funcname.size()-1);
  706. else
  707. str << ucf.funcname;
  708. }
  709. else
  710. str << "Func" << std::setw(4) << ucf._funcid;
  711. }
  712. else
  713. {
  714. unsigned int t = charnum2uint(c);
  715. assert(ucf._externs.size()>=t);
  716. assert(t!=0);
  717. assert(op._params_parsed.size()>=1);
  718. FuncMap::const_iterator fmp = funcmap.find(ucf._externs[op._params_parsed[t-1]]);
  719. if(fmp->second.funcname.size())
  720. {
  721. if(fmp->second.funcname[0]=='&')
  722. str << fmp->second.funcname.substr(1, fmp->second.funcname.size()-1);
  723. else
  724. str << fmp->second.funcname;
  725. }
  726. else
  727. str << "Func" << std::setw(4) << ucf._externs[op._params_parsed[t-1]];
  728. }
  729. break;
  730. }
  731. // if it's the character representation of a text data string we want
  732. else if(c=='p')
  733. {
  734. i++; c = asmstr[i];
  735. unsigned int t = charnum2uint(c);
  736. // FIXME: this is the special 'call' case, it may be a good idea to make more general
  737. if((t==0) && (c==','))
  738. {
  739. special_call=true;
  740. for(vector<UCc *>::const_iterator i=op._popped.begin(); i!=op._popped.end();)
  741. {
  742. str << demunge_ocstring(ucf, funcmap, opcode_table_data[(*i)->_id].ucs_nmo, (*i)->_params_parsed, intrinsics, **i, ucs_output);
  743. if(++i!=op._popped.end())
  744. str << ", ";
  745. }
  746. }
  747. if(t!=0)
  748. {
  749. if(t>op._popped.size())
  750. str << "SOMETHING_GOES_HERE()";
  751. else
  752. {
  753. UCc &ucc(*op._popped[t-1]);
  754. str << demunge_ocstring(ucf, funcmap, opcode_table_data[ucc._id].ucs_nmo, ucc._params_parsed, intrinsics, ucc, ucs_output);
  755. }
  756. }
  757. break;
  758. }
  759. if(special_call!=true)
  760. {
  761. unsigned int t = charnum2uint(c);
  762. if(t!=0)
  763. {
  764. assert(params.size()>=t);
  765. str << std::setw(width) << params[t-1];
  766. }
  767. else if(c=='%')
  768. str << '%';
  769. }
  770. }
  771. break;
  772. default: // it's just a character, leave it be
  773. str << c;
  774. }
  775. i++;
  776. if(i==asmstr.size()) finished=true;
  777. }
  778. if(ucs_output && opcode_table_data[op._id].flag_paren) str << ')';
  779. str << std::ends;
  780. string tstr(str.str());
  781. #ifndef HAVE_SSTREAM
  782. str.freeze(false);
  783. #endif
  784. return tstr;
  785. }
  786. void readbin_U7UCFunc(ifstream &f, UCFunc &ucf, const UCOptions &options)
  787. {
  788. // #define DEBUG_READ_PAIR(X, Y) cout << '\t' << X << '\t' << Y << endl;
  789. // offset to start of function
  790. ucf._offset = f.tellg();
  791. DEBUG_READ_PAIR(" Offset: ", ucf._offset);
  792. // Read Function Header
  793. ucf._funcid = Read2(f);
  794. if(options.very_verbose)
  795. cout << "\tReading Function: " << setw(4) << ucf._funcid << endl;
  796. DEBUG_READ_PAIR(" FuncID: ", ucf._funcid);
  797. if(ucf._funcid!=0xFFFF)
  798. {
  799. // This is the original usecode function header
  800. ucf._funcsize = Read2(f);
  801. DEBUG_READ_PAIR(" FuncSize: ", ucf._funcsize);
  802. // save body offset in case we need it
  803. ucf._bodyoffset = f.tellg();
  804. ucf._datasize = Read2(f);
  805. DEBUG_READ_PAIR(" DataSize: ", ucf._datasize);
  806. }
  807. else
  808. {
  809. // This is the ext32 extended usecode function header
  810. ucf.ext32=true;
  811. ucf._funcid = Read2(f);
  812. if(options.very_verbose)
  813. cout << "\tReading Function: " << setw(4) << ucf._funcid << endl;
  814. DEBUG_READ_PAIR(" extFuncID: ", ucf._funcid);
  815. ucf._funcsize = Read4(f);
  816. DEBUG_READ_PAIR(" extFuncSize: ", ucf._funcsize);
  817. // save body offset in case we need it
  818. ucf._bodyoffset = f.tellg();
  819. ucf._datasize = Read4(f);
  820. DEBUG_READ_PAIR(" extDataSize: ", ucf._datasize);
  821. }
  822. // process ze data segment!
  823. {
  824. streampos pos = f.tellg(); // paranoia
  825. unsigned int off = 0;
  826. // Load all strings & their offsets
  827. while( off < ucf._datasize )
  828. {
  829. assert(!f.eof());
  830. string data;
  831. getline(f, data, static_cast<char>(0x00));
  832. ucf._data.insert(pair<unsigned int, string>(off, data));
  833. off+=data.size()+1;
  834. }
  835. f.seekg(pos, ios::beg); // paranoia
  836. f.seekg(ucf._datasize, ios::cur); // paranoia
  837. }
  838. #if 0
  839. if(ucf._funcid==_search_func)
  840. for(map<unsigned int, string>::iterator i=ucf._data.begin(); i!=ucf._data.end(); i++)
  841. cout << i->first << "\t" << i->second << endl;
  842. #endif
  843. // process code segment
  844. {
  845. //streampos start_of_code_seg = f.tellg(); // what's this used for?
  846. ucf._codeoffset = f.tellg();
  847. // get the number of arguments to the function
  848. ucf._num_args = Read2(f);
  849. // get the number of local variables
  850. ucf._num_locals = Read2(f);
  851. // get the number of external function numbers
  852. ucf._num_externs = Read2(f);
  853. // load the external function numbers
  854. for(unsigned int i=0; i<ucf._num_externs; i++)
  855. ucf._externs.push_back(Read2(f));
  856. // ok, now to load the usecode
  857. unsigned int code_offset=0;
  858. /* Here the '3+' comes from the sizeof(_datasize) + sizeof(_num_args)
  859. + sizeof(_num_locals) + sizeof(_num_externs)
  860. which are stored in the file as 16bit shorts, with the exception of
  861. an ext32 function header, where the _datasize is a 32bit structure */
  862. unsigned int code_size = ucf._funcsize - ucf._datasize - ((4+ucf._num_externs) * SIZEOF_USHORT);
  863. if(ucf.ext32==true) code_size-=2;
  864. DEBUG_READ_PAIR("Code Size: ", code_size);
  865. while(code_offset<code_size)
  866. {
  867. assert(!f.eof());
  868. UCc ucop;
  869. ucop._offset = code_offset;
  870. ucop._id = Read1(f);
  871. code_offset++;
  872. const UCOpcodeData &otd = opcode_table_data[ucop._id];
  873. if(otd.opcode==0x00)
  874. {
  875. cout << ucop._id << ' ' << code_offset << endl;
  876. assert(otd.opcode!=0x00);
  877. }
  878. //assert(((otd.asm_nmo.size()!=0) && (otd.ucs_nmo.size()!=0)));
  879. for(unsigned int i=0; i<otd.num_bytes; i++)
  880. ucop._params.push_back(Read1(f));
  881. // parse the parameters
  882. ucc_parse_parambytes(ucop, otd);
  883. code_offset+=otd.num_bytes;
  884. /* if we're an opcode that sets a return value, we need to mark the
  885. function as one that returns a value */
  886. if(otd.flag_return==true)
  887. ucf.return_var=true;
  888. /* if we're a function debugging opcode, set the debuging flag, and
  889. assign the variable name string offset
  890. TODO: Add this to opcodes.txt */
  891. if((ucop._id==0x4D) && (options.game_bg() || options.game_si()))
  892. {
  893. ucf.debugging_info=true;
  894. assert(ucop._params_parsed.size()>=2);
  895. ucf.debugging_offset = ucop._params_parsed[1];
  896. ucf.funcname = ucf._data.find(0x0000)->second;
  897. }
  898. ucf._opcodes.push_back(ucop);
  899. #ifdef DEBUG_READ
  900. cout << std::setw(4) << code_size << "\t" << std::setw(4) << code_offset << "\t" << std::setw(4) << (unsigned int)ucop._offset << "\t" << std::setw(2) << (unsigned int)ucop._id << "\t";
  901. for(unsigned int i=0; i<ucop._params.size(); i++)
  902. cout << std::setw(2) << (unsigned int)ucop._params[i] << ',';
  903. cout << endl;
  904. #endif
  905. }
  906. }
  907. }
  908. void readbin_U8UCFunc(ifstream &f, UCFunc &ucf)
  909. {
  910. }