assemble.pas 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430
  1. {
  2. Copyright (c) 1998-2004 by Peter Vreman
  3. This unit handles the assemblerfile write and assembler calls of FPC
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. {# @abstract(This unit handles the assembler file write and assembler calls of FPC)
  18. Handles the calls to the actual external assemblers, as well as the generation
  19. of object files for smart linking. Also contains the base class for writing
  20. the assembler statements to file.
  21. }
  22. unit assemble;
  23. {$i fpcdefs.inc}
  24. interface
  25. uses
  26. {$IFDEF USE_SYSUTILS}
  27. sysutils,
  28. {$ELSE USE_SYSUTILS}
  29. strings,
  30. dos,
  31. {$ENDIF USE_SYSUTILS}
  32. systems,globtype,globals,aasmbase,aasmtai,ogbase;
  33. const
  34. { maximum of aasmoutput lists there will be }
  35. maxoutputlists = 20;
  36. { buffer size for writing the .s file }
  37. AsmOutSize=32768;
  38. type
  39. TAssembler=class(TAbstractAssembler)
  40. public
  41. {filenames}
  42. path : pathstr;
  43. name : namestr;
  44. asmfile, { current .s and .o file }
  45. objfile : string;
  46. ppufilename : string;
  47. asmprefix : string;
  48. SmartAsm : boolean;
  49. SmartFilesCount,
  50. SmartHeaderCount : longint;
  51. Constructor Create(smart:boolean);virtual;
  52. Destructor Destroy;override;
  53. procedure NextSmartName(place:tcutplace);
  54. procedure MakeObject;virtual;abstract;
  55. end;
  56. {# This is the base class which should be overriden for each each
  57. assembler writer. It is used to actually assembler a file,
  58. and write the output to the assembler file.
  59. }
  60. TExternalAssembler=class(TAssembler)
  61. private
  62. procedure CreateSmartLinkPath(const s:string);
  63. protected
  64. {outfile}
  65. AsmSize,
  66. AsmStartSize,
  67. outcnt : longint;
  68. outbuf : array[0..AsmOutSize-1] of char;
  69. outfile : file;
  70. ioerror : boolean;
  71. public
  72. {# Returns the complete path and executable name of the assembler
  73. program.
  74. It first tries looking in the UTIL directory if specified,
  75. otherwise it searches in the free pascal binary directory, in
  76. the current working directory and then in the directories
  77. in the $PATH environment.}
  78. Function FindAssembler:string;
  79. {# Actually does the call to the assembler file. Returns false
  80. if the assembling of the file failed.}
  81. Function CallAssembler(const command:string; const para:TCmdStr):Boolean;
  82. Function DoAssemble:boolean;virtual;
  83. Procedure RemoveAsm;
  84. Procedure AsmFlush;
  85. Procedure AsmClear;
  86. {# Write a string to the assembler file }
  87. Procedure AsmWrite(const s:string);
  88. {# Write a string to the assembler file }
  89. Procedure AsmWritePChar(p:pchar);
  90. {# Write a string to the assembler file followed by a new line }
  91. Procedure AsmWriteLn(const s:string);
  92. {# Write a new line to the assembler file }
  93. Procedure AsmLn;
  94. procedure AsmCreate(Aplace:tcutplace);
  95. procedure AsmClose;
  96. {# This routine should be overriden for each assembler, it is used
  97. to actually write the abstract assembler stream to file.}
  98. procedure WriteTree(p:TAAsmoutput);virtual;
  99. {# This routine should be overriden for each assembler, it is used
  100. to actually write all the different abstract assembler streams
  101. by calling for each stream type, the @var(WriteTree) method.}
  102. procedure WriteAsmList;virtual;
  103. {# Constructs the command line for calling the assembler }
  104. function MakeCmdLine: TCmdStr;
  105. public
  106. Constructor Create(smart:boolean);override;
  107. procedure MakeObject;override;
  108. end;
  109. TInternalAssembler=class(TAssembler)
  110. private
  111. FCObjOutput : TObjOutputclass;
  112. { the aasmoutput lists that need to be processed }
  113. lists : byte;
  114. list : array[1..maxoutputlists] of TAAsmoutput;
  115. { current processing }
  116. currlistidx : byte;
  117. currlist : TAAsmoutput;
  118. procedure convertstab(p:pchar);
  119. function MaybeNextList(var hp:Tai):boolean;
  120. function TreePass0(hp:Tai):Tai;
  121. function TreePass1(hp:Tai):Tai;
  122. function TreePass2(hp:Tai):Tai;
  123. procedure writetree;
  124. procedure writetreesmart;
  125. protected
  126. ObjData : TObjData;
  127. ObjOutput : tObjOutput;
  128. property CObjOutput:TObjOutputclass read FCObjOutput write FCObjOutput;
  129. public
  130. constructor create(smart:boolean);override;
  131. destructor destroy;override;
  132. procedure MakeObject;override;
  133. end;
  134. TAssemblerClass = class of TAssembler;
  135. Procedure GenerateAsm(smart:boolean);
  136. Procedure OnlyAsm;
  137. procedure RegisterAssembler(const r:tasminfo;c:TAssemblerClass);
  138. procedure InitAssembler;
  139. procedure DoneAssembler;
  140. Implementation
  141. uses
  142. {$ifdef hasunix}
  143. {$ifdef havelinuxrtl10}
  144. linux,
  145. {$else}
  146. unix,
  147. {$endif}
  148. {$endif}
  149. cutils,script,fmodule,verbose,
  150. {$ifdef memdebug}
  151. cclasses,
  152. {$endif memdebug}
  153. {$ifdef m68k}
  154. cpuinfo,
  155. {$endif m68k}
  156. aasmcpu
  157. ;
  158. var
  159. CAssembler : array[tasm] of TAssemblerClass;
  160. {*****************************************************************************
  161. TAssembler
  162. *****************************************************************************}
  163. Constructor TAssembler.Create(smart:boolean);
  164. begin
  165. { load start values }
  166. asmfile:=current_module.get_asmfilename;
  167. objfile:=current_module.objfilename^;
  168. name:=Lower(current_module.modulename^);
  169. path:=current_module.outputpath^;
  170. asmprefix := current_module.asmprefix^;
  171. if not assigned(current_module.outputpath) then
  172. ppufilename := ''
  173. else
  174. ppufilename := current_module.ppufilename^;
  175. SmartAsm:=smart;
  176. SmartFilesCount:=0;
  177. SmartHeaderCount:=0;
  178. SmartLinkOFiles.Clear;
  179. end;
  180. Destructor TAssembler.Destroy;
  181. begin
  182. end;
  183. procedure TAssembler.NextSmartName(place:tcutplace);
  184. var
  185. s : string;
  186. begin
  187. inc(SmartFilesCount);
  188. if SmartFilesCount>999999 then
  189. Message(asmw_f_too_many_asm_files);
  190. case place of
  191. cut_begin :
  192. begin
  193. inc(SmartHeaderCount);
  194. s:=asmprefix+tostr(SmartHeaderCount)+'h';
  195. end;
  196. cut_normal :
  197. s:=asmprefix+tostr(SmartHeaderCount)+'s';
  198. cut_end :
  199. s:=asmprefix+tostr(SmartHeaderCount)+'t';
  200. end;
  201. AsmFile:=Path+FixFileName(s+tostr(SmartFilesCount)+target_info.asmext);
  202. ObjFile:=Path+FixFileName(s+tostr(SmartFilesCount)+target_info.objext);
  203. { insert in container so it can be cleared after the linking }
  204. SmartLinkOFiles.Insert(Objfile);
  205. end;
  206. {*****************************************************************************
  207. TExternalAssembler
  208. *****************************************************************************}
  209. Function DoPipe:boolean;
  210. begin
  211. DoPipe:=(cs_asm_pipe in aktglobalswitches) and
  212. (([cs_asm_leave,cs_link_on_target] * aktglobalswitches) = []) and
  213. ((target_asm.id in [as_gas,as_darwin]));
  214. end;
  215. Constructor TExternalAssembler.Create(smart:boolean);
  216. begin
  217. inherited Create(smart);
  218. if SmartAsm then
  219. begin
  220. path:=FixPath(path+FixFileName(name)+target_info.smartext,false);
  221. CreateSmartLinkPath(path);
  222. end;
  223. Outcnt:=0;
  224. end;
  225. procedure TExternalAssembler.CreateSmartLinkPath(const s:string);
  226. var
  227. {$IFDEF USE_SYSUTILS}
  228. dir : TSearchRec;
  229. {$ELSE USE_SYSUTILS}
  230. dir : searchrec;
  231. {$ENDIF USE_SYSUTILS}
  232. hs : string;
  233. begin
  234. if PathExists(s) then
  235. begin
  236. { the path exists, now we clean only all the .o and .s files }
  237. { .o files }
  238. {$IFDEF USE_SYSUTILS}
  239. if findfirst(s+source_info.dirsep+'*'+target_info.objext,faAnyFile,dir) = 0
  240. then repeat
  241. RemoveFile(s+source_info.dirsep+dir.name);
  242. until findnext(dir) <> 0;
  243. {$ELSE USE_SYSUTILS}
  244. findfirst(s+source_info.dirsep+'*'+target_info.objext,anyfile,dir);
  245. while (doserror=0) do
  246. begin
  247. RemoveFile(s+source_info.dirsep+dir.name);
  248. findnext(dir);
  249. end;
  250. {$ENDIF USE_SYSUTILS}
  251. findclose(dir);
  252. { .s files }
  253. {$IFDEF USE_SYSUTILS}
  254. if findfirst(s+source_info.dirsep+'*'+target_info.asmext,faAnyFile,dir) = 0
  255. then repeat
  256. RemoveFile(s+source_info.dirsep+dir.name);
  257. until findnext(dir) <> 0;
  258. {$ELSE USE_SYSUTILS}
  259. findfirst(s+source_info.dirsep+'*'+target_info.asmext,anyfile,dir);
  260. while (doserror=0) do
  261. begin
  262. RemoveFile(s+source_info.dirsep+dir.name);
  263. findnext(dir);
  264. end;
  265. {$ENDIF USE_SYSUTILS}
  266. findclose(dir);
  267. end
  268. else
  269. begin
  270. hs:=s;
  271. if hs[length(hs)] in ['/','\'] then
  272. delete(hs,length(hs),1);
  273. {$I-}
  274. mkdir(hs);
  275. {$I+}
  276. if ioresult<>0 then;
  277. end;
  278. end;
  279. const
  280. lastas : byte=255;
  281. var
  282. LastASBin : pathstr;
  283. Function TExternalAssembler.FindAssembler:string;
  284. var
  285. asfound : boolean;
  286. UtilExe : string;
  287. begin
  288. asfound:=false;
  289. if cs_link_on_target in aktglobalswitches then
  290. begin
  291. { If linking on target, don't add any path PM }
  292. FindAssembler:=utilsprefix+AddExtension(target_asm.asmbin,target_info.exeext);
  293. exit;
  294. end
  295. else
  296. UtilExe:=utilsprefix+AddExtension(target_asm.asmbin,source_info.exeext);
  297. if lastas<>ord(target_asm.id) then
  298. begin
  299. lastas:=ord(target_asm.id);
  300. { is an assembler passed ? }
  301. if utilsdirectory<>'' then
  302. asfound:=FindFile(UtilExe,utilsdirectory,LastASBin);
  303. if not AsFound then
  304. asfound:=FindExe(UtilExe,LastASBin);
  305. if (not asfound) and not(cs_asm_extern in aktglobalswitches) then
  306. begin
  307. Message1(exec_e_assembler_not_found,LastASBin);
  308. aktglobalswitches:=aktglobalswitches+[cs_asm_extern];
  309. end;
  310. if asfound then
  311. Message1(exec_t_using_assembler,LastASBin);
  312. end;
  313. FindAssembler:=LastASBin;
  314. end;
  315. Function TExternalAssembler.CallAssembler(const command:string; const para:TCmdStr):Boolean;
  316. {$IFDEF USE_SYSUTILS}
  317. var
  318. DosExitCode:Integer;
  319. {$ENDIF USE_SYSUTILS}
  320. begin
  321. callassembler:=true;
  322. if not(cs_asm_extern in aktglobalswitches) then
  323. {$IFDEF USE_SYSUTILS}
  324. try
  325. DosExitCode := ExecuteProcess(command,para);
  326. if DosExitCode <>0
  327. then begin
  328. Message1(exec_e_error_while_assembling,tostr(dosexitcode));
  329. callassembler:=false;
  330. end;
  331. except on E:EOSError do
  332. begin
  333. Message1(exec_e_cant_call_assembler,tostr(E.ErrorCode));
  334. aktglobalswitches:=aktglobalswitches+[cs_asm_extern];
  335. callassembler:=false;
  336. end
  337. end
  338. {$ELSE USE_SYSUTILS}
  339. begin
  340. swapvectors;
  341. exec(maybequoted(command),para);
  342. swapvectors;
  343. if (doserror<>0) then
  344. begin
  345. Message1(exec_e_cant_call_assembler,tostr(doserror));
  346. aktglobalswitches:=aktglobalswitches+[cs_asm_extern];
  347. callassembler:=false;
  348. end
  349. else
  350. if (dosexitcode<>0) then
  351. begin
  352. Message1(exec_e_error_while_assembling,tostr(dosexitcode));
  353. callassembler:=false;
  354. end;
  355. end
  356. {$ENDIF USE_SYSUTILS}
  357. else
  358. AsmRes.AddAsmCommand(command,para,name);
  359. end;
  360. procedure TExternalAssembler.RemoveAsm;
  361. var
  362. g : file;
  363. begin
  364. if cs_asm_leave in aktglobalswitches then
  365. exit;
  366. if cs_asm_extern in aktglobalswitches then
  367. AsmRes.AddDeleteCommand(AsmFile)
  368. else
  369. begin
  370. assign(g,AsmFile);
  371. {$I-}
  372. erase(g);
  373. {$I+}
  374. if ioresult<>0 then;
  375. end;
  376. end;
  377. Function TExternalAssembler.DoAssemble:boolean;
  378. begin
  379. DoAssemble:=true;
  380. if DoPipe then
  381. exit;
  382. if not(cs_asm_extern in aktglobalswitches) then
  383. begin
  384. if SmartAsm then
  385. begin
  386. if (SmartFilesCount<=1) then
  387. Message1(exec_i_assembling_smart,name);
  388. end
  389. else
  390. Message1(exec_i_assembling,name);
  391. end;
  392. if CallAssembler(FindAssembler,MakeCmdLine) then
  393. RemoveAsm
  394. else
  395. begin
  396. DoAssemble:=false;
  397. GenerateError;
  398. end;
  399. end;
  400. Procedure TExternalAssembler.AsmFlush;
  401. begin
  402. if outcnt>0 then
  403. begin
  404. { suppress i/o error }
  405. {$i-}
  406. BlockWrite(outfile,outbuf,outcnt);
  407. {$i+}
  408. ioerror:=ioerror or (ioresult<>0);
  409. outcnt:=0;
  410. end;
  411. end;
  412. Procedure TExternalAssembler.AsmClear;
  413. begin
  414. outcnt:=0;
  415. end;
  416. Procedure TExternalAssembler.AsmWrite(const s:string);
  417. begin
  418. if OutCnt+length(s)>=AsmOutSize then
  419. AsmFlush;
  420. Move(s[1],OutBuf[OutCnt],length(s));
  421. inc(OutCnt,length(s));
  422. inc(AsmSize,length(s));
  423. end;
  424. Procedure TExternalAssembler.AsmWriteLn(const s:string);
  425. begin
  426. AsmWrite(s);
  427. AsmLn;
  428. end;
  429. Procedure TExternalAssembler.AsmWritePChar(p:pchar);
  430. var
  431. i,j : longint;
  432. begin
  433. i:=StrLen(p);
  434. j:=i;
  435. while j>0 do
  436. begin
  437. i:=min(j,AsmOutSize);
  438. if OutCnt+i>=AsmOutSize then
  439. AsmFlush;
  440. Move(p[0],OutBuf[OutCnt],i);
  441. inc(OutCnt,i);
  442. inc(AsmSize,i);
  443. dec(j,i);
  444. p:=pchar(@p[i]);
  445. end;
  446. end;
  447. Procedure TExternalAssembler.AsmLn;
  448. begin
  449. if OutCnt>=AsmOutSize-2 then
  450. AsmFlush;
  451. if (cs_link_on_target in aktglobalswitches) then
  452. begin
  453. OutBuf[OutCnt]:=target_info.newline[1];
  454. inc(OutCnt);
  455. inc(AsmSize);
  456. if length(target_info.newline)>1 then
  457. begin
  458. OutBuf[OutCnt]:=target_info.newline[2];
  459. inc(OutCnt);
  460. inc(AsmSize);
  461. end;
  462. end
  463. else
  464. begin
  465. OutBuf[OutCnt]:=source_info.newline[1];
  466. inc(OutCnt);
  467. inc(AsmSize);
  468. if length(source_info.newline)>1 then
  469. begin
  470. OutBuf[OutCnt]:=source_info.newline[2];
  471. inc(OutCnt);
  472. inc(AsmSize);
  473. end;
  474. end;
  475. end;
  476. function TExternalAssembler.MakeCmdLine: TCmdStr;
  477. begin
  478. result:=target_asm.asmcmd;
  479. {$ifdef m68k}
  480. if aktoptprocessor = MC68020 then
  481. result:='-m68020 '+result
  482. else
  483. result:='-m68000 '+result;
  484. {$endif}
  485. if (cs_link_on_target in aktglobalswitches) then
  486. begin
  487. Replace(result,'$ASM',maybequoted(ScriptFixFileName(AsmFile)));
  488. Replace(result,'$OBJ',maybequoted(ScriptFixFileName(ObjFile)));
  489. end
  490. else
  491. begin
  492. {$ifdef hasunix}
  493. if DoPipe then
  494. Replace(result,'$ASM','')
  495. else
  496. {$endif}
  497. Replace(result,'$ASM',maybequoted(AsmFile));
  498. Replace(result,'$OBJ',maybequoted(ObjFile));
  499. end;
  500. end;
  501. procedure TExternalAssembler.AsmCreate(Aplace:tcutplace);
  502. begin
  503. if SmartAsm then
  504. NextSmartName(Aplace);
  505. {$ifdef hasunix}
  506. if DoPipe then
  507. begin
  508. Message1(exec_i_assembling_pipe,asmfile);
  509. POpen(outfile,FindAssembler+' '+MakeCmdLine,'W');
  510. end
  511. else
  512. {$endif}
  513. begin
  514. Assign(outfile,asmfile);
  515. {$I-}
  516. Rewrite(outfile,1);
  517. {$I+}
  518. if ioresult<>0 then
  519. begin
  520. ioerror:=true;
  521. Message1(exec_d_cant_create_asmfile,asmfile);
  522. end;
  523. end;
  524. outcnt:=0;
  525. AsmSize:=0;
  526. AsmStartSize:=0;
  527. end;
  528. procedure TExternalAssembler.AsmClose;
  529. var
  530. f : file;
  531. FileAge : longint;
  532. begin
  533. AsmFlush;
  534. {$ifdef hasunix}
  535. if DoPipe then
  536. begin
  537. if PClose(outfile) <> 0 then
  538. GenerateError;
  539. end
  540. else
  541. {$endif}
  542. begin
  543. {Touch Assembler time to ppu time is there is a ppufilename}
  544. if ppufilename<>'' then
  545. begin
  546. Assign(f,ppufilename);
  547. {$I-}
  548. reset(f,1);
  549. {$I+}
  550. if ioresult=0 then
  551. begin
  552. {$IFDEF USE_SYSUTILS}
  553. FileAge := FileGetDate(GetFileHandle(f));
  554. {$ELSE USE_SYSUTILS}
  555. GetFTime(f, FileAge);
  556. {$ENDIF USE_SYSUTILS}
  557. close(f);
  558. reset(outfile,1);
  559. {$IFDEF USE_SYSUTILS}
  560. FileSetDate(GetFileHandle(outFile),FileAge);
  561. {$ELSE USE_SYSUTILS}
  562. SetFTime(f, FileAge);
  563. {$ENDIF USE_SYSUTILS}
  564. end;
  565. end;
  566. close(outfile);
  567. end;
  568. end;
  569. procedure TExternalAssembler.WriteTree(p:TAAsmoutput);
  570. begin
  571. end;
  572. procedure TExternalAssembler.WriteAsmList;
  573. begin
  574. end;
  575. procedure TExternalAssembler.MakeObject;
  576. begin
  577. AsmCreate(cut_normal);
  578. WriteAsmList;
  579. AsmClose;
  580. if not(ioerror) then
  581. DoAssemble;
  582. end;
  583. {*****************************************************************************
  584. TInternalAssembler
  585. *****************************************************************************}
  586. constructor TInternalAssembler.create(smart:boolean);
  587. begin
  588. inherited create(smart);
  589. ObjOutput:=nil;
  590. ObjData:=nil;
  591. SmartAsm:=smart;
  592. end;
  593. destructor TInternalAssembler.destroy;
  594. {$ifdef MEMDEBUG}
  595. var
  596. d : tmemdebug;
  597. {$endif}
  598. begin
  599. {$ifdef MEMDEBUG}
  600. d := tmemdebug.create(name+' - agbin');
  601. {$endif}
  602. if assigned(ObjData) then
  603. ObjData.free;
  604. if assigned(ObjOutput) then
  605. ObjOutput.free;
  606. {$ifdef MEMDEBUG}
  607. d.free;
  608. {$endif}
  609. end;
  610. procedure TInternalAssembler.convertstab(p:pchar);
  611. function consumecomma(var p:pchar):boolean;
  612. begin
  613. while (p^=' ') do
  614. inc(p);
  615. result:=(p^=',');
  616. inc(p);
  617. end;
  618. function consumenumber(var p:pchar;out value:longint):boolean;
  619. var
  620. hs : string;
  621. len,
  622. code : integer;
  623. begin
  624. value:=0;
  625. while (p^=' ') do
  626. inc(p);
  627. len:=0;
  628. while (p^ in ['0'..'9']) do
  629. begin
  630. inc(len);
  631. hs[len]:=p^;
  632. inc(p);
  633. end;
  634. if len>0 then
  635. begin
  636. hs[0]:=chr(len);
  637. val(hs,value,code);
  638. end
  639. else
  640. code:=-1;
  641. result:=(code=0);
  642. end;
  643. function consumeoffset(var p:pchar;out relocsym:tobjsymbol;out value:longint):boolean;
  644. var
  645. hs : string;
  646. len,
  647. code : integer;
  648. pstart : pchar;
  649. sym : tobjsymbol;
  650. exprvalue : longint;
  651. gotmin,
  652. dosub : boolean;
  653. begin
  654. result:=false;
  655. value:=0;
  656. relocsym:=nil;
  657. gotmin:=false;
  658. repeat
  659. dosub:=false;
  660. exprvalue:=0;
  661. if gotmin then
  662. begin
  663. dosub:=true;
  664. gotmin:=false;
  665. end;
  666. while (p^=' ') do
  667. inc(p);
  668. case p^ of
  669. #0 :
  670. break;
  671. ' ' :
  672. inc(p);
  673. '0'..'9' :
  674. begin
  675. len:=0;
  676. while (p^ in ['0'..'9']) do
  677. begin
  678. inc(len);
  679. hs[len]:=p^;
  680. inc(p);
  681. end;
  682. hs[0]:=chr(len);
  683. val(hs,exprvalue,code);
  684. end;
  685. '.','_',
  686. 'A'..'Z',
  687. 'a'..'z' :
  688. begin
  689. pstart:=p;
  690. while not(p^ in [#0,' ','-','+']) do
  691. inc(p);
  692. len:=p-pstart;
  693. if len>255 then
  694. internalerror(200509187);
  695. move(pstart^,hs[1],len);
  696. hs[0]:=chr(len);
  697. sym:=objdata.symbolref(hs);
  698. { Second symbol? }
  699. if assigned(relocsym) then
  700. begin
  701. if (relocsym.objsection<>sym.objsection) then
  702. internalerror(2005091810);
  703. relocsym:=nil;
  704. end
  705. else
  706. relocsym:=sym;
  707. exprvalue:=sym.address;
  708. end;
  709. '+' :
  710. begin
  711. { nothing, by default addition is done }
  712. inc(p);
  713. end;
  714. '-' :
  715. begin
  716. gotmin:=true;
  717. inc(p);
  718. end;
  719. else
  720. internalerror(200509189);
  721. end;
  722. if dosub then
  723. dec(value,exprvalue)
  724. else
  725. inc(value,exprvalue);
  726. until false;
  727. result:=true;
  728. end;
  729. const
  730. N_Function = $24; { function or const }
  731. var
  732. ofs,
  733. nline,
  734. nidx,
  735. nother,
  736. i : longint;
  737. relocsym : TObjSymbol;
  738. pstr,
  739. pcurr,
  740. pendquote : pchar;
  741. begin
  742. pcurr:=nil;
  743. pstr:=nil;
  744. pendquote:=nil;
  745. { Parse string part }
  746. if p[0]='"' then
  747. begin
  748. pstr:=@p[1];
  749. { Ignore \" inside the string }
  750. i:=1;
  751. while not((p[i]='"') and (p[i-1]<>'\')) and
  752. (p[i]<>#0) do
  753. inc(i);
  754. pendquote:=@p[i];
  755. pendquote^:=#0;
  756. pcurr:=@p[i+1];
  757. if not consumecomma(pcurr) then
  758. internalerror(200509181);
  759. end
  760. else
  761. pcurr:=p;
  762. { When in pass 1 then only alloc and leave }
  763. if ObjData.currpass=1 then
  764. ObjData.allocstab(pstr)
  765. else
  766. begin
  767. { Stabs format: nidx,nother,nline[,offset] }
  768. if not consumenumber(pcurr,nidx) then
  769. internalerror(200509182);
  770. if not consumecomma(pcurr) then
  771. internalerror(200509183);
  772. if not consumenumber(pcurr,nother) then
  773. internalerror(200509184);
  774. if not consumecomma(pcurr) then
  775. internalerror(200509185);
  776. if not consumenumber(pcurr,nline) then
  777. internalerror(200509186);
  778. if consumecomma(pcurr) then
  779. consumeoffset(pcurr,relocsym,ofs)
  780. else
  781. begin
  782. ofs:=0;
  783. relocsym:=nil;
  784. end;
  785. if (nidx=N_Function) and
  786. (tf_use_function_relative_addresses in target_info.flags) then
  787. ofs:=0;
  788. ObjData.writestab(ofs,relocsym,byte(nidx),byte(nother),word(nline),pstr);
  789. end;
  790. if assigned(pendquote) then
  791. pendquote^:='"';
  792. end;
  793. function TInternalAssembler.MaybeNextList(var hp:Tai):boolean;
  794. begin
  795. { maybe end of list }
  796. while not assigned(hp) do
  797. begin
  798. if currlistidx<lists then
  799. begin
  800. inc(currlistidx);
  801. currlist:=list[currlistidx];
  802. hp:=Tai(currList.first);
  803. end
  804. else
  805. begin
  806. MaybeNextList:=false;
  807. exit;
  808. end;
  809. end;
  810. MaybeNextList:=true;
  811. end;
  812. function TInternalAssembler.TreePass0(hp:Tai):Tai;
  813. var
  814. objsym : TObjSymbol;
  815. begin
  816. while assigned(hp) do
  817. begin
  818. case hp.typ of
  819. ait_align :
  820. begin
  821. { always use the maximum fillsize in this pass to avoid possible
  822. short jumps to become out of range }
  823. Tai_align(hp).fillsize:=Tai_align(hp).aligntype;
  824. ObjData.alloc(Tai_align(hp).fillsize);
  825. end;
  826. ait_datablock :
  827. begin
  828. ObjData.allocalign(used_align(size_2_align(Tai_datablock(hp).size),0,ObjData.CurrObjSec.secalign));
  829. ObjData.SymbolDefine(Tai_datablock(hp).sym);
  830. ObjData.alloc(Tai_datablock(hp).size);
  831. end;
  832. ait_real_80bit :
  833. ObjData.alloc(10);
  834. ait_real_64bit :
  835. ObjData.alloc(8);
  836. ait_real_32bit :
  837. ObjData.alloc(4);
  838. ait_comp_64bit :
  839. ObjData.alloc(8);
  840. ait_const:
  841. ObjData.alloc(tai_const(hp).size);
  842. ait_section:
  843. begin
  844. ObjData.CreateSection(Tai_section(hp).sectype,Tai_section(hp).name^);
  845. Tai_section(hp).sec:=ObjData.CurrObjSec;
  846. end;
  847. ait_symbol :
  848. ObjData.SymbolDefine(Tai_symbol(hp).sym);
  849. ait_label :
  850. ObjData.SymbolDefine(Tai_label(hp).labsym);
  851. ait_string :
  852. ObjData.alloc(Tai_string(hp).len);
  853. ait_instruction :
  854. begin
  855. { reset instructions which could change in pass 2 }
  856. Taicpu(hp).resetpass2;
  857. ObjData.alloc(Taicpu(hp).Pass1(ObjData));
  858. end;
  859. ait_cutobject :
  860. if SmartAsm then
  861. break;
  862. end;
  863. hp:=Tai(hp.next);
  864. end;
  865. TreePass0:=hp;
  866. end;
  867. function TInternalAssembler.TreePass1(hp:Tai):Tai;
  868. var
  869. InlineLevel,
  870. i : longint;
  871. objsym : TObjSymbol;
  872. begin
  873. inlinelevel:=0;
  874. while assigned(hp) do
  875. begin
  876. case hp.typ of
  877. ait_align :
  878. begin
  879. { here we must determine the fillsize which is used in pass2 }
  880. Tai_align(hp).fillsize:=align(ObjData.CurrObjSec.Size,Tai_align(hp).aligntype)-
  881. ObjData.CurrObjSec.Size;
  882. ObjData.alloc(Tai_align(hp).fillsize);
  883. end;
  884. ait_datablock :
  885. begin
  886. if (oso_data in ObjData.CurrObjSec.secoptions) then
  887. Message(asmw_e_alloc_data_only_in_bss);
  888. ObjData.allocalign(used_align(size_2_align(Tai_datablock(hp).size),0,ObjData.CurrObjSec.secalign));
  889. objsym:=ObjData.SymbolDefine(Tai_datablock(hp).sym);
  890. objsym.size:=Tai_datablock(hp).size;
  891. ObjData.alloc(Tai_datablock(hp).size);
  892. end;
  893. ait_real_80bit :
  894. ObjData.alloc(10);
  895. ait_real_64bit :
  896. ObjData.alloc(8);
  897. ait_real_32bit :
  898. ObjData.alloc(4);
  899. ait_comp_64bit :
  900. ObjData.alloc(8);
  901. ait_const:
  902. begin
  903. ObjData.alloc(tai_const(hp).size);
  904. if assigned(Tai_const(hp).sym) then
  905. ObjData.SymbolRef(Tai_const(hp).sym);
  906. if assigned(Tai_const(hp).endsym) then
  907. ObjData.SymbolRef(Tai_const(hp).endsym);
  908. end;
  909. ait_section:
  910. begin
  911. { use cached value }
  912. ObjData.setsection(Tai_section(hp).sec);
  913. end;
  914. ait_stab :
  915. begin
  916. if assigned(Tai_stab(hp).str) then
  917. convertstab(Tai_stab(hp).str);
  918. end;
  919. ait_function_name,
  920. ait_force_line : ;
  921. ait_symbol :
  922. ObjData.SymbolDefine(Tai_symbol(hp).sym);
  923. ait_symbol_end :
  924. begin
  925. objsym:=ObjData.SymbolRef(Tai_symbol_end(hp).sym);
  926. objsym.size:=ObjData.CurrObjSec.Size-objsym.offset;
  927. end;
  928. ait_label :
  929. ObjData.SymbolDefine(Tai_label(hp).labsym);
  930. ait_string :
  931. ObjData.alloc(Tai_string(hp).len);
  932. ait_instruction :
  933. ObjData.alloc(Taicpu(hp).Pass1(ObjData));
  934. ait_cutobject :
  935. if SmartAsm then
  936. break;
  937. ait_marker :
  938. if tai_marker(hp).kind=InlineStart then
  939. inc(InlineLevel)
  940. else if tai_marker(hp).kind=InlineEnd then
  941. dec(InlineLevel);
  942. end;
  943. hp:=Tai(hp.next);
  944. end;
  945. TreePass1:=hp;
  946. end;
  947. function TInternalAssembler.TreePass2(hp:Tai):Tai;
  948. var
  949. fillbuffer : tfillbuffer;
  950. InlineLevel,
  951. v : int64;
  952. {$ifdef x86}
  953. co : comp;
  954. {$endif x86}
  955. objsym,
  956. objsymend : TObjSymbol;
  957. begin
  958. inlinelevel:=0;
  959. { main loop }
  960. while assigned(hp) do
  961. begin
  962. case hp.typ of
  963. ait_align :
  964. begin
  965. if (oso_data in ObjData.CurrObjSec.secoptions) then
  966. ObjData.writebytes(Tai_align(hp).calculatefillbuf(fillbuffer)^,Tai_align(hp).fillsize)
  967. else
  968. ObjData.alloc(Tai_align(hp).fillsize);
  969. end;
  970. ait_section :
  971. begin
  972. { use cached value }
  973. ObjData.setsection(Tai_section(hp).sec);
  974. end;
  975. ait_symbol :
  976. begin
  977. ObjOutput.exportsymbol(ObjData.SymbolRef(Tai_symbol(hp).sym));
  978. end;
  979. ait_datablock :
  980. begin
  981. ObjData.allocalign(used_align(size_2_align(Tai_datablock(hp).size),0,ObjData.CurrObjSec.secalign));
  982. ObjOutput.exportsymbol(ObjData.SymbolRef(Tai_datablock(hp).sym));
  983. ObjData.alloc(Tai_datablock(hp).size);
  984. end;
  985. ait_real_80bit :
  986. ObjData.writebytes(Tai_real_80bit(hp).value,10);
  987. ait_real_64bit :
  988. ObjData.writebytes(Tai_real_64bit(hp).value,8);
  989. ait_real_32bit :
  990. ObjData.writebytes(Tai_real_32bit(hp).value,4);
  991. ait_comp_64bit :
  992. begin
  993. {$ifdef x86}
  994. co:=comp(Tai_comp_64bit(hp).value);
  995. ObjData.writebytes(co,8);
  996. {$endif x86}
  997. end;
  998. ait_string :
  999. ObjData.writebytes(Tai_string(hp).str^,Tai_string(hp).len);
  1000. ait_const :
  1001. begin
  1002. case tai_const(hp).consttype of
  1003. aitconst_64bit,
  1004. aitconst_32bit,
  1005. aitconst_16bit,
  1006. aitconst_8bit :
  1007. begin
  1008. if assigned(tai_const(hp).sym) then
  1009. begin
  1010. objsym:=Objdata.SymbolRef(tai_const(hp).sym);
  1011. if assigned(tai_const(hp).endsym) then
  1012. begin
  1013. objsymend:=Objdata.SymbolRef(tai_const(hp).endsym);
  1014. if objsymend.objsection<>objsym.objsection then
  1015. internalerror(200404124);
  1016. v:=objsymend.address-objsym.address+Tai_const(hp).value;
  1017. ObjData.writebytes(v,tai_const(hp).size);
  1018. end
  1019. else
  1020. ObjData.writereloc(Tai_const(hp).value,Tai_const(hp).size,objsym,RELOC_ABSOLUTE);
  1021. end
  1022. else
  1023. ObjData.writebytes(Tai_const(hp).value,tai_const(hp).size);
  1024. end;
  1025. aitconst_rva_symbol :
  1026. ObjData.writereloc(Tai_const(hp).value,sizeof(aint),Objdata.SymbolRef(tai_const(hp).sym),RELOC_RVA);
  1027. end;
  1028. end;
  1029. ait_label :
  1030. begin
  1031. { exporting shouldn't be necessary as labels are local,
  1032. but it's better to be on the safe side (PFV) }
  1033. ObjOutput.exportsymbol(ObjData.SymbolRef(Tai_label(hp).labsym));
  1034. end;
  1035. ait_instruction :
  1036. Taicpu(hp).Pass2(ObjData);
  1037. ait_stab :
  1038. convertstab(Tai_stab(hp).str);
  1039. ait_function_name,
  1040. ait_force_line : ;
  1041. ait_cutobject :
  1042. if SmartAsm then
  1043. break;
  1044. ait_marker :
  1045. if tai_marker(hp).kind=InlineStart then
  1046. inc(InlineLevel)
  1047. else if tai_marker(hp).kind=InlineEnd then
  1048. dec(InlineLevel);
  1049. end;
  1050. hp:=Tai(hp.next);
  1051. end;
  1052. TreePass2:=hp;
  1053. end;
  1054. procedure TInternalAssembler.writetree;
  1055. label
  1056. doexit;
  1057. var
  1058. hp : Tai;
  1059. begin
  1060. ObjOutput:=CObjOutput.Create(false);
  1061. ObjData:=ObjOutput.newObjData(Objfile);
  1062. { Pass 0 }
  1063. ObjData.currpass:=0;
  1064. ObjData.createsection(sec_code,'');
  1065. ObjData.beforealloc;
  1066. { start with list 1 }
  1067. currlistidx:=1;
  1068. currlist:=list[currlistidx];
  1069. hp:=Tai(currList.first);
  1070. while assigned(hp) do
  1071. begin
  1072. hp:=TreePass0(hp);
  1073. MaybeNextList(hp);
  1074. end;
  1075. ObjData.afteralloc;
  1076. { leave if errors have occured }
  1077. if errorcount>0 then
  1078. goto doexit;
  1079. { Pass 1 }
  1080. ObjData.currpass:=1;
  1081. ObjData.resetsections;
  1082. ObjData.beforealloc;
  1083. ObjData.createsection(sec_code,'');
  1084. { start with list 1 }
  1085. currlistidx:=1;
  1086. currlist:=list[currlistidx];
  1087. hp:=Tai(currList.first);
  1088. while assigned(hp) do
  1089. begin
  1090. hp:=TreePass1(hp);
  1091. MaybeNextList(hp);
  1092. end;
  1093. ObjData.createsection(sec_code,'');
  1094. ObjData.afteralloc;
  1095. { leave if errors have occured }
  1096. if errorcount>0 then
  1097. goto doexit;
  1098. { Pass 2 }
  1099. ObjData.currpass:=2;
  1100. ObjData.resetsections;
  1101. ObjData.beforewrite;
  1102. ObjData.createsection(sec_code,'');
  1103. { start with list 1 }
  1104. currlistidx:=1;
  1105. currlist:=list[currlistidx];
  1106. hp:=Tai(currList.first);
  1107. while assigned(hp) do
  1108. begin
  1109. hp:=TreePass2(hp);
  1110. MaybeNextList(hp);
  1111. end;
  1112. ObjData.createsection(sec_code,'');
  1113. ObjData.afterwrite;
  1114. { don't write the .o file if errors have occured }
  1115. if errorcount=0 then
  1116. begin
  1117. { write objectfile }
  1118. ObjOutput.startobjectfile(ObjFile);
  1119. ObjOutput.writeobjectfile(ObjData);
  1120. end;
  1121. doexit:
  1122. { Cleanup }
  1123. ObjData.free;
  1124. ObjData:=nil;
  1125. end;
  1126. procedure TInternalAssembler.writetreesmart;
  1127. var
  1128. hp : Tai;
  1129. startsectype : TAsmSectiontype;
  1130. place: tcutplace;
  1131. begin
  1132. NextSmartName(cut_normal);
  1133. ObjOutput:=CObjOutput.Create(true);
  1134. startsectype:=sec_code;
  1135. { start with list 1 }
  1136. currlistidx:=1;
  1137. currlist:=list[currlistidx];
  1138. hp:=Tai(currList.first);
  1139. while assigned(hp) do
  1140. begin
  1141. ObjData:=ObjOutput.newObjData(Objfile);
  1142. { Pass 0 }
  1143. ObjData.currpass:=0;
  1144. ObjData.resetsections;
  1145. ObjData.beforealloc;
  1146. ObjData.createsection(startsectype,'');
  1147. TreePass0(hp);
  1148. ObjData.afteralloc;
  1149. { leave if errors have occured }
  1150. if errorcount>0 then
  1151. exit;
  1152. { Pass 1 }
  1153. ObjData.currpass:=1;
  1154. ObjData.resetsections;
  1155. ObjData.beforealloc;
  1156. ObjData.createsection(startsectype,'');
  1157. TreePass1(hp);
  1158. ObjData.afteralloc;
  1159. { leave if errors have occured }
  1160. if errorcount>0 then
  1161. break;
  1162. { Pass 2 }
  1163. ObjData.currpass:=2;
  1164. ObjOutput.startobjectfile(Objfile);
  1165. ObjData.resetsections;
  1166. ObjData.beforewrite;
  1167. ObjData.createsection(startsectype,'');
  1168. hp:=TreePass2(hp);
  1169. ObjData.afterwrite;
  1170. { leave if errors have occured }
  1171. if errorcount>0 then
  1172. break;
  1173. { write the current objectfile }
  1174. ObjOutput.writeobjectfile(ObjData);
  1175. ObjData.free;
  1176. ObjData:=nil;
  1177. { end of lists? }
  1178. if not MaybeNextList(hp) then
  1179. break;
  1180. { we will start a new objectfile so reset everything }
  1181. { The place can still change in the next while loop, so don't init }
  1182. { the writer yet (JM) }
  1183. if (hp.typ=ait_cutobject) then
  1184. place := Tai_cutobject(hp).place
  1185. else
  1186. place := cut_normal;
  1187. { avoid empty files }
  1188. startsectype:=sec_code;
  1189. while assigned(hp) and
  1190. (Tai(hp).typ in [ait_marker,ait_comment,ait_section,ait_cutobject]) do
  1191. begin
  1192. if Tai(hp).typ=ait_section then
  1193. startsectype:=Tai_section(hp).sectype;
  1194. if (Tai(hp).typ=ait_cutobject) then
  1195. place:=Tai_cutobject(hp).place;
  1196. hp:=Tai(hp.next);
  1197. end;
  1198. if not MaybeNextList(hp) then
  1199. break;
  1200. { start next objectfile }
  1201. NextSmartName(place);
  1202. end;
  1203. end;
  1204. procedure TInternalAssembler.MakeObject;
  1205. var to_do:set of Tasmlist;
  1206. i:Tasmlist;
  1207. {$ifdef MEMDEBUG}
  1208. d : tmemdebug;
  1209. {$endif}
  1210. procedure addlist(p:TAAsmoutput);
  1211. begin
  1212. inc(lists);
  1213. list[lists]:=p;
  1214. end;
  1215. begin
  1216. to_do:=[low(Tasmlist)..high(Tasmlist)];
  1217. if usedeffileforexports then
  1218. exclude(to_do,al_exports);
  1219. {$warning TODO internal writer support for dwarf}
  1220. exclude(to_do,al_dwarf);
  1221. if not(tf_section_threadvars in target_info.flags) then
  1222. exclude(to_do,al_threadvars);
  1223. for i:=low(Tasmlist) to high(Tasmlist) do
  1224. if (i in to_do) and (asmlist[i]<>nil) then
  1225. addlist(asmlist[i]);
  1226. if SmartAsm then
  1227. writetreesmart
  1228. else
  1229. writetree;
  1230. (*
  1231. if assigned(objectlibrary) then
  1232. begin
  1233. if objectlibrary<>current_module.librarydata then
  1234. internalerror(200603013);
  1235. {$ifdef MEMDEBUG}
  1236. d:=tmemdebug.create(modulename^+' - librarydata');
  1237. {$endif}
  1238. objectlibrary.free;
  1239. objectlibrary:=nil;
  1240. current_module.librarydata:=nil;
  1241. {$ifdef MEMDEBUG}
  1242. d.free;
  1243. {$endif}
  1244. end;
  1245. *)
  1246. end;
  1247. {*****************************************************************************
  1248. Generate Assembler Files Main Procedure
  1249. *****************************************************************************}
  1250. Procedure GenerateAsm(smart:boolean);
  1251. var
  1252. a : TAssembler;
  1253. begin
  1254. if not assigned(CAssembler[target_asm.id]) then
  1255. Message(asmw_f_assembler_output_not_supported);
  1256. a:=CAssembler[target_asm.id].Create(smart);
  1257. a.MakeObject;
  1258. a.Free;
  1259. end;
  1260. Procedure OnlyAsm;
  1261. var
  1262. a : TExternalAssembler;
  1263. begin
  1264. a:=TExternalAssembler.Create(false);
  1265. a.DoAssemble;
  1266. a.Free;
  1267. end;
  1268. {*****************************************************************************
  1269. Init/Done
  1270. *****************************************************************************}
  1271. procedure RegisterAssembler(const r:tasminfo;c:TAssemblerClass);
  1272. var
  1273. t : tasm;
  1274. begin
  1275. t:=r.id;
  1276. if assigned(asminfos[t]) then
  1277. writeln('Warning: Assembler is already registered!')
  1278. else
  1279. Getmem(asminfos[t],sizeof(tasminfo));
  1280. asminfos[t]^:=r;
  1281. CAssembler[t]:=c;
  1282. end;
  1283. procedure InitAssembler;
  1284. begin
  1285. end;
  1286. procedure DoneAssembler;
  1287. begin
  1288. end;
  1289. end.