pass_2.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. {
  2. $Id$
  3. Copyright (c) 1993-98 by Florian Klaempfl
  4. This unit handles the codegeneration pass
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  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 General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. {$ifdef TP}
  19. {$E+,F+,N+}
  20. {$endif}
  21. unit pass_2;
  22. interface
  23. uses
  24. tree;
  25. { produces assembler for the expression in variable p }
  26. { and produces an assembler node at the end }
  27. procedure generatecode(var p : ptree);
  28. { produces the actual code }
  29. function do_secondpass(var p : ptree) : boolean;
  30. procedure secondpass(var p : ptree);
  31. implementation
  32. uses
  33. globtype,systems,
  34. cobjects,comphook,verbose,globals,files,
  35. symconst,symtable,types,aasm,scanner,
  36. pass_1,hcodegen,temp_gen,cpubase,cpuasm
  37. {$ifdef GDB}
  38. ,gdb
  39. {$endif}
  40. {$ifdef i386}
  41. ,tgeni386,cgai386
  42. ,cg386con,cg386mat,cg386cnv,cg386set,cg386add
  43. ,cg386mem,cg386cal,cg386ld,cg386flw,cg386inl
  44. {$endif}
  45. {$ifdef m68k}
  46. ,tgen68k,cga68k
  47. ,cg68kcon,cg68kmat,cg68kcnv,cg68kset,cg68kadd
  48. ,cg68kmem,cg68kcal,cg68kld,cg68kflw,cg68kinl
  49. {$endif}
  50. ;
  51. {*****************************************************************************
  52. SecondPass
  53. *****************************************************************************}
  54. type
  55. secondpassproc = procedure(var p : ptree);
  56. procedure secondnothing(var p : ptree);
  57. begin
  58. end;
  59. procedure seconderror(var p : ptree);
  60. begin
  61. p^.error:=true;
  62. codegenerror:=true;
  63. end;
  64. procedure secondstatement(var p : ptree);
  65. var
  66. hp : ptree;
  67. begin
  68. hp:=p;
  69. while assigned(hp) do
  70. begin
  71. if assigned(hp^.right) then
  72. begin
  73. cleartempgen;
  74. {!!!!!!
  75. oldrl:=temptoremove;
  76. temptoremove:=new(plinkedlist,init);
  77. }
  78. secondpass(hp^.right);
  79. { !!!!!!!
  80. some temporary data which can't be released elsewhere
  81. removetemps(exprasmlist,temptoremove);
  82. dispose(temptoremove,done);
  83. temptoremove:=oldrl;
  84. }
  85. end;
  86. hp:=hp^.left;
  87. end;
  88. end;
  89. procedure secondblockn(var p : ptree);
  90. begin
  91. { do second pass on left node }
  92. if assigned(p^.left) then
  93. secondpass(p^.left);
  94. end;
  95. procedure secondasm(var p : ptree);
  96. var
  97. hp,hp2 : pai;
  98. localfixup,parafixup,
  99. i : longint;
  100. r : preference;
  101. begin
  102. if (pocall_inline in aktprocsym^.definition^.proccalloptions) then
  103. begin
  104. localfixup:=aktprocsym^.definition^.localst^.address_fixup;
  105. parafixup:=aktprocsym^.definition^.parast^.address_fixup;
  106. hp:=pai(p^.p_asm^.first);
  107. while assigned(hp) do
  108. begin
  109. hp2:=pai(hp^.getcopy);
  110. case hp2^.typ of
  111. ait_instruction :
  112. begin
  113. { fixup the references }
  114. for i:=1 to pai386(hp2)^.ops do
  115. if pai386(hp2)^.oper[i-1].typ=top_ref then
  116. begin
  117. r:=pai386(hp2)^.oper[i-1].ref;
  118. case r^.options of
  119. ref_parafixup :
  120. r^.offsetfixup:=parafixup;
  121. ref_localfixup :
  122. r^.offsetfixup:=localfixup;
  123. end;
  124. end;
  125. exprasmlist^.concat(hp2);
  126. end;
  127. ait_marker :
  128. begin
  129. { it's not an assembler block anymore }
  130. if not(pai_marker(hp2)^.kind in [AsmBlockStart, AsmBlockEnd]) then
  131. exprasmlist^.concat(hp2);
  132. end;
  133. else
  134. exprasmlist^.concat(hp2);
  135. end;
  136. hp:=pai(hp^.next);
  137. end
  138. end
  139. else
  140. exprasmlist^.concatlist(p^.p_asm);
  141. if not p^.object_preserved then
  142. begin
  143. {$ifdef i386}
  144. maybe_loadesi;
  145. {$endif}
  146. {$ifdef m68k}
  147. maybe_loada5;
  148. {$endif}
  149. end;
  150. end;
  151. procedure secondpass(var p : ptree);
  152. const
  153. procedures : array[ttreetyp] of secondpassproc =
  154. (secondadd, {addn}
  155. secondadd, {muln}
  156. secondadd, {subn}
  157. secondmoddiv, {divn}
  158. secondadd, {symdifn}
  159. secondmoddiv, {modn}
  160. secondassignment, {assignn}
  161. secondload, {loadn}
  162. secondnothing, {range}
  163. secondadd, {ltn}
  164. secondadd, {lten}
  165. secondadd, {gtn}
  166. secondadd, {gten}
  167. secondadd, {equaln}
  168. secondadd, {unequaln}
  169. secondin, {inn}
  170. secondadd, {orn}
  171. secondadd, {xorn}
  172. secondshlshr, {shrn}
  173. secondshlshr, {shln}
  174. secondadd, {slashn}
  175. secondadd, {andn}
  176. secondsubscriptn, {subscriptn}
  177. secondderef, {derefn}
  178. secondaddr, {addrn}
  179. seconddoubleaddr, {doubleaddrn}
  180. secondordconst, {ordconstn}
  181. secondtypeconv, {typeconvn}
  182. secondcalln, {calln}
  183. secondnothing, {callparan}
  184. secondrealconst, {realconstn}
  185. secondfixconst, {fixconstn}
  186. secondumminus, {umminusn}
  187. secondasm, {asmn}
  188. secondvecn, {vecn}
  189. secondstringconst, {stringconstn}
  190. secondfuncret, {funcretn}
  191. secondselfn, {selfn}
  192. secondnot, {notn}
  193. secondinline, {inlinen}
  194. secondniln, {niln}
  195. seconderror, {errorn}
  196. secondnothing, {typen}
  197. secondhnewn, {hnewn}
  198. secondhdisposen, {hdisposen}
  199. secondnewn, {newn}
  200. secondsimplenewdispose, {simpledisposen}
  201. secondsetelement, {setelementn}
  202. secondsetconst, {setconstn}
  203. secondblockn, {blockn}
  204. secondstatement, {statementn}
  205. secondnothing, {loopn}
  206. secondifn, {ifn}
  207. secondbreakn, {breakn}
  208. secondcontinuen, {continuen}
  209. second_while_repeatn, {repeatn}
  210. second_while_repeatn, {whilen}
  211. secondfor, {forn}
  212. secondexitn, {exitn}
  213. secondwith, {withn}
  214. secondcase, {casen}
  215. secondlabel, {labeln}
  216. secondgoto, {goton}
  217. secondsimplenewdispose, {simplenewn}
  218. secondtryexcept, {tryexceptn}
  219. secondraise, {raisen}
  220. secondnothing, {switchesn}
  221. secondtryfinally, {tryfinallyn}
  222. secondon, {onn}
  223. secondis, {isn}
  224. secondas, {asn}
  225. seconderror, {caretn}
  226. secondfail, {failn}
  227. secondadd, {starstarn}
  228. secondprocinline, {procinlinen}
  229. secondarrayconstruct, {arrayconstructn}
  230. secondnothing, {arrayconstructrangen}
  231. secondnothing, {nothingn}
  232. secondloadvmt {loadvmtn}
  233. );
  234. var
  235. oldcodegenerror : boolean;
  236. oldlocalswitches : tlocalswitches;
  237. oldpos : tfileposinfo;
  238. begin
  239. if not(p^.error) then
  240. begin
  241. oldcodegenerror:=codegenerror;
  242. oldlocalswitches:=aktlocalswitches;
  243. oldpos:=aktfilepos;
  244. aktfilepos:=p^.fileinfo;
  245. aktlocalswitches:=p^.localswitches;
  246. codegenerror:=false;
  247. procedures[p^.treetype](p);
  248. p^.error:=codegenerror;
  249. codegenerror:=codegenerror or oldcodegenerror;
  250. aktlocalswitches:=oldlocalswitches;
  251. aktfilepos:=oldpos;
  252. end
  253. else
  254. codegenerror:=true;
  255. end;
  256. function do_secondpass(var p : ptree) : boolean;
  257. begin
  258. codegenerror:=false;
  259. if not(p^.error) then
  260. secondpass(p);
  261. do_secondpass:=codegenerror;
  262. end;
  263. var
  264. regvars : array[1..maxvarregs] of pvarsym;
  265. regvars_para : array[1..maxvarregs] of boolean;
  266. regvars_refs : array[1..maxvarregs] of longint;
  267. parasym : boolean;
  268. procedure searchregvars(p : pnamedindexobject);
  269. var
  270. i,j,k : longint;
  271. begin
  272. if (psym(p)^.typ=varsym) and (vo_regable in pvarsym(p)^.varoptions) then
  273. begin
  274. { walk through all momentary register variables }
  275. for i:=1 to maxvarregs do
  276. begin
  277. { free register ? }
  278. if regvars[i]=nil then
  279. begin
  280. regvars[i]:=pvarsym(p);
  281. regvars_para[i]:=parasym;
  282. break;
  283. end;
  284. { else throw out a variable ? }
  285. j:=pvarsym(p)^.refs;
  286. { parameter get a less value }
  287. if parasym then
  288. begin
  289. if cs_littlesize in aktglobalswitches then
  290. dec(j,1)
  291. else
  292. dec(j,100);
  293. end;
  294. if (j>regvars_refs[i]) and (j>0) then
  295. begin
  296. for k:=maxvarregs-1 downto i do
  297. begin
  298. regvars[k+1]:=regvars[k];
  299. regvars_para[k+1]:=regvars_para[k];
  300. end;
  301. { calc the new refs
  302. pvarsym(p)^.refs:=j; }
  303. regvars[i]:=pvarsym(p);
  304. regvars_para[i]:=parasym;
  305. regvars_refs[i]:=j;
  306. break;
  307. end;
  308. end;
  309. end;
  310. end;
  311. procedure generatecode(var p : ptree);
  312. var
  313. i : longint;
  314. regsize : topsize;
  315. hr : preference;
  316. label
  317. nextreg;
  318. begin
  319. {!!!!!!!! temptoremove:=nil; }
  320. cleartempgen;
  321. { when size optimization only count occurrence }
  322. if cs_littlesize in aktglobalswitches then
  323. t_times:=1
  324. else
  325. { reference for repetition is 100 }
  326. t_times:=100;
  327. { clear register count }
  328. clearregistercount;
  329. use_esp_stackframe:=false;
  330. if not(do_firstpass(p)) then
  331. begin
  332. { max. optimizations }
  333. { only if no asm is used }
  334. { and no try statement }
  335. if (cs_regalloc in aktglobalswitches) and
  336. ((procinfo.flags and (pi_uses_asm or pi_uses_exceptions))=0) then
  337. begin
  338. { can we omit the stack frame ? }
  339. { conditions:
  340. 1. procedure (not main block)
  341. 2. no constructor or destructor
  342. 3. no call to other procedures
  343. 4. no interrupt handler
  344. }
  345. if assigned(aktprocsym) then
  346. begin
  347. if not(aktprocsym^.definition^.proctypeoption in [potype_constructor,potype_destructor]) and
  348. not(po_interrupt in aktprocsym^.definition^.procoptions) and
  349. ((procinfo.flags and pi_do_call)=0) and
  350. (lexlevel>=normal_function_level) then
  351. begin
  352. { use ESP as frame pointer }
  353. procinfo.framepointer:=stack_pointer;
  354. use_esp_stackframe:=true;
  355. { calc parameter distance new }
  356. dec(procinfo.framepointer_offset,4);
  357. dec(procinfo.ESI_offset,4);
  358. { is this correct ???}
  359. { retoffset can be negativ for results in eax !! }
  360. { the value should be decreased only if positive }
  361. if procinfo.retoffset>=0 then
  362. dec(procinfo.retoffset,4);
  363. dec(procinfo.call_offset,4);
  364. aktprocsym^.definition^.parast^.address_fixup:=procinfo.call_offset;
  365. end;
  366. end;
  367. if (p^.registers32<4) then
  368. begin
  369. for i:=1 to maxvarregs do
  370. regvars[i]:=nil;
  371. parasym:=false;
  372. symtablestack^.foreach({$ifndef TP}@{$endif}searchregvars);
  373. { copy parameter into a register ? }
  374. parasym:=true;
  375. symtablestack^.next^.foreach({$ifndef TP}@{$endif}searchregvars);
  376. { hold needed registers free }
  377. for i:=maxvarregs downto maxvarregs-p^.registers32+1 do
  378. regvars[i]:=nil;
  379. { now assign register }
  380. for i:=1 to maxvarregs-p^.registers32 do
  381. begin
  382. if assigned(regvars[i]) then
  383. begin
  384. { it is nonsens, to copy the variable to }
  385. { a register because we need then much }
  386. { too pushes ? }
  387. if reg_pushes[varregs[i]]>=regvars[i]^.refs then
  388. begin
  389. regvars[i]:=nil;
  390. goto nextreg;
  391. end;
  392. { register is no longer available for }
  393. { expressions }
  394. { search the register which is the most }
  395. { unused }
  396. usableregs:=usableregs-[varregs[i]];
  397. is_reg_var[varregs[i]]:=true;
  398. dec(c_usableregs);
  399. { possibly no 32 bit register are needed }
  400. { call by reference/const ? }
  401. if (regvars[i]^.varspez=vs_var) or
  402. ((regvars[i]^.varspez=vs_const) and
  403. push_addr_param(regvars[i]^.definition)) then
  404. begin
  405. regvars[i]^.reg:=varregs[i];
  406. regsize:=S_L;
  407. end
  408. else
  409. if (regvars[i]^.definition^.deftype=orddef) and
  410. (porddef(regvars[i]^.definition)^.size=1) then
  411. begin
  412. {$ifdef i386}
  413. regvars[i]^.reg:=reg32toreg8(varregs[i]);
  414. {$endif}
  415. regsize:=S_B;
  416. end
  417. else
  418. if (regvars[i]^.definition^.deftype=orddef) and
  419. (porddef(regvars[i]^.definition)^.size=2) then
  420. begin
  421. {$ifdef i386}
  422. regvars[i]^.reg:=reg32toreg16(varregs[i]);
  423. {$endif}
  424. regsize:=S_W;
  425. end
  426. else
  427. begin
  428. regvars[i]^.reg:=varregs[i];
  429. regsize:=S_L;
  430. end;
  431. { parameter must be load }
  432. if regvars_para[i] then
  433. begin
  434. { procinfo is there actual, }
  435. { because we can't never be in a }
  436. { nested procedure }
  437. { when loading parameter to reg }
  438. new(hr);
  439. reset_reference(hr^);
  440. hr^.offset:=pvarsym(regvars[i])^.address+procinfo.call_offset;
  441. hr^.base:=procinfo.framepointer;
  442. {$ifdef i386}
  443. procinfo.aktentrycode^.concat(new(pai386,op_ref_reg(A_MOV,regsize,
  444. hr,regvars[i]^.reg)));
  445. {$endif i386}
  446. {$ifdef m68k}
  447. procinfo.aktentrycode^.concat(new(pai68k,op_ref_reg(A_MOVE,regsize,
  448. hr,regvars[i]^.reg)));
  449. {$endif m68k}
  450. unused:=unused - [regvars[i]^.reg];
  451. end;
  452. { procedure uses this register }
  453. {$ifdef i386}
  454. usedinproc:=usedinproc or ($80 shr byte(varregs[i]));
  455. {$endif i386}
  456. {$ifdef m68k}
  457. usedinproc:=usedinproc or ($800 shr word(varregs[i]));
  458. {$endif m68k}
  459. end;
  460. nextreg:
  461. { dummy }
  462. regsize:=S_W;
  463. end;
  464. for i:=1 to maxvarregs do
  465. begin
  466. if assigned(regvars[i]) then
  467. begin
  468. if cs_asm_source in aktglobalswitches then
  469. procinfo.aktentrycode^.insert(new(pai_asm_comment,init(strpnew(regvars[i]^.name+
  470. ' with weight '+tostr(regvars[i]^.refs)+' assigned to register '+
  471. reg2str(regvars[i]^.reg)))));
  472. if (status.verbosity and v_debug)=v_debug then
  473. Message3(cg_d_register_weight,reg2str(regvars[i]^.reg),
  474. tostr(regvars[i]^.refs),regvars[i]^.name);
  475. end;
  476. end;
  477. if cs_asm_source in aktglobalswitches then
  478. procinfo.aktentrycode^.insert(new(pai_asm_comment,init(strpnew('Register variable assignment:'))));
  479. end;
  480. end;
  481. if assigned(aktprocsym) and
  482. (pocall_inline in aktprocsym^.definition^.proccalloptions) then
  483. make_const_global:=true;
  484. do_secondpass(p);
  485. if assigned(procinfo.def) then
  486. procinfo.def^.fpu_used:=p^.registersfpu;
  487. { all registers can be used again }
  488. resetusableregisters;
  489. end;
  490. procinfo.aktproccode^.concatlist(exprasmlist);
  491. make_const_global:=false;
  492. end;
  493. end.
  494. {
  495. $Log$
  496. Revision 1.28 1999-08-04 00:23:10 florian
  497. * renamed i386asm and i386base to cpuasm and cpubase
  498. Revision 1.27 1999/08/03 22:02:55 peter
  499. * moved bitmask constants to sets
  500. * some other type/const renamings
  501. Revision 1.26 1999/06/02 22:44:08 pierre
  502. * previous wrong log corrected
  503. Revision 1.25 1999/06/02 22:25:41 pierre
  504. * changed $ifdef FPC @ into $ifndef TP
  505. Revision 1.24 1999/06/01 14:45:50 peter
  506. * @procvar is now always needed for FPC
  507. Revision 1.23 1999/05/27 19:44:43 peter
  508. * removed oldasm
  509. * plabel -> pasmlabel
  510. * -a switches to source writing automaticly
  511. * assembler readers OOPed
  512. * asmsymbol automaticly external
  513. * jumptables and other label fixes for asm readers
  514. Revision 1.22 1999/05/18 14:15:50 peter
  515. * containsself fixes
  516. * checktypes()
  517. Revision 1.21 1999/05/17 21:57:11 florian
  518. * new temporary ansistring handling
  519. Revision 1.20 1999/05/02 21:33:54 florian
  520. * several bugs regarding -Or fixed
  521. Revision 1.19 1999/05/01 13:24:28 peter
  522. * merged nasm compiler
  523. * old asm moved to oldasm/
  524. Revision 1.18 1999/04/28 06:02:04 florian
  525. * changes of Bruessel:
  526. + message handler can now take an explicit self
  527. * typinfo fixed: sometimes the type names weren't written
  528. * the type checking for pointer comparisations and subtraction
  529. and are now more strict (was also buggy)
  530. * small bug fix to link.pas to support compiling on another
  531. drive
  532. * probable bug in popt386 fixed: call/jmp => push/jmp
  533. transformation didn't count correctly the jmp references
  534. + threadvar support
  535. * warning if ln/sqrt gets an invalid constant argument
  536. Revision 1.17 1999/03/31 13:55:11 peter
  537. * assembler inlining working for ag386bin
  538. Revision 1.16 1999/03/24 23:17:11 peter
  539. * fixed bugs 212,222,225,227,229,231,233
  540. Revision 1.15 1999/02/22 02:15:25 peter
  541. * updates for ag386bin
  542. Revision 1.14 1999/01/23 23:29:37 florian
  543. * first running version of the new code generator
  544. * when compiling exceptions under Linux fixed
  545. Revision 1.13 1998/12/30 13:41:09 peter
  546. * released valuepara
  547. Revision 1.12 1998/12/19 00:23:51 florian
  548. * ansistring memory leaks fixed
  549. Revision 1.11 1998/12/11 00:03:28 peter
  550. + globtype,tokens,version unit splitted from globals
  551. Revision 1.10 1998/11/18 15:44:14 peter
  552. * VALUEPARA for tp7 compatible value parameters
  553. Revision 1.9 1998/11/13 15:40:21 pierre
  554. + added -Se in Makefile cvstest target
  555. + lexlevel cleanup
  556. normal_function_level main_program_level and unit_init_level defined
  557. * tins_cache grown to A_EMMS (gave range check error in asm readers)
  558. (test added in code !)
  559. * -Un option was wrong
  560. * _FAIL and _SELF only keyword inside
  561. constructors and methods respectively
  562. Revision 1.8 1998/10/29 15:42:49 florian
  563. + partial disposing of temp. ansistrings
  564. Revision 1.7 1998/10/26 22:58:19 florian
  565. * new introduded problem with classes fix, the parent class wasn't set
  566. correct, if the class was defined forward before
  567. Revision 1.6 1998/09/23 09:58:52 peter
  568. * first working array of const things
  569. Revision 1.5 1998/09/21 10:01:06 peter
  570. * check if procinfo.def is assigned before storing registersfpu
  571. Revision 1.4 1998/09/21 08:45:16 pierre
  572. + added vmt_offset in tobjectdef.write for fututre use
  573. (first steps to have objects without vmt if no virtual !!)
  574. + added fpu_used field for tabstractprocdef :
  575. sets this level to 2 if the functions return with value in FPU
  576. (is then set to correct value at parsing of implementation)
  577. THIS MIGHT refuse some code with FPU expression too complex
  578. that were accepted before and even in some cases
  579. that don't overflow in fact
  580. ( like if f : float; is a forward that finally in implementation
  581. only uses one fpu register !!)
  582. Nevertheless I think that it will improve security on
  583. FPU operations !!
  584. * most other changes only for UseBrowser code
  585. (added symtable references for record and objects)
  586. local switch for refs to args and local of each function
  587. (static symtable still missing)
  588. UseBrowser still not stable and probably broken by
  589. the definition hash array !!
  590. Revision 1.3 1998/09/17 09:42:40 peter
  591. + pass_2 for cg386
  592. * Message() -> CGMessage() for pass_1/pass_2
  593. Revision 1.2 1998/09/07 18:46:07 peter
  594. * update smartlinking, uses getdatalabel
  595. * renamed ptree.value vars to value_str,value_real,value_set
  596. Revision 1.1 1998/09/01 09:07:12 peter
  597. * m68k fixes, splitted cg68k like cgi386
  598. }