fugue_x86.c 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. /*
  2. * Fugue-MIDI to x86 compiler
  3. *
  4. * Copyright (c) 2007 by Martin Rosenau
  5. *
  6. * Fugue is an esoteric programming language that uses
  7. * notes instead of letters as input.
  8. * The input file must be in MIDI file format
  9. */
  10. /*
  11. * Comment added in January 2010:
  12. *
  13. * This program is FREEWARE and you can do with it
  14. * whatever you like.
  15. *
  16. * According the German law nearly all written text
  17. * (even texts like discussions on the "discussion"
  18. * page of Wiki articles) are copyrighted; therefore
  19. * I had to add the copyright notice at the top of
  20. * this file.
  21. */
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <time.h>
  26. typedef struct {
  27. unsigned stack:2;
  28. unsigned time:2;
  29. unsigned push:2;
  30. unsigned obj:3;
  31. unsigned data:2;
  32. unsigned submode:2;
  33. unsigned eofmode:2;
  34. long eofvalue;
  35. } CompOptions;
  36. #define STACK_ERR 1
  37. //#define STACK_WRAP 2
  38. #define STACK_ZERO 3
  39. #define TIME_SECOND 1
  40. #define TIME_FIRST 2
  41. #define TIME_THIRD 3
  42. #define OUTPUT_COFF 1
  43. #define OUTPUT_RAW 2
  44. #define OUTPUT_COFFH 3
  45. #define OUTPUT_COFFC 4
  46. #define OUTPUT_DISASSEMBLE 5
  47. #define DATA_32 1
  48. #define DATA_8 2
  49. #define SUBMODE_NEG 1
  50. #define SUBMODE_POS 2
  51. #define EOFMODE_VALUE 1
  52. #define EOFMODE_STOP 2
  53. #define DEFAULT(x) if(!compoption.x) compoption.x=1
  54. #define ARGCHECK(s,x,y) \
  55. else if(!stricmp(argv[i],s) && !compoption.x) \
  56. compoption.x=y;
  57. #define CMD_POP 1
  58. #define CMD_STACK_ABOVE 2
  59. #define CMD_STACK_BELOW 3
  60. #define CMD_ADD 4
  61. #define CMD_SUBTRACT 5
  62. #define CMD_LOOP_START 6
  63. #define CMD_LOOP_END 7
  64. #define CMD_GETCHAR 8
  65. #define CMD_PUTCHAR 9
  66. #define CMD_PUSH_OFFSET 30
  67. #define CMD_PUSH_MIN 20
  68. #define CMD_PUSH_MAX 40
  69. const char * const disasm_cmds[]={NULL,
  70. "POP","-> PUSH ABOVE","PUSH BELOW <-",
  71. "ADD","SUBTRACT","LOOP START","LOOP END",
  72. "GETCHAR","PUTCHAR"};
  73. /* returns a pointer to the string of the filename src
  74. * with a different file extension.
  75. * The pointer is only valid up to the next call of
  76. * this function but it is allowed to pass the pointer
  77. * as "src" argument to the next call of the function.
  78. * newext must already contain the "." */
  79. const char *flncpy(const char *src,const char *newext)
  80. {
  81. int i,l;
  82. static char dest[1000];
  83. for(i=0,l=(-1);src[i];i++)
  84. {
  85. dest[i]=src[i];
  86. if(src[i]=='.') l=i;
  87. else if(src[i]=='/' || src[i]=='\\' || src[i]==':')
  88. l=(-1);
  89. }
  90. if(l==(-1)) strcat(dest,newext);
  91. else strcpy(&(dest[l]),newext);
  92. return &(dest[0]);
  93. }
  94. /* Load a MIDI track and pre-interpret the data
  95. * in the Fugue way */
  96. int LoadTrack(FILE *f,long **cmds,const CompOptions *options)
  97. {
  98. unsigned char hdr[8],cmd,oldcmd;
  99. int n,c,c2,nnote;
  100. long timep,endpos,tdiff;
  101. /* Read the header */
  102. if(fread(hdr,1,8,f)!=8) hdr[0]=0;
  103. if(memcmp(hdr,"MTrk",4))
  104. {
  105. fprintf(stderr,"MIDI file format error.\n");
  106. return 5;
  107. }
  108. tdiff=(hdr[4]<<24)+(hdr[5]<<16)+(hdr[6]<<8)+hdr[7];
  109. endpos=ftell(f)+tdiff;
  110. *cmds=(long *)malloc(sizeof(long)*(tdiff+50));
  111. /* Read the track's data */
  112. for(cmd=0,timep=0,nnote=0;cmd>0x7F || !cmd;)
  113. {
  114. /* Read the time */
  115. tdiff=0;
  116. do {
  117. c=fgetc(f);
  118. tdiff=(tdiff<<7)|(c&0x7F);
  119. } while(c!=EOF && (c&0x80));
  120. timep+=tdiff;
  121. /* Get the command
  122. * Some compilers do not like fseek(f,-1,SEEK_CUR)
  123. * => We use fseek(f,ftell(f)-1,SEEK_SET) */
  124. if(ftell(f)>=endpos) c=EOF;
  125. else c=fgetc(f);
  126. if(c==EOF) cmd=2;
  127. else if(c<0x80) fseek(f,ftell(f)-1,SEEK_SET);
  128. else
  129. {
  130. oldcmd=cmd;
  131. cmd=c;
  132. }
  133. /* Now interpret the command
  134. * 0x9x: Play a tone */
  135. if(cmd>=0x90 && cmd<=0x9F)
  136. {
  137. c=fgetc(f);
  138. c2=fgetc(f);
  139. if(c2)
  140. {
  141. (*cmds)[2*nnote+2]=timep;
  142. (*cmds)[2*(nnote++)+3]=c;
  143. }
  144. }
  145. /* Three-byte commands we ignore */
  146. else if(cmd>=0x80 && cmd<=0xBF) fseek(f,2,SEEK_CUR);
  147. /* Two-byte commands we ignore */
  148. else if(cmd>=0xC0 && cmd<=0xF0) fgetc(f);
  149. /* Keyboard specific commands */
  150. else if(cmd==0xF0 || cmd==0xF7)
  151. {
  152. c=fgetc(f);
  153. if(c>0) fseek(f,c,SEEK_CUR);
  154. cmd=oldcmd;
  155. }
  156. /* Special commands */
  157. else if(cmd==0xFF)
  158. {
  159. c=fgetc(f);
  160. /* EOF */
  161. if(c==0x2F) cmd=1;
  162. else
  163. {
  164. c=fgetc(f);
  165. if(c>0) fseek(f,c,SEEK_CUR);
  166. cmd=oldcmd;
  167. }
  168. }
  169. /* Oups... */
  170. else cmd=2;
  171. }
  172. if(cmd!=1)
  173. {
  174. fprintf(stderr,"MIDI file format error.\n");
  175. return 5;
  176. }
  177. /* SEEK to the next track */
  178. fseek(f,endpos,SEEK_SET);
  179. /* There are no tones in this voice */
  180. if(!nnote)
  181. {
  182. free(*cmds);
  183. return (-1);
  184. }
  185. /* Pre-interpret the notes */
  186. for(c=0,n=1;n<nnote;n++)
  187. {
  188. c2=(*cmds)[2*n+3]-(*cmds)[2*n+1];
  189. /* PUSH command */
  190. if((c2>=3 && c2<=4) || (c2>=-4 && c2<=-3))
  191. {
  192. if(n+1<nnote)
  193. {
  194. c2=(*cmds)[2*n+5]-(*cmds)[2*n+3];
  195. if(c2>=-10 && c2<=10)
  196. {
  197. if(options->push==TIME_FIRST)
  198. (*cmds)[2*c]=(*cmds)[2*n];
  199. else if(options->push==TIME_THIRD)
  200. (*cmds)[2*c]=(*cmds)[2*n+4];
  201. else (*cmds)[2*c]=(*cmds)[2*n+2];
  202. (*cmds)[2*(c++)+1]=c2+CMD_PUSH_OFFSET;
  203. }
  204. }
  205. /* Skip one tone in every case. */
  206. n++;
  207. }
  208. /* Other commands */
  209. else if(c2>-10 && c2<10)
  210. {
  211. if(!c2) c2=CMD_POP;
  212. else if(c2>=1 && c2<=2) c2=CMD_STACK_ABOVE;
  213. else if(c2>=-2 && c2<=-1) c2=CMD_STACK_BELOW;
  214. else if(c2>=5 && c2<=6) c2=CMD_ADD;
  215. else if(c2>=-6 && c2<=5) c2=CMD_SUBTRACT;
  216. else if(c2==7) c2=CMD_LOOP_START;
  217. else if(c2==-7) c2=CMD_LOOP_END;
  218. else if(c2<0) c2=CMD_GETCHAR;
  219. else c2=CMD_PUTCHAR;
  220. if(options->time==TIME_FIRST)
  221. (*cmds)[2*c]=(*cmds)[2*n];
  222. else (*cmds)[2*c]=(*cmds)[2*n+2];
  223. (*cmds)[2*(c++)+1]=c2;
  224. }
  225. }
  226. (*cmds)[2*c]=(*cmds)[2*c+1]=(-1);
  227. return 0;
  228. }
  229. /* Ensure that an array is big enough */
  230. void ArraySize(void *array,int size,long *max)
  231. {
  232. if(size+100<(*max)) return;
  233. *max=size+2000;
  234. *(void **)array=realloc(*(void **)array,*max);
  235. }
  236. /* The main program */
  237. int main(int argc,char **argv)
  238. {
  239. int i,nerr,options,nvoices,
  240. *vptrs,bps,loopcmd,loopvoice,oldnerr;
  241. long **voices,binlen,maxbin,l,ctime,
  242. *loopstack,*exitptrs,
  243. maxloop,maxexit,nloop,nexit;
  244. const char *outfile,*infile,*funcname;
  245. unsigned char *binary;
  246. CompOptions compoption;
  247. unsigned char hdr[14];
  248. FILE *f;
  249. time_t curtime;
  250. /* Check the input syntax */
  251. outfile=infile=funcname=0;
  252. memset(&compoption,0,sizeof(compoption));
  253. for(i=nerr=1;i<argc && nerr;i++)
  254. {
  255. /* "/o" */
  256. if(!stricmp(argv[i],"/o") && !outfile && i+1<argc)
  257. outfile=argv[++i];
  258. /* "/fn" */
  259. else if(!stricmp(argv[i],"/fn") && !funcname && i+1<argc)
  260. funcname=argv[++i];
  261. /* "/eof" */
  262. else if(!stricmp(argv[i],"/eof") &&
  263. !funcname && i+1<argc && !compoption.eofmode)
  264. {
  265. compoption.eofvalue=atoi(argv[++i]);
  266. compoption.eofmode=EOFMODE_VALUE;
  267. }
  268. /* Other options */
  269. ARGCHECK("/eofstop",eofmode,EOFMODE_STOP)
  270. ARGCHECK("/coff",obj,OUTPUT_COFF)
  271. ARGCHECK("/coffh",obj,OUTPUT_COFFH)
  272. ARGCHECK("/coffc",obj,OUTPUT_COFFC)
  273. ARGCHECK("/dis",obj,OUTPUT_DISASSEMBLE)
  274. ARGCHECK("/raw",obj,OUTPUT_RAW)
  275. ARGCHECK("/8",data,DATA_8)
  276. ARGCHECK("/32",data,DATA_32)
  277. ARGCHECK("/subpos",submode,SUBMODE_POS)
  278. ARGCHECK("/subneg",submode,SUBMODE_NEG)
  279. ARGCHECK("/ostackerr",stack,STACK_ERR)
  280. // ARGCHECK("/ostackwrap",stack,STACK_WRAP)
  281. ARGCHECK("/ostackzero",stack,STACK_ZERO)
  282. ARGCHECK("/t1",time,TIME_FIRST)
  283. ARGCHECK("/t2",time,TIME_SECOND)
  284. ARGCHECK("/p1",push,TIME_FIRST)
  285. ARGCHECK("/p2",push,TIME_SECOND)
  286. ARGCHECK("/p3",push,TIME_THIRD)
  287. /* Must be the input file name */
  288. else if(infile || argv[i][0]=='/') nerr=0;
  289. else infile=argv[i];
  290. }
  291. if(!(infile && nerr))
  292. {
  293. fprintf(stderr,
  294. "\n\nFugue compiler v. 0.9\n"
  295. "Copyright (c) 2007 by Martin Rosenau\n\n"
  296. "Syntax:\n"
  297. " fuguec mid_file [options]\n\n"
  298. "Output options:\n"
  299. " /o output_file_name (name of the output file)\n"
  300. " /coff (generate a COFF file - default)\n"
  301. " /raw (generate a RAW file)\n"
  302. " /coffh (generate a COFF file with header)\n"
  303. " /coffc (generate a COFF file with C wrapper)\n"
  304. " /dis (\"disassemble\" the MIDI file "
  305. "into a CSV table)\n"
  306. " /fn funcname (function name to generate - "
  307. "default: /fn fugue)\n\n"
  308. "Language options:\n"
  309. " /8 (use 8 bit data)\n"
  310. " /32 (use 32 bit data - default)\n"
  311. " /subpos (subtract "
  312. "top_of_stack-second_of_stack)\n"
  313. " /subneg (subtract "
  314. "second_of_stack-top_of_stack - default)\n\n"
  315. "Stack options:\n"
  316. " /ostackerr (don\'t compile if first track "
  317. "wants to read the stack\n"
  318. " of the previous track or the "
  319. "last track wants to read\n"
  320. " the next track\'s stack - default)\n"
  321. " /ostackzero (result is zero in the case "
  322. "mentioned above)\n"
  323. " /ostackwrap (wrap around from the last track "
  324. "to to first one\n"
  325. " NOT SUPPORTED IN THIS VERSION.)\n\n"
  326. "I/O options:\n"
  327. " /eofstop (stop if getchar() returns EOF)\n"
  328. " /eof N (translate EOF to N - "
  329. "default: /eof 0)\n\n"
  330. "Timing options:\n"
  331. " *** Note that these options influence "
  332. "the \"disassembler\", too ! ***\n"
  333. " /t1 (Time of a non-PUSH command is the "
  334. "first of the two notes)\n"
  335. " /t2 (Time of a non-PUSH command is the "
  336. "second of the two\n"
  337. " notes - default)\n"
  338. " /p1 (Time of a PUSH command is the first"
  339. "of three notes)\n"
  340. " /p2 (Time of a PUSH command is the second"
  341. "of three notes - default)\n"
  342. " /p3 (Time of a PUSH command is the third"
  343. "of three notes)\n");
  344. return 1;
  345. }
  346. /* Default values if nothing was specified */
  347. DEFAULT(stack);
  348. DEFAULT(time);
  349. DEFAULT(push);
  350. DEFAULT(obj);
  351. DEFAULT(data);
  352. DEFAULT(submode);
  353. DEFAULT(eofmode);
  354. /* Load the MIDI file:
  355. * Open it and check the header */
  356. f=fopen(infile,"rb");
  357. if(!f)
  358. {
  359. fprintf(stderr,"Could not open %s.\n",infile);
  360. return 2;
  361. }
  362. if(fread(hdr,1,14,f)!=14) hdr[0]=0;
  363. if(memcmp(hdr,"MThd",4))
  364. {
  365. fclose(f);
  366. fprintf(stderr,"File is not a MIDI file.\n");
  367. return 3;
  368. }
  369. fseek(f,8+(hdr[4]<<24)+(hdr[5]<<16)+
  370. (hdr[6]<<8)+hdr[7],SEEK_SET);
  371. if(hdr[8] || hdr[9]!=1)
  372. printf("** WARNING: ** - MIDI file "
  373. "format variant may be unsupported.\n");
  374. bps=(hdr[12]<<8)+hdr[13];
  375. /* Load all voices */
  376. voices=(long **)malloc(sizeof(long *)*((hdr[11]<<8)+hdr[12]));
  377. for(i=nvoices=nerr=0;i<(hdr[10]<<8)+hdr[11];i++)
  378. {
  379. nerr=LoadTrack(f,voices+nvoices,&compoption);
  380. if(nerr==(-1))
  381. { /* No notes in the track */ }
  382. else if(nerr==0) nvoices++;
  383. else
  384. {
  385. fclose(f);
  386. return nerr;
  387. }
  388. }
  389. fclose(f);
  390. /* Here voices[i] contains a pointer to the voice array
  391. * while i=0..(nvoices-1) is the voice (i+1):
  392. * time1 (divide by (double)bps to get
  393. * the time in quaters !)
  394. * command1 (CMD_xxx)
  395. * time2
  396. * command2
  397. * ...
  398. * (-1) (marks EOF) */
  399. /* These are needed for the compiler
  400. * and for the disassembler */
  401. vptrs=(int *)malloc(nvoices*sizeof(int));
  402. memset(vptrs,0,nvoices*sizeof(int));
  403. /* "Disassemble" the Fugue program but
  404. * do not compile it. */
  405. if(compoption.obj==OUTPUT_DISASSEMBLE)
  406. {
  407. /* Open the CSV file */
  408. if(!outfile) outfile=flncpy(infile,".csv");
  409. printf("Writing %s ...\n",outfile);
  410. f=fopen(outfile,"wt");
  411. if(!f)
  412. {
  413. fprintf(stderr,"Could not create %s.\n",outfile);
  414. return 19;
  415. }
  416. /* Write the header */
  417. fprintf(f,"\"Time in quaters\"");
  418. for(i=0;i<nvoices;i++) fprintf(f,";\"Voice %u\"",i+1);
  419. /* Disassemble the program
  420. * The code has been taken and modified from the
  421. * compile code below! */
  422. while(1)
  423. {
  424. /* The last line has not been terminated by
  425. * a new line, yet! */
  426. fputc('\n',f);
  427. /* Find the next note (it is in any voice) */
  428. ctime=(-1);
  429. for(i=0;i<nvoices;i++)
  430. if(ctime==(-1) || (voices[i][vptrs[i]]<ctime))
  431. ctime=voices[i][vptrs[i]];
  432. /* All voices have been done. */
  433. if(ctime==(-1)) break;
  434. /* Print the time */
  435. fprintf(f,"%f",ctime/(double)bps);
  436. /* OK. Perform all commands at this time in
  437. * each voice. */
  438. for(i=nerr=loopcmd=0;i<nvoices;i++)
  439. {
  440. fputc(';',f);
  441. if(ctime==voices[i][vptrs[i]])
  442. {
  443. l=voices[i][vptrs[i]+1];
  444. if(l>=CMD_PUSH_MIN)
  445. fprintf(f,"\"PUSH %i\"",l-CMD_PUSH_OFFSET);
  446. else fprintf(f,"\"%s\"",disasm_cmds[l]);
  447. vptrs[i]+=2;
  448. }
  449. }
  450. }
  451. /* Disassembly is done... */
  452. fclose(f);
  453. return 0;
  454. }
  455. /* Initialize the compiler */
  456. binlen=maxbin=maxloop=maxexit=nloop=nexit=0;
  457. binary=0;
  458. loopstack=exitptrs=0;
  459. /* Code header
  460. * Real stack layout within the file:
  461. * pgetchar (cdecl argument)
  462. * pputchar (cdecl argument)
  463. * Return address
  464. * Original EBP
  465. * Original ESI
  466. * top-of-stack value for a loop command
  467. * voice n stack pointer
  468. * ...
  469. * voice 2 stack pointer
  470. * voice 1 stack pointer
  471. * -> New EBP
  472. * 0 (bottommost value of voice stack is always 0)
  473. * ...
  474. * 0
  475. * 0
  476. * voice n second bottommost stack value
  477. * ...
  478. * voice 1 second bottommost stack value
  479. * voice n third bottommost stack value
  480. * ...
  481. * voice 1 third bottommost stack value
  482. * ...
  483. * -> ESP is decresed when the stack size of a voice
  484. * increses!
  485. */
  486. ArraySize(&binary,100,&maxbin);
  487. /* PUSH EBP
  488. * PUSH ESI
  489. * MOV EAX,ESP
  490. * SUB EAX,4*nvoices+8
  491. * PUSH EAX just to increment the stack */
  492. memcpy(binary,"\x55\x56\x89\xE0\x2D????\x50",10);
  493. l=4*nvoices+8;
  494. memcpy(binary+5,&l,4);
  495. binlen=10;
  496. /* For each voice do:
  497. * PUSH EAX
  498. * SUB EAX,4 */
  499. for(i=0;i<nvoices;i++)
  500. {
  501. memcpy(binary+binlen,"\x50\x83\xE8\4",4);
  502. /* In the last loop we remove the SUB EAX,4 */
  503. binlen+=(i+1==nvoices)?1:4;
  504. }
  505. binary[binlen++]=0x89; /* MOV EBP,ESP */
  506. binary[binlen++]=0xE5;
  507. /* For each voice do:
  508. * PUSH DWORD 0 */
  509. for(i=0;i<nvoices;i++)
  510. {
  511. binary[binlen++]=0x6A;
  512. binary[binlen++]=0;
  513. }
  514. /* OK. Now compile the program */
  515. while(1)
  516. {
  517. /* Find the next note (it is in any voice) */
  518. ctime=(-1);
  519. for(i=0;i<nvoices;i++)
  520. if(ctime==(-1) || (voices[i][vptrs[i]]<ctime))
  521. ctime=voices[i][vptrs[i]];
  522. /* All voices have been done. */
  523. if(ctime==(-1)) break;
  524. /* OK. Perform all commands at this time in
  525. * each voice.
  526. * Commands are executed from voice 1 to
  527. * voice n when they are executed "the same
  528. * time".
  529. * This is done to handle the "push value
  530. * from voice above" command:
  531. * nerr=0: This is voice 1 (i=0)
  532. * nerr=1: The stack of the voice above
  533. * has not been modified and ESI has
  534. * nothing to do with it.
  535. * // nerr=2: ESI points to the old ToS value
  536. * // of the voice above (old=before
  537. * // executing the command)
  538. * nerr=3: ESI+4*nvoices points to the old ToS ...
  539. * nerr=4: ESI-4*nvoices points to the old ToS ...
  540. * nerr=5: EAX contains the old ToS value */
  541. for(i=nerr=loopcmd=0;i<nvoices;i++)
  542. {
  543. if(ctime==voices[i][vptrs[i]])
  544. {
  545. oldnerr=nerr;
  546. /* Only check out what the stack will do
  547. * Only in the case of CMD_STACK_ABOVE
  548. * we may need to load the ToS because
  549. * ESI will be overwritten in the
  550. * next command... */
  551. switch(voices[i][vptrs[i]+1])
  552. {
  553. case CMD_ADD:
  554. case CMD_SUBTRACT:
  555. nerr=5;
  556. break;
  557. case CMD_POP:
  558. case CMD_PUTCHAR:
  559. nerr=4;
  560. break;
  561. case CMD_LOOP_START:
  562. case CMD_LOOP_END:
  563. nerr=1;
  564. break;
  565. case CMD_STACK_ABOVE:
  566. if(oldnerr==3 || oldnerr==4)
  567. {
  568. ArraySize(&binary,100+binlen,&maxbin);
  569. /* MOV EAX,[ESI +/- 4] */
  570. binary[binlen++]=0x8B;
  571. binary[binlen++]=0x86;
  572. l=((oldnerr==3)?4:(-4))*nvoices;
  573. memcpy(binary+binlen,&l,4);
  574. binlen+=4;
  575. oldnerr=5;
  576. }
  577. nerr=3;
  578. break;
  579. default:
  580. nerr=3;
  581. }
  582. /* Load the voice stack pointer:
  583. * MOV ESI,[EBP+4*voice]
  584. * This is not needed for loop
  585. * commands but the array is already
  586. * resized! */
  587. ArraySize(&binary,100+binlen,&maxbin);
  588. if(nerr!=1)
  589. {
  590. l=4*i;
  591. binary[binlen++]=0x8B;
  592. binary[binlen++]=0xB5;
  593. memcpy(binary+binlen,&l,4);
  594. binlen+=4;
  595. }
  596. /* Case PUSH: modify the voice
  597. * stack pointer and the stack
  598. * pointer if necessary
  599. * SUB ESI,4*nvoices
  600. * CMP ESI,ESP
  601. * JAE dontdo
  602. * MOV ESP,ESI
  603. * dontdo: */
  604. if(nerr==3)
  605. {
  606. memcpy(binary+binlen,
  607. "\x81\xee????\x39\xe6\x73\2\x89\xf4",12);
  608. l=4*nvoices;
  609. memcpy(binary+binlen+2,&l,4);
  610. binlen+=12;
  611. }
  612. /* The actual command */
  613. switch(voices[i][vptrs[i]+1])
  614. {
  615. case CMD_POP:
  616. /* Nothing must be done here ! */
  617. break;
  618. case CMD_STACK_ABOVE:
  619. if(oldnerr==1)
  620. {
  621. /* MOV EAX,[EBP+4*(voice-1)]
  622. * MOV EAX,[EAX]
  623. * MOV [ESI],EAX */
  624. memcpy(binary+binlen,
  625. "\x8B\x85????\x8B\0\x89\6",10);
  626. l=4*(i-1);
  627. memcpy(binary+binlen+2,&l,4);
  628. binlen+=10;
  629. }
  630. else if(oldnerr==5)
  631. {
  632. /* MOV [ESI],EAX */
  633. binary[binlen++]=0x89;
  634. binary[binlen++]=6;
  635. }
  636. else if(compoption.stack==STACK_ZERO)
  637. {
  638. /* MOV DWORD PTR [ESI],0 */
  639. memcpy(binary+binlen,"\xC7\6\0\0\0\0",6);
  640. binlen+=6;
  641. }
  642. else
  643. {
  644. fprintf(stderr,"Time=%f quarters:\n"
  645. " The first voice wants to read the "
  646. "stack of the voice above.\n"
  647. " This is not allowed in "
  648. "/ostackerr mode.",ctime/(double)bps);
  649. return 22;
  650. }
  651. break;
  652. case CMD_STACK_BELOW:
  653. if(i+1<nvoices)
  654. {
  655. /* MOV EAX,[EBP+4*(voice+1)]
  656. * MOV EAX,[EAX]
  657. * MOV [ESI],EAX */
  658. memcpy(binary+binlen,
  659. "\x8B\x85????\x8B\0\x89\6",10);
  660. l=4*(i+1);
  661. memcpy(binary+binlen+2,&l,4);
  662. binlen+=10;
  663. }
  664. else if(compoption.stack==STACK_ZERO)
  665. {
  666. /* MOV DWORD PTR [ESI],0 */
  667. memcpy(binary+binlen,"\xC7\6\0\0\0\0",6);
  668. binlen+=6;
  669. }
  670. else
  671. {
  672. fprintf(stderr,"Time=%f quarters:\n"
  673. " The last voice wants to read the "
  674. "stack of the voice below.\n"
  675. " This is not allowed in "
  676. "/ostackerr mode.",ctime/(double)bps);
  677. return 23;
  678. }
  679. break;
  680. case CMD_ADD:
  681. /* MOV EAX,[ESI]
  682. * ; check if the stack has at
  683. * ; least 2 elements
  684. * ; do nothing if not
  685. * ADD ESI,8*nvoices
  686. * CMP ESI,EBP
  687. * JAE end_of_this
  688. * ; Do the action
  689. * SUB ESI,4*nvoices
  690. * MOV [EBP+4*voice],ESI
  691. * MOV [ESI],EAX */
  692. memcpy(binary+binlen,"\x8B\6\x81\xC6????"
  693. "\x39\xEE\x73\xE\x81\xEE????"
  694. "\x89\xB5????\1\6",26);
  695. l=8*nvoices;
  696. memcpy(binary+binlen+4,&l,4);
  697. l=4*nvoices;
  698. memcpy(binary+binlen+14,&l,4);
  699. l=4*i;
  700. memcpy(binary+binlen+20,&l,4);
  701. binlen+=26;
  702. break;
  703. case CMD_SUBTRACT:
  704. if(compoption.submode==SUBMODE_NEG)
  705. {
  706. /* MOV EAX,[ESI]
  707. * ; This is only there for the
  708. * ; case that there is exactly
  709. * ; one element on the stack.
  710. * NEG DWORD PTR [ESI]
  711. * ; check if the stack has at
  712. * ; least 2 elements
  713. * ; do nothing if not
  714. * ADD ESI,8*nvoices
  715. * CMP ESI,EBP
  716. * JAE end_of_this
  717. * ; Do the action
  718. * SUB ESI,4*nvoices
  719. * MOV [EBP+4*voice],ESI
  720. * SUB [ESI],EAX */
  721. memcpy(binary+binlen,"\x8B\6\xF7\x1E"
  722. "\x81\xC6????\x39\xEE\x73\xE"
  723. "\x81\xEE????\x89\xB5????\x29\6",28);
  724. l=8*nvoices;
  725. memcpy(binary+binlen+6,&l,4);
  726. l=4*nvoices;
  727. memcpy(binary+binlen+16,&l,4);
  728. l=4*i;
  729. memcpy(binary+binlen+22,&l,4);
  730. binlen+=28;
  731. break;
  732. }
  733. else
  734. {
  735. /* MOV EAX,[ESI]
  736. * ; check if the stack has at
  737. * ; least 2 elements
  738. * ; do nothing if not
  739. * ADD ESI,8*nvoices
  740. * CMP ESI,EBP
  741. * JAE end_of_this
  742. * ; Do the action
  743. * SUB ESI,4*nvoices
  744. * MOV [EBP+4*voice],ESI
  745. * SUB [ESI],EAX
  746. * NEG DWORD PTR [ESI] */
  747. memcpy(binary+binlen,"\x8B\6\x81\xC6????"
  748. "\x39\xEE\x73\x10\x81\xEE????"
  749. "\x89\xB5????\x29\6\xF7\x1E",28);
  750. l=8*nvoices;
  751. memcpy(binary+binlen+4,&l,4);
  752. l=4*nvoices;
  753. memcpy(binary+binlen+14,&l,4);
  754. l=4*i;
  755. memcpy(binary+binlen+20,&l,4);
  756. binlen+=28;
  757. break;
  758. }
  759. break;
  760. case CMD_LOOP_START:
  761. case CMD_LOOP_END:
  762. if(loopcmd)
  763. {
  764. fprintf(stderr,"Time=%f quarters:\n"
  765. " Two loop commands in voices %u"
  766. " and %u at the same time.\n",
  767. ctime/(double)bps,loopvoice+1,i+1);
  768. return 20;
  769. }
  770. loopcmd=voices[i][vptrs[i]+1];
  771. loopvoice=i;
  772. break;
  773. case CMD_GETCHAR:
  774. /* CALL [EBP+4*nvoices+20]
  775. * CMP EAX,(-1) */
  776. memcpy(binary+binlen,
  777. "\xFF\x95????\x83\xF8\xFF",9);
  778. l=4*nvoices+20;
  779. memcpy(binary+binlen+2,&l,4);
  780. binlen+=9;
  781. if(compoption.eofmode==EOFMODE_STOP)
  782. {
  783. /* JE end_of_file
  784. * The last 4 bytes are set later! */
  785. binary[binlen++]=0xF;
  786. binary[binlen++]=0x84;
  787. ArraySize(&exitptrs,
  788. sizeof(long)*(nexit+1),&maxexit);
  789. exitptrs[nexit++]=binlen;
  790. binlen+=4;
  791. }
  792. else
  793. {
  794. /* JNE +5
  795. * MOV EAX,value */
  796. binary[binlen++]=0x75;
  797. binary[binlen++]=5;
  798. binary[binlen++]=0xB8;
  799. memcpy(binary+binlen,&(compoption.eofvalue),4);
  800. binlen+=4;
  801. }
  802. /* MOV [ESI],EAX */
  803. binary[binlen++]=0x89;
  804. binary[binlen++]=6;
  805. break;
  806. case CMD_PUTCHAR:
  807. /* MOV EAX,[ESI] */
  808. binary[binlen++]=0x8B;
  809. binary[binlen++]=6;
  810. if(compoption.data==DATA_8)
  811. {
  812. /* MOVZX EAX,AL */
  813. binary[binlen++]=0xF;
  814. binary[binlen++]=0xB6;
  815. binary[binlen++]=0xC0;
  816. }
  817. binary[binlen++]=0x50; /* PUSH EAX */
  818. /* CALL [EBP+4*nvoices+16] */
  819. binary[binlen++]=0xFF;
  820. binary[binlen++]=0x95;
  821. l=4*nvoices+16;
  822. memcpy(binary+binlen,&l,4);
  823. binlen+=4;
  824. /* POP EAX just to restore the stack */
  825. binary[binlen++]=0x58;
  826. break;
  827. default: /* Push a value */
  828. l=voices[i][vptrs[i]+1]-CMD_PUSH_OFFSET;
  829. /* MOV DWORD PTR [ESI],pused_value */
  830. binary[binlen++]=0xC7;
  831. binary[binlen++]=0x06;
  832. memcpy(binary+binlen,&l,4);
  833. binlen+=4;
  834. }
  835. /* Update the voice's stack pointer */
  836. if(nerr==4)
  837. {
  838. /* ADD ESI,4*nvoices
  839. * CMP ESI,EBP
  840. * JAE +6 (skip the MOV [EBP+x],ESI) */
  841. memcpy(binary+binlen,
  842. "\x81\xC6????\x39\xEE\x73\6",10);
  843. l=4*nvoices;
  844. memcpy(binary+binlen+2,&l,4);
  845. binlen+=10;
  846. }
  847. if(nerr==3 || nerr==4)
  848. {
  849. /* MOV [EBP+4*voice],ESI */
  850. l=4*i;
  851. binary[binlen++]=0x89;
  852. binary[binlen++]=0xB5;
  853. memcpy(binary+binlen,&l,4);
  854. binlen+=4;
  855. }
  856. vptrs[i]+=2;
  857. }
  858. else nerr=1;
  859. /* Loop_start and Loop_end commands
  860. * are actually handled here! */
  861. if(loopcmd==CMD_LOOP_START)
  862. {
  863. /* MOV ESI,[EBP+4*loopvoice]
  864. * JMP xxx (will be updated later) */
  865. memcpy(binary+binlen,"\x8B\xB5????\xE9",7);
  866. l=4*loopvoice;
  867. memcpy(binary+binlen+2,&l,4);
  868. /* Remember the loop's position */
  869. ArraySize(&loopstack,
  870. sizeof(long)*(3*nloop+3),&maxloop);
  871. loopstack[3*nloop]=ctime;
  872. loopstack[3*nloop+1]=loopvoice;
  873. loopstack[3*(nloop++)+2]=binlen+7;
  874. binlen+=11;
  875. }
  876. else if(loopcmd==CMD_LOOP_END)
  877. {
  878. if(nloop<1)
  879. {
  880. fprintf(stderr,"Time=%f quarters:\n"
  881. " Loop End command found outside "
  882. "a loop.\n",
  883. ctime/(double)bps,loopvoice+1,i+1);
  884. return 22;
  885. }
  886. /* MOV ESI,[EBP+4*loopvoice] */
  887. binary[binlen++]=0x8B;
  888. binary[binlen++]=0xB5;
  889. l=4*loopvoice;
  890. memcpy(binary+binlen,&l,4);
  891. binlen+=4;
  892. /* Update the "JMP" instruction of the
  893. * loop begin command - it jumps here */
  894. l=binlen-loopstack[3*(--nloop)+2]-4;
  895. memcpy(binary+loopstack[3*nloop+2],&l,4);
  896. /* CMP BYTE/WORD PTR [ESI],0 */
  897. binary[binlen++]=(compoption.data==DATA_8)?0x80:0x83;
  898. binary[binlen++]=0x3E;
  899. binary[binlen++]=0;
  900. /* JNE xxx */
  901. binary[binlen++]=0xF;
  902. binary[binlen++]=0x85;
  903. l=loopstack[3*nloop+2]-binlen;
  904. memcpy(binary+binlen,&l,4);
  905. binlen+=4;
  906. }
  907. }
  908. }
  909. /* Open loops ? */
  910. if(nloop)
  911. {
  912. fprintf(stderr,"ERROR: The following "
  913. "loop(s) is/are not terminated:\n");
  914. for(i=0;i<nloop;i++)
  915. fprintf(stderr,
  916. " Starting time: %f quaters, "
  917. "starting in voice %u\n",
  918. loopstack[3*i]/(double)bps,
  919. loopstack[3*i+1]+1);
  920. return 21;
  921. }
  922. /* Code trailer */
  923. for(i=0;i<nexit;i++)
  924. {
  925. /* Update the "JE" instructions for /EOFSTOP */
  926. l=binlen-exitptrs[i]-4;
  927. memcpy(binary+exitptrs[i],&l,4);
  928. }
  929. ArraySize(&binary,100+binlen,&maxbin);
  930. binary[binlen++]=0x81; /* ADD EBP,4*nvoices+4 */
  931. binary[binlen++]=0xC5;
  932. l=4*nvoices+4;
  933. memcpy(binary+binlen,&l,4);
  934. binlen+=4;
  935. /* MOV ESP,EBP
  936. * POP ESI
  937. * POP EBP
  938. * RET */
  939. memcpy(binary+binlen,"\x89\xEC\x5E\x5D\xC3",5);
  940. binlen+=5;
  941. /* Alignment */
  942. while(binlen&7) binary[binlen++]=0x90; /* NOP */
  943. /* Print some statistics */
  944. printf("Compiling complete. Code size: %u bytes.\n",binlen);
  945. /* Build the output file name and open the output file */
  946. if(!funcname) funcname="fugue";
  947. if(!outfile) outfile=flncpy(infile,
  948. (compoption.obj==OUTPUT_RAW)?".bin":".obj");
  949. printf("Writing %s ...\n",outfile);
  950. f=fopen(outfile,"wb");
  951. if(!f)
  952. {
  953. fprintf(stderr,"Could not write %s.\n",outfile);
  954. return 10;
  955. }
  956. /* Write the COFF file stuff (not including the raw data) */
  957. if(compoption.obj!=OUTPUT_RAW)
  958. {
  959. fwrite("\x4C\1\1\0",1,4,f); /* HDR, 1 section */
  960. time(&curtime);
  961. fwrite(&curtime,1,4,f);
  962. /* Symtab following first section entry;
  963. * lenght = 1 symbol */
  964. fwrite("\x3C\0\0\0\1\0\0\0\0\0\0\0"
  965. /* Only section entry: ".text" */
  966. ".text\0\0\0\0\0\0\0\0\0\0\0",1,28,f);
  967. l=(strlen(funcname)+0x3C+18+9)&~3;
  968. fwrite(&binlen,1,4,f); /* Length of section */
  969. fwrite(&l,1,4,f); /* Pos. of section */
  970. /* No relocations, no debug symbols, Code */
  971. fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\x20\0\0\x60"
  972. /* Symbol 1: Pos=4 in name table
  973. * Offset = 0, Section = 1, Global */
  974. "\0\0\0\0\4\0\0\0\0\0\0\0\1\0\0\0\2\0",1,34,f);
  975. /* Symbol name table */
  976. l=(8+strlen(funcname))&~3;
  977. fwrite(&l,1,4,f);
  978. fputc('_',f);
  979. fwrite(funcname,1,strlen(funcname)+1,f);
  980. l=strlen(funcname)+4;
  981. while((l++)&3) fputc(0,f);
  982. }
  983. /* Write the binary data */
  984. fwrite(binary,1,binlen,f);
  985. fclose(f);
  986. /* Build the header file */
  987. if(compoption.obj==OUTPUT_COFFH || compoption.obj==OUTPUT_COFFC)
  988. {
  989. outfile=flncpy(outfile,
  990. (compoption.obj==OUTPUT_COFFH)?".h":"_wrp.c");
  991. printf("Writing %s ...\n",outfile);
  992. f=fopen(outfile,"wt");
  993. if(!f)
  994. {
  995. fprintf(stderr,"Could not write %s.\n",outfile);
  996. return 11;
  997. }
  998. fprintf(f,"/*\n"
  999. " * This file belongs to an object file\n"
  1000. " * generated by the Fugue compiler.\n"
  1001. " */\n\n");
  1002. if(compoption.obj==OUTPUT_COFFC)
  1003. fprintf(f,"#include <stdio.h>\n\n");
  1004. fprintf(f,"extern void __cdecl %s(\n"
  1005. " /* pputchar\'s retuned value is ignored ! */\n"
  1006. " int (__cdecl *pputchar)(int),\n"
  1007. " int (__cdecl *pgetchar)(void));\n\n",funcname);
  1008. if(compoption.obj==OUTPUT_COFFC)
  1009. fprintf(f,
  1010. "\n/* In cygwin getchar() and "
  1011. "putchar() do __NOT__ work!!\n"
  1012. " * They are #defines there and not "
  1013. "__cdecl functions! */\n"
  1014. "int __cdecl my_putchar(int c)\n"
  1015. "{\n return putchar(c);\n}\n"
  1016. "int __cdecl my_getchar(void)\n"
  1017. "{\n return getchar();\n}\n\n"
  1018. "int main(int argc,char **argv)\n"
  1019. "{\n %s(my_putchar,my_getchar);\n"
  1020. " fflush(stdout);\n"
  1021. " return 0;\n}\n",funcname);
  1022. fclose(f);
  1023. }
  1024. return 0;
  1025. }