cgobj.pas 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. {
  2. $Id$
  3. Copyright (c) 1998-2000 by Florian Klaempfl
  4. Member of the Free Pascal development team
  5. This unit implements the basic code generator object
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  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., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. ****************************************************************************
  18. }
  19. unit cgobj;
  20. interface
  21. uses
  22. cobjects,aasm,symtable,symconst,cpuasm,cpubase,cgbase,cpuinfo,tainst;
  23. type
  24. talignment = (AM_NATURAL,AM_NONE,AM_2BYTE,AM_4BYTE,AM_8BYTE);
  25. pcg = ^tcg;
  26. tcg = object
  27. scratch_register_array_pointer : aword;
  28. unusedscratchregisters : tregisterset;
  29. alignment : talignment;
  30. {************************************************}
  31. { basic routines }
  32. constructor init;
  33. destructor done;virtual;
  34. procedure a_label(list : paasmoutput;l : pasmlabel);virtual;
  35. { allocates register r by inserting a pai_realloc record }
  36. procedure a_reg_alloc(list : paasmoutput;r : tregister);
  37. { deallocates register r by inserting a pa_regdealloc record}
  38. procedure a_reg_dealloc(list : paasmoutput;r : tregister);
  39. { returns a register for use as scratch register }
  40. function get_scratch_reg(list : paasmoutput) : tregister;
  41. { releases a scratch register }
  42. procedure free_scratch_reg(list : paasmoutput;r : tregister);
  43. {************************************************}
  44. { code generation for subroutine entry/exit code }
  45. { initilizes data of type t }
  46. { if is_already_ref is true then the routines assumes }
  47. { that r points to the data to initialize }
  48. procedure g_initialize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  49. { finalizes data of type t }
  50. { if is_already_ref is true then the routines assumes }
  51. { that r points to the data to finalizes }
  52. procedure g_finalize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  53. { helper routines }
  54. procedure g_initialize_data(list : paasmoutput;p : psym);
  55. procedure g_incr_data(list : paasmoutput;p : psym);
  56. procedure g_finalize_data(list : paasmoutput;p : pnamedindexobject);
  57. procedure g_copyvalueparas(list : paasmoutput;p : pnamedindexobject);
  58. procedure g_finalizetempansistrings(list : paasmoutput);
  59. procedure g_entrycode(list : paasmoutput;
  60. const proc_names : tstringcontainer;make_global : boolean;
  61. stackframe : longint;var parasize : longint;
  62. var nostackframe : boolean;inlined : boolean);
  63. procedure g_exitcode(list : paasmoutput;parasize : longint;
  64. nostackframe,inlined : boolean);
  65. { string helper routines }
  66. procedure g_decrstrref(list : paasmoutput;const ref : treference;t : pdef);
  67. procedure g_removetemps(list : paasmoutput;p : plinkedlist);
  68. { passing parameters, per default the parameter is pushed }
  69. { nr gives the number of the parameter (enumerated from }
  70. { left to right), this allows to move the parameter to }
  71. { register, if the cpu supports register calling }
  72. { conventions }
  73. procedure a_param_reg(list : paasmoutput;size : tcgsize;r : tregister;nr : longint);virtual;
  74. procedure a_param_const(list : paasmoutput;size : tcgsize;a : aword;nr : longint);virtual;
  75. procedure a_param_ref(list : paasmoutput;size : tcgsize;const r : treference;nr : longint);virtual;
  76. procedure a_paramaddr_ref(list : paasmoutput;const r : treference;nr : longint);virtual;
  77. {**********************************}
  78. { these methods must be overriden: }
  79. { Remarks:
  80. * If a method specifies a size you have only to take care
  81. of that number of bits, i.e. load_const_reg with OP_8 must
  82. only load the lower 8 bit of the specified register
  83. the rest of the register can be undefined
  84. if necessary the compiler will call a method
  85. to zero or sign extend the register
  86. * The a_load_XX_XX with OP_64 needn't to be
  87. implemented for 32 bit
  88. processors, the code generator takes care of that
  89. * the addr size is for work with the natural pointer
  90. size
  91. * the procedures without fpu/mm are only for integer usage
  92. * normally the first location is the source and the
  93. second the destination
  94. }
  95. procedure a_call_name(list : paasmoutput;const s : string;
  96. offset : longint);virtual;
  97. { move instructions }
  98. procedure a_load_const_reg(list : paasmoutput;size : tcgsize;a : aword;register : tregister);virtual;
  99. procedure a_load_reg_ref(list : paasmoutput;size : tcgsize;register : tregister;const ref : treference);virtual;
  100. procedure a_load_ref_reg(list : paasmoutput;size : tcgsize;const ref : treference;register : tregister);virtual;
  101. procedure a_load_reg_reg(list : paasmoutput;size : tcgsize;reg1,reg2 : tregister);virtual;
  102. { comparison operations }
  103. procedure a_cmp_reg_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  104. l : pasmlabel);virtual;
  105. procedure a_cmp_reg_reg_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : pasmlabel);
  106. procedure a_cmp_reg_ref_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg : tregister;l : pasmlabel);
  107. procedure a_cmp_ref_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  108. l : pasmlabel);
  109. procedure a_jmp_cond(list : paasmoutput;cond : TOpCmp;l: pasmlabel);
  110. procedure a_loadaddress_ref_reg(list : paasmoutput;const ref : treference;r : tregister);virtual;
  111. procedure g_stackframe_entry(list : paasmoutput;localsize : longint);virtual;
  112. { restores the frame pointer at procedure exit, for the }
  113. { i386 it generates a simple leave }
  114. procedure g_restore_frame_pointer(list : paasmoutput);virtual;
  115. { some processors like the PPC doesn't allow to change the stack in }
  116. { a procedure, so we need to maintain an extra stack for the }
  117. { result values of setjmp in exception code }
  118. { this two procedures are for pushing an exception value, }
  119. { they can use the scratch registers }
  120. procedure g_push_exception_value_reg(list : paasmoutput;reg : tregister);virtual;
  121. procedure g_push_exception_value_const(list : paasmoutput;reg : tregister);virtual;
  122. { that procedure pops a exception value }
  123. procedure g_pop_exception_value_reg(list : paasmoutput;reg : tregister);virtual;
  124. procedure g_return_from_proc(list : paasmoutput;parasize : aword);virtual;
  125. {********************************************************}
  126. { these methods can be overriden for extra functionality }
  127. { the following methods do nothing: }
  128. procedure g_interrupt_stackframe_entry(list : paasmoutput);virtual;
  129. procedure g_interrupt_stackframe_exit(list : paasmoutput);virtual;
  130. procedure g_profilecode(list : paasmoutput);virtual;
  131. procedure g_stackcheck(list : paasmoutput;stackframesize : longint);virtual;
  132. procedure a_load_const_ref(list : paasmoutput;size : tcgsize;a : aword;const ref : treference);virtual;
  133. procedure g_maybe_loadself(list : paasmoutput);virtual;
  134. { copies len bytes from the source to destination, if }
  135. { loadref is true, it assumes that it first must load }
  136. { the source address from the memory location where }
  137. { source points to }
  138. procedure g_concatcopy(list : paasmoutput;const source,dest : treference;len : aword;loadref : boolean);virtual;
  139. { uses the addr of ref as param, was emitpushreferenceaddr }
  140. procedure a_param_ref_addr(list : paasmoutput;r : treference;nr : longint);virtual;
  141. end;
  142. var
  143. cg : pcg; { this is the main code generator class }
  144. implementation
  145. uses
  146. strings,globals,globtype,options,files,gdb,systems,
  147. ppu,verbose,types,tgobj,tgcpu;
  148. {*****************************************************************************
  149. basic functionallity
  150. ******************************************************************************}
  151. constructor tcg.init;
  152. var
  153. i : longint;
  154. begin
  155. scratch_register_array_pointer:=1;
  156. for i:=1 to max_scratch_regs do
  157. include(unusedscratchregisters,scratch_regs[i]);
  158. end;
  159. destructor tcg.done;
  160. begin
  161. end;
  162. procedure tcg.a_reg_alloc(list : paasmoutput;r : tregister);
  163. begin
  164. list^.concat(new(pairegalloc,alloc(r)));
  165. end;
  166. procedure tcg.a_reg_dealloc(list : paasmoutput;r : tregister);
  167. begin
  168. list^.concat(new(pairegalloc,dealloc(r)));
  169. end;
  170. procedure tcg.a_label(list : paasmoutput;l : pasmlabel);
  171. begin
  172. list^.concat(new(pai_label,init(l)));
  173. end;
  174. function tcg.get_scratch_reg(list : paasmoutput) : tregister;
  175. var
  176. r : tregister;
  177. i : longint;
  178. begin
  179. if unusedscratchregisters=[] then
  180. internalerror(68996);
  181. for i:=scratch_register_array_pointer to
  182. (scratch_register_array_pointer+max_scratch_regs) do
  183. if scratch_regs[(i mod max_scratch_regs)+1] in unusedscratchregisters then
  184. begin
  185. r:=scratch_regs[(i mod max_scratch_regs)+1];
  186. break;
  187. end;
  188. exclude(unusedscratchregisters,r);
  189. inc(scratch_register_array_pointer);
  190. if scratch_register_array_pointer>max_scratch_regs then
  191. scratch_register_array_pointer:=1;
  192. a_reg_alloc(list,r);
  193. get_scratch_reg:=r;
  194. end;
  195. procedure tcg.free_scratch_reg(list : paasmoutput;r : tregister);
  196. begin
  197. include(unusedscratchregisters,r);
  198. a_reg_dealloc(list,r);
  199. end;
  200. {*****************************************************************************
  201. this methods must be overridden for extra functionality
  202. ******************************************************************************}
  203. procedure tcg.g_interrupt_stackframe_entry(list : paasmoutput);
  204. begin
  205. end;
  206. procedure tcg.g_interrupt_stackframe_exit(list : paasmoutput);
  207. begin
  208. end;
  209. procedure tcg.g_profilecode(list : paasmoutput);
  210. begin
  211. end;
  212. {*****************************************************************************
  213. for better code generation these methods should be overridden
  214. ******************************************************************************}
  215. procedure tcg.a_param_const(list : paasmoutput;size : tcgsize;a : aword;nr : longint);
  216. var
  217. hr : tregister;
  218. begin
  219. hr:=get_scratch_reg(list);
  220. a_load_const_reg(list,size,a,hr);
  221. a_param_reg(list,size,hr,nr);
  222. free_scratch_reg(list,hr);
  223. end;
  224. procedure tcg.a_param_ref(list : paasmoutput;size : tcgsize;const r : treference;nr : longint);
  225. var
  226. hr : tregister;
  227. begin
  228. hr:=get_scratch_reg(list);
  229. a_load_ref_reg(list,size,r,hr);
  230. a_param_reg(list,size,hr,nr);
  231. free_scratch_reg(list,hr);
  232. end;
  233. procedure tcg.a_param_ref_addr(list : paasmoutput;r : treference;nr : longint);
  234. var
  235. hr : tregister;
  236. begin
  237. hr:=get_scratch_reg(list);
  238. a_loadaddress_ref_reg(list,r,hr);
  239. a_param_reg(list,OS_ADDR,hr,nr);
  240. free_scratch_reg(list,hr);
  241. end;
  242. procedure tcg.g_stackcheck(list : paasmoutput;stackframesize : longint);
  243. begin
  244. a_param_const(list,OS_32,stackframesize,1);
  245. a_call_name(list,'FPC_STACKCHECK',0);
  246. end;
  247. procedure tcg.a_load_const_ref(list : paasmoutput;size : tcgsize;a : aword;const ref : treference);
  248. var
  249. hr : tregister;
  250. begin
  251. hr:=get_scratch_reg(list);
  252. a_load_const_reg(list,size,a,hr);
  253. a_load_reg_ref(list,size,hr,ref);
  254. free_scratch_reg(list,hr);
  255. end;
  256. procedure tcg.g_concatcopy(list : paasmoutput;const source,dest : treference;len : aword;loadref : boolean);
  257. begin
  258. abstract;
  259. end;
  260. {*****************************************************************************
  261. String helper routines
  262. *****************************************************************************}
  263. procedure tcg.g_removetemps(list : paasmoutput;p : plinkedlist);
  264. var
  265. hp : ptemptodestroy;
  266. pushedregs : tpushed;
  267. begin
  268. hp:=ptemptodestroy(p^.first);
  269. if not(assigned(hp)) then
  270. exit;
  271. tg.pushusedregisters(pushedregs,$ff);
  272. while assigned(hp) do
  273. begin
  274. if is_ansistring(hp^.typ) then
  275. begin
  276. g_decrstrref(list,hp^.address,hp^.typ);
  277. tg.ungetiftemp(hp^.address);
  278. end;
  279. hp:=ptemptodestroy(hp^.next);
  280. end;
  281. tg.popusedregisters(pushedregs);
  282. end;
  283. procedure tcg.g_decrstrref(list : paasmoutput;const ref : treference;t : pdef);
  284. var
  285. pushedregs : tpushed;
  286. begin
  287. tg.pushusedregisters(pushedregs,$ff);
  288. a_param_ref_addr(list,ref,1);
  289. if is_ansistring(t) then
  290. a_call_name(list,'FPC_ANSISTR_DECR_REF',0)
  291. else if is_widestring(t) then
  292. a_call_name(list,'FPC_WIDESTR_DECR_REF',0)
  293. else internalerror(58993);
  294. tg.popusedregisters(pushedregs);
  295. end;
  296. {*****************************************************************************
  297. Code generation for subroutine entry- and exit code
  298. *****************************************************************************}
  299. { initilizes data of type t }
  300. { if is_already_ref is true then the routines assumes }
  301. { that r points to the data to initialize }
  302. procedure tcg.g_initialize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  303. var
  304. hr : treference;
  305. begin
  306. if is_ansistring(t) or
  307. is_widestring(t) then
  308. a_load_const_ref(list,OS_8,0,ref)
  309. else
  310. begin
  311. reset_reference(hr);
  312. hr.symbol:=t^.get_inittable_label;
  313. a_param_ref_addr(list,hr,2);
  314. if is_already_ref then
  315. a_param_ref(list,OS_ADDR,ref,1)
  316. else
  317. a_param_ref_addr(list,ref,1);
  318. a_call_name(list,'FPC_INITIALIZE',0);
  319. end;
  320. end;
  321. procedure tcg.g_finalize(list : paasmoutput;t : pdef;const ref : treference;is_already_ref : boolean);
  322. var
  323. r : treference;
  324. begin
  325. if is_ansistring(t) or
  326. is_widestring(t) then
  327. begin
  328. g_decrstrref(list,ref,t);
  329. end
  330. else
  331. begin
  332. reset_reference(r);
  333. r.symbol:=t^.get_inittable_label;
  334. a_param_ref_addr(list,r,2);
  335. if is_already_ref then
  336. a_paramaddr_ref(list,ref,1)
  337. else
  338. a_param_ref_addr(list,ref,1);
  339. a_call_name(list,'FPC_FINALIZE',0);
  340. end;
  341. end;
  342. { generates the code for initialisation of local data }
  343. procedure tcg.g_initialize_data(list : paasmoutput;p : psym);
  344. var
  345. hr : treference;
  346. begin
  347. if (psym(p)^.typ=varsym) and
  348. assigned(pvarsym(p)^.vartype.def) and
  349. not((pvarsym(p)^.vartype.def^.deftype=objectdef) and
  350. pobjectdef(pvarsym(p)^.vartype.def)^.is_class) and
  351. pvarsym(p)^.vartype.def^.needs_inittable then
  352. begin
  353. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  354. reset_reference(hr);
  355. if psym(p)^.owner^.symtabletype=localsymtable then
  356. begin
  357. hr.base:=procinfo^.framepointer;
  358. hr.offset:=-pvarsym(p)^.address;
  359. end
  360. else
  361. begin
  362. hr.symbol:=newasmsymbol(pvarsym(p)^.mangledname);
  363. end;
  364. g_initialize(list,pvarsym(p)^.vartype.def,hr,false);
  365. end;
  366. end;
  367. { generates the code for incrementing the reference count of parameters }
  368. procedure tcg.g_incr_data(list : paasmoutput;p : psym);
  369. var
  370. hr : treference;
  371. begin
  372. if (psym(p)^.typ=varsym) and
  373. not((pvarsym(p)^.vartype.def^.deftype=objectdef) and
  374. pobjectdef(pvarsym(p)^.vartype.def)^.is_class) and
  375. pvarsym(p)^.vartype.def^.needs_inittable and
  376. ((pvarsym(p)^.varspez=vs_value)) then
  377. begin
  378. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  379. reset_reference(hr);
  380. hr.symbol:=pvarsym(p)^.vartype.def^.get_inittable_label;
  381. a_param_ref_addr(list,hr,2);
  382. reset_reference(hr);
  383. hr.base:=procinfo^.framepointer;
  384. hr.offset:=pvarsym(p)^.address+procinfo^.para_offset;
  385. a_param_ref_addr(list,hr,1);
  386. reset_reference(hr);
  387. a_call_name(list,'FPC_ADDREF',0);
  388. end;
  389. end;
  390. { generates the code for finalisation of local data }
  391. procedure tcg.g_finalize_data(list : paasmoutput;p : pnamedindexobject);
  392. var
  393. hr : treference;
  394. begin
  395. if (psym(p)^.typ=varsym) and
  396. assigned(pvarsym(p)^.vartype.def) and
  397. not((pvarsym(p)^.vartype.def^.deftype=objectdef) and
  398. pobjectdef(pvarsym(p)^.vartype.def)^.is_class) and
  399. pvarsym(p)^.vartype.def^.needs_inittable then
  400. begin
  401. { not all kind of parameters need to be finalized }
  402. if (psym(p)^.owner^.symtabletype=parasymtable) and
  403. ((pvarsym(p)^.varspez=vs_var) or
  404. (pvarsym(p)^.varspez=vs_const) { and
  405. (dont_copy_const_param(pvarsym(p)^.definition)) } ) then
  406. exit;
  407. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  408. reset_reference(hr);
  409. case psym(p)^.owner^.symtabletype of
  410. localsymtable:
  411. begin
  412. hr.base:=procinfo^.framepointer;
  413. hr.offset:=-pvarsym(p)^.address;
  414. end;
  415. parasymtable:
  416. begin
  417. hr.base:=procinfo^.framepointer;
  418. hr.offset:=pvarsym(p)^.address+procinfo^.para_offset;
  419. end;
  420. else
  421. hr.symbol:=newasmsymbol(pvarsym(p)^.mangledname);
  422. end;
  423. g_finalize(list,pvarsym(p)^.vartype.def,hr,false);
  424. end;
  425. end;
  426. { generates the code to make local copies of the value parameters }
  427. procedure tcg.g_copyvalueparas(list : paasmoutput;p : pnamedindexobject);
  428. begin
  429. runerror(255);
  430. end;
  431. var
  432. _list : paasmoutput;
  433. { wrappers for the methods, because TP doesn't know procedures }
  434. { of objects }
  435. procedure _copyvalueparas(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  436. begin
  437. cg^.g_copyvalueparas(_list,s);
  438. end;
  439. procedure tcg.g_finalizetempansistrings(list : paasmoutput);
  440. var
  441. hp : ptemprecord;
  442. hr : treference;
  443. begin
  444. hp:=tg.templist;
  445. while assigned(hp) do
  446. begin
  447. if hp^.temptype in [tt_ansistring,tt_freeansistring] then
  448. begin
  449. procinfo^.flags:=procinfo^.flags or pi_needs_implicit_finally;
  450. reset_reference(hr);
  451. hr.base:=procinfo^.framepointer;
  452. hr.offset:=hp^.pos;
  453. a_param_ref_addr(list,hr,1);
  454. a_call_name(list,'FPC_ANSISTR_DECR_REF',0);
  455. end;
  456. hp:=hp^.next;
  457. end;
  458. end;
  459. procedure _finalize_data(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  460. begin
  461. cg^.g_finalize_data(_list,s);
  462. end;
  463. procedure _incr_data(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  464. begin
  465. cg^.g_incr_data(_list,psym(s));
  466. end;
  467. procedure _initialize_data(s : pnamedindexobject);{$ifndef FPC}far;{$endif}
  468. begin
  469. cg^.g_initialize_data(_list,psym(s));
  470. end;
  471. { generates the entry code for a procedure }
  472. procedure tcg.g_entrycode(list : paasmoutput;const proc_names:Tstringcontainer;make_global:boolean;
  473. stackframe:longint;var parasize:longint;var nostackframe:boolean;
  474. inlined : boolean);
  475. var
  476. hs : string;
  477. hp : pused_unit;
  478. initcode : taasmoutput;
  479. {$ifdef GDB}
  480. stab_function_name : Pai_stab_function_name;
  481. {$endif GDB}
  482. hr : treference;
  483. r : tregister;
  484. begin
  485. { Align }
  486. if (not inlined) then
  487. begin
  488. { gprof uses 16 byte granularity !! }
  489. if (cs_profile in aktmoduleswitches) then
  490. list^.insert(new(pai_align,init(16)))
  491. else
  492. if not(cs_littlesize in aktglobalswitches) then
  493. list^.insert(new(pai_align,init(4)));
  494. end;
  495. { save registers on cdecl }
  496. if (po_savestdregs in aktprocsym^.definition^.procoptions) then
  497. begin
  498. for r:=firstreg to lastreg do
  499. begin
  500. if (r in registers_saved_on_cdecl) then
  501. if (r in (tg.availabletempregsint+
  502. tg.availabletempregsfpu+
  503. tg.availabletempregsmm)) then
  504. begin
  505. if not(r in tg.usedinproc) then
  506. {!!!!!!!!!!!! a_push_reg(list,r) }
  507. end
  508. else
  509. {!!!!!!!! a_push_reg(list,r) };
  510. end;
  511. end;
  512. { omit stack frame ? }
  513. if not inlined then
  514. if procinfo^.framepointer=stack_pointer then
  515. begin
  516. CGMessage(cg_d_stackframe_omited);
  517. nostackframe:=true;
  518. if (aktprocsym^.definition^.proctypeoption in [potype_unitinit,potype_proginit,potype_unitfinalize]) then
  519. parasize:=0
  520. else
  521. parasize:=aktprocsym^.definition^.parast^.datasize+procinfo^.para_offset-pointersize;
  522. end
  523. else
  524. begin
  525. if (aktprocsym^.definition^.proctypeoption in [potype_unitinit,potype_proginit,potype_unitfinalize]) then
  526. parasize:=0
  527. else
  528. parasize:=aktprocsym^.definition^.parast^.datasize+procinfo^.para_offset-pointersize*2;
  529. nostackframe:=false;
  530. if (po_interrupt in aktprocsym^.definition^.procoptions) then
  531. g_interrupt_stackframe_entry(list);
  532. g_stackframe_entry(list,stackframe);
  533. if (cs_check_stack in aktlocalswitches) and
  534. (tf_supports_stack_checking in target_info.flags) then
  535. g_stackcheck(@initcode,stackframe);
  536. end;
  537. if cs_profile in aktmoduleswitches then
  538. g_profilecode(@initcode);
  539. if (not inlined) and (aktprocsym^.definition^.proctypeoption in [potype_unitinit]) then
  540. begin
  541. { needs the target a console flags ? }
  542. if tf_needs_isconsole in target_info.flags then
  543. begin
  544. hr.symbol:=newasmsymbol('U_'+target_info.system_unit+'_ISCONSOLE');
  545. if apptype=at_cui then
  546. a_load_const_ref(list,OS_8,1,hr)
  547. else
  548. a_load_const_ref(list,OS_8,0,hr);
  549. dispose(hr.symbol,done);
  550. end;
  551. hp:=pused_unit(usedunits.first);
  552. while assigned(hp) do
  553. begin
  554. { call the unit init code and make it external }
  555. if (hp^.u^.flags and uf_init)<>0 then
  556. a_call_name(list,
  557. 'INIT$$'+hp^.u^.modulename^,0);
  558. hp:=Pused_unit(hp^.next);
  559. end;
  560. end;
  561. {$ifdef dummy}
  562. { a constructor needs a help procedure }
  563. if (aktprocsym^.definition^.options and poconstructor)<>0 then
  564. begin
  565. if procinfo^._class^.isclass then
  566. begin
  567. list^.concat(new(paicpu,op_sym(A_CALL,S_NO,newasmsymbol('FPC_NEW_CLASS'))));
  568. list^.concat(new(paicpu,op_cond_sym(A_Jcc,C_Z,S_NO,quickexitlabel)));
  569. end
  570. else
  571. begin
  572. {
  573. list^.insert(new(pai_labeled,init(A_JZ,quickexitlabel)));
  574. list^.insert(new(paicpu,op_csymbol(A_CALL,S_NO,
  575. newcsymbol('FPC_HELP_CONSTRUCTOR',0))));
  576. list^.insert(new(paicpu,op_const_reg(A_MOV,S_L,procinfo^._class^.vmt_offset,R_EDI)));
  577. concat_external('FPC_HELP_CONSTRUCTOR',EXT_NEAR);
  578. }
  579. end;
  580. end;
  581. {$endif dummy}
  582. {$ifdef GDB}
  583. if (cs_debuginfo in aktmoduleswitches) then
  584. list^.insert(new(pai_force_line,init));
  585. {$endif GDB}
  586. { initialize return value }
  587. if assigned(procinfo^.returntype.def) and
  588. is_ansistring(procinfo^.returntype.def) or
  589. is_widestring(procinfo^.returntype.def) then
  590. begin
  591. reset_reference(hr);
  592. hr.offset:=procinfo^.return_offset;
  593. hr.base:=procinfo^.framepointer;
  594. a_load_const_ref(list,OS_32,0,hr);
  595. end;
  596. _list:=list;
  597. { generate copies of call by value parameters }
  598. if (po_assembler in aktprocsym^.definition^.procoptions) then
  599. aktprocsym^.definition^.parast^.foreach({$ifdef FPC}@{$endif FPC}_copyvalueparas);
  600. { initialisizes local data }
  601. aktprocsym^.definition^.localst^.foreach({$ifdef FPC}@{$endif FPC}_initialize_data);
  602. { add a reference to all call by value/const parameters }
  603. aktprocsym^.definition^.parast^.foreach({$ifdef FPC}@{$endif FPC}_incr_data);
  604. if (cs_profile in aktmoduleswitches) or
  605. (aktprocsym^.definition^.owner^.symtabletype=globalsymtable) or
  606. (assigned(procinfo^._class) and (procinfo^._class^.owner^.symtabletype=globalsymtable)) then
  607. make_global:=true;
  608. if not inlined then
  609. begin
  610. hs:=proc_names.get;
  611. {$ifdef GDB}
  612. if (cs_debuginfo in aktmoduleswitches) and target_os.use_function_relative_addresses then
  613. stab_function_name := new(pai_stab_function_name,init(strpnew(hs)));
  614. {$endif GDB}
  615. { insert the names for the procedure }
  616. while hs<>'' do
  617. begin
  618. if make_global then
  619. exprasmlist^.insert(new(pai_symbol,initname_global(hs,0)))
  620. else
  621. exprasmlist^.insert(new(pai_symbol,initname(hs,0)));
  622. {$ifdef GDB}
  623. if (cs_debuginfo in aktmoduleswitches) then
  624. begin
  625. if target_os.use_function_relative_addresses then
  626. list^.insert(new(pai_stab_function_name,init(strpnew(hs))));
  627. end;
  628. {$endif GDB}
  629. hs:=proc_names.get;
  630. end;
  631. end;
  632. {$ifdef GDB}
  633. if (not inlined) and (cs_debuginfo in aktmoduleswitches) then
  634. begin
  635. if target_os.use_function_relative_addresses then
  636. list^.insert(stab_function_name);
  637. if make_global or ((procinfo^.flags and pi_is_global) <> 0) then
  638. aktprocsym^.is_global := True;
  639. list^.insert(new(pai_stabs,init(aktprocsym^.stabstring)));
  640. aktprocsym^.isstabwritten:=true;
  641. end;
  642. {$endif GDB}
  643. end;
  644. procedure tcg.g_exitcode(list : paasmoutput;parasize:longint;nostackframe,inlined:boolean);
  645. var
  646. {$ifdef GDB}
  647. mangled_length : longint;
  648. p : pchar;
  649. {$endif GDB}
  650. nofinal,noreraiselabel : pasmlabel;
  651. hr : treference;
  652. r : tregister;
  653. begin
  654. if aktexitlabel^.is_used then
  655. list^.insert(new(pai_label,init(aktexitlabel)));
  656. { call the destructor help procedure }
  657. if (aktprocsym^.definition^.proctypeoption=potype_destructor) then
  658. begin
  659. if procinfo^._class^.is_class then
  660. a_call_name(list,'FPC_DISPOSE_CLASS',0)
  661. else
  662. begin
  663. if procinfo^._class^.needs_inittable then
  664. begin
  665. getlabel(nofinal);
  666. {!!!!!!!!!!
  667. reset_reference(hr);
  668. hr.base:=R_EBP;
  669. hr.offset:=8;
  670. a_cmp_reg_const_label(list,OS_ADDR,OZ_EQ,
  671. }
  672. reset_reference(hr);
  673. hr.symbol:=procinfo^._class^.get_inittable_label;
  674. a_paramaddr_ref(list,hr,2);
  675. a_param_reg(list,OS_ADDR,self_pointer,1);
  676. a_call_name(list,'FPC_FINALIZE',0);
  677. a_label(list,nofinal);
  678. end;
  679. { vmt_offset_reg can be a scratch register, }
  680. { but it must be always the same }
  681. a_reg_alloc(list,vmt_offset_reg);
  682. a_load_const_reg(list,OS_32,procinfo^._class^.vmt_offset,vmt_offset_reg);
  683. a_call_name(list,'FPC_HELP_DESTRUCTOR',0);
  684. a_reg_dealloc(list,vmt_offset_reg);
  685. end;
  686. end;
  687. { finalize temporary data }
  688. g_finalizetempansistrings(list);
  689. _list:=list;
  690. { finalize local data }
  691. aktprocsym^.definition^.localst^.foreach({$ifndef TP}@{$endif}_finalize_data);
  692. { finalize paras data }
  693. if assigned(aktprocsym^.definition^.parast) then
  694. aktprocsym^.definition^.parast^.foreach({$ifndef TP}@{$endif}_finalize_data);
  695. { do we need to handle exceptions because of ansi/widestrings ? }
  696. if (procinfo^.flags and pi_needs_implicit_finally)<>0 then
  697. begin
  698. getlabel(noreraiselabel);
  699. a_call_name(list,'FPC_POPADDRSTACK',0);
  700. a_reg_alloc(list,accumulator);
  701. g_pop_exception_value_reg(list,accumulator);
  702. a_cmp_reg_const_label(list,OS_32,OC_EQ,0,accumulator,noreraiselabel);
  703. a_reg_dealloc(list,accumulator);
  704. { must be the return value finalized before reraising the exception? }
  705. if (procinfo^.returntype.def<>pdef(voiddef)) and
  706. (procinfo^.returntype.def^.needs_inittable) and
  707. ((procinfo^.returntype.def^.deftype<>objectdef) or
  708. not(pobjectdef(procinfo^.returntype.def)^.is_class)) then
  709. begin
  710. reset_reference(hr);
  711. hr.offset:=procinfo^.return_offset;
  712. hr.base:=procinfo^.framepointer;
  713. g_finalize(list,procinfo^.returntype.def,hr,ret_in_param(procinfo^.returntype.def));
  714. end;
  715. a_call_name(list,'FPC_RERAISE',0);
  716. a_label(list,noreraiselabel);
  717. end;
  718. { call __EXIT for main program }
  719. if (not DLLsource) and (not inlined) and (aktprocsym^.definition^.proctypeoption=potype_proginit) then
  720. a_call_name(list,'FPC_DO_EXIT',0);
  721. { handle return value }
  722. if not(po_assembler in aktprocsym^.definition^.procoptions) then
  723. if (aktprocsym^.definition^.proctypeoption<>potype_constructor) then
  724. { handle_return_value(inlined) }
  725. else
  726. begin
  727. { return self in EAX }
  728. a_label(list,quickexitlabel);
  729. a_reg_alloc(list,accumulator);
  730. a_load_reg_reg(list,OS_ADDR,self_pointer,accumulator);
  731. a_reg_dealloc(list,self_pointer);
  732. a_label(list,quickexitlabel);
  733. { we can't clear the zero flag because the Alpha }
  734. { for example doesn't have flags, we have to compare }
  735. { the accu. in the caller }
  736. end;
  737. { stabs uses the label also ! }
  738. if aktexit2label^.is_used or
  739. ((cs_debuginfo in aktmoduleswitches) and not inlined) then
  740. a_label(list,aktexit2label);
  741. {$ifdef dummy}
  742. { should we restore edi ? }
  743. { for all i386 gcc implementations }
  744. {!!!!!!!!!!! I don't know how to handle register saving yet }
  745. if (po_savestdregs in aktprocsym^.definition^.procoptions) then
  746. begin
  747. if (aktprocsym^.definition^.usedregisters and ($80 shr byte(R_EBX)))<>0 then
  748. exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_EBX)));
  749. exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_ESI)));
  750. exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_EDI)));
  751. { here we could reset R_EBX
  752. but that is risky because it only works
  753. if genexitcode is called after genentrycode
  754. so lets skip this for the moment PM
  755. aktprocsym^.definition^.usedregisters:=
  756. aktprocsym^.definition^.usedregisters or not ($80 shr byte(R_EBX));
  757. }
  758. end;
  759. {$endif dummy}
  760. if not(nostackframe) and not inlined then
  761. g_restore_frame_pointer(list);
  762. { at last, the return is generated }
  763. if not inlined then
  764. if po_interrupt in aktprocsym^.definition^.procoptions then
  765. g_interrupt_stackframe_exit(list)
  766. else
  767. g_return_from_proc(list,parasize);
  768. list^.concat(new(pai_symbol_end,initname(aktprocsym^.definition^.mangledname)));
  769. {$ifdef GDB}
  770. if (cs_debuginfo in aktmoduleswitches) and not inlined then
  771. begin
  772. aktprocsym^.concatstabto(list);
  773. if assigned(procinfo^._class) then
  774. if (not assigned(procinfo^.parent) or
  775. not assigned(procinfo^.parent^._class)) then
  776. list^.concat(new(pai_stabs,init(strpnew(
  777. '"$t:v'+procinfo^._class^.numberstring+'",'+
  778. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.selfpointer_offset)))));
  779. {!!!!!!!!!!!!
  780. else
  781. list^.concat(new(pai_stabs,init(strpnew(
  782. '"$t:r'+procinfo^._class^.numberstring+'",'+
  783. tostr(N_RSYM)+',0,0,'+tostr(GDB_i386index[R_ESI])))));
  784. }
  785. if (pdef(aktprocsym^.definition^.rettype.def) <> pdef(voiddef)) then
  786. begin
  787. if ret_in_param(aktprocsym^.definition^.rettype.def) then
  788. list^.concat(new(pai_stabs,init(strpnew(
  789. '"'+aktprocsym^.name+':X*'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  790. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))))
  791. else
  792. list^.concat(new(pai_stabs,init(strpnew(
  793. '"'+aktprocsym^.name+':X'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  794. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))));
  795. if (m_result in aktmodeswitches) then
  796. if ret_in_param(aktprocsym^.definition^.rettype.def) then
  797. list^.concat(new(pai_stabs,init(strpnew(
  798. '"RESULT:X*'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  799. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))))
  800. else
  801. list^.concat(new(pai_stabs,init(strpnew(
  802. '"RESULT:X'+aktprocsym^.definition^.rettype.def^.numberstring+'",'+
  803. tostr(N_PSYM)+',0,0,'+tostr(procinfo^.return_offset)))));
  804. end;
  805. mangled_length:=length(aktprocsym^.definition^.mangledname);
  806. getmem(p,mangled_length+50);
  807. strpcopy(p,'192,0,0,');
  808. strpcopy(strend(p),aktprocsym^.definition^.mangledname);
  809. list^.concat(new(pai_stabn,init(strnew(p))));
  810. {list^.concat(new(pai_stabn,init(strpnew('192,0,0,'
  811. +aktprocsym^.definition^.mangledname))));
  812. p[0]:='2';p[1]:='2';p[2]:='4';
  813. strpcopy(strend(p),'_end');}
  814. freemem(p,mangled_length+50);
  815. list^.concat(new(pai_stabn,init(
  816. strpnew('224,0,0,'+aktexit2label^.name))));
  817. { strpnew('224,0,0,'
  818. +aktprocsym^.definition^.mangledname+'_end'))));}
  819. end;
  820. {$endif GDB}
  821. end;
  822. {*****************************************************************************
  823. some abstract definitions
  824. ****************************************************************************}
  825. procedure tcg.a_call_name(list : paasmoutput;const s : string;
  826. offset : longint);
  827. begin
  828. abstract;
  829. end;
  830. procedure tcg.g_stackframe_entry(list : paasmoutput;localsize : longint);
  831. begin
  832. abstract;
  833. end;
  834. procedure tcg.g_maybe_loadself(list : paasmoutput);
  835. begin
  836. abstract;
  837. end;
  838. procedure tcg.g_restore_frame_pointer(list : paasmoutput);
  839. begin
  840. abstract;
  841. end;
  842. procedure g_return_from_proc(list : paasmoutput;parasize : aword);
  843. begin
  844. abstract;
  845. end;
  846. procedure tcg.a_loadaddress_ref_reg(list : paasmoutput;const ref : treference;r : tregister);
  847. begin
  848. abstract;
  849. end;
  850. procedure tcg.g_push_exception_value_reg(list : paasmoutput;reg : tregister);
  851. begin
  852. abstract;
  853. end;
  854. procedure tcg.g_push_exception_value_const(list : paasmoutput;reg : tregister);
  855. begin
  856. abstract;
  857. end;
  858. procedure tcg.g_pop_exception_value_reg(list : paasmoutput;reg : tregister);
  859. begin
  860. abstract;
  861. end;
  862. procedure tcg.a_load_const_reg(list : paasmoutput;size : tcgsize;a : aword;register : tregister);
  863. begin
  864. abstract;
  865. end;
  866. procedure tcg.a_load_reg_ref(list : paasmoutput;size : tcgsize;register : tregister;const ref : treference);
  867. begin
  868. abstract;
  869. end;
  870. procedure tcg.a_load_ref_reg(list : paasmoutput;size : tcgsize;const ref : treference;register : tregister);
  871. begin
  872. abstract;
  873. end;
  874. procedure tcg.a_load_reg_reg(list : paasmoutput;size : tcgsize;reg1,reg2 : tregister);
  875. begin
  876. abstract;
  877. end;
  878. procedure tcg.a_cmp_reg_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  879. l : pasmlabel);
  880. begin
  881. abstract;
  882. end;
  883. procedure tcg.a_cmp_reg_reg_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : pasmlabel);
  884. begin
  885. abstract;
  886. end;
  887. procedure tcg.a_cmp_reg_ref_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;reg : tregister;l : pasmlabel);
  888. begin
  889. abstract;
  890. end;
  891. procedure tcg.a_cmp_ref_const_label(list : paasmoutput;size : tcgsize;cmp_op : topcmp;a : aword;reg : tregister;
  892. l : pasmlabel);
  893. begin
  894. abstract;
  895. end;
  896. procedure tcg.a_jmp_cond(list : paasmoutput;cond : TOpCmp;l: pasmlabel);
  897. begin
  898. abstract;
  899. end;
  900. procedure tcg.g_return_from_proc(list : paasmoutput;parasize : aword);
  901. begin
  902. abstract;
  903. end;
  904. procedure tcg.a_param_reg(list : paasmoutput;size : tcgsize;r : tregister;nr : longint);
  905. begin
  906. abstract;
  907. end;
  908. procedure tcg.a_paramaddr_ref(list : paasmoutput;const r : treference;nr : longint);
  909. begin
  910. abstract;
  911. end;
  912. end.
  913. {
  914. $Log$
  915. Revision 1.35 2000-03-01 15:36:13 florian
  916. * some new stuff for the new cg
  917. Revision 1.34 2000/02/20 20:49:46 florian
  918. * newcg is compiling
  919. * fixed the dup id problem reported by Paul Y.
  920. Revision 1.33 2000/01/07 01:14:53 peter
  921. * updated copyright to 2000
  922. Revision 1.32 1999/12/01 12:42:33 peter
  923. * fixed bug 698
  924. * removed some notes about unused vars
  925. Revision 1.31 1999/11/05 13:15:00 florian
  926. * some fixes to get the new cg compiling again
  927. Revision 1.30 1999/11/05 07:05:56 jonas
  928. + a_jmp_cond()
  929. Revision 1.29 1999/10/21 16:41:41 florian
  930. * problems with readln fixed: esi wasn't restored correctly when
  931. reading ordinal fields of objects futher the register allocation
  932. didn't take care of the extra register when reading ordinal values
  933. * enumerations can now be used in constant indexes of properties
  934. Revision 1.28 1999/10/12 21:20:46 florian
  935. * new codegenerator compiles again
  936. Revision 1.27 1999/09/29 11:46:20 florian
  937. * fixed bug 292 from bugs directory
  938. Revision 1.26 1999/09/14 11:16:09 florian
  939. * only small updates to work with the current compiler
  940. Revision 1.25 1999/09/03 13:09:09 jonas
  941. * fixed typo regarding scratchregs pointer
  942. Revision 1.24 1999/08/26 14:51:54 jonas
  943. * changed get_scratch_reg so it actually uses the
  944. scratch_reg_array_pointer
  945. Revision 1.23 1999/08/25 12:00:11 jonas
  946. * changed pai386, paippc and paiapha (same for tai*) to paicpu (taicpu)
  947. Revision 1.22 1999/08/18 17:05:55 florian
  948. + implemented initilizing of data for the new code generator
  949. so it should compile now simple programs
  950. Revision 1.21 1999/08/07 14:21:08 florian
  951. * some small problems fixed
  952. Revision 1.20 1999/08/06 18:05:52 florian
  953. * implemented some stuff for assignments
  954. Revision 1.19 1999/08/06 17:00:54 florian
  955. + definition of concatcopy
  956. Revision 1.18 1999/08/06 16:37:45 jonas
  957. * completed bugfix done by Florian o I wouldn't get conflicts :)
  958. Revision 1.17 1999/08/06 16:27:26 florian
  959. * for Jonas: else he will get conflicts
  960. Revision 1.16 1999/08/06 16:04:05 michael
  961. + introduced tainstruction
  962. Revision 1.15 1999/08/06 15:53:50 florian
  963. * made the alpha version compilable
  964. Revision 1.14 1999/08/06 14:15:51 florian
  965. * made the alpha version compilable
  966. Revision 1.13 1999/08/06 13:26:50 florian
  967. * more changes ...
  968. Revision 1.12 1999/08/05 17:10:56 florian
  969. * some more additions, especially procedure
  970. exit code generation
  971. Revision 1.11 1999/08/05 14:58:11 florian
  972. * some fixes for the floating point registers
  973. * more things for the new code generator
  974. Revision 1.10 1999/08/04 00:23:52 florian
  975. * renamed i386asm and i386base to cpuasm and cpubase
  976. Revision 1.9 1999/08/02 23:13:21 florian
  977. * more changes to compile for the Alpha
  978. Revision 1.8 1999/08/02 17:14:07 florian
  979. + changed the temp. generator to an object
  980. Revision 1.7 1999/08/01 23:05:55 florian
  981. * changes to compile with FPC
  982. Revision 1.6 1999/08/01 18:22:33 florian
  983. * made it again compilable
  984. Revision 1.5 1999/01/23 23:29:46 florian
  985. * first running version of the new code generator
  986. * when compiling exceptions under Linux fixed
  987. Revision 1.4 1999/01/13 22:52:36 florian
  988. + YES, finally the new code generator is compilable, but it doesn't run yet :(
  989. Revision 1.3 1998/12/26 15:20:30 florian
  990. + more changes for the new version
  991. Revision 1.2 1998/12/15 22:18:55 florian
  992. * some code added
  993. Revision 1.1 1998/12/15 16:32:58 florian
  994. + first version, derived from old routines
  995. }