t_sinclairql.pas 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. {
  2. Copyright (c) 2020 by Free Pascal Development Team
  3. This unit implements support import, export, link routines
  4. for the m68k Sinclair QL target
  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. unit t_sinclairql;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. rescmn, comprsrc, link;
  23. type
  24. PLinkerSinclairQL = ^TLinkerSinclairQL;
  25. TLinkerSinclairQL = class(texternallinker)
  26. private
  27. Origin: DWord;
  28. UseVLink: boolean;
  29. ExeLength: longint;
  30. function WriteResponseFile(isdll: boolean): boolean;
  31. procedure SetSinclairQLInfo;
  32. function MakeSinclairQLExe: boolean;
  33. public
  34. constructor Create; override;
  35. procedure SetDefaultInfo; override;
  36. procedure InitSysInitUnitName; override;
  37. function MakeExecutable: boolean; override;
  38. end;
  39. implementation
  40. uses
  41. sysutils,cutils,cfileutl,cclasses,aasmbase,
  42. globtype,globals,systems,verbose,cscript,fmodule,i_sinclairql;
  43. const
  44. DefaultOrigin = $0;
  45. ProgramHeaderName = 'main';
  46. constructor TLinkerSinclairQL.Create;
  47. begin
  48. UseVLink:=(cs_link_vlink in current_settings.globalswitches);
  49. Inherited Create;
  50. { allow duplicated libs (PM) }
  51. SharedLibFiles.doubles:=true;
  52. StaticLibFiles.doubles:=true;
  53. end;
  54. procedure TLinkerSinclairQL.SetSinclairQLInfo;
  55. begin
  56. if ImageBaseSetExplicity then
  57. Origin:=ImageBase
  58. else
  59. Origin:=DefaultOrigin;
  60. with Info do
  61. begin
  62. if not UseVLink then
  63. begin
  64. ExeCmd[1]:='ld $DYNLINK $OPT -d -n -o $EXE $RES';
  65. end
  66. else
  67. begin
  68. ExeCmd[1]:='vlink -b rawseg -q $FLAGS $GCSECTIONS $OPT $STRIP $MAP -o $EXE -T $RES';
  69. end;
  70. end;
  71. end;
  72. procedure TLinkerSinclairQL.SetDefaultInfo;
  73. begin
  74. if target_info.system = system_m68k_sinclairql then
  75. SetSinclairQLInfo;
  76. end;
  77. procedure TLinkerSinclairQL.InitSysInitUnitName;
  78. begin
  79. sysinitunit:='si_prc';
  80. end;
  81. function TLinkerSinclairQL.WriteResponseFile(isdll: boolean): boolean;
  82. var
  83. linkres : TLinkRes;
  84. HPath : TCmdStrListItem;
  85. s : string;
  86. begin
  87. WriteResponseFile:=False;
  88. { Open link.res file }
  89. LinkRes:=TLinkRes.Create(outputexedir+Info.ResName,true);
  90. { Write path to search libraries }
  91. HPath:=TCmdStrListItem(current_module.locallibrarysearchpath.First);
  92. while assigned(HPath) do
  93. begin
  94. s:=HPath.Str;
  95. if (cs_link_on_target in current_settings.globalswitches) then
  96. s:=ScriptFixFileName(s);
  97. LinkRes.Add('-L'+s);
  98. HPath:=TCmdStrListItem(HPath.Next);
  99. end;
  100. HPath:=TCmdStrListItem(LibrarySearchPath.First);
  101. while assigned(HPath) do
  102. begin
  103. s:=HPath.Str;
  104. if s<>'' then
  105. LinkRes.Add('SEARCH_DIR("'+s+'")');
  106. HPath:=TCmdStrListItem(HPath.Next);
  107. end;
  108. LinkRes.Add('INPUT (');
  109. { add objectfiles, start with prt0 always }
  110. if not (target_info.system in systems_internal_sysinit) then
  111. begin
  112. s:=FindObjectFile('prt0','',false);
  113. LinkRes.AddFileName(maybequoted(s));
  114. end;
  115. while not ObjectFiles.Empty do
  116. begin
  117. s:=ObjectFiles.GetFirst;
  118. if s<>'' then
  119. begin
  120. { vlink doesn't use SEARCH_DIR for object files }
  121. if UseVLink then
  122. s:=FindObjectFile(s,'',false);
  123. LinkRes.AddFileName(maybequoted(s));
  124. end;
  125. end;
  126. { Write staticlibraries }
  127. if not StaticLibFiles.Empty then
  128. begin
  129. { vlink doesn't need, and doesn't support GROUP }
  130. if not UseVLink then
  131. begin
  132. LinkRes.Add(')');
  133. LinkRes.Add('GROUP(');
  134. end;
  135. while not StaticLibFiles.Empty do
  136. begin
  137. S:=StaticLibFiles.GetFirst;
  138. LinkRes.AddFileName(maybequoted(s));
  139. end;
  140. end;
  141. LinkRes.Add(')');
  142. with LinkRes do
  143. begin
  144. Add('');
  145. Add('PHDRS {');
  146. Add(' '+ProgramHeaderName+' PT_LOAD;');
  147. Add('}');
  148. Add('SECTIONS');
  149. Add('{');
  150. Add(' . = 0x'+hexstr(Origin,8)+';');
  151. Add(' .text : {');
  152. Add(' _stext = .;');
  153. Add(' *(.text .text.* )');
  154. Add(' *(.data .data.* .rodata .rodata.* .fpc.* )');
  155. Add(' *(.stack .stack.*)');
  156. { force the end of section to be word aligned }
  157. Add(' . = ALIGN(2); SHORT(0x514C);');
  158. Add(' _etext = .;');
  159. Add(' } :'+ProgramHeaderName);
  160. Add(' .bss (NOLOAD): {');
  161. Add(' _sbss = .;');
  162. Add(' *(.bss .bss.*)');
  163. Add(' _ebss = .;');
  164. Add(' } :'+ProgramHeaderName);
  165. Add('}');
  166. end;
  167. { Write and Close response }
  168. linkres.writetodisk;
  169. linkres.free;
  170. WriteResponseFile:=True;
  171. end;
  172. function TLinkerSinclairQL.MakeSinclairQLExe: boolean;
  173. var
  174. BinStr,
  175. CmdStr : TCmdStr;
  176. StripStr: string[40];
  177. DynLinkStr : string;
  178. GCSectionsStr : string;
  179. FlagsStr : string;
  180. MapStr : string;
  181. ExeName: string;
  182. fd,fs: file;
  183. fhdr: text;
  184. buf: pointer;
  185. bufread,bufsize: longint;
  186. HdrName: string;
  187. HeaderLine: string;
  188. HeaderSize: longint;
  189. code: word;
  190. begin
  191. StripStr:='';
  192. GCSectionsStr:='';
  193. DynLinkStr:='';
  194. FlagsStr:='';
  195. MapStr:='';
  196. if (cs_link_map in current_settings.globalswitches) then
  197. MapStr:='-M'+maybequoted(ScriptFixFilename(current_module.mapfilename));
  198. if (cs_link_strip in current_settings.globalswitches) then
  199. StripStr:='-s';
  200. if rlinkpath<>'' then
  201. DynLinkStr:='--rpath-link '+rlinkpath;
  202. if UseVLink then
  203. begin
  204. if create_smartlink_sections then
  205. GCSectionsStr:='-gc-all';
  206. end;
  207. ExeName:=current_module.exefilename;
  208. HdrName:=ExeName+'.hdr';
  209. { Call linker }
  210. SplitBinCmd(Info.ExeCmd[1],BinStr,CmdStr);
  211. binstr:=FindUtil(utilsprefix+BinStr);
  212. Replace(cmdstr,'$OPT',Info.ExtraOptions);
  213. Replace(cmdstr,'$EXE',maybequoted(ScriptFixFileName(ExeName)));
  214. Replace(cmdstr,'$RES',maybequoted(ScriptFixFileName(outputexedir+Info.ResName)));
  215. Replace(cmdstr,'$MAP',MapStr);
  216. Replace(cmdstr,'$FLAGS',FlagsStr);
  217. Replace(cmdstr,'$STRIP',StripStr);
  218. Replace(cmdstr,'$GCSECTIONS',GCSectionsStr);
  219. Replace(cmdstr,'$DYNLINK',DynLinkStr);
  220. MakeSinclairQLExe:=DoExec(BinStr,CmdStr,true,false);
  221. { Kludge:
  222. With the above linker script, vlink will produce two files. The main binary
  223. and the relocation info. Here we copy the two together. (KB) }
  224. if MakeSinclairQLExe then
  225. begin
  226. ExeLength:=0;
  227. bufsize:=16384;
  228. {$push}
  229. {$i-}
  230. { Rename vlink's output file into the header file it is, then parse the
  231. expected length from it. Later we use either this size or the final binary
  232. size in the BASIC loader, depending on which one is bigger. (KB) }
  233. RenameFile(ExeName,HdrName);
  234. assign(fhdr,HdrName);
  235. reset(fhdr);
  236. readln(fhdr,HeaderLine);
  237. Val(Copy(HeaderLine,RPos('0x',HeaderLine),Length(HeaderLine)),HeaderSize,code);
  238. close(fhdr);
  239. buf:=GetMem(bufsize);
  240. assign(fd,ExeName);
  241. rewrite(fd,1);
  242. assign(fs,ExeName+'.'+ProgramHeaderName);
  243. reset(fs,1);
  244. repeat
  245. blockread(fs,buf^,bufsize,bufread);
  246. blockwrite(fd,buf^,bufread);
  247. until eof(fs);
  248. close(fs);
  249. // erase(fs);
  250. assign(fs,ExeName+'.'+ProgramHeaderName+'.rel'+ProgramHeaderName);
  251. reset(fs,1);
  252. repeat
  253. blockread(fs,buf^,bufsize,bufread);
  254. blockwrite(fd,buf^,bufread);
  255. until eof(fs);
  256. close(fs);
  257. // erase(fs);
  258. ExeLength:=FileSize(fd);
  259. close(fd);
  260. {$pop}
  261. FreeMem(buf);
  262. if HeaderSize > ExeLength then
  263. ExeLength:=HeaderSize;
  264. MakeSinclairQLExe:=(code = 0) and not (ExeLength = 0);
  265. end;
  266. end;
  267. function TLinkerSinclairQL.MakeExecutable:boolean;
  268. const
  269. DefaultBootString = '10 $SYM=RESPR($BINSIZE):LBYTES"win1_$EXENAME",$SYM:CALL $SYM';
  270. var
  271. success : boolean;
  272. bootfile : TScript;
  273. ExeName: String;
  274. BootStr: String;
  275. begin
  276. if not(cs_link_nolink in current_settings.globalswitches) then
  277. Message1(exec_i_linking,current_module.exefilename);
  278. { Write used files and libraries }
  279. WriteResponseFile(false);
  280. success:=MakeSinclairQLExe;
  281. { Remove ReponseFile }
  282. if (success) and not(cs_link_nolink in current_settings.globalswitches) then
  283. DeleteFile(outputexedir+Info.ResName);
  284. if (success) then
  285. begin
  286. ExeName:=current_module.exefilename;
  287. BootStr:=DefaultBootString;
  288. Replace(BootStr,'$BINSIZE',tostr(ExeLength));
  289. Replace(BootStr,'$EXENAME',ExeName);
  290. Replace(ExeName,target_info.exeext,'');
  291. Replace(BootStr,'$SYM',ExeName);
  292. { Write bootfile }
  293. bootfile:=TScript.Create(outputexedir+ExeName);
  294. bootfile.Add(BootStr);
  295. bootfile.writetodisk;
  296. bootfile.Free;
  297. end;
  298. MakeExecutable:=success; { otherwise a recursive call to link method }
  299. end;
  300. {*****************************************************************************
  301. Initialize
  302. *****************************************************************************}
  303. initialization
  304. RegisterLinker(ld_sinclairql,TLinkerSinclairQL);
  305. RegisterTarget(system_m68k_sinclairql_info);
  306. end.