q3asm.c 21 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. #include "cmdlib.h"
  19. #include "mathlib.h"
  20. #include "qfiles.h"
  21. /* MSVC-ism fix. */
  22. #define atoi(s) strtoul(s,NULL,10)
  23. char outputFilename[MAX_OS_PATH];
  24. // the zero page size is just used for detecting run time faults
  25. #define ZERO_PAGE_SIZE 0 // 256
  26. typedef enum {
  27. OP_UNDEF,
  28. OP_IGNORE,
  29. OP_BREAK,
  30. OP_ENTER,
  31. OP_LEAVE,
  32. OP_CALL,
  33. OP_PUSH,
  34. OP_POP,
  35. OP_CONST,
  36. OP_LOCAL,
  37. OP_JUMP,
  38. //-------------------
  39. OP_EQ,
  40. OP_NE,
  41. OP_LTI,
  42. OP_LEI,
  43. OP_GTI,
  44. OP_GEI,
  45. OP_LTU,
  46. OP_LEU,
  47. OP_GTU,
  48. OP_GEU,
  49. OP_EQF,
  50. OP_NEF,
  51. OP_LTF,
  52. OP_LEF,
  53. OP_GTF,
  54. OP_GEF,
  55. //-------------------
  56. OP_LOAD1,
  57. OP_LOAD2,
  58. OP_LOAD4,
  59. OP_STORE1,
  60. OP_STORE2,
  61. OP_STORE4, // *(stack[top-1]) = stack[yop
  62. OP_ARG,
  63. OP_BLOCK_COPY,
  64. //-------------------
  65. OP_SEX8,
  66. OP_SEX16,
  67. OP_NEGI,
  68. OP_ADD,
  69. OP_SUB,
  70. OP_DIVI,
  71. OP_DIVU,
  72. OP_MODI,
  73. OP_MODU,
  74. OP_MULI,
  75. OP_MULU,
  76. OP_BAND,
  77. OP_BOR,
  78. OP_BXOR,
  79. OP_BCOM,
  80. OP_LSH,
  81. OP_RSHI,
  82. OP_RSHU,
  83. OP_NEGF,
  84. OP_ADDF,
  85. OP_SUBF,
  86. OP_DIVF,
  87. OP_MULF,
  88. OP_CVIF,
  89. OP_CVFI
  90. } opcode_t;
  91. typedef struct {
  92. int imageBytes; // after decompression
  93. int entryPoint;
  94. int stackBase;
  95. int stackSize;
  96. } executableHeader_t;
  97. typedef enum {
  98. CODESEG,
  99. DATASEG, // initialized 32 bit data, will be byte swapped
  100. LITSEG, // strings
  101. BSSSEG, // 0 filled
  102. NUM_SEGMENTS
  103. } segmentName_t;
  104. #define MAX_IMAGE 0x400000
  105. typedef struct {
  106. byte image[MAX_IMAGE];
  107. int imageUsed;
  108. int segmentBase; // only valid on second pass
  109. } segment_t;
  110. typedef struct symbol_s {
  111. struct symbol_s *next;
  112. int hash;
  113. segment_t *segment;
  114. char *name;
  115. int value;
  116. } symbol_t;
  117. segment_t segment[NUM_SEGMENTS];
  118. segment_t *currentSegment;
  119. int passNumber;
  120. int numSymbols;
  121. int errorCount;
  122. symbol_t *symbols;
  123. symbol_t *lastSymbol;
  124. #define MAX_ASM_FILES 256
  125. int numAsmFiles;
  126. char *asmFiles[MAX_ASM_FILES];
  127. char *asmFileNames[MAX_ASM_FILES];
  128. int currentFileIndex;
  129. char *currentFileName;
  130. int currentFileLine;
  131. //int stackSize = 16384;
  132. int stackSize = 0x10000;
  133. // we need to convert arg and ret instructions to
  134. // stores to the local stack frame, so we need to track the
  135. // characteristics of the current functions stack frame
  136. int currentLocals; // bytes of locals needed by this function
  137. int currentArgs; // bytes of largest argument list called from this function
  138. int currentArgOffset; // byte offset in currentArgs to store next arg, reset each call
  139. #define MAX_LINE_LENGTH 1024
  140. char lineBuffer[MAX_LINE_LENGTH];
  141. int lineParseOffset;
  142. char token[MAX_LINE_LENGTH];
  143. int instructionCount;
  144. typedef struct {
  145. char *name;
  146. int opcode;
  147. } sourceOps_t;
  148. sourceOps_t sourceOps[] = {
  149. #include "opstrings.h"
  150. };
  151. #define NUM_SOURCE_OPS ( sizeof( sourceOps ) / sizeof( sourceOps[0] ) )
  152. int opcodesHash[ NUM_SOURCE_OPS ];
  153. /*
  154. =============
  155. HashString
  156. =============
  157. */
  158. int HashString( char *s ) {
  159. int v = 0;
  160. while ( *s ) {
  161. v += *s;
  162. s++;
  163. }
  164. return v;
  165. }
  166. /*
  167. ============
  168. CodeError
  169. ============
  170. */
  171. void CodeError( char *fmt, ... ) {
  172. va_list argptr;
  173. errorCount++;
  174. printf( "%s:%i ", currentFileName, currentFileLine );
  175. va_start( argptr,fmt );
  176. vprintf( fmt,argptr );
  177. va_end( argptr );
  178. }
  179. /*
  180. ============
  181. EmitByte
  182. ============
  183. */
  184. void EmitByte( segment_t *seg, int v ) {
  185. if ( seg->imageUsed >= MAX_IMAGE ) {
  186. Error( "MAX_IMAGE" );
  187. }
  188. seg->image[ seg->imageUsed ] = v;
  189. seg->imageUsed++;
  190. }
  191. /*
  192. ============
  193. EmitInt
  194. ============
  195. */
  196. void EmitInt( segment_t *seg, int v ) {
  197. if ( seg->imageUsed >= MAX_IMAGE - 4) {
  198. Error( "MAX_IMAGE" );
  199. }
  200. seg->image[ seg->imageUsed ] = v & 255;
  201. seg->image[ seg->imageUsed + 1 ] = ( v >> 8 ) & 255;
  202. seg->image[ seg->imageUsed + 2 ] = ( v >> 16 ) & 255;
  203. seg->image[ seg->imageUsed + 3 ] = ( v >> 24 ) & 255;
  204. seg->imageUsed += 4;
  205. }
  206. /*
  207. ============
  208. DefineSymbol
  209. Symbols can only be defined on pass 0
  210. ============
  211. */
  212. void DefineSymbol( char *sym, int value ) {
  213. symbol_t *s, *after;
  214. char expanded[MAX_LINE_LENGTH];
  215. int hash;
  216. if ( passNumber == 1 ) {
  217. return;
  218. }
  219. // TTimo
  220. // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=381
  221. // as a security, bail out if vmMain entry point is not first
  222. if (!Q_stricmp(sym, "vmMain"))
  223. if (value)
  224. Error( "vmMain must be the first symbol in the qvm (got offset %d)\n", value );
  225. // add the file prefix to local symbols to guarantee unique
  226. if ( sym[0] == '$' ) {
  227. sprintf( expanded, "%s_%i", sym, currentFileIndex );
  228. sym = expanded;
  229. }
  230. hash = HashString( sym );
  231. for ( s = symbols ; s ; s = s->next ) {
  232. if ( hash == s->hash && !strcmp( sym, s->name ) ) {
  233. CodeError( "Multiple definitions for %s\n", sym );
  234. return;
  235. }
  236. }
  237. s = malloc( sizeof( *s ) );
  238. s->name = copystring( sym );
  239. s->hash = hash;
  240. s->value = value;
  241. s->segment = currentSegment;
  242. lastSymbol = s; /* for the move-to-lit-segment byteswap hack */
  243. // insert it in order
  244. if ( !symbols || s->value < symbols->value ) {
  245. s->next = symbols;
  246. symbols = s;
  247. return;
  248. }
  249. for ( after = symbols ; after->next && after->next->value < value ; after = after->next ) {
  250. }
  251. s->next = after->next;
  252. after->next = s;
  253. }
  254. /*
  255. ============
  256. LookupSymbol
  257. Symbols can only be evaluated on pass 1
  258. ============
  259. */
  260. int LookupSymbol( char *sym ) {
  261. symbol_t *s;
  262. char expanded[MAX_LINE_LENGTH];
  263. int hash;
  264. if ( passNumber == 0 ) {
  265. return 0;
  266. }
  267. // add the file prefix to local symbols to guarantee unique
  268. if ( sym[0] == '$' ) {
  269. sprintf( expanded, "%s_%i", sym, currentFileIndex );
  270. sym = expanded;
  271. }
  272. hash = HashString( sym );
  273. for ( s = symbols ; s ; s = s->next ) {
  274. if ( hash == s->hash && !strcmp( sym, s->name ) ) {
  275. return s->segment->segmentBase + s->value;
  276. }
  277. }
  278. CodeError( "ERROR: symbol %s undefined\n", sym );
  279. passNumber = 0;
  280. DefineSymbol( sym, 0 ); // so more errors aren't printed
  281. passNumber = 1;
  282. return 0;
  283. }
  284. /*
  285. ==============
  286. ExtractLine
  287. Extracts the next line from the given text block.
  288. If a full line isn't parsed, returns NULL
  289. Otherwise returns the updated parse pointer
  290. ===============
  291. */
  292. char *ExtractLine( char *data ) {
  293. int i;
  294. currentFileLine++;
  295. lineParseOffset = 0;
  296. token[0] = 0;
  297. if ( data[0] == 0 ) {
  298. lineBuffer[0] = 0;
  299. return NULL;
  300. }
  301. for ( i = 0 ; i < MAX_LINE_LENGTH ; i++ ) {
  302. if ( data[i] == 0 || data[i] == '\n' ) {
  303. break;
  304. }
  305. }
  306. if ( i == MAX_LINE_LENGTH ) {
  307. CodeError( "MAX_LINE_LENGTH" );
  308. return data;
  309. }
  310. memcpy( lineBuffer, data, i );
  311. lineBuffer[i] = 0;
  312. data += i;
  313. if ( data[0] == '\n' ) {
  314. data++;
  315. }
  316. return data;
  317. }
  318. /*
  319. ==============
  320. Parse
  321. Parse a token out of linebuffer
  322. ==============
  323. */
  324. qboolean Parse( void ) {
  325. int c;
  326. int len;
  327. len = 0;
  328. token[0] = 0;
  329. // skip whitespace
  330. while ( lineBuffer[ lineParseOffset ] <= ' ' ) {
  331. if ( lineBuffer[ lineParseOffset ] == 0 ) {
  332. return qfalse;
  333. }
  334. lineParseOffset++;
  335. }
  336. // skip ; comments
  337. c = lineBuffer[ lineParseOffset ];
  338. if ( c == ';' ) {
  339. return qfalse;
  340. }
  341. // parse a regular word
  342. do {
  343. token[len] = c;
  344. len++;
  345. lineParseOffset++;
  346. c = lineBuffer[ lineParseOffset ];
  347. } while (c>32);
  348. token[len] = 0;
  349. return qtrue;
  350. }
  351. /*
  352. ==============
  353. ParseValue
  354. ==============
  355. */
  356. int ParseValue( void ) {
  357. Parse();
  358. return atoi( token );
  359. }
  360. /*
  361. ==============
  362. ParseExpression
  363. ==============
  364. */
  365. int ParseExpression(void) {
  366. int i, j;
  367. char sym[MAX_LINE_LENGTH];
  368. int v;
  369. if ( token[0] == '-' ) {
  370. i = 1;
  371. } else {
  372. i = 0;
  373. }
  374. for ( ; i < MAX_LINE_LENGTH ; i++ ) {
  375. if ( token[i] == '+' || token[i] == '-' || token[i] == 0 ) {
  376. break;
  377. }
  378. }
  379. memcpy( sym, token, i );
  380. sym[i] = 0;
  381. if ( ( sym[0] >= '0' && sym[0] <= '9' ) || sym[0] == '-' ) {
  382. v = atoi( sym );
  383. } else {
  384. v = LookupSymbol( sym );
  385. }
  386. // parse add / subtract offsets
  387. while ( token[i] != 0 ) {
  388. for ( j = i + 1 ; j < MAX_LINE_LENGTH ; j++ ) {
  389. if ( token[j] == '+' || token[j] == '-' || token[j] == 0 ) {
  390. break;
  391. }
  392. }
  393. memcpy( sym, token+i+1, j-i-1 );
  394. sym[j-i-1] = 0;
  395. if ( token[i] == '+' ) {
  396. v += atoi( sym );
  397. }
  398. if ( token[i] == '-' ) {
  399. v -= atoi( sym );
  400. }
  401. i = j;
  402. }
  403. return v;
  404. }
  405. /*
  406. ==============
  407. HackToSegment
  408. BIG HACK: I want to put all 32 bit values in the data
  409. segment so they can be byte swapped, and all char data in the lit
  410. segment, but switch jump tables are emited in the lit segment and
  411. initialized strng variables are put in the data segment.
  412. I can change segments here, but I also need to fixup the
  413. label that was just defined
  414. Note that the lit segment is read-write in the VM, so strings
  415. aren't read only as in some architectures.
  416. ==============
  417. */
  418. void HackToSegment( segmentName_t seg ) {
  419. if ( currentSegment == &segment[seg] ) {
  420. return;
  421. }
  422. currentSegment = &segment[seg];
  423. if ( passNumber == 0 ) {
  424. lastSymbol->segment = currentSegment;
  425. lastSymbol->value = currentSegment->imageUsed;
  426. }
  427. }
  428. /*
  429. ==============
  430. AssembleLine
  431. ==============
  432. */
  433. void AssembleLine( void ) {
  434. int v, v2;
  435. int i;
  436. int hash;
  437. Parse();
  438. if ( !token[0] ) {
  439. return;
  440. }
  441. hash = HashString( token );
  442. for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
  443. if ( hash == opcodesHash[i] && !strcmp( token, sourceOps[i].name ) ) {
  444. int opcode;
  445. int expression;
  446. if ( sourceOps[i].opcode == OP_UNDEF ) {
  447. CodeError( "Undefined opcode: %s\n", token );
  448. }
  449. if ( sourceOps[i].opcode == OP_IGNORE ) {
  450. return; // we ignore most conversions
  451. }
  452. // sign extensions need to check next parm
  453. opcode = sourceOps[i].opcode;
  454. if ( opcode == OP_SEX8 ) {
  455. Parse();
  456. if ( token[0] == '1' ) {
  457. opcode = OP_SEX8;
  458. } else if ( token[0] == '2' ) {
  459. opcode = OP_SEX16;
  460. } else {
  461. CodeError( "Bad sign extension: %s\n", token );
  462. return;
  463. }
  464. }
  465. // check for expression
  466. Parse();
  467. if ( token[0] && sourceOps[i].opcode != OP_CVIF
  468. && sourceOps[i].opcode != OP_CVFI ) {
  469. expression = ParseExpression();
  470. // code like this can generate non-dword block copies:
  471. // auto char buf[2] = " ";
  472. // we are just going to round up. This might conceivably
  473. // be incorrect if other initialized chars follow.
  474. if ( opcode == OP_BLOCK_COPY ) {
  475. expression = ( expression + 3 ) & ~3;
  476. }
  477. EmitByte( &segment[CODESEG], opcode );
  478. EmitInt( &segment[CODESEG], expression );
  479. } else {
  480. EmitByte( &segment[CODESEG], opcode );
  481. }
  482. instructionCount++;
  483. return;
  484. }
  485. }
  486. // call instructions reset currentArgOffset
  487. if ( !strncmp( token, "CALL", 4 ) ) {
  488. EmitByte( &segment[CODESEG], OP_CALL );
  489. instructionCount++;
  490. currentArgOffset = 0;
  491. return;
  492. }
  493. // arg is converted to a reversed store
  494. if ( !strncmp( token, "ARG", 3 ) ) {
  495. EmitByte( &segment[CODESEG], OP_ARG );
  496. instructionCount++;
  497. if ( 8 + currentArgOffset >= 256 ) {
  498. CodeError( "currentArgOffset >= 256" );
  499. return;
  500. }
  501. EmitByte( &segment[CODESEG], 8 + currentArgOffset );
  502. currentArgOffset += 4;
  503. return;
  504. }
  505. // ret just leaves something on the op stack
  506. if ( !strncmp( token, "RET", 3 ) ) {
  507. EmitByte( &segment[CODESEG], OP_LEAVE );
  508. instructionCount++;
  509. EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
  510. return;
  511. }
  512. // pop is needed to discard the return value of
  513. // a function
  514. if ( !strncmp( token, "pop", 3 ) ) {
  515. EmitByte( &segment[CODESEG], OP_POP );
  516. instructionCount++;
  517. return;
  518. }
  519. // address of a parameter is converted to OP_LOCAL
  520. if ( !strncmp( token, "ADDRF", 5 ) ) {
  521. instructionCount++;
  522. Parse();
  523. v = ParseExpression();
  524. v = 16 + currentArgs + currentLocals + v;
  525. EmitByte( &segment[CODESEG], OP_LOCAL );
  526. EmitInt( &segment[CODESEG], v );
  527. return;
  528. }
  529. // address of a local is converted to OP_LOCAL
  530. if ( !strncmp( token, "ADDRL", 5 ) ) {
  531. instructionCount++;
  532. Parse();
  533. v = ParseExpression();
  534. v = 8 + currentArgs + v;
  535. EmitByte( &segment[CODESEG], OP_LOCAL );
  536. EmitInt( &segment[CODESEG], v );
  537. return;
  538. }
  539. if ( !strcmp( token, "proc" ) ) {
  540. char name[1024];
  541. Parse(); // function name
  542. strcpy( name, token );
  543. DefineSymbol( token, instructionCount ); // segment[CODESEG].imageUsed );
  544. currentLocals = ParseValue(); // locals
  545. currentLocals = ( currentLocals + 3 ) & ~3;
  546. currentArgs = ParseValue(); // arg marshalling
  547. currentArgs = ( currentArgs + 3 ) & ~3;
  548. if ( 8 + currentLocals + currentArgs >= 32767 ) {
  549. CodeError( "Locals > 32k in %s\n", name );
  550. }
  551. instructionCount++;
  552. EmitByte( &segment[CODESEG], OP_ENTER );
  553. EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
  554. return;
  555. }
  556. if ( !strcmp( token, "endproc" ) ) {
  557. Parse(); // skip the function name
  558. v = ParseValue(); // locals
  559. v2 = ParseValue(); // arg marshalling
  560. // all functions must leave something on the opstack
  561. instructionCount++;
  562. EmitByte( &segment[CODESEG], OP_PUSH );
  563. instructionCount++;
  564. EmitByte( &segment[CODESEG], OP_LEAVE );
  565. EmitInt( &segment[CODESEG], 8 + currentLocals + currentArgs );
  566. return;
  567. }
  568. if ( !strcmp( token, "address" ) ) {
  569. Parse();
  570. v = ParseExpression();
  571. HackToSegment( DATASEG );
  572. EmitInt( currentSegment, v );
  573. return;
  574. }
  575. if ( !strcmp( token, "export" ) ) {
  576. return;
  577. }
  578. if ( !strcmp( token, "import" ) ) {
  579. return;
  580. }
  581. if ( !strcmp( token, "code" ) ) {
  582. currentSegment = &segment[CODESEG];
  583. return;
  584. }
  585. if ( !strcmp( token, "bss" ) ) {
  586. currentSegment = &segment[BSSSEG];
  587. return;
  588. }
  589. if ( !strcmp( token, "data" ) ) {
  590. currentSegment = &segment[DATASEG];
  591. return;
  592. }
  593. if ( !strcmp( token, "lit" ) ) {
  594. currentSegment = &segment[LITSEG];
  595. return;
  596. }
  597. if ( !strcmp( token, "line" ) ) {
  598. return;
  599. }
  600. if ( !strcmp( token, "file" ) ) {
  601. return;
  602. }
  603. if ( !strcmp( token, "equ" ) ) {
  604. char name[1024];
  605. Parse();
  606. strcpy( name, token );
  607. Parse();
  608. DefineSymbol( name, atoi(token) );
  609. return;
  610. }
  611. if ( !strcmp( token, "align" ) ) {
  612. v = ParseValue();
  613. currentSegment->imageUsed = (currentSegment->imageUsed + v - 1 ) & ~( v - 1 );
  614. return;
  615. }
  616. if ( !strcmp( token, "skip" ) ) {
  617. v = ParseValue();
  618. currentSegment->imageUsed += v;
  619. return;
  620. }
  621. if ( !strcmp( token, "byte" ) ) {
  622. v = ParseValue();
  623. v2 = ParseValue();
  624. if ( v == 1 ) {
  625. HackToSegment( LITSEG );
  626. } else if ( v == 4 ) {
  627. HackToSegment( DATASEG );
  628. } else if ( v == 2 ) {
  629. CodeError( "16 bit initialized data not supported" );
  630. }
  631. // emit little endien
  632. for ( i = 0 ; i < v ; i++ ) {
  633. EmitByte( currentSegment, v2 );
  634. v2 >>= 8;
  635. }
  636. return;
  637. }
  638. // code labels are emited as instruction counts, not byte offsets,
  639. // because the physical size of the code will change with
  640. // different run time compilers and we want to minimize the
  641. // size of the required translation table
  642. if ( !strncmp( token, "LABEL", 5 ) ) {
  643. Parse();
  644. if ( currentSegment == &segment[CODESEG] ) {
  645. DefineSymbol( token, instructionCount );
  646. } else {
  647. DefineSymbol( token, currentSegment->imageUsed );
  648. }
  649. return;
  650. }
  651. CodeError( "Unknown token: %s\n", token );
  652. }
  653. /*
  654. ==============
  655. InitTables
  656. ==============
  657. */
  658. void InitTables( void ) {
  659. int i;
  660. for ( i = 0 ; i < NUM_SOURCE_OPS ; i++ ) {
  661. opcodesHash[i] = HashString( sourceOps[i].name );
  662. }
  663. }
  664. /*
  665. ==============
  666. WriteMapFile
  667. ==============
  668. */
  669. void WriteMapFile( void ) {
  670. FILE *f;
  671. symbol_t *s;
  672. char imageName[MAX_OS_PATH];
  673. int seg;
  674. strcpy( imageName, outputFilename );
  675. StripExtension( imageName );
  676. strcat( imageName, ".map" );
  677. printf( "Writing %s...\n", imageName );
  678. f = SafeOpenWrite( imageName );
  679. for ( seg = CODESEG ; seg <= BSSSEG ; seg++ ) {
  680. for ( s = symbols ; s ; s = s->next ) {
  681. if ( s->name[0] == '$' ) {
  682. continue; // skip locals
  683. }
  684. if ( &segment[seg] != s->segment ) {
  685. continue;
  686. }
  687. fprintf( f, "%i %8x %s\n", seg, s->value, s->name );
  688. }
  689. }
  690. fclose( f );
  691. }
  692. /*
  693. ===============
  694. WriteVmFile
  695. ===============
  696. */
  697. void WriteVmFile( void ) {
  698. char imageName[MAX_OS_PATH];
  699. vmHeader_t header;
  700. FILE *f;
  701. printf( "%i total errors\n", errorCount );
  702. strcpy( imageName, outputFilename );
  703. StripExtension( imageName );
  704. strcat( imageName, ".qvm" );
  705. remove( imageName );
  706. printf( "code segment: %7i\n", segment[CODESEG].imageUsed );
  707. printf( "data segment: %7i\n", segment[DATASEG].imageUsed );
  708. printf( "lit segment: %7i\n", segment[LITSEG].imageUsed );
  709. printf( "bss segment: %7i\n", segment[BSSSEG].imageUsed );
  710. printf( "instruction count: %i\n", instructionCount );
  711. if ( errorCount != 0 ) {
  712. printf( "Not writing a file due to errors\n" );
  713. return;
  714. }
  715. header.vmMagic = VM_MAGIC;
  716. header.instructionCount = instructionCount;
  717. header.codeOffset = sizeof( header );
  718. header.codeLength = segment[CODESEG].imageUsed;
  719. header.dataOffset = header.codeOffset + segment[CODESEG].imageUsed;
  720. header.dataLength = segment[DATASEG].imageUsed;
  721. header.litLength = segment[LITSEG].imageUsed;
  722. header.bssLength = segment[BSSSEG].imageUsed;
  723. printf( "Writing to %s\n", imageName );
  724. CreatePath( imageName );
  725. f = SafeOpenWrite( imageName );
  726. SafeWrite( f, &header, sizeof( header ) );
  727. SafeWrite( f, &segment[CODESEG].image, segment[CODESEG].imageUsed );
  728. SafeWrite( f, &segment[DATASEG].image, segment[DATASEG].imageUsed );
  729. SafeWrite( f, &segment[LITSEG].image, segment[LITSEG].imageUsed );
  730. fclose( f );
  731. }
  732. /*
  733. ===============
  734. Assemble
  735. ===============
  736. */
  737. void Assemble( void ) {
  738. int i;
  739. char filename[MAX_OS_PATH];
  740. char *ptr;
  741. printf( "outputFilename: %s\n", outputFilename );
  742. for ( i = 0 ; i < numAsmFiles ; i++ ) {
  743. strcpy( filename, asmFileNames[ i ] );
  744. DefaultExtension( filename, ".asm" );
  745. LoadFile( filename, (void **)&asmFiles[i] );
  746. }
  747. // assemble
  748. for ( passNumber = 0 ; passNumber < 2 ; passNumber++ ) {
  749. segment[LITSEG].segmentBase = segment[DATASEG].imageUsed;
  750. segment[BSSSEG].segmentBase = segment[LITSEG].segmentBase + segment[LITSEG].imageUsed;
  751. for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) {
  752. segment[i].imageUsed = 0;
  753. }
  754. segment[DATASEG].imageUsed = 4; // skip the 0 byte, so NULL pointers are fixed up properly
  755. instructionCount = 0;
  756. for ( i = 0 ; i < numAsmFiles ; i++ ) {
  757. currentFileIndex = i;
  758. currentFileName = asmFileNames[ i ];
  759. currentFileLine = 0;
  760. printf("pass %i: %s\n", passNumber, currentFileName );
  761. ptr = asmFiles[i];
  762. while ( ptr ) {
  763. ptr = ExtractLine( ptr );
  764. AssembleLine();
  765. }
  766. }
  767. // align all segment
  768. for ( i = 0 ; i < NUM_SEGMENTS ; i++ ) {
  769. segment[i].imageUsed = (segment[i].imageUsed + 3) & ~3;
  770. }
  771. }
  772. // reserve the stack in bss
  773. DefineSymbol( "_stackStart", segment[BSSSEG].imageUsed );
  774. segment[BSSSEG].imageUsed += stackSize;
  775. DefineSymbol( "_stackEnd", segment[BSSSEG].imageUsed );
  776. // write the image
  777. WriteVmFile();
  778. // write the map file even if there were errors
  779. WriteMapFile();
  780. }
  781. /*
  782. =============
  783. ParseOptionFile
  784. =============
  785. */
  786. void ParseOptionFile( const char *filename ) {
  787. char expanded[MAX_OS_PATH];
  788. char *text, *text_p;
  789. strcpy( expanded, filename );
  790. DefaultExtension( expanded, ".q3asm" );
  791. LoadFile( expanded, (void **)&text );
  792. if ( !text ) {
  793. return;
  794. }
  795. text_p = text;
  796. while( ( text_p = COM_Parse( text_p ) ) != 0 ) {
  797. if ( !strcmp( com_token, "-o" ) ) {
  798. // allow output override in option file
  799. text_p = COM_Parse( text_p );
  800. if ( text_p ) {
  801. strcpy( outputFilename, com_token );
  802. }
  803. continue;
  804. }
  805. asmFileNames[ numAsmFiles ] = copystring( com_token );
  806. numAsmFiles++;
  807. }
  808. }
  809. /*
  810. ==============
  811. main
  812. ==============
  813. */
  814. int main( int argc, char **argv ) {
  815. int i;
  816. double start, end;
  817. // _chdir( "/quake3/jccode/cgame/lccout" ); // hack for vc profiler
  818. if ( argc < 2 ) {
  819. Error( "usage: q3asm [-o output] <files> or q3asm -f <listfile>\n" );
  820. }
  821. start = I_FloatTime ();
  822. InitTables();
  823. // default filename is "q3asm"
  824. strcpy( outputFilename, "q3asm" );
  825. numAsmFiles = 0;
  826. for ( i = 1 ; i < argc ; i++ ) {
  827. if ( argv[i][0] != '-' ) {
  828. break;
  829. }
  830. if ( !strcmp( argv[i], "-o" ) ) {
  831. if ( i == argc - 1 ) {
  832. Error( "-o must preceed a filename" );
  833. }
  834. strcpy( outputFilename, argv[ i+1 ] );
  835. i++;
  836. continue;
  837. }
  838. if ( !strcmp( argv[i], "-f" ) ) {
  839. if ( i == argc - 1 ) {
  840. Error( "-f must preceed a filename" );
  841. }
  842. ParseOptionFile( argv[ i+1 ] );
  843. i++;
  844. continue;
  845. }
  846. Error( "Unknown option: %s", argv[i] );
  847. }
  848. // the rest of the command line args are asm files
  849. for ( ; i < argc ; i++ ) {
  850. asmFileNames[ numAsmFiles ] = copystring( argv[ i ] );
  851. numAsmFiles++;
  852. }
  853. Assemble();
  854. end = I_FloatTime ();
  855. printf ("%5.0f seconds elapsed\n", end-start);
  856. return 0;
  857. }