PageRenderTime 94ms CodeModel.GetById 53ms RepoModel.GetById 2ms app.codeStats 0ms

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

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